Import Cobalt 20.master.0.246192
diff --git a/src/base/base.gyp b/src/base/base.gyp
index 5a7fb5d..22276ff 100644
--- a/src/base/base.gyp
+++ b/src/base/base.gyp
@@ -784,6 +784,9 @@
         'json/json_writer_unittest.cc',
         'json/string_escape_unittest.cc',
         'lazy_instance_unittest.cc',
+        'log_once_unittest.cc',
+        'log_once_unittest_1.h',
+        'log_once_unittest_2.h',
         'logging_unittest.cc',
         'md5_unittest.cc',
         'memory/aligned_memory_unittest.cc',
diff --git a/src/base/base_switches.cc b/src/base/base_switches.cc
index c1650ea..bced748 100644
--- a/src/base/base_switches.cc
+++ b/src/base/base_switches.cc
@@ -24,6 +24,12 @@
 // Comma-separated list of feature names to enable. See also kDisableFeatures.
 const char kEnableFeatures[] = "enable-features";
 
+// Setting this switch defines which font format Cobalt will load from it's
+// local font package. Values include 'ttf', 'ttf-preferred', 'woff2', and
+// 'woff2-preferred'. Values with 'preferred' load all fonts but prioritize the
+// format specified. 'ttf' is the default value.
+const char kFontFormat[] = "font-format";
+
 // Generates full memory crash dump.
 const char kFullMemoryCrashReport[]         = "full-memory-crash-report";
 
diff --git a/src/base/base_switches.h b/src/base/base_switches.h
index 4ef070d..0a79f18 100644
--- a/src/base/base_switches.h
+++ b/src/base/base_switches.h
@@ -18,6 +18,7 @@
 extern const char kEnableCrashReporter[];
 extern const char kEnableFeatures[];
 extern const char kEnableLowEndDeviceMode[];
+extern const char kFontFormat[];
 extern const char kForceFieldTrials[];
 extern const char kFullMemoryCrashReport[];
 extern const char kNoErrorDialogs[];
diff --git a/src/base/log_once_unittest.cc b/src/base/log_once_unittest.cc
new file mode 100644
index 0000000..8a9e953
--- /dev/null
+++ b/src/base/log_once_unittest.cc
@@ -0,0 +1,127 @@
+// 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 "base/log_once_unittest_1.h"
+#include "base/log_once_unittest_2.h"
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+static inline void log_once_unittest() {
+  // Note: The LOG_ONCE statement below must remain on the same line as the
+  // LOG_ONCE statements in log_once_unittest_1.h and log_once_unittest_2.h
+  LOG_ONCE(INFO) << "This is log_once_unittest().";
+  SB_DCHECK(__LINE__ == 28);  // Note keep in sync with the other statements.
+}
+
+static inline void notimplemented_log_once_unittest() {
+  // Note: The NOTIMPLEMENTED_LOG_ONCE statement below must remain on the same
+  // line as the NOTIMPLEMENTED_LOG_ONCE statements in log_once_unittest_1.h
+  // and log_once_unittest_2.h
+  NOTIMPLEMENTED_LOG_ONCE() << "This is notimplemented_log_once_unittest().";
+  SB_DCHECK(__LINE__ == 36);  // Note keep in sync with the other statements.
+}
+}  // namespace
+
+namespace logging {
+namespace {
+
+TEST(LogOnceTest, LogOnce) {
+  // Set up a callback function to capture the log output string.
+  auto old_log_message_handler = GetLogMessageHandler();
+  // Use a static because only captureless lambdas can be converted to a
+  // function pointer for SetLogMessageHandler().
+  static std::string* log_string_ptr = nullptr;
+  std::string log_string;
+  log_string_ptr = &log_string;
+  SetLogMessageHandler([](int severity, const char* file, int line,
+                          size_t start, const std::string& str) -> bool {
+    *log_string_ptr = str;
+    return true;
+  });
+
+  log_once_unittest();
+  EXPECT_NE(std::string::npos, log_string.find("This is log_once_unittest()."));
+
+  log_once_unittest_1();
+  EXPECT_NE(std::string::npos,
+            log_string.find("This is log_once_unittest_1()."));
+
+  log_once_unittest_2();
+  EXPECT_NE(std::string::npos,
+            log_string.find("This is log_once_unittest_2()."));
+
+  log_string.clear();
+
+  log_once_unittest();
+  EXPECT_TRUE(log_string.empty());
+
+  log_once_unittest_1();
+  EXPECT_TRUE(log_string.empty());
+
+  log_once_unittest_2();
+  EXPECT_TRUE(log_string.empty());
+
+  // Clean up.
+  SetLogMessageHandler(old_log_message_handler);
+  log_string_ptr = nullptr;
+}
+
+TEST(LogOnceTest, NotImplementedLogOnce) {
+  // Set up a callback function to capture the log output string.
+  auto old_log_message_handler = GetLogMessageHandler();
+  // Use a static because only captureless lambdas can be converted to a
+  // function pointer for SetLogMessageHandler().
+  static std::string* log_string_ptr = nullptr;
+  std::string log_string;
+  log_string_ptr = &log_string;
+  SetLogMessageHandler([](int severity, const char* file, int line,
+                          size_t start, const std::string& str) -> bool {
+    *log_string_ptr = str;
+    return true;
+  });
+
+  notimplemented_log_once_unittest();
+  EXPECT_NE(std::string::npos,
+            log_string.find("This is notimplemented_log_once_unittest()."));
+
+  notimplemented_log_once_unittest_1();
+  EXPECT_NE(std::string::npos,
+            log_string.find("This is notimplemented_log_once_unittest_1()."));
+
+  notimplemented_log_once_unittest_2();
+  EXPECT_NE(std::string::npos,
+            log_string.find("This is notimplemented_log_once_unittest_2()."));
+
+  log_string.clear();
+
+  notimplemented_log_once_unittest();
+  EXPECT_TRUE(log_string.empty());
+
+  notimplemented_log_once_unittest_1();
+  EXPECT_TRUE(log_string.empty());
+
+  notimplemented_log_once_unittest_2();
+  EXPECT_TRUE(log_string.empty());
+
+  // Clean up.
+  SetLogMessageHandler(old_log_message_handler);
+  log_string_ptr = nullptr;
+}
+
+}  // namespace
+}  // namespace logging
diff --git a/src/base/log_once_unittest_1.h b/src/base/log_once_unittest_1.h
new file mode 100644
index 0000000..42ebdde
--- /dev/null
+++ b/src/base/log_once_unittest_1.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef BASE_LOG_ONCE_UNITTEST_1_H_
+#define BASE_LOG_ONCE_UNITTEST_1_H_
+
+#include "base/logging.h"
+
+// Empty lines for alignment follow.
+// ...
+// ...
+// ...
+static inline void log_once_unittest_1() {
+  // Note: The LOG_ONCE statement below must remain on the same line as the
+  // LOG_ONCE statements in log_once_unittest_2.h and logging_unittest.c
+  LOG_ONCE(INFO) << "This is log_once_unittest_1().";
+  SB_DCHECK(__LINE__ == 28);  // Note keep in sync with the other statements.
+}
+
+static inline void notimplemented_log_once_unittest_1() {
+  // Note: The NOTIMPLEMENTED_LOG_ONCE statement below must remain on the same
+  // line as the NOTIMPLEMENTED_LOG_ONCE statements in log_once_unittest_2.h
+  // and logging_unittest.c
+  NOTIMPLEMENTED_LOG_ONCE() << "This is notimplemented_log_once_unittest_1().";
+  SB_DCHECK(__LINE__ == 36);  // Note keep in sync with the other statements.
+}
+
+#endif  // BASE_LOG_ONCE_UNITTEST_1_H_
diff --git a/src/base/log_once_unittest_2.h b/src/base/log_once_unittest_2.h
new file mode 100644
index 0000000..1930031
--- /dev/null
+++ b/src/base/log_once_unittest_2.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef BASE_LOG_ONCE_UNITTEST_2_H_
+#define BASE_LOG_ONCE_UNITTEST_2_H_
+
+#include "base/logging.h"
+
+// Empty lines for alignment follow.
+// ...
+// ...
+// ...
+static inline void log_once_unittest_2() {
+  // Note: The LOG_ONCE statement below must remain on the same line as the
+  // LOG_ONCE statements in log_once_unittest_1.h and logging_unittest.c
+  LOG_ONCE(INFO) << "This is log_once_unittest_2().";
+  SB_DCHECK(__LINE__ == 28);  // Note keep in sync with the other statements.
+}
+
+static inline void notimplemented_log_once_unittest_2() {
+  // Note: The NOTIMPLEMENTED_LOG_ONCE statement below must remain on the same
+  // line as the NOTIMPLEMENTED_LOG_ONCE statements in log_once_unittest_1.h
+  // and logging_unittest.c
+  NOTIMPLEMENTED_LOG_ONCE() << "This is notimplemented_log_once_unittest_2().";
+  SB_DCHECK(__LINE__ == 36);  // Note keep in sync with the other statements.
+}
+
+#endif  // BASE_LOG_ONCE_UNITTEST_2_H_
diff --git a/src/base/logging.h b/src/base/logging.h
index da6384d..b916a0e 100644
--- a/src/base/logging.h
+++ b/src/base/logging.h
@@ -335,7 +335,7 @@
   return arg || AnalyzerNoReturn();
 }
 
-#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg))
+#define ANALYZER_ASSUME_TRUE(arg) ::logging::AnalyzerAssumeTrue(!!(arg))
 #define ANALYZER_SKIP_THIS_PATH() \
   static_cast<void>(::logging::AnalyzerNoReturn())
 #define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
@@ -445,6 +445,44 @@
 #define LOG_IF(severity, condition) \
   LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
 
+#if defined(OFFICIAL_BUILD)
+#define LOG_ONCE(severity) EAT_STREAM_PARAMETERS
+#else  // defined(OFFICIAL_BUILD)
+#define LOG_ONCE_MSG "[once] "
+
+constexpr uint32_t kFnvOffsetBasis32 = 0x811c9dc5U;
+constexpr uint32_t kFnvPrime32 = 0x01000193U;
+
+inline constexpr uint32_t hash_32_fnv1a_const(
+    const char* const str,
+    const uint32_t value = ::logging::kFnvOffsetBasis32) noexcept {
+  return (str[0] == '\0')
+             ? value
+             : ::logging::hash_32_fnv1a_const(
+                   &str[1], static_cast<uint32_t>(1ULL * (value ^ str[0]) *
+                                                  kFnvPrime32));
+}
+
+template <uint32_t FILE_HASH, int LINE>
+struct LogOnceHelper {
+  static bool logged_;
+};
+
+template <uint32_t FILE_HASH, int LINE>
+bool LogOnceHelper<FILE_HASH, LINE>::logged_ = false;
+
+// LOG_ONCE() logs the streamed message only the first time when the
+// statement is executed. Note: When this is inline functions in included files,
+// the statement can be logged for each compilation unit.
+#define LOG_ONCE(severity)                                                    \
+  LOG_IF(severity,                                                            \
+         (!::logging::LogOnceHelper<::logging::hash_32_fnv1a_const(__FILE__), \
+                                    __LINE__>::logged_ &&                     \
+          (::logging::LogOnceHelper<::logging::hash_32_fnv1a_const(__FILE__), \
+                                    __LINE__>::logged_ = true)))              \
+      << LOG_ONCE_MSG
+#endif  // defined(OFFICIAL_BUILD)
+
 // The VLOG macros log with negative verbosities.
 #define VLOG_STREAM(verbose_level) \
   ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
@@ -851,6 +889,7 @@
 #define DLOG_IS_ON(severity) LOG_IS_ON(severity)
 #define DLOG_IF(severity, condition) LOG_IF(severity, condition)
 #define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DLOG_ONCE(severity) LOG_ONCE(severity)
 #define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
 #define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
 #define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
@@ -864,6 +903,7 @@
 #define DLOG_IS_ON(severity) false
 #define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
 #define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DLOG_ONCE(severity) EAT_STREAM_PARAMETERS
 #define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
 #define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
 #define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
@@ -1218,7 +1258,10 @@
 // The NOTIMPLEMENTED() macro annotates codepaths which have not been
 // implemented yet. If output spam is a serious concern,
 // NOTIMPLEMENTED_LOG_ONCE can be used.
-
+#if defined(OFFICIAL_BUILD)
+#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
+#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS
+#else
 #if defined(COMPILER_GCC)
 // On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
 // of the current function in the NOTIMPLEMENTED message.
@@ -1227,18 +1270,14 @@
 #define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
 #endif
 
-#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
-#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
-#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS
-#else
 #define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
-#define NOTIMPLEMENTED_LOG_ONCE()                      \
-  do {                                                 \
-    static bool logged_once = false;                   \
-    LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
-    logged_once = true;                                \
-  } while (0);                                         \
-  EAT_STREAM_PARAMETERS
+#define NOTIMPLEMENTED_LOG_ONCE()                                             \
+  LOG_IF(ERROR,                                                               \
+         (!::logging::LogOnceHelper<::logging::hash_32_fnv1a_const(__FILE__), \
+                                    __LINE__>::logged_ &&                     \
+          (::logging::LogOnceHelper<::logging::hash_32_fnv1a_const(__FILE__), \
+                                    __LINE__>::logged_ = true)))              \
+      << NOTIMPLEMENTED_MSG
 #endif
 
 #endif  // BASE_LOGGING_H_
diff --git a/src/base/message_loop/f.txt b/src/base/message_loop/f.txt
deleted file mode 100644
index afeaeae..0000000
--- a/src/base/message_loop/f.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-/usr/local/google/home/aabtop/goma/gomacc
-]0;Building /usr/local/google/home/aabtop/src/cobalt_rebase -j2000 linux-x64x11_debug base_unittests -k0 -nBuilding /usr/local/google/home/aabtop/src/cobalt_rebase -j2000 linux-x64x11_debug base_unittests -k0 -n
-[1/3] CXX obj/base/trace_event/base.trace_event_memory_overhead.cc.o
-[2/3] AR obj/base/libbase.a
-[3/3] LINK base_unittests
diff --git a/src/base/third_party/symbolize/symbolize.cc b/src/base/third_party/symbolize/symbolize.cc
index 290b54b..c13be34 100644
--- a/src/base/third_party/symbolize/symbolize.cc
+++ b/src/base/third_party/symbolize/symbolize.cc
@@ -51,8 +51,15 @@
 // this file.
 
 #include "build/build_config.h"
+
 #include "utilities.h"
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/common/log.h"
+#include "starboard/elf_loader/evergreen_info.h"
+#include "starboard/memory.h"
+#endif
+
 #if defined(HAVE_SYMBOLIZE)
 
 #include <limits>
@@ -491,6 +498,17 @@
   return const_cast<char *>(p);
 }
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+static ATTRIBUTE_NOINLINE int OpenFile(const char* file_name) {
+  int object_fd = -1;
+  NO_INTR(object_fd = open(file_name, O_RDONLY));
+  if (object_fd < 0) {
+    return -1;
+  }
+  return object_fd;
+}
+#endif
+
 // Search for the object file (from /proc/self/maps) that contains
 // the specified pc. If found, open this file and return the file handle,
 // and also set start_address to the start address of where this object
@@ -601,8 +619,26 @@
   uint64_t pc0 = reinterpret_cast<uintptr_t>(pc);
   uint64_t start_address = 0;
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  char* file_name = NULL;
+  EvergreenInfo evergreen_info;
+  if (GetEvergreenInfo(&evergreen_info)) {
+    if (IS_EVERGREEN_ADDRESS(pc, evergreen_info)) {
+      file_name = evergreen_info.file_path_buf;
+      start_address = evergreen_info.base_address;
+    }
+  }
+  int object_fd = -1;
+  if (file_name != NULL) {
+    object_fd = OpenFile(file_name);
+  } else {
+    object_fd =
+        OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address);
+  }
+#else
   int object_fd =
       OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address);
+#endif
   if (object_fd == -1) {
     return false;
   }
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 8b7159d..c91d303 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -44,9 +44,11 @@
                                      int* offset_in_frames, bool* is_playing,
                                      bool* is_eos_reached, void* context);
   static void ConsumeFramesFunc(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                 SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+                                // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                 void* context);
 
   void UpdateSourceStatus(int* frames_in_buffer, int* offset_in_frames,
@@ -144,14 +146,19 @@
 }
 
 // static
-void AudioDevice::Impl::ConsumeFramesFunc(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                          SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                          void* context) {
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+void AudioDevice::Impl::ConsumeFramesFunc(
+    int frames_consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    SbTime frames_consumed_at,
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    void* context) {
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   SB_UNREFERENCED_PARAMETER(frames_consumed_at);
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   AudioDevice::Impl* impl = reinterpret_cast<AudioDevice::Impl*>(context);
   DCHECK(impl);
@@ -174,7 +181,7 @@
   DCHECK_GE(frames_rendered_, frames_consumed_);
   *frames_in_buffer = static_cast<int>(frames_rendered_ - frames_consumed_);
 
-  if ((frames_per_channel_ - *frames_in_buffer) >= kRenderBufferSizeFrames) {
+  while ((frames_per_channel_ - *frames_in_buffer) >= kRenderBufferSizeFrames) {
     // If there was silence last time we were called, then the buffer has
     // already been zeroed out and we don't need to do it again.
     if (!was_silence_last_update_) {
diff --git a/src/cobalt/base/localized_strings.cc b/src/cobalt/base/localized_strings.cc
index 1d44ba7..1c590a4 100644
--- a/src/cobalt/base/localized_strings.cc
+++ b/src/cobalt/base/localized_strings.cc
@@ -126,7 +126,7 @@
   const std::string filename = GetFilenameForLanguage(language);
   std::string file_contents;
   if (!ReadFile(filename, &file_contents)) {
-    DLOG(ERROR) << "Error reading i18n file.";
+    DLOG_ONCE(ERROR) << "Error reading i18n file.";
     return false;
   }
   DCHECK_GT(file_contents.length(), 0);
diff --git a/src/cobalt/black_box_tests/tests/web_debugger.py b/src/cobalt/black_box_tests/tests/web_debugger.py
index 7baa7ba..f7b9b11 100644
--- a/src/cobalt/black_box_tests/tests/web_debugger.py
+++ b/src/cobalt/black_box_tests/tests/web_debugger.py
@@ -193,6 +193,13 @@
     self.context_id = context_event['params']['context']['id']
     assert self.context_id > 0
 
+  def enable_dom(self):
+    """Helper to enable the 'DOM' domain and get the document."""
+    self.run_command('DOM.enable')
+    self.wait_event('DOM.documentUpdated')
+    doc_response = self.run_command('DOM.getDocument')
+    return doc_response['result']['root']
+
   def evaluate_js(self, expression):
     """Helper for the 'Runtime.evaluate' command to run some JavaScript."""
     if _DEBUG:
@@ -268,10 +275,7 @@
     self.assertEqual('hello', console_event['params']['args'][0]['value'])
 
   def test_dom_tree(self):
-    self.debugger.run_command('DOM.enable')
-
-    doc_response = self.debugger.run_command('DOM.getDocument')
-    doc_root = doc_response['result']['root']
+    doc_root = self.debugger.enable_dom()
     self.assertEqual('#document', doc_root['nodeName'])
 
     doc_url = doc_root['documentURL']
@@ -413,10 +417,7 @@
     self.assertEqual(['id', 'test'], div_test['attributes'])
 
   def test_dom_remote_object(self):
-    self.debugger.run_command('DOM.enable')
-
-    doc_response = self.debugger.run_command('DOM.getDocument')
-    doc_root = doc_response['result']['root']
+    doc_root = self.debugger.enable_dom()
     html_node = doc_root['children'][0]
     body_node = html_node['children'][1]
     self.debugger.run_command('DOM.requestChildNodes', {
@@ -463,10 +464,7 @@
     self.assertEqual(div_test['nodeId'], node_response['result']['nodeId'])
 
   def test_dom_childlist_mutation(self):
-    self.debugger.run_command('DOM.enable')
-
-    doc_response = self.debugger.run_command('DOM.getDocument')
-    doc_root = doc_response['result']['root']
+    doc_root = self.debugger.enable_dom()
     # document: <html><head></head><body></body></html>
     html_node = doc_root['children'][0]
     head_node = html_node['children'][0]
@@ -619,10 +617,7 @@
     self.assertEqual(0, div_d['childNodeCount'])  # Whitespace not reported.
 
   def test_dom_text_mutation(self):
-    self.debugger.run_command('DOM.enable')
-
-    doc_response = self.debugger.run_command('DOM.getDocument')
-    doc_root = doc_response['result']['root']
+    doc_root = self.debugger.enable_dom()
     # document: <html><head></head><body></body></html>
     html_node = doc_root['children'][0]
     body_node = html_node['children'][1]
@@ -762,10 +757,7 @@
     self.assertEqual('Four', inserted_event['params']['node']['nodeValue'])
 
   def test_dom_attribute_mutation(self):
-    self.debugger.run_command('DOM.enable')
-
-    doc_response = self.debugger.run_command('DOM.getDocument')
-    doc_root = doc_response['result']['root']
+    doc_root = self.debugger.enable_dom()
     # document: <html><head></head><body></body></html>
     html_node = doc_root['children'][0]
     body_node = html_node['children'][1]
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 666c6e9..bc81212 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -183,9 +183,6 @@
         ['enable_about_scheme == 1', {
           'defines': [ 'ENABLE_ABOUT_SCHEME' ],
         }],
-        ['enable_map_to_mesh == 1', {
-          'defines' : ['ENABLE_MAP_TO_MESH'],
-        }],
         ['enable_debugger == 1', {
           'sources': [
             'debug_console.cc',
@@ -198,16 +195,8 @@
           ],
         }],
         ['mesh_cache_size_in_bytes == "auto"', {
-          'conditions': [
-            ['enable_map_to_mesh==1', {
-              'defines': [
-                'COBALT_MESH_CACHE_SIZE_IN_BYTES=1*1024*1024',
-              ],
-            }, {
-              'defines': [
-                'COBALT_MESH_CACHE_SIZE_IN_BYTES=0',
-              ],
-            }],
+          'defines': [
+            'COBALT_MESH_CACHE_SIZE_IN_BYTES=1*1024*1024',
           ],
         }, {
           'defines': [
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 59982d1..dfd451f 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -58,7 +58,7 @@
 
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #include "base/memory/ptr_util.h"
-#include "starboard/ps4/core_dump_handler.h"
+#include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 
 using cobalt::cssom::ViewportSize;
@@ -438,8 +438,11 @@
   lifecycle_observers_.AddObserver(debug_console_.get());
 #endif  // defined(ENABLE_DEBUGGER)
 
-  if (command_line->HasSwitch(switches::kEnableMapToMeshRectanglar)) {
-    options_.web_module_options.enable_map_to_mesh_rectangular = true;
+  const renderer::Pipeline* pipeline =
+      renderer_module_ ? renderer_module_->pipeline() : nullptr;
+  if (command_line->HasSwitch(switches::kDisableMapToMesh) ||
+      !renderer::Pipeline::IsMapToMeshEnabled(pipeline)) {
+    options_.web_module_options.enable_map_to_mesh = false;
   }
 
   if (qr_overlay_info_layer_) {
@@ -1820,7 +1823,7 @@
       GetViewportSize().width_height(), options_.command_line_auto_mem_settings,
       options_.build_auto_mem_settings));
 
-  LOG(INFO) << "\n\n" << auto_mem_->ToPrettyPrintString(SbLogIsTty()) << "\n\n";
+  LOG(INFO) << auto_mem_->ToPrettyPrintString(SbLogIsTty());
 
   if (javascript_gc_threshold_in_bytes_) {
     DCHECK_EQ(*javascript_gc_threshold_in_bytes_,
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
index 03c57a4..e50ae16 100644
--- a/src/cobalt/browser/memory_settings/auto_mem.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -367,9 +367,9 @@
 std::string AutoMem::ToPrettyPrintString(bool use_color_ascii) const {
   std::stringstream ss;
 
-  ss << "AutoMem:\n\n";
+  ss << "AutoMem:\n";
   std::vector<const MemorySetting*> all_settings = AllMemorySettings();
-  ss << GeneratePrettyPrintTable(use_color_ascii, all_settings) << "\n";
+  ss << GeneratePrettyPrintTable(use_color_ascii, all_settings);
 
   int64_t cpu_consumption =
       SumMemoryConsumption(MemorySetting::kCPU, all_settings);
diff --git a/src/cobalt/browser/memory_settings/auto_mem_settings.cc b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
index 55e0a7c..9ef9a59 100644
--- a/src/cobalt/browser/memory_settings/auto_mem_settings.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
@@ -33,9 +33,7 @@
 namespace memory_settings {
 namespace {
 bool HasBlitter() {
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-  const bool has_blitter = SbBlitterIsBlitterSupported();
-#elif SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
   const bool has_blitter = true;
 #else
   const bool has_blitter = false;
diff --git a/src/cobalt/browser/memory_settings/pretty_print.cc b/src/cobalt/browser/memory_settings/pretty_print.cc
index 81c6228..4dd633b 100644
--- a/src/cobalt/browser/memory_settings/pretty_print.cc
+++ b/src/cobalt/browser/memory_settings/pretty_print.cc
@@ -164,7 +164,7 @@
   }
 
   std::stringstream ss;
-  ss << "\n\n";
+  ss << "\n";
 
   FillStream(max_span + 4, border_ch, &ss);
   ss << "\n";
@@ -177,7 +177,7 @@
     ss << "\n";
   }
   FillStream(max_span + 4, border_ch, &ss);
-  ss << "\n\n";
+  ss << "\n";
   return ss.str();
 }
 
diff --git a/src/cobalt/browser/memory_settings/pretty_print_test.cc b/src/cobalt/browser/memory_settings/pretty_print_test.cc
index 716fb86..9f4ffe5 100644
--- a/src/cobalt/browser/memory_settings/pretty_print_test.cc
+++ b/src/cobalt/browser/memory_settings/pretty_print_test.cc
@@ -23,12 +23,12 @@
 #include <vector>
 
 #include "base/command_line.h"
-#include "base/logging.h"
 #include "cobalt/browser/memory_settings/memory_settings.h"
 #include "cobalt/browser/memory_settings/test_common.h"
 #include "cobalt/browser/switches.h"
 #include "starboard/common/log.h"
 #include "starboard/memory.h"
+#include "starboard/string.h"
 #include "starboard/system.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,32 +37,42 @@
 namespace browser {
 namespace memory_settings {
 
+namespace {
+// Returns true if all tokens exist in the string in the given order.
+bool HasTokensInOrder(const std::string& value,
+                      std::initializer_list<const char*> tokens) {
+  std::string::size_type current_position = 0;
+  for (auto token : tokens) {
+    std::string::size_type position = value.find(token, current_position);
+    EXPECT_NE(position, std::string::npos);
+    EXPECT_GE(position, current_position);
+    if (position == std::string::npos) {
+      SB_DLOG(INFO) << "Token \"" << token << "\" not found in order.";
+      return false;
+    }
+    current_position = position + SbStringGetLength(token);
+  }
+  return true;
+}
+}  // namespace
+
 TEST(MemorySettingsPrettyPrint, GeneratePrettyPrintTable) {
   TestSettingGroup setting_group;
   setting_group.LoadDefault();
   std::string actual_string =
       GeneratePrettyPrintTable(false, setting_group.AsConstVector());
 
-  const char* expected_string =
-      " SETTING NAME                           VALUE                   TYPE   SOURCE    \n"
-      " _______________________________________________________________________________ \n"
-      "|                                      |             |         |      |         |\n"
-      "| image_cache_size_in_bytes            |        1234 |  0.0 MB |  GPU | CmdLine |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| javascript_gc_threshold_in_bytes     |        1112 |  0.0 MB |  CPU | AutoSet |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| skia_atlas_texture_dimensions        | 1234x4567x2 | 10.7 MB |  GPU | CmdLine |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| skia_cache_size_in_bytes             |    12345678 | 11.8 MB |  GPU | CmdLine |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| software_surface_cache_size_in_bytes |         N/A |     N/A |  N/A |     N/A |\n"
-      "|______________________________________|_____________|_________|______|_________|\n";
-
-  EXPECT_STREQ(expected_string, actual_string.c_str());
+  // clang-format off
+  EXPECT_TRUE(HasTokensInOrder(
+      actual_string,
+      {"SETTING NAME", "VALUE", "TYPE", "SOURCE", "\n",
+       "image_cache_size_in_bytes", "1234", "0.0 MB", "GPU", "CmdLine", "\n",
+       "javascript_gc_threshold_in_bytes", "1112", "0.0 MB", "CPU", "AutoSet", "\n",  // NOLINT(whitespace/line_length)
+       "skia_atlas_texture_dimensions", "1234x4567x2", "10.7 MB", "GPU", "CmdLine", "\n",  // NOLINT(whitespace/line_length)
+       "skia_cache_size_in_bytes", "12345678", "11.8 MB", "GPU", "CmdLine", "\n",  // NOLINT(whitespace/line_length)
+       "software_surface_cache_size_in_bytes", "N/A", "N/A", "N/A", "N/A", "\n"
+      }));
+  // clang-format on
 }
 
 TEST(MemorySettingsPrettyPrint, GenerateMemoryTableWithUnsetGpuMemory) {
@@ -77,17 +87,12 @@
                           128 * 1024 * 1024,  // 128 MB CPU consumption
                           0);                 // 0 MB GPU consumption.
 
-  const char* expected_output =
-      " MEMORY           SOURCE   TOTAL       SETTINGS CONSUME   \n"
-      " ________________________________________________________ \n"
-      "|                |        |           |                  |\n"
-      "| max_cpu_memory |  Build |  256.0 MB |         128.0 MB |\n"
-      "|________________|________|___________|__________________|\n"
-      "|                |        |           |                  |\n"
-      "| max_gpu_memory |  Unset | <UNKNOWN> |           0.0 MB |\n"
-      "|________________|________|___________|__________________|\n";
-
-  EXPECT_STREQ(expected_output, actual_output.c_str()) << actual_output;
+  // clang-format off
+  EXPECT_TRUE(HasTokensInOrder(
+      actual_output, {"MEMORY", "SOURCE", "TOTAL", "SETTINGS CONSUME", "\n",
+                      "max_cpu_memory", "Build", "256.0 MB", "128.0 MB", "\n",
+                      "max_gpu_memory", "Unset", "<UNKNOWN>", "0.0 MB", "\n"}));
+  // clang-format on
 }
 
 TEST(MemorySettingsPrettyPrint, GenerateMemoryTableWithGpuMemory) {
@@ -103,47 +108,12 @@
                           128 * 1024 * 1024,   // 128 MB CPU consumption.
                           23592960);           // 22.5 MB GPU consumption.
 
-  const char* expected_output =
-      " MEMORY           SOURCE   TOTAL      SETTINGS CONSUME   \n"
-      " _______________________________________________________ \n"
-      "|                |        |          |                  |\n"
-      "| max_cpu_memory |  Build | 256.0 MB |         128.0 MB |\n"
-      "|________________|________|__________|__________________|\n"
-      "|                |        |          |                  |\n"
-      "| max_gpu_memory |  Build |  64.0 MB |          22.5 MB |\n"
-      "|________________|________|__________|__________________|\n";
-
-  EXPECT_STREQ(expected_output, actual_output.c_str()) << actual_output;
-}
-
-TEST(MemorySettingsPrettyPrint, ToString) {
-  TestSettingGroup test_setting_group;
-  test_setting_group.LoadDefault();
-
-  std::string actual_string =
-      GeneratePrettyPrintTable(false,  // No color.
-                               test_setting_group.AsConstVector());
-
-  const char* expected_string =
-      " SETTING NAME                           VALUE                   TYPE   SOURCE    \n"
-      " _______________________________________________________________________________ \n"
-      "|                                      |             |         |      |         |\n"
-      "| image_cache_size_in_bytes            |        1234 |  0.0 MB |  GPU | CmdLine |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| javascript_gc_threshold_in_bytes     |        1112 |  0.0 MB |  CPU | AutoSet |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| skia_atlas_texture_dimensions        | 1234x4567x2 | 10.7 MB |  GPU | CmdLine |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| skia_cache_size_in_bytes             |    12345678 | 11.8 MB |  GPU | CmdLine |\n"
-      "|______________________________________|_____________|_________|______|_________|\n"
-      "|                                      |             |         |      |         |\n"
-      "| software_surface_cache_size_in_bytes |         N/A |     N/A |  N/A |     N/A |\n"
-      "|______________________________________|_____________|_________|______|_________|\n";
-
-  EXPECT_STREQ(expected_string, actual_string.c_str()) << actual_string;
+  // clang-format off
+  EXPECT_TRUE(HasTokensInOrder(
+      actual_output, {"MEMORY", "SOURCE", "TOTAL", "SETTINGS CONSUME", "\n",
+                      "max_cpu_memory", "Build", "256.0 MB", "128.0 MB", "\n",
+                      "max_gpu_memory", "Build", "64.0 MB", "22.5 MB", "\n"}));
+  // clang-format on
 }
 
 TEST(MemorySettingsPrettyPrint, GenerateMemoryWithInvalidGpuMemoryConsumption) {
@@ -160,17 +130,13 @@
       128 * 1024 * 1024,   // 128 MB CPU consumption.
       16 * 1024 * 1024);   // 16 MB GPU consumption.
 
-  const char* expected_output =
-      " MEMORY           SOURCE          TOTAL       SETTINGS CONSUME   \n"
-      " _______________________________________________________________ \n"
-      "|                |               |           |                  |\n"
-      "| max_cpu_memory |         Build |  256.0 MB |         128.0 MB |\n"
-      "|________________|_______________|___________|__________________|\n"
-      "|                |               |           |                  |\n"
-      "| max_gpu_memory | Starboard API | <UNKNOWN> |          16.0 MB |\n"
-      "|________________|_______________|___________|__________________|\n";
-
-  EXPECT_STREQ(expected_output, actual_output.c_str()) << actual_output;
+  // clang-format off
+  EXPECT_TRUE(HasTokensInOrder(
+      actual_output, {"MEMORY", "SOURCE", "TOTAL", "SETTINGS CONSUME", "\n",
+                      "max_cpu_memory", "Build", "256.0 MB", "128.0 MB", "\n",
+                      "max_gpu_memory", "Starboard API", "<UNKNOWN>", "16.0 MB", "\n"  // NOLINT(whitespace/line_length)
+                     }));
+  // clang-format on
 }
 
 }  // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/table_printer.cc b/src/cobalt/browser/memory_settings/table_printer.cc
index db19092..70276e5 100644
--- a/src/cobalt/browser/memory_settings/table_printer.cc
+++ b/src/cobalt/browser/memory_settings/table_printer.cc
@@ -115,10 +115,6 @@
   // ex: "|________|________|"
   std::string MakeRowDelimiter() const;
 
-  // Follows a data row to provide verticle space before a TopSeparatorRow().
-  // ex: "|        |        |"
-  std::string MakeTopSeparatorRowAbove() const;
-
   // ex: " _________________ "
   std::string MakeTopSeparatorRow() const;
 
@@ -140,10 +136,9 @@
   TablePrinterImpl printer(column_sizes, text_color, table_color);
 
   std::stringstream output_ss;
-  output_ss << printer.MakeHeaderRow(rows[0]) << "\n";
   output_ss << printer.MakeTopSeparatorRow() << "\n";
+  output_ss << printer.MakeHeaderRow(rows[0]) << "\n";
 
-  std::string separator_row_above = printer.MakeTopSeparatorRowAbove();
   std::string row_delimiter = printer.MakeRowDelimiter();
 
   // Print body.
@@ -152,10 +147,9 @@
 
     const std::string row_string = printer.MakeDataRow(row);
 
-    output_ss << separator_row_above << "\n";
     output_ss << row_string << "\n";
-    output_ss << row_delimiter << "\n";
   }
+  output_ss << row_delimiter << "\n";
   return output_ss.str();
 }
 
@@ -220,7 +214,7 @@
   std::stringstream ss;
 
   for (size_t i = 0; i < header_row.size(); ++i) {
-    ss << " " << header_row[i];
+    ss << "|" << header_row[i];
     const size_t name_size = header_row[i].size();
     const size_t col_width = column_sizes_[i];
 
@@ -230,7 +224,7 @@
       ss << " ";
     }
   }
-  ss << " ";
+  ss << "|";
   return ss.str();
 }
 
@@ -304,21 +298,6 @@
   std::string output = AddColor(table_color_, ss.str());
   return output;
 }
-
-std::string TablePrinterImpl::MakeTopSeparatorRowAbove() const {
-  std::stringstream ss;
-  for (size_t i = 0; i < column_sizes_.size(); ++i) {
-    ss << "|";
-    const size_t col_width = column_sizes_[i];
-    for (size_t j = 0; j < col_width; ++j) {
-      ss << " ";
-    }
-  }
-  ss << "|";
-  std::string output = AddColor(table_color_, ss.str());
-  return output;
-}
-
 }  // namespace.
 
 bool TablePrinter::SystemSupportsColor() { return SbLogIsTty(); }
diff --git a/src/cobalt/browser/memory_settings/table_printer_test.cc b/src/cobalt/browser/memory_settings/table_printer_test.cc
index ff122a4..d29c9a7 100644
--- a/src/cobalt/browser/memory_settings/table_printer_test.cc
+++ b/src/cobalt/browser/memory_settings/table_printer_test.cc
@@ -16,6 +16,8 @@
 
 #include "cobalt/browser/memory_settings/table_printer.h"
 
+#include "starboard/common/log.h"
+#include "starboard/string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
@@ -26,6 +28,7 @@
 #define GREEN_START "\x1b[32m"
 #define COLOR_END "\x1b[0m"
 
+namespace {
 std::vector<std::string> MakeRow2(const std::string& col1,
                                   const std::string& col2) {
   std::vector<std::string> row;
@@ -34,6 +37,24 @@
   return row;
 }
 
+// Returns true if all tokens exist in the string in the given order.
+bool HasTokensInOrder(const std::string& value,
+                      std::initializer_list<const char*> tokens) {
+  std::string::size_type current_position = 0;
+  for (auto token : tokens) {
+    std::string::size_type position = value.find(token, current_position);
+    EXPECT_NE(position, std::string::npos);
+    EXPECT_GE(position, current_position);
+    if (position == std::string::npos) {
+      SB_DLOG(INFO) << "Token \"" << token << "\" not found in order.";
+      return false;
+    }
+    current_position = position + SbStringGetLength(token);
+  }
+  return true;
+}
+}  // namespace
+
 TEST(TablePrinter, ToString) {
   TablePrinter table;
 
@@ -41,15 +62,8 @@
   table.AddRow(MakeRow2("col1", "col2"));
   table.AddRow(MakeRow2("value1", "value2"));
 
-  std::string table_str = table.ToString();
-  std::string expected_table_str =
-      " col1     col2     \n"
-      " _________________ \n"
-      "|        |        |\n"
-      "| value1 | value2 |\n"
-      "|________|________|\n";
-
-  EXPECT_STREQ(expected_table_str.c_str(), table_str.c_str());
+  EXPECT_TRUE(HasTokensInOrder(
+      table.ToString(), {"col1", "col2", "\n", "value1", "value2", "\n"}));
 }
 
 TEST(TablePrinter, ToStringWithColor) {
@@ -63,22 +77,10 @@
   table.AddRow(MakeRow2("col1", "col2"));
   table.AddRow(MakeRow2("value1", "value2"));
 
-// These defines will wrap color constants for string literals.
-#define R(STR) RED_START STR COLOR_END
-#define G(STR) GREEN_START STR COLOR_END
-
-  std::string table_str = table.ToString();
-  std::string expected_table_str =
-      " col1     col2     \n"
-      G(" _________________ ") "\n"
-      G("|        |        |") "\n"
-      G("|") " " R("value1") " " G("|") " " R("value2") " " G("|") "\n"
-      G("|________|________|") "\n";
-
-#undef R
-#undef G
-
-  EXPECT_STREQ(expected_table_str.c_str(), table_str.c_str());
+  EXPECT_TRUE(HasTokensInOrder(
+      table.ToString(),
+      {"col1", "col2", "\n", RED_START, "value1", COLOR_END, RED_START,
+       "value2", COLOR_END, "\n", GREEN_START, COLOR_END}));
 }
 
 #undef RED_START
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 3665aa4..6f62d51 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -213,6 +213,12 @@
 const char kDisableJavaScriptJitHelp[] =
     "Specifies that javascript jit should be disabled.";
 
+const char kDisableMapToMesh[] = "disable_map_to_mesh";
+const char kDisableMapToMeshHelp[] =
+    "Specifies that map to mesh should be disabled. Cobalt maps 360 video "
+    "textures onto a sphere in order to project 360 video onto the viewport "
+    "by default. Specifying this flag renders 360 degree video unprojected.";
+
 const char kDisableTimerResolutionLimit[] = "disable_timer_resolution_limit";
 const char kDisableTimerResolutionLimitHelp[] =
     "By default, window.performance.now() will return values at a clamped "
@@ -220,13 +226,6 @@
     "removed and the resolution will be 1us (or larger depending on the "
     "platform.";
 
-const char kEnableMapToMeshRectanglar[] = "enable_map_to_mesh_rectangular";
-const char kEnableMapToMeshRectanglarHelp[] =
-    "If toggled and map-to-mesh is supported on this platform, this allows it "
-    "to accept the 'rectangular' keyword. Useful to get rectangular stereo "
-    "video on platforms that do not support stereoscopy natively, letting the "
-    "client apply a stereo mesh projection (one that differs for each eye).";
-
 const char kEncodedImageCacheSizeInBytes[] =
     "encoded_image_cache_size_in_bytes";
 const char kEncodedImageCacheSizeInBytesHelp[] =
@@ -447,8 +446,8 @@
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
         {kDisableJavaScriptJit, kDisableJavaScriptJitHelp},
+        {kDisableMapToMesh, kDisableMapToMeshHelp},
         {kDisableTimerResolutionLimit, kDisableTimerResolutionLimitHelp},
-        {kEnableMapToMeshRectanglar, kEnableMapToMeshRectanglarHelp},
         {kEncodedImageCacheSizeInBytes, kEncodedImageCacheSizeInBytesHelp},
         {kForceMigrationForStoragePartitioning,
          kForceMigrationForStoragePartitioningHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 6d516f9..c17989e 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -99,9 +99,10 @@
 
 extern const char kDisableJavaScriptJit[];
 extern const char kDisableJavaScriptJitHelp[];
+extern const char kDisableMapToMesh[];
+extern const char kDisableMapToMeshHelp[];
 extern const char kDisableTimerResolutionLimit[];
 extern const char kDisableTimerResolutionLimitHelp[];
-extern const char kEnableMapToMeshRectanglar[];
 extern const char kEncodedImageCacheSizeInBytes[];
 extern const char kEncodedImageCacheSizeInBytesHelp[];
 extern const char kForceMigrationForStoragePartitioning[];
diff --git a/src/cobalt/browser/user_agent_string.cc b/src/cobalt/browser/user_agent_string.cc
index 42c7034..45869c4 100644
--- a/src/cobalt/browser/user_agent_string.cc
+++ b/src/cobalt/browser/user_agent_string.cc
@@ -27,6 +27,9 @@
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
 #include "starboard/system.h"
+#if SB_IS(EVERGREEN)
+#include "cobalt/updater/util.h"
+#endif
 
 namespace cobalt {
 namespace browser {
@@ -246,6 +249,15 @@
                         platform_info.rasterizer_type.c_str());
   }
 
+// Evergreen version
+#if SB_IS(EVERGREEN)
+  const std::string evergreen_version = updater::GetEvergreenVersion();
+  if (!evergreen_version.empty()) {
+    base::StringAppendF(&user_agent, " Evergreen/%s",
+                        evergreen_version.c_str());
+  }
+#endif
+
   // Starboard/APIVersion,
   if (!platform_info.starboard_version.empty()) {
     base::StringAppendF(&user_agent, " %s",
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index d1c4ad2..52c9768 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -61,6 +61,7 @@
 #include "cobalt/h5vcc/h5vcc.h"
 #include "cobalt/layout/topmost_event_target.h"
 #include "cobalt/loader/image/animated_image_tracker.h"
+#include "cobalt/loader/switches.h"
 #include "cobalt/media_session/media_session_client.h"
 #include "cobalt/page_visibility/visibility_state.h"
 #include "cobalt/script/error_report.h"
@@ -486,37 +487,10 @@
           false, "True when a render tree is produced but not yet rasterized."),
       resource_provider_(data.resource_provider),
       resource_provider_type_id_(data.resource_provider->GetTypeId()) {
-  // Currently we rely on a platform to explicitly specify that it supports
-  // the map-to-mesh filter via the ENABLE_MAP_TO_MESH define (and the
-  // 'enable_map_to_mesh' gyp variable).  When we have better support for
-  // checking for decode to texture support, it would be nice to switch this
-  // logic to something like:
-  //
-  //   supports_map_to_mesh =
-  //      (resource_provider_->Supports3D() && SbPlayerSupportsDecodeToTexture()
-  //           ? css_parser::Parser::kSupportsMapToMesh
-  //           : css_parser::Parser::kDoesNotSupportMapToMesh);
-  //
-  // Note that it is important that we do not parse map-to-mesh filters if we
-  // cannot render them, since web apps may check for map-to-mesh support by
-  // testing whether it parses or not via the CSS.supports() Web API.
-  css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh;
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-  if (SbGetGlesInterface()) {
-    supports_map_to_mesh =
-        data.options.enable_map_to_mesh_rectangular
-            ? css_parser::Parser::kSupportsMapToMeshRectangular
-            : css_parser::Parser::kSupportsMapToMesh;
-  } else {
-    supports_map_to_mesh = css_parser::Parser::kDoesNotSupportMapToMesh;
-  }
-#elif defined(ENABLE_MAP_TO_MESH)
-  supports_map_to_mesh = data.options.enable_map_to_mesh_rectangular
-                             ? css_parser::Parser::kSupportsMapToMeshRectangular
-                             : css_parser::Parser::kSupportsMapToMesh;
-#else
-  supports_map_to_mesh = css_parser::Parser::kDoesNotSupportMapToMesh;
-#endif
+  css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh =
+      data.options.enable_map_to_mesh
+          ? css_parser::Parser::kSupportsMapToMesh
+          : css_parser::Parser::kDoesNotSupportMapToMesh;
 
   css_parser_ =
       css_parser::Parser::Create(debugger_hooks_, supports_map_to_mesh);
@@ -689,8 +663,8 @@
       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.csp_insecure_allowed_token, data.dom_max_element_depth,
-      data.options.video_playback_rate_multiplier,
+      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
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 4796e89..8dc72ac 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -157,8 +157,8 @@
     // Mesh cache capacity in bytes.
     int mesh_cache_capacity;
 
-    // Whether map-to-mesh for rectangular videos is enabled.
-    bool enable_map_to_mesh_rectangular = false;
+    // Whether map-to-mesh is enabled.
+    bool enable_map_to_mesh = true;
 
     // Content Security Policy enforcement mode for this web module.
     dom::CspEnforcementType csp_enforcement_mode = dom::kCspEnforcementEnable;
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 36bfb6e..676f2c5 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -12,14 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This files contains all targets that should be created by gyp_cobalt by
+# This file contains all targets that should be created by gyp_cobalt by
 # default.
 
 {
-  'variables': {
-    'has_elf_loader%' : '<!(python ../../build/file_exists.py <(DEPTH)/starboard/elf_loader/elf_loader.gyp)',
-    'has_loader_app%' : '<!(python ../../build/file_exists.py <(DEPTH)/starboard/loader_app/loader_app.gyp)',
-  },
   'targets': [
     {
       'target_name': 'Default',
@@ -94,14 +90,9 @@
         '<(DEPTH)/sql/sql.gyp:sql_unittests_deploy',
       ],
       'conditions': [
-        ['has_elf_loader == "True" and sb_evergreen != 1', {
+        ['sb_evergreen != 1', {
           'dependencies': [
-            '<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader_test_deploy',
-          ],
-        }],
-        ['has_loader_app == "True" and sb_evergreen != 1', {
-          'dependencies': [
-            '<(DEPTH)/starboard/loader_app/loader_app.gyp:*',
+            '<!@pymod_do_main(starboard.optional.get_optional_tests -g)',
           ],
         }],
         ['OS=="starboard"', {
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 497b1fc..689d9d7 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-243969
\ No newline at end of file
+246192
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 7e556be..b9c9774 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -47,7 +47,15 @@
 
     # Enable support for the map to mesh filter, which is primarily used to
     # implement spherical video playback.
-    'enable_map_to_mesh%': 0,
+    # This setting is deprecated in favor of the cobalt graphics extension
+    # (CobaltGraphicsExtensionApi) function `IsMapToMeshEnabled()`.
+    # If the CobaltGraphicsExtensionApi is not implemented, then Cobalt will
+    # fall back onto a default. For starboard API versions 12 and later, the
+    # default is true (i.e. Cobalt will assume map to mesh is supported).
+    # For earlier starboard API versions, if this gyp variable is redefined to
+    # a value other than -1, it will use the new value as the default. If it is
+    # not redefined, the default is false.
+    'enable_map_to_mesh%': -1,
 
     # This variable defines what Cobalt's preferred strategy should be for
     # handling internally triggered application exit requests (e.g. the user
@@ -230,6 +238,10 @@
     # Set to "true" to enable v8 snapshot generation at Cobalt build time.
     'cobalt_v8_buildtime_snapshot%': '<(cobalt_v8_buildtime_snapshot)',
 
+    # Some compiler can not compile with raw assembly(.S files) and v8
+    # converts asm to inline assembly for these platforms.
+    'cobalt_v8_emit_builtins_as_inline_asm%': 1,
+
     'cobalt_enable_quic%': 1,
 
     # Cache parameters
diff --git a/src/cobalt/content/fonts/config/common/fonts.xml b/src/cobalt/content/fonts/config/common/fonts.xml
index a41d838..766e6e2 100644
--- a/src/cobalt/content/fonts/config/common/fonts.xml
+++ b/src/cobalt/content/fonts/config/common/fonts.xml
@@ -119,6 +119,18 @@
             <font weight="700" style="italic" font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic">Roboto-BoldItalic.ttf</font>
             <font weight="900" style="normal" font_name="Roboto Black" postscript_name="Roboto-Black">Roboto-Black.ttf</font>
             <font weight="900" style="italic" font_name="Roboto Black Italic" postscript_name="Roboto-Black-Italic">Roboto-BlackItalic.ttf</font>
+            <font weight="100" style="normal" font_name="Roboto Thin" postscript_name="Roboto-Thin">Roboto-Thin.woff2</font>
+            <font weight="100" style="italic" font_name="Roboto Thin Italic" postscript_name="Roboto-Thin-Italic">Roboto-ThinItalic.woff2</font>
+            <font weight="300" style="normal" font_name="Roboto Light" postscript_name="Roboto-Light">Roboto-Light.woff2</font>
+            <font weight="300" style="italic" font_name="Roboto Light Italic" postscript_name="Roboto-Light-Italic">Roboto-LightItalic.woff2</font>
+            <font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.woff2</font>
+            <font weight="400" style="italic" font_name="Roboto Regular Italic" postscript_name="Roboto-Italic">Roboto-Italic.woff2</font>
+            <font weight="500" style="normal" font_name="Roboto Medium" postscript_name="Roboto-Medium">Roboto-Medium.woff2</font>
+            <font weight="500" style="italic" font_name="Roboto Medium Italic" postscript_name="Roboto-Medium-Italic">Roboto-MediumItalic.woff2</font>
+            <font weight="700" style="normal" font_name="Roboto Bold" postscript_name="Roboto-Bold">Roboto-Bold.woff2</font>
+            <font weight="700" style="italic" font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic">Roboto-BoldItalic.woff2</font>
+            <font weight="900" style="normal" font_name="Roboto Black" postscript_name="Roboto-Black">Roboto-Black.woff2</font>
+            <font weight="900" style="italic" font_name="Roboto Black Italic" postscript_name="Roboto-Black-Italic">Roboto-BlackItalic.woff2</font>
         </family>
         <!-- Note that aliases must come after the fonts they reference. -->
         <alias name="roboto" to="sans-serif" />
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Black.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Black.woff2
new file mode 100644
index 0000000..d6787ec
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Black.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-BlackItalic.woff2 b/src/cobalt/content/fonts/font_files/Roboto-BlackItalic.woff2
new file mode 100644
index 0000000..460c8b2
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-BlackItalic.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Bold.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Bold.woff2
new file mode 100644
index 0000000..4ac54c0
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Bold.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-BoldItalic.woff2 b/src/cobalt/content/fonts/font_files/Roboto-BoldItalic.woff2
new file mode 100644
index 0000000..8f28ca1
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-BoldItalic.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Italic.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Italic.woff2
new file mode 100644
index 0000000..386f15c
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Italic.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Light.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Light.woff2
new file mode 100644
index 0000000..a77bf39
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Light.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-LightItalic.woff2 b/src/cobalt/content/fonts/font_files/Roboto-LightItalic.woff2
new file mode 100644
index 0000000..db5600c
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-LightItalic.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Medium.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Medium.woff2
new file mode 100644
index 0000000..71e47f9
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Medium.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-MediumItalic.woff2 b/src/cobalt/content/fonts/font_files/Roboto-MediumItalic.woff2
new file mode 100644
index 0000000..35dd816
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-MediumItalic.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Regular.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Regular.woff2
new file mode 100644
index 0000000..6cc9a33
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Regular.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-Thin.woff2 b/src/cobalt/content/fonts/font_files/Roboto-Thin.woff2
new file mode 100644
index 0000000..afbb768
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-Thin.woff2
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/Roboto-ThinItalic.woff2 b/src/cobalt/content/fonts/font_files/Roboto-ThinItalic.woff2
new file mode 100644
index 0000000..7ad4aa9
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/Roboto-ThinItalic.woff2
Binary files differ
diff --git a/src/cobalt/cssom/combinator.h b/src/cobalt/cssom/combinator.h
index 1e54192..d97fe80 100644
--- a/src/cobalt/cssom/combinator.h
+++ b/src/cobalt/cssom/combinator.h
@@ -34,9 +34,12 @@
   kDescendantCombinator,
   kNextSiblingCombinator,
   kFollowingSiblingCombinator,
-  kCombinatorCount,
+  kCombinatorTypeCount,
 };
 
+// Avoids the clang error: arithmetic between different enumeration types.
+constexpr size_t kCombinatorCount = static_cast<size_t>(kCombinatorTypeCount);
+
 // A combinator is punctuation that represents a particular kind of relationship
 // between the selectors on either side.
 //   https://www.w3.org/TR/selectors4/#combinator
diff --git a/src/cobalt/debug/backend/agent_base.cc b/src/cobalt/debug/backend/agent_base.cc
new file mode 100644
index 0000000..06075f7
--- /dev/null
+++ b/src/cobalt/debug/backend/agent_base.cc
@@ -0,0 +1,95 @@
+// 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 "cobalt/debug/backend/agent_base.h"
+
+#include "base/values.h"
+
+namespace cobalt {
+namespace debug {
+namespace backend {
+
+using base::Value;
+
+namespace {
+// State parameters
+constexpr char kEnabled[] = "enabled";
+}  // namespace
+
+AgentBase::AgentBase(const std::string& domain, const std::string& script_file,
+                     DebugDispatcher* dispatcher)
+    : domain_(domain),
+      script_file_(script_file),
+      dispatcher_(dispatcher),
+      commands_(domain) {
+  DCHECK(!domain_.empty());
+  DCHECK(dispatcher_);
+
+  commands_["disable"] =
+      base::Bind(&AgentBase::Disable, base::Unretained(this));
+  commands_["enable"] = base::Bind(&AgentBase::Enable, base::Unretained(this));
+}
+
+void AgentBase::Thaw(JSONObject agent_state) {
+  dispatcher_->AddDomain(domain_, commands_.Bind());
+  if (!agent_state) return;
+
+  // Restore state
+  if (agent_state->FindKeyOfType(kEnabled, Value::Type::BOOLEAN)->GetBool()) {
+    Enable(Command::IgnoreResponse(domain_ + ".enable"));
+  }
+}
+
+JSONObject AgentBase::Freeze() {
+  dispatcher_->RemoveDomain(domain_);
+
+  JSONObject agent_state(new base::DictionaryValue());
+  agent_state->SetKey(kEnabled, Value(enabled_));
+  return agent_state;
+}
+
+bool AgentBase::EnsureEnabled(Command* command) {
+  if (!enabled_) {
+    command->SendErrorResponse(Command::kInvalidRequest,
+                               domain_ + " not enabled");
+  }
+  return enabled_;
+}
+
+bool AgentBase::DoEnable(Command* command) {
+  if (enabled_) {
+    command->SendErrorResponse(Command::kInvalidRequest,
+                               domain_ + " already enabled");
+    return false;
+  }
+  enabled_ = script_file_.empty() || dispatcher_->RunScriptFile(script_file_);
+  if (!enabled_) {
+    DLOG(ERROR) << "Failed to load " << script_file_;
+    command->SendErrorResponse(Command::kInternalError,
+                               domain_ + " agent failed to initialize");
+    return false;
+  }
+  return true;
+}
+
+bool AgentBase::DoDisable(Command* command) {
+  if (!EnsureEnabled(command)) return false;
+  // TODO: Unload the script file
+  enabled_ = false;
+  return true;
+}
+
+}  // namespace backend
+}  // namespace debug
+}  // namespace cobalt
diff --git a/src/cobalt/debug/backend/agent_base.h b/src/cobalt/debug/backend/agent_base.h
new file mode 100644
index 0000000..9ce0867
--- /dev/null
+++ b/src/cobalt/debug/backend/agent_base.h
@@ -0,0 +1,80 @@
+// 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.
+#ifndef COBALT_DEBUG_BACKEND_AGENT_BASE_H_
+#define COBALT_DEBUG_BACKEND_AGENT_BASE_H_
+
+#include <string>
+
+#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/debug_dispatcher.h"
+#include "cobalt/debug/command.h"
+#include "cobalt/debug/json_object.h"
+
+namespace cobalt {
+namespace debug {
+namespace backend {
+
+// Utility base class with common logic for agents. It is not required that
+// agents use this base class if they have other behaviour.
+class AgentBase {
+ public:
+  // Constructor for agents that do not have hybrid JS implementation.
+  AgentBase(const std::string& domain, DebugDispatcher* dispatcher)
+      : AgentBase(domain, "", dispatcher) {}
+
+  // Constructor for agents that have a script file that is their hybrid JS
+  // implementation.
+  AgentBase(const std::string& domain, const std::string& script_file,
+            DebugDispatcher* dispatcher);
+
+  virtual ~AgentBase() {}
+
+  virtual void Thaw(JSONObject agent_state);
+  virtual JSONObject Freeze();
+
+ protected:
+  // Commands added to the CommandMap. These just call their respective
+  // DoCommand() methods and send a success response if appropriate.
+  void Enable(Command command) {
+    if (DoEnable(&command)) command.SendResponse();
+  }
+  void Disable(Command command) {
+    if (DoDisable(&command)) command.SendResponse();
+  }
+
+  // Default command implementations that may be overridden. Returns false if an
+  // error response has already been sent, or true if successful so far and no
+  // response has been sent yet.
+  virtual bool DoEnable(Command* command);
+  virtual bool DoDisable(Command* command);
+
+  // Returns enabled, and if not enabled sends an error response for |command|.
+  bool EnsureEnabled(Command* command);
+
+  std::string domain_;
+  std::string script_file_;
+  DebugDispatcher* dispatcher_;
+
+  // Map of member functions implementing commands.
+  CommandMap commands_;
+
+  // Whether the domain is enabled.
+  bool enabled_ = false;
+};
+
+}  // namespace backend
+}  // namespace debug
+}  // namespace cobalt
+
+#endif  // COBALT_DEBUG_BACKEND_AGENT_BASE_H_
diff --git a/src/cobalt/debug/backend/command_map.h b/src/cobalt/debug/backend/command_map.h
index 49632a1..919fc36 100644
--- a/src/cobalt/debug/backend/command_map.h
+++ b/src/cobalt/debug/backend/command_map.h
@@ -28,19 +28,17 @@
 namespace debug {
 namespace backend {
 
-// Type of an agent member function that implements a protocol command.
-template <class T>
-using CommandFn = void (T::*)(Command command);
+// Type of a function that implements a protocol command.
+using CommandFn = base::Callback<void(Command command)>;
 
 // A map of method names to agent command functions. Provides a standard
 // implementation of the top-level domain command handler.
-template <class T>
-class CommandMap : public std::map<std::string, CommandFn<T>> {
+class CommandMap : public std::map<std::string, CommandFn> {
  public:
   // If |domain| is specified, then commands for that domain should be mapped
   // using just the the simple method name (i.e. "method" not "Domain.method").
-  explicit CommandMap(T* agent, const std::string& domain = std::string())
-      : agent_(agent), domain_(domain) {}
+  explicit CommandMap(const std::string& domain = std::string())
+      : domain_(domain) {}
 
   // Calls the mapped method implementation.
   // Passes ownership of the command to the mapped method, otherwise returns
@@ -53,18 +51,17 @@
             : command.GetMethod();
     auto iter = this->find(method);
     if (iter == this->end()) return base::make_optional(std::move(command));
-    auto command_fn = iter->second;
-    (agent_->*command_fn)(std::move(command));
+    auto command_impl = iter->second;
+    command_impl.Run(std::move(command));
     return base::nullopt;
   }
 
   // Binds |RunCommand| to a callback to be registered with |DebugDispatcher|.
   DebugDispatcher::CommandHandler Bind() {
-    return base::Bind(&CommandMap<T>::RunCommand, base::Unretained(this));
+    return base::Bind(&CommandMap::RunCommand, base::Unretained(this));
   }
 
  private:
-  T* agent_;
   std::string domain_;
 };
 
diff --git a/src/cobalt/debug/backend/console_agent.cc b/src/cobalt/debug/backend/console_agent.cc
index a5ebfed..505e99b 100644
--- a/src/cobalt/debug/backend/console_agent.cc
+++ b/src/cobalt/debug/backend/console_agent.cc
@@ -21,15 +21,9 @@
 namespace debug {
 namespace backend {
 
-namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/tot/Console
-constexpr char kInspectorDomain[] = "Console";
-
-// The "Console" protocol domain is deprecated, but we still use it to forward
-// console messages from our console web API implementation (used only with
-// mozjs) to avoid blurring the line to the "Runtime" domain implementation.
-}  // namespace
+ConsoleAgent::ConsoleAgent(DebugDispatcher* dispatcher, dom::Console* console)
+    : AgentBase("Console", dispatcher),
+      ALLOW_THIS_IN_INITIALIZER_LIST(console_listener_(console, this)) {}
 
 ConsoleAgent::Listener::Listener(dom::Console* console,
                                  ConsoleAgent* console_agent)
@@ -40,37 +34,13 @@
   console_agent_->OnMessageAdded(message, level);
 }
 
-ConsoleAgent::ConsoleAgent(DebugDispatcher* dispatcher, dom::Console* console)
-    : dispatcher_(dispatcher),
-      ALLOW_THIS_IN_INITIALIZER_LIST(console_listener_(console, this)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
-  DCHECK(dispatcher_);
-
-  commands_["disable"] = &ConsoleAgent::Disable;
-  commands_["enable"] = &ConsoleAgent::Enable;
-}
-
-void ConsoleAgent::Thaw(JSONObject agent_state) {
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-}
-
-JSONObject ConsoleAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-  return JSONObject();
-}
-
-void ConsoleAgent::Disable(Command command) { command.SendResponse(); }
-
-void ConsoleAgent::Enable(Command command) { command.SendResponse(); }
-
 void ConsoleAgent::OnMessageAdded(const std::string& text,
                                   dom::Console::Level level) {
   JSONObject params(new base::DictionaryValue());
   params->SetString("message.text", text);
   params->SetString("message.level", dom::Console::GetLevelAsString(level));
   params->SetString("message.source", "console-api");
-  dispatcher_->SendEvent(std::string(kInspectorDomain) + ".messageAdded",
-                         params);
+  dispatcher_->SendEvent(domain_ + ".messageAdded", params);
 }
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/console_agent.h b/src/cobalt/debug/backend/console_agent.h
index e3c9b2b..04a3e90 100644
--- a/src/cobalt/debug/backend/console_agent.h
+++ b/src/cobalt/debug/backend/console_agent.h
@@ -17,48 +17,39 @@
 
 #include <string>
 
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
-#include "cobalt/debug/command.h"
-#include "cobalt/debug/json_object.h"
 #include "cobalt/dom/console.h"
 
 namespace cobalt {
 namespace debug {
 namespace backend {
 
-// The ConsoleAgent forwards messages from our IDL implementation of the
-// 'console' web API. Since V8 obscures our console with its own builtin, this
-// ends up only being used when mozjs is the JS engine.
-class ConsoleAgent {
+// Even though the "Console" protocol domain is deprecated, we still use it to
+// forward console messages from our own console web API implementation to avoid
+// blurring the line to our "Runtime" domain implementation (the modern event
+// for console logging is "Runtime.consoleAPICalled"). This is only
+// relevant when running mozjs since V8 obscures our console with its own
+// builtin implementation.
+//
+// https://chromedevtools.github.io/devtools-protocol/tot/Console
+class ConsoleAgent : public AgentBase {
  public:
   ConsoleAgent(DebugDispatcher* dispatcher, dom::Console* console);
 
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
  private:
   class Listener : public dom::Console::Listener {
    public:
     Listener(dom::Console* console, ConsoleAgent* console_agent);
     void OnMessage(const std::string& message,
                    dom::Console::Level level) override;
-
-   private:
     ConsoleAgent* console_agent_;
   };
 
-  void Enable(Command command);
-  void Disable(Command command);
-
   // Called by |console_listener_| when a new message is output.
   void OnMessageAdded(const std::string& text, dom::Console::Level level);
 
-  DebugDispatcher* dispatcher_;
   Listener console_listener_;
-
-  // Map of member functions implementing commands.
-  CommandMap<ConsoleAgent> commands_;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/content/dom_agent.js b/src/cobalt/debug/backend/content/dom_agent.js
index 88231e6..7c8d70f 100644
--- a/src/cobalt/debug/backend/content/dom_agent.js
+++ b/src/cobalt/debug/backend/content/dom_agent.js
@@ -323,6 +323,24 @@
 let _nodeStore = new Map();
 let _nextNodeId = 1;
 
+// Clear the Elements tree in DevTools. It will call DOM.getDocument in response
+// to re-load the document.
+// https://chromedevtools.github.io/devtools-protocol/tot/DOM#event-documentUpdated
+function _documentUpdated() {
+  debugBackend.sendEvent('DOM.documentUpdated', '{}');
+}
+
+// This script is injected when the 'DOM' domain is enabled. When navigating
+// with DevTools connected, that happens when restoring debugger state for the
+// new WebModule and its associated DebugModule along with its agents. That all
+// happens before the new document is loaded, so immediately clear the old
+// document from the Elements tree in DevTools while loading the new document.
+_documentUpdated();
+
+// Once the document is loaded, make DevTools reload its Elements tree again to
+// pick up the new document.
+document.addEventListener('load', _documentUpdated);
+
 // Namespace for constructors of types defined in the Devtools protocol.
 let devtools = {};
 
diff --git a/src/cobalt/debug/backend/css_agent.cc b/src/cobalt/debug/backend/css_agent.cc
index 270fc57..0902a7b 100644
--- a/src/cobalt/debug/backend/css_agent.cc
+++ b/src/cobalt/debug/backend/css_agent.cc
@@ -21,45 +21,8 @@
 namespace debug {
 namespace backend {
 
-namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/tot/DOM
-constexpr char kInspectorDomain[] = "CSS";
-
-// File to load JavaScript CSS debugging domain implementation from.
-constexpr char kScriptFile[] = "css_agent.js";
-}  // namespace
-
 CSSAgent::CSSAgent(DebugDispatcher* dispatcher)
-    : dispatcher_(dispatcher),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
-  DCHECK(dispatcher_);
-
-  commands_["disable"] = &CSSAgent::Disable;
-  commands_["enable"] = &CSSAgent::Enable;
-}
-
-void CSSAgent::Thaw(JSONObject agent_state) {
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-  script_loaded_ = dispatcher_->RunScriptFile(kScriptFile);
-  DLOG_IF(ERROR, !script_loaded_) << "Failed to load " << kScriptFile;
-}
-
-JSONObject CSSAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-  return JSONObject();
-}
-
-void CSSAgent::Enable(Command command) {
-  if (script_loaded_) {
-    command.SendResponse();
-  } else {
-    command.SendErrorResponse(Command::kInternalError,
-                              "Cannot create CSS inspector.");
-  }
-}
-
-void CSSAgent::Disable(Command command) { command.SendResponse(); }
+    : AgentBase("CSS", "css_agent.js", dispatcher) {}
 
 CSSAgent::CSSStyleRuleSequence CSSAgent::GetMatchingCSSRules(
     const scoped_refptr<dom::Element>& element) {
diff --git a/src/cobalt/debug/backend/css_agent.h b/src/cobalt/debug/backend/css_agent.h
index 6941c5d..30c4324 100644
--- a/src/cobalt/debug/backend/css_agent.h
+++ b/src/cobalt/debug/backend/css_agent.h
@@ -15,9 +15,8 @@
 #define COBALT_DEBUG_BACKEND_CSS_AGENT_H_
 
 #include "cobalt/cssom/css_style_rule.h"
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
-#include "cobalt/debug/command.h"
 #include "cobalt/dom/element.h"
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
@@ -26,34 +25,19 @@
 namespace debug {
 namespace backend {
 
-class CSSAgent : public script::Wrappable {
+// https://chromedevtools.github.io/devtools-protocol/tot/CSS
+class CSSAgent : public AgentBase, public script::Wrappable {
  public:
   typedef script::Sequence<scoped_refptr<cssom::CSSStyleRule>>
       CSSStyleRuleSequence;
 
   explicit CSSAgent(DebugDispatcher* dispatcher);
 
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
   // IDL: Returns a sequence of CSSStyleRules that match the element.
   CSSStyleRuleSequence GetMatchingCSSRules(
       const scoped_refptr<dom::Element>& element);
 
   DEFINE_WRAPPABLE_TYPE(CSSAgent);
-
- private:
-  void Enable(Command command);
-  void Disable(Command command);
-
-  // Helper object to connect to the debug dispatcher, etc.
-  DebugDispatcher* dispatcher_;
-
-  // Map of member functions implementing commands.
-  CommandMap<CSSAgent> commands_;
-
-  // Whether we successfully loaded the agent's JavaScript implementation.
-  bool script_loaded_ = false;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/debug_dispatcher.cc b/src/cobalt/debug/backend/debug_dispatcher.cc
index 4f54097..4178c2f 100644
--- a/src/cobalt/debug/backend/debug_dispatcher.cc
+++ b/src/cobalt/debug/backend/debug_dispatcher.cc
@@ -22,10 +22,6 @@
 #include "base/values.h"
 #include "cobalt/debug/debug_client.h"
 
-namespace {
-void NoOpResponseCallback(const base::Optional<std::string>& response) {}
-}  // namespace
-
 namespace cobalt {
 namespace debug {
 namespace backend {
@@ -107,8 +103,7 @@
   // Inspector will send the "Runtime.executionContextCreated" event for every
   // "Runtime.enable" command rather than just for the first one.
   if (command.GetMethod() == "Runtime.enable") {
-    DispatchCommand(
-        Command("Runtime.disable", "", base::Bind(&NoOpResponseCallback)));
+    DispatchCommand(Command::IgnoreResponse("Runtime.disable"));
   }
 
   DomainRegistry::iterator iter = domain_registry_.find(command.GetDomain());
diff --git a/src/cobalt/debug/backend/dom_agent.cc b/src/cobalt/debug/backend/dom_agent.cc
index 188dcde..e1a8a75 100644
--- a/src/cobalt/debug/backend/dom_agent.cc
+++ b/src/cobalt/debug/backend/dom_agent.cc
@@ -18,45 +18,8 @@
 namespace debug {
 namespace backend {
 
-namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/tot/DOM
-constexpr char kInspectorDomain[] = "DOM";
-
-// File to load JavaScript DOM debugging domain implementation from.
-constexpr char kScriptFile[] = "dom_agent.js";
-}  // namespace
-
 DOMAgent::DOMAgent(DebugDispatcher* dispatcher)
-    : dispatcher_(dispatcher),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
-  DCHECK(dispatcher_);
-
-  commands_["disable"] = &DOMAgent::Disable;
-  commands_["enable"] = &DOMAgent::Enable;
-}
-
-void DOMAgent::Thaw(JSONObject agent_state) {
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-  script_loaded_ = dispatcher_->RunScriptFile(kScriptFile);
-  DLOG_IF(ERROR, !script_loaded_) << "Failed to load " << kScriptFile;
-}
-
-JSONObject DOMAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-  return JSONObject();
-}
-
-void DOMAgent::Enable(Command command) {
-  if (script_loaded_) {
-    command.SendResponse();
-  } else {
-    command.SendErrorResponse(Command::kInternalError,
-                              "Cannot create DOM inspector.");
-  }
-}
-
-void DOMAgent::Disable(Command command) { command.SendResponse(); }
+    : AgentBase("DOM", "dom_agent.js", dispatcher) {}
 
 }  // namespace backend
 }  // namespace debug
diff --git a/src/cobalt/debug/backend/dom_agent.h b/src/cobalt/debug/backend/dom_agent.h
index 0ccd4c9..accbf55 100644
--- a/src/cobalt/debug/backend/dom_agent.h
+++ b/src/cobalt/debug/backend/dom_agent.h
@@ -14,33 +14,17 @@
 #ifndef COBALT_DEBUG_BACKEND_DOM_AGENT_H_
 #define COBALT_DEBUG_BACKEND_DOM_AGENT_H_
 
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
-#include "cobalt/debug/command.h"
-#include "cobalt/debug/json_object.h"
 
 namespace cobalt {
 namespace debug {
 namespace backend {
 
-class DOMAgent {
+// https://chromedevtools.github.io/devtools-protocol/tot/DOM
+class DOMAgent : public AgentBase {
  public:
   explicit DOMAgent(DebugDispatcher* dispatcher);
-
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
- private:
-  void Enable(Command command);
-  void Disable(Command command);
-
-  DebugDispatcher* dispatcher_;
-
-  // Map of member functions implementing commands.
-  CommandMap<DOMAgent> commands_;
-
-  // Whether we successfully loaded the agent's JavaScript implementation.
-  bool script_loaded_ = false;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/log_agent.cc b/src/cobalt/debug/backend/log_agent.cc
index 64f3790..e3fc196 100644
--- a/src/cobalt/debug/backend/log_agent.cc
+++ b/src/cobalt/debug/backend/log_agent.cc
@@ -21,18 +21,11 @@
 namespace backend {
 
 namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/1-3/Log
-constexpr char kInspectorDomain[] = "Log";
-
 // Error levels:
 constexpr char kInfoLevel[] = "info";
 constexpr char kWarningLevel[] = "warning";
 constexpr char kErrorLevel[] = "error";
 
-// State keys
-constexpr char kEnabledState[] = "enabled";
-
 const char* GetLogLevelFromSeverity(int severity) {
   switch (severity) {
     // LOG_VERBOSE is a pseudo-severity, which we never get here.
@@ -49,15 +42,7 @@
 }
 }  // namespace
 
-LogAgent::LogAgent(DebugDispatcher* dispatcher)
-    : dispatcher_(dispatcher),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)),
-      enabled_(false) {
-  DCHECK(dispatcher_);
-
-  commands_["enable"] = &LogAgent::Enable;
-  commands_["disable"] = &LogAgent::Disable;
-
+LogAgent::LogAgent(DebugDispatcher* dispatcher) : AgentBase("Log", dispatcher) {
   // Get log output while still making it available elsewhere.
   log_message_handler_callback_id_ =
       base::LogMessageHandler::GetInstance()->AddCallback(
@@ -69,32 +54,6 @@
       log_message_handler_callback_id_);
 }
 
-void LogAgent::Thaw(JSONObject agent_state) {
-  if (agent_state) {
-    agent_state->GetBoolean(kEnabledState, &enabled_);
-  }
-
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-}
-
-JSONObject LogAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-
-  JSONObject agent_state(new base::DictionaryValue());
-  agent_state->SetBoolean(kEnabledState, enabled_);
-  return agent_state;
-}
-
-void LogAgent::Enable(Command command) {
-  enabled_ = true;
-  command.SendResponse();
-}
-
-void LogAgent::Disable(Command command) {
-  enabled_ = false;
-  command.SendResponse();
-}
-
 bool LogAgent::OnLogMessage(int severity, const char* file, int line,
                             size_t message_start, const std::string& str) {
   SB_UNREFERENCED_PARAMETER(file);
@@ -109,8 +68,7 @@
     JSONObject params(new base::DictionaryValue());
     params->SetString("entry.text", str);
     params->SetString("entry.level", GetLogLevelFromSeverity(severity));
-    dispatcher_->SendEvent(std::string(kInspectorDomain) + ".browserEntryAdded",
-                           params);
+    dispatcher_->SendEvent(domain_ + ".browserEntryAdded", params);
   }
 
   // Don't suppress the log message.
diff --git a/src/cobalt/debug/backend/log_agent.h b/src/cobalt/debug/backend/log_agent.h
index da8ecab..97e6fe0 100644
--- a/src/cobalt/debug/backend/log_agent.h
+++ b/src/cobalt/debug/backend/log_agent.h
@@ -16,29 +16,24 @@
 
 #include <string>
 
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
 #include "cobalt/base/log_message_handler.h"
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
-#include "cobalt/debug/command.h"
 
 namespace cobalt {
 namespace debug {
 namespace backend {
 
-class LogAgent {
+// Sends Cobalt C++ log messages (not the JS Console log messages) to a custom
+// "Logging.browserEntryAdded" event to be displayed in the overlay console.
+//
+// https://chromedevtools.github.io/devtools-protocol/tot/Log
+class LogAgent : public AgentBase {
  public:
   explicit LogAgent(DebugDispatcher* dispatcher);
   ~LogAgent();
 
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
  private:
-  void Enable(Command command);
-  void Disable(Command command);
-
   // Called by LogMessageHandler for each log message.
   // May be called from any thread.
   bool OnLogMessage(int severity, const char* file, int line,
@@ -49,15 +44,6 @@
   void OnLogMessageInternal(int severity, const char* file, int line,
                             size_t message_start, const std::string& str);
 
-  // Helper object to connect to the debug dispatcher, etc.
-  DebugDispatcher* dispatcher_;
-
-  // Map of member functions implementing commands.
-  CommandMap<LogAgent> commands_;
-
-  // Indicates whether a client has enabled the "Log" protocol domain.
-  bool enabled_;
-
   // The callback id of our recipient of log messages so we can unregister it.
   base::LogMessageHandler::CallbackId log_message_handler_callback_id_;
 };
diff --git a/src/cobalt/debug/backend/overlay_agent.cc b/src/cobalt/debug/backend/overlay_agent.cc
index f3e944f..2a3a6a2 100644
--- a/src/cobalt/debug/backend/overlay_agent.cc
+++ b/src/cobalt/debug/backend/overlay_agent.cc
@@ -30,13 +30,6 @@
 using render_tree::ColorRGBA;
 
 namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/tot/Overlay
-constexpr char kInspectorDomain[] = "Overlay";
-
-// File to load JavaScript Overlay DevTools domain implementation from.
-constexpr char kScriptFile[] = "overlay_agent.js";
-
 // Returns the float value of a param, or 0.0 if undefined or non-numeric.
 float GetFloatParam(const Value* params, base::StringPiece key) {
   if (!params || !params->is_dict()) return 0.0f;
@@ -78,51 +71,21 @@
 
 OverlayAgent::OverlayAgent(DebugDispatcher* dispatcher,
                            std::unique_ptr<RenderLayer> render_layer)
-    : dispatcher_(dispatcher),
-      render_layer_(std::move(render_layer)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
-  DCHECK(dispatcher_);
+    : AgentBase("Overlay", "overlay_agent.js", dispatcher),
+      render_layer_(std::move(render_layer)) {
   DCHECK(render_layer_);
 
-  commands_["disable"] = &OverlayAgent::Disable;
-  commands_["enable"] = &OverlayAgent::Enable;
-  commands_["highlightNode"] = &OverlayAgent::HighlightNode;
-  commands_["highlightRect"] = &OverlayAgent::HighlightRect;
-  commands_["hideHighlight"] = &OverlayAgent::HideHighlight;
-}
-
-void OverlayAgent::Thaw(JSONObject agent_state) {
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-  script_loaded_ = dispatcher_->RunScriptFile(kScriptFile);
-  DLOG_IF(ERROR, !script_loaded_) << "Failed to load " << kScriptFile;
-}
-
-JSONObject OverlayAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-  return JSONObject();
-}
-
-void OverlayAgent::Enable(Command command) {
-  if (script_loaded_) {
-    enabled_ = true;
-    command.SendResponse();
-  } else {
-    command.SendErrorResponse(Command::kInternalError,
-                              "Cannot create Overlay inspector.");
-  }
-}
-
-void OverlayAgent::Disable(Command command) {
-  enabled_ = false;
-  command.SendResponse();
+  commands_["highlightNode"] =
+      base::Bind(&OverlayAgent::HighlightNode, base::Unretained(this));
+  commands_["highlightRect"] =
+      base::Bind(&OverlayAgent::HighlightRect, base::Unretained(this));
+  commands_["hideHighlight"] =
+      base::Bind(&OverlayAgent::HideHighlight, base::Unretained(this));
 }
 
 void OverlayAgent::HighlightNode(Command command) {
-  if (!enabled_) {
-    command.SendErrorResponse(Command::kInvalidRequest,
-                              "Overlay inspector not enabled.");
-    return;
-  }
+  if (!EnsureEnabled(&command)) return;
+
   // Use the injected JavaScript helper to get the rectangles to highlight for
   // the specified node.
   JSONObject rects_response = dispatcher_->RunScriptCommand(
@@ -147,12 +110,16 @@
 }
 
 void OverlayAgent::HighlightRect(Command command) {
+  if (!EnsureEnabled(&command)) return;
+
   JSONObject params = JSONParse(command.GetParams());
   render_layer_->SetFrontLayer(RenderHighlightRect(params.get()));
   command.SendResponse();
 }
 
 void OverlayAgent::HideHighlight(Command command) {
+  if (!EnsureEnabled(&command)) return;
+
   render_layer_->SetFrontLayer(scoped_refptr<render_tree::Node>());
   command.SendResponse();
 }
diff --git a/src/cobalt/debug/backend/overlay_agent.h b/src/cobalt/debug/backend/overlay_agent.h
index 7f408c1..2bd6057 100644
--- a/src/cobalt/debug/backend/overlay_agent.h
+++ b/src/cobalt/debug/backend/overlay_agent.h
@@ -14,44 +14,28 @@
 #ifndef COBALT_DEBUG_BACKEND_OVERLAY_AGENT_H_
 #define COBALT_DEBUG_BACKEND_OVERLAY_AGENT_H_
 
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
 #include "cobalt/debug/backend/render_layer.h"
 #include "cobalt/debug/command.h"
-#include "cobalt/debug/json_object.h"
 
 namespace cobalt {
 namespace debug {
 namespace backend {
 
-class OverlayAgent {
+// https://chromedevtools.github.io/devtools-protocol/tot/Overlay
+class OverlayAgent : public AgentBase {
  public:
   OverlayAgent(DebugDispatcher* dispatcher,
                std::unique_ptr<RenderLayer> render_layer);
 
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
  private:
-  void Enable(Command command);
-  void Disable(Command command);
-
   void HighlightNode(Command command);
   void HighlightRect(Command command);
   void HideHighlight(Command command);
 
-  DebugDispatcher* dispatcher_;
-
   // Render layer owned by this object.
   std::unique_ptr<RenderLayer> render_layer_;
-
-  // Map of member functions implementing commands.
-  CommandMap<OverlayAgent> commands_;
-
-  // Whether we successfully loaded the agent's JavaScript implementation.
-  bool script_loaded_ = false;
-
-  bool enabled_ = false;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/page_agent.cc b/src/cobalt/debug/backend/page_agent.cc
index c5be165..8c96420 100644
--- a/src/cobalt/debug/backend/page_agent.cc
+++ b/src/cobalt/debug/backend/page_agent.cc
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/debug/backend/page_agent.h"
 
+#include <string>
+
 #include "base/bind.h"
 #include "base/values.h"
+#include "cobalt/debug/json_object.h"
 #include "cobalt/dom/document.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/transform_2d.h"
@@ -32,47 +33,28 @@
 namespace debug {
 namespace backend {
 
-namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/tot/Page
-constexpr char kInspectorDomain[] = "Page";
-}  // namespace
-
 PageAgent::PageAgent(DebugDispatcher* dispatcher, dom::Window* window,
                      std::unique_ptr<RenderLayer> render_layer,
                      render_tree::ResourceProvider* resource_provider)
-    : window_(window),
+    : AgentBase("Page", dispatcher),
+      window_(window),
       render_layer_(std::move(render_layer)),
-      resource_provider_(resource_provider),
-      dispatcher_(dispatcher),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
-  DCHECK(dispatcher_);
+      resource_provider_(resource_provider) {
   DCHECK(window_);
   DCHECK(window_->document());
   DCHECK(render_layer_);
   DCHECK(resource_provider_);
 
-  commands_["disable"] = &PageAgent::Disable;
-  commands_["enable"] = &PageAgent::Enable;
-  commands_["reload"] = &PageAgent::Reload;
-  commands_["getResourceTree"] = &PageAgent::GetResourceTree;
-  commands_["setOverlayMessage"] = &PageAgent::SetOverlayMessage;
+  commands_["reload"] = base::Bind(&PageAgent::Reload, base::Unretained(this));
+  commands_["getResourceTree"] =
+      base::Bind(&PageAgent::GetResourceTree, base::Unretained(this));
+  commands_["setOverlayMessage"] =
+      base::Bind(&PageAgent::SetOverlayMessage, base::Unretained(this));
 }
 
-void PageAgent::Thaw(JSONObject agent_state) {
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-}
-
-JSONObject PageAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-  return JSONObject();
-}
-
-void PageAgent::Disable(Command command) { command.SendResponse(); }
-
-void PageAgent::Enable(Command command) { command.SendResponse(); }
-
 void PageAgent::Reload(Command command) {
+  if (!EnsureEnabled(&command)) return;
+
   // We don't care about the 'ignoreCache' parameter since navigating creates a
   // new WebModule with a new cache (i.e. cache is always cleared on navigate).
   window_->location()->Reload();
@@ -80,6 +62,8 @@
 }
 
 void PageAgent::GetResourceTree(Command command) {
+  if (!EnsureEnabled(&command)) return;
+
   JSONObject response(new base::DictionaryValue());
   JSONObject frame(new base::DictionaryValue());
   frame->SetString("id", "Cobalt");
@@ -94,6 +78,8 @@
 }
 
 void PageAgent::SetOverlayMessage(Command command) {
+  if (!EnsureEnabled(&command)) return;
+
   std::string message;
   JSONObject params = JSONParse(command.GetParams());
   bool got_message = false;
diff --git a/src/cobalt/debug/backend/page_agent.h b/src/cobalt/debug/backend/page_agent.h
index a67d631..6fea51c 100644
--- a/src/cobalt/debug/backend/page_agent.h
+++ b/src/cobalt/debug/backend/page_agent.h
@@ -16,34 +16,26 @@
 #define COBALT_DEBUG_BACKEND_PAGE_AGENT_H_
 
 #include <memory>
-#include <string>
 
-#include "base/memory/weak_ptr.h"
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
 #include "cobalt/debug/backend/render_layer.h"
 #include "cobalt/debug/command.h"
-#include "cobalt/debug/json_object.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/render_tree/resource_provider.h"
-#include "cobalt/script/script_debugger.h"
 
 namespace cobalt {
 namespace debug {
 namespace backend {
 
-class PageAgent {
+// https://chromedevtools.github.io/devtools-protocol/tot/Page
+class PageAgent : public AgentBase {
  public:
   PageAgent(DebugDispatcher* dispatcher, dom::Window* window,
             std::unique_ptr<RenderLayer> render_layer,
             render_tree::ResourceProvider* resource_provider);
 
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
  private:
-  void Enable(Command command);
-  void Disable(Command command);
   void Reload(Command command);
   void GetResourceTree(Command command);
   void SetOverlayMessage(Command command);
@@ -51,11 +43,6 @@
   dom::Window* window_;
   std::unique_ptr<RenderLayer> render_layer_;
   render_tree::ResourceProvider* resource_provider_;
-
-  DebugDispatcher* dispatcher_;
-
-  // Map of member functions implementing commands.
-  CommandMap<PageAgent> commands_;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/runtime_agent.cc b/src/cobalt/debug/backend/runtime_agent.cc
index cd11287..84f8c1a 100644
--- a/src/cobalt/debug/backend/runtime_agent.cc
+++ b/src/cobalt/debug/backend/runtime_agent.cc
@@ -22,46 +22,14 @@
 namespace debug {
 namespace backend {
 
-namespace {
-// Definitions from the set specified here:
-// https://chromedevtools.github.io/devtools-protocol/tot/Runtime
-constexpr char kInspectorDomain[] = "Runtime";
-
-// File to load JavaScript runtime implementation from.
-constexpr char kScriptFile[] = "runtime_agent.js";
-}  // namespace
-
 RuntimeAgent::RuntimeAgent(DebugDispatcher* dispatcher, dom::Window* window)
-    : dispatcher_(dispatcher),
-      window_(window),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
-  DCHECK(dispatcher_);
-  if (!dispatcher_->RunScriptFile(kScriptFile)) {
-    DLOG(WARNING) << "Cannot execute Runtime initialization script.";
-  }
-
-  commands_["enable"] = &RuntimeAgent::Enable;
-  commands_["disable"] = &RuntimeAgent::Disable;
-  commands_["compileScript"] = &RuntimeAgent::CompileScript;
+    : AgentBase("Runtime", "runtime_agent.js", dispatcher), window_(window) {
+  commands_["compileScript"] =
+      base::Bind(&RuntimeAgent::CompileScript, base::Unretained(this));
 }
 
-void RuntimeAgent::Thaw(JSONObject agent_state) {
-  dispatcher_->AddDomain(kInspectorDomain, commands_.Bind());
-  script_loaded_ = dispatcher_->RunScriptFile(kScriptFile);
-  DLOG_IF(ERROR, !script_loaded_) << "Failed to load " << kScriptFile;
-}
-
-JSONObject RuntimeAgent::Freeze() {
-  dispatcher_->RemoveDomain(kInspectorDomain);
-  return JSONObject();
-}
-
-void RuntimeAgent::Enable(Command command) {
-  if (!script_loaded_) {
-    command.SendErrorResponse(Command::kInternalError,
-                              "Cannot create Runtime inspector.");
-    return;
-  }
+bool RuntimeAgent::DoEnable(Command* command) {
+  if (!AgentBase::DoEnable(command)) return false;
 
   // Send an executionContextCreated event.
   // https://chromedevtools.github.io/devtools-protocol/1-3/Runtime#event-executionContextCreated
@@ -70,15 +38,13 @@
   params->SetString("context.origin", window_->location()->origin());
   params->SetString("context.name", "Cobalt");
   params->SetBoolean("context.auxData.isDefault", true);
-  dispatcher_->SendEvent(
-      std::string(kInspectorDomain) + ".executionContextCreated", params);
-
-  command.SendResponse();
+  dispatcher_->SendEvent(domain_ + ".executionContextCreated", params);
+  return true;
 }
 
-void RuntimeAgent::Disable(Command command) { command.SendResponse(); }
-
 void RuntimeAgent::CompileScript(Command command) {
+  if (!EnsureEnabled(&command)) return;
+
   // TODO: Parse the JS without eval-ing it... This is to support:
   // a) Multi-line input from the devtools console
   // b) https://developers.google.com/web/tools/chrome-devtools/snippets
diff --git a/src/cobalt/debug/backend/runtime_agent.h b/src/cobalt/debug/backend/runtime_agent.h
index 777b74c..1e5b5be 100644
--- a/src/cobalt/debug/backend/runtime_agent.h
+++ b/src/cobalt/debug/backend/runtime_agent.h
@@ -15,7 +15,7 @@
 #ifndef COBALT_DEBUG_BACKEND_RUNTIME_AGENT_H_
 #define COBALT_DEBUG_BACKEND_RUNTIME_AGENT_H_
 
-#include "cobalt/debug/backend/command_map.h"
+#include "cobalt/debug/backend/agent_base.h"
 #include "cobalt/debug/backend/debug_dispatcher.h"
 #include "cobalt/debug/command.h"
 #include "cobalt/debug/json_object.h"
@@ -29,26 +29,18 @@
 // just enough to support console input. When using the V8 JavaScript engine,
 // this class is not needed since the V8 inspector implements the Runtime domain
 // for us.
-class RuntimeAgent {
+//
+// https://chromedevtools.github.io/devtools-protocol/tot/Runtime
+class RuntimeAgent : public AgentBase {
  public:
   RuntimeAgent(DebugDispatcher* dispatcher, dom::Window* window);
 
-  void Thaw(JSONObject agent_state);
-  JSONObject Freeze();
-
  private:
   void CompileScript(Command command);
-  void Disable(Command command);
-  void Enable(Command command);
 
-  DebugDispatcher* dispatcher_;
+  bool DoEnable(Command* command) override;
+
   dom::Window* window_;
-
-  // Map of member functions implementing commands.
-  CommandMap<RuntimeAgent> commands_;
-
-  // Whether we successfully loaded the agent's JavaScript implementation.
-  bool script_loaded_ = false;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/backend/script_debugger_agent.h b/src/cobalt/debug/backend/script_debugger_agent.h
index a406209..bf78518 100644
--- a/src/cobalt/debug/backend/script_debugger_agent.h
+++ b/src/cobalt/debug/backend/script_debugger_agent.h
@@ -31,6 +31,9 @@
 namespace backend {
 
 // Routes protocol messages to the V8 inspector through the V8cScriptDebugger.
+//
+// Since V8cScriptDebugger keeps track of its own enabled state for the domains
+// it implements, the ScriptDebuggerAgent doesn't use AgentBase.
 class ScriptDebuggerAgent {
  public:
   ScriptDebuggerAgent(DebugDispatcher* dispatcher,
diff --git a/src/cobalt/debug/backend/tracing_agent.cc b/src/cobalt/debug/backend/tracing_agent.cc
index 15af1c5..666fb7f 100644
--- a/src/cobalt/debug/backend/tracing_agent.cc
+++ b/src/cobalt/debug/backend/tracing_agent.cc
@@ -38,11 +38,11 @@
       script_debugger_(script_debugger),
       tracing_started_(false),
       collected_size_(0),
-      ALLOW_THIS_IN_INITIALIZER_LIST(commands_(this, kInspectorDomain)) {
+      commands_(kInspectorDomain) {
   DCHECK(dispatcher_);
 
-  commands_["end"] = &TracingAgent::End;
-  commands_["start"] = &TracingAgent::Start;
+  commands_["end"] = base::Bind(&TracingAgent::End, base::Unretained(this));
+  commands_["start"] = base::Bind(&TracingAgent::Start, base::Unretained(this));
 }
 
 void TracingAgent::Thaw(JSONObject agent_state) {
diff --git a/src/cobalt/debug/backend/tracing_agent.h b/src/cobalt/debug/backend/tracing_agent.h
index 3a5e4a6..bf114c8 100644
--- a/src/cobalt/debug/backend/tracing_agent.h
+++ b/src/cobalt/debug/backend/tracing_agent.h
@@ -28,6 +28,10 @@
 namespace debug {
 namespace backend {
 
+// There aren't enable/disable commands in the Tracing domain, so the
+// TracingAgent doesn't use AgentBase.
+//
+// https://chromedevtools.github.io/devtools-protocol/tot/Tracing
 class TracingAgent : public script::ScriptDebugger::TraceDelegate  {
  public:
   explicit TracingAgent(DebugDispatcher* dispatcher,
@@ -56,7 +60,7 @@
   JSONList collected_events_;
 
   // Map of member functions implementing commands.
-  CommandMap<TracingAgent> commands_;
+  CommandMap commands_;
 };
 
 }  // namespace backend
diff --git a/src/cobalt/debug/command.h b/src/cobalt/debug/command.h
index f81449f..566d58a 100644
--- a/src/cobalt/debug/command.h
+++ b/src/cobalt/debug/command.h
@@ -103,6 +103,14 @@
     SendResponse(error_response);
   }
 
+  // Constructs a Command with a no-op response callback.
+  static Command IgnoreResponse(const std::string& method,
+                                const std::string& params = "") {
+    return Command(
+        method, params,
+        base::Bind([](const base::Optional<std::string>& response) {}));
+  }
+
  private:
   std::string method_;
   std::string domain_;
diff --git a/src/cobalt/debug/debug.gyp b/src/cobalt/debug/debug.gyp
index 35da044..73c8be8 100644
--- a/src/cobalt/debug/debug.gyp
+++ b/src/cobalt/debug/debug.gyp
@@ -21,6 +21,8 @@
       'target_name': 'debug',
       'type': 'static_library',
       'sources': [
+        'backend/agent_base.cc',
+        'backend/agent_base.h',
         'backend/command_map.h',
         'backend/console_agent.cc',
         'backend/console_agent.h',
diff --git a/src/cobalt/doc/spherical_video.md b/src/cobalt/doc/spherical_video.md
index c38da9f..d7646a3 100644
--- a/src/cobalt/doc/spherical_video.md
+++ b/src/cobalt/doc/spherical_video.md
@@ -11,19 +11,14 @@
 
 ## Enabling spherical video support
 
-Spherical video support requires `map-to-mesh` support, which must be
-explicitly enabled in your Starboard platform's `gyp_configuration.gypi` file.
+Spherical video support requires `map-to-mesh` support, which is enabled by
+default. You can explicitly disable it either through the command line switch
+`--disable_map_to_mesh` or by implementing the CobaltExtensionGraphicsApi
+function `IsMapToMeshEnabled()` to return `false`.
 
-In particular, your platform's `gyp_configuration.gypi` file should contain the
-line,
-
-```
-'enable_map_to_mesh': 1,
-```
-
-When `enable_map_to_mesh` is set to 1, Cobalt will make the `map-to-mesh`
-CSS filter parseable.  The web app can then detect whether the browser,
-Cobalt, supports spherical video by evaluating the following JavaScript:
+When `map-to-mesh` is supported, Cobalt will make the `map-to-mesh` CSS filter
+parseable.  The web app can then detect whether the browser, Cobalt, supports
+spherical video by evaluating the following JavaScript:
 
 ```
 function checkForMapToMeshSupport() {
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index f60ca4e..f0a1a2e 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -347,11 +347,6 @@
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
         '<(DEPTH)/url/url.gyp:url',
       ],
-      'conditions': [
-        ['enable_map_to_mesh == 1', {
-          'defines' : ['ENABLE_MAP_TO_MESH'],
-        }],
-      ],
       # This target doesn't generate any headers, but it exposes generated
       # header files (for idl dictionaries) through this module's public header
       # files. So mark this target as a hard dependency to ensure that any
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 29a0a2f..f0b181f 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -44,6 +44,7 @@
         'font_cache_test.cc',
         'html_element_factory_test.cc',
         'html_element_test.cc',
+        'intersection_observer_test.cc',
         'keyboard_event_test.cc',
         'local_storage_database_test.cc',
         'location_test.cc',
diff --git a/src/cobalt/dom/element_intersection_observer_module.cc b/src/cobalt/dom/element_intersection_observer_module.cc
index 61c8c08..b4b3950 100644
--- a/src/cobalt/dom/element_intersection_observer_module.cc
+++ b/src/cobalt/dom/element_intersection_observer_module.cc
@@ -98,6 +98,13 @@
   AddLayoutTargetForObserver(observer);
 
   InvalidateLayoutBoxesForElement();
+  // We record a mutation when an intersection observer target is added, in
+  // order to mark the layout as dirty and make sure that the intersection
+  // observer creates an entry for any new intersection.
+  Document* node_document = element_->node_document();
+  if (node_document != nullptr) {
+    node_document->RecordMutation();
+  }
 }
 
 void ElementIntersectionObserverModule::UnregisterIntersectionObserverForTarget(
@@ -199,7 +206,7 @@
     NOTREACHED();
     return;
   }
-  html_element->InvalidateLayoutBoxesOfNodeAndDescendants();
+  html_element->InvalidateLayoutBoxesOfNodeAndAncestors();
 }
 
 }  // namespace dom
diff --git a/src/cobalt/dom/element_intersection_observer_module.h b/src/cobalt/dom/element_intersection_observer_module.h
index 1484312..57b6fae 100644
--- a/src/cobalt/dom/element_intersection_observer_module.h
+++ b/src/cobalt/dom/element_intersection_observer_module.h
@@ -60,9 +60,12 @@
   void TraceMembers(script::Tracer* tracer) override;
 
  private:
-  void InvalidateLayoutBoxesForElement();
   void AddLayoutTargetForObserver(IntersectionObserver* observer);
   void RemoveLayoutTargetForObserver(IntersectionObserver* observer);
+  // We invalidate the layout boxes when an intersection observer root/target
+  // is added/removed, so that the boxes associated with the roots and targets
+  // get updated accordingly during the box generation phase.
+  void InvalidateLayoutBoxesForElement();
 
   Element* element_;
   IntersectionObserverVector root_registered_intersection_observers_;
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index dc43248..1fa7ece 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -419,6 +419,10 @@
 
 // Algorithm for scrollLeft:
 //   https://www.w3.org/TR/cssom-view-1/#dom-element-scrollleft
+// For RTL, the rightmost content is visible when scrollLeft == 0, and
+// scrollLeft values are <= 0. Chrome does not behave this way currently, but
+// it is the spec that has been adopted.
+//   https://readable-email.org/list/www-style/topic/cssom-view-value-of-scrollleft-in-rtl-situations-is-completely-busted-across-browsers
 float HTMLElement::scroll_left() {
   // This is only partially implemented and will only work for elements with
   // UI navigation containers.
@@ -2059,9 +2063,15 @@
   }
 
   if (ui_nav_item_type) {
+    ui_navigation::NativeItemDir ui_nav_item_dir;
+    ui_nav_item_dir.is_left_to_right =
+        directionality() == kLeftToRightDirectionality;
+    ui_nav_item_dir.is_top_to_bottom = true;
+
     if (ui_nav_item_) {
       if (ui_nav_item_->GetType() == *ui_nav_item_type) {
         // Keep using the existing navigation item.
+        ui_nav_item_->SetDir(ui_nav_item_dir);
         return;
       }
       // The current navigation item isn't of the correct type. Disable it so
@@ -2086,6 +2096,7 @@
             base::Unretained(base::MessageLoop::current()->task_runner().get()),
             FROM_HERE,
             base::Bind(&HTMLElement::OnUiNavScroll, base::AsWeakPtr(this))));
+    ui_nav_item_->SetDir(ui_nav_item_dir);
   } else if (ui_nav_item_) {
     // This navigation item is no longer relevant.
     ui_nav_item_->SetEnabled(false);
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index 2bc1ac2..2dd4dc1 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -30,8 +30,8 @@
 #include "cobalt/dom/html_body_element.h"
 #include "cobalt/dom/html_div_element.h"
 #include "cobalt/dom/html_element_context.h"
-#include "cobalt/dom/layout_boxes.h"
 #include "cobalt/dom/named_node_map.h"
+#include "cobalt/dom/testing/mock_layout_boxes.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/dom/window.h"
@@ -41,6 +41,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using cobalt::cssom::ViewportSize;
+using cobalt::dom::testing::MockLayoutBoxes;
 using testing::_;
 using testing::Return;
 
@@ -74,44 +75,6 @@
     "a",   "body", "br",   "div", "head", "h1",    "html",
     "img", "link", "meta", "p",   "span", "style", "title"};
 
-class MockLayoutBoxes : public LayoutBoxes {
- public:
-  MOCK_CONST_METHOD0(type, Type());
-  MOCK_CONST_METHOD0(GetClientRects, scoped_refptr<DOMRectList>());
-
-  MOCK_CONST_METHOD0(IsInline, bool());
-
-  MOCK_CONST_METHOD0(GetBorderEdgeLeft, float());
-  MOCK_CONST_METHOD0(GetBorderEdgeTop, float());
-  MOCK_CONST_METHOD0(GetBorderEdgeWidth, float());
-  MOCK_CONST_METHOD0(GetBorderEdgeHeight, float());
-  MOCK_CONST_METHOD0(GetBorderEdgeOffsetFromContainingBlock, math::Vector2dF());
-
-  MOCK_CONST_METHOD0(GetBorderLeftWidth, float());
-  MOCK_CONST_METHOD0(GetBorderTopWidth, float());
-
-  MOCK_CONST_METHOD0(GetMarginEdgeWidth, float());
-  MOCK_CONST_METHOD0(GetMarginEdgeHeight, float());
-
-  MOCK_CONST_METHOD0(GetPaddingEdgeOffset, math::Vector2dF());
-  MOCK_CONST_METHOD0(GetPaddingEdgeWidth, float());
-  MOCK_CONST_METHOD0(GetPaddingEdgeHeight, float());
-  MOCK_CONST_METHOD0(GetPaddingEdgeOffsetFromContainingBlock,
-                     math::Vector2dF());
-
-  MOCK_CONST_METHOD0(GetContentEdgeOffset, math::Vector2dF());
-  MOCK_CONST_METHOD0(GetContentEdgeWidth, float());
-  MOCK_CONST_METHOD0(GetContentEdgeHeight, float());
-  MOCK_CONST_METHOD0(GetContentEdgeOffsetFromContainingBlock,
-                     math::Vector2dF());
-
-  MOCK_CONST_METHOD1(GetScrollArea, math::RectF(dom::Directionality));
-
-  MOCK_METHOD0(InvalidateSizes, void());
-  MOCK_METHOD0(InvalidateCrossReferences, void());
-  MOCK_METHOD0(InvalidateRenderTreeNodes, void());
-};
-
 // Takes the fist child of the given element repeatedly to the given depth.
 scoped_refptr<HTMLElement> GetFirstChildAtDepth(
     const scoped_refptr<HTMLElement>& element, int depth) {
diff --git a/src/cobalt/dom/html_html_element.cc b/src/cobalt/dom/html_html_element.cc
index aff89d9..4a5472d 100644
--- a/src/cobalt/dom/html_html_element.cc
+++ b/src/cobalt/dom/html_html_element.cc
@@ -36,7 +36,9 @@
   // root element itself.
   // https://www.w3.org/TR/css-writing-modes-3/#principal-flow
   if (node_document() && node_document()->body()) {
-    return node_document()->body()->GetUsedDirState();
+    if (node_document()->body()->dir_state() != kDirNotDefined) {
+      return node_document()->body()->GetUsedDirState();
+    }
   }
   return HTMLElement::GetUsedDirState();
 }
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 44340f2..e86dabc 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -1630,7 +1630,8 @@
 bool HTMLMediaElement::PreferDecodeToTexture() {
   TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PreferDecodeToTexture()");
 
-#if defined(ENABLE_MAP_TO_MESH)
+  if (!node_document()->window()->enable_map_to_mesh()) return false;
+
   cssom::PropertyValue* filter = NULL;
   if (node_document()->UpdateComputedStyleOnElementAndAncestor(this) &&
       computed_style()) {
@@ -1654,10 +1655,6 @@
       cssom::MapToMeshFunction::ExtractFromFilterList(filter);
 
   return map_to_mesh_filter;
-#else   // defined(ENABLE_MAP_TO_MESH)
-  // If map-to-mesh is disabled, we never prefer decode-to-texture.
-  return false;
-#endif  // defined(ENABLE_MAP_TO_MESH)
 }
 
 namespace {
diff --git a/src/cobalt/dom/intersection_observer.cc b/src/cobalt/dom/intersection_observer.cc
index 6fce9cd..a500813 100644
--- a/src/cobalt/dom/intersection_observer.cc
+++ b/src/cobalt/dom/intersection_observer.cc
@@ -20,7 +20,6 @@
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/base/source_location.h"
-#include "cobalt/cssom/css_parser.h"
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/element.h"
@@ -61,22 +60,66 @@
   IntersectionObserver::IntersectionObserverCallbackArg::Reference callback_;
 };
 
+// Implement the CallbackInternal interface for a native callback.
+class NativeCallback : public IntersectionObserver::CallbackInternal {
+ public:
+  explicit NativeCallback(
+      const IntersectionObserver::NativeIntersectionObserverCallback& callback)
+      : callback_(callback) {}
+  bool RunCallback(
+      const IntersectionObserver::IntersectionObserverEntrySequence&
+          intersections,
+      const scoped_refptr<IntersectionObserver>& observer) override {
+    callback_.Run(intersections, observer);
+    return true;
+  }
+
+ private:
+  IntersectionObserver::NativeIntersectionObserverCallback callback_;
+};
+
 }  // namespace
 
 IntersectionObserver::IntersectionObserver(
+    const scoped_refptr<Document>& document, cssom::CSSParser* css_parser,
+    const NativeIntersectionObserverCallback& native_callback,
+    script::ExceptionState* exception_state)
+    : IntersectionObserver(document, css_parser, native_callback,
+                           IntersectionObserverInit(), exception_state) {}
+
+IntersectionObserver::IntersectionObserver(
+    const scoped_refptr<Document>& document, cssom::CSSParser* css_parser,
+    const NativeIntersectionObserverCallback& native_callback,
+    const IntersectionObserverInit& options,
+    script::ExceptionState* exception_state) {
+  InitNativeCallback(native_callback);
+  InitIntersectionObserverInternal(document, css_parser, options,
+                                   exception_state);
+}
+
+IntersectionObserver::IntersectionObserver(
     script::EnvironmentSettings* settings,
     const IntersectionObserverCallbackArg& callback,
-    script::ExceptionState* exception_state) {
-  InitIntersectionObserverInternal(settings, callback,
-                                   IntersectionObserverInit(), exception_state);
-}
+    script::ExceptionState* exception_state)
+    : IntersectionObserver(settings, callback, IntersectionObserverInit(),
+                           exception_state) {}
 
 IntersectionObserver::IntersectionObserver(
     script::EnvironmentSettings* settings,
     const IntersectionObserverCallbackArg& callback,
     const IntersectionObserverInit& options,
     script::ExceptionState* exception_state) {
-  InitIntersectionObserverInternal(settings, callback, options,
+  InitScriptCallback(callback);
+  const scoped_refptr<Document>& document =
+      base::polymorphic_downcast<dom::DOMSettings*>(settings)
+          ->window()
+          ->document();
+  cssom::CSSParser* css_parser =
+      base::polymorphic_downcast<dom::DOMSettings*>(settings)
+          ->window()
+          ->html_element_context()
+          ->css_parser();
+  InitIntersectionObserverInternal(document, css_parser, options,
                                    exception_state);
 }
 
@@ -186,22 +229,33 @@
   tracer->TraceItems(queued_entries_);
 }
 
+void IntersectionObserver::InitNativeCallback(
+    const NativeIntersectionObserverCallback& native_callback) {
+  callback_.reset(new NativeCallback(native_callback));
+}
+
+void IntersectionObserver::InitScriptCallback(
+    const IntersectionObserverCallbackArg& callback) {
+  callback_.reset(new ScriptCallback(callback, this));
+}
+
 void IntersectionObserver::InitIntersectionObserverInternal(
-    script::EnvironmentSettings* settings,
-    const IntersectionObserverCallbackArg& callback,
+    const scoped_refptr<Document>& document, cssom::CSSParser* css_parser,
     const IntersectionObserverInit& options,
     script::ExceptionState* exception_state) {
   // https://www.w3.org/TR/intersection-observer/#intersection-observer-interface
+  // Set this.root to options.root. If not provided, set to the implicit root.
+  if (options.root()) {
+    root_ = base::AsWeakPtr(options.root().get());
+  } else {
+    root_ = base::AsWeakPtr(document->document_element().get());
+  }
+
   // Attempt to parse a root margin from options.rootMargin. If a list is
   // returned, set this's internal [[rootMargin]] slot to that. Otherwise, throw
   // a SyntaxError exception.
   // https://www.w3.org/TR/intersection-observer/#parse-a-root-margin
   root_margin_ = options.root_margin();
-  cssom::CSSParser* css_parser =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings)
-          ->window()
-          ->html_element_context()
-          ->css_parser();
   scoped_refptr<cssom::PropertyListValue> property_list_value =
       base::polymorphic_downcast<cssom::PropertyListValue*>(
           css_parser
@@ -247,25 +301,10 @@
     sort(thresholds_.begin(), thresholds_.end());
   }
   // If thresholds is empty, append 0 to thresholds.
-
   if (thresholds_.empty()) {
     thresholds_.push_back(0);
   }
 
-  // Set this's internal [[callback]] slot to callback.
-  callback_.reset(new ScriptCallback(callback, this));
-
-  // Set this.root to options.root.
-  if (options.root()) {
-    root_ = base::AsWeakPtr(options.root().get());
-  } else {
-    root_ =
-        base::AsWeakPtr(base::polymorphic_downcast<dom::DOMSettings*>(settings)
-                            ->window()
-                            ->document()
-                            ->document_element()
-                            .get());
-  }
   root_->RegisterIntersectionObserverRoot(this);
   intersection_observer_task_manager_ =
       root_->owner_document()->intersection_observer_task_manager();
diff --git a/src/cobalt/dom/intersection_observer.h b/src/cobalt/dom/intersection_observer.h
index c1b6ba6..1e2373b 100644
--- a/src/cobalt/dom/intersection_observer.h
+++ b/src/cobalt/dom/intersection_observer.h
@@ -21,6 +21,7 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "cobalt/cssom/css_parser.h"
 #include "cobalt/cssom/property_list_value.h"
 #include "cobalt/dom/intersection_observer_entry.h"
 #include "cobalt/layout/intersection_observer_root.h"
@@ -36,6 +37,7 @@
 namespace cobalt {
 namespace dom {
 
+class Document;
 class Element;
 class IntersectionObserverInit;
 class IntersectionObserverTaskManager;
@@ -56,8 +58,25 @@
   typedef script::ScriptValue<IntersectionObserverCallback>
       IntersectionObserverCallbackArg;
 
+  typedef base::Callback<void(const IntersectionObserverEntrySequence&,
+                              const scoped_refptr<IntersectionObserver>&)>
+      NativeIntersectionObserverCallback;
+
   typedef script::UnionType2<double, script::Sequence<double>> ThresholdType;
 
+  // Not part of the spec. Support creating IntersectionObservers from native
+  // Cobalt code.
+  IntersectionObserver(
+      const scoped_refptr<Document>& document, cssom::CSSParser* css_parser,
+      const NativeIntersectionObserverCallback& native_callback,
+      script::ExceptionState* exception_state);
+
+  IntersectionObserver(
+      const scoped_refptr<Document>& document, cssom::CSSParser* css_parser,
+      const NativeIntersectionObserverCallback& native_callback,
+      const IntersectionObserverInit& options,
+      script::ExceptionState* exception_state);
+
   // Web API: IntersectionObserver
   IntersectionObserver(script::EnvironmentSettings* settings,
                        const IntersectionObserverCallbackArg& callback,
@@ -112,9 +131,11 @@
 
  private:
   IntersectionObserverTaskManager* task_manager();
+  void InitNativeCallback(
+      const NativeIntersectionObserverCallback& native_callback);
+  void InitScriptCallback(const IntersectionObserverCallbackArg& callback);
   void InitIntersectionObserverInternal(
-      script::EnvironmentSettings* settings,
-      const IntersectionObserverCallbackArg& callback,
+      const scoped_refptr<Document>& document, cssom::CSSParser* css_parser,
       const IntersectionObserverInit& options,
       script::ExceptionState* exception_state);
   void TrackObservationTarget(const scoped_refptr<Element>& target);
diff --git a/src/cobalt/dom/intersection_observer_test.cc b/src/cobalt/dom/intersection_observer_test.cc
new file mode 100644
index 0000000..e2b9486
--- /dev/null
+++ b/src/cobalt/dom/intersection_observer_test.cc
@@ -0,0 +1,141 @@
+// 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 "cobalt/cssom/percentage_value.h"
+#include "cobalt/cssom/testing/mock_css_parser.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/dom_stat_tracker.h"
+#include "cobalt/dom/element.h"
+#include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/testing/fake_exception_state.h"
+#include "cobalt/dom/testing/mock_layout_boxes.h"
+#include "cobalt/dom/testing/stub_environment_settings.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using cobalt::cssom::PercentageValue;
+using cobalt::cssom::PropertyListValue;
+using cobalt::dom::testing::MockLayoutBoxes;
+using ::testing::NiceMock;
+using ::testing::_;
+using ::testing::Return;
+
+namespace cobalt {
+namespace dom {
+
+class MockDocumentObserver : public DocumentObserver {
+ public:
+  MOCK_METHOD0(OnLoad, void());
+
+  MOCK_METHOD0(OnMutation, void());
+
+  MOCK_METHOD0(OnFocusChanged, void());
+};
+
+class IntersectionCallbackMock {
+ public:
+  MOCK_METHOD2(
+      NativeIntersectionObserverCallback,
+      void(const IntersectionObserver::IntersectionObserverEntrySequence&,
+           const scoped_refptr<IntersectionObserver>&));
+};
+
+class IntersectionObserverTest : public ::testing::Test {
+ protected:
+  IntersectionObserverTest()
+      : dom_stat_tracker_(new DomStatTracker("IntersectionObserverTest")),
+        html_element_context_(&environment_settings_, NULL, NULL, &css_parser_,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
+                              "", base::kApplicationStateStarted, NULL),
+        document_(new Document(&html_element_context_)) {}
+
+  scoped_refptr<Document> document() { return document_; }
+  scoped_refptr<IntersectionObserver> CreateIntersectionObserver();
+  void CreateDocumentWithTwoChildrenAndMockLayoutBoxes();
+
+ private:
+  std::unique_ptr<DomStatTracker> dom_stat_tracker_;
+  testing::StubEnvironmentSettings environment_settings_;
+  NiceMock<cssom::testing::MockCSSParser> css_parser_;
+  testing::FakeExceptionState exception_state_;
+  HTMLElementContext html_element_context_;
+  scoped_refptr<Document> document_;
+  IntersectionCallbackMock callback_mock_;
+};
+
+scoped_refptr<IntersectionObserver>
+IntersectionObserverTest::CreateIntersectionObserver() {
+  // The intersection observer constructor will use the mock css parser to parse
+  // the root margin property value. Because "ParsePropertyValue()"" returns a
+  // "PropertyValue", which is not a built-in type, we must set a default
+  // action and return type here.
+  std::unique_ptr<PropertyListValue::Builder> builder(
+      new PropertyListValue::Builder());
+  builder->push_back(new PercentageValue(0.0f));
+  scoped_refptr<PropertyListValue> property_list_value =
+      new PropertyListValue(std::move(builder));
+  ON_CALL(css_parser_, ParsePropertyValue(_, _, _))
+      .WillByDefault(Return(property_list_value));
+
+  return new IntersectionObserver(
+      document_, &css_parser_,
+      base::Bind(&IntersectionCallbackMock::NativeIntersectionObserverCallback,
+                 base::Unretained(&callback_mock_)),
+      &exception_state_);
+}
+
+void IntersectionObserverTest::
+    CreateDocumentWithTwoChildrenAndMockLayoutBoxes() {
+  scoped_refptr<HTMLElement> first_child_element_ =
+      document_->CreateElement("div")->AsHTMLElement();
+  document_->AppendChild(first_child_element_);
+
+  scoped_refptr<HTMLElement> second_child_element_ =
+      document_->CreateElement("div")->AsHTMLElement();
+  first_child_element_->AppendChild(second_child_element_);
+
+  std::unique_ptr<MockLayoutBoxes> layout_boxes(new MockLayoutBoxes);
+  document_->document_element()->AsHTMLElement()->set_layout_boxes(
+      std::unique_ptr<LayoutBoxes>(layout_boxes.release()));
+}
+
+TEST_F(IntersectionObserverTest, RegisterIntersectionObserverForTarget) {
+  CreateDocumentWithTwoChildrenAndMockLayoutBoxes();
+
+  NiceMock<MockDocumentObserver> document_observer;
+  document()->AddObserver(&document_observer);
+
+  std::unique_ptr<ElementIntersectionObserverModule>
+      element_intersection_observer_module =
+          std::make_unique<ElementIntersectionObserverModule>(
+              document()->last_element_child());
+
+  // Treat the second child element as an intersection observer target
+  // (the first child element is the same as the document element).
+  // When the observer is registered, it should record a mutation on the
+  // document and invalidate the layout boxes up to the document element.
+  EXPECT_NE(document()->document_element()->AsHTMLElement()->layout_boxes(),
+            nullptr);
+  EXPECT_CALL(document_observer, OnMutation()).Times(1);
+  scoped_refptr<IntersectionObserver> intersection_observer =
+      CreateIntersectionObserver();
+  element_intersection_observer_module->RegisterIntersectionObserverForTarget(
+      intersection_observer);
+  EXPECT_EQ(document()->document_element()->AsHTMLElement()->layout_boxes(),
+            nullptr);
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index c3df595..61ee100 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -51,8 +51,6 @@
 #include "base/compiler_specific.h"
 #include "base/guid.h"
 #include "base/logging.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/dom_settings.h"
@@ -69,39 +67,6 @@
 using media::PIPELINE_OK;
 using media::PipelineStatus;
 
-namespace {
-
-// Parse mime and codecs from content type. It will return "video/mp4" and
-// "avc1.42E01E, mp4a.40.2" for "video/mp4; codecs="avc1.42E01E, mp4a.40.2".
-// Note that this function does minimum validation as the media stack will check
-// the mime type and codecs strictly.
-bool ParseContentType(const std::string& content_type, std::string* mime,
-                      std::string* codecs) {
-  DCHECK(mime);
-  DCHECK(codecs);
-  static const char kCodecs[] = "codecs=";
-
-  // SplitString will also trim the results.
-  std::vector<std::string> tokens = ::base::SplitString(
-      content_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  // The first one has to be mime type with delimiter '/' like 'video/mp4'.
-  if (tokens.size() < 2 || tokens[0].find('/') == tokens[0].npos) {
-    return false;
-  }
-  *mime = tokens[0];
-  for (size_t i = 1; i < tokens.size(); ++i) {
-    if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) {
-      continue;
-    }
-    *codecs = tokens[i].substr(strlen("codecs="));
-    base::TrimString(*codecs, " \"", codecs);
-    break;
-  }
-  return !codecs->empty();
-}
-
-}  // namespace
-
 MediaSource::MediaSource(script::EnvironmentSettings* settings)
     : EventTarget(settings),
       chunk_demuxer_(NULL),
@@ -204,18 +169,9 @@
     return NULL;
   }
 
-  std::string mime;
-  std::string codecs;
-
-  if (!ParseContentType(type, &mime, &codecs)) {
-    DOMException::Raise(DOMException::kNotSupportedErr, exception_state);
-    // Return value should be ignored.
-    return NULL;
-  }
-
   std::string guid = base::GenerateGUID();
   scoped_refptr<SourceBuffer> source_buffer;
-  ChunkDemuxer::Status status = chunk_demuxer_->AddId(guid, mime, codecs);
+  ChunkDemuxer::Status status = chunk_demuxer_->AddId(guid, type);
   switch (status) {
     case ChunkDemuxer::kOk:
       source_buffer =
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index a86bc8f..c94c34c 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -43,7 +43,7 @@
 #include "starboard/configuration.h"
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #define HANDLE_CORE_DUMP
-#include "starboard/ps4/core_dump_handler.h"
+#include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #endif  // defined(OS_STARBOARD)
 
diff --git a/src/cobalt/dom/testing/dom_testing.gyp b/src/cobalt/dom/testing/dom_testing.gyp
index dfb0175..35a11f1 100644
--- a/src/cobalt/dom/testing/dom_testing.gyp
+++ b/src/cobalt/dom/testing/dom_testing.gyp
@@ -24,6 +24,7 @@
         'gtest_workarounds.h',
         'html_collection_testing.h',
         'mock_event_listener.h',
+        'mock_layout_boxes.h',
         'stub_css_parser.cc',
         'stub_css_parser.h',
         'stub_environment_settings.h',
diff --git a/src/cobalt/dom/testing/mock_layout_boxes.h b/src/cobalt/dom/testing/mock_layout_boxes.h
new file mode 100644
index 0000000..2451961
--- /dev/null
+++ b/src/cobalt/dom/testing/mock_layout_boxes.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef COBALT_DOM_TESTING_MOCK_LAYOUT_BOXES_H_
+#define COBALT_DOM_TESTING_MOCK_LAYOUT_BOXES_H_
+
+#include "cobalt/dom/layout_boxes.h"
+
+namespace cobalt {
+namespace dom {
+namespace testing {
+
+class MockLayoutBoxes : public LayoutBoxes {
+ public:
+  MOCK_CONST_METHOD0(type, Type());
+  MOCK_CONST_METHOD0(GetClientRects, scoped_refptr<DOMRectList>());
+
+  MOCK_CONST_METHOD0(IsInline, bool());
+
+  MOCK_CONST_METHOD0(GetBorderEdgeLeft, float());
+  MOCK_CONST_METHOD0(GetBorderEdgeTop, float());
+  MOCK_CONST_METHOD0(GetBorderEdgeWidth, float());
+  MOCK_CONST_METHOD0(GetBorderEdgeHeight, float());
+  MOCK_CONST_METHOD0(GetBorderEdgeOffsetFromContainingBlock, math::Vector2dF());
+
+  MOCK_CONST_METHOD0(GetBorderLeftWidth, float());
+  MOCK_CONST_METHOD0(GetBorderTopWidth, float());
+
+  MOCK_CONST_METHOD0(GetMarginEdgeWidth, float());
+  MOCK_CONST_METHOD0(GetMarginEdgeHeight, float());
+
+  MOCK_CONST_METHOD0(GetPaddingEdgeOffset, math::Vector2dF());
+  MOCK_CONST_METHOD0(GetPaddingEdgeWidth, float());
+  MOCK_CONST_METHOD0(GetPaddingEdgeHeight, float());
+  MOCK_CONST_METHOD0(GetPaddingEdgeOffsetFromContainingBlock,
+                     math::Vector2dF());
+
+  MOCK_CONST_METHOD0(GetContentEdgeOffset, math::Vector2dF());
+  MOCK_CONST_METHOD0(GetContentEdgeWidth, float());
+  MOCK_CONST_METHOD0(GetContentEdgeHeight, float());
+  MOCK_CONST_METHOD0(GetContentEdgeOffsetFromContainingBlock,
+                     math::Vector2dF());
+
+  MOCK_CONST_METHOD1(GetScrollArea, math::RectF(dom::Directionality));
+
+  MOCK_METHOD0(InvalidateSizes, void());
+  MOCK_METHOD0(InvalidateCrossReferences, void());
+  MOCK_METHOD0(InvalidateRenderTreeNodes, void());
+};
+}  // namespace testing
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_TESTING_MOCK_LAYOUT_BOXES_H_
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 524880f..2dc70c4 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -132,9 +132,9 @@
     base::WaitableEvent* synchronous_loader_interrupt,
     bool enable_inline_script_warnings,
     const scoped_refptr<ui_navigation::NavItem>& ui_nav_root,
-    int csp_insecure_allowed_token, int dom_max_element_depth,
-    float video_playback_rate_multiplier, ClockType clock_type,
-    const CacheCallback& splash_screen_cache_callback,
+    bool enable_map_to_mesh, int csp_insecure_allowed_token,
+    int dom_max_element_depth, float video_playback_rate_multiplier,
+    ClockType clock_type, const CacheCallback& splash_screen_cache_callback,
     const scoped_refptr<captions::SystemCaptionSettings>& captions,
     bool log_tts)
     // 'window' object EventTargets require special handling for onerror events,
@@ -202,7 +202,8 @@
       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),
-      ui_nav_root_(ui_nav_root) {
+      ui_nav_root_(ui_nav_root),
+      enable_map_to_mesh_(enable_map_to_mesh) {
 #if !defined(ENABLE_TEST_RUNNER)
   SB_UNREFERENCED_PARAMETER(clock_type);
 #endif
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 27bd732..40d0fbf 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -175,8 +175,8 @@
       base::WaitableEvent* synchronous_loader_interrupt,
       bool enable_inline_script_warnings = false,
       const scoped_refptr<ui_navigation::NavItem>& ui_nav_root = nullptr,
-      int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0,
-      float video_playback_rate_multiplier = 1.f,
+      bool enable_map_to_mesh = true, int csp_insecure_allowed_token = 0,
+      int dom_max_element_depth = 0, float video_playback_rate_multiplier = 1.f,
       ClockType clock_type = kClockTypeSystemTime,
       const CacheCallback& splash_screen_cache_callback = CacheCallback(),
       const scoped_refptr<captions::SystemCaptionSettings>& captions = nullptr,
@@ -402,6 +402,8 @@
     return ui_nav_root_;
   }
 
+  bool enable_map_to_mesh() { return enable_map_to_mesh_; }
+
   DEFINE_WRAPPABLE_TYPE(Window);
 
  private:
@@ -480,6 +482,8 @@
   // items for this window.
   scoped_refptr<ui_navigation::NavItem> ui_nav_root_;
 
+  bool enable_map_to_mesh_;
+
   DISALLOW_COPY_AND_ASSIGN(Window);
 };
 
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
index dea28bc..d33f8ca 100644
--- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -33,7 +33,7 @@
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #define HANDLE_CORE_DUMP
 #include "base/lazy_instance.h"
-#include "starboard/ps4/core_dump_handler.h"
+#include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #endif  // defined(OS_STARBOARD)
 #include "third_party/libxml/src/include/libxml/xmlerror.h"
diff --git a/src/cobalt/extension/extension_test.cc b/src/cobalt/extension/extension_test.cc
index 2615ba2..b49922a 100644
--- a/src/cobalt/extension/extension_test.cc
+++ b/src/cobalt/extension/extension_test.cc
@@ -36,7 +36,8 @@
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
   EXPECT_TRUE(extension_api->version == 1 ||
-              extension_api->version == 2) << "Invalid version";
+              extension_api->version == 2 ||
+              extension_api->version == 3) << "Invalid version";
   EXPECT_TRUE(extension_api->Has != NULL);
   EXPECT_TRUE(extension_api->Open != NULL);
   EXPECT_TRUE(extension_api->Close != NULL);
@@ -60,11 +61,15 @@
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
   EXPECT_TRUE(extension_api->version == 1 ||
-              extension_api->version == 2) << "Invalid version";
+              extension_api->version == 2 ||
+              extension_api->version == 3) << "Invalid version";
   EXPECT_TRUE(extension_api->GetMaximumFrameIntervalInMilliseconds != NULL);
   if (extension_api->version >= 2) {
     EXPECT_TRUE(extension_api->GetMinimumFrameIntervalInMilliseconds != NULL);
   }
+  if (extension_api->version >= 3) {
+    EXPECT_TRUE(extension_api->IsMapToMeshEnabled != NULL);
+  }
 
   float maximum_frame_interval =
       extension_api->GetMaximumFrameIntervalInMilliseconds();
@@ -93,7 +98,8 @@
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
   EXPECT_TRUE(extension_api->version == 1 ||
-              extension_api->version == 2) << "Invalid version";
+              extension_api->version == 2 ||
+              extension_api->version == 3) << "Invalid version";
   EXPECT_TRUE(extension_api->GetCurrentInstallationIndex != NULL);
   EXPECT_TRUE(extension_api->MarkInstallationSuccessful != NULL);
   EXPECT_TRUE(extension_api->RequestRollForwardToInstallation != NULL);
diff --git a/src/cobalt/extension/graphics.h b/src/cobalt/extension/graphics.h
index b0e2bb8..9c993c5 100644
--- a/src/cobalt/extension/graphics.h
+++ b/src/cobalt/extension/graphics.h
@@ -57,6 +57,11 @@
   // better to specify a lower delay. For example, '33' instead of '33.33'
   // for 30 Hz refresh.
   float (*GetMinimumFrameIntervalInMilliseconds)();
+
+  // The fields below this point were added in version 3 or later.
+
+  // Get whether the renderer should support 360 degree video or not.
+  bool (*IsMapToMeshEnabled)();
 } CobaltExtensionGraphicsApi;
 
 #ifdef __cplusplus
diff --git a/src/cobalt/h5vcc/h5vcc_crash_log.cc b/src/cobalt/h5vcc/h5vcc_crash_log.cc
index 820ca2d..79f0e20 100644
--- a/src/cobalt/h5vcc/h5vcc_crash_log.cc
+++ b/src/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -21,9 +21,9 @@
 #include "base/memory/singleton.h"
 #include "base/synchronization/lock.h"
 
-// Unfortunately, the Starboard interface is still experimental and supported
-// only on the PS4 for now.
-#include "starboard/ps4/core_dump_handler.h"
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+#include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
+#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 
 #include "starboard/system.h"
 
diff --git a/src/cobalt/layout/block_container_box.cc b/src/cobalt/layout/block_container_box.cc
index a5fe9e9..76b0046 100644
--- a/src/cobalt/layout/block_container_box.cc
+++ b/src/cobalt/layout/block_container_box.cc
@@ -47,7 +47,8 @@
     const base::Optional<LayoutUnit>& maybe_height) {
   if (IsAbsolutelyPositioned()) {
     UpdateWidthAssumingAbsolutelyPositionedBox(
-        containing_block_width, maybe_left, maybe_right, maybe_width,
+        containing_block_direction, containing_block_width,
+        maybe_left, maybe_right, maybe_width,
         maybe_margin_left, maybe_margin_right, maybe_height);
   } else {
     base::Optional<LayoutUnit> maybe_nulled_width = maybe_width;
@@ -346,6 +347,7 @@
 //     + "padding-right" + "border-right-width" + "margin-right" + "right"
 //     = width of containing block
 void BlockContainerBox::UpdateWidthAssumingAbsolutelyPositionedBox(
+    BaseDirection containing_block_direction,
     LayoutUnit containing_block_width,
     const base::Optional<LayoutUnit>& maybe_left,
     const base::Optional<LayoutUnit>& maybe_right,
@@ -359,11 +361,23 @@
     set_margin_left(maybe_margin_left.value_or(LayoutUnit()));
     set_margin_right(maybe_margin_right.value_or(LayoutUnit()));
 
-    // Then set "left" to the static position...
-    set_left(GetStaticPositionLeft());
+    // Then, if the "direction" property of the element establishing the
+    // static-position containing block is "ltr"...
+    if (containing_block_direction == kLeftToRightBaseDirection) {
+      // ...set "left" to the static position...
+      set_left(GetStaticPositionLeft());
 
-    // ...and apply rule number three (the width is shrink-to-fit).
-    set_width(GetShrinkToFitWidth(containing_block_width, maybe_height));
+      // ...and apply rule number three (the width is shrink-to-fit; solve for
+      // "right").
+      set_width(GetShrinkToFitWidth(containing_block_width, maybe_height));
+    } else {
+      // ...otherwise, set "right" to the static position...
+      // ...and apply rule number one (the width is shrink-to-fit; solve for
+      // "left").
+      set_width(GetShrinkToFitWidth(containing_block_width, maybe_height));
+      set_left(containing_block_width - GetStaticPositionRight() -
+               GetMarginBoxWidth());
+    }
     return;
   }
 
@@ -380,10 +394,17 @@
       // under the extra constraint that the two margins get equal values...
       LayoutUnit horizontal_margin = horizontal_margin_sum / 2;
       if (horizontal_margin < LayoutUnit()) {
-        // ...unless this would make them negative, in which case set
-        // "margin-left" to zero and solve for "margin-right".
-        set_margin_left(LayoutUnit());
-        set_margin_right(horizontal_margin_sum);
+        // ...unless this would make them negative, in which case when direction
+        // of the containing block is "ltr" ("rtl"), set "margin-left"
+        // ("margin-right") to zero and solve for "margin-right"
+        // ("margin-left").
+        if (containing_block_direction == kLeftToRightBaseDirection) {
+          set_margin_left(LayoutUnit());
+          set_margin_right(horizontal_margin_sum);
+        } else {
+          set_margin_left(horizontal_margin_sum);
+          set_margin_right(LayoutUnit());
+        }
       } else {
         set_margin_left(horizontal_margin);
         set_margin_right(horizontal_margin);
@@ -399,9 +420,14 @@
       set_margin_left(*maybe_margin_left);
       set_margin_right(horizontal_margin_sum - *maybe_margin_left);
     } else {
-      // If the values are over-constrained, ignore the value for "right".
+      // If the values are over-constrained, ignore the value for "left" (in
+      // case the "direction" property of the containing block is "rtl") or
+      // "right" (in case "direction" is "ltr") and solve for that value.
       set_margin_left(*maybe_margin_left);
       set_margin_right(*maybe_margin_right);
+      if (containing_block_direction == kRightToLeftBaseDirection) {
+        set_left(containing_block_width - GetMarginBoxWidth() - *maybe_right);
+      }
     }
     return;
   }
@@ -424,8 +450,16 @@
   // 2. "left" and "right" are "auto" and "width" is not "auto"...
   if (!maybe_left && !maybe_right && maybe_width) {
     set_width(*maybe_width);
-    // ...then set "left" to the static position.
-    set_left(GetStaticPositionLeft());
+    // ...if the "direction" property of the element establishing the
+    // static-position containing block is "ltr" set "left" to the static
+    // position, otherwise set "right" to the static position. Then solve for
+    // "left" (if "direction" is "rtl") or "right" (if "direction" is "ltr").
+    if (containing_block_direction == kLeftToRightBaseDirection) {
+      set_left(GetStaticPositionLeft());
+    } else {
+      set_left(containing_block_width - GetStaticPositionRight() -
+               GetMarginBoxWidth());
+    }
     DCHECK_EQ(left(), left());  // Check for NaN.
     return;
   }
diff --git a/src/cobalt/layout/block_container_box.h b/src/cobalt/layout/block_container_box.h
index 3334991..3f4572b 100644
--- a/src/cobalt/layout/block_container_box.h
+++ b/src/cobalt/layout/block_container_box.h
@@ -118,6 +118,7 @@
       const base::Optional<LayoutUnit>& maybe_height);
 
   void UpdateWidthAssumingAbsolutelyPositionedBox(
+      BaseDirection containing_block_direction,
       LayoutUnit containing_block_width,
       const base::Optional<LayoutUnit>& maybe_left,
       const base::Optional<LayoutUnit>& maybe_right,
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index f881901..5e15b02 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -116,9 +116,10 @@
 #ifdef _DEBUG
   margin_box_offset_from_containing_block_.SetVector(LayoutUnit(),
                                                      LayoutUnit());
-  static_position_offset_from_parent_.SetVector(LayoutUnit(), LayoutUnit());
-  static_position_offset_from_containing_block_to_parent_.SetVector(
-      LayoutUnit(), LayoutUnit());
+  static_position_offset_from_parent_.SetInsets(
+      LayoutUnit(), LayoutUnit(), LayoutUnit(), LayoutUnit());
+  static_position_offset_from_containing_block_to_parent_.SetInsets(
+      LayoutUnit(), LayoutUnit(), LayoutUnit(), LayoutUnit());
   margin_insets_.SetInsets(LayoutUnit(), LayoutUnit(), LayoutUnit(),
                            LayoutUnit());
   border_insets_.SetInsets(LayoutUnit(), LayoutUnit(), LayoutUnit(),
@@ -207,6 +208,25 @@
              : Vector2dLayoutUnit();
 }
 
+InsetsLayoutUnit Box::GetContainingBlockInsetFromItsContentBox(
+    const ContainerBox* containing_block) const {
+  // NOTE: Bottom inset is not computed and should not be queried.
+  DCHECK(containing_block == GetContainingBlock());
+  // If the box is absolutely positioned, then its containing block is formed by
+  // the padding box instead of the content box, as described in
+  // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+  // NOTE: While not explicitly stated in the spec, which specifies that the
+  // containing block of a 'fixed' position element must always be the viewport,
+  // all major browsers use the padding box of a transformed ancestor as the
+  // containing block for 'fixed' position elements.
+  return IsAbsolutelyPositioned()
+             ? InsetsLayoutUnit(-containing_block->padding_left(),
+                                -containing_block->padding_top(),
+                                -containing_block->padding_right(),
+                                LayoutUnit())
+             : InsetsLayoutUnit();
+}
+
 RectLayoutUnit Box::GetTransformedBoxFromRoot(
     const RectLayoutUnit& box_from_margin_box) const {
   return GetTransformedBoxFromContainingBlock(nullptr, box_from_margin_box);
@@ -258,8 +278,8 @@
 }
 
 void Box::SetStaticPositionLeftFromParent(LayoutUnit left) {
-  if (left != static_position_offset_from_parent_.x()) {
-    static_position_offset_from_parent_.set_x(left);
+  if (left != static_position_offset_from_parent_.left()) {
+    static_position_offset_from_parent_.set_left(left);
     // Invalidate the size if the static position offset changes, as the
     // positioning for absolutely positioned elements is handled within the size
     // update.
@@ -268,8 +288,8 @@
 }
 
 void Box::SetStaticPositionLeftFromContainingBlockToParent(LayoutUnit left) {
-  if (left != static_position_offset_from_containing_block_to_parent_.x()) {
-    static_position_offset_from_containing_block_to_parent_.set_x(left);
+  if (left != static_position_offset_from_containing_block_to_parent_.left()) {
+    static_position_offset_from_containing_block_to_parent_.set_left(left);
     // Invalidate the size if the static position offset changes, as the
     // positioning for absolutely positioned elements is handled within the size
     // update.
@@ -279,13 +299,40 @@
 
 LayoutUnit Box::GetStaticPositionLeft() const {
   DCHECK(IsAbsolutelyPositioned());
-  return static_position_offset_from_parent_.x() +
-         static_position_offset_from_containing_block_to_parent_.x();
+  return static_position_offset_from_parent_.left() +
+         static_position_offset_from_containing_block_to_parent_.left();
+}
+
+void Box::SetStaticPositionRightFromParent(LayoutUnit right) {
+  if (right != static_position_offset_from_parent_.right()) {
+    static_position_offset_from_parent_.set_right(right);
+    // Invalidate the size if the static position offset changes, as the
+    // positioning for absolutely positioned elements is handled within the size
+    // update.
+    InvalidateUpdateSizeInputsOfBox();
+  }
+}
+
+void Box::SetStaticPositionRightFromContainingBlockToParent(LayoutUnit right) {
+  if (right !=
+      static_position_offset_from_containing_block_to_parent_.right()) {
+    static_position_offset_from_containing_block_to_parent_.set_right(right);
+    // Invalidate the size if the static position offset changes, as the
+    // positioning for absolutely positioned elements is handled within the size
+    // update.
+    InvalidateUpdateSizeInputsOfBox();
+  }
+}
+
+LayoutUnit Box::GetStaticPositionRight() const {
+  DCHECK(IsAbsolutelyPositioned());
+  return static_position_offset_from_parent_.right() +
+         static_position_offset_from_containing_block_to_parent_.right();
 }
 
 void Box::SetStaticPositionTopFromParent(LayoutUnit top) {
-  if (top != static_position_offset_from_parent_.y()) {
-    static_position_offset_from_parent_.set_y(top);
+  if (top != static_position_offset_from_parent_.top()) {
+    static_position_offset_from_parent_.set_top(top);
     // Invalidate the size if the static position offset changes, as the
     // positioning for absolutely positioned elements is handled within the size
     // update.
@@ -294,8 +341,8 @@
 }
 
 void Box::SetStaticPositionTopFromContainingBlockToParent(LayoutUnit top) {
-  if (top != static_position_offset_from_containing_block_to_parent_.y()) {
-    static_position_offset_from_containing_block_to_parent_.set_y(top);
+  if (top != static_position_offset_from_containing_block_to_parent_.top()) {
+    static_position_offset_from_containing_block_to_parent_.set_top(top);
     // Invalidate the size if the static position offset changes, as the
     // positioning for absolutely positioned elements is handled within the size
     // update.
@@ -305,8 +352,8 @@
 
 LayoutUnit Box::GetStaticPositionTop() const {
   DCHECK(IsAbsolutelyPositioned());
-  return static_position_offset_from_parent_.y() +
-         static_position_offset_from_containing_block_to_parent_.y();
+  return static_position_offset_from_parent_.top() +
+         static_position_offset_from_containing_block_to_parent_.top();
 }
 
 void Box::InvalidateCrossReferencesOfBoxAndAncestors() {
@@ -532,11 +579,30 @@
          GetContentBoxOffsetFromContainingBlock();
 }
 
+InsetsLayoutUnit Box::GetContentBoxInsetFromContainingBlockContentBox(
+    const ContainerBox* containing_block) const {
+  // NOTE: Bottom inset is not computed and should not be queried.
+  return GetContainingBlockInsetFromItsContentBox(containing_block) +
+         GetContentBoxInsetFromContainingBlock(containing_block);
+}
+
 Vector2dLayoutUnit Box::GetContentBoxOffsetFromContainingBlock() const {
   return Vector2dLayoutUnit(GetContentBoxLeftEdgeOffsetFromContainingBlock(),
                             GetContentBoxTopEdgeOffsetFromContainingBlock());
 }
 
+InsetsLayoutUnit Box::GetContentBoxInsetFromContainingBlock(
+    const ContainerBox* containing_block) const {
+  // NOTE: Bottom inset is not computed and should not be queried.
+  LayoutUnit left_inset =
+      left() + margin_left() + border_left_width() + padding_left();
+  return InsetsLayoutUnit(
+      left_inset,
+      top() + margin_top() + border_top_width() + padding_top(),
+      containing_block->width() - left_inset - width(),
+      LayoutUnit());
+}
+
 LayoutUnit Box::GetContentBoxLeftEdgeOffsetFromContainingBlock() const {
   return left() + GetContentBoxLeftEdgeOffsetFromMarginBox();
 }
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index a058f3b..501f3ad 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -272,6 +272,8 @@
   // containing block's content box or padding box) to its content box.
   Vector2dLayoutUnit GetContainingBlockOffsetFromItsContentBox(
       const ContainerBox* containing_block) const;
+  InsetsLayoutUnit GetContainingBlockInsetFromItsContentBox(
+      const ContainerBox* containing_block) const;
 
   // Returns boxes relative to the root or containing block, that take into
   // account transforms.
@@ -314,6 +316,15 @@
   void SetStaticPositionLeftFromContainingBlockToParent(LayoutUnit left);
   LayoutUnit GetStaticPositionLeft() const;
 
+  // The static position for 'right' is the distance from the right edge of the
+  // containing block to the right margin edge of the same hypothetical box as
+  // above. The value is positive if the hypothetical box is to the left of the
+  // containing block's edge.
+  //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+  void SetStaticPositionRightFromParent(LayoutUnit right);
+  void SetStaticPositionRightFromContainingBlockToParent(LayoutUnit right);
+  LayoutUnit GetStaticPositionRight() const;
+
   // For the purposes of this section and the next, the term "static position"
   // (of an element) refers, roughly, to the position an element would have had
   // in the normal flow. More precisely, the static position for 'top' is the
@@ -418,7 +429,11 @@
   LayoutUnit GetContentBoxTopEdgeOffsetFromMarginBox() const;
   Vector2dLayoutUnit GetContentBoxOffsetFromContainingBlockContentBox(
       const ContainerBox* containing_block) const;
+  InsetsLayoutUnit GetContentBoxInsetFromContainingBlockContentBox(
+      const ContainerBox* containing_block) const;
   Vector2dLayoutUnit GetContentBoxOffsetFromContainingBlock() const;
+  InsetsLayoutUnit GetContentBoxInsetFromContainingBlock(
+      const ContainerBox* containing_block) const;
   LayoutUnit GetContentBoxLeftEdgeOffsetFromContainingBlock() const;
   LayoutUnit GetContentBoxTopEdgeOffsetFromContainingBlock() const;
   LayoutUnit GetContentBoxStartEdgeOffsetFromContainingBlock(
@@ -919,6 +934,11 @@
   // 'static' and 'float' had been 'none'. The value is negative if the
   // hypothetical box is to the left of the containing block.
   //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+  // The static position for 'right' is the distance from the right edge of the
+  // containing block to the right margin edge of the same hypothetical box as
+  // above. The value is positive if the hypothetical box is to the left of the
+  // containing block's edge.
+  //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
   // For the purposes of this section and the next, the term "static position"
   // (of an element) refers, roughly, to the position an element would have had
   // in the normal flow. More precisely, the static position for 'top' is the
@@ -926,8 +946,8 @@
   // of a hypothetical box that would have been the first box of the element if
   // its specified 'position' value had been 'static'.
   //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
-  Vector2dLayoutUnit static_position_offset_from_parent_;
-  Vector2dLayoutUnit static_position_offset_from_containing_block_to_parent_;
+  InsetsLayoutUnit static_position_offset_from_parent_;
+  InsetsLayoutUnit static_position_offset_from_containing_block_to_parent_;
 
   // Used values of "margin-left", "margin-top", "margin-right",
   // and "margin-bottom".
diff --git a/src/cobalt/layout/container_box.cc b/src/cobalt/layout/container_box.cc
index 18122e9..3951702 100644
--- a/src/cobalt/layout/container_box.cc
+++ b/src/cobalt/layout/container_box.cc
@@ -279,13 +279,14 @@
 
 namespace {
 
-Vector2dLayoutUnit
+InsetsLayoutUnit
 GetOffsetFromContainingBlockToParentOfAbsolutelyPositionedBox(
     const ContainerBox* containing_block, Box* child_box) {
+  // NOTE: Bottom inset is not computed and should not be queried.
   DCHECK(child_box->IsAbsolutelyPositioned());
   DCHECK_EQ(child_box->GetContainingBlock(), containing_block);
 
-  Vector2dLayoutUnit offset;
+  InsetsLayoutUnit offset;
 
   const ContainerBox* current_box = child_box->parent();
   while (current_box != containing_block) {
@@ -293,7 +294,7 @@
     DCHECK(!current_box->IsTransformed());
     const ContainerBox* next_box = current_box->GetContainingBlock();
     offset +=
-        current_box->GetContentBoxOffsetFromContainingBlockContentBox(next_box);
+        current_box->GetContentBoxInsetFromContainingBlockContentBox(next_box);
     current_box = next_box;
   }
 
@@ -304,7 +305,10 @@
   // the containing block of a 'fixed' position element must always be the
   // viewport, all major browsers use the padding box of a transformed ancestor
   // as the containing block for 'fixed' position elements.
-  offset += containing_block->GetContentBoxOffsetFromPaddingBox();
+  offset += InsetsLayoutUnit(containing_block->padding_left(),
+                             containing_block->padding_top(),
+                             containing_block->padding_right(),
+                             LayoutUnit());
 
   return offset;
 }
@@ -435,14 +439,16 @@
 
 void ContainerBox::UpdateRectOfAbsolutelyPositionedChildBox(
     Box* child_box, const LayoutParams& child_layout_params) {
-  Vector2dLayoutUnit offset_from_containing_block_to_parent =
+  InsetsLayoutUnit offset_from_containing_block_to_parent =
       GetOffsetFromContainingBlockToParentOfAbsolutelyPositionedBox(this,
                                                                     child_box);
 
   child_box->SetStaticPositionLeftFromContainingBlockToParent(
-      offset_from_containing_block_to_parent.x());
+      offset_from_containing_block_to_parent.left());
+  child_box->SetStaticPositionRightFromContainingBlockToParent(
+      offset_from_containing_block_to_parent.right());
   child_box->SetStaticPositionTopFromContainingBlockToParent(
-      offset_from_containing_block_to_parent.y());
+      offset_from_containing_block_to_parent.top());
   child_box->UpdateSize(child_layout_params);
 }
 
diff --git a/src/cobalt/layout/flex_container_box.cc b/src/cobalt/layout/flex_container_box.cc
index bd58db9..2fcd325 100644
--- a/src/cobalt/layout/flex_container_box.cc
+++ b/src/cobalt/layout/flex_container_box.cc
@@ -232,6 +232,7 @@
 
   if (IsAbsolutelyPositioned()) {
     UpdateWidthAssumingAbsolutelyPositionedBox(
+        layout_params.containing_block_direction,
         layout_params.containing_block_size.width(), maybe_left, maybe_right,
         maybe_width, maybe_margin_left, maybe_margin_right, maybe_height);
 
diff --git a/src/cobalt/layout/insets_layout_unit.h b/src/cobalt/layout/insets_layout_unit.h
index fd15222..5b4264d 100644
--- a/src/cobalt/layout/insets_layout_unit.h
+++ b/src/cobalt/layout/insets_layout_unit.h
@@ -25,6 +25,14 @@
   std::string ToString() const;
 };
 
+inline InsetsLayoutUnit operator+(const InsetsLayoutUnit& lhs,
+                                  const InsetsLayoutUnit& rhs) {
+  return InsetsLayoutUnit(lhs.left() + rhs.left(),
+                          lhs.top() + rhs.top(),
+                          lhs.right() + rhs.right(),
+                          lhs.bottom() + rhs.bottom());
+}
+
 }  // namespace layout
 }  // namespace cobalt
 
diff --git a/src/cobalt/layout/line_box.cc b/src/cobalt/layout/line_box.cc
index 75b83c1..fef8a93 100644
--- a/src/cobalt/layout/line_box.cc
+++ b/src/cobalt/layout/line_box.cc
@@ -475,7 +475,7 @@
   // The static-position containing block is the containing block of a
   // hypothetical box that would have been the first box of the element if its
   // specified 'position' value had been 'static'.
-  //
+
   // The static position for 'left' is the distance from the left edge of the
   // containing block to the left margin edge of a hypothetical box that would
   // have been the first box of the element if its 'position' property had been
@@ -483,6 +483,12 @@
   // hypothetical box is to the left of the containing block.
   //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
 
+  // The static position for 'right' is the distance from the right edge of the
+  // containing block to the right margin edge of the same hypothetical box as
+  // above. The value is positive if the hypothetical box is to the left of the
+  // containing block's edge.
+  //   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+
   // For the purposes of this section and the next, the term "static position"
   // (of an element) refers, roughly, to the position an element would have had
   // in the normal flow. More precisely, the static position for 'top' is the
@@ -498,6 +504,7 @@
   } else {
     child_box->SetStaticPositionLeftFromParent(LayoutUnit());
   }
+  child_box->SetStaticPositionRightFromParent(LayoutUnit());
   child_box->SetStaticPositionTopFromParent(LayoutUnit());
 }
 
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-expected.png
new file mode 100644
index 0000000..c63bafd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl-expected.png
new file mode 100644
index 0000000..a82081c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl.html
similarity index 61%
copy from src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained.html
copy to src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl.html
index fa7a680..80ca101 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl.html
@@ -1,8 +1,11 @@
 <!DOCTYPE html>
 <!--
  | If all of properties have a computed value other than "auto", the values are
- | said to be "over-constrained" and the specified value of "margin-right" is
- | ignored.
+ | said to be "over-constrained" and one of the used values will have to be
+ | different from its computed value. If the "direction" property of the
+ | containing block has the value "ltr", the specified value of "margin-right"
+ | is ignored and the value is calculated so as to make the equality true. If
+ | the value of "direction" is "rtl", this happens to "margin-left" instead.
  |   https://www.w3.org/TR/CSS21/visudet.html#blockwidth
  -->
 <html>
@@ -30,12 +33,15 @@
     }
   </style>
 </head>
-<body>
+<body dir="rtl">
   <div class="containing-block">
     <div class="margin-left block"></div>
   </div>
   <div class="containing-block">
     <div class="margin-right block"></div>
   </div>
+  <div class="containing-block">
+    <div class="margin-left margin-right block"></div>
+  </div>
 </body>
 </html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained.html
similarity index 63%
rename from src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained.html
rename to src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained.html
index fa7a680..3f7f681 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained.html
@@ -1,8 +1,11 @@
 <!DOCTYPE html>
 <!--
  | If all of properties have a computed value other than "auto", the values are
- | said to be "over-constrained" and the specified value of "margin-right" is
- | ignored.
+ | said to be "over-constrained" and one of the used values will have to be
+ | different from its computed value. If the "direction" property of the
+ | containing block has the value "ltr", the specified value of "margin-right"
+ | is ignored and the value is calculated so as to make the equality true. If
+ | the value of "direction" is "rtl", this happens to "margin-left" instead.
  |   https://www.w3.org/TR/CSS21/visudet.html#blockwidth
  -->
 <html>
@@ -37,5 +40,8 @@
   <div class="containing-block">
     <div class="margin-right block"></div>
   </div>
+  <div class="containing-block">
+    <div class="margin-left margin-right block"></div>
+  </div>
 </body>
 </html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained-expected.png
deleted file mode 100644
index 662e153..0000000
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-3-margin-right-should-be-ignored-if-overconstrained-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl-expected.png
new file mode 100644
index 0000000..c9b859a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl.html
new file mode 100644
index 0000000..f10de9a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+ | The direct static children of absolutely positioned elements should
+ | participate in layout within their absolutely positioned containing block.
+ -->
+<html>
+<head>
+  <style>
+    body {
+      color: #fff;
+      font-family: Roboto;
+      font-size: 100px;
+      font-weight: bold;
+    }
+    .absolute-block {
+      position: absolute;
+      left: 10px;
+      top: 10px;
+    }
+    .level-1 {
+      background-color: #40c4ff;
+    }
+    .level-2 {
+      background-color: #00b0ff;
+    }
+    .level-3 {
+      background-color: #0091ea;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="absolute-block">
+    <span class="level-1">
+      I
+      <span class="level-2">
+        II
+        <div class="level-3">III</div>
+        II
+      </span>
+      I
+    </span>
+  </div>
+</body>
+
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl-expected.png
new file mode 100644
index 0000000..f9a8cd6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl.html
new file mode 100644
index 0000000..d1ac008
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+ | The direct static children of absolutely positioned elements should
+ | participate in layout within their absolutely positioned containing block.
+ -->
+<html>
+<head>
+  <style>
+    body {
+      color: #fff;
+      font-family: Roboto;
+      font-size: 100px;
+      font-weight: bold;
+    }
+    .absolute-block {
+      position: absolute;
+      left: 10px;
+      top: 10px;
+    }
+    .level-1 {
+      background-color: #40c4ff;
+    }
+    .level-2 {
+      background-color: #00b0ff;
+    }
+    .level-3 {
+      background-color: #0091ea;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="absolute-block">
+    <span class="level-1">
+      I
+      <span class="level-2">
+        II<br>
+        <span class="level-3">III<br></span>
+        II
+      </span>
+      I
+    </span>
+  </div>
+</body>
+
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl-expected.png
new file mode 100644
index 0000000..4e72bf3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl.html
new file mode 100644
index 0000000..cf67983
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!--
+ | Blockification should not affect the outer display style used for the static position.
+ |   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+ |   https://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
+ -->
+<html>
+<head>
+  <style>
+    body {
+      margin: 0px;
+      font-family: Roboto;
+      font-size: 30px;
+      font-weight: bold;
+    }
+    .block {
+      display: block;
+    }
+    .inline {
+      display: inline;
+    }
+    .inline-block {
+      background-color: #0091ea;
+      display: inline-block;
+    }
+    .absolute {
+       position: absolute;
+     }
+    .fixed {
+       position: fixed;
+     }
+    .lightblue {
+       background-color: #40c4ff;
+     }
+     .cobaltblue {
+       background-color: #00b0ff;
+     }
+     .darkblue {
+       background-color: #0091ea;
+     }
+  </style>
+</head>
+<body dir="rtl">
+<div>
+  <span class="lightblue">
+    inline
+    <span class="absolute darkblue">absolute</span>
+  </span>
+</div>
+<div>
+  <span class="lightblue">
+    inline
+    <div class="inline absolute darkblue">absolute</span>
+  </span>
+</div>
+<div>
+  <span class="lightblue">
+    inline
+    <div class="inline-block absolute darkblue">absolute</span>
+  </span>
+</div>
+<div>
+  <span class="lightblue">
+    inline
+    <span class="fixed darkblue">fixed</span>
+  </span>
+</div>
+<div>
+  <span class="lightblue">
+    inline
+    <div class="inline fixed darkblue">fixed</span>
+  </span>
+</div>
+<div>
+  <span class="lightblue">
+    inline
+    <div class="inline-block fixed darkblue">fixed</span>
+  </span>
+</div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position.html
index f9e38d8..973e224 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-blockification-should-not-affect-static-position.html
@@ -27,7 +27,7 @@
        position: absolute;
      }
     .fixed {
-       position: absolute;
+       position: fixed;
      }
     .lightblue {
        background-color: #40c4ff;
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-expected.png
index 3bee4a9..629ef43 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-expected.png
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl-expected.png
new file mode 100644
index 0000000..3eceece
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl.html
new file mode 100644
index 0000000..6ee4c42
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!--
+ | Absolutely positioned elements are positioned relative to their containing
+ | block when specifying left and top.  Their containing block is their first
+ | ancestor with a position that is set to 'relative', 'absolute' or 'fixed'.
+ -->
+<html>
+<head>
+  <style>
+    body {
+      margin: 0;
+    }
+    .outer-absolute-block {
+      position: absolute;
+    }
+    .static-block {
+      position: static;
+      width: 100px;
+      height: 100px;
+      background-color: rgb(0, 255, 100);
+    }
+    .inner-absolute-block {
+      position: absolute;
+      width: 50px;
+      height: 50px;
+      left: -100px;
+      top: 100px;
+      background-color: rgb(0, 100, 255);
+    }
+  </style>
+</head>
+
+<body dir="rtl">
+  <div class="outer-absolute-block">
+    <div class="static-block"></div>
+    <div class="inner-absolute-block"></div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block.html
index d6e4959..d35df36 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block.html
@@ -17,7 +17,7 @@
       position: static;
       width: 100px;
       height: 100px;
-      background-color: rgb(255, 100, 0);
+      background-color: rgb(0, 255, 100);
     }
     .inner-absolute-block {
       position: absolute;
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl-expected.png
new file mode 100644
index 0000000..af3d18a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl.html
new file mode 100644
index 0000000..faff7f4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<!--
+ | If all three of "left", "width", and "right" are "auto": first set any "auto"
+ | values for "margin-left" and "margin-right" to 0. Then, for dir=rtl, set
+ | "right" to the static position and apply rule:
+ |
+ |     1. "left" and "width" are "auto" and "right" is not "auto", then
+ |         the width is shrink-to-fit. Then solve for "left".
+ |
+ |   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+ -->
+<html>
+<head>
+  <style>
+    body {
+      margin: 0px;
+    }
+    .first {
+      top: 10px;
+    }
+    .second {
+      top: 45px;
+    }
+    .third {
+      top: 80px;
+    }
+    .containing-block {
+      background-color: #2196f3;
+      height: 5px;
+      padding: 10px 0;
+      position: absolute;
+      width: 500px;
+    }
+    .block {
+      /* TODO: Add borders when supported. */
+      background-color: #3f51b5;
+      display: block;
+      height: 5px;
+      left: auto;
+      padding: 0 100px;
+      position: absolute;
+      right: auto;
+      width: auto;
+    }
+    .auto-margin-left {
+      margin-left: auto;
+    }
+    .non-auto-margin-left {
+      margin-left: 50px;
+    }
+    .auto-margin-right {
+      margin-right: auto;
+    }
+    .non-auto-margin-right {
+      margin-right: 50px;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="first containing-block">
+    <span>
+      <div class="auto-margin-left non-auto-margin-right block"></div>
+    </span>
+  </div>
+  <div class="second containing-block">
+    <span>
+      <div class="non-auto-margin-left auto-margin-right block"></div>
+    </span>
+  </div>
+  <div class="third containing-block">
+    <span>
+      <div class="auto-margin-left auto-margin-right block"></div>
+    </span>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element.html
index 50cd0e5..062ee9c 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <!--
  | If all three of "left", "width", and "right" are "auto": first set any "auto"
- | values for "margin-left" and "margin-right" to 0. Then, set "left" to
- | the static position and apply rule:
+ | values for "margin-left" and "margin-right" to 0. Then, for dir=ltr, set
+ | "left" to the static position and apply rule:
  |
  |     3. "width" and "right" are "auto" and "left" is not "auto", then
  |        the width is shrink-to-fit . Then solve for "right".
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl-expected.png
new file mode 100644
index 0000000..af3d18a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl.html
new file mode 100644
index 0000000..12e3d7a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!--
+ | If all three of "left", "width", and "right" are "auto": first set any "auto"
+ | values for "margin-left" and "margin-right" to 0. For dir=rtl, set "right" to
+ | the static position and apply rule:
+ |
+ |     1. "left" and "width" are "auto" and "right" is not 'auto', then
+ |        the width is shrink-to-fit. Then solve for "left".
+ |
+ |   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+ -->
+<html>
+<head>
+  <style>
+    .first {
+      top: 10px;
+    }
+    .second {
+      top: 45px;
+    }
+    .third {
+      top: 80px;
+    }
+    .containing-block {
+      background-color: #2196f3;
+      height: 5px;
+      padding: 10px 0;
+      position: absolute;
+      width: 500px;
+    }
+    .block {
+      /* TODO: Add borders when supported. */
+      background-color: #3f51b5;
+      display: block;
+      height: 5px;
+      left: auto;
+      padding: 0 100px;
+      position: absolute;
+      right: auto;
+      width: auto;
+    }
+    .auto-margin-left {
+      margin-left: auto;
+    }
+    .non-auto-margin-left {
+      margin-left: 50px;
+    }
+    .auto-margin-right {
+      margin-right: auto;
+    }
+    .non-auto-margin-right {
+      margin-right: 50px;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="first containing-block">
+    <div class="auto-margin-left non-auto-margin-right block"></div>
+  </div>
+  <div class="second containing-block">
+    <div class="non-auto-margin-left auto-margin-right block"></div>
+  </div>
+  <div class="third containing-block">
+    <div class="auto-margin-left auto-margin-right block"></div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto.html
index e5fc316..b316dbe 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-left-and-width-and-right-are-auto.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <!--
  | If all three of "left", "width", and "right" are "auto": first set any "auto"
- | values for "margin-left" and "margin-right" to 0. Then, set "left" to
+ | values for "margin-left" and "margin-right" to 0. For dir=ltr, set "left" to
  | the static position and apply rule:
  |
  |     3. "width" and "right" are "auto" and "left" is not "auto", then
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl-expected.png
new file mode 100644
index 0000000..49cb1d5
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl.html
new file mode 100644
index 0000000..548a22d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<!--
+ | If none of the three is "auto": if both "margin-left" and "margin-right" are
+ | "auto", solve the equation under the extra constraint that the two margins
+ | get equal values, unless this would make them negative, in which case set
+ | "margin-left" to zero and solve for "margin-right". If one of "margin-left"
+ | or "margin-right" is "auto", solve the equation for that value. If the values
+ | are over-constrained, ignore the value for "right" and solve for that value.
+ |   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+ -->
+<html>
+<head>
+  <style>
+    .first {
+      top: 10px;
+    }
+    .second {
+      top: 45px;
+    }
+    .third {
+      top: 80px;
+    }
+    .fourth {
+      top: 115px;
+    }
+    .fifth {
+      top: 150px;
+    }
+    .containing-block {
+      background-color: #2196f3;
+      height: 5px;
+      padding: 10px 0;
+      position: absolute;
+      width: 500px;
+    }
+    .block {
+      /* TODO: Add borders when supported. */
+      background-color: #3f51b5;
+      display: block;
+      height: 5px;
+      left: 50px;
+      padding: 0 100px;
+      position: absolute;
+      right: 50px;
+      width: 150px;
+    }
+    .wide {
+      width: 300px;
+    }
+    .auto-margin-left {
+      margin-left: auto;
+    }
+    .non-auto-margin-left {
+      margin-left: 50px;
+    }
+    .auto-margin-right {
+      margin-right: auto;
+    }
+    .non-auto-margin-right {
+      margin-right: 50px;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="first containing-block">
+    <div class="auto-margin-left auto-margin-right block"></div>
+  </div>
+  <div class="second containing-block">
+    <div class="auto-margin-left auto-margin-right wide block"></div>
+  </div>
+  <div class="third containing-block">
+    <div class="auto-margin-left non-auto-margin-right block"></div>
+  </div>
+  <div class="fourth containing-block">
+    <div class="non-auto-margin-left auto-margin-right block"></div>
+  </div>
+  <div class="fifth containing-block">
+    <div class="non-auto-margin-left non-auto-margin-right block"></div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl-expected.png
new file mode 100644
index 0000000..58b414a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl.html
new file mode 100644
index 0000000..3d64994
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<!--
+ | Set "auto" values for "margin-left" and "margin-right" to 0, and pick the one
+ | of the following six rules that applies.
+ |
+ |     1. "left" and "width" are "auto" and "right" is not "auto", then
+ |        the width is shrink-to-fit. Then solve for "left".
+ |     2. "left" and "right" are "auto" and "width" is not "auto", then set
+ |        "left" to the static position. Then solve for "right".
+ |     3. "width" and "right" are "auto" and "left" is not "auto", then
+ |        the width is shrink-to-fit . Then solve for "right".
+ |     4. "left" is "auto", "width" and "right" are not "auto", then solve
+ |        for "left".
+ |     5. "width" is "auto", "left" and "right" are not "auto", then solve
+ |        for "width".
+ |     6. "right" is "auto", "left" and "width" are not "auto", then solve
+ |        for "right".
+ |
+ |   https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
+ -->
+<html>
+<head>
+  <style>
+    .first {
+      top: 10px;
+    }
+    .second {
+      top: 45px;
+    }
+    .third {
+      top: 80px;
+    }
+    .fourth {
+      top: 115px;
+    }
+    .fifth {
+      top: 150px;
+    }
+    .sixth {
+      top: 185px;
+    }
+    .containing-block {
+      background-color: #2196f3;
+      height: 5px;
+      padding: 10px 0;
+      position: absolute;
+      width: 500px;
+    }
+    .block {
+      /* TODO: Add borders when supported. */
+      background-color: #3f51b5;
+      display: block;
+      height: 5px;
+      margin: auto;
+      padding: 0 100px;
+      position: absolute;
+    }
+    .auto-left {
+      left: auto;
+    }
+    .non-auto-left {
+      left: 50px;
+    }
+    .auto-width {
+      width: auto;
+    }
+    .non-auto-width {
+      width: 150px;
+    }
+    .auto-right {
+      right: auto;
+    }
+    .non-auto-right {
+      right: 50px;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="first containing-block">
+    <div class="auto-left auto-width non-auto-right block"></div>
+  </div>
+  <div class="second containing-block">
+    <div class="auto-left non-auto-width auto-right block"></div>
+  </div>
+  <div class="third containing-block">
+    <div class="non-auto-left auto-width auto-right block"></div>
+  </div>
+  <div class="fourth containing-block">
+    <div class="auto-left non-auto-width non-auto-right block"></div>
+  </div>
+  <div class="fifth containing-block">
+    <div class="non-auto-left auto-width non-auto-right block"></div>
+  </div>
+  <div class="sixth containing-block">
+    <div class="non-auto-left non-auto-width auto-right block"></div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-static-position-for-block-level-elements-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-static-position-for-block-level-elements-rtl-expected.png
new file mode 100644
index 0000000..69a8bc6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-static-position-for-block-level-elements-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-static-position-for-block-level-elements-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-static-position-for-block-level-elements-rtl.html
new file mode 100644
index 0000000..c36f2b7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-3-7-static-position-for-block-level-elements-rtl.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!--
+ | The 'static position' of block-level elements is 'the position an element
+ | would have had in the normal flow', as specified in
+ | https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width and
+ | https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height.
+ -->
+<html>
+<head>
+ <style>
+   body {
+     margin: 0px;
+     font-size: 16px;
+     font-family: Roboto;
+     font-weight: bold;
+     color: #F44336;
+   }
+ </style>
+</head>
+<body dir="rtl">
+<div style="width:100px; height:50px; position:absolute; padding:10px; background-color:#4E342E;">
+ I'm hidden
+ <div style="position:absolute; width:100%; top:0%; height:50%; background-color: #795548;">
+</div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl-expected.png
new file mode 100644
index 0000000..f087680
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl.html b/src/cobalt/layout_tests/testdata/css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl.html
new file mode 100644
index 0000000..4846cab
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html dir="rtl">
+<!--
+ | Absolutely positioned elements in a positioned container should be scrolled.
+ |   https://www.w3.org/TR/CSS21/visufx.html
+ |
+ | Unfortunately, major browsers handle scrollLeft differently when dir=rtl.
+ | Follow the spec and treat scrollLeft == 0 as the rightmost position with
+ | scrollLeft values going negative as the user scrolls left.
+ |   https://lists.w3.org/Archives/Public/www-style/2012Aug/0156.html
+ -->
+<head>
+  <style>
+    body {
+      font-family: Roboto;
+      font-size: 16px;
+      background-color: #FF0000;
+    }
+
+    .overflow-scroll {
+      width: 150px;
+      height: 100px;
+      overflow: scroll;
+      background-color: #000000;
+    }
+
+    .container {
+      width: 300px;
+      height: 75px;
+      overflow: hidden;
+      position: relative;
+      background-color: #808080;
+    }
+
+    .content {
+      width: 50px;
+      height: 50px;
+      position: absolute;
+    }
+  </style>
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.onload = function() {
+      var scroller = document.getElementById("scroller");
+      scroller.scrollLeft = -50;
+      if (scroller.scrollLeft != -50) {
+        console.log("FAIL" +
+          "\nValue of: scrollLeft" +
+          "\n  Actual: " + scroller.scrollLeft +
+          "\nExpected: " + "-50");
+      } else {
+        document.body.style.backgroundColor = "#FFFFFF";
+      }
+      if (window.testRunner) {
+        window.testRunner.notifyDone();
+      }
+    }
+  </script>
+</head>
+<body>
+  <div id="scroller" class="overflow-scroll">
+    <div class="container">
+      <div class="content"
+           style="background-color: #FF0000; transform: translateX(0px)"></div>
+      <div class="content"
+           style="background-color: #00FF00; transform: translateX(-75px)"></div>
+      <div class="content"
+           style="background-color: #0000FF; transform: translateX(-150px)"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
index 4fbd942..7f7ba6b 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
@@ -15,24 +15,34 @@
 10-3-2-replaced-box-width
 10-3-3-auto-margin-left-and-right-should-center-element-horizontally
 10-3-3-auto-width-should-zero-other-autos
+10-3-3-correct-margin-is-ignored-if-overconstrained
+10-3-3-correct-margin-is-ignored-if-overconstrained-rtl
 10-3-3-margin-auto-should-be-treated-as-zero-if-sum-is-greater-than-containing-block-width
-10-3-3-margin-right-should-be-ignored-if-overconstrained
 10-3-3-one-auto-should-follow-from-equality
 10-3-3-overconstrained-should-respect-direction
 10-3-4-block-level-replaced-box-margins-should-be-calculated-as-for-non-replaced-box
 10-3-4-block-level-replaced-box-width-should-be-calculated-as-for-inline-replaced-box
 10-3-7-absolute-element-children-should-shrink-to-fit
+10-3-7-absolute-element-children-should-shrink-to-fit-rtl
 10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit
+10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl
 10-3-7-absolute-position-elements-solve-for-height-when-top-and-bottom-are-specified
 10-3-7-absolute-position-elements-solve-for-width-when-left-and-right-are-specified
 10-3-7-blockification-should-not-affect-static-position
+10-3-7-blockification-should-not-affect-static-position-rtl
 10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block
+10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl
 10-3-7-left-and-width-and-right-are-auto
 10-3-7-left-and-width-and-right-are-auto-in-inline-element
+10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl
+10-3-7-left-and-width-and-right-are-auto-rtl
 10-3-7-multiple-absolute-positioned-elements-share-same-static-position
 10-3-7-none-of-left-and-width-and-right-is-auto
+10-3-7-none-of-left-and-width-and-right-is-auto-rtl
 10-3-7-some-of-left-and-width-and-right-are-auto
+10-3-7-some-of-left-and-width-and-right-are-auto-rtl
 10-3-7-static-position-for-block-level-elements
+10-3-7-static-position-for-block-level-elements-rtl
 10-3-9-auto-margin-should-become-zero-in-inline-block-non-replaced-elements
 10-3-9-child-width-should-be-calculated-after-width-of-inline-block-parent
 10-3-9-inline-block-non-replaced-and-br-elements-with-auto-width-should-shrink-to-fit
@@ -85,6 +95,7 @@
 11-1-1-overflow-scroll-absolute-positioned-elements-positioned-scroller
 11-1-1-overflow-scroll-absolute-positioned-elements-unpositioned-scroller
 11-1-1-overflow-scroll-container-scrolled
+11-1-1-overflow-scroll-container-scrolled-rtl
 11-1-1-overflow-scroll-fixed-positioned-elements-transformed-scroller
 11-1-1-overflow-scroll-fixed-positioned-elements-untransformed-scroller
 11-1-1-overflow-scroll-should-affect-descendants-with-z-index
diff --git a/src/cobalt/layout_tests/testdata/css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl-expected.png b/src/cobalt/layout_tests/testdata/css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl-expected.png
new file mode 100644
index 0000000..eac9cef
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl.html b/src/cobalt/layout_tests/testdata/css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl.html
new file mode 100644
index 0000000..b863f35
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!--
+ | Absolutely positioned boxes should not be considered during line
+ | wrapping.
+ -->
+<html>
+<head>
+  <style>
+    body {
+      margin: 0px;
+      font-family: Roboto;
+      font-size: 20px;
+    }
+    .containing-block {
+      background-color: #03a9f4;
+      width: 100px;
+    }
+    .absolute-block {
+      position: absolute;
+      margin: 40px;
+    }
+  </style>
+</head>
+<body dir="rtl">
+  <div class="containing-block">
+    <div><span>abcdefghijkl<span class="absolute-block"> mnop</span>qrst</span></div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-text-3/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-text-3/layout_tests.txt
index f76855c..8372916 100644
--- a/src/cobalt/layout_tests/testdata/css-text-3/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-text-3/layout_tests.txt
@@ -31,6 +31,7 @@
 4-1-3-spaces-at-beginning-and-end-of-line-should-be-collapsed
 4-1-empty-inline-block-should-be-treated-as-non-empty-text
 5-absolute-boxes-in-span-should-not-impact-line-wrapping
+5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl
 5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity
 5-collapsible-trailing-white-space-should-not-prevent-soft-wrap-opportunity
 5-collapsible-trailing-white-space-that-overflows-line-prior-to-collapse-should-not-cause-wrap
@@ -72,4 +73,4 @@
 9-1-text-indent-should-indent-first-line-in-rtl-paragraph-on-right
 9-1-text-indent-should-indent-nested-inline-block
 9-1-text-indent-should-indent-nested-inline-rtl-block
-9-1-text-indent-should-not-indent-nested-span
\ No newline at end of file
+9-1-text-indent-should-not-indent-nested-span
diff --git a/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-children-rtl-expected.png b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-children-rtl-expected.png
new file mode 100644
index 0000000..27dcbc0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-children-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-children-rtl.html b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-children-rtl.html
new file mode 100644
index 0000000..c1b8fd4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-children-rtl.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html dir="rtl">
+<!--
+ | Absolute positioned elements in the containing block are included in the
+ | scrolling area.
+ |   https://www.w3.org/TR/cssom-view-1/#extension-to-the-element-interface
+ |   https://www.w3.org/TR/cssom-view-1/#scrolling-area
+ -->
+<head>
+  <style>
+    body {
+      font-family: Roboto;
+      font-size: 16px;
+      background-color: #FF0000;
+    }
+
+    .overflow-scroll {
+      right: 50px;
+      width: 150px;
+      height: 150px;
+      overflow: scroll;
+      position: relative;
+      background-color: #808080;
+    }
+
+    .content-positioned {
+      width: 35px;
+      height: 35px;
+      margin: 3px;
+      border: 5px solid #00FF00;
+      padding: 7px;
+      position: absolute;
+      background-color: #0000FF;
+    }
+  </style>
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    function runTest() {
+      var error = "";
+      var scroller = document.getElementById("scroller");
+      if (scroller.scrollWidth != 187) {
+        error += "\nValue of: scrollWidth"
+               + "\n  Actual: " + scroller.scrollWidth
+               + "\nExpected: " + 187;
+      }
+      if (scroller.scrollHeight != 150) {
+        error += "\nValue of: scrollHeight"
+               + "\n  Actual: " + scroller.scrollHeight
+               + "\nExpected: " + 150;
+      }
+      return error;
+    }
+
+    window.onload = function() {
+      var error = runTest();
+      if (error) {
+        console.log("FAIL" + error);
+      } else {
+        document.body.style.backgroundColor = "rgba(0,0,0,0)";
+      }
+
+      if (window.testRunner) {
+        window.testRunner.notifyDone();
+      }
+    }
+  </script>
+</head>
+<body>
+  <div id="scroller" class="overflow-scroll">
+    <div class="content-positioned" style="transform: translateX(25px)"></div>
+    <div class="content-positioned" style="transform: translateX(-50px)"></div>
+    <div class="content-positioned" style="transform: translateX(-125px)"></div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl-expected.png b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl-expected.png
new file mode 100644
index 0000000..a62e5c0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl.html b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl.html
new file mode 100644
index 0000000..f750540
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html dir="rtl">
+<!--
+ | Absolute positioned grandchildren in the containing block are included in
+ | the scrolling area.
+ |   https://www.w3.org/TR/cssom-view-1/#extension-to-the-element-interface
+ |   https://www.w3.org/TR/cssom-view-1/#scrolling-area
+ -->
+<head>
+  <style>
+    body {
+      font-family: Roboto;
+      font-size: 16px;
+      background-color: #FF0000;
+    }
+
+    .overflow-scroll {
+      width: 150px;
+      height: 150px;
+      overflow: scroll;
+      position: relative;
+      background-color: #808080;
+    }
+
+    .content-static {
+      width: 200px;
+      height: 200px;
+      background-color: #00FF00;
+    }
+
+    .content-positioned {
+      width: 50px;
+      height: 50px;
+      position: absolute;
+      background-color: #0000FF;
+    }
+  </style>
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    function runTest() {
+      var error = "";
+      var scroller = document.getElementById("scroller");
+      if (scroller.scrollWidth != 200) {
+        error += "\nValue of: scrollWidth"
+               + "\n  Actual: " + scroller.scrollWidth
+               + "\nExpected: " + 200;
+      }
+      if (scroller.scrollHeight != 250) {
+        error += "\nValue of: scrollHeight"
+               + "\n  Actual: " + scroller.scrollHeight
+               + "\nExpected: " + 250;
+      }
+      return error;
+    }
+
+    window.onload = function() {
+      var error = runTest();
+      if (error) {
+        console.log("FAIL" + error);
+      } else {
+        document.body.style.backgroundColor = "rgba(0,0,0,0)";
+      }
+
+      if (window.testRunner) {
+        window.testRunner.notifyDone();
+      }
+    }
+  </script>
+</head>
+<body>
+  <div id="scroller" class="overflow-scroll">
+    <div class="content-static">
+      <div class="content-positioned" style="transform: translateY(0px)"></div>
+      <div class="content-positioned" style="transform: translateY(100px)"></div>
+      <div class="content-positioned" style="transform: translateY(200px)"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/cssom-view/layout_tests.txt b/src/cobalt/layout_tests/testdata/cssom-view/layout_tests.txt
index aa85235..39f0e9c 100644
--- a/src/cobalt/layout_tests/testdata/cssom-view/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/cssom-view/layout_tests.txt
@@ -1,5 +1,7 @@
 6-scrolling-area-absolute-positioned-children
+6-scrolling-area-absolute-positioned-children-rtl
 6-scrolling-area-absolute-positioned-grandchildren
+6-scrolling-area-absolute-positioned-grandchildren-rtl
 6-scrolling-area-fix-positioned-children-excluded
 6-scrolling-area-fix-positioned-children-included
 6-scrolling-area-not-visible-grandchildren-excluded
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/layout_tests.txt b/src/cobalt/layout_tests/testdata/the-dir-attribute/layout_tests.txt
index 5a437dc..79c278b 100644
--- a/src/cobalt/layout_tests/testdata/the-dir-attribute/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/layout_tests.txt
@@ -45,3 +45,4 @@
 the-dir-attribute-070
 the-dir-attribute-071
 writing-modes-principal-flow-body-propagation
+writing-modes-principal-flow-root-propagation
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-body-propagation.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-body-propagation.html
index 093472a..30888f3 100644
--- a/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-body-propagation.html
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-body-propagation.html
@@ -1,6 +1,9 @@
 <!DOCTYPE html>
 <!--
- | The body element's direction should propagate to the root element.
+ | As a special case for handling HTML documents, if the root element has a body
+ | child element, the used value of the of writing-mode and direction properties
+ | on root element are taken from the computed writing-mode and direction of the
+ | first such child element instead of from the root element's own values.
  |   https://www.w3.org/TR/css-writing-modes-3/#principal-flow
  -->
 <html dir="ltr">
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-root-propagation-expected.png b/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-root-propagation-expected.png
new file mode 100644
index 0000000..ef7454f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-root-propagation-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-root-propagation.html b/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-root-propagation.html
new file mode 100644
index 0000000..1ac662b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/the-dir-attribute/writing-modes-principal-flow-root-propagation.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!--
+ | The principal writing mode of the document is determined by the used
+ | writing-mode, direction, and text-orientation values of the root element.
+ |   https://www.w3.org/TR/css-writing-modes-3/#principal-flow
+ -->
+<html dir="rtl">
+<head>
+  <style>
+    body {
+      background-color: rgb(128, 128, 128);
+      width: 200px;
+      height: 200px;
+      border: 10px solid rgb(255, 255, 255);
+    }
+    .box {
+      background-color: rgb(0, 128, 0);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div class="box"></div>
+</body>
+</html>
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index 9aaea1d..c0a4000 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -56,7 +56,11 @@
       return 0;
     }
 
-    if (!config_.input.has_animation) {
+    if (config_.input.has_animation) {
+      animated_webp_image_ = new AnimatedWebPImage(
+          math::Size(config_.input.width, config_.input.height),
+          !!config_.input.has_alpha, resource_provider());
+    } else {
       decoded_image_data_ = AllocateImageData(
           math::Size(config_.input.width, config_.input.height),
           !!config_.input.has_alpha);
@@ -66,20 +70,43 @@
       if (!CreateInternalDecoder()) {
         return 0;
       }
-    } else {
-      animated_webp_image_ = new AnimatedWebPImage(
-          math::Size(config_.input.width, config_.input.height),
-          !!config_.input.has_alpha, resource_provider());
     }
     set_state(kReadLines);
   }
 
   if (state() == kReadLines) {
-    if (!config_.input.has_animation) {
-      // Copies and decodes the next available data. Returns VP8_STATUS_OK when
-      // the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when
-      // more data is expected. Returns error in other cases.
-      VP8StatusCode status = WebPIAppend(internal_decoder_, data, input_byte);
+    if (config_.input.has_animation) {
+      animated_webp_image_->AppendChunk(data, input_byte);
+    } else {
+      auto data_to_decode = data;
+      auto bytes_to_decode = input_byte;
+
+      // |cached_uncompressed_data_| is non-empty indicates that the last
+      // WebPIUpdate() returns VP8_STATUS_SUSPENDED, and we have to concatenate
+      // the data before send it to WebPIUpdate() again.  This should rarely
+      // happen.
+      if (!cached_uncompressed_data_.empty()) {
+        cached_uncompressed_data_.insert(
+            cached_uncompressed_data_.end(),
+            reinterpret_cast<const char*>(data),
+            reinterpret_cast<const char*>(data) + input_byte);
+        data_to_decode =
+            reinterpret_cast<const uint8*>(cached_uncompressed_data_.data());
+        bytes_to_decode = cached_uncompressed_data_.size();
+      }
+      // Send the available data to the decoder without copying. Note that as
+      // webp images are mostly used in the form of animated webp, the data sent
+      // to DecodeChunkInternal() contains a whole webp image most of the time.
+      // Not making an extra copy of the data inside the internal decoder is
+      // more optimal in such casts, so WebPIUpdate() is used, instead of
+      // WebPIAppend().  The latter makes a copying of the data inside the
+      // internal decoder.
+      // Returns VP8_STATUS_OK when the image is successfully decoded. Returns
+      // VP8_STATUS_SUSPENDED when more data is expected, in such case we have
+      // to cache the data already appended as required by WebPIUpdate().
+      // Any other return codes indicate an error.
+      VP8StatusCode status =
+          WebPIUpdate(internal_decoder_, data_to_decode, bytes_to_decode);
       if (status == VP8_STATUS_OK) {
         DCHECK(decoded_image_data_);
         DCHECK(config_.output.u.RGBA.rgba);
@@ -87,14 +114,21 @@
         DCHECK_EQ(config_.output.u.RGBA.stride,
                   decoded_image_data_->GetDescriptor().pitch_in_bytes);
         set_state(kDone);
-      } else if (status != VP8_STATUS_SUSPENDED) {
+      } else if (status == VP8_STATUS_SUSPENDED) {
+        // Only copying the data into |cached_uncompressed_data_| when it is
+        // empty, as otherwise the data has already been appended into it before
+        // calling WebPIUpdate().
+        if (cached_uncompressed_data_.empty()) {
+          cached_uncompressed_data_.assign(
+              reinterpret_cast<const char*>(data),
+              reinterpret_cast<const char*>(data) + input_byte);
+        }
+      } else {
         DLOG(ERROR) << "WebPIAppend error, status code: " << status;
         DeleteInternalDecoder();
         set_state(kError);
         return 0;
       }
-    } else {
-      animated_webp_image_->AppendChunk(data, input_byte);
     }
   }
 
diff --git a/src/cobalt/loader/image/webp_image_decoder.h b/src/cobalt/loader/image/webp_image_decoder.h
index f93cc4c..1ff4a0f 100644
--- a/src/cobalt/loader/image/webp_image_decoder.h
+++ b/src/cobalt/loader/image/webp_image_decoder.h
@@ -49,6 +49,8 @@
   WebPDecoderConfig config_;
   scoped_refptr<AnimatedWebPImage> animated_webp_image_;
   std::unique_ptr<render_tree::ImageData> decoded_image_data_;
+
+  std::string cached_uncompressed_data_;
 };
 
 }  // namespace image
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc
index bf5a097..6402f9d 100644
--- a/src/cobalt/loader/net_fetcher.cc
+++ b/src/cobalt/loader/net_fetcher.cc
@@ -28,7 +28,7 @@
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #define HANDLE_CORE_DUMP
 #include "base/lazy_instance.h"
-#include "starboard/ps4/core_dump_handler.h"
+#include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #endif  // OS_STARBOARD
 
diff --git a/src/cobalt/media/base/audio_decoder_config.h b/src/cobalt/media/base/audio_decoder_config.h
index 06c4b5a..5c15879 100644
--- a/src/cobalt/media/base/audio_decoder_config.h
+++ b/src/cobalt/media/base/audio_decoder_config.h
@@ -80,6 +80,9 @@
     return encryption_scheme_;
   }
 
+  void set_mime(const std::string& mime) { mime_ = mime; }
+  const std::string& mime() const { return mime_; }
+
  private:
   AudioCodec codec_;
   SampleFormat sample_format_;
@@ -99,6 +102,13 @@
   // as padding added during encoding.
   int codec_delay_;
 
+  // |mime_| contains the mime type string specified when creating the stream.
+  // For example, when the stream is created via MediaSource, it is the mime
+  // string passed to addSourceBuffer().  It can be an empty string when the
+  // appropriate mime string is unknown.  It is provided as an extra information
+  // and can be inconsistent with the other member variables.
+  std::string mime_;
+
   // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
   // generated copy constructor and assignment operator. Since the extra data is
   // typically small, the performance impact is minimal.
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 209e3ff..4e5af13 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -191,7 +191,7 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(audio_config.IsValidConfig());
 
-  LOG(INFO) << "New audio config -- " << audio_config_.AsHumanReadableString();
+  LOG(INFO) << "New audio config -- " << audio_config.AsHumanReadableString();
 
   audio_config_ = audio_config;
   audio_sample_info_ = MediaAudioConfigToSbMediaAudioSampleInfo(audio_config_);
@@ -202,7 +202,7 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(video_config.IsValidConfig());
 
-  LOG(INFO) << "New video config -- " << video_config_.AsHumanReadableString();
+  LOG(INFO) << "New video config -- " << video_config.AsHumanReadableString();
 
   video_config_ = video_config;
   video_sample_info_.frame_width =
@@ -557,8 +557,30 @@
   }
 #endif  // SB_API_VERSION >= 11
 
-  DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
   bool has_audio = audio_codec != kSbMediaAudioCodecNone;
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+  SbPlayerCreationParam creation_param = {};
+  creation_param.audio_mime =
+      audio_config_.IsValidConfig() ? audio_config_.mime().c_str() : "";
+  creation_param.video_mime =
+      video_config_.IsValidConfig() ? video_config_.mime().c_str() : "";
+  creation_param.drm_system = drm_system_;
+  creation_param.audio_sample_info = audio_sample_info_;
+  creation_param.video_sample_info = video_sample_info_;
+  creation_param.output_mode = output_mode_;
+  creation_param.max_video_capabilities = max_video_capabilities_.c_str();
+  DCHECK_EQ(SbPlayerGetPreferredOutputMode(&creation_param), output_mode_);
+  player_ = SbPlayerCreate(
+      window_, &creation_param, &StarboardPlayer::DeallocateSampleCB,
+      &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB,
+      &StarboardPlayer::PlayerErrorCB, this,
+      get_decode_target_graphics_context_provider_func_.Run());
+
+#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+  DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
   player_ = SbPlayerCreate(
       window_, video_codec, audio_codec,
 #if SB_API_VERSION < 10
@@ -576,6 +598,9 @@
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       this, output_mode_,
       get_decode_target_graphics_context_provider_func_.Run());
+
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
   DCHECK(SbPlayerIsValid(player_));
 
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
@@ -939,6 +964,28 @@
 // static
 SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
     bool prefer_decode_to_texture) const {
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+  SbPlayerCreationParam creation_param = {};
+  creation_param.audio_mime =
+      audio_config_.IsValidConfig() ? audio_config_.mime().c_str() : "";
+  creation_param.video_mime =
+      video_config_.IsValidConfig() ? video_config_.mime().c_str() : "";
+  creation_param.drm_system = drm_system_;
+  creation_param.audio_sample_info = audio_sample_info_;
+  creation_param.video_sample_info = video_sample_info_;
+  creation_param.max_video_capabilities = max_video_capabilities_.c_str();
+
+  // Try to choose |kSbPlayerOutputModeDecodeToTexture| when
+  // |prefer_decode_to_texture| is true.
+  if (prefer_decode_to_texture) {
+    creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
+  } else {
+    creation_param.output_mode = kSbPlayerOutputModePunchOut;
+  }
+  auto output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  CHECK_NE(kSbPlayerOutputModeInvalid, output_mode);
+  return output_mode;
+#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
   SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
 
 #if SB_API_VERSION >= 11
@@ -956,13 +1003,6 @@
     }
   }
 
-#if SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
-  auto output_mode = SbPlayerGetPreferredOutputMode(
-      &audio_sample_info_, &video_sample_info_, drm_system_,
-      max_video_capabilities_.c_str());
-  CHECK_NE(kSbPlayerOutputModeInvalid, output_mode);
-  return output_mode;
-#else   // SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
   if (SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, video_codec,
                                   drm_system_)) {
     return kSbPlayerOutputModePunchOut;
@@ -970,7 +1010,7 @@
   CHECK(SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture,
                                     video_codec, drm_system_));
   return kSbPlayerOutputModeDecodeToTexture;
-#endif  // SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/base/video_decoder_config.h b/src/cobalt/media/base/video_decoder_config.h
index ffcdb8a..ae8cf34 100644
--- a/src/cobalt/media/base/video_decoder_config.h
+++ b/src/cobalt/media/base/video_decoder_config.h
@@ -112,6 +112,9 @@
     return webm_color_metadata_;
   }
 
+  void set_mime(const std::string& mime) { mime_ = mime; }
+  const std::string& mime() const { return mime_; }
+
  private:
   VideoCodec codec_;
   VideoCodecProfile profile_;
@@ -131,6 +134,13 @@
 
   WebMColorMetadata webm_color_metadata_;
 
+  // |mime_| contains the mime type string specified when creating the stream.
+  // For example, when the stream is created via MediaSource, it is the mime
+  // string passed to addSourceBuffer().  It can be an empty string when the
+  // appropriate mime string is unknown.  It is provided as an extra information
+  // and can be inconsistent with the other member variables.
+  std::string mime_;
+
   // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
   // generated copy constructor and assignment operator. Since the extra data is
   // typically small, the performance impact is minimal.
diff --git a/src/cobalt/media/filters/chunk_demuxer.cc b/src/cobalt/media/filters/chunk_demuxer.cc
index bdc048d..e88c81d 100644
--- a/src/cobalt/media/filters/chunk_demuxer.cc
+++ b/src/cobalt/media/filters/chunk_demuxer.cc
@@ -16,6 +16,8 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/media/base/audio_decoder_config.h"
 #include "cobalt/media/base/bind_to_current_loop.h"
@@ -34,9 +36,44 @@
 namespace cobalt {
 namespace media {
 
-ChunkDemuxerStream::ChunkDemuxerStream(Type type, bool splice_frames_enabled,
+namespace {
+
+// Parse type and codecs from mime type. It will return "video/mp4" and
+// "avc1.42E01E, mp4a.40.2" for "video/mp4; codecs="avc1.42E01E, mp4a.40.2".
+// Note that this function does minimum validation as the media stack will check
+// the type and codecs strictly.
+bool ParseMimeType(const std::string& mime_type, std::string* type,
+                   std::string* codecs) {
+  DCHECK(type);
+  DCHECK(codecs);
+  static const char kCodecs[] = "codecs=";
+
+  // SplitString will also trim the results.
+  std::vector<std::string> tokens = ::base::SplitString(
+      mime_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  // The first one has to be mime type with delimiter '/' like 'video/mp4'.
+  if (tokens.size() < 2 || tokens[0].find('/') == tokens[0].npos) {
+    return false;
+  }
+  *type = tokens[0];
+  for (size_t i = 1; i < tokens.size(); ++i) {
+    if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) {
+      continue;
+    }
+    *codecs = tokens[i].substr(strlen("codecs="));
+    base::TrimString(*codecs, " \"", codecs);
+    break;
+  }
+  return !codecs->empty();
+}
+
+}  // namespace
+
+ChunkDemuxerStream::ChunkDemuxerStream(Type type, const std::string& mime,
+                                       bool splice_frames_enabled,
                                        MediaTrack::Id media_track_id)
     : type_(type),
+      mime_(mime),
       liveness_(DemuxerStream::LIVENESS_UNKNOWN),
       media_track_id_(media_track_id),
       state_(UNINITIALIZED),
@@ -272,13 +309,17 @@
 AudioDecoderConfig ChunkDemuxerStream::audio_decoder_config() {
   CHECK_EQ(type_, AUDIO);
   base::AutoLock auto_lock(lock_);
-  return stream_->GetCurrentAudioDecoderConfig();
+  auto config = stream_->GetCurrentAudioDecoderConfig();
+  config.set_mime(mime_);
+  return config;
 }
 
 VideoDecoderConfig ChunkDemuxerStream::video_decoder_config() {
   CHECK_EQ(type_, VIDEO);
   base::AutoLock auto_lock(lock_);
-  return stream_->GetCurrentVideoDecoderConfig();
+  auto config = stream_->GetCurrentVideoDecoderConfig();
+  config.set_mime(mime_);
+  return config;
 }
 
 bool ChunkDemuxerStream::SupportsConfigChanges() { return true; }
@@ -607,10 +648,16 @@
 }
 
 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
-                                         const std::string& type,
-                                         const std::string& codecs) {
-  DVLOG(1) << __func__ << " id=" << id << " mime_type=" << type
-           << " codecs=" << codecs;
+                                         const std::string& mime) {
+  DVLOG(1) << __func__ << " id=" << id << " mime_type=" << mime;
+
+  std::string type;
+  std::string codecs;
+
+  if (!ParseMimeType(mime, &type, &codecs)) {
+    return kNotSupported;
+  }
+
   base::AutoLock auto_lock(lock_);
 
   if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id))
@@ -632,8 +679,8 @@
 
   std::unique_ptr<SourceBufferState> source_state(new SourceBufferState(
       std::move(stream_parser), std::move(frame_processor),
-      base::Bind(&ChunkDemuxer::CreateDemuxerStream, base::Unretained(this),
-                 id),
+      base::Bind(&ChunkDemuxer::CreateDemuxerStream, base::Unretained(this), id,
+                 mime),
       media_log_, buffer_allocator_));
 
   SourceBufferState::NewTextTrackCB new_text_track_cb;
@@ -1195,7 +1242,8 @@
 }
 
 ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream(
-    const std::string& source_id, DemuxerStream::Type type) {
+    const std::string& source_id, const std::string& mime,
+    DemuxerStream::Type type) {
   // New ChunkDemuxerStreams can be created only during initialization segment
   // processing, which happens when a new chunk of data is appended and the
   // lock_ must be held by ChunkDemuxer::AppendData.
@@ -1223,8 +1271,8 @@
       return NULL;
   }
 
-  std::unique_ptr<ChunkDemuxerStream> stream(
-      new ChunkDemuxerStream(type, splice_frames_enabled_, media_track_id));
+  std::unique_ptr<ChunkDemuxerStream> stream(new ChunkDemuxerStream(
+      type, mime, splice_frames_enabled_, media_track_id));
   DCHECK(track_id_to_demux_stream_map_.find(media_track_id) ==
          track_id_to_demux_stream_map_.end());
   track_id_to_demux_stream_map_[media_track_id] = stream.get();
diff --git a/src/cobalt/media/filters/chunk_demuxer.h b/src/cobalt/media/filters/chunk_demuxer.h
index 574e82e..f0d2428 100644
--- a/src/cobalt/media/filters/chunk_demuxer.h
+++ b/src/cobalt/media/filters/chunk_demuxer.h
@@ -35,8 +35,8 @@
  public:
   typedef std::deque<scoped_refptr<StreamParserBuffer>> BufferQueue;
 
-  ChunkDemuxerStream(Type type, bool splice_frames_enabled,
-                     MediaTrack::Id media_track_id);
+  ChunkDemuxerStream(Type type, const std::string& mime,
+                     bool splice_frames_enabled, MediaTrack::Id media_track_id);
   ~ChunkDemuxerStream() override;
 
   // ChunkDemuxerStream control methods.
@@ -148,6 +148,8 @@
   // Specifies the type of the stream.
   Type type_;
 
+  const std::string mime_;
+
   Liveness liveness_;
 
   std::unique_ptr<SourceBufferStream> stream_;
@@ -212,15 +214,14 @@
   void StartWaitingForSeek(base::TimeDelta seek_time) override;
   void CancelPendingSeek(base::TimeDelta seek_time) override;
 
-  // Registers a new |id| to use for AppendData() calls. |type| indicates
+  // Registers a new |id| to use for AppendData() calls. |mime| indicates
   // the MIME type for the data that we intend to append for this ID.
   // kOk is returned if the demuxer has enough resources to support another ID
   //    and supports the format indicated by |type|.
   // kNotSupported is returned if |type| is not a supported format.
   // kReachedIdLimit is returned if the demuxer cannot handle another ID right
   //    now.
-  Status AddId(const std::string& id, const std::string& type,
-               const std::string& codecs);
+  Status AddId(const std::string& id, const std::string& mime);
 
   // Notifies a caller via |tracks_updated_cb| that the set of media tracks
   // for a given |id| has changed.
@@ -351,6 +352,7 @@
   // Returns a pointer to a new ChunkDemuxerStream instance, which is owned by
   // ChunkDemuxer.
   ChunkDemuxerStream* CreateDemuxerStream(const std::string& source_id,
+                                          const std::string& mime,
                                           DemuxerStream::Type type);
 
   void OnNewTextTrack(ChunkDemuxerStream* text_stream,
diff --git a/src/cobalt/media/sandbox/format_guesstimator.cc b/src/cobalt/media/sandbox/format_guesstimator.cc
index 06fe8a0..7dd939c 100644
--- a/src/cobalt/media/sandbox/format_guesstimator.cc
+++ b/src/cobalt/media/sandbox/format_guesstimator.cc
@@ -19,6 +19,8 @@
 
 #include "base/bind.h"
 #include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "cobalt/math/size.h"
 #include "cobalt/media/base/audio_codecs.h"
@@ -43,20 +45,18 @@
 
 namespace {
 
-// Container to organize the pairs of supported mime types and their codecs.
-struct SupportedTypeCodecInfo {
-  std::string mime;
-  std::string codecs;
-};
+// The possible mime type configurations that are supported by cobalt.
+const std::vector<std::string> kSupportedMimeTypes = {
+    "audio/mp4; codecs=\"ac-3\"",
+    "audio/mp4; codecs=\"ec-3\"",
+    "audio/mp4; codecs=\"mp4a.40.2\"",
+    "audio/webm; codecs=\"opus\"",
 
-// The possible mime and codec configurations that are supported by cobalt.
-const std::vector<SupportedTypeCodecInfo> kSupportedTypesAndCodecs = {
-    {"audio/mp4", "ac-3"},          {"audio/mp4", "ec-3"},
-    {"audio/mp4", "mp4a.40.2"},     {"audio/webm", "opus"},
-
-    {"video/mp4", "av01.0.05M.08"}, {"video/mp4", "avc1.640028, mp4a.40.2"},
-    {"video/mp4", "avc1.640028"},   {"video/mp4", "hvc1.1.6.H150.90"},
-    {"video/webm", "vp9"},
+    "video/mp4; codecs=\"av01.0.05M.08\"",
+    "video/mp4; codecs=\"avc1.640028, mp4a.40.2\"",
+    "video/mp4; codecs=\"avc1.640028\"",
+    "video/mp4; codecs=\"hvc1.1.6.H150.90\"",
+    "video/webm; codecs=\"vp9\"",
 };
 
 // Can be called as:
@@ -103,6 +103,28 @@
   SB_UNREFERENCED_PARAMETER(tracks);
 }
 
+// Extract the value of "codecs" parameter from content type. It will return
+// "avc1.42E01E" for "video/mp4; codecs="avc1.42E01E".
+// Note that this function assumes that the input is always valid and does
+// minimum validation..
+std::string ExtractCodec(const std::string& content_type) {
+  static const char kCodecs[] = "codecs=";
+
+  // SplitString will also trim the results.
+  std::vector<std::string> tokens = ::base::SplitString(
+      content_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  for (size_t i = 1; i < tokens.size(); ++i) {
+    if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) {
+      continue;
+    }
+    auto codec = tokens[i].substr(strlen("codecs="));
+    base::TrimString(codec, " \"", &codec);
+    return codec;
+  }
+  NOTREACHED();
+  return "";
+}
+
 }  // namespace
 
 FormatGuesstimator::FormatGuesstimator(const std::string& path_or_url,
@@ -126,17 +148,15 @@
     return;
   }
   progressive_url_ = url;
-  mime_ = "video/mp4";
-  codecs_ = "avc1.640028, mp4a.40.2";
+  mime_type_ = "video/mp4; codecs=\"avc1.640028, mp4a.40.2\"";
 }
 
 void FormatGuesstimator::InitializeAsAdaptive(const base::FilePath& path,
                                               MediaModule* media_module) {
   std::vector<uint8_t> header = ReadHeader(path);
 
-  for (const auto& expected_supported_info : kSupportedTypesAndCodecs) {
-    DCHECK(mime_.empty());
-    DCHECK(codecs_.empty());
+  for (const auto& expected_supported_mime_type : kSupportedMimeTypes) {
+    DCHECK(mime_type_.empty());
 
     ChunkDemuxer* chunk_demuxer = NULL;
     WebMediaPlayerHelper::ChunkDemuxerOpenCB open_cb = base::Bind(
@@ -158,8 +178,7 @@
     }
 
     const std::string id = "stream";
-    if (chunk_demuxer->AddId(id, expected_supported_info.mime,
-                             expected_supported_info.codecs) !=
+    if (chunk_demuxer->AddId(id, expected_supported_mime_type) !=
         ChunkDemuxer::kOk) {
       continue;
     }
@@ -183,11 +202,10 @@
             chunk_demuxer->GetStream(DemuxerStream::Type::AUDIO)) {
       const AudioDecoderConfig& decoder_config =
           demuxer_stream->audio_decoder_config();
-      if (StringToAudioCodec(expected_supported_info.codecs) ==
+      if (StringToAudioCodec(ExtractCodec(expected_supported_mime_type)) ==
           decoder_config.codec()) {
         adaptive_path_ = path;
-        mime_ = expected_supported_info.mime;
-        codecs_ = expected_supported_info.codecs;
+        mime_type_ = expected_supported_mime_type;
         break;
       }
       continue;
@@ -196,11 +214,10 @@
     DCHECK(demuxer_stream);
     const VideoDecoderConfig& decoder_config =
         demuxer_stream->video_decoder_config();
-    if (StringToVideoCodec(expected_supported_info.codecs) ==
+    if (StringToVideoCodec(ExtractCodec(expected_supported_mime_type)) ==
         decoder_config.codec()) {
       adaptive_path_ = path;
-      mime_ = expected_supported_info.mime;
-      codecs_ = expected_supported_info.codecs;
+      mime_type_ = expected_supported_mime_type;
       break;
     }
   }
diff --git a/src/cobalt/media/sandbox/format_guesstimator.h b/src/cobalt/media/sandbox/format_guesstimator.h
index dd95e9c..f9b25fb 100644
--- a/src/cobalt/media/sandbox/format_guesstimator.h
+++ b/src/cobalt/media/sandbox/format_guesstimator.h
@@ -38,7 +38,7 @@
   bool is_adaptive() const { return !adaptive_path_.empty(); }
   bool is_audio() const {
     DCHECK(is_adaptive());
-    return mime_.find("audio/") == 0;
+    return mime_type_.find("audio/") == 0;
   }
 
   const GURL& progressive_url() const {
@@ -53,13 +53,9 @@
     return adaptive_path_.value();
   }
 
-  const std::string& mime() const {
+  const std::string& mime_type() const {
     DCHECK(is_valid());
-    return mime_;
-  }
-  const std::string& codecs() const {
-    DCHECK(is_valid());
-    return codecs_;
+    return mime_type_;
   }
 
  private:
@@ -69,8 +65,7 @@
 
   GURL progressive_url_;
   base::FilePath adaptive_path_;
-  std::string mime_;
-  std::string codecs_;
+  std::string mime_type_;
 };
 
 }  // namespace sandbox
diff --git a/src/cobalt/media/sandbox/media2_sandbox.cc b/src/cobalt/media/sandbox/media2_sandbox.cc
index fdbb7df..0228744 100644
--- a/src/cobalt/media/sandbox/media2_sandbox.cc
+++ b/src/cobalt/media/sandbox/media2_sandbox.cc
@@ -118,15 +118,15 @@
   demuxer->Initialize(&demuxer_host, base::Bind(OnDemuxerStatus), false);
 
   ChunkDemuxer::Status status =
-      demuxer->AddId("audio", "audio/mp4", "mp4a.40.2");
+      demuxer->AddId("audio", "audio/mp4; codecs=\"mp4a.40.2\"");
   DCHECK_EQ(status, ChunkDemuxer::kOk);
 
   int video_url_length = SbStringGetLength(argv[2]);
   if (video_url_length > 5 &&
       SbStringCompare(argv[2] + video_url_length - 5, ".webm", 5) == 0) {
-    status = demuxer->AddId("video", "video/webm", "vp9");
+    status = demuxer->AddId("video", "video/webm; codecs=\"vp9\"");
   } else {
-    status = demuxer->AddId("video", "video/mp4", "avc1.640028");
+    status = demuxer->AddId("video", "video/mp4; codecs=\"avc1.640028\"");
   }
   DCHECK_EQ(status, ChunkDemuxer::kOk);
 
diff --git a/src/cobalt/media/sandbox/web_media_player_sandbox.cc b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
index f50e862..4b91af1 100644
--- a/src/cobalt/media/sandbox/web_media_player_sandbox.cc
+++ b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
@@ -90,8 +90,6 @@
   SbLogRaw(ss.str().c_str());
 }
 
-std::string MakeCodecParameter(const std::string& string) { return string; }
-
 void OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks) {
   SB_UNREFERENCED_PARAMETER(tracks);
 }
@@ -182,8 +180,7 @@
     LOG(INFO) << "Playing " << guesstimator.adaptive_path();
 
     std::string id = guesstimator.is_audio() ? kAudioId : kVideoId;
-    auto codecs = MakeCodecParameter(guesstimator.codecs());
-    auto status = chunk_demuxer_->AddId(id, guesstimator.mime(), codecs);
+    auto status = chunk_demuxer_->AddId(id, guesstimator.mime_type());
     CHECK_EQ(status, ChunkDemuxer::kOk);
 
     chunk_demuxer_->SetTracksWatcher(id, base::Bind(OnInitSegmentReceived));
@@ -234,13 +231,11 @@
     LOG(INFO) << "Playing " << audio_guesstimator.adaptive_path() << " and "
               << video_guesstimator.adaptive_path();
 
-    auto codecs = MakeCodecParameter(audio_guesstimator.codecs());
     auto status =
-        chunk_demuxer_->AddId(kAudioId, audio_guesstimator.mime(), codecs);
+        chunk_demuxer_->AddId(kAudioId, audio_guesstimator.mime_type());
     CHECK_EQ(status, ChunkDemuxer::kOk);
 
-    codecs = MakeCodecParameter(video_guesstimator.codecs());
-    status = chunk_demuxer_->AddId(kVideoId, video_guesstimator.mime(), codecs);
+    status = chunk_demuxer_->AddId(kVideoId, video_guesstimator.mime_type());
     CHECK_EQ(status, ChunkDemuxer::kOk);
 
     chunk_demuxer_->SetTracksWatcher(kAudioId,
diff --git a/src/cobalt/network/url_request_context.cc b/src/cobalt/network/url_request_context.cc
index 7b532ae..671de56 100644
--- a/src/cobalt/network/url_request_context.cc
+++ b/src/cobalt/network/url_request_context.cc
@@ -37,6 +37,7 @@
 #include "net/http/http_transaction_factory.h"
 #include "net/proxy_resolution/proxy_config.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
+#include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/ssl/ssl_config_service.h"
 #include "net/ssl/ssl_config_service_defaults.h"
 #include "net/url_request/data_protocol_handler.h"
@@ -93,6 +94,10 @@
               new ProxyConfigService(proxy_config)),
           net_log));
 
+  // ack decimation significantly increases download bandwidth on low-end
+  // android devices.
+  SetQuicFlag(&FLAGS_quic_reloadable_flag_quic_enable_ack_decimation, true);
+
   net::HostResolver::Options options;
   options.max_concurrent_resolves = net::HostResolver::kDefaultParallelism;
   options.max_retry_attempts = net::HostResolver::kDefaultRetryAttempts;
diff --git a/src/cobalt/renderer/backend/backend.gyp b/src/cobalt/renderer/backend/backend.gyp
index edf8da0..3b012e0 100644
--- a/src/cobalt/renderer/backend/backend.gyp
+++ b/src/cobalt/renderer/backend/backend.gyp
@@ -28,6 +28,13 @@
         '<(DEPTH)/cobalt/math/math.gyp:math',
         '<(DEPTH)/cobalt/renderer/backend/starboard/platform_backend.gyp:renderer_platform_backend',
       ],
+      'conditions': [
+        ['enable_map_to_mesh != -1', {
+          'defines' : [
+            'ENABLE_MAP_TO_MESH=<(enable_map_to_mesh)',
+          ],
+        }],
+      ],
     },
     {
       'target_name': 'graphics_system_test',
diff --git a/src/cobalt/renderer/backend/blitter/display.cc b/src/cobalt/renderer/backend/blitter/display.cc
index 2c98837..21e4883 100644
--- a/src/cobalt/renderer/backend/blitter/display.cc
+++ b/src/cobalt/renderer/backend/blitter/display.cc
@@ -19,7 +19,7 @@
 #include "cobalt/renderer/backend/blitter/render_target.h"
 #include "cobalt/system_window/system_window.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -97,5 +97,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/backend/blitter/display.h b/src/cobalt/renderer/backend/blitter/display.h
index f48b7b5..634d437 100644
--- a/src/cobalt/renderer/backend/blitter/display.h
+++ b/src/cobalt/renderer/backend/blitter/display.h
@@ -21,7 +21,7 @@
 #include "cobalt/renderer/backend/graphics_system.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -44,7 +44,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_BACKEND_BLITTER_DISPLAY_H_
diff --git a/src/cobalt/renderer/backend/blitter/graphics_context.cc b/src/cobalt/renderer/backend/blitter/graphics_context.cc
index a762d69..3b24772 100644
--- a/src/cobalt/renderer/backend/blitter/graphics_context.cc
+++ b/src/cobalt/renderer/backend/blitter/graphics_context.cc
@@ -23,7 +23,7 @@
 #include "cobalt/renderer/backend/blitter/surface_render_target.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -90,5 +90,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/backend/blitter/graphics_context.h b/src/cobalt/renderer/backend/blitter/graphics_context.h
index 4dee93f..6fc9fc6 100644
--- a/src/cobalt/renderer/backend/blitter/graphics_context.h
+++ b/src/cobalt/renderer/backend/blitter/graphics_context.h
@@ -23,7 +23,7 @@
 #include "cobalt/renderer/backend/graphics_context.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -52,7 +52,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_BACKEND_BLITTER_GRAPHICS_CONTEXT_H_
diff --git a/src/cobalt/renderer/backend/blitter/graphics_system.cc b/src/cobalt/renderer/backend/blitter/graphics_system.cc
index 96938b3..ede2ab1 100644
--- a/src/cobalt/renderer/backend/blitter/graphics_system.cc
+++ b/src/cobalt/renderer/backend/blitter/graphics_system.cc
@@ -19,7 +19,7 @@
 #include "cobalt/renderer/backend/blitter/display.h"
 #include "cobalt/renderer/backend/blitter/graphics_context.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -47,5 +47,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/backend/blitter/graphics_system.h b/src/cobalt/renderer/backend/blitter/graphics_system.h
index 636f676..2f76b88 100644
--- a/src/cobalt/renderer/backend/blitter/graphics_system.h
+++ b/src/cobalt/renderer/backend/blitter/graphics_system.h
@@ -22,7 +22,7 @@
 #include "starboard/blitter.h"
 #include "starboard/common/optional.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -50,7 +50,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_BACKEND_BLITTER_GRAPHICS_SYSTEM_H_
diff --git a/src/cobalt/renderer/backend/blitter/render_target.h b/src/cobalt/renderer/backend/blitter/render_target.h
index 2f2db2d..bbcc8d5 100644
--- a/src/cobalt/renderer/backend/blitter/render_target.h
+++ b/src/cobalt/renderer/backend/blitter/render_target.h
@@ -18,7 +18,7 @@
 #include "cobalt/renderer/backend/render_target.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -42,7 +42,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_BACKEND_BLITTER_RENDER_TARGET_H_
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.cc b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
index 6cb8bab..b88ce46 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.cc
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
@@ -16,7 +16,7 @@
 
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -59,5 +59,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.h b/src/cobalt/renderer/backend/blitter/surface_render_target.h
index 920e948..b424cf5 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.h
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.h
@@ -19,7 +19,7 @@
 #include "cobalt/renderer/backend/blitter/render_target.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -62,7 +62,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_BACKEND_BLITTER_SURFACE_RENDER_TARGET_H_
diff --git a/src/cobalt/renderer/backend/graphics_context.cc b/src/cobalt/renderer/backend/graphics_context.cc
index 4008b32..7ab9ff3 100644
--- a/src/cobalt/renderer/backend/graphics_context.cc
+++ b/src/cobalt/renderer/backend/graphics_context.cc
@@ -15,6 +15,7 @@
 #include "cobalt/renderer/backend/graphics_context.h"
 
 #include "base/logging.h"
+#include "starboard/gles.h"
 #include "starboard/string.h"
 
 namespace cobalt {
@@ -62,6 +63,43 @@
   return -1.0f;
 }
 
+bool GraphicsContext::IsMapToMeshEnabled(
+    const GraphicsContext* graphics_context) {
+  const CobaltExtensionGraphicsApi* graphics_ext =
+      graphics_context ? graphics_context->graphics_extension_ : nullptr;
+#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#if defined(ENABLE_MAP_TO_MESH)
+#error \
+    "ENABLE_MAP_TO_MESH is deprecated after Starboard version 12, use \
+the Cobalt graphics extension function IsMapToMeshEnabled() instead."
+#endif  // defined(ENABLE_MAP_TO_MESH)
+  if (graphics_ext && graphics_ext->version >= 3) {
+    return graphics_ext->IsMapToMeshEnabled();
+  }
+
+  // If there is a callable gles interface, assume map to mesh is enabled, as
+  // it is for most platforms.
+  return SbGetGlesInterface() != nullptr;
+#else  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#if defined(ENABLE_MAP_TO_MESH)
+  if (graphics_ext && graphics_ext->version >= 3) {
+    DLOG(ERROR)
+        << "ENABLE_MAP_TO_MESH and "
+           "CobaltExtensionGraphicsApi::IsMapToMeshEnabled() are both defined. "
+           "Remove 'enable_map_to_mesh' from your \"gyp_configuration.gypi\" "
+           "file in favor of using IsMapToMeshEnabled().";
+  }
+  return static_cast<bool>(ENABLE_MAP_TO_MESH);
+#endif
+
+  if (graphics_ext && graphics_ext->version >= 3) {
+    return graphics_ext->IsMapToMeshEnabled();
+  }
+
+  return false;
+#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+}
+
 }  // namespace backend
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/backend/graphics_context.h b/src/cobalt/renderer/backend/graphics_context.h
index ead28c3..aaf4569 100644
--- a/src/cobalt/renderer/backend/graphics_context.h
+++ b/src/cobalt/renderer/backend/graphics_context.h
@@ -94,6 +94,9 @@
   // Note: Return a negative number if no value is specified by the platform.
   virtual float GetMinimumFrameIntervalInMilliseconds();
 
+  // Get whether the renderer should support 360 degree video or not.
+  static bool IsMapToMeshEnabled(const GraphicsContext* graphics_context);
+
  private:
   GraphicsSystem* system_;
 
diff --git a/src/cobalt/renderer/backend/starboard/default_graphics_system.cc b/src/cobalt/renderer/backend/starboard/default_graphics_system.cc
index c561115..f193937 100644
--- a/src/cobalt/renderer/backend/starboard/default_graphics_system.cc
+++ b/src/cobalt/renderer/backend/starboard/default_graphics_system.cc
@@ -34,9 +34,6 @@
   if (SbGetGlesInterface()) {
     return std::unique_ptr<GraphicsSystem>(
         new GraphicsSystemEGL(system_window));
-  } else if (SbBlitterIsBlitterSupported()) {
-    SB_UNREFERENCED_PARAMETER(system_window);
-    return std::unique_ptr<GraphicsSystem>(new GraphicsSystemBlitter());
   } else {
     SB_UNREFERENCED_PARAMETER(system_window);
     return std::unique_ptr<GraphicsSystem>(new GraphicsSystemStub());
@@ -44,7 +41,7 @@
 #else  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
 #if SB_HAS(GLES2)
   return std::unique_ptr<GraphicsSystem>(new GraphicsSystemEGL(system_window));
-#elif SB_HAS(BLITTER)
+#elif SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
   SB_UNREFERENCED_PARAMETER(system_window);
   return std::unique_ptr<GraphicsSystem>(new GraphicsSystemBlitter());
 #else
diff --git a/src/cobalt/renderer/get_default_rasterizer_for_platform.cc b/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
index 879e38a..6e9ca04 100644
--- a/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
+++ b/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
@@ -80,7 +80,7 @@
 #endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
         // SB_HAS(GLES2)
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 std::unique_ptr<rasterizer::Rasterizer> CreateBlitterSoftwareRasterizer(
     backend::GraphicsContext* graphics_context,
     const RendererModule::Options& options) {
@@ -100,8 +100,7 @@
           options.software_surface_cache_size_in_bytes,
           options.purge_skia_font_caches_on_destruction));
 }
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 }  // namespace
 
@@ -115,11 +114,9 @@
 #else
     return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
 #endif
-  } else if (SbBlitterIsBlitterSupported()) {
-    return {"blitter", base::Bind(&CreateBlitterHardwareRasterizer)};
   } else {
     SB_LOG(ERROR)
-        << "Either GLES2 or the Starboard Blitter API must be available.";
+        << "GLES2 must be available.";
     SB_DCHECK(false);
     return {};
   }
@@ -130,7 +127,7 @@
 #else
   return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
 #endif
-#elif SB_HAS(BLITTER)
+#elif SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
   return {"blitter", base::Bind(&CreateBlitterHardwareRasterizer)};
 #else
 #error "Either GLES2 or the Starboard Blitter API must be available."
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 50e58cf..833afb8 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -207,6 +207,12 @@
   return rasterizer_->GetResourceProvider();
 }
 
+bool Pipeline::IsMapToMeshEnabled(const Pipeline* pipeline) {
+  backend::GraphicsContext* graphics_context =
+      pipeline ? pipeline->graphics_context_ : nullptr;
+  return backend::GraphicsContext::IsMapToMeshEnabled(graphics_context);
+}
+
 void Pipeline::Submit(const Submission& render_tree_submission) {
   TRACE_EVENT0("cobalt::renderer", "Pipeline::Submit()");
 
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 4fc3bf0..bbb9658 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -115,6 +115,8 @@
   // subsequently submitted to this pipeline.
   render_tree::ResourceProvider* GetResourceProvider();
 
+  static bool IsMapToMeshEnabled(const Pipeline* pipeline);
+
  private:
   // All private data members should be accessed only on the rasterizer thread,
   // with the exception of rasterizer_thread_ itself through which messages
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
index 0b8b31b..23e6eaa 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
@@ -23,7 +23,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -256,5 +256,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
index a3540ed..20e85a4 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
@@ -25,7 +25,7 @@
 #include "net/base/linked_hash_map.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -150,7 +150,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
index 018dadf..3c24a26 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
+++ b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
@@ -19,7 +19,7 @@
 #include "cobalt/math/rect_f.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -39,7 +39,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_COBALT_BLITTER_CONVERSIONS_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index f0e3f37..2200e63 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -33,7 +33,7 @@
 #include "cobalt/debug/console/command_manager.h"
 #endif
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -308,5 +308,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
index 4f2b7de..34b39d1 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
@@ -24,7 +24,7 @@
 #include "cobalt/renderer/rasterizer/blitter/scratch_surface_cache.h"
 #include "cobalt/renderer/rasterizer/rasterizer.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -65,7 +65,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_HARDWARE_RASTERIZER_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc
index d8856f4..cdb6471 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -24,7 +24,7 @@
 #include "cobalt/renderer/rasterizer/skia/image.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -171,5 +171,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.h b/src/cobalt/renderer/rasterizer/blitter/image.h
index a0f1357..9768782 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.h
+++ b/src/cobalt/renderer/rasterizer/blitter/image.h
@@ -30,7 +30,7 @@
 #include "cobalt/renderer/rasterizer/skia/image.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -130,7 +130,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_IMAGE_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
index f02688e..37a053d 100644
--- a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
@@ -29,7 +29,7 @@
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace {
 
@@ -410,5 +410,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
index 49d7b7b..e78b3ad 100644
--- a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
@@ -21,7 +21,7 @@
 #include "cobalt/renderer/rasterizer/blitter/render_state.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -38,7 +38,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.cc b/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.cc
index daa4e46..c986753 100644
--- a/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.cc
@@ -21,7 +21,7 @@
 #include "cobalt/render_tree/brush.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -53,5 +53,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.h b/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.h
index 0425d7e..e3e0b31 100644
--- a/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.h
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache.h
@@ -22,7 +22,7 @@
 #include "cobalt/render_tree/brush.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -85,7 +85,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_CACHE_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_state.cc b/src/cobalt/renderer/rasterizer/blitter/render_state.cc
index bdf92d0..314cd82 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_state.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_state.cc
@@ -16,7 +16,7 @@
 
 #include "cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -55,5 +55,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_state.h b/src/cobalt/renderer/rasterizer/blitter/render_state.h
index 2ffe022..eb3d488 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_state.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_state.h
@@ -26,7 +26,7 @@
 #include "cobalt/render_tree/brush.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -144,7 +144,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_RENDER_STATE_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.cc
index 6feb80a..e1451de 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.cc
@@ -17,7 +17,7 @@
 #include "cobalt/render_tree/image.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -52,5 +52,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h b/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h
index a0910a9..4ab5577 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h
@@ -18,7 +18,7 @@
 #include "cobalt/render_tree/image.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -35,7 +35,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_RENDER_TREE_BLITTER_CONVERSIONS_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
index 6cc626e..296ff78 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -33,7 +33,7 @@
 #include "cobalt/renderer/rasterizer/common/utils.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 // This define exists so that developers can quickly toggle it temporarily and
 // obtain trace results for the render tree visit process here.  In general
@@ -582,5 +582,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
index 8a341b2..a35e804 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
@@ -41,7 +41,7 @@
 #include "starboard/blitter.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 
@@ -126,7 +126,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_RENDER_TREE_NODE_VISITOR_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 5142d9f..56f77ff 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -20,7 +20,7 @@
 #include "cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -205,5 +205,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index 89dad0a..1e736b9 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -27,7 +27,7 @@
 #include "starboard/blitter.h"
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -130,7 +130,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_RESOURCE_PROVIDER_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc b/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc
index bef9902..3f694af 100644
--- a/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.cc
@@ -17,7 +17,7 @@
 #include "cobalt/base/polymorphic_downcast.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -95,5 +95,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.h b/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.h
index 0272bee..fa513ce 100644
--- a/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.h
+++ b/src/cobalt/renderer/rasterizer/blitter/scratch_surface_cache.h
@@ -19,7 +19,7 @@
 #include "cobalt/renderer/rasterizer/common/scratch_surface_cache.h"
 #include "starboard/blitter.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -82,7 +82,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_SCRATCH_SURFACE_CACHE_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.cc b/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.cc
index d184ab9..9cf9879 100644
--- a/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.cc
@@ -16,7 +16,7 @@
 
 #include "base/logging.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -40,5 +40,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h b/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h
index dd40a7d..a172df5 100644
--- a/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h
+++ b/src/cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h
@@ -18,7 +18,7 @@
 #include "starboard/blitter.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 namespace cobalt {
 namespace renderer {
@@ -34,7 +34,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_SKIA_BLITTER_CONVERSIONS_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
index a9a721f..a439cc2 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
@@ -14,7 +14,7 @@
 
 #include "cobalt/renderer/rasterizer/blitter/software_rasterizer.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #include "cobalt/renderer/backend/graphics_system.h"
 #include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
@@ -106,5 +106,4 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
index f53c403..b1214e2 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
@@ -17,7 +17,7 @@
 
 #include "starboard/configuration.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #include "base/memory/ref_counted.h"
 #include "cobalt/render_tree/resource_provider.h"
@@ -59,7 +59,6 @@
 }  // namespace renderer
 }  // namespace cobalt
 
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // COBALT_RENDERER_RASTERIZER_BLITTER_SOFTWARE_RASTERIZER_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index b86cedf..96b39b3 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -116,9 +116,6 @@
       graphics_context_(
           base::polymorphic_downcast<backend::GraphicsContextEGL*>(
               graphics_context)) {
-  DLOG(INFO) << "offscreen_target_cache_size_in_bytes: "
-             << offscreen_target_cache_size_in_bytes;
-
   backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
       graphics_context_);
   graphics_state_.reset(new GraphicsState());
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
index f541018..6b6826b 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
@@ -20,6 +20,7 @@
 #include <algorithm>
 #include <memory>
 #include <unordered_map>
+#include <vector>
 
 #include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -376,8 +377,6 @@
 }
 
 void OffscreenTargetManager::InitializeTargets(const math::Size& frame_size) {
-  DLOG(INFO) << "offscreen render target memory limit: " << memory_limit_;
-
   if (frame_size.width() >= 64 && frame_size.height() >= 64) {
     offscreen_target_size_mask_.SetSize(
         NextPowerOf2(frame_size.width() / 64) - 1,
@@ -430,8 +429,8 @@
   } else if (num_atlases > kMaxAtlases) {
     DCHECK(atlas_size == max_size);
     num_atlases = kMaxAtlases;
-    DLOG(WARNING) << "More memory was allotted for offscreen render targets"
-                  << " than will be used.";
+    DLOG_ONCE(WARNING) << "More memory was allotted for offscreen render"
+                       << " targets than will be used.";
   }
   offscreen_cache_ = CreateOffscreenAtlas(atlas_size, true);
   CHECK(offscreen_cache_);
@@ -441,9 +440,6 @@
     CHECK(offscreen_atlases_.back());
   }
 
-  DLOG(INFO) << "Created " << num_atlases << " offscreen atlases of size "
-             << atlas_size.width() << " x " << atlas_size.height();
-
   // Create 1D texture atlases. These are just regular 2D textures that will
   // be used as 1D row textures. These atlases are not intended to be used by
   // skia.
@@ -456,9 +452,6 @@
   CHECK(offscreen_atlases_1d_.back());
   offscreen_cache_1d_ = CreateOffscreenAtlas(atlas_size_1d, false);
   CHECK(offscreen_cache_1d_);
-  DLOG(INFO) << "Created " << offscreen_atlases_1d_.size() + 1
-             << " offscreen atlases of size " << atlas_size_1d.width() << " x "
-             << atlas_size_1d.height();
 }
 
 std::unique_ptr<OffscreenTargetManager::OffscreenAtlas>
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 97e679e..361bdfd 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -3890,7 +3890,7 @@
 }
 #endif  // !SB_HAS(BLITTER)
 
-#if defined(ENABLE_MAP_TO_MESH)
+#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || ENABLE_MAP_TO_MESH
 
 namespace {
 scoped_refptr<Mesh> CreateCubeMesh(ResourceProvider* resource_provider) {
@@ -3972,6 +3972,11 @@
 }  // namespace
 
 TEST_F(PixelTest, MapToMeshRGBTest) {
+  if (!IsMapToMeshEnabled()) {
+    SB_LOG(INFO) << "Map to mesh not supported. Test skipped.";
+    return;
+  }
+
   // Tests that MapToMesh filter works as expected with an RGBA texture.
   scoped_refptr<Image> image =
       CreateColoredCheckersImage(GetResourceProvider(), Size(200, 200));
@@ -4008,7 +4013,8 @@
   TestTree(CreateMapToMeshTestRenderTree(GetResourceProvider(), image));
 }
 
-#endif  // defined(ENABLE_MAP_TO_MESH)
+#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
+        // ENABLE_MAP_TO_MESH
 
 TEST_F(PixelTest, DrawNullImage) {
   // An ImageNode with no source is legal, though it should result in nothing
diff --git a/src/cobalt/renderer/rasterizer/pixel_test_fixture.h b/src/cobalt/renderer/rasterizer/pixel_test_fixture.h
index 279bfcf..7a10100 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test_fixture.h
+++ b/src/cobalt/renderer/rasterizer/pixel_test_fixture.h
@@ -55,6 +55,10 @@
   static backend::GraphicsSystem* graphics_system_;
   static backend::GraphicsContext* graphics_context_;
 
+  bool IsMapToMeshEnabled() {
+    return pixel_tester_->IsMapToMeshEnabled();
+  }
+
  private:
   base::Optional<RenderTreePixelTester> pixel_tester_;
   math::Size output_surface_size_;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 391bc47..419137c 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -612,10 +612,6 @@
       force_deterministic_rendering_(force_deterministic_rendering) {
   TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::Impl::Impl()");
 
-  DLOG(INFO) << "skia_cache_size_in_bytes: " << skia_cache_size_in_bytes;
-  DLOG(INFO) << "scratch_surface_cache_size_in_bytes: "
-             << scratch_surface_cache_size_in_bytes;
-
   graphics_context_->MakeCurrent();
 
   GrContextOptions context_options;
@@ -656,7 +652,6 @@
 
   int max_surface_size = std::max(gr_context_->caps()->maxRenderTargetSize(),
                                   gr_context_->caps()->maxTextureSize());
-  DLOG(INFO) << "Max renderer surface size: " << max_surface_size;
 }
 
 HardwareRasterizer::Impl::~Impl() {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
index 0b5b0b9..d28ee88 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
@@ -21,6 +21,8 @@
 #include "SkStream.h"
 #include "SkString.h"
 #include "SkTSearch.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h"
@@ -280,6 +282,25 @@
     SkTDArray<FontFamilyInfo*>* config_font_families,
     PriorityStyleSetArrayMap* priority_fallback_families) {
   TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::BuildNameToFamilyMap()");
+
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  SkFontStyleSet_Cobalt::FontFormatSetting font_format =
+      SkFontStyleSet_Cobalt::kTtf;
+  if (command_line->HasSwitch(switches::kFontFormat)) {
+    std::string setting =
+        command_line->GetSwitchValueASCII(switches::kFontFormat);
+    if (setting.compare("woff2") == 0) {
+      font_format = SkFontStyleSet_Cobalt::kWoff2;
+    } else if (setting.compare("woff2-preferred") == 0) {
+      font_format = SkFontStyleSet_Cobalt::kWoff2Preferred;
+    } else if (setting.compare("ttf-preferred") == 0) {
+      font_format = SkFontStyleSet_Cobalt::kTtfPreferred;
+    } else if (setting.compare("ttf") != 0) {
+      LOG(WARNING) << "Invalid setting specified for font format. "
+                   << "Using default TTF.";
+    }
+  }
+
   for (int i = 0; i < config_font_families->count(); i++) {
     FontFamilyInfo& family_info = *(*config_font_families)[i];
     bool is_named_family = family_info.names.count() > 0;
@@ -293,7 +314,7 @@
 
     sk_sp<SkFontStyleSet_Cobalt> new_family(new SkFontStyleSet_Cobalt(
         family_info, font_files_directory, &local_typeface_stream_manager_,
-        &family_mutex_));
+        &family_mutex_, font_format));
 
     // Do not add the family if none of its fonts were available. This allows
     // the configuration files to specify a superset of all fonts, and ones that
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
index 4123ada..f186517 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
@@ -24,6 +24,7 @@
 #include "base/trace_event/trace_event.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFreeType_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+#include "starboard/common/string.h"
 #include "third_party/skia/src/utils/SkOSPath.h"
 
 namespace {
@@ -46,7 +47,7 @@
   // The 'closer' to the target weight, the higher the score.
   // 1000 is the 'heaviest' recognized weight
   if (pattern.weight() == candidate.weight()) {
-    score += 1000;
+    score += 1001;
   } else if (pattern.weight() <= 500) {
     if (400 <= pattern.weight() && pattern.weight() < 450) {
       if (450 <= candidate.weight() && candidate.weight() <= 500) {
@@ -74,7 +75,7 @@
 SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt(
     const FontFamilyInfo& family_info, const char* base_path,
     SkFileMemoryChunkStreamManager* const local_typeface_stream_manager,
-    SkMutex* const manager_owned_mutex)
+    SkMutex* const manager_owned_mutex, FontFormatSetting font_format_setting)
     : local_typeface_stream_manager_(local_typeface_stream_manager),
       manager_owned_mutex_(manager_owned_mutex),
       is_fallback_family_(family_info.is_fallback_family),
@@ -90,6 +91,7 @@
   }
 
   family_name_ = family_info.names[0];
+  SkTHashMap<SkString, int> styles_index_map;
 
   for (int i = 0; i < family_info.fonts.count(); ++i) {
     const FontFileInfo& font_file = family_info.fonts[i];
@@ -103,6 +105,25 @@
       continue;
     }
 
+    auto file_name = font_file.file_name.c_str();
+    auto extension = strrchr(file_name, '.');
+    if (!extension) {
+      continue;
+    }
+    ++extension;
+
+    // Only add font formats that match the format setting, except ttc for CJK.
+    if (font_format_setting == kTtf) {
+      if (SbStringCompareNoCase("ttf", extension) != 0 &&
+          SbStringCompareNoCase(extension, "ttc") != 0) {
+        continue;
+      }
+    } else if (font_format_setting == kWoff2 &&
+               SbStringCompareNoCase("woff2", extension) != 0 &&
+               SbStringCompareNoCase(extension, "ttc") != 0) {
+      continue;
+    }
+
     SkFontStyle style(font_file.weight, SkFontStyle::kNormal_Width,
                       font_file.style == FontFileInfo::kItalic_FontStyle
                           ? SkFontStyle::kItalic_Slant
@@ -119,9 +140,32 @@
                                     font_file.postscript_name.size());
     }
 
-    styles_.push_back().reset(new SkFontStyleSetEntry_Cobalt(
+    SkString font_name;
+    if (full_font_name.empty()) {
+      // If full font name is missing, use file name.
+      size_t name_len = font_file.file_name.size() - strlen(extension) - 1;
+      font_name = SkString(file_name, name_len);
+    } else {
+      font_name = SkString(full_font_name.c_str());
+    }
+    auto font = new SkFontStyleSetEntry_Cobalt(
         file_path, font_file.index, style, full_font_name, postscript_name,
-        font_file.disable_synthetic_bolding));
+        font_file.disable_synthetic_bolding);
+    int* index = styles_index_map.find(font_name);
+    if (index != NULL) {
+      // If style with name already exists in family, replace it.
+      if (font_format_setting == kTtfPreferred &&
+          SbStringCompareNoCase("ttf", extension) == 0) {
+        styles_[*index].reset(font);
+      } else if (font_format_setting == kWoff2Preferred &&
+                 SbStringCompareNoCase("woff2", extension) == 0) {
+        styles_[*index].reset(font);
+      }
+    } else {
+      int count = styles_.count();
+      styles_index_map.set(font_name, count);
+      styles_.push_back().reset(font);
+    }
   }
 }
 
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
index 3cfc112..f01c8c1 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
@@ -20,7 +20,7 @@
 #include "SkFontMgr.h"
 #include "SkStream.h"
 #include "SkString.h"
-#include "SkTArray.h"
+#include "SkTHash.h"
 #include "SkTypeface.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
@@ -76,10 +76,18 @@
     sk_sp<SkTypeface> typeface;
   };
 
+  enum FontFormatSetting {
+    kTtf,
+    kWoff2,
+    kTtfPreferred,
+    kWoff2Preferred,
+  };
+
   SkFontStyleSet_Cobalt(
       const FontFamilyInfo& family_info, const char* base_path,
       SkFileMemoryChunkStreamManager* const local_typeface_stream_manager,
-      SkMutex* const manager_owned_mutex);
+      SkMutex* const manager_owned_mutex,
+      FontFormatSetting font_format_setting);
 
   // From SkFontStyleSet
   int count() override;
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.h b/src/cobalt/renderer/render_tree_pixel_tester.h
index 976a4ca..6d0eaab 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.h
+++ b/src/cobalt/renderer/render_tree_pixel_tester.h
@@ -35,6 +35,10 @@
 // provided on each call to TestTree().
 class RenderTreePixelTester {
  public:
+  bool IsMapToMeshEnabled() {
+    return backend::GraphicsContext::IsMapToMeshEnabled(graphics_context_);
+  }
+
   struct Options {
     Options();
 
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index 8447cd5..491a894 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -110,8 +110,10 @@
         'renderer_copy_test_data',
       ],
       'conditions': [
-        ['enable_map_to_mesh == 1', {
-          'defines' : ['ENABLE_MAP_TO_MESH'],
+        ['enable_map_to_mesh != -1', {
+          'defines' : [
+            'ENABLE_MAP_TO_MESH=<(enable_map_to_mesh)',
+          ],
         }],
       ],
       'includes': [ '<(DEPTH)/cobalt/test/test.gypi' ],
diff --git a/src/cobalt/script/script_runner.cc b/src/cobalt/script/script_runner.cc
index 949618a..883b646 100644
--- a/src/cobalt/script/script_runner.cc
+++ b/src/cobalt/script/script_runner.cc
@@ -24,7 +24,7 @@
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #define HANDLE_CORE_DUMP
 #include "base/lazy_instance.h"
-#include "starboard/ps4/core_dump_handler.h"
+#include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #endif  // defined(OS_STARBOARD)
 
diff --git a/src/cobalt/script/v8c/isolate_fellowship.cc b/src/cobalt/script/v8c/isolate_fellowship.cc
index 4906dc4..0afcf0b 100644
--- a/src/cobalt/script/v8c/isolate_fellowship.cc
+++ b/src/cobalt/script/v8c/isolate_fellowship.cc
@@ -24,6 +24,7 @@
 #include "cobalt/script/v8c/v8c_tracing_controller.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/file.h"
+#include "starboard/memory.h"
 
 namespace cobalt {
 namespace script {
@@ -44,7 +45,10 @@
                                      "--noexpose_wasm",
                                      "--novalidate_asm",
 #if defined(COBALT_GC_ZEAL)
-                                     "--gc_interval=1200"
+                                     "--gc_interval=1200",
+#endif
+#if !defined(ENGINE_SUPPORTS_JIT)
+                                     "--jitless"
 #endif
 };
 
@@ -109,7 +113,8 @@
     // If there is no cache directory, then just save the startup data in
     // memory.
     LOG(WARNING) << "Unable to read/write V8 startup snapshot data to file.";
-    startup_data = v8::V8::CreateSnapshotDataBlob();
+    startup_data = v8::SnapshotCreator().CreateBlob(
+        v8::SnapshotCreator::FunctionCodeHandling::kKeep);
     return;
   }
 
@@ -194,7 +199,8 @@
   // to write it.
   if (!read_file) {
     ([&]() {
-      startup_data = v8::V8::CreateSnapshotDataBlob();
+      startup_data = v8::SnapshotCreator().CreateBlob(
+          v8::SnapshotCreator::FunctionCodeHandling::kKeep);
       if (startup_data.data == nullptr) {
         // Trust the V8 API, but verify.  |raw_size| should also be 0.
         DCHECK_EQ(startup_data.raw_size, 0);
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp
index e570dcb..2c8d692 100644
--- a/src/cobalt/script/v8c/v8c.gyp
+++ b/src/cobalt/script/v8c/v8c.gyp
@@ -83,7 +83,6 @@
       ],
       'defines': [
         'ENGINE_SUPPORTS_INT64',
-        'ENGINE_SUPPORTS_JIT',
         # The file name to store our V8 startup snapshot file at.  This is a
         # serialized representation of a |v8::Isolate| after completing all
         # tasks prior to creation of the global object (e.g., executing self
@@ -100,11 +99,9 @@
           'ENGINE_SUPPORTS_INT64',
         ],
       },
-      # V8 always requires JIT.  |cobalt_enable_jit| must be set to true to
-      # use V8.  Failure to do will result in a build failure.
       'conditions' :[
-        ['cobalt_enable_jit == 0', {
-          'defines': [ '<(gyp_static_assert_false)', ],
+        ['cobalt_enable_jit == 1', {
+          'defines': ['ENGINE_SUPPORTS_JIT',],
         }],
       ],
     },
diff --git a/src/cobalt/script/v8c/v8c_global_environment.cc b/src/cobalt/script/v8c/v8c_global_environment.cc
index c23949f..6400fe9 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.cc
+++ b/src/cobalt/script/v8c/v8c_global_environment.cc
@@ -348,9 +348,10 @@
     isolate_->VisitHandlesWithClassIds(&force_weak_visitor);
   }
 
-  isolate_->SetEmbedderHeapTracer(nullptr);
+  V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->DisableForShutdown();
   isolate_->SetData(kIsolateDataIndex, nullptr);
   isolate_->LowMemoryNotification();
+  isolate_->SetEmbedderHeapTracer(nullptr);
 }
 
 // static
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.cc b/src/cobalt/script/v8c/v8c_heap_tracer.cc
index c3e9bf5..362d0cd 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.cc
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.cc
@@ -42,6 +42,9 @@
 void V8cHeapTracer::TracePrologue() {
   TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TracePrologue");
 
+  if (disabled_) {
+    return;
+  }
   DCHECK_EQ(frontier_.size(), 0);
   DCHECK_EQ(visited_.size(), 0);
 
@@ -58,6 +61,9 @@
 bool V8cHeapTracer::AdvanceTracing(double deadline_in_ms) {
   TRACE_EVENT0("cobalt::script", "V8cHeapTracer::AdvanceTracing");
 
+  if (disabled_) {
+    return true;
+  }
   double start_time = platform_->MonotonicallyIncreasingTime();
   while (platform_->MonotonicallyIncreasingTime() - start_time <
          deadline_in_ms) {
@@ -92,11 +98,19 @@
   return true;
 }
 
-bool V8cHeapTracer::IsTracingDone() { return frontier_.empty(); }
+bool V8cHeapTracer::IsTracingDone() {
+  if (disabled_) {
+    return true;
+  }
+  return frontier_.empty();
+}
 
 void V8cHeapTracer::TraceEpilogue() {
   TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TraceEpilogue");
 
+  if (disabled_) {
+    return;
+  }
   DCHECK(frontier_.empty());
   visited_.clear();
 }
@@ -106,6 +120,7 @@
 }
 
 void V8cHeapTracer::Trace(Traceable* traceable) {
+  DCHECK(!disabled_);
   MaybeAddToFrontier(traceable);
 }
 
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.h b/src/cobalt/script/v8c/v8c_heap_tracer.h
index 152227c..ef1f4ae 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.h
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.h
@@ -64,6 +64,12 @@
   void AddRoot(Traceable* traceable);
   void RemoveRoot(Traceable* traceable);
 
+  // Used during shutdown to ask V8cHeapTracer do nothing so that V8 can
+  // GC every embedder-created object.
+  void DisableForShutdown() {
+    disabled_ = true;
+  }
+
  private:
   void MaybeAddToFrontier(Traceable* traceable);
 
@@ -79,6 +85,8 @@
   // TODO: A "counted" multiset approach here would be a bit nicer than
   // std::multiset.
   std::unordered_multiset<Traceable*> roots_;
+
+  bool disabled_ = false;
 };
 
 }  // namespace v8c
diff --git a/src/cobalt/site/docs/reference/starboard/modules/drm.md b/src/cobalt/site/docs/reference/starboard/modules/drm.md
index b76b672..230ea16 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/drm.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/drm.md
@@ -305,30 +305,6 @@
 void SbDrmGenerateSessionUpdateRequest(SbDrmSystem drm_system, int ticket, const char *type, const void *initialization_data, int initialization_data_size)
 ```
 
-### SbDrmGetKeyCount ###
-
-Returns the number of keys installed in `drm_system`.
-
-`drm_system`: The system for which the number of installed keys is retrieved.
-
-#### Declaration ####
-
-```
-int SbDrmGetKeyCount(SbDrmSystem drm_system)
-```
-
-### SbDrmGetKeyStatus ###
-
-Gets `out_key`, `out_key_size`, and `out_status` for the key with `index` in
-`drm_system`. Returns whether a key is installed at `index`. If not, the output
-parameters, which all must not be NULL, will not be modified.
-
-#### Declaration ####
-
-```
-bool SbDrmGetKeyStatus(SbDrmSystem drm_system, const void *session_id, int session_id_size, int index, void **out_key, int *out_key_size, SbDrmKeyStatus *out_status)
-```
-
 ### SbDrmIsServerCertificateUpdatable ###
 
 Returns true if server certificate of `drm_system` can be updated via
@@ -343,19 +319,6 @@
 bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system)
 ```
 
-### SbDrmRemoveAllKeys ###
-
-Removes all installed keys for `drm_system`. Any outstanding session update
-requests are also invalidated.
-
-`drm_system`: The DRM system for which keys should be removed.
-
-#### Declaration ####
-
-```
-void SbDrmRemoveAllKeys(SbDrmSystem drm_system)
-```
-
 ### SbDrmSystemIsValid ###
 
 Indicates whether `drm_system` is a valid SbDrmSystem.
diff --git a/src/cobalt/ui_navigation/interface.cc b/src/cobalt/ui_navigation/interface.cc
index d2b0532..2b96bab 100644
--- a/src/cobalt/ui_navigation/interface.cc
+++ b/src/cobalt/ui_navigation/interface.cc
@@ -52,6 +52,11 @@
   SB_UNREFERENCED_PARAMETER(enabled);
 }
 
+void SetItemDir(NativeItem item, NativeItemDir dir) {
+  SB_UNREFERENCED_PARAMETER(item);
+  SB_UNREFERENCED_PARAMETER(dir);
+}
+
 void SetItemSize(NativeItem item, float width, float height) {
   SB_UNREFERENCED_PARAMETER(item);
   SB_UNREFERENCED_PARAMETER(width);
@@ -116,6 +121,7 @@
   interface.destroy_item = &DestroyItem;
   interface.set_focus = &SetFocus;
   interface.set_item_enabled = &SetItemEnabled;
+  interface.set_item_dir = &SetItemDir;
   interface.set_item_size = &SetItemSize;
   interface.set_item_transform = &SetItemTransform;
   interface.get_item_focus_transform = &GetItemFocusTransform;
diff --git a/src/cobalt/ui_navigation/interface.h b/src/cobalt/ui_navigation/interface.h
index 703f532..9ca7fa9 100644
--- a/src/cobalt/ui_navigation/interface.h
+++ b/src/cobalt/ui_navigation/interface.h
@@ -28,6 +28,7 @@
 using NativeItemType = SbUiNavItemType;
 constexpr NativeItemType kNativeItemTypeFocus = kSbUiNavItemTypeFocus;
 constexpr NativeItemType kNativeItemTypeContainer = kSbUiNavItemTypeContainer;
+using NativeItemDir = SbUiNavItemDir;
 using NativeMatrix2x3 = SbUiNavMatrix2x3;
 using NativeMatrix4 = SbUiNavMatrix4;
 using NativeCallbacks = SbUiNavCallbacks;
@@ -44,6 +45,11 @@
   kNativeItemTypeContainer,
 };
 
+struct NativeItemDir {
+  bool is_left_to_right;
+  bool is_top_to_bottom;
+};
+
 struct NativeMatrix2x3 {
   float m[6];
 };
@@ -65,6 +71,7 @@
   void (*destroy_item)(NativeItem item);
   void (*set_focus)(NativeItem item);
   void (*set_item_enabled)(NativeItem item, bool enabled);
+  void (*set_item_dir)(NativeItem item, NativeItemDir dir);
   void (*set_item_size)(NativeItem item, float width, float height);
   void (*set_item_transform)(NativeItem item, const NativeMatrix2x3* transform);
   bool (*get_item_focus_transform)(NativeItem item,
diff --git a/src/cobalt/ui_navigation/nav_item.h b/src/cobalt/ui_navigation/nav_item.h
index ea3b259..64d6aac 100644
--- a/src/cobalt/ui_navigation/nav_item.h
+++ b/src/cobalt/ui_navigation/nav_item.h
@@ -46,6 +46,10 @@
     GetInterface().set_item_enabled(nav_item_, enabled);
   }
 
+  void SetDir(NativeItemDir dir) {
+    GetInterface().set_item_dir(nav_item_, dir);
+  }
+
   void SetSize(float width, float height) {
     GetInterface().set_item_size(nav_item_, width, height);
   }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
index a811521..4e1586d 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
@@ -546,7 +546,12 @@
         Log.e(TAG, "Failed to provision device during MediaCrypto creation.");
         return false;
       }
-      return true;
+      try {
+        mMediaCryptoSession = openSession();
+      } catch (NotProvisionedException e2) {
+        Log.e(TAG, "Device still not provisioned after supposedly successful provisioning", e2);
+        return false;
+      }
     }
 
     if (mMediaCryptoSession == null) {
diff --git a/src/starboard/shared/stub/blitter_destroy_device.cc b/src/starboard/android/arm64/vulkan/atomic_public.h
similarity index 65%
rename from src/starboard/shared/stub/blitter_destroy_device.cc
rename to src/starboard/android/arm64/vulkan/atomic_public.h
index 599dc99..ae8ac7f 100644
--- a/src/starboard/shared/stub/blitter_destroy_device.cc
+++ b/src/starboard/android/arm64/vulkan/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,12 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/blitter.h"
+#ifndef STARBOARD_ANDROID_ARM64_VULKAN_ATOMIC_PUBLIC_H_
+#define STARBOARD_ANDROID_ARM64_VULKAN_ATOMIC_PUBLIC_H_
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#include "starboard/android/shared/atomic_public.h"
 
-bool SbBlitterDestroyDevice(SbBlitterDevice device) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#endif  // STARBOARD_ANDROID_ARM64_VULKAN_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/shared/stub/blitter_destroy_device.cc b/src/starboard/android/arm64/vulkan/configuration_public.h
similarity index 63%
copy from src/starboard/shared/stub/blitter_destroy_device.cc
copy to src/starboard/android/arm64/vulkan/configuration_public.h
index 599dc99..61313f6 100644
--- a/src/starboard/shared/stub/blitter_destroy_device.cc
+++ b/src/starboard/android/arm64/vulkan/configuration_public.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,12 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/blitter.h"
+#ifndef STARBOARD_ANDROID_ARM64_VULKAN_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_ANDROID_ARM64_VULKAN_CONFIGURATION_PUBLIC_H_
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#include "starboard/android/shared/configuration_public.h"
 
-bool SbBlitterDestroyDevice(SbBlitterDevice device) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#endif  // STARBOARD_ANDROID_ARM64_VULKAN_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/android/arm64/vulkan/gyp_configuration.gypi b/src/starboard/android/arm64/vulkan/gyp_configuration.gypi
new file mode 100644
index 0000000..f37376b
--- /dev/null
+++ b/src/starboard/android/arm64/vulkan/gyp_configuration.gypi
@@ -0,0 +1,46 @@
+# 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.
+
+{
+  'variables': {
+    'target_arch': 'arm64',
+    'arm_version': 8,
+    'armv7': 0,
+    'arm_thumb': 0,
+    'arm_neon': 0,
+    'arm_fpu': 'vfpv3-d16',
+  },
+
+  'target_defaults': {
+    'default_configuration': 'android-arm64-vulkan_debug',
+    'configurations': {
+      'android-arm64-vulkan_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'android-arm64-vulkan_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'android-arm64-vulkan_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'android-arm64-vulkan_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '<(DEPTH)/starboard/android/shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/android/arm64/vulkan/gyp_configuration.py b/src/starboard/android/arm64/vulkan/gyp_configuration.py
new file mode 100644
index 0000000..8fb4bb5
--- /dev/null
+++ b/src/starboard/android/arm64/vulkan/gyp_configuration.py
@@ -0,0 +1,23 @@
+# 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.
+"""Starboard Android ARM-64 Vulkan platform build configuration."""
+
+from starboard.android.shared import gyp_configuration as shared_configuration
+
+
+def CreatePlatformConfig():
+  return shared_configuration.AndroidConfiguration(
+      'android-arm64-vulkan',
+      'arm64-v8a',
+      sabi_json_path='starboard/sabi/arm64/sabi.json')
diff --git a/src/starboard/android/arm64/vulkan/starboard_platform.gyp b/src/starboard/android/arm64/vulkan/starboard_platform.gyp
new file mode 100644
index 0000000..d605723
--- /dev/null
+++ b/src/starboard/android/arm64/vulkan/starboard_platform.gyp
@@ -0,0 +1,19 @@
+# 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.
+
+{
+  'includes': [
+    '<(DEPTH)/starboard/android/shared/starboard_platform.gypi',
+  ],
+}
diff --git a/src/starboard/android/arm64/vulkan/starboard_platform_tests.gyp b/src/starboard/android/arm64/vulkan/starboard_platform_tests.gyp
new file mode 100644
index 0000000..ab226cd
--- /dev/null
+++ b/src/starboard/android/arm64/vulkan/starboard_platform_tests.gyp
@@ -0,0 +1,19 @@
+# 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.
+
+{
+  'includes': [
+    '<(DEPTH)/starboard/android/shared/starboard_platform_tests.gypi',
+  ],
+}
diff --git a/src/starboard/shared/stub/blitter_destroy_device.cc b/src/starboard/android/arm64/vulkan/thread_types_public.h
similarity index 64%
copy from src/starboard/shared/stub/blitter_destroy_device.cc
copy to src/starboard/android/arm64/vulkan/thread_types_public.h
index 599dc99..6d980f7 100644
--- a/src/starboard/shared/stub/blitter_destroy_device.cc
+++ b/src/starboard/android/arm64/vulkan/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,12 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/blitter.h"
+#ifndef STARBOARD_ANDROID_ARM64_VULKAN_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_ANDROID_ARM64_VULKAN_THREAD_TYPES_PUBLIC_H_
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#include "starboard/android/shared/thread_types_public.h"
 
-bool SbBlitterDestroyDevice(SbBlitterDevice device) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+#endif  // STARBOARD_ANDROID_ARM64_VULKAN_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/android/shared/audio_track_audio_sink_type.cc b/src/starboard/android/shared/audio_track_audio_sink_type.cc
index 363d4f9..25dbb62 100644
--- a/src/starboard/android/shared/audio_track_audio_sink_type.cc
+++ b/src/starboard/android/shared/audio_track_audio_sink_type.cc
@@ -389,14 +389,11 @@
     SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
     SbAudioSinkConsumeFramesFunc consume_frames_func,
     void* context) {
-  // Try to create AudioTrack with the same size buffer as in renderer. But
-  // AudioTrack would not start playing until the buffer is fully filled once. A
-  // large buffer may cause AudioTrack not able to start. And Cobalt now write
-  // no more than 1s of audio data and no more than 0.5 ahead to starboard
-  // player, limit the buffer size to store at most 0.5s of audio data.
+  int min_required_frames = SbAudioSinkGetMinBufferSizeInFrames(
+      channels, audio_sample_type, sampling_frequency_hz);
+  SB_DCHECK(frames_per_channel >= min_required_frames);
   int preferred_buffer_size_in_bytes =
-      std::min(frames_per_channel, sampling_frequency_hz / 2) * channels *
-      GetSampleSize(audio_sample_type);
+      min_required_frames * channels * GetSampleSize(audio_sample_type);
   AudioTrackAudioSink* audio_sink = new AudioTrackAudioSink(
       this, channels, sampling_frequency_hz, audio_sample_type, frame_buffers,
       frames_per_channel, preferred_buffer_size_in_bytes,
diff --git a/src/starboard/android/shared/cobalt/configuration.gypi b/src/starboard/android/shared/cobalt/configuration.gypi
index 27b99cd..ef81612 100644
--- a/src/starboard/android/shared/cobalt/configuration.gypi
+++ b/src/starboard/android/shared/cobalt/configuration.gypi
@@ -20,7 +20,6 @@
 
     'custom_media_session_client': 1,
     'enable_account_manager': 1,
-    'enable_map_to_mesh': 1,
 
     'cobalt_enable_quic': 0,
 
diff --git a/src/starboard/android/shared/configuration_constants.cc b/src/starboard/android/shared/configuration_constants.cc
index 044f286..42a8676 100644
--- a/src/starboard/android/shared/configuration_constants.cc
+++ b/src/starboard/android/shared/configuration_constants.cc
@@ -52,6 +52,9 @@
 // Allow ac3 and ec3 support
 const bool kSbHasAc3Audio = false;
 
+// Specifies whether this platform updates audio frames asynchronously.
+const bool kSbHasAsyncAudioFramesReporting = true;
+
 // Allow playing audioless video.
 const bool kSbHasAudiolessVideo = false;
 
diff --git a/src/starboard/android/shared/configuration_public.h b/src/starboard/android/shared/configuration_public.h
index 41d3327..27a6b7f 100644
--- a/src/starboard/android/shared/configuration_public.h
+++ b/src/starboard/android/shared/configuration_public.h
@@ -177,15 +177,6 @@
 // Whether the current platform has speech synthesis.
 #define SB_HAS_SPEECH_SYNTHESIS 1
 
-// --- Media Configuration ---------------------------------------------------
-
-// Specifies whether this platform updates audio frames asynchronously.  In such
-// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
-// indicate the absolute time that the consumed audio frames are reported.
-// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
-// details.
-#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 1
-
 // --- Decoder-only Params ---
 
 // --- Memory Configuration --------------------------------------------------
diff --git a/src/starboard/android/shared/player_components_impl.cc b/src/starboard/android/shared/player_components_impl.cc
index b25de7a..d7f1eaa 100644
--- a/src/starboard/android/shared/player_components_impl.cc
+++ b/src/starboard/android/shared/player_components_impl.cc
@@ -110,15 +110,6 @@
     video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
     return true;
   }
-
-  void GetAudioRendererParams(int* max_cached_frames,
-                              int* max_frames_per_append) const override {
-    SB_DCHECK(max_cached_frames);
-    SB_DCHECK(max_frames_per_append);
-
-    *max_cached_frames = 128 * 1024;
-    *max_frames_per_append = 16384;
-  }
 };
 
 }  // namespace
diff --git a/src/starboard/android/shared/player_create.cc b/src/starboard/android/shared/player_create.cc
index ba6f92f..8f919dd 100644
--- a/src/starboard/android/shared/player_create.cc
+++ b/src/starboard/android/shared/player_create.cc
@@ -30,21 +30,38 @@
     UpdateActiveSessionPlatformPlaybackState;
 
 SbPlayer SbPlayerCreate(SbWindow window,
-                        SbMediaVideoCodec video_codec,
-                        SbMediaAudioCodec audio_codec,
-                        SbDrmSystem drm_system,
-                        const SbMediaAudioSampleInfo* audio_sample_info,
-                        const char* max_video_capabilities,
+                        const SbPlayerCreationParam* creation_param,
                         SbPlayerDeallocateSampleFunc sample_deallocate_func,
                         SbPlayerDecoderStatusFunc decoder_status_func,
                         SbPlayerStatusFunc player_status_func,
                         SbPlayerErrorFunc player_error_func,
                         void* context,
-                        SbPlayerOutputMode output_mode,
                         SbDecodeTargetGraphicsContextProvider* provider) {
   SB_UNREFERENCED_PARAMETER(window);
-  SB_UNREFERENCED_PARAMETER(max_video_capabilities);
-  SB_UNREFERENCED_PARAMETER(provider);
+
+  if (!creation_param) {
+    SB_LOG(ERROR) << "CreationParam cannot be null.";
+    return kSbPlayerInvalid;
+  }
+
+  if (!creation_param->audio_mime) {
+    SB_LOG(ERROR) << "creation_param->audio_mime cannot be null.";
+    return kSbPlayerInvalid;
+  }
+  if (!creation_param->video_mime) {
+    SB_LOG(ERROR) << "creation_param->video_mime cannot be null.";
+    return kSbPlayerInvalid;
+  }
+  if (!creation_param->max_video_capabilities) {
+    SB_LOG(ERROR) << "creation_param->max_video_capabilities cannot be null.";
+    return kSbPlayerInvalid;
+  }
+
+  SB_LOG(INFO) << "SbPlayerCreate() called with audio mime \""
+               << creation_param->audio_mime << "\", video mime \""
+               << creation_param->video_mime
+               << "\", and max video capabilities \""
+               << creation_param->max_video_capabilities << "\".";
 
   if (!sample_deallocate_func || !decoder_status_func || !player_status_func
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -54,6 +71,9 @@
     return kSbPlayerInvalid;
   }
 
+  auto audio_codec = creation_param->audio_sample_info.codec;
+  auto video_codec = creation_param->video_sample_info.codec;
+
   if (audio_codec != kSbMediaAudioCodecNone &&
       audio_codec != kSbMediaAudioCodecAac &&
       audio_codec != kSbMediaAudioCodecOpus) {
@@ -61,13 +81,6 @@
     return kSbPlayerInvalid;
   }
 
-  if (audio_codec == kSbMediaAudioCodecAac && !audio_sample_info) {
-    SB_LOG(ERROR)
-        << "SbPlayerCreate() requires a non-NULL SbMediaAudioSampleInfo "
-        << "when |audio_codec| is not kSbMediaAudioCodecNone";
-    return kSbPlayerInvalid;
-  }
-
   if (video_codec != kSbMediaVideoCodecNone &&
       video_codec != kSbMediaVideoCodecH264 &&
       video_codec != kSbMediaVideoCodecH265 &&
@@ -84,7 +97,8 @@
     return kSbPlayerInvalid;
   }
 
-  if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) {
+  auto output_mode = creation_param->output_mode;
+  if (SbPlayerGetPreferredOutputMode(creation_param) != output_mode) {
     SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
     return kSbPlayerInvalid;
   }
@@ -98,13 +112,13 @@
   UpdateActiveSessionPlatformPlaybackState(kPlaying);
 
   starboard::scoped_ptr<PlayerWorker::Handler> handler(
-      new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
-                                         audio_sample_info, output_mode,
-                                         provider));
+      new FilterBasedPlayerWorkerHandler(
+          video_codec, audio_codec, creation_param->drm_system,
+          &creation_param->audio_sample_info, output_mode, 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, &creation_param->audio_sample_info,
+      sample_deallocate_func, decoder_status_func, player_status_func,
+      player_error_func, context, handler.Pass());
 
   // TODO: accomplish this through more direct means.
   // Set the bounds to initialize the VideoSurfaceView. The initial values don't
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 49e74b9..213def6 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -18,7 +18,6 @@
   },
   'includes': [
     '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
-    '<(DEPTH)/starboard/stub/blitter_stub_sources.gypi',
   ],
   'targets': [
     {
@@ -50,7 +49,6 @@
         '<(DEPTH)/starboard/android/shared/bionic',
       ],
       'sources': [
-        '<@(blitter_stub_sources)',
         '<@(filter_based_player_sources)',
         'accessibility_get_caption_settings.cc',
         'accessibility_get_display_settings.cc',
@@ -371,7 +369,6 @@
         '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
         '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
         '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_message.cc',
         '<(DEPTH)/starboard/shared/starboard/log_mutex.cc',
         '<(DEPTH)/starboard/shared/starboard/log_mutex.h',
         '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
@@ -417,7 +414,6 @@
         '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
diff --git a/src/starboard/audio_sink.h b/src/starboard/audio_sink.h
index c0655b8..7bb5805 100644
--- a/src/starboard/audio_sink.h
+++ b/src/starboard/audio_sink.h
@@ -63,16 +63,21 @@
 
 // Callback used to report frames consumed.  The consumed frames will be
 // removed from the source frame buffer to free space for new audio frames.
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 // When |frames_consumed| is updated asynchnously and the last time that it has
 // been updated is known, it can be passed in |frames_consumed_at| so the audio
 // time calculating can be more accurate.
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-typedef void (*SbAudioSinkConsumeFramesFunc)(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                             SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                             void* context);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+typedef void (*SbAudioSinkConsumeFramesFunc)(
+    int frames_consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    SbTime frames_consumed_at,
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    void* context);
 
 // Well-defined value for an invalid audio sink.
 #define kSbAudioSinkInvalid ((SbAudioSink)NULL)
diff --git a/src/starboard/blitter.h b/src/starboard/blitter.h
index bdbc88c..24ebad9 100644
--- a/src/starboard/blitter.h
+++ b/src/starboard/blitter.h
@@ -55,7 +55,7 @@
 #include "starboard/types.h"
 #include "starboard/window.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #ifdef __cplusplus
 extern "C" {
@@ -304,11 +304,6 @@
   return context != kSbBlitterInvalidContext;
 }
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-// Returns whether the platform supports blitter.
-SB_EXPORT bool SbBlitterIsBlitterSupported();
-#endif
-
 // Creates and returns an |SbBlitterDevice| based on the Blitter API
 // implementation's decision of which device should be the default. The returned
 // |SbBlitterDevice| represents a connection to a device (like a GPU).
@@ -323,7 +318,7 @@
 // This function is thread-safe.
 //
 // Returns |kSbBlitterInvalidDevice| on failure.
-SB_EXPORT SbBlitterDevice SbBlitterCreateDefaultDevice();
+SB_DEPRECATED(SB_EXPORT SbBlitterDevice SbBlitterCreateDefaultDevice());
 
 // Destroys |device|, cleaning up all resources associated with it.
 // This function is thread-safe, but it should not be called if |device| is
@@ -332,7 +327,7 @@
 // The return value indicates whether the destruction succeeded.
 //
 // |device|: The SbBlitterDevice object to be destroyed.
-SB_EXPORT bool SbBlitterDestroyDevice(SbBlitterDevice device);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterDestroyDevice(SbBlitterDevice device));
 
 // Creates and returns an |SbBlitterSwapChain| that can then be used to send
 // graphics to the display. This function links |device| to |window|'s output,
@@ -343,8 +338,9 @@
 // to create |window|.
 //
 // Returns |kSbBlitterInvalidSwapChain| on failure.
-SB_EXPORT SbBlitterSwapChain
-SbBlitterCreateSwapChainFromWindow(SbBlitterDevice device, SbWindow window);
+SB_DEPRECATED(SB_EXPORT SbBlitterSwapChain
+                  SbBlitterCreateSwapChainFromWindow(SbBlitterDevice device,
+                                                     SbWindow window));
 
 // Destroys |swap_chain|, cleaning up all resources associated with it.
 // This function is not thread-safe and must be called on the same thread
@@ -353,7 +349,8 @@
 // The return value indicates whether the destruction succeeded.
 //
 // |swap_chain|: The SbBlitterSwapChain to be destroyed.
-SB_EXPORT bool SbBlitterDestroySwapChain(SbBlitterSwapChain swap_chain);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterDestroySwapChain(SbBlitterSwapChain swap_chain));
 
 // Returns the |SbBlitterRenderTarget| object that is owned by |swap_chain|.
 // The returned object can be used to provide a target to blitter draw calls
@@ -363,8 +360,9 @@
 //
 // |swap_chain|: The SbBlitterSwapChain for which the target object is being
 // retrieved.
-SB_EXPORT SbBlitterRenderTarget
-SbBlitterGetRenderTargetFromSwapChain(SbBlitterSwapChain swap_chain);
+SB_DEPRECATED(
+    SB_EXPORT SbBlitterRenderTarget
+        SbBlitterGetRenderTargetFromSwapChain(SbBlitterSwapChain swap_chain));
 
 // Indicates whether |device| supports calls to |SbBlitterCreatePixelData|
 // with the specified |pixel_format|. This function is thread-safe.
@@ -372,9 +370,9 @@
 // |device|: The device for which compatibility is being checked.
 // |pixel_format|: The SbBlitterPixelDataFormat for which compatibility is
 // being checked.
-SB_EXPORT bool SbBlitterIsPixelFormatSupportedByPixelData(
+SB_DEPRECATED(SB_EXPORT bool SbBlitterIsPixelFormatSupportedByPixelData(
     SbBlitterDevice device,
-    SbBlitterPixelDataFormat pixel_format);
+    SbBlitterPixelDataFormat pixel_format));
 
 // Allocates an |SbBlitterPixelData| object through |device| with |width|,
 // |height| and |pixel_format|. |pixel_format| must be supported by |device|
@@ -390,11 +388,11 @@
 // |SbBlitterDestroyPixelData()|.
 //
 // Returns |kSbBlitterInvalidPixelData| upon failure.
-SB_EXPORT SbBlitterPixelData
-SbBlitterCreatePixelData(SbBlitterDevice device,
-                         int width,
-                         int height,
-                         SbBlitterPixelDataFormat pixel_format);
+SB_DEPRECATED(SB_EXPORT SbBlitterPixelData SbBlitterCreatePixelData(
+    SbBlitterDevice device,
+    int width,
+    int height,
+    SbBlitterPixelDataFormat pixel_format));
 
 // Destroys |pixel_data|. Note that this function does not need to be called
 // and should not be called if |SbBlitterCreateSurfaceFromPixelData()| has been
@@ -405,7 +403,8 @@
 // The return value indicates whether the destruction succeeded.
 //
 // |pixel_data|: The object to be destroyed.
-SB_EXPORT bool SbBlitterDestroyPixelData(SbBlitterPixelData pixel_data);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterDestroyPixelData(SbBlitterPixelData pixel_data));
 
 // Retrieves the pitch (in bytes) for |pixel_data|. This indicates the number of
 // bytes per row of pixel data in the image.
@@ -415,7 +414,8 @@
 // Returns |-1| in the event of an error.
 //
 // |pixel_data|: The object for which you are retrieving the pitch.
-SB_EXPORT int SbBlitterGetPixelDataPitchInBytes(SbBlitterPixelData pixel_data);
+SB_DEPRECATED(SB_EXPORT int SbBlitterGetPixelDataPitchInBytes(
+    SbBlitterPixelData pixel_data));
 
 // Retrieves a CPU-accessible pointer to the pixel data represented by
 // |pixel_data|. This pixel data can be modified by the CPU to initialize it
@@ -429,7 +429,8 @@
 // This function is not thread-safe.
 //
 // Returns |NULL| in the event of an error.
-SB_EXPORT void* SbBlitterGetPixelDataPointer(SbBlitterPixelData pixel_data);
+SB_DEPRECATED(SB_EXPORT void* SbBlitterGetPixelDataPointer(
+    SbBlitterPixelData pixel_data));
 
 // Creates an |SbBlitterSurface| object on |device|. Note that |device| must
 // match the device that was used to create the |SbBlitterPixelData| object
@@ -446,9 +447,9 @@
 // should not be modified on another thread while this function is called.
 //
 // Returns |kSbBlitterInvalidSurface| in the event of an error.
-SB_EXPORT SbBlitterSurface
-SbBlitterCreateSurfaceFromPixelData(SbBlitterDevice device,
-                                    SbBlitterPixelData pixel_data);
+SB_DEPRECATED(SB_EXPORT SbBlitterSurface SbBlitterCreateSurfaceFromPixelData(
+    SbBlitterDevice device,
+    SbBlitterPixelData pixel_data));
 
 // Indicates whether the |device| supports calls to
 // |SbBlitterCreateRenderTargetSurface()| with |surface_format|.
@@ -457,9 +458,10 @@
 //
 // |device|: The device being checked for compatibility.
 // |surface_format|: The surface format being checked for compatibility.
-SB_EXPORT bool SbBlitterIsSurfaceFormatSupportedByRenderTargetSurface(
-    SbBlitterDevice device,
-    SbBlitterSurfaceFormat surface_format);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterIsSurfaceFormatSupportedByRenderTargetSurface(
+        SbBlitterDevice device,
+        SbBlitterSurfaceFormat surface_format));
 
 // Creates a new surface with undefined pixel data on |device| with the
 // specified |width|, |height| and |surface_format|. One can set the pixel data
@@ -469,11 +471,11 @@
 // This function is thread-safe.
 //
 // Returns |kSbBlitterInvalidSurface| upon failure.
-SB_EXPORT SbBlitterSurface
-SbBlitterCreateRenderTargetSurface(SbBlitterDevice device,
-                                   int width,
-                                   int height,
-                                   SbBlitterSurfaceFormat surface_format);
+SB_DEPRECATED(SB_EXPORT SbBlitterSurface SbBlitterCreateRenderTargetSurface(
+    SbBlitterDevice device,
+    int width,
+    int height,
+    SbBlitterSurfaceFormat surface_format));
 
 // Destroys the |surface| object, cleaning up all resources associated with it.
 //
@@ -482,7 +484,7 @@
 // The return value indicates whether the destruction succeeded.
 //
 // |surface|: The object to be destroyed.
-SB_EXPORT bool SbBlitterDestroySurface(SbBlitterSurface surface);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterDestroySurface(SbBlitterSurface surface));
 
 // Returns the |SbBlitterRenderTarget| object owned by |surface|.  The returned
 // object can be used as a target for draw calls.
@@ -491,8 +493,9 @@
 // able to provide a render target or on any other error.
 //
 // This function is not thread-safe.
-SB_EXPORT SbBlitterRenderTarget
-SbBlitterGetRenderTargetFromSurface(SbBlitterSurface surface);
+SB_DEPRECATED(
+    SB_EXPORT SbBlitterRenderTarget
+        SbBlitterGetRenderTargetFromSurface(SbBlitterSurface surface));
 
 // Retrieves an |SbBlitterSurfaceInfo| structure, which describes immutable
 // parameters of the |surface|, such as its width, height and pixel format.
@@ -503,8 +506,9 @@
 // successfully.
 //
 // This function is not thread-safe.
-SB_EXPORT bool SbBlitterGetSurfaceInfo(SbBlitterSurface surface,
-                                       SbBlitterSurfaceInfo* surface_info);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterGetSurfaceInfo(SbBlitterSurface surface,
+                                           SbBlitterSurfaceInfo* surface_info));
 
 // Indicates whether the combination of parameter values is valid for calls
 // to |SbBlitterDownloadSurfacePixels()|.
@@ -513,9 +517,10 @@
 //
 // |surface|: The surface being checked.
 // |pixel_format|: The pixel format that would be used on the surface.
-SB_EXPORT bool SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels(
-    SbBlitterSurface surface,
-    SbBlitterPixelDataFormat pixel_format);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels(
+        SbBlitterSurface surface,
+        SbBlitterPixelDataFormat pixel_format));
 
 // Downloads |surface| pixel data into CPU memory pointed to by
 // |out_pixel_data|, formatted according to the requested |pixel_format| and
@@ -537,11 +542,11 @@
 //
 // |out_pixel_data|: A pointer to a region of memory with a size of
 //                   surface_height * |pitch_in_bytes| bytes.
-SB_EXPORT bool SbBlitterDownloadSurfacePixels(
+SB_DEPRECATED(SB_EXPORT bool SbBlitterDownloadSurfacePixels(
     SbBlitterSurface surface,
     SbBlitterPixelDataFormat pixel_format,
     int pitch_in_bytes,
-    void* out_pixel_data);
+    void* out_pixel_data));
 
 // Flips the |swap_chain| by making the buffer previously accessible to
 // draw commands via |SbBlitterGetRenderTargetFromSwapChain()| visible on the
@@ -560,7 +565,8 @@
 // This function is not thread-safe.
 //
 // |swap_chain|: The SbBlitterSwapChain to be flipped.
-SB_EXPORT bool SbBlitterFlipSwapChain(SbBlitterSwapChain swap_chain);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterFlipSwapChain(SbBlitterSwapChain swap_chain));
 
 // Returns the maximum number of contexts that |device| can support in
 // parallel. Note that devices often support only a single context.
@@ -571,7 +577,7 @@
 //
 // |device|: The SbBlitterDevice for which the maximum number of contexts is
 // returned.
-SB_EXPORT int SbBlitterGetMaxContexts(SbBlitterDevice device);
+SB_DEPRECATED(SB_EXPORT int SbBlitterGetMaxContexts(SbBlitterDevice device));
 
 // Creates an |SbBlitterContext| object on |device|. The returned context can be
 // used to set up draw state and issue draw calls.
@@ -592,7 +598,8 @@
 //
 // |device|: The |SbBlitterDevice| for which the |SbBlitterContext| object is
 // created.
-SB_EXPORT SbBlitterContext SbBlitterCreateContext(SbBlitterDevice device);
+SB_DEPRECATED(
+    SB_EXPORT SbBlitterContext SbBlitterCreateContext(SbBlitterDevice device));
 
 // Destroys the specified |context|, freeing all its resources.
 //
@@ -601,7 +608,7 @@
 // The return value indicates whether the destruction succeeded.
 //
 // |context|: The object to be destroyed.
-SB_EXPORT bool SbBlitterDestroyContext(SbBlitterContext context);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterDestroyContext(SbBlitterContext context));
 
 // Flushes all draw calls previously issued to |context|. Calling this function
 // guarantees that the device processes all draw calls issued to this point on
@@ -615,7 +622,7 @@
 // The return value indicates whether the flush succeeded.
 //
 // |context|: The context for which draw calls are being flushed.
-SB_EXPORT bool SbBlitterFlushContext(SbBlitterContext context);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterFlushContext(SbBlitterContext context));
 
 // Sets up |render_target| as the render target that all subsequent draw calls
 // made on |context| will use.
@@ -626,8 +633,9 @@
 //
 // |context|: The object for which the render target is being set.
 // |render_target|: The target that the |context| should use for draw calls.
-SB_EXPORT bool SbBlitterSetRenderTarget(SbBlitterContext context,
-                                        SbBlitterRenderTarget render_target);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterSetRenderTarget(
+    SbBlitterContext context,
+    SbBlitterRenderTarget render_target));
 
 // Sets the blending state for the specified |context|. By default, blending
 // is disabled on a |SbBlitterContext|.
@@ -653,7 +661,8 @@
 //
 // If |blending| is |false|, the source color and source alpha overwrite
 // the destination color and alpha.
-SB_EXPORT bool SbBlitterSetBlending(SbBlitterContext context, bool blending);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterSetBlending(SbBlitterContext context,
+                                                  bool blending));
 
 // Sets the context's current color.  The current color's default value is
 // |SbBlitterColorFromRGBA(255, 255, 255 255)|.
@@ -669,8 +678,8 @@
 //
 // |context|: The context for which the color is being set.
 // |color|: The context's new color, specified in unpremultiplied alpha format.
-SB_EXPORT bool SbBlitterSetColor(SbBlitterContext context,
-                                 SbBlitterColor color);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterSetColor(SbBlitterContext context,
+                                               SbBlitterColor color));
 
 // Sets whether or not blit calls should have their source pixels modulated by
 // the current color, which is set using |SbBlitterSetColor()|, before being
@@ -683,9 +692,9 @@
 //
 // |modulate_blits_with_color|: Indicates whether to modulate source pixels
 // in blit calls.
-SB_EXPORT bool SbBlitterSetModulateBlitsWithColor(
+SB_DEPRECATED(SB_EXPORT bool SbBlitterSetModulateBlitsWithColor(
     SbBlitterContext context,
-    bool modulate_blits_with_color);
+    bool modulate_blits_with_color));
 
 // Sets the scissor rectangle, which dictates a visibility area that affects
 // all draw calls. Only pixels within the scissor rectangle are rendered, and
@@ -700,8 +709,8 @@
 //
 // Returns whether the scissor was successfully set. It returns an error if
 // it is called before a render target has been specified for the context.
-SB_EXPORT bool SbBlitterSetScissor(SbBlitterContext context,
-                                   SbBlitterRect rect);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterSetScissor(SbBlitterContext context,
+                                                 SbBlitterRect rect));
 
 // Issues a draw call on |context| that fills the specified rectangle |rect|.
 // The rectangle's color is determined by the last call to
@@ -713,7 +722,8 @@
 //
 // |context|: The context on which the draw call will operate.
 // |rect|: The rectangle to be filled.
-SB_EXPORT bool SbBlitterFillRect(SbBlitterContext context, SbBlitterRect rect);
+SB_DEPRECATED(SB_EXPORT bool SbBlitterFillRect(SbBlitterContext context,
+                                               SbBlitterRect rect));
 
 // Issues a draw call on |context| that blits the area of |source_surface|
 // specified by |src_rect| to |context|'s current render target at |dst_rect|.
@@ -727,10 +737,11 @@
 // The return value indicates whether the draw call succeeded.
 //
 // |src_rect|: The area to be block transferred (blitted).
-SB_EXPORT bool SbBlitterBlitRectToRect(SbBlitterContext context,
-                                       SbBlitterSurface source_surface,
-                                       SbBlitterRect src_rect,
-                                       SbBlitterRect dst_rect);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterBlitRectToRect(SbBlitterContext context,
+                                           SbBlitterSurface source_surface,
+                                           SbBlitterRect src_rect,
+                                           SbBlitterRect dst_rect));
 
 // This function functions identically to SbBlitterBlitRectToRect(), except
 // it permits values of |src_rect| outside the dimensions of |source_surface|.
@@ -742,10 +753,11 @@
 // This function is not thread-safe.
 //
 // The return value indicates whether the draw call succeeded.
-SB_EXPORT bool SbBlitterBlitRectToRectTiled(SbBlitterContext context,
-                                            SbBlitterSurface source_surface,
-                                            SbBlitterRect src_rect,
-                                            SbBlitterRect dst_rect);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterBlitRectToRectTiled(SbBlitterContext context,
+                                                SbBlitterSurface source_surface,
+                                                SbBlitterRect src_rect,
+                                                SbBlitterRect dst_rect));
 
 // This function achieves the same effect as calling |SbBlitterBlitRectToRect()|
 // |num_rects| times with each of the |num_rects| values of |src_rects| and
@@ -755,17 +767,17 @@
 // This function is not thread-safe.
 //
 // The return value indicates whether the draw call succeeded.
-SB_EXPORT bool SbBlitterBlitRectsToRects(SbBlitterContext context,
-                                         SbBlitterSurface source_surface,
-                                         const SbBlitterRect* src_rects,
-                                         const SbBlitterRect* dst_rects,
-                                         int num_rects);
+SB_DEPRECATED(
+    SB_EXPORT bool SbBlitterBlitRectsToRects(SbBlitterContext context,
+                                             SbBlitterSurface source_surface,
+                                             const SbBlitterRect* src_rects,
+                                             const SbBlitterRect* dst_rects,
+                                             int num_rects));
 
 #ifdef __cplusplus
 }  // extern "C"
 #endif
 
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #endif  // STARBOARD_BLITTER_H_
diff --git a/src/starboard/build/base_configuration.gypi b/src/starboard/build/base_configuration.gypi
index c0dfb8b..3b0d447 100644
--- a/src/starboard/build/base_configuration.gypi
+++ b/src/starboard/build/base_configuration.gypi
@@ -73,6 +73,10 @@
     # Whether this is an evergreen build.
     'sb_evergreen': 0,
 
+    # Whether this is an evergreen compatible platform. A compatible platform
+    # can run the elf_loader and launch the evergreen build.
+    'sb_evergreen_compatible': 0,
+
     # The operating system of the target, separate from the target_arch. In many
     # cases, an 'unknown' value is fine, but, if set to 'linux', then we can
     # assume some things, and it'll save us some configuration time.
diff --git a/src/starboard/build/collect_deploy_content.py b/src/starboard/build/collect_deploy_content.py
index 9f97896..8d3fffd 100644
--- a/src/starboard/build/collect_deploy_content.py
+++ b/src/starboard/build/collect_deploy_content.py
@@ -12,9 +12,7 @@
 # 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.
-
-"""Builds a symlink farm pointing to specified subdirectories of the input dir.
-"""
+"""Builds a symlink farm pointing to specified subdirectories of the input dir."""
 
 import argparse
 import logging
@@ -24,7 +22,6 @@
 import _env  # pylint: disable=unused-import
 import starboard.tools.port_symlink as port_symlink
 
-
 # The name of an environment variable that when set to |'1'|, signals to us that
 # we should log all output directories that we have populated.
 _SHOULD_LOG_ENV_KEY = 'STARBOARD_GYP_SHOULD_LOG_COPIES'
@@ -49,13 +46,18 @@
   parser.add_argument(
       '-o', dest='output_dir', required=True, help='output directory')
   parser.add_argument(
-      '-s', dest='stamp_file', required=True,
+      '-s',
+      dest='stamp_file',
+      required=True,
       help='stamp file to update after the output directory is populated')
   parser.add_argument(
-      '--use_absolute_symlinks', action='store_true',
+      '--use_absolute_symlinks',
+      action='store_true',
       help='Generated symlinks are stored as absolute paths.')
   parser.add_argument(
-      'subdirs', metavar='subdirs', nargs='*',
+      'subdirs',
+      metavar='subdirs',
+      nargs='*',
       help='subdirectories within both the input and output directories')
   options = parser.parse_args(argv[1:])
 
@@ -106,10 +108,11 @@
         logging.error(msg)
 
     if options.use_absolute_symlinks:
-      port_symlink.MakeSymLink(from_folder=os.path.abspath(src_path),
-                               link_folder=os.path.abspath(dst_path))
+      port_symlink.MakeSymLink(
+          target_path=os.path.abspath(src_path),
+          link_path=os.path.abspath(dst_path))
     else:
-      port_symlink.MakeSymLink(from_folder=rel_path, link_folder=dst_path)
+      port_symlink.MakeSymLink(target_path=rel_path, link_path=dst_path)
 
   if options.stamp_file:
     with open(options.stamp_file, 'w') as stamp_file:
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index 40b1898..80673a3 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -20,6 +20,7 @@
 
 import _env  # pylint: disable=unused-import, relative-import
 from starboard.build.application_configuration import ApplicationConfiguration
+from starboard.optional import get_optional_tests
 from starboard.tools import environment
 from starboard.tools import paths
 from starboard.tools import platform
@@ -256,13 +257,6 @@
         # support JIT.
         'javascript_engine': 'v8',
 
-        # Disable JIT and run in interpreter-only mode by default. It can be
-        # set to 1 to run in JIT mode.  For SpiderMonkey in particular, we
-        # have found that disabling JIT often results in faster JavaScript
-        # execution and lower memory usage.  Setting this to 0 for engine that
-        # requires JIT, or 1 on a platform that does not support JIT, is a
-        # usage error.
-        'cobalt_enable_jit': 1,
         'sabi_json_path': self.GetPathToSabiJsonFile(),
 
         # TODO: Remove these compatibility variables.
@@ -363,10 +357,7 @@
         'player_filter_tests',
         'starboard_platform_tests',
     ]
-    if os.path.exists(os.path.join(paths.STARBOARD_ROOT, 'elf_loader')):
-      tests.append('elf_loader_test')
-    if os.path.exists(os.path.join(paths.STARBOARD_ROOT, 'loader_app')):
-      tests.append('installation_manager_test')
+    tests.extend(get_optional_tests.DoMain())
     return tests
 
   def GetDefaultTargetBuildFile(self):
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 9d527aa..384b1ac 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -84,8 +84,19 @@
 // `SbGetGlesInterface()` to return `nullptr` when OpenGL is not supported and
 // implement `SbBlitterIsBlitterSupported()` to return false when blitter is
 // not supported, as the stubs do.
+//
+// This change also effectively deprecates the gyp variable
+// "enable_map_to_mesh" in favor of CobaltGraphicsExtensionApi function
+// `IsMapToMeshEnabled()` and the command line switch --disable_map_to_mesh.
+// Now, Cobalt will assume the platform supports map_to_mesh, so platforms that
+// do not will have to have return |false| from `IsMapToMeshEnabled()` or use
+// the provided command line switch.
 #define SB_ALL_RENDERERS_REQUIRED_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// Blitter API is no longer supported on any platform. Use the OpenGL ES
+// interface instead.
+#define SB_BLITTER_DEPRECATED_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // Require the captions API.
 // The system must implement the captions functions in
 // `starboard/accessibility.h` or use the provided stub implementations.
@@ -218,15 +229,21 @@
 // always become the constant kSbFoo.
 #define SB_FEATURE_RUNTIME_CONFIGS_VERSION SB_EXPERIMENTAL_API_VERSION
 
-// Introduce SbPlayerGetPreferredOutputMode() so the SbPlayer implementation can
-// explicitly indicate its preference on output mode, when all output modes are
-// supported.
-// For example, Cobalt used to always query for |kSbPlayerOutputModePunchOut|
-// first, without providing details about the video going to be played, and not
-// query for output modes if punch out is supported.  The new interface allows
-// the implementation to fine tune its output mode.  For example, it may decide
-// to use |kSbPlayerOutputModeDecodeToTexture| for low resolution videos.
-#define SB_PLAYER_GET_PREFERRED_OUTPUT_MODE_VERSION SB_EXPERIMENTAL_API_VERSION
+// Improve player creation and output mode query.
+// 1. Introduce the new type SbPlayerCreationParam that holds the common
+//    parameters used to create an SbPlayer() and to query for the output mode
+//    support.
+// 2. Replace SbPlayerOutputModeSupported() by SbPlayerGetPreferredOutputMode()
+//    so the SbPlayer implementation can explicitly indicate its preference on
+//    output mode, when all output modes are supported.
+//    For example, Cobalt used to always query for |kSbPlayerOutputModePunchOut|
+//    first, without providing details about the video going to be played, and
+//    not query for output modes if punch out is supported.  The new interface
+//    allows the implementation to fine tune its output mode.  For example, it
+//    may decide to use |kSbPlayerOutputModeDecodeToTexture| for low resolution
+//    videos.
+#define SB_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT_VERSION \
+  SB_EXPERIMENTAL_API_VERSION
 
 // Introduce support of cbcs encryption scheme into SbDrmSystem, as defined in
 // ISO/IEC 23001 part 7.
@@ -675,6 +692,13 @@
 "starboard/<PLATFORM_PATH>/configuration_constants.cc."
 #endif
 
+#if defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+#error \
+    "SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING should not be defined in Starboard " \
+"versions 12 and later. Instead, define kSbHasAsyncAudioFramesReporting in " \
+"starboard/<PLATFORM_PATH>/configuration_constants.cc."
+#endif
+
 #if defined(SB_HAS_AUDIOLESS_VIDEO)
 #error \
     "SB_HAS_AUDIOLESS_VIDEO should not be defined in Starboard " \
@@ -825,6 +849,14 @@
 #endif  // SB_API_VERSION >= 11
 
 #if SB_API_VERSION >= 10
+#if !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+#error \
+    "Your platform must define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING in API "\
+    "version 10 or later."
+#endif  // !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= 10
+
+#if SB_API_VERSION >= 10
 #define SB_HAS_AUDIOLESS_VIDEO 1
 #endif
 
@@ -998,33 +1030,33 @@
 #endif  // !SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
 #endif  // SB_API_VERSION < 10
 
-#if SB_API_VERSION >= 10
-#if !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= \
+    SB_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT_VERSION
+#if defined(SB_HAS_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+#if !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 #error \
-    "Your platform must define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING in API "\
-    "version 10 or later."
-#endif  // !defined(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
-#endif  // SB_API_VERSION >= 10
+    "SB_HAS_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT is required in" \
+    " this API version."
+#endif  // !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+#else   // defined(SB_HAS_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+#define SB_HAS_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT 1
+#endif  // defined(SB_HAS_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+#endif  // SB_API_VERSION >=
+        // SB_PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT_VERSION
 
-#if SB_API_VERSION >= SB_PLAYER_GET_PREFERRED_OUTPUT_MODE_VERSION
-#if defined(SB_HAS_PLAYER_GET_PREFERRED_OUTPUT_MODE)
-#if !SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
-#error \
-    "SB_HAS_PLAYER_GET_PREFERRED_OUTPUT_MODE is required in this API "\
-       "version."
-#endif  // !SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
-#else   // defined(SB_HAS_PLAYER_GET_PREFERRED_OUTPUT_MODE)
-#define SB_HAS_PLAYER_GET_PREFERRED_OUTPUT_MODE 1
-#endif  // defined(SB_HAS_PLAYER_GET_PREFERRED_OUTPUT_MODE)
-#endif  // SB_API_VERSION >= SB_PLAYER_GET_PREFERRED_OUTPUT_MODE_VERSION
-
-#if SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 #if SB_API_VERSION < 11
 #error \
-    "SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE) requires SB_API_VERSION 11 "\
-       "or later."
+    "SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) requires " \
+    "SB_API_VERSION 11 or later."
 #endif  // SB_API_VERSION < 11
-#endif  // SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+#if SB_API_VERSION >= SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
+#error \
+    "Blitter API is no longer supported. All blitter functions in " \
+"'starboard/blitter.h' are deprecated."
+#endif  // Deprecate Blitter API
 
 // --- Derived Configuration -------------------------------------------------
 
diff --git a/src/starboard/configuration_constants.h b/src/starboard/configuration_constants.h
index b594eea..c88e127 100644
--- a/src/starboard/configuration_constants.h
+++ b/src/starboard/configuration_constants.h
@@ -61,6 +61,9 @@
 // Allow ac3 and ec3 support
 extern const bool kSbHasAc3Audio;
 
+// Specifies whether this platform updates audio frames asynchronously.
+extern const bool kSbHasAsyncAudioFramesReporting;
+
 // Allow playing audioless video.
 extern const bool kSbHasAudiolessVideo;
 
diff --git a/src/starboard/contrib/creator/shared/starboard_platform.gypi b/src/starboard/contrib/creator/shared/starboard_platform.gypi
index 49e7b4c..a7f092d 100644
--- a/src/starboard/contrib/creator/shared/starboard_platform.gypi
+++ b/src/starboard/contrib/creator/shared/starboard_platform.gypi
@@ -14,11 +14,9 @@
 {
   'includes': [
     '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
-    '<(DEPTH)/starboard/stub/blitter_stub_sources.gypi',
   ],
   'variables': {
     'starboard_platform_sources': [
-      '<@(blitter_stub_sources)',
       '<@(filter_based_player_sources)',
       '<(DEPTH)/starboard/contrib/creator/shared/media_is_video_supported.cc',
       '<(DEPTH)/starboard/contrib/creator/shared/player_components_impl.cc',
diff --git a/src/starboard/contrib/tizen/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/contrib/tizen/shared/alsa/alsa_audio_sink_type.cc
index 8c70f71..9e6bf7c 100644
--- a/src/starboard/contrib/tizen/shared/alsa/alsa_audio_sink_type.cc
+++ b/src/starboard/contrib/tizen/shared/alsa/alsa_audio_sink_type.cc
@@ -330,7 +330,12 @@
                                       offset_in_frames * bytes_per_frame_),
               frames_to_buffer_end * bytes_per_frame_))
         consumed = 0;
-      consume_frame_func_(consumed, context_);
+      consume_frame_func_(
+          consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+          (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+          context_);
       if (consumed != frames_to_buffer_end) {
         return;
       }
@@ -347,7 +352,12 @@
             bytes_per_frame_ * frames_to_write)) {
       consumed = 0;
     }
-    consume_frame_func_(consumed, context_);
+    consume_frame_func_(
+        consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        context_);
   } else {
     // A very low quality resampler that simply shift the audio frames to play
     // at the right time.
@@ -379,7 +389,12 @@
                          bytes_per_frame_ * target_frames)) {
       consumed = 0;
     }
-    consume_frame_func_(consumed * playback_rate_, context_);
+    consume_frame_func_(
+        consumed * playback_rate_,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        context_);
   }
 }
 
diff --git a/src/starboard/contrib/tizen/shared/starboard_platform.gypi b/src/starboard/contrib/tizen/shared/starboard_platform.gypi
index ca4802d..650d7f7 100644
--- a/src/starboard/contrib/tizen/shared/starboard_platform.gypi
+++ b/src/starboard/contrib/tizen/shared/starboard_platform.gypi
@@ -14,7 +14,6 @@
 {
   'includes': [
     '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
-    '<(DEPTH)/starboard/stub/blitter_stub_sources.gypi',
   ],
   'variables': {
     'variables': {
@@ -25,7 +24,6 @@
     'has_cdm%': '<(has_cdm)',
     'starboard_platform_sources': [
       '<@(filter_based_player_sources)',
-      '<@(blitter_stub_sources)',
       '<(DEPTH)/starboard/contrib/tizen/shared/atomic_public.h',
       '<(DEPTH)/starboard/contrib/tizen/shared/configuration_public.h',
       '<(DEPTH)/starboard/contrib/tizen/shared/get_home_directory.cc',
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index 2b09430..3daddc5 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -96,10 +96,9 @@
 #include "starboard/export.h"
 #include "starboard/types.h"
 
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 #include "starboard/blitter.h"
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 
 #ifdef __cplusplus
 extern "C" {
@@ -204,7 +203,7 @@
 // should be provided to all Starboard functions that might create
 // SbDecodeTargets (e.g. SbImageDecode()).
 typedef struct SbDecodeTargetGraphicsContextProvider {
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
   // The SbBlitterDevice object that will be used to render any produced
   // SbDecodeTargets.
   SbBlitterDevice device;
@@ -259,11 +258,10 @@
 
 // Defines an image plane within a SbDecodeTargetInfo object.
 typedef struct SbDecodeTargetInfoPlane {
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(BLITTER)
+#if SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
   // A handle to the Blitter surface that can be used for rendering.
   SbBlitterSurface surface;
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION ||
-        // SB_HAS(BLITTER)
+#endif  // SB_API_VERSION < SB_BLITTER_DEPRECATED_VERSION && SB_HAS(BLITTER)
 #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(GLES2)
   // A handle to the GL texture that can be used for rendering.
   uint32_t texture;
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index 3b549e5..43c4332 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -389,29 +389,6 @@
                                  const void* session_id,
                                  int session_id_size);
 
-// Returns the number of keys installed in |drm_system|.
-//
-// |drm_system|: The system for which the number of installed keys is retrieved.
-SB_EXPORT int SbDrmGetKeyCount(SbDrmSystem drm_system);
-
-// Gets |out_key|, |out_key_size|, and |out_status| for the key with |index|
-// in |drm_system|. Returns whether a key is installed at |index|.
-// If not, the output parameters, which all must not be NULL, will not be
-// modified.
-SB_EXPORT bool SbDrmGetKeyStatus(SbDrmSystem drm_system,
-                                 const void* session_id,
-                                 int session_id_size,
-                                 int index,
-                                 void** out_key,
-                                 int* out_key_size,
-                                 SbDrmKeyStatus* out_status);
-
-// Removes all installed keys for |drm_system|. Any outstanding session update
-// requests are also invalidated.
-//
-// |drm_system|: The DRM system for which keys should be removed.
-SB_EXPORT void SbDrmRemoveAllKeys(SbDrmSystem drm_system);
-
 #if SB_API_VERSION >= 10
 
 // Returns true if server certificate of |drm_system| can be updated via
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
index 6bcf56e..e4a258e 100644
--- a/src/starboard/linux/shared/BUILD.gn
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -480,7 +480,6 @@
     "//starboard/shared/starboard/file_storage/storage_get_record_size.cc",
     "//starboard/shared/starboard/file_storage/storage_open_record.cc",
     "//starboard/shared/starboard/file_storage/storage_read_record.cc",
-    "//starboard/shared/starboard/log_message.cc",
     "//starboard/shared/starboard/log_raw_dump_stack.cc",
     "//starboard/shared/starboard/log_raw_format.cc",
     "//starboard/shared/starboard/media/codec_util.cc",
@@ -541,7 +540,6 @@
     "//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_output_mode_supported.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",
diff --git a/src/starboard/linux/shared/configuration_constants.cc b/src/starboard/linux/shared/configuration_constants.cc
index 974b1e0..eec85fc 100644
--- a/src/starboard/linux/shared/configuration_constants.cc
+++ b/src/starboard/linux/shared/configuration_constants.cc
@@ -54,6 +54,9 @@
 // Allow ac3 and ec3 support
 const bool kSbHasAc3Audio = true;
 
+// Specifies whether this platform updates audio frames asynchronously.
+const bool kSbHasAsyncAudioFramesReporting = false;
+
 // Allow playing audioless video.
 const bool kSbHasAudiolessVideo = true;
 
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index f6e236d..9c32fa1 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -242,12 +242,14 @@
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
 #endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 
+#if SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 // Specifies whether this platform updates audio frames asynchronously.  In such
 // case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
 // indicate the absolute time that the consumed audio frames are reported.
 // Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
 // details.
 #define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 0
+#endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 
 #if SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION
 // Specifies the stack size for threads created inside media stack.  Set to 0 to
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index f49b596..7c1b3da 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -45,8 +45,10 @@
     variables = super(LinuxConfiguration, self).GetVariables(
         config_name, use_clang=1)
     variables.update({
-        'javascript_engine': 'v8',
-        'cobalt_enable_jit': 1,
+        'javascript_engine':
+            'v8',
+        'cobalt_enable_jit':
+            1,
         'include_path_platform_deploy_gypi':
             'starboard/linux/shared/platform_deploy.gypi',
     })
@@ -98,24 +100,4 @@
 
   __FILTERED_TESTS = {  # pylint: disable=invalid-name
       'nplb': ['SbDrmTest.AnySupportedKeySystems',],
-      # TODO: Temporarily disable these tests because they are failing on av1
-      # formatted files that were added. The root cause is probably within the
-      # decoder implementation so it is pending further investigation.
-      'player_filter_tests': [
-          'VideoDecoderTests/VideoDecoderTest.HoldFramesUntilFull/4',
-          'VideoDecoderTests/VideoDecoderTest.HoldFramesUntilFull/8',
-          'VideoDecoderTests/VideoDecoderTest.HoldFramesUntilFull/10',
-          'VideoDecoderTests/VideoDecoderTest.MultipleInputs/4',
-          'VideoDecoderTests/VideoDecoderTest.MultipleInputs/8',
-          'VideoDecoderTests/VideoDecoderTest.MultipleInputs/10',
-          'VideoDecoderTests/VideoDecoderTest.MultipleResets/4',
-          'VideoDecoderTests/VideoDecoderTest.MultipleResets/8',
-          'VideoDecoderTests/VideoDecoderTest.MultipleResets/10',
-          'VideoDecoderTests/VideoDecoderTest.Preroll/4',
-          'VideoDecoderTests/VideoDecoderTest.Preroll/8',
-          'VideoDecoderTests/VideoDecoderTest.Preroll/10',
-          'VideoDecoderTests/VideoDecoderTest.ResetAfterInput/4',
-          'VideoDecoderTests/VideoDecoderTest.ResetAfterInput/8',
-          'VideoDecoderTests/VideoDecoderTest.ResetAfterInput/10',
-      ],
   }
diff --git a/src/starboard/linux/shared/player_components_impl.cc b/src/starboard/linux/shared/player_components_impl.cc
index 7e45385..02b7d92 100644
--- a/src/starboard/linux/shared/player_components_impl.cc
+++ b/src/starboard/linux/shared/player_components_impl.cc
@@ -183,15 +183,6 @@
 
     return true;
   }
-
-  void GetAudioRendererParams(int* max_cached_frames,
-                              int* max_frames_per_append) const override {
-    SB_DCHECK(max_cached_frames);
-    SB_DCHECK(max_frames_per_append);
-
-    *max_cached_frames = 128 * 1024;
-    *max_frames_per_append = 16384;
-  }
 };
 
 }  // namespace
diff --git a/src/starboard/linux/shared/starboard_base_symbolize.gyp b/src/starboard/linux/shared/starboard_base_symbolize.gyp
index 353ed12..41bb3c1 100644
--- a/src/starboard/linux/shared/starboard_base_symbolize.gyp
+++ b/src/starboard/linux/shared/starboard_base_symbolize.gyp
@@ -20,6 +20,13 @@
         '<(DEPTH)/base/third_party/symbolize/demangle.cc',
         '<(DEPTH)/base/third_party/symbolize/symbolize.cc',
       ],
+      'conditions': [
+        ['sb_evergreen_compatible == 1', {
+          'dependencies': [
+            '<(DEPTH)/starboard/elf_loader/evergreen_info.gyp:evergreen_info',
+          ],},
+        ],
+      ],
     },
   ],
 }
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 5406485..b7f9831 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -14,7 +14,6 @@
 {
   'includes': [
     '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
-    '<(DEPTH)/starboard/stub/blitter_stub_sources.gypi',
   ],
   'variables': {
     'variables': {
@@ -24,7 +23,6 @@
     # This has_cdm gets exported to gyp files that include this one.
     'has_cdm%': '<(has_cdm)',
     'starboard_platform_sources': [
-      '<@(blitter_stub_sources)',
       '<@(filter_based_player_sources)',
       '<(DEPTH)/starboard/linux/shared/atomic_public.h',
       '<(DEPTH)/starboard/linux/shared/audio_sink_type_dispatcher.cc',
@@ -89,6 +87,8 @@
       '<(DEPTH)/starboard/shared/libaom/aom_library_loader.h',
       '<(DEPTH)/starboard/shared/libaom/aom_video_decoder.cc',
       '<(DEPTH)/starboard/shared/libaom/aom_video_decoder.h',
+      '<(DEPTH)/starboard/shared/libdav1d/dav1d_video_decoder.cc',
+      '<(DEPTH)/starboard/shared/libdav1d/dav1d_video_decoder.h',
       '<(DEPTH)/starboard/shared/libde265/de265_library_loader.cc',
       '<(DEPTH)/starboard/shared/libde265/de265_library_loader.h',
       '<(DEPTH)/starboard/shared/libde265/de265_video_decoder.cc',
@@ -258,7 +258,6 @@
       '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
       '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
       '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
-      '<(DEPTH)/starboard/shared/starboard/log_message.cc',
       '<(DEPTH)/starboard/shared/starboard/log_mutex.cc',
       '<(DEPTH)/starboard/shared/starboard/log_mutex.h',
       '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
@@ -330,6 +329,7 @@
       '<(DEPTH)/starboard/shared/stub/accessibility_get_caption_settings.cc',
       '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
       '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+      '<(DEPTH)/starboard/shared/stub/accessibility_set_captions_enabled.cc',
       '<(DEPTH)/starboard/shared/linux/cpu_features_get.cc',
       '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
       '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
@@ -384,6 +384,7 @@
       '<(DEPTH)/third_party/boringssl/boringssl.gyp:crypto',
       '<(DEPTH)/third_party/de265_includes/de265_includes.gyp:de265',
       '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
+      '<(DEPTH)/third_party/libdav1d/libdav1d.gyp:libdav1d',
       '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
       '<(DEPTH)/third_party/opus/opus.gyp:opus',
       '<(DEPTH)/third_party/pulseaudio_includes/pulseaudio_includes.gyp:pulseaudio',
@@ -453,6 +454,11 @@
           '<(DEPTH)/third_party/vpx_includes/vpx_includes.gyp:vpx',
         ],
       }],
+      ['sb_evergreen_compatible == 1', {
+        'starboard_platform_dependencies': [
+          '<(DEPTH)/third_party/llvm-project/libunwind/libunwind.gyp:unwind_starboard',
+       ]},
+      ],
     ],
   },
   'conditions': [
diff --git a/src/starboard/linux/x64x11/blittergles/shared/gyp_configuration.gypi b/src/starboard/linux/x64x11/blittergles/shared/gyp_configuration.gypi
index 2e844a1..39765de 100644
--- a/src/starboard/linux/x64x11/blittergles/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/blittergles/shared/gyp_configuration.gypi
@@ -26,7 +26,6 @@
       # Since we pretend gl_type is none, exclude this dependency and below,
       # explicitly include the one we need.
       '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
-      '<(DEPTH)/starboard/stub/blitter_stub_sources.gypi:blitter_stub_sources',
     ],
 
     'cobalt_platform_dependencies': [
diff --git a/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp b/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp
index 644ab06..67b3b7e 100644
--- a/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/blittergles/starboard_platform.gyp
@@ -85,7 +85,6 @@
 	      '<(DEPTH)/starboard/shared/stub/system_gles.cc',
       ],
       'sources!': [
-        '<@(blitter_stub_sources)',
 	      '<(DEPTH)/starboard/shared/gles/system_gles2.cc',
       ],
       'defines': [
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.gypi b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.gypi
index bbda64f..03c533b 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.gypi
@@ -19,6 +19,7 @@
             '-Wl,--wrap=malloc',
             '-Wl,--wrap=free',
     ],
+    'cobalt_v8_emit_builtins_as_inline_asm': 0,
   },
 
   'target_defaults': {
diff --git a/src/starboard/linux/x64x11/cobalt/configuration.gypi b/src/starboard/linux/x64x11/cobalt/configuration.gypi
index 5772a92..d2a0334 100644
--- a/src/starboard/linux/x64x11/cobalt/configuration.gypi
+++ b/src/starboard/linux/x64x11/cobalt/configuration.gypi
@@ -13,10 +13,6 @@
 # limitations under the License.
 
 {
-  'variables': {
-    'enable_map_to_mesh%': 1,
-  },
-
   'includes': [
     '../../shared/cobalt/configuration.gypi',
   ],
diff --git a/src/starboard/linux/x64x11/gyp_configuration.gypi b/src/starboard/linux/x64x11/gyp_configuration.gypi
index 96e16b7..a3b2f2e 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gyp_configuration.gypi
@@ -13,6 +13,9 @@
 # limitations under the License.
 
 {
+  'variables': {
+    'sb_evergreen_compatible%': '<!(python <(DEPTH)/build/file_exists.py <(DEPTH)/starboard/elf_loader/evergreen_info.gyp)',
+  },
   'target_defaults': {
     'default_configuration': 'linux-x64x11_debug',
     'configurations': {
diff --git a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
index ac34ad2..e8bba52 100644
--- a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
@@ -16,8 +16,8 @@
 from starboard.linux.x64x11 import gyp_configuration as linux_configuration
 
 
-class LinuxX64X11MozjsConfiguration(
-    linux_configuration.LinuxX64X11Configuration):
+class LinuxX64X11MozjsConfiguration(linux_configuration.LinuxX64X11Configuration
+                                   ):
   """Starboard Linux X64 X11 mozjs platform configuration."""
 
   def GetVariables(self, config_name):
@@ -32,5 +32,4 @@
 
 def CreatePlatformConfig():
   return LinuxX64X11MozjsConfiguration(
-      'linux-x64x11-mozjs',
-      sabi_json_path='starboard/sabi/x64/sysv/sabi.json')
+      'linux-x64x11-mozjs', sabi_json_path='starboard/sabi/x64/sysv/sabi.json')
diff --git a/src/starboard/linux/x64x11/sbversion/10/gyp_configuration.gypi b/src/starboard/linux/x64x11/sbversion/10/gyp_configuration.gypi
index 0175804..072dd55 100644
--- a/src/starboard/linux/x64x11/sbversion/10/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/sbversion/10/gyp_configuration.gypi
@@ -34,6 +34,10 @@
     }, # end of configurations
   },
 
+  'variables': {
+    'enable_map_to_mesh': 1,
+  },
+
   'includes': [
     '<(DEPTH)/starboard/linux/x64x11/shared/gyp_configuration.gypi',
   ],
diff --git a/src/starboard/linux/x64x11/sbversion/11/gyp_configuration.gypi b/src/starboard/linux/x64x11/sbversion/11/gyp_configuration.gypi
index 37ee3f0..727d68e 100644
--- a/src/starboard/linux/x64x11/sbversion/11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/sbversion/11/gyp_configuration.gypi
@@ -34,6 +34,10 @@
     }, # end of configurations
   },
 
+  'variables': {
+    'enable_map_to_mesh': 1,
+  },
+
   'includes': [
     '<(DEPTH)/starboard/linux/x64x11/shared/gyp_configuration.gypi',
   ],
diff --git a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
index 9a3ed06..9b8d915 100644
--- a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
@@ -34,6 +34,10 @@
     }, # end of configurations
   },
 
+  'variables': {
+    'enable_map_to_mesh': 1,
+  },
+
   'includes': [
     '<(DEPTH)/starboard/linux/x64x11/shared/gyp_configuration.gypi',
   ],
diff --git a/src/starboard/linux/x64x11/shared/gyp_configuration.gypi b/src/starboard/linux/x64x11/shared/gyp_configuration.gypi
index a9fbd7f..d9528ca 100644
--- a/src/starboard/linux/x64x11/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/shared/gyp_configuration.gypi
@@ -13,10 +13,6 @@
 # limitations under the License.
 
 {
-  'variables': {
-    'enable_map_to_mesh': 1,
-  },
-
   'includes': [
     '<(DEPTH)/starboard/linux/shared/compiler_flags.gypi',
     '<(DEPTH)/starboard/linux/shared/enable_glx_via_angle.gypi',
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc
index 694d154..eba658d 100644
--- a/src/starboard/nplb/audio_sink_create_test.cc
+++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -36,9 +36,11 @@
 }
 
 void ConsumeFramesFuncStub(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                            SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+                           // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                            void* context) {
 }
 
diff --git a/src/starboard/nplb/audio_sink_helpers.cc b/src/starboard/nplb/audio_sink_helpers.cc
index b0a0d9c..1e13451 100644
--- a/src/starboard/nplb/audio_sink_helpers.cc
+++ b/src/starboard/nplb/audio_sink_helpers.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 
 #include "starboard/common/log.h"
+#include "starboard/configuration_constants.h"
 
 namespace starboard {
 namespace nplb {
@@ -203,15 +204,22 @@
   condition_variable_.Signal();
 }
 
-void AudioSinkTestEnvironment::OnConsumeFrames(int frames_consumed
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                               ,
-                                               SbTime frames_consumed_at
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                               ) {
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-  SB_DCHECK(frames_consumed_at <= SbTimeGetMonotonicNow());
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+void AudioSinkTestEnvironment::OnConsumeFrames(
+    int frames_consumed
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    ,
+    SbTime frames_consumed_at
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    ) {
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING)
+  if (kSbHasAsyncAudioFramesReporting) {
+    SB_DCHECK(frames_consumed_at <= SbTimeGetMonotonicNow());
+  }
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   ScopedLock lock(mutex_);
   frames_consumed_ += frames_consumed;
   condition_variable_.Signal();
@@ -230,18 +238,28 @@
 }
 
 // static
-void AudioSinkTestEnvironment::ConsumeFramesFunc(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                                 SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                                 void* context) {
+void AudioSinkTestEnvironment::ConsumeFramesFunc(
+    int frames_consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    SbTime frames_consumed_at,
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    void* context) {
   AudioSinkTestEnvironment* environment =
       reinterpret_cast<AudioSinkTestEnvironment*>(context);
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  if (!kSbHasAsyncAudioFramesReporting)
+    frames_consumed_at = (SbTime)kSbTimeMax;
+#endif
   environment->OnConsumeFrames(frames_consumed, frames_consumed_at);
-#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   environment->OnConsumeFrames(frames_consumed);
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 }
 
 }  // namespace nplb
diff --git a/src/starboard/nplb/audio_sink_helpers.h b/src/starboard/nplb/audio_sink_helpers.h
index 72921e0..2c1494e 100644
--- a/src/starboard/nplb/audio_sink_helpers.h
+++ b/src/starboard/nplb/audio_sink_helpers.h
@@ -96,11 +96,14 @@
                             int* offset_in_frames,
                             bool* is_playing,
                             bool* is_eos_reached);
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   void OnConsumeFrames(int frames_consumed, SbTime frames_consumed_at);
-#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   void OnConsumeFrames(int frames_consumed);
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   static void UpdateSourceStatusFunc(int* frames_in_buffer,
                                      int* offset_in_frames,
@@ -108,9 +111,11 @@
                                      bool* is_eos_reached,
                                      void* context);
   static void ConsumeFramesFunc(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                 SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+                                // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                 void* context);
   SbAudioSink sink_;
 
diff --git a/src/starboard/nplb/media_set_audio_write_duration_test.cc b/src/starboard/nplb/media_set_audio_write_duration_test.cc
index fd6d323..e8d3b41 100644
--- a/src/starboard/nplb/media_set_audio_write_duration_test.cc
+++ b/src/starboard/nplb/media_set_audio_write_duration_test.cc
@@ -17,6 +17,7 @@
 #include "starboard/common/optional.h"
 #include "starboard/common/spin_lock.h"
 #include "starboard/configuration_constants.h"
+#include "starboard/nplb/player_creation_param_helpers.h"
 #include "starboard/player.h"
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
@@ -134,16 +135,31 @@
     SbMediaAudioCodec kAudioCodec = dmp_reader_.audio_codec();
     SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
+    last_input_timestamp_ =
+        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, 0).timestamp;
+    first_input_timestamp_ = last_input_timestamp_;
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+    SbPlayerCreationParam creation_param = CreatePlayerCreationParam(
+        audio_sample_info.codec, kSbMediaVideoCodecNone);
+    creation_param.audio_sample_info = audio_sample_info;
+    creation_param.output_mode =
+        SbPlayerGetPreferredOutputMode(&creation_param);
+    EXPECT_NE(creation_param.output_mode, kSbPlayerOutputModeInvalid);
+
+    SbPlayer player = SbPlayerCreate(
+        fake_graphics_context_provider_.window(), &creation_param,
+        DummyDeallocateSampleFunc, DecoderStatusFunc, PlayerStatusFunc,
+        DummyErrorFunc, this /* context */,
+        fake_graphics_context_provider_.decoder_target_provider());
+#else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
     SbPlayerOutputMode output_mode = kSbPlayerOutputModeDecodeToTexture;
+
     if (!SbPlayerOutputModeSupported(output_mode, kSbMediaVideoCodecNone,
                                      kSbDrmSystemInvalid)) {
       output_mode = kSbPlayerOutputModePunchOut;
     }
 
-    last_input_timestamp_ =
-        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, 0).timestamp;
-    first_input_timestamp_ = last_input_timestamp_;
-
     SbPlayer player = SbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone,
         kAudioCodec, kSbDrmSystemInvalid, &audio_sample_info,
@@ -151,6 +167,8 @@
         DummyDeallocateSampleFunc, DecoderStatusFunc, PlayerStatusFunc,
         DummyErrorFunc, this /* context */, output_mode,
         fake_graphics_context_provider_.decoder_target_provider());
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
     EXPECT_TRUE(SbPlayerIsValid(player));
     return player;
   }
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 17f72df..6b8646d 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -181,6 +181,8 @@
         'once_test.cc',
         'optional_test.cc',
         'player_create_test.cc',
+        'player_creation_param_helpers.cc',
+        'player_creation_param_helpers.h',
         'player_get_preferred_output_mode_test.cc',
         'player_output_mode_supported_test.cc',
         'random_helpers.cc',
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index 004afa4..460bd0f 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -17,6 +17,7 @@
 #include "starboard/blitter.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/decode_target.h"
+#include "starboard/nplb/player_creation_param_helpers.h"
 #include "starboard/player.h"
 #include "starboard/testing/fake_graphics_context_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -54,56 +55,92 @@
                     const char* message) {}
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
-SbMediaAudioSampleInfo GetDefaultAudioSampleInfo() {
-  SbMediaAudioSampleInfo audio_sample_info;
+SbPlayer CallSbPlayerCreate(
+    SbWindow window,
+    SbMediaVideoCodec video_codec,
+    SbMediaAudioCodec audio_codec,
+    SbDrmSystem drm_system,
+    const SbMediaAudioSampleInfo* audio_sample_info,
+    const char* max_video_capabilities,
+    SbPlayerDeallocateSampleFunc sample_deallocate_func,
+    SbPlayerDecoderStatusFunc decoder_status_func,
+    SbPlayerStatusFunc player_status_func,
+    void* context,
+    SbPlayerOutputMode output_mode,
+    SbDecodeTargetGraphicsContextProvider* context_provider) {
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
+  if (audio_sample_info) {
+    SB_CHECK(audio_sample_info->codec == audio_codec);
+  } else {
+    SB_CHECK(audio_codec == kSbMediaAudioCodecNone);
+  }
+
+  SbPlayerCreationParam creation_param =
+      CreatePlayerCreationParam(audio_codec, video_codec);
+  if (audio_sample_info) {
+    creation_param.audio_sample_info = *audio_sample_info;
+  }
+  creation_param.drm_system = drm_system;
+  creation_param.output_mode = output_mode;
+  creation_param.max_video_capabilities = max_video_capabilities;
+
+  return SbPlayerCreate(window, &creation_param, sample_deallocate_func,
+                        decoder_status_func, player_status_func, DummyErrorFunc,
+                        context, context_provider);
+
+#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+  return SbPlayerCreate(window, video_codec, audio_codec,
+#if SB_API_VERSION < 10
+                        SB_PLAYER_NO_DURATION,
+#endif  // SB_API_VERSION < 10
+                        kSbDrmSystemInvalid, audio_sample_info,
 #if SB_API_VERSION >= 11
-  audio_sample_info.codec = kSbMediaAudioCodecAac;
+                        max_video_capabilities,
 #endif  // SB_API_VERSION >= 11
-  audio_sample_info.format_tag = 0xff;
-  audio_sample_info.number_of_channels = 2;
-  audio_sample_info.samples_per_second = 22050;
-  audio_sample_info.block_alignment = 4;
-  audio_sample_info.bits_per_sample = 32;
-  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;
+                        sample_deallocate_func, decoder_status_func,
+                        player_status_func,
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+                        DummyErrorFunc,
+#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
+                        context, output_mode, context_provider);
 
-  return audio_sample_info;
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+}
+
+bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
+                           SbMediaVideoCodec codec) {
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+  SbPlayerCreationParam creation_param =
+      CreatePlayerCreationParam(kSbMediaAudioCodecNone, codec);
+  creation_param.output_mode = output_mode;
+  return SbPlayerGetPreferredOutputMode(&creation_param) == output_mode;
+#else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+  return SbPlayerOutputModeSupported(output_mode, codec, kSbDrmSystemInvalid);
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 }
 
 TEST_F(SbPlayerTest, SunnyDay) {
-  SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo();
+  SbMediaAudioSampleInfo audio_sample_info =
+      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
-  SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
   SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
                                        kSbPlayerOutputModePunchOut};
 
   for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
     SbPlayerOutputMode output_mode = output_modes[i];
-    if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+
+    if (!IsOutputModeSupported(output_mode, kVideoCodec)) {
       continue;
     }
-
-    SbPlayer player = SbPlayerCreate(
+    SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecAac,
-#if SB_API_VERSION < 10
-        SB_PLAYER_NO_DURATION,
-#endif  // SB_API_VERSION < 10
-        kSbDrmSystemInvalid, &audio_sample_info,
-#if SB_API_VERSION >= 11
-        NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
-        DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-        DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-        NULL /* context */, output_mode,
-        fake_graphics_context_provider_.decoder_target_provider());
+        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+        "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
+        DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */,
+        output_mode, fake_graphics_context_provider_.decoder_target_provider());
     EXPECT_TRUE(SbPlayerIsValid(player));
 
     if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
@@ -116,33 +153,26 @@
 
 #if SB_API_VERSION >= 10
 TEST_F(SbPlayerTest, NullCallbacks) {
-  SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo();
+  SbMediaAudioSampleInfo audio_sample_info =
+      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
-  SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
   SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
                                        kSbPlayerOutputModePunchOut};
 
   for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
     SbPlayerOutputMode output_mode = output_modes[i];
-    if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+    if (!IsOutputModeSupported(output_mode, kVideoCodec)) {
       continue;
     }
 
     {
-      SbPlayer player = SbPlayerCreate(
+      SbPlayer player = CallSbPlayerCreate(
           fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-          kSbMediaAudioCodecAac,
-          kSbDrmSystemInvalid, &audio_sample_info,
-#if SB_API_VERSION >= 11
-          NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
-          NULL /* deallocate_sample_func */, DummyDecoderStatusFunc,
-          DummyStatusFunc,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-          DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-          NULL /* context */, output_mode,
+          kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+          "" /* max_video_capabilities */, NULL /* deallocate_sample_func */,
+          DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */,
+          output_mode,
           fake_graphics_context_provider_.decoder_target_provider());
       EXPECT_FALSE(SbPlayerIsValid(player));
 
@@ -150,19 +180,12 @@
     }
 
     {
-      SbPlayer player = SbPlayerCreate(
+      SbPlayer player = CallSbPlayerCreate(
           fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-          kSbMediaAudioCodecAac,
-          kSbDrmSystemInvalid, &audio_sample_info,
-#if SB_API_VERSION >= 11
-          NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
-          DummyDeallocateSampleFunc, NULL /* decoder_status_func */,
-          DummyStatusFunc,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-          DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-          NULL /* context */, output_mode,
+          kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+          "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
+          NULL /* decoder_status_func */, DummyStatusFunc, NULL /* context */,
+          output_mode,
           fake_graphics_context_provider_.decoder_target_provider());
       EXPECT_FALSE(SbPlayerIsValid(player));
 
@@ -170,19 +193,12 @@
     }
 
     {
-      SbPlayer player = SbPlayerCreate(
+      SbPlayer player = CallSbPlayerCreate(
           fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-          kSbMediaAudioCodecAac,
-          kSbDrmSystemInvalid, &audio_sample_info,
-#if SB_API_VERSION >= 11
-          NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
-          DummyDeallocateSampleFunc, DummyDecoderStatusFunc,
-          NULL /*status_func */,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-          DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-          NULL /* context */, output_mode,
+          kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+          "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
+          DummyDecoderStatusFunc, NULL /*status_func */, NULL /* context */,
+          output_mode,
           fake_graphics_context_provider_.decoder_target_provider());
       EXPECT_FALSE(SbPlayerIsValid(player));
 
@@ -190,6 +206,25 @@
     }
 
 #if SB_HAS(PLAYER_ERROR_MESSAGE)
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+    {
+      SbPlayerCreationParam creation_param = CreatePlayerCreationParam(
+          kSbMediaAudioCodecAac, kSbMediaVideoCodecH264);
+
+      SbPlayer player = SbPlayerCreate(
+          fake_graphics_context_provider_.window(), &creation_param,
+          DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc,
+          NULL /* error_func */, NULL /* context */,
+          fake_graphics_context_provider_.decoder_target_provider());
+      EXPECT_FALSE(SbPlayerIsValid(player));
+
+      SbPlayerDestroy(player);
+    }
+
+#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
     {
       SbPlayer player = SbPlayerCreate(
           fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
@@ -205,6 +240,9 @@
 
       SbPlayerDestroy(player);
     }
+
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 #endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
@@ -218,31 +256,21 @@
   }
 
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
-  SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
   SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
                                        kSbPlayerOutputModePunchOut};
 
   for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
     SbPlayerOutputMode output_mode = output_modes[i];
-    if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+    if (!IsOutputModeSupported(output_mode, kVideoCodec)) {
       continue;
     }
 
-    SbPlayer player = SbPlayerCreate(
+    SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kVideoCodec,
-        kSbMediaAudioCodecNone,
-#if SB_API_VERSION < 10
-        SB_PLAYER_NO_DURATION,
-#endif  // SB_API_VERSION < 10
-        kSbDrmSystemInvalid, NULL /* audio_sample_info */,
-#if SB_API_VERSION >= 11
-        NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
+        kSbMediaAudioCodecNone, kSbDrmSystemInvalid,
+        NULL /* audio_sample_info */, "" /* max_video_capabilities */,
         DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-        DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
         NULL /* context */, output_mode,
         fake_graphics_context_provider_.decoder_target_provider());
     EXPECT_TRUE(SbPlayerIsValid(player));
@@ -259,33 +287,26 @@
 
 #if SB_API_VERSION >= 10
 TEST_F(SbPlayerTest, AudioOnly) {
-  SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo();
+  SbMediaAudioSampleInfo audio_sample_info =
+      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
   SbMediaAudioCodec kAudioCodec = kSbMediaAudioCodecAac;
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
-  SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
   SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
                                        kSbPlayerOutputModePunchOut};
 
   for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
     SbPlayerOutputMode output_mode = output_modes[i];
-    if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+    if (!IsOutputModeSupported(output_mode, kVideoCodec)) {
       continue;
     }
 
-    SbPlayer player = SbPlayerCreate(
+    SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone,
-        kAudioCodec,
-        kSbDrmSystemInvalid, &audio_sample_info,
-#if SB_API_VERSION >= 11
-        NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
-        DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyStatusFunc,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
-        DummyErrorFunc,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
-        NULL /* context */, output_mode,
-        fake_graphics_context_provider_.decoder_target_provider());
+        kAudioCodec, kSbDrmSystemInvalid, &audio_sample_info,
+        "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
+        DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */,
+        output_mode, fake_graphics_context_provider_.decoder_target_provider());
     EXPECT_TRUE(SbPlayerIsValid(player));
 
     if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
@@ -297,7 +318,8 @@
 }
 
 TEST_F(SbPlayerTest, MultiPlayer) {
-  SbMediaAudioSampleInfo audio_sample_info = GetDefaultAudioSampleInfo();
+  SbMediaAudioSampleInfo audio_sample_info =
+      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
   SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
   constexpr SbPlayerOutputMode kOutputModes[] = {
@@ -373,14 +395,11 @@
 #if SB_API_VERSION >= 11
           audio_sample_info.codec = kAudioCodecs[k];
 #endif  // SB_API_VERSION >= 11
-          created_players.push_back(SbPlayerCreate(
+          created_players.push_back(CallSbPlayerCreate(
               fake_graphics_context_provider_.window(), kVideoCodecs[l],
               kAudioCodecs[k], kSbDrmSystemInvalid, &audio_sample_info,
-#if SB_API_VERSION >= 11
-              NULL /* max_video_capabilities */,
-#endif  // SB_API_VERSION >= 11
-              DummyDeallocateSampleFunc, DummyDecoderStatusFunc,
-              DummyStatusFunc, DummyErrorFunc, NULL /* context */,
+              "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
+              DummyDecoderStatusFunc, DummyStatusFunc, NULL /* context */,
               kOutputModes[j],
               fake_graphics_context_provider_.decoder_target_provider()));
           if (!SbPlayerIsValid(created_players.back())) {
diff --git a/src/starboard/nplb/player_creation_param_helpers.cc b/src/starboard/nplb/player_creation_param_helpers.cc
new file mode 100644
index 0000000..62009ff
--- /dev/null
+++ b/src/starboard/nplb/player_creation_param_helpers.cc
@@ -0,0 +1,149 @@
+// 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 "starboard/nplb/player_creation_param_helpers.h"
+
+#include "starboard/common/log.h"
+
+namespace starboard {
+namespace nplb {
+
+SbMediaAudioSampleInfo CreateAudioSampleInfo(SbMediaAudioCodec codec) {
+  SbMediaAudioSampleInfo audio_sample_info = {};
+
+#if SB_API_VERSION >= 11
+  audio_sample_info.codec = codec;
+#endif  // SB_API_VERSION >= 11
+
+  switch (codec) {
+    case kSbMediaAudioCodecNone:
+      break;
+    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;
+      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;
+      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;
+      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;
+      break;
+    }
+  }
+  return audio_sample_info;
+}
+
+SbMediaVideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec) {
+  SbMediaVideoSampleInfo video_sample_info = {};
+
+#if SB_API_VERSION >= 11
+  video_sample_info.codec = codec;
+#endif  // SB_API_VERSION >= 11
+
+#if SB_API_VERSION >= 11
+  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;
+#else   // SB_API_VERSION >= 11
+  static SbMediaColorMetadata color_metadata;
+  color_metadata.primaries = kSbMediaPrimaryIdBt709;
+  color_metadata.transfer = kSbMediaTransferIdBt709;
+  color_metadata.matrix = kSbMediaMatrixIdBt709;
+  color_metadata.range = kSbMediaRangeIdLimited;
+  video_sample_info.color_metadata = &color_metadata;
+#endif  // SB_API_VERSION >= 11
+
+  video_sample_info.frame_width = 1920;
+  video_sample_info.frame_height = 1080;
+
+  return video_sample_info;
+}
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+SbPlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec,
+                                                SbMediaVideoCodec video_codec) {
+  SbPlayerCreationParam creation_param = {};
+
+  creation_param.audio_mime = "";
+  creation_param.video_mime = "";
+  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.max_video_capabilities = "";
+
+  return creation_param;
+}
+
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/player_creation_param_helpers.h b/src/starboard/nplb/player_creation_param_helpers.h
new file mode 100644
index 0000000..8cb327d
--- /dev/null
+++ b/src/starboard/nplb/player_creation_param_helpers.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef STARBOARD_NPLB_PLAYER_CREATION_PARAM_HELPERS_H_
+#define STARBOARD_NPLB_PLAYER_CREATION_PARAM_HELPERS_H_
+
+#include "starboard/configuration.h"
+
+#include "starboard/media.h"
+#include "starboard/player.h"
+
+namespace starboard {
+namespace nplb {
+
+SbMediaAudioSampleInfo CreateAudioSampleInfo(SbMediaAudioCodec codec);
+SbMediaVideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec);
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+SbPlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec,
+                                                SbMediaVideoCodec video_codec);
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // STARBOARD_NPLB_PLAYER_CREATION_PARAM_HELPERS_H_
diff --git a/src/starboard/nplb/player_get_preferred_output_mode_test.cc b/src/starboard/nplb/player_get_preferred_output_mode_test.cc
index 3a489df..4167dd9 100644
--- a/src/starboard/nplb/player_get_preferred_output_mode_test.cc
+++ b/src/starboard/nplb/player_get_preferred_output_mode_test.cc
@@ -15,90 +15,38 @@
 #include "starboard/player.h"
 
 #include "starboard/configuration.h"
+#include "starboard/nplb/player_creation_param_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
 namespace starboard {
 namespace nplb {
 namespace {
 
-SbMediaAudioSampleInfo GetDefaultAudioSampleInfo() {
-  SbMediaAudioSampleInfo audio_sample_info = {};
-
-  audio_sample_info.codec = kSbMediaAudioCodecAac;
-  audio_sample_info.format_tag = 0xff;
-  audio_sample_info.number_of_channels = 2;
-  audio_sample_info.samples_per_second = 22050;
-  audio_sample_info.block_alignment = 4;
-  audio_sample_info.bits_per_sample = 32;
-  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;
-
-  return audio_sample_info;
-}
-
-SbMediaVideoSampleInfo GetDefaultVideoSampleInfo() {
-  SbMediaVideoSampleInfo video_sample_info = {};
-
-  video_sample_info.codec = kSbMediaVideoCodecH264;
-  video_sample_info.frame_width = 1920;
-  video_sample_info.frame_height = 1080;
-  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;
-
-  return video_sample_info;
-}
-
 TEST(SbPlayerGetPreferredOutputModeTest, SunnyDay) {
-  const SbMediaAudioSampleInfo kAudioSampleInfo = GetDefaultAudioSampleInfo();
-  const SbMediaVideoSampleInfo kVideoSampleInfo = GetDefaultVideoSampleInfo();
-  const char* kMaxVideoCapabilities = "";
+  auto creation_param =
+      CreatePlayerCreationParam(kSbMediaAudioCodecAac, kSbMediaVideoCodecH264);
 
-  auto output_mode = SbPlayerGetPreferredOutputMode(
-      &kAudioSampleInfo, &kVideoSampleInfo, kSbDrmSystemInvalid,
-      kMaxVideoCapabilities);
+  creation_param.output_mode = kSbPlayerOutputModeInvalid;
+  auto output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
   ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid);
-  EXPECT_TRUE(SbPlayerOutputModeSupported(output_mode, kVideoSampleInfo.codec,
-                                          kSbDrmSystemInvalid));
-}
 
-TEST(SbPlayerGetPreferredOutputModeTest, RainyDayInvalid) {
-  const SbMediaAudioSampleInfo kAudioSampleInfo = GetDefaultAudioSampleInfo();
-  const SbMediaVideoSampleInfo kVideoSampleInfo = GetDefaultVideoSampleInfo();
-  const char* kMaxVideoCapabilities = "";
-  const SbMediaAudioSampleInfo* kInvalidAudioSampleInfo = nullptr;
-  const SbMediaVideoSampleInfo* kInvalidVideoSampleInfo = nullptr;
-  const char* kInvalidVideoCapabilities = nullptr;
+  creation_param.output_mode = output_mode;
+  output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  ASSERT_EQ(output_mode, creation_param.output_mode);
 
-  auto output_mode = SbPlayerGetPreferredOutputMode(
-      kInvalidAudioSampleInfo, kInvalidVideoSampleInfo, kSbDrmSystemInvalid,
-      kMaxVideoCapabilities);
-  EXPECT_EQ(output_mode, kSbPlayerOutputModeInvalid);
+  creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
+  output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid);
 
-  output_mode = SbPlayerGetPreferredOutputMode(
-      &kAudioSampleInfo, kInvalidVideoSampleInfo, kSbDrmSystemInvalid,
-      kMaxVideoCapabilities);
-  EXPECT_EQ(output_mode, kSbPlayerOutputModeInvalid);
-
-  output_mode = SbPlayerGetPreferredOutputMode(
-      kInvalidAudioSampleInfo, &kVideoSampleInfo, kSbDrmSystemInvalid,
-      kMaxVideoCapabilities);
-  EXPECT_EQ(output_mode, kSbPlayerOutputModeInvalid);
-
-  output_mode = SbPlayerGetPreferredOutputMode(
-      &kAudioSampleInfo, &kVideoSampleInfo, kSbDrmSystemInvalid,
-      kInvalidVideoCapabilities);
-  EXPECT_EQ(output_mode, kSbPlayerOutputModeInvalid);
+  creation_param.output_mode = kSbPlayerOutputModePunchOut;
+  output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid);
 }
 
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
 
-#endif  // SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/nplb/player_output_mode_supported_test.cc b/src/starboard/nplb/player_output_mode_supported_test.cc
index 65b35b3..d4e77f2 100644
--- a/src/starboard/nplb/player_output_mode_supported_test.cc
+++ b/src/starboard/nplb/player_output_mode_supported_test.cc
@@ -21,6 +21,8 @@
 namespace nplb {
 namespace {
 
+#if !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 TEST(SbPlayerOutputModeSupportedTest, SunnyDay) {
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
   SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
@@ -46,6 +48,8 @@
   EXPECT_FALSE(result);
 }
 
+#endif  // !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_get_interface_address_test.cc b/src/starboard/nplb/socket_get_interface_address_test.cc
index 36ed9c3..3c1a317 100644
--- a/src/starboard/nplb/socket_get_interface_address_test.cc
+++ b/src/starboard/nplb/socket_get_interface_address_test.cc
@@ -64,7 +64,7 @@
 
   EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &source, &netmask));
   // A netmask that starts with 0 is likely incorrect.
-  EXPECT_TRUE(netmask.address[0] & 0x8);
+  EXPECT_TRUE(netmask.address[0] & 0x80);
   EXPECT_TRUE(source.type == kSbSocketAddressTypeIpv4 ||
               source.type == kSbSocketAddressTypeIpv6);
   EXPECT_TRUE(netmask.type == kSbSocketAddressTypeIpv4 ||
@@ -89,7 +89,7 @@
   EXPECT_FALSE(IsLocalhost(&source));
 
   // A netmask that starts with 0 is likely incorrect.
-  EXPECT_TRUE(netmask.address[0] & 0x8);
+  EXPECT_TRUE(netmask.address[0] & 0x80);
   EXPECT_EQ(GetAddressType(), source.type);
   EXPECT_EQ(GetAddressType(), netmask.type);
   EXPECT_EQ(0, source.port);
@@ -128,7 +128,7 @@
   EXPECT_EQ(GetAddressType(), source.type);
   EXPECT_NE(0, source.port);
   // A netmask that starts with 0 is likely incorrect.
-  EXPECT_TRUE(netmask.address[0] & 0x8);
+  EXPECT_TRUE(netmask.address[0] & 0x80);
   EXPECT_EQ(GetAddressType(), netmask.type);
   EXPECT_NE(0, SbMemoryCompare(source.address, invalid_address.address,
                                SB_ARRAY_SIZE(source.address)));
diff --git a/src/starboard/optional/README.md b/src/starboard/optional/README.md
new file mode 100644
index 0000000..84c058a
--- /dev/null
+++ b/src/starboard/optional/README.md
@@ -0,0 +1,10 @@
+# Optional
+
+This directory houses code that provides the ability to conditionally include
+code in a safe manner.
+
+## Current State
+
+The only code that we currently support optionally including is test targets.
+The test targets can be retrieved in formats friendly to both Python and GYP.
+
diff --git a/src/starboard/optional/__init__.py b/src/starboard/optional/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/starboard/optional/__init__.py
diff --git a/src/starboard/optional/_env.py b/src/starboard/optional/_env.py
new file mode 100644
index 0000000..021908e
--- /dev/null
+++ b/src/starboard/optional/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/optional/get_optional_tests.py b/src/starboard/optional/get_optional_tests.py
new file mode 100644
index 0000000..8a15e67
--- /dev/null
+++ b/src/starboard/optional/get_optional_tests.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+
+# 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.
+"""Returns optional test targets for use in GYP or Python."""
+
+import _env  # pylint: disable=unused-import
+
+import argparse
+import os
+import posixpath
+import sys
+
+from starboard.tools import paths
+
+
+def _Posixify(path):
+  """Returns an absolute, POSIX-ified version of |path|.
+
+  When providing dependencies in GYP the paths must be POSIX paths. This
+  function strips drive information from the path, joins the path with the
+  repository root to make it absolute, and normalizes the path.
+
+  Args:
+    path: The path to turn into an absolute, POSIX path.
+
+  Returns:
+    An absolute, POSIX path.
+  """
+  _, repository_root = os.path.splitdrive(paths.REPOSITORY_ROOT)
+  return os.path.normpath(os.path.join(repository_root,
+                                       path)).replace(os.path.sep,
+                                                      posixpath.sep)
+
+
+def _GetOptionalTestTargets(as_gyp_dependencies):
+  """Returns optional test targets for use in GYP or Python.
+
+  This function will try to import optional test targets, and fail gracefully if
+  they cannot be imported.
+
+  GYP has a hard time parsing lists passed to it, so when the test targets
+  should be returned in a GYP-friendly format we need to generate a string of
+  concatenated items, delimited by a newline. Further, each test target should
+  be an absolute path to ensure no confusion about which target is desired.
+
+  Args:
+    as_gyp_dependencies: Whether or not the test targets should be returned in a
+      GYP-friendly format.
+
+  Returns:
+    When |as_gyp_dependencies| is |True|, a string of concatenated paths to test
+    targets, otherwise a list of test targets. On failure, either an empty
+    string or an empty list.
+  """
+  try:
+    # pylint: disable=g-import-not-at-top
+    from starboard.optional.internal import test_targets
+    if as_gyp_dependencies:
+      return '\n'.join([
+          _Posixify(path) for path in test_targets.TEST_TARGET_GYP_DEPENDENCIES
+      ])
+    return test_targets.TEST_TARGET_NAMES
+  except ImportError:
+    pass
+  return '' if as_gyp_dependencies else []
+
+
+def DoMain(argv=None):
+  """Function to allow the use of this script from GYP's pymod_do_main."""
+  arg_parser = argparse.ArgumentParser()
+  arg_parser.add_argument(
+      '-g',
+      '--as-gyp-dependencies',
+      action='store_true',
+      default=False,
+      help='Whether or not to return the test targets in a GYP-friendly format.'
+  )
+  args, _ = arg_parser.parse_known_args(argv)
+  return _GetOptionalTestTargets(args.as_gyp_dependencies)
+
+
+def main():
+  print(DoMain())
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/starboard/player.h b/src/starboard/player.h
index abd51cc..00dfcc8 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -117,6 +117,51 @@
   kSbPlayerOutputModeInvalid,
 } SbPlayerOutputMode;
 
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+// The playback related parameters to pass into SbPlayerCreate() and
+// SbPlayerGetPreferredOutputMode().
+typedef struct SbPlayerCreationParam {
+  // The audio mime of the stream if available.  Otherwise it will point to an
+  // empty string.  It will never be NULL.
+  const char* audio_mime;
+  // The video mime of the stream if available.  Otherwise it will point to an
+  // empty string.  It will never be NULL.
+  const char* video_mime;
+  // Provides an appropriate DRM system if the media stream has encrypted
+  // portions.  It will be |kSbDrmSystemInvalid| if the stream does not have
+  // encrypted portions.
+  SbDrmSystem drm_system;
+  // 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.
+  SbMediaAudioSampleInfo audio_sample_info;
+  // Contains a populated SbMediaVideoSampleInfo if |video_sample_info.codec|
+  // isn't |kSbMediaVideoCodecNone|.  When |video_sample_info.codec| is
+  // |kSbMediaVideoCodecNone|, the video is audio only.
+  SbMediaVideoSampleInfo video_sample_info;
+  // 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
+  // |kSbPlayerOutputDecodeToTexture| indicates that the decoded video frames
+  // should be made available for the application to pull via calls to
+  // SbPlayerGetCurrentFrame().
+  SbPlayerOutputMode output_mode;
+  // 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
+  // will never be set to NULL.
+  const char* max_video_capabilities;
+} SbPlayerCreationParam;
+
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 #if SB_API_VERSION >= 11
 
 // Identify the type of side data accompanied with |SbPlayerSampleInfo|, as side
@@ -414,15 +459,16 @@
 //   is no longer valid after this function returns.  The implementation has to
 //   make a copy of the content if it is needed after the function returns.
 #if SB_API_VERSION >= 11
-// |max_video_capabilities|: This string communicates the video maximums to the
-//   platform. 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 NULL.
+// |max_video_capabilities|: This string communicates the max video capabilities
+//   required to the platform. 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 NULL.
 #endif  // SB_API_VERSION >= 11
 #if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
     SB_HAS(AUDIOLESS_VIDEO)
@@ -473,6 +519,21 @@
 // |decoder_status_func|, |player_status_func|, or |player_error_func| if it
 // applies), then |kSbPlayerInvalid| must be returned.
 #endif  // SB_API_VERSION >= 10
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+SB_EXPORT SbPlayer
+SbPlayerCreate(SbWindow window,
+               const SbPlayerCreationParam* creation_param,
+               SbPlayerDeallocateSampleFunc sample_deallocate_func,
+               SbPlayerDecoderStatusFunc decoder_status_func,
+               SbPlayerStatusFunc player_status_func,
+               SbPlayerErrorFunc player_error_func,
+               void* context,
+               SbDecodeTargetGraphicsContextProvider* context_provider);
+
+#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 SB_EXPORT SbPlayer
 SbPlayerCreate(SbWindow window,
                SbMediaVideoCodec video_codec,
@@ -498,42 +559,38 @@
                void* context,
                SbPlayerOutputMode output_mode,
                SbDecodeTargetGraphicsContextProvider* context_provider);
+
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+// Returns the preferred output mode of the implementation when a video
+// described by |creation_param| is played.  It is assumed that it is okay to
+// call SbPlayerCreate() with the same video described by |creation_param|,
+// with its |output_mode| member replaced by the returned output mode.
+// When the caller has no preference on the output mode, it will set
+// |creation_param->output_mode| to |kSbPlayerOutputModeInvalid|, and the
+// implementation can return its preferred output mode based on the information
+// contained in |creation_param|.  The caller can also set
+// |creation_param->output_mode| to its preferred output mode, and the
+// implementation should return the same output mode if it is supported,
+// otherwise the implementation should return an output mode that it is
+// supported, as if |creation_param->output_mode| is set to
+// |kSbPlayerOutputModeInvalid| prior to the call.
+// 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.
+SB_EXPORT SbPlayerOutputMode
+SbPlayerGetPreferredOutputMode(const SbPlayerCreationParam* creation_param);
+
+#else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 // Returns true if the given player output mode is supported by the platform.
 // If this function returns true, it is okay to call SbPlayerCreate() with
 // the given |output_mode|.
 SB_EXPORT bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode,
                                            SbMediaVideoCodec codec,
                                            SbDrmSystem drm_system);
-
-#if SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
-// Returns the preferred output mode of the platform when a video described by
-// the parameters is played.
-// If the returned output mode is not |kSbPlayerOutputModeInvalid|, it is
-// implied that calling SbPlayerOutputModeSupported() with the returned output
-// will return true.
-// |audio_sample_info|: The caller must provide a populated |audio_sample_info|
-//   if its |codec| member isn't |kSbMediaAudioCodecNone|.  When the video
-//   doesn't contain an audio track, |codec| contained in |audio_sample_info|
-//   will be |kSbMediaAudioCodecNone|.  In either case, |audio_sample_info| will
-//   not be NULL.  See media.h for the format of the |SbMediaAudioSampleInfo|
-//   struct.
-// |video_sample_info|: The caller must provide a populated |video_sample_info|
-//   if its |codec| member isn't |kSbMediaVideoCodecNone|.  When the video
-//   doesn't contain a video track, |codec| contained in |video_sample_info|
-//   will be |kSbMediaVideoCodecNone|.  In either case, |video_sample_info| will
-//   not be NULL.  See media.h for the format of the |SbMediaVideoSampleInfo|
-//   struct.
-// |max_video_capabilities| points to the max video capabilities associated with
-//   the video if there is any.  It points to an empty string when there is no
-//   max video capabilities associated with the video.  It will never be NULL.
-// When any input parameters are invalid, the function should return
-// |kSbPlayerOutputModeInvalid|, for example, when |audio_sample_info| is NULL.
-SB_EXPORT SbPlayerOutputMode
-SbPlayerGetPreferredOutputMode(const SbMediaAudioSampleInfo* audio_sample_info,
-                               const SbMediaVideoSampleInfo* video_sample_info,
-                               SbDrmSystem drm_system,
-                               const char* max_video_capabilities);
-#endif  // SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
 // Destroys |player|, freeing all associated resources. Each callback must
 // receive one more callback to say that the player was destroyed. Callbacks
@@ -574,6 +631,7 @@
 //   been called with ticket X, a client should ignore all
 //   |SbPlayerDecoderStatusFunc| calls that do not pass in ticket X.
 
+// presubmit: allow sb_export mismatch
 SB_EXPORT void SbPlayerSeek(SbPlayer player,
                             SbMediaTime seek_to_pts,
                             int ticket);
@@ -648,6 +706,7 @@
 // |sample_drm_info|: The DRM system related info for the media sample. This
 //   value is required for encrypted samples. Otherwise, it must be |NULL|.
 #if SB_API_VERSION < 10
+// presubmit: allow sb_export mismatch
 SB_EXPORT void SbPlayerWriteSample(
     SbPlayer player,
     SbMediaType sample_type,
@@ -768,6 +827,8 @@
 //
 // |player|: The player about which information is being retrieved.
 // |out_player_info|: The information retrieved for the player.
+
+// presubmit: allow sb_export mismatch
 SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info);
 #else   // SB_API_VERSION < 10
 // SbPlayerGetInfo2 is like the deprecated SbPlayerGetInfo, but accepts
diff --git a/src/starboard/raspi/shared/configuration_constants.cc b/src/starboard/raspi/shared/configuration_constants.cc
index db143a4..88661c7 100644
--- a/src/starboard/raspi/shared/configuration_constants.cc
+++ b/src/starboard/raspi/shared/configuration_constants.cc
@@ -52,6 +52,9 @@
 // Allow ac3 and ec3 support
 const bool kSbHasAc3Audio = false;
 
+// Specifies whether this platform updates audio frames asynchronously.
+const bool kSbHasAsyncAudioFramesReporting = false;
+
 // Allow playing audioless video.
 const bool kSbHasAudiolessVideo = false;
 
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index c76e62e..374a37f 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -168,15 +168,6 @@
 // Whether the current platform has speech synthesis.
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
-// --- Media Configuration ---------------------------------------------------
-
-// Specifies whether this platform updates audio frames asynchronously.  In such
-// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
-// indicate the absolute time that the consumed audio frames are reported.
-// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
-// details.
-#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 0
-
 // --- Decoder-only Params ---
 
 // --- Memory Configuration --------------------------------------------------
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index 54413a7..6b5607d 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -14,6 +14,8 @@
 
 {
   'variables': {
+    'sb_evergreen_compatible%': '<!(python <(DEPTH)/build/file_exists.py <(DEPTH)/starboard/elf_loader/evergreen_info.gyp)',
+
     # Override that omits the "data" subdirectory.
     # TODO: Remove when omitted for all platforms in base_configuration.gypi.
     'sb_static_contents_output_data_dir': '<(PRODUCT_DIR)/content',
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index 2205b26..7b6a213 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -46,11 +46,14 @@
   def GetVariables(self, configuration):
     variables = super(RaspiPlatformConfig, self).GetVariables(configuration)
     variables.update({
-        'clang': 0,
-        'sysroot': self.sysroot,
+        'clang':
+            0,
+        'sysroot':
+            self.sysroot,
         'include_path_platform_deploy_gypi':
             'starboard/raspi/shared/platform_deploy.gypi',
-        'STRIP': os.environ.get('STRIP')
+        'STRIP':
+            os.environ.get('STRIP')
     })
 
     return variables
diff --git a/src/starboard/raspi/shared/player_components_impl.cc b/src/starboard/raspi/shared/player_components_impl.cc
index b6b2a8d..fa61dd7 100644
--- a/src/starboard/raspi/shared/player_components_impl.cc
+++ b/src/starboard/raspi/shared/player_components_impl.cc
@@ -82,15 +82,6 @@
     *video_renderer_sink = new VideoRendererSinkImpl(video_parameters.player);
     return true;
   }
-
-  void GetAudioRendererParams(int* max_cached_frames,
-                              int* max_frames_per_append) const override {
-    SB_DCHECK(max_cached_frames);
-    SB_DCHECK(max_frames_per_append);
-
-    *max_cached_frames = 128 * 1024;
-    *max_frames_per_append = 16384;
-  }
 };
 
 }  // namespace
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 330028f..8b3a089 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -14,7 +14,6 @@
 {
   'includes': [
     '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
-    '<(DEPTH)/starboard/stub/blitter_stub_sources.gypi',
   ],
   'variables': {
     'sb_pedantic_warnings': 1,
@@ -39,7 +38,6 @@
       'target_name': 'starboard_platform',
       'type': 'static_library',
       'sources': [
-        '<@(blitter_stub_sources)',
         '<@(filter_based_player_sources)',
         '<(DEPTH)/starboard/linux/shared/atomic_public.h',
         '<(DEPTH)/starboard/linux/shared/configuration_constants.cc',
@@ -290,7 +288,6 @@
         '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
         '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
         '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_message.cc',
         '<(DEPTH)/starboard/shared/starboard/log_mutex.cc',
         '<(DEPTH)/starboard/shared/starboard/log_mutex.h',
         '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
@@ -338,7 +335,6 @@
         '<(DEPTH)/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
@@ -364,6 +360,7 @@
         '<(DEPTH)/starboard/shared/stub/accessibility_get_caption_settings.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+        '<(DEPTH)/starboard/shared/stub/accessibility_set_captions_enabled.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
         '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
@@ -427,6 +424,13 @@
         '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg.gyp:ffmpeg_linked',
         'starboard_base_symbolize',
       ],
+      'conditions': [
+        ['sb_evergreen_compatible == 1', {
+          'dependencies': [
+            '<(DEPTH)/third_party/llvm-project/libunwind/libunwind.gyp:unwind_starboard',
+          ],},
+        ],
+      ],
       'cflags': [
         # Generated by Audio Renderer and Audio Sink implementations.
         '-Wno-reorder',
diff --git a/src/starboard/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
index 62ae12b..611eeac 100644
--- a/src/starboard/shared/alsa/alsa_audio_sink_type.cc
+++ b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
@@ -346,7 +346,12 @@
           IncrementPointerByBytes(frame_buffer_,
                                   offset_in_frames * bytes_per_frame),
           frames_to_buffer_end);
-      consume_frame_func_(consumed, context_);
+      consume_frame_func_(
+          consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+          (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+          context_);
       if (consumed != frames_to_buffer_end) {
         SB_DLOG(INFO) << "alsa::AlsaAudioSink exits write frames : consumed "
                       << consumed << " frames, with " << frames_to_buffer_end
@@ -363,7 +368,12 @@
                         IncrementPointerByBytes(
                             frame_buffer_, offset_in_frames * bytes_per_frame),
                         frames_to_write);
-    consume_frame_func_(consumed, context_);
+    consume_frame_func_(
+        consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        context_);
   } else {
     // A very low quality resampler that simply shift the audio frames to play
     // at the right time.
@@ -392,7 +402,12 @@
 
     int consumed =
         AlsaWriteFrames(playback_handle_, &resample_buffer_[0], target_frames);
-    consume_frame_func_(consumed * playback_rate_, context_);
+    consume_frame_func_(
+        consumed * playback_rate_,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+        context_);
   }
 }
 
diff --git a/src/starboard/shared/blittergles/blitter_context.cc b/src/starboard/shared/blittergles/blitter_context.cc
index d9549df..dcf92e8 100644
--- a/src/starboard/shared/blittergles/blitter_context.cc
+++ b/src/starboard/shared/blittergles/blitter_context.cc
@@ -198,13 +198,14 @@
 }
 
 SbBlitterContextPrivate::ScopedCurrentContext::~ScopedCurrentContext() {
-  if (was_current_) {
+  if (was_current_ &&
+      previous_render_target_ != kSbBlitterInvalidRenderTarget) {
     context_->MakeCurrentWithRenderTarget(previous_render_target_);
   } else {
     GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
     EGL_CALL(eglMakeCurrent(context_->device->display, EGL_NO_SURFACE,
                             EGL_NO_SURFACE, EGL_NO_CONTEXT));
-    context_->is_current = false;
-    context_->device->mutex.Release();
   }
+  context_->is_current = was_current_;
+  context_->device->mutex.Release();
 }
diff --git a/src/starboard/shared/blittergles/blitter_surface.cc b/src/starboard/shared/blittergles/blitter_surface.cc
index 06edbd6..35d6e3f 100644
--- a/src/starboard/shared/blittergles/blitter_surface.cc
+++ b/src/starboard/shared/blittergles/blitter_surface.cc
@@ -22,6 +22,7 @@
 #include "starboard/shared/gles/gl_call.h"
 
 SbBlitterSurfacePrivate::~SbBlitterSurfacePrivate() {
+  SbBlitterContextPrivate::ScopedCurrentContext scoped_current_context;
   GL_CALL(glDeleteTextures(1, &color_texture_handle));
   if (render_target != NULL) {
     GL_CALL(glDeleteFramebuffers(1, &render_target->framebuffer_handle));
diff --git a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
new file mode 100644
index 0000000..de260eb
--- /dev/null
+++ b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
@@ -0,0 +1,332 @@
+// 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 "starboard/shared/libdav1d/dav1d_video_decoder.h"
+
+#include <string>
+
+#if SB_API_VERSION >= 11
+#include "starboard/format_string.h"
+#endif  // SB_API_VERSION >= 11
+#include "starboard/common/log.h"
+#include "starboard/common/string.h"
+#include "starboard/shared/starboard/player/filter/cpu_video_frame.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "third_party/libdav1d/include/dav1d/common.h"
+#include "third_party/libdav1d/include/dav1d/data.h"
+#include "third_party/libdav1d/include/dav1d/headers.h"
+#include "third_party/libdav1d/include/dav1d/picture.h"
+
+namespace starboard {
+namespace shared {
+namespace libdav1d {
+
+namespace {
+
+using starboard::player::InputBuffer;
+using starboard::player::JobThread;
+using starboard::player::filter::CpuVideoFrame;
+
+void ReleaseInputBuffer(const uint8_t* buf, void* context) {
+  SB_DCHECK(context);
+  SB_DCHECK(buf);
+
+  InputBuffer* input_buffer = static_cast<InputBuffer*>(context);
+  SB_DCHECK(input_buffer->data() == buf);
+
+  input_buffer->Release();
+}
+
+}  // namespace
+
+VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
+                           SbPlayerOutputMode output_mode,
+                           SbDecodeTargetGraphicsContextProvider*
+                               decode_target_graphics_context_provider)
+    : output_mode_(output_mode) {
+  // TODO: Remove this check and implement DecodeToTexture.
+  SB_DCHECK(output_mode_ == kSbPlayerOutputModePunchOut);
+#if SB_API_VERSION >= 11
+  SB_DCHECK(video_codec == kSbMediaVideoCodecAv1);
+#else  // SB_API_VERSION >= 11
+  SB_DCHECK(video_codec == kSbMediaVideoCodecVp10);
+#endif  // SB_API_VERSION >= 11
+}
+
+VideoDecoder::~VideoDecoder() {
+  SB_DCHECK(BelongsToCurrentThread());
+
+  Reset();
+}
+
+void VideoDecoder::Initialize(const DecoderStatusCB& decoder_status_cb,
+                              const ErrorCB& error_cb) {
+  SB_DCHECK(BelongsToCurrentThread());
+  SB_DCHECK(decoder_status_cb);
+  SB_DCHECK(!decoder_status_cb_);
+  SB_DCHECK(error_cb);
+  SB_DCHECK(!error_cb_);
+
+  decoder_status_cb_ = decoder_status_cb;
+  error_cb_ = error_cb;
+}
+
+void VideoDecoder::WriteInputBuffer(
+    const scoped_refptr<InputBuffer>& input_buffer) {
+  SB_DCHECK(BelongsToCurrentThread());
+  SB_DCHECK(input_buffer);
+  SB_DCHECK(decoder_status_cb_);
+
+  if (stream_ended_) {
+    ReportError("WriteInputBuffer() was called after WriteEndOfStream().");
+    return;
+  }
+  if (!decoder_thread_) {
+    decoder_thread_.reset(new JobThread("dav1d_video_decoder"));
+    SB_DCHECK(decoder_thread_);
+  }
+  decoder_thread_->job_queue()->Schedule(
+      std::bind(&VideoDecoder::DecodeOneBuffer, this, input_buffer));
+}
+
+void VideoDecoder::WriteEndOfStream() {
+  SB_DCHECK(BelongsToCurrentThread());
+  SB_DCHECK(decoder_status_cb_);
+
+  // We have to flush the decoder to decode the rest frames and to ensure that
+  // Decode() is not called when the stream is ended.
+  stream_ended_ = true;
+
+  if (!decoder_thread_) {
+    // In case there is no WriteInputBuffer() call before WriteEndOfStream(),
+    // don't create the decoder thread and send the EOS frame directly.
+    decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
+    return;
+  }
+
+  decoder_thread_->job_queue()->Schedule(std::bind(
+      &VideoDecoder::DecodeEndOfStream, this, 100 * kSbTimeMillisecond));
+}
+
+void VideoDecoder::Reset() {
+  SB_DCHECK(BelongsToCurrentThread());
+
+  if (decoder_thread_) {
+    decoder_thread_->job_queue()->Schedule(
+        std::bind(&VideoDecoder::TeardownCodec, this));
+
+    // Join the thread to ensure that all callbacks in process are finished.
+    decoder_thread_.reset();
+  }
+  stream_ended_ = false;
+  CancelPendingJobs();
+  frames_being_decoded_ = 0;
+}
+
+void VideoDecoder::ReportError(const std::string& error_message) {
+  SB_LOG(ERROR) << error_message;
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+  Schedule(std::bind(error_cb_, kSbPlayerErrorDecode, error_message));
+#else   // SB_HAS(PLAYER_ERROR_MESSAGE)
+  Schedule(error_cb_);
+#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
+}
+
+void VideoDecoder::InitializeCodec() {
+  SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
+  SB_DCHECK(dav1d_context_ == NULL);
+
+  Dav1dSettings dav1d_settings{0};
+  dav1d_default_settings(&dav1d_settings);
+  // TODO: Verify this setting is optimal.
+  dav1d_settings.n_frame_threads = 8;
+
+  int result = dav1d_open(&dav1d_context_, &dav1d_settings);
+  if (result != kDav1dSuccess) {
+    ReportError(FormatString("|dav1d_open| failed with code %d.", result));
+    dav1d_context_ = NULL;
+  }
+}
+
+void VideoDecoder::TeardownCodec() {
+  SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
+
+  if (dav1d_context_) {
+    dav1d_close(&dav1d_context_);
+    dav1d_context_ = NULL;
+  }
+}
+
+void VideoDecoder::DecodeOneBuffer(
+    const scoped_refptr<InputBuffer>& input_buffer) {
+  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;
+    TeardownCodec();
+    InitializeCodec();
+  }
+
+  SB_DCHECK(dav1d_context_);
+
+  Dav1dData dav1d_data;
+  int result =
+      dav1d_data_wrap(&dav1d_data, input_buffer->data(), input_buffer->size(),
+                      &ReleaseInputBuffer, input_buffer.get());
+  if (result != kDav1dSuccess) {
+    ReportError(FormatString("|dav1d_data_wrap| failed with code %d.", result));
+    return;
+  }
+
+  // Increment the refcount for |input_buffer| so that its data buffer persists.
+  // This allows the buffer to be accessed by the dav1d decoder while decoding,
+  // without it being destructed asynchronously.
+  // When the dav1d decoder is done with the buffer, it will call the
+  // ReleaseInputBuffer callback, which will call InputBuffer::Release(),
+  // and decrement the reference count.
+  input_buffer->AddRef();
+
+  dav1d_data.m.timestamp = input_buffer->timestamp();
+
+  // Sometimes |dav1d_send_data| can fail with EAGAIN error code. In this case,
+  // we need to output frames from dav1d before continuing to send data packets
+  // to it. The following loop retries sending data after outputting frames.
+  for (int i = 0; i < 2; i++) {
+    result = dav1d_send_data(dav1d_context_, &dav1d_data);
+    if (result != kDav1dSuccess && result != DAV1D_ERR(EAGAIN)) {
+      break;
+    }
+    if (result == kDav1dSuccess) {
+      SB_DCHECK(dav1d_data.sz == 0);  // Check if all data has been consumed.
+      ++frames_being_decoded_;
+    }
+    if (!TryToOutputFrames()) {
+      return;
+    }
+    if (result == kDav1dSuccess) {
+      Schedule(std::bind(decoder_status_cb_, kNeedMoreInput, nullptr));
+      return;
+    }
+  }
+  // Encountered a fatal error in dav1d.
+  dav1d_data_unref(&dav1d_data);
+  ReportError(FormatString("|dav1d_send_data| failed with code %d.", result));
+}
+
+void VideoDecoder::DecodeEndOfStream(SbTime timeout) {
+  SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
+
+  if (!TryToOutputFrames()) {
+    return;
+  }
+  if (frames_being_decoded_ > 0 && timeout > 0) {
+    const SbTime delay_period = 5 * kSbTimeMillisecond;
+    decoder_thread_->job_queue()->Schedule(
+        std::bind(&VideoDecoder::DecodeEndOfStream, this,
+                  timeout - delay_period),
+        delay_period);
+    return;
+  } else {
+    SB_LOG_IF(WARNING, frames_being_decoded_ > 0)
+        << "Timed out waiting to output frames on end of stream.";
+  }
+
+  Schedule(
+      std::bind(decoder_status_cb_, kBufferFull, VideoFrame::CreateEOSFrame()));
+}
+
+bool VideoDecoder::TryToOutputFrames() {
+  SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
+
+  bool error_occurred = false;
+
+  // Helper lambda to extract a frame from dav1d decoder.
+  // Returns a nullptr if no frame is available, or an error occurred, or more
+  // input is required.
+  const auto get_frame_from_dav1d =
+      [this, &error_occurred]() -> scoped_refptr<CpuVideoFrame> {
+    error_occurred = false;
+    Dav1dPicture dav1d_picture{0};
+    int dav1d_result = dav1d_get_picture(dav1d_context_, &dav1d_picture);
+    if (dav1d_result != kDav1dSuccess) {
+      // NOTE: dav1d_get_pictures returns EAGAIN when a frame is not ready to be
+      // decoded, or when more input data is needed.
+      if (dav1d_result != DAV1D_ERR(EAGAIN)) {
+        ReportError(FormatString("|dav1d_get_picture| failed with code %d.",
+                                 dav1d_result));
+        error_occurred = true;
+      }
+      return nullptr;
+    }
+
+    if (dav1d_picture.p.layout != DAV1D_PIXEL_LAYOUT_I420) {
+      ReportError(FormatString("|dav1d_picture| has unexpected layout %d.",
+                               static_cast<int>(dav1d_picture.p.layout)));
+      dav1d_picture_unref(&dav1d_picture);
+      error_occurred = true;
+      return nullptr;
+    }
+
+    if (dav1d_picture.p.bpc != 8 && dav1d_picture.p.bpc != 10) {
+      ReportError(FormatString("|dav1d_picture| has unexpected bitdepth %d.",
+                               dav1d_picture.p.bpc));
+      dav1d_picture_unref(&dav1d_picture);
+      error_occurred = true;
+      return nullptr;
+    }
+
+    SB_DCHECK(dav1d_picture.stride[kDav1dPlaneY] ==
+              dav1d_picture.stride[kDav1dPlaneU] * 2);
+    SB_DCHECK(dav1d_picture.data[kDav1dPlaneY] <
+              dav1d_picture.data[kDav1dPlaneU]);
+    SB_DCHECK(dav1d_picture.data[kDav1dPlaneU] <
+              dav1d_picture.data[kDav1dPlaneV]);
+
+    if (dav1d_picture.stride[kDav1dPlaneY] !=
+            dav1d_picture.stride[kDav1dPlaneU] * 2 ||
+        dav1d_picture.data[kDav1dPlaneY] >= dav1d_picture.data[kDav1dPlaneU] ||
+        dav1d_picture.data[kDav1dPlaneU] >= dav1d_picture.data[kDav1dPlaneV]) {
+      ReportError("Unsupported yuv plane format.");
+      error_occurred = true;
+      return nullptr;
+    }
+
+    scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame(
+        dav1d_picture.p.bpc, dav1d_picture.p.w, dav1d_picture.p.h,
+        dav1d_picture.stride[0], dav1d_picture.m.timestamp,
+        static_cast<uint8_t*>(dav1d_picture.data[0]),
+        static_cast<uint8_t*>(dav1d_picture.data[1]),
+        static_cast<uint8_t*>(dav1d_picture.data[2]));
+    dav1d_picture_unref(&dav1d_picture);
+    SB_DCHECK(!error_occurred);
+    return frame;
+  };
+
+  auto frame = get_frame_from_dav1d();
+  while (frame && !error_occurred) {
+    SB_DCHECK(frames_being_decoded_ > 0);
+    --frames_being_decoded_;
+    Schedule(std::bind(decoder_status_cb_, kNeedMoreInput, frame));
+    frame = get_frame_from_dav1d();
+  }
+  return true;
+}
+
+}  // namespace libdav1d
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/libdav1d/dav1d_video_decoder.h b/src/starboard/shared/libdav1d/dav1d_video_decoder.h
new file mode 100644
index 0000000..2028a9a
--- /dev/null
+++ b/src/starboard/shared/libdav1d/dav1d_video_decoder.h
@@ -0,0 +1,103 @@
+// 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.
+
+#ifndef STARBOARD_SHARED_LIBDAV1D_DAV1D_VIDEO_DECODER_H_
+#define STARBOARD_SHARED_LIBDAV1D_DAV1D_VIDEO_DECODER_H_
+
+#include "third_party/libdav1d/include/dav1d/dav1d.h"
+
+#include <string>
+
+#include "starboard/atomic.h"
+#include "starboard/common/ref_counted.h"
+#include "starboard/decode_target.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_thread.h"
+
+namespace starboard {
+namespace shared {
+namespace libdav1d {
+
+class VideoDecoder : public starboard::player::filter::VideoDecoder,
+                     private starboard::player::JobQueue::JobOwner {
+ public:
+  VideoDecoder(SbMediaVideoCodec video_codec,
+               SbPlayerOutputMode output_mode,
+               SbDecodeTargetGraphicsContextProvider*
+                   decode_target_graphics_context_provider);
+  ~VideoDecoder() override;
+
+  void Initialize(const DecoderStatusCB& decoder_status_cb,
+                  const ErrorCB& error_cb) override;
+
+  // TODO: Verify if these values are correct.
+  size_t GetPrerollFrameCount() const override { return 8; }
+  SbTime GetPrerollTimeout() const override { return kSbTimeMax; }
+  size_t GetMaxNumberOfCachedFrames() const override { return 12; }
+
+  void WriteInputBuffer(
+      const scoped_refptr<InputBuffer>& input_buffer) override;
+  void WriteEndOfStream() override;
+  void Reset() override;
+
+ private:
+  const int kDav1dSuccess = 0;
+
+  const int kDav1dPlaneY = 0;
+  const int kDav1dPlaneU = 1;
+  const int kDav1dPlaneV = 2;
+
+  void ReportError(const std::string& error_message);
+
+  // The following functions are only called on the decoder thread.
+  void InitializeCodec();
+  void TeardownCodec();
+  void DecodeOneBuffer(const scoped_refptr<InputBuffer>& input_buffer);
+  void DecodeEndOfStream(SbTime timeout);
+
+  // Sends all available decoded frames from dav1d for outputting. Returns the
+  // number of frames decoded. The output argument tracks whether an
+  // unrecoverable error occured in dav1d while trying to output frames.
+  bool TryToOutputFrames();
+
+  // TODO: Implement DecodeToTexture functionality for libdav1d.
+  SbDecodeTarget GetCurrentDecodeTarget() override {
+    return kSbDecodeTargetInvalid;
+  }
+
+  const SbPlayerOutputMode output_mode_;
+
+  // Working thread to avoid lengthy decoding work block the player thread.
+  scoped_ptr<starboard::player::JobThread> decoder_thread_;
+
+  // The following callbacks will be initialized in Initialize() and won't be
+  // changed during the life time of this class.
+  DecoderStatusCB decoder_status_cb_;
+  ErrorCB error_cb_;
+
+  Dav1dContext* dav1d_context_ = NULL;
+
+  bool stream_ended_ = false;
+  int current_frame_height_ = 0;
+  int current_frame_width_ = 0;
+  int frames_being_decoded_ = 0;
+};
+
+}  // namespace libdav1d
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_LIBDAV1D_DAV1D_VIDEO_DECODER_H_
diff --git a/src/starboard/shared/linux/system_get_stack.cc b/src/starboard/shared/linux/system_get_stack.cc
index 4c50a76..6289e1f 100644
--- a/src/starboard/shared/linux/system_get_stack.cc
+++ b/src/starboard/shared/linux/system_get_stack.cc
@@ -14,10 +14,47 @@
 
 #include "starboard/system.h"
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#include "starboard/common/log.h"
+#include "starboard/memory.h"
+#else
 #include <execinfo.h>
-
 #include <algorithm>
+#endif
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+int SbSystemGetStack(void** out_stack, int stack_size) {
+  unw_cursor_t cursor;
+  unw_context_t context;
+
+  // Initialize cursor to current frame for local unwinding.
+  int ret = unw_getcontext(&context);
+  if (ret < 0) {
+    return 0;
+  }
+  ret = unw_init_local(&cursor, &context);
+  if (ret < 0) {
+    return 0;
+  }
+  // Unwind frames one by one, going up the frame stack.
+  int i = 0;
+  for (; i < stack_size; i++) {
+    ret = unw_step(&cursor);
+    if (ret <= 0) {
+      break;
+    }
+    unw_word_t pc = 0;
+    ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+    if (pc == 0) {
+      break;
+    }
+    out_stack[i] = reinterpret_cast<void*>(pc);
+  }
+  return i;
+}
+#else
 int SbSystemGetStack(void** out_stack, int stack_size) {
   int count = std::max(backtrace(out_stack, stack_size), 0);
 
@@ -33,3 +70,4 @@
 
   return count - 1;
 }
+#endif
diff --git a/src/starboard/shared/pulse/pulse_audio_sink_type.cc b/src/starboard/shared/pulse/pulse_audio_sink_type.cc
index ff87de2..22d45f3 100644
--- a/src/starboard/shared/pulse/pulse_audio_sink_type.cc
+++ b/src/starboard/shared/pulse/pulse_audio_sink_type.cc
@@ -275,7 +275,12 @@
       SB_DCHECK(total_frames_played_ <= new_total_frames_played);
       int64_t consume = new_total_frames_played - total_frames_played_;
       if (consume > 0) {
-        consume_frame_func_(consume, context_);
+        consume_frame_func_(
+            consume,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+            (SbTime)kSbTimeMax,  // Async audio frames reporting not supported
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+            context_);
         total_frames_played_ = new_total_frames_played;
       }
     }
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_get_min_buffer_size_in_frames.cc b/src/starboard/shared/starboard/audio_sink/audio_sink_get_min_buffer_size_in_frames.cc
index 6bbaf73..89e6f39 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_get_min_buffer_size_in_frames.cc
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_get_min_buffer_size_in_frames.cc
@@ -32,6 +32,10 @@
     SB_LOG(ERROR) << "Not support sample frequency " << sampling_frequency_hz;
     return -1;
   }
+  // Larger buffer size would have more latency when changing playback rate.
+  if (sampling_frequency_hz < 24000) {
+    return 4 * 1024;
+  }
 
   return 8 * 1024;
 }
diff --git a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
index 924e6c0..3486f6c 100644
--- a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
+++ b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
@@ -18,6 +18,7 @@
 
 #include "starboard/common/mutex.h"
 #include "starboard/configuration.h"
+#include "starboard/configuration_constants.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
 
@@ -119,9 +120,13 @@
 
       SbThreadSleep(frames_to_consume * kSbTimeSecond / sampling_frequency_hz_);
       consume_frame_func_(frames_to_consume,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+                          kSbHasAsyncAudioFramesReporting
+                              ? SbTimeGetMonotonicNow()
+                              : (SbTime)kSbTimeMax,
+#elif SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                           SbTimeGetMonotonicNow(),
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif
                           context_);
     } else {
       // Wait for five millisecond if we are paused.
diff --git a/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h b/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
index 4f3487c..d0a4846 100644
--- a/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
+++ b/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
@@ -45,6 +45,8 @@
 
 #define kSbHasAc3Audio SB_HAS_AC3_AUDIO
 
+#define kSbHasAsyncAudioFramesReporting SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING
+
 #define kSbHasAudiolessVideo SB_HAS_AUDIOLESS_VIDEO
 
 #define kSbHasMediaWebmVp9Support SB_HAS_MEDIA_WEBM_VP9_SUPPORT
diff --git a/src/starboard/shared/starboard/log_message.cc b/src/starboard/shared/starboard/log_message.cc
index b39790e..d517f51 100644
--- a/src/starboard/shared/starboard/log_message.cc
+++ b/src/starboard/shared/starboard/log_message.cc
@@ -12,4 +12,4 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-// This file has been moved to //starboard/common/log_message.cc.
+// This file has been moved to //starboard/common/log.cc.
diff --git a/src/starboard/shared/starboard/log_raw_dump_stack.cc b/src/starboard/shared/starboard/log_raw_dump_stack.cc
index af76b5c..8972ce1 100644
--- a/src/starboard/shared/starboard/log_raw_dump_stack.cc
+++ b/src/starboard/shared/starboard/log_raw_dump_stack.cc
@@ -14,6 +14,10 @@
 
 #include "starboard/common/log.h"
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_info.h"
+#include "starboard/memory.h"
+#endif
 #include "starboard/system.h"
 
 void SbLogRawDumpStack(int frames_to_skip) {
@@ -24,12 +28,24 @@
   void* stack[256];
   int count = SbSystemGetStack(stack, SB_ARRAY_SIZE_INT(stack));
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  EvergreenInfo evergreen_info;
+  bool valid_evergreen_info = GetEvergreenInfo(&evergreen_info);
+#endif
   // Skip over SbLogRawDumpStack's stack frame.
   for (int i = 1 + frames_to_skip; i < count; ++i) {
     char symbol[512];
+    void* address = stack[i];
     bool result =
         SbSystemSymbolize(stack[i], symbol, SB_ARRAY_SIZE_INT(symbol));
-    SbLogRawFormatF("\t%s [%p]\n", (result ? symbol : "<unknown>"), stack[i]);
+#if SB_IS(EVERGREEN_COMPATIBLE)
+    if (!result && valid_evergreen_info &&
+        IS_EVERGREEN_ADDRESS(stack[i], evergreen_info)) {
+      address = reinterpret_cast<void*>(reinterpret_cast<uint64_t>(stack[i]) -
+                                        evergreen_info.base_address);
+    }
+#endif
+    SbLogRawFormatF("\t%s [%p]\n", (result ? symbol : "<unknown>"), address);
     SbLogFlush();
   }
 }
diff --git a/src/starboard/shared/starboard/media/media_support_internal.h b/src/starboard/shared/starboard/media/media_support_internal.h
index 918b1c6..5e14541 100644
--- a/src/starboard/shared/starboard/media/media_support_internal.h
+++ b/src/starboard/shared/starboard/media/media_support_internal.h
@@ -54,24 +54,24 @@
 //        it indicates that the fps shouldn't be considered.
 // |decode_to_texture_required|: Whether or not the resulting video frames can
 //                               be decoded and used as textures by the GPU.
-SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-                                       int profile,
-                                       int level,
-                                       int bit_depth,
-                                       SbMediaPrimaryId primary_id,
-                                       SbMediaTransferId transfer_id,
-                                       SbMediaMatrixId matrix_id,
+                             int profile,
+                             int level,
+                             int bit_depth,
+                             SbMediaPrimaryId primary_id,
+                             SbMediaTransferId transfer_id,
+                             SbMediaMatrixId matrix_id,
 #endif  // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
-                                       int frame_width,
-                                       int frame_height,
-                                       int64_t bitrate,
-                                       int fps
+                             int frame_width,
+                             int frame_height,
+                             int64_t bitrate,
+                             int fps
 #if SB_API_VERSION >= 10
-                                       ,
-                                       bool decode_to_texture_required
+                             ,
+                             bool decode_to_texture_required
 #endif  // SB_API_VERSION >= 10
-                                       );
+                             );
 
 // Indicates whether this platform supports |audio_codec| at |bitrate|.
 // If |audio_codec| is not supported under any condition, this function
@@ -79,8 +79,7 @@
 //
 // |audio_codec|: The media's audio codec (|SbMediaAudioCodec|).
 // |bitrate|: The media's bitrate.
-SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
-                                       int64_t bitrate);
+bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, int64_t bitrate);
 
 #if !SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
 // Indicates whether this platform supports |transfer_id| as a transfer
@@ -88,8 +87,7 @@
 // function returns |false|.
 //
 // |transfer_id|: The id of transfer charateristics listed in SbMediaTransferId.
-SB_EXPORT bool SbMediaIsTransferCharacteristicsSupported(
-    SbMediaTransferId transfer_id);
+bool SbMediaIsTransferCharacteristicsSupported(SbMediaTransferId transfer_id);
 #endif  // !SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
 
 #ifdef __cplusplus
diff --git a/src/starboard/shared/starboard/memory_reporter_internal.h b/src/starboard/shared/starboard/memory_reporter_internal.h
index b600fd9..898d7dd 100644
--- a/src/starboard/shared/starboard/memory_reporter_internal.h
+++ b/src/starboard/shared/starboard/memory_reporter_internal.h
@@ -25,13 +25,11 @@
 
 // Internal function used to track mapped memory. This is used internally by
 // implementations of SbMemoryMap().
-SB_EXPORT void SbMemoryReporterReportMappedMemory(const void* memory,
-                                                  size_t size);
+void SbMemoryReporterReportMappedMemory(const void* memory, size_t size);
 
 // Internal function used to track mapped memory. This is used internally by
 // implementations of SbMemoryUnmap().
-SB_EXPORT void SbMemoryReporterReportUnmappedMemory(const void* memory,
-                                                    size_t size);
+void SbMemoryReporterReportUnmappedMemory(const void* memory, size_t size);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
index 8ebbfad..7e66ed3 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -68,9 +68,9 @@
                              scoped_ptr<AudioRendererSink> audio_renderer_sink,
                              const SbMediaAudioSampleInfo& audio_sample_info,
                              int max_cached_frames,
-                             int max_frames_per_append)
+                             int min_frames_per_append)
     : max_cached_frames_(max_cached_frames),
-      max_frames_per_append_(max_frames_per_append),
+      min_frames_per_append_(min_frames_per_append),
       channels_(audio_sample_info.number_of_channels),
       sink_sample_type_(GetSinkAudioSampleType(audio_renderer_sink.get())),
       bytes_per_frame_(media::GetBytesPerSample(sink_sample_type_) * channels_),
@@ -83,10 +83,10 @@
   SB_DLOG(INFO) << "Creating AudioRenderer with " << channels_ << " channels, "
                 << bytes_per_frame_ << " bytes per frame, "
                 << max_cached_frames_ << " max cached frames, and "
-                << max_frames_per_append_ << " max frames per append.";
+                << min_frames_per_append_ << " min frames per append.";
   SB_DCHECK(decoder_ != NULL);
-  SB_DCHECK(max_frames_per_append_ > 0);
-  SB_DCHECK(max_cached_frames_ >= max_frames_per_append_ * 2);
+  SB_DCHECK(min_frames_per_append_ > 0);
+  SB_DCHECK(max_cached_frames_ >= min_frames_per_append_ * 2);
 
   frame_buffers_[0] = &frame_buffer_[0];
 
@@ -100,7 +100,7 @@
   SB_DLOG(INFO) << "Destroying AudioRenderer with " << channels_
                 << " channels, " << bytes_per_frame_ << " bytes per frame, "
                 << max_cached_frames_ << " max cached frames, and "
-                << max_frames_per_append_ << " max frames per append.";
+                << min_frames_per_append_ << " min frames per append.";
   SB_DCHECK(BelongsToCurrentThread());
 }
 
@@ -396,12 +396,9 @@
 
   if (*is_eos_reached && *frames_in_buffer < max_cached_frames_) {
     // Fill silence frames on EOS to ensure keep the audio sink playing.
-    auto silence_frames_to_write = std::min(
-        max_cached_frames_ - *frames_in_buffer, max_frames_per_append_);
     auto start_offset =
         (*offset_in_frames + *frames_in_buffer) % max_cached_frames_;
-    silence_frames_to_write =
-        std::min(silence_frames_to_write, max_cached_frames_ - start_offset);
+    auto silence_frames_to_write = max_cached_frames_ - start_offset;
     SB_DCHECK(start_offset >= 0);
     SB_DCHECK(silence_frames_to_write >= 0);
     SB_DCHECK(start_offset + silence_frames_to_write <= max_cached_frames_);
@@ -413,10 +410,12 @@
 }
 
 void AudioRenderer::ConsumeFrames(int frames_consumed
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                   ,
                                   SbTime frames_consumed_at
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                   ) {
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   sink_callbacks_since_last_check_.increment();
@@ -432,9 +431,15 @@
 // 3. It doesn't affect frame presenting even with a 60fps video.
 // However, if this ever becomes a problem, we can smooth it out over multiple
 // ConsumeFrames() calls.
-#if !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  if (!kSbHasAsyncAudioFramesReporting) {
+    frames_consumed_at = SbTimeGetMonotonicNow();
+  }
+#elif SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION && \
+    !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   SbTime frames_consumed_at = SbTimeGetMonotonicNow();
-#endif  // !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION &&
+        // !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   ScopedTryLock lock(mutex_);
   if (lock.is_locked()) {
@@ -506,8 +511,8 @@
   time_stretcher_.Initialize(kSbMediaAudioSampleTypeFloat32, channels_,
                              destination_sample_rate);
 
-  // Start play after have enough buffered frames to play 0.2s.
-  buffered_frames_to_start_ = destination_sample_rate * 0.2;
+  buffered_frames_to_start_ = std::min(
+      destination_sample_rate / 5, max_cached_frames_ - min_frames_per_append_);
 
   if (decoded_sample_rate != destination_sample_rate ||
       decoded_sample_type != sink_sample_type_ ||
@@ -683,15 +688,15 @@
   int frames_in_buffer = static_cast<int>(total_frames_sent_to_sink_ -
                                           total_frames_consumed_by_sink_);
 
-  if (max_cached_frames_ - frames_in_buffer < max_frames_per_append_) {
+  if (max_cached_frames_ - frames_in_buffer < min_frames_per_append_) {
     *is_frame_buffer_full = true;
     return false;
   }
 
   int offset_to_append = total_frames_sent_to_sink_ % max_cached_frames_;
 
-  scoped_refptr<DecodedAudio> decoded_audio =
-      time_stretcher_.Read(max_frames_per_append_, playback_rate_);
+  scoped_refptr<DecodedAudio> decoded_audio = time_stretcher_.Read(
+      max_cached_frames_ - frames_in_buffer, playback_rate_);
   SB_DCHECK(decoded_audio);
 
   {
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index af4fd90..2f85ee7 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -61,13 +61,13 @@
   // 1. Avoid using too much memory.
   // 2. Have the audio cache full to simulate the state that the renderer can no
   //    longer accept more data.
-  // |max_frames_per_append| is the max number of frames that the audio renderer
+  // |min_frames_per_append| is the min number of frames that the audio renderer
   // tries to append to the sink buffer at once.
   AudioRenderer(scoped_ptr<AudioDecoder> decoder,
                 scoped_ptr<AudioRendererSink> audio_renderer_sink,
                 const SbMediaAudioSampleInfo& audio_sample_info,
                 int max_cached_frames,
-                int max_frames_per_append);
+                int min_frames_per_append);
   ~AudioRenderer();
 
   void WriteSample(const scoped_refptr<InputBuffer>& input_buffer);
@@ -103,7 +103,7 @@
   };
 
   const int max_cached_frames_;
-  const int max_frames_per_append_;
+  const int min_frames_per_append_;
   // |buffered_frames_to_start_| would be initialized in OnFirstOutput().
   // Before it's initialized, set it to a large number.
   int buffered_frames_to_start_ = 48 * 1024;
@@ -136,11 +136,14 @@
                        int* offset_in_frames,
                        bool* is_playing,
                        bool* is_eos_reached) override;
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   void ConsumeFrames(int frames_consumed, SbTime frames_consumed_at) override;
-#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   void ConsumeFrames(int frames_consumed) override;
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
   void UpdateVariablesOnSinkThread_Locked(SbTime system_time_on_consume_frames);
 
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h b/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h
index 4f4afe3..bd4ea3c 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_sink.h
@@ -34,12 +34,15 @@
                                  int* offset_in_frames,
                                  bool* is_playing,
                                  bool* is_eos_reached) = 0;
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
     virtual void ConsumeFrames(int frames_consumed,
                                SbTime frames_consumed_at) = 0;
-#else   //  SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#else   //  SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        //  SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
     virtual void ConsumeFrames(int frames_consumed) = 0;
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
    protected:
     ~RenderCallback() {}
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc
index 73d0877..c349638 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc
@@ -16,6 +16,7 @@
 
 #include "starboard/audio_sink.h"
 #include "starboard/common/log.h"
+#include "starboard/configuration_constants.h"
 #include "starboard/shared/starboard/thread_checker.h"
 
 namespace starboard {
@@ -167,22 +168,34 @@
 }
 
 // static
-void AudioRendererSinkImpl::ConsumeFramesFunc(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                              SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-                                              void* context) {
+void AudioRendererSinkImpl::ConsumeFramesFunc(
+    int frames_consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    SbTime frames_consumed_at,
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+    void* context) {
   AudioRendererSinkImpl* audio_renderer_sink =
       static_cast<AudioRendererSinkImpl*>(context);
   SB_DCHECK(audio_renderer_sink);
   SB_DCHECK(audio_renderer_sink->render_callback_);
 
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
-  audio_renderer_sink->render_callback_->ConsumeFrames(frames_consumed,
-                                                       frames_consumed_at);
-#else   // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  audio_renderer_sink->render_callback_->ConsumeFrames(
+      frames_consumed,
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+      kSbHasAsyncAudioFramesReporting ? frames_consumed_at
+                                      : (SbTime)kSbTimeMax);
+#else
+      frames_consumed_at);
+#endif
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
   audio_renderer_sink->render_callback_->ConsumeFrames(frames_consumed);
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 }
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h
index cd9f14b..d178bb0 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h
@@ -60,9 +60,11 @@
                                      bool* is_eos_reached,
                                      void* context);
   static void ConsumeFramesFunc(int frames_consumed,
-#if SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                 SbTime frames_consumed_at,
-#endif  // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
+        // SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
                                 void* context);
 
   ThreadChecker thread_checker_;
diff --git a/src/starboard/shared/starboard/player/filter/player_components.cc b/src/starboard/shared/starboard/player/filter/player_components.cc
index 6753841..51b1016 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.cc
+++ b/src/starboard/shared/starboard/player/filter/player_components.cc
@@ -52,12 +52,14 @@
   SB_DCHECK(audio_decoder);
   SB_DCHECK(audio_renderer_sink);
 
-  int max_cached_frames, max_frames_per_append;
-  GetAudioRendererParams(&max_cached_frames, &max_frames_per_append);
+  int max_cached_frames, min_frames_per_append;
+  GetAudioRendererParams(audio_parameters, &max_cached_frames,
+                         &min_frames_per_append);
+
   return make_scoped_ptr(
       new AudioRenderer(audio_decoder.Pass(), audio_renderer_sink.Pass(),
                         audio_parameters.audio_sample_info, max_cached_frames,
-                        max_frames_per_append));
+                        min_frames_per_append));
 }
 
 scoped_ptr<VideoRenderer> PlayerComponents::CreateVideoRenderer(
@@ -130,6 +132,30 @@
       video_parameters.player, kVideoSinkRenderInterval);
 }
 
+void PlayerComponents::GetAudioRendererParams(
+    const AudioParameters& audio_parameters,
+    int* max_cached_frames,
+    int* min_frames_per_append) const {
+  SB_DCHECK(max_cached_frames);
+  SB_DCHECK(min_frames_per_append);
+
+#if SB_API_VERSION >= 11
+  *min_frames_per_append = 1024;
+  // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
+  // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
+  int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
+      audio_parameters.audio_sample_info.number_of_channels,
+      SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
+          ? kSbMediaAudioSampleTypeFloat32
+          : kSbMediaAudioSampleTypeInt16Deprecated,
+      audio_parameters.audio_sample_info.samples_per_second);
+  *max_cached_frames = min_frames_required + *min_frames_per_append * 2;
+#else   // SB_API_VERSION >= 11
+  *max_cached_frames = 8 * 1024;
+  *min_frames_per_append = 1024;
+#endif  // SB_API_VERSION >= 11
+}
+
 }  // namespace filter
 }  // namespace player
 }  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index 6ea8e71..068e49c 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -92,10 +92,6 @@
       scoped_refptr<VideoRendererSink>* video_renderer_sink,
       std::string* error_message) = 0;
 
-  // Check AudioRenderer ctor for more details on the parameters.
-  virtual void GetAudioRendererParams(int* max_cached_frames,
-                                      int* max_frames_per_append) const = 0;
-
  protected:
   PlayerComponents() {}
 
@@ -110,6 +106,11 @@
       scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
       scoped_refptr<VideoRendererSink>* video_renderer_sink);
 
+  // Check AudioRenderer ctor for more details on the parameters.
+  void GetAudioRendererParams(const AudioParameters& audio_parameters,
+                              int* max_cached_frames,
+                              int* min_frames_per_append) const;
+
  private:
   SB_DISALLOW_COPY_AND_ASSIGN(PlayerComponents);
 };
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h
index 1d7d8c1..bcdd39d 100644
--- a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.h
@@ -52,15 +52,6 @@
                               video_render_algorithm, video_renderer_sink);
     return true;
   }
-
-  void GetAudioRendererParams(int* max_cached_frames,
-                              int* max_frames_per_append) const override {
-    SB_DCHECK(max_cached_frames);
-    SB_DCHECK(max_frames_per_append);
-
-    *max_cached_frames = 128 * 1024;
-    *max_frames_per_append = 16384;
-  }
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index 78331f7..501aed2 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -17,6 +17,7 @@
 #include <functional>
 #include <set>
 
+#include "base/logging.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/media.h"
 #include "starboard/memory.h"
@@ -270,6 +271,20 @@
   }
 };
 
+bool SkipAsyncAudioFramesReportingTests() {
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  if (!kSbHasAsyncAudioFramesReporting) {
+    SB_LOG(INFO) << "Platform does not have async audio frames reporting. "
+                    "Test skipped. ";
+  }
+  return !kSbHasAsyncAudioFramesReporting;
+#elif SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+  return false;
+#else
+  return true;
+#endif
+}
+
 // static
 const int AudioRendererTest::kDefaultSamplesPerSecond = 100000;
 
@@ -289,8 +304,12 @@
 }
 
 // TODO: adapt these tests for async audio frames reporting.
-#if !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
+    !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 TEST_F(AudioRendererTest, SunnyDay) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -302,7 +321,7 @@
 
   Seek(0);
 
-  FillRendererWithDecodedAudioAndWriteEOS();
+  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
 
   bool is_playing = true;
   bool is_eos_played = true;
@@ -335,12 +354,16 @@
 
   // Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
   // is incrementing in an expected manner.
-  const int frames_to_consume = frames_in_buffer / 4;
+  const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
   SbTime new_media_time;
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
 
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_to_consume, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(frames_to_consume);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_TRUE(is_playing);
@@ -349,7 +372,11 @@
   media_time = new_media_time;
 
   const int remaining_frames = frames_in_buffer - frames_to_consume;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(remaining_frames, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(remaining_frames);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_TRUE(is_playing);
@@ -361,6 +388,9 @@
 
 #if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
 TEST_F(AudioRendererTest, SunnyDayWithDoublePlaybackRateAndInt16Samples) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   const int kPlaybackRate = 2;
 
   // Resets |audio_renderer_sink_|, so all the gtest codes need to be below
@@ -420,14 +450,22 @@
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
 
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_to_consume, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(frames_to_consume);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_GT(new_media_time, media_time);
   media_time = new_media_time;
 
   const int remaining_frames = frames_in_buffer - frames_to_consume;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(remaining_frames, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(remaining_frames);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_GT(new_media_time, media_time);
@@ -437,6 +475,9 @@
 #endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
 
 TEST_F(AudioRendererTest, StartPlayBeforePreroll) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     ::testing::InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -450,7 +491,7 @@
 
   audio_renderer_->Play();
 
-  FillRendererWithDecodedAudioAndWriteEOS();
+  int frames_written = FillRendererWithDecodedAudioAndWriteEOS();
 
   SendDecoderOutput(new DecodedAudio);
 
@@ -472,12 +513,16 @@
 
   // Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
   // is incrementing in an expected manner.
-  const int frames_to_consume = frames_in_buffer / 4;
+  const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
   SbTime new_media_time;
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
 
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_to_consume, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(frames_to_consume);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_TRUE(is_playing);
@@ -486,7 +531,11 @@
   media_time = new_media_time;
 
   const int remaining_frames = frames_in_buffer - frames_to_consume;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(remaining_frames, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(remaining_frames);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_TRUE(is_playing);
@@ -497,6 +546,9 @@
 }
 
 TEST_F(AudioRendererTest, DecoderReturnsEOSWithoutAnyData) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     ::testing::InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -534,6 +586,9 @@
 
 // Test decoders that take many input samples before returning any output.
 TEST_F(AudioRendererTest, DecoderConsumeAllInputBeforeReturningData) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     ::testing::InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -577,6 +632,9 @@
 }
 
 TEST_F(AudioRendererTest, MoreNumberOfOuputBuffersThanInputBuffers) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     ::testing::InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -636,12 +694,16 @@
 
   // Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
   // is incrementing in an expected manner.
-  const int frames_to_consume = frames_in_buffer / 4;
+  const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
   SbTime new_media_time;
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
 
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_to_consume, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(frames_to_consume);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_TRUE(is_playing);
@@ -650,7 +712,11 @@
   media_time = new_media_time;
 
   const int remaining_frames = frames_in_buffer - frames_to_consume;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(remaining_frames, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(remaining_frames);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_TRUE(is_playing);
@@ -661,6 +727,9 @@
 }
 
 TEST_F(AudioRendererTest, LessNumberOfOuputBuffersThanInputBuffers) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     ::testing::InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -724,19 +793,27 @@
 
   // Consume frames in two batches, so we can test if |GetCurrentMediaTime()|
   // is incrementing in an expected manner.
-  const int frames_to_consume = frames_in_buffer / 4;
+  const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 2;
   SbTime new_media_time;
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
 
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_to_consume, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(frames_to_consume);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_GE(new_media_time, media_time);
   media_time = new_media_time;
 
   const int remaining_frames = frames_in_buffer - frames_to_consume;
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(remaining_frames, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(remaining_frames);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_GE(new_media_time, media_time);
@@ -745,6 +822,9 @@
 }
 
 TEST_F(AudioRendererTest, Seek) {
+  if (SkipAsyncAudioFramesReportingTests())
+    return;
+
   {
     ::testing::InSequence seq;
     EXPECT_CALL(*audio_renderer_sink_, Stop()).Times(AnyNumber());
@@ -792,18 +872,22 @@
   // Consume frames in multiple batches, so we can test if
   // |GetCurrentMediaTime()| is incrementing in an expected manner.
   const double seek_time = 0.5 * kSbTimeSecond;
-  const int frames_to_consume = frames_in_buffer / 10;
+  const int frames_to_consume = std::min(frames_written, frames_in_buffer) / 10;
   SbTime new_media_time;
 
   EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
 
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_to_consume, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   renderer_callback_->ConsumeFrames(frames_to_consume);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_GE(new_media_time, media_time);
   Seek(seek_time);
 
-  frames_written = FillRendererWithDecodedAudioAndWriteEOS();
+  frames_written += FillRendererWithDecodedAudioAndWriteEOS();
 
   EXPECT_GE(audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played,
                                                  &is_underflow),
@@ -819,15 +903,19 @@
   EXPECT_GE(offset_in_frames, 0);
   EXPECT_TRUE(is_playing);
   EXPECT_TRUE(is_eos_reached);
-  const int remaining_frames = frames_in_buffer - offset_in_frames;
-  renderer_callback_->ConsumeFrames(remaining_frames);
+#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_in_buffer, (SbTime)kSbTimeMax);
+#else   // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+  renderer_callback_->ConsumeFrames(frames_in_buffer);
+#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
   new_media_time = audio_renderer_->GetCurrentMediaTime(
       &is_playing, &is_eos_played, &is_underflow);
   EXPECT_GE(new_media_time, seek_time);
 
   EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
 }
-#endif  // !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
+#endif  // SB_API_VERSION < SB_FEATURE_RUNTIME_CONFIGS_VERSION &&
+        // !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
 
 // TODO: Add more Seek tests.
 
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index fd6f379..c8dfbdd 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -33,6 +33,49 @@
     FilterBasedPlayerWorkerHandler;
 using starboard::shared::starboard::player::PlayerWorker;
 
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
+SbPlayer SbPlayerCreate(SbWindow window,
+                        const SbPlayerCreationParam* creation_param,
+                        SbPlayerDeallocateSampleFunc sample_deallocate_func,
+                        SbPlayerDecoderStatusFunc decoder_status_func,
+                        SbPlayerStatusFunc player_status_func,
+                        SbPlayerErrorFunc player_error_func,
+                        void* context,
+                        SbDecodeTargetGraphicsContextProvider* provider) {
+  if (!creation_param) {
+    SB_LOG(ERROR) << "CreationParam cannot be null.";
+    return kSbPlayerInvalid;
+  }
+  if (!creation_param->audio_mime) {
+    SB_LOG(ERROR) << "creation_param->audio_mime cannot be null.";
+    return kSbPlayerInvalid;
+  }
+  if (!creation_param->video_mime) {
+    SB_LOG(ERROR) << "creation_param->video_mime cannot be null.";
+    return kSbPlayerInvalid;
+  }
+  if (!creation_param->max_video_capabilities) {
+    SB_LOG(ERROR) << "creation_param->max_video_capabilities cannot be null.";
+    return kSbPlayerInvalid;
+  }
+
+  SB_LOG(INFO) << "SbPlayerCreate() called with audio mime \""
+               << creation_param->audio_mime << "\", video mime \""
+               << creation_param->video_mime
+               << "\", and max video capabilities \""
+               << creation_param->max_video_capabilities << "\".";
+
+  SbMediaAudioCodec audio_codec = creation_param->audio_sample_info.codec;
+  SbMediaVideoCodec video_codec = creation_param->video_sample_info.codec;
+  SbDrmSystem drm_system = creation_param->drm_system;
+  const SbMediaAudioSampleInfo* audio_sample_info =
+      &creation_param->audio_sample_info;
+  const char* max_video_capabilities = creation_param->max_video_capabilities;
+  const auto output_mode = creation_param->output_mode;
+
+#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 SbPlayer SbPlayerCreate(SbWindow window,
                         SbMediaVideoCodec video_codec,
                         SbMediaAudioCodec audio_codec,
@@ -53,6 +96,8 @@
                         void* context,
                         SbPlayerOutputMode output_mode,
                         SbDecodeTargetGraphicsContextProvider* provider) {
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
   SB_UNREFERENCED_PARAMETER(window);
 #if SB_API_VERSION >= 11
   SB_UNREFERENCED_PARAMETER(max_video_capabilities);
@@ -120,10 +165,17 @@
     return kSbPlayerInvalid;
   }
 
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+  if (SbPlayerGetPreferredOutputMode(creation_param) != output_mode) {
+    SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
+    return kSbPlayerInvalid;
+  }
+#else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
   if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) {
     SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
     return kSbPlayerInvalid;
   }
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
   UpdateActiveSessionPlatformPlaybackState(kPlaying);
 
diff --git a/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc b/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
index 9e991d7..f69b6d7 100644
--- a/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
+++ b/src/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
@@ -14,43 +14,61 @@
 
 #include "starboard/player.h"
 
-#include "starboard/configuration.h"
+#include <algorithm>
 
-#if SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#include "starboard/configuration.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+
+#if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
 SbPlayerOutputMode SbPlayerGetPreferredOutputMode(
-    const SbMediaAudioSampleInfo* audio_sample_info,
-    const SbMediaVideoSampleInfo* video_sample_info,
-    SbDrmSystem drm_system,
-    const char* max_video_capabilities) {
-  SB_UNREFERENCED_PARAMETER(max_video_capabilities);
+    const SbPlayerCreationParam* creation_param) {
+  using starboard::shared::starboard::player::filter::VideoDecoder;
 
-  if (!audio_sample_info) {
-    SB_LOG(ERROR) << "audio_sample_info cannot be NULL";
+  if (!creation_param) {
+    SB_LOG(ERROR) << "creation_param cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (!video_sample_info) {
-    SB_LOG(ERROR) << "video_sample_info cannot be NULL";
+  if (!creation_param->audio_mime) {
+    SB_LOG(ERROR) << "creation_param->audio_mime cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (!max_video_capabilities) {
-    SB_LOG(ERROR) << "max_video_capabilities cannot be NULL";
+  if (!creation_param->video_mime) {
+    SB_LOG(ERROR) << "creation_param->video_mime cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut,
-                                  video_sample_info->codec, drm_system)) {
-    return kSbPlayerOutputModePunchOut;
+  if (!creation_param->max_video_capabilities) {
+    SB_LOG(ERROR) << "creation_param->max_video_capabilities cannot be NULL";
+    return kSbPlayerOutputModeInvalid;
   }
-  if (SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture,
-                                  video_sample_info->codec, drm_system)) {
-    return kSbPlayerOutputModeDecodeToTexture;
+
+  auto codec = creation_param->video_sample_info.codec;
+  auto drm_system = creation_param->drm_system;
+
+  SbPlayerOutputMode output_modes_to_check[] = {
+      kSbPlayerOutputModePunchOut, kSbPlayerOutputModeDecodeToTexture,
+  };
+
+  // Check |kSbPlayerOutputModeDecodeToTexture| first if the caller prefers it.
+  if (creation_param->output_mode == kSbPlayerOutputModeDecodeToTexture) {
+    std::swap(output_modes_to_check[0], output_modes_to_check[1]);
+  }
+
+  if (VideoDecoder::OutputModeSupported(output_modes_to_check[0], codec,
+                                        drm_system)) {
+    return output_modes_to_check[0];
+  }
+
+  if (VideoDecoder::OutputModeSupported(output_modes_to_check[1], codec,
+                                        drm_system)) {
+    return output_modes_to_check[1];
   }
 
   SB_NOTREACHED();
   return kSbPlayerOutputModeInvalid;
 }
 
-#endif  // SB_HAS(PLAYER_GET_PREFERRED_OUTPUT_MODE)
+#endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/shared/starboard/player/player_output_mode_supported.cc b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
index e3af869..beadfb7 100644
--- a/src/starboard/shared/starboard/player/player_output_mode_supported.cc
+++ b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
@@ -18,6 +18,8 @@
 #include "starboard/configuration.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 
+#if !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+
 bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode,
                                  SbMediaVideoCodec codec,
                                  SbDrmSystem drm_system) {
@@ -25,3 +27,4 @@
       OutputModeSupported(output_mode, codec, drm_system);
 }
 
+#endif  // !SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
diff --git a/src/starboard/shared/starboard/player/testdata/sintel_399_av1.dmp.sha1 b/src/starboard/shared/starboard/player/testdata/sintel_399_av1.dmp.sha1
index 1b7d053..b2a1296 100644
--- a/src/starboard/shared/starboard/player/testdata/sintel_399_av1.dmp.sha1
+++ b/src/starboard/shared/starboard/player/testdata/sintel_399_av1.dmp.sha1
@@ -1 +1 @@
-49eabd9cc00ccec48209c7fbe3b8f2f30649dc5f
\ No newline at end of file
+6efec071ba03d758d76a00e6ca8f968e5cf65e62
\ No newline at end of file
diff --git a/src/starboard/shared/stub/blitter_blit_rect_to_rect.cc b/src/starboard/shared/stub/blitter_blit_rect_to_rect.cc
deleted file mode 100644
index ec5dc26..0000000
--- a/src/starboard/shared/stub/blitter_blit_rect_to_rect.cc
+++ /dev/null
@@ -1,26 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterBlitRectToRect(SbBlitterContext context,
-                             SbBlitterSurface source_surface,
-                             SbBlitterRect src_rect,
-                             SbBlitterRect dst_rect) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_blit_rect_to_rect_tiled.cc b/src/starboard/shared/stub/blitter_blit_rect_to_rect_tiled.cc
deleted file mode 100644
index 7823d2e..0000000
--- a/src/starboard/shared/stub/blitter_blit_rect_to_rect_tiled.cc
+++ /dev/null
@@ -1,26 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterBlitRectToRectTiled(SbBlitterContext context,
-                                  SbBlitterSurface source_surface,
-                                  SbBlitterRect src_rect,
-                                  SbBlitterRect dst_rect) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_blit_rects_to_rects.cc b/src/starboard/shared/stub/blitter_blit_rects_to_rects.cc
deleted file mode 100644
index 0d6f19f..0000000
--- a/src/starboard/shared/stub/blitter_blit_rects_to_rects.cc
+++ /dev/null
@@ -1,27 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterBlitRectsToRects(SbBlitterContext context,
-                               SbBlitterSurface source_surface,
-                               const SbBlitterRect* src_rects,
-                               const SbBlitterRect* dst_rects,
-                               int num_rects) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_create_context.cc b/src/starboard/shared/stub/blitter_create_context.cc
deleted file mode 100644
index 206d574..0000000
--- a/src/starboard/shared/stub/blitter_create_context.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-SbBlitterContext SbBlitterCreateContext(SbBlitterDevice device) {
-  return kSbBlitterInvalidContext;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_create_default_device.cc b/src/starboard/shared/stub/blitter_create_default_device.cc
deleted file mode 100644
index 0b77ab6..0000000
--- a/src/starboard/shared/stub/blitter_create_default_device.cc
+++ /dev/null
@@ -1,26 +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/blitter.h"
-
-#include "base/logging.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-SbBlitterDevice SbBlitterCreateDefaultDevice() {
-  SB_LOG(ERROR) << "SbBlitter API not supported on this platform.";
-  return kSbBlitterInvalidDevice;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_create_pixel_data.cc b/src/starboard/shared/stub/blitter_create_pixel_data.cc
deleted file mode 100644
index 3f63bb8..0000000
--- a/src/starboard/shared/stub/blitter_create_pixel_data.cc
+++ /dev/null
@@ -1,27 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-SbBlitterPixelData SbBlitterCreatePixelData(
-    SbBlitterDevice device,
-    int width,
-    int height,
-    SbBlitterPixelDataFormat pixel_format) {
-  return kSbBlitterInvalidPixelData;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_create_render_target_surface.cc b/src/starboard/shared/stub/blitter_create_render_target_surface.cc
deleted file mode 100644
index 6d31b3b..0000000
--- a/src/starboard/shared/stub/blitter_create_render_target_surface.cc
+++ /dev/null
@@ -1,27 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-SbBlitterSurface SbBlitterCreateRenderTargetSurface(
-    SbBlitterDevice device,
-    int width,
-    int height,
-    SbBlitterSurfaceFormat surface_format) {
-  return kSbBlitterInvalidSurface;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_create_surface_from_pixel_data.cc b/src/starboard/shared/stub/blitter_create_surface_from_pixel_data.cc
deleted file mode 100644
index 94810c2..0000000
--- a/src/starboard/shared/stub/blitter_create_surface_from_pixel_data.cc
+++ /dev/null
@@ -1,25 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-SbBlitterSurface SbBlitterCreateSurfaceFromPixelData(
-    SbBlitterDevice device,
-    SbBlitterPixelData pixel_data) {
-  return kSbBlitterInvalidSurface;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_create_swap_chain_from_window.cc b/src/starboard/shared/stub/blitter_create_swap_chain_from_window.cc
deleted file mode 100644
index 05c4301..0000000
--- a/src/starboard/shared/stub/blitter_create_swap_chain_from_window.cc
+++ /dev/null
@@ -1,24 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-SbBlitterSwapChain SbBlitterCreateSwapChainFromWindow(SbBlitterDevice device,
-                                                      SbWindow window) {
-  return kSbBlitterInvalidSwapChain;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_destroy_context.cc b/src/starboard/shared/stub/blitter_destroy_context.cc
deleted file mode 100644
index d48e1ea..0000000
--- a/src/starboard/shared/stub/blitter_destroy_context.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterDestroyContext(SbBlitterContext context) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_destroy_pixel_data.cc b/src/starboard/shared/stub/blitter_destroy_pixel_data.cc
deleted file mode 100644
index c966170..0000000
--- a/src/starboard/shared/stub/blitter_destroy_pixel_data.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterDestroyPixelData(SbBlitterPixelData pixel_data) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_destroy_surface.cc b/src/starboard/shared/stub/blitter_destroy_surface.cc
deleted file mode 100644
index 47756c4..0000000
--- a/src/starboard/shared/stub/blitter_destroy_surface.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterDestroySurface(SbBlitterSurface surface) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_destroy_swap_chain.cc b/src/starboard/shared/stub/blitter_destroy_swap_chain.cc
deleted file mode 100644
index 49a8562..0000000
--- a/src/starboard/shared/stub/blitter_destroy_swap_chain.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterDestroySwapChain(SbBlitterSwapChain swap_chain) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_download_surface_pixels.cc b/src/starboard/shared/stub/blitter_download_surface_pixels.cc
deleted file mode 100644
index 11c0fc3..0000000
--- a/src/starboard/shared/stub/blitter_download_surface_pixels.cc
+++ /dev/null
@@ -1,26 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterDownloadSurfacePixels(SbBlitterSurface surface,
-                                    SbBlitterPixelDataFormat pixel_format,
-                                    int pitch_in_bytes,
-                                    void* out_pixel_data) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_fill_rect.cc b/src/starboard/shared/stub/blitter_fill_rect.cc
deleted file mode 100644
index bc84d3a..0000000
--- a/src/starboard/shared/stub/blitter_fill_rect.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterFillRect(SbBlitterContext context, SbBlitterRect rect) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_flip_swap_chain.cc b/src/starboard/shared/stub/blitter_flip_swap_chain.cc
deleted file mode 100644
index da0f88b..0000000
--- a/src/starboard/shared/stub/blitter_flip_swap_chain.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterFlipSwapChain(SbBlitterSwapChain swap_chain) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_flush_context.cc b/src/starboard/shared/stub/blitter_flush_context.cc
deleted file mode 100644
index ca0b932..0000000
--- a/src/starboard/shared/stub/blitter_flush_context.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-bool SbBlitterFlushContext(SbBlitterContext context) {
-  return false;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_get_max_contexts.cc b/src/starboard/shared/stub/blitter_get_max_contexts.cc
deleted file mode 100644
index c04255a..0000000
--- a/src/starboard/shared/stub/blitter_get_max_contexts.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-int SbBlitterGetMaxContexts(SbBlitterDevice device) {
-  return -1;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_get_pixel_data_pitch_in_bytes.cc b/src/starboard/shared/stub/blitter_get_pixel_data_pitch_in_bytes.cc
deleted file mode 100644
index 1ab06ea..0000000
--- a/src/starboard/shared/stub/blitter_get_pixel_data_pitch_in_bytes.cc
+++ /dev/null
@@ -1,23 +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/blitter.h"
-
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-
-int SbBlitterGetPixelDataPitchInBytes(SbBlitterPixelData pixel_data) {
-  return -1;
-}
-
-#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
diff --git a/src/starboard/shared/stub/blitter_get_pixel_data_pointer.cc b/src/starboard/shared/stub/blitter_get_pixel_data_pointer.cc
deleted file mode 100644
index 96b27f0..0000000
--- a/src/starboard/shared/stub/blitter_get_pixel_data_pointer.cc
+++ /dev/null
@@ -1,23 +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.
-