Import Cobalt 21.master.0.260628
diff --git a/src/DOCKER_README.txt b/src/DOCKER_README.txt
new file mode 100644
index 0000000..edf0c0a
--- /dev/null
+++ b/src/DOCKER_README.txt
@@ -0,0 +1,18 @@
+Ensure you have a COBALT_SRC environment variable set up,
+pointing to your local copy of Cobalt. e.g. ~/cobalt/src
+
+Then choose your target, e.g. stub, linux-x64x11, etc..
+
+then you can build that for that platform via:
+
+docker-compose run <target>
+
+By default, a debug build will be build. You can override this with an env
+variable.
+
+e.g. docker-compose run -e CONFIG=devel <target>
+where config is one of the four optimization levels, debug, devel, qa and gold.
+See https://cobalt.googlesource.com/cobalt/+/master/src/README.md
+
+Builds will be available in your ${COBALT_SRC}/out dir
+
diff --git a/src/base/logging.h b/src/base/logging.h
index b916a0e..ba07729 100644
--- a/src/base/logging.h
+++ b/src/base/logging.h
@@ -474,12 +474,12 @@
 // 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)))              \
+#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) == true)))      \
       << LOG_ONCE_MSG
 #endif  // defined(OFFICIAL_BUILD)
 
diff --git a/src/build/build_config.h b/src/build/build_config.h
index 6135125..d7a522a 100644
--- a/src/build/build_config.h
+++ b/src/build/build_config.h
@@ -133,16 +133,7 @@
 # else   // SB_IS(BIG_ENDIAN)
 #  define ARCH_CPU_ARMEL 1
 # endif  // SB_IS(BIG_ENDIAN)
-#elif SB_IS(ARCH_PPC)
-# define ARCH_CPU_PPC_FAMILY 1
-#elif SB_IS(ARCH_MIPS)
-# define ARCH_CPU_MIPS_FAMILY 1
-# if SB_IS(BIG_ENDIAN)
-#  define ARCH_CPU_MIPS 1
-# else   // SB_IS(BIG_ENDIAN)
-#  define ARCH_CPU_MIPSEL 1
-# endif  // SB_IS(BIG_ENDIAN)
-#endif  // SB_IS(ARCH_X86)
+#endif
 #elif defined(_M_X64) || defined(__x86_64__)
 #define ARCH_CPU_X86_FAMILY 1
 #define ARCH_CPU_X86_64 1
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index fb5d552..91fa034 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -33,6 +33,8 @@
 
 _PORT_SELECTION_RETRY_LIMIT = 10
 _PORT_SELECTION_RANGE = [5000, 7000]
+# List of blocked ports.
+_RESTRICTED_PORTS = [6000, 6665, 6666, 6667, 6668, 6669, 6697]
 _SERVER_EXIT_TIMEOUT_SECONDS = 30
 # These tests can only be run on platforms whose app launcher can send suspend/
 # resume signals.
@@ -215,8 +217,10 @@
       socks.append((address, socket.socket(socket.AF_INET, socket.SOCK_STREAM)))
     try:
       for _ in range(_PORT_SELECTION_RETRY_LIMIT):
-        port = random.randint(_PORT_SELECTION_RANGE[0],
-                              _PORT_SELECTION_RANGE[1])
+        port = random.choice([
+            i for i in range(_PORT_SELECTION_RANGE[0], _PORT_SELECTION_RANGE[1])
+            if i not in _RESTRICTED_PORTS
+        ])
         unused = True
         for sock in socks:
           result = sock[1].connect_ex((sock[0], port))
diff --git a/src/cobalt/black_box_tests/tests/web_platform_tests.py b/src/cobalt/black_box_tests/tests/web_platform_tests.py
index 9f5f468..9c27785 100644
--- a/src/cobalt/black_box_tests/tests/web_platform_tests.py
+++ b/src/cobalt/black_box_tests/tests/web_platform_tests.py
@@ -21,6 +21,7 @@
 from cobalt.black_box_tests import black_box_tests
 from cobalt.black_box_tests.web_platform_test_server import WebPlatformTestServer
 from starboard.tools import abstract_launcher
+from starboard.tools import build
 from starboard.tools.testing import test_filter
 
 
@@ -38,6 +39,20 @@
       target_params = []
 
       filters = self.cobalt_config.GetWebPlatformTestFilters()
+
+      # Regardless of our own platform, if we are Evergreen we also need to
+      # filter the tests that are filtered by the underlying platform. For
+      # example, the 'evergreen-arm-hardfp' needs to filter the 'raspi-2'
+      # filtered tests when it is running on a Raspberry Pi 2.
+      if self.launcher_params.IsEvergreen():
+        loader_platform_config = build.GetPlatformConfig(
+            self.launcher_params.loader_platform)
+        loader_platform_cobalt_config = loader_platform_config.GetApplicationConfiguration(
+            'cobalt')
+        for filter in loader_platform_cobalt_config.GetWebPlatformTestFilters():
+          if filter not in filters:
+            filters.append(filter)
+
       used_filters = []
 
       for filter in filters:
@@ -64,7 +79,9 @@
       if self.launcher_params.IsEvergreen():
         # TODO: Remove this once the memory leaks when running executables in
         # Evergreen mode have been resolved.
-        env_variables_config = {'ASAN_OPTIONS': 'detect_leaks=0:intercept_tls_get_addr=0'}
+        env_variables_config = {
+            'ASAN_OPTIONS': 'detect_leaks=0:intercept_tls_get_addr=0'
+        }
       else:
         env_variables_config = {'ASAN_OPTIONS': 'intercept_tls_get_addr=0'}
 
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 176cdb4..f4a248e 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -25,6 +25,7 @@
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/metrics/statistics_recorder.h"
 #include "base/optional.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
@@ -59,6 +60,7 @@
 #include "cobalt/browser/storage_upgrade_handler.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/browser/user_agent_string.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/loader/image/image_decoder.h"
 #include "cobalt/math/size.h"
 #include "cobalt/script/javascript_engine.h"
@@ -248,7 +250,8 @@
     fallback_splash_screen_string =
         command_line->GetSwitchValueASCII(switches::kFallbackSplashScreenURL);
   } else {
-    fallback_splash_screen_string = COBALT_FALLBACK_SPLASH_SCREEN_URL;
+    fallback_splash_screen_string = configuration::Configuration::GetInstance()
+                                        ->CobaltFallbackSplashScreenUrl();
   }
   if (IsStringNone(fallback_splash_screen_string)) {
     return base::Optional<GURL>();
@@ -449,6 +452,11 @@
 
 }  // namespace
 
+// Helper stub to disable histogram tracking in StatisticsRecorder
+struct RecordCheckerStub : public base::RecordHistogramChecker {
+  bool ShouldRecord(uint64_t) const override { return false; }
+};
+
 // Static user logs
 ssize_t Application::available_memory_ = 0;
 int64 Application::lifetime_in_ms_ = 0;
@@ -518,6 +526,11 @@
   std::string language = base::GetSystemLanguage();
   base::LocalizedStrings::GetInstance()->Initialize(language);
 
+  // Disable histogram tracking before TaskScheduler creates StatisticsRecorder
+  // instances.
+  auto record_checker = std::make_unique<RecordCheckerStub>();
+  base::StatisticsRecorder::SetRecordChecker(std::move(record_checker));
+
   // A one-per-process task scheduler is needed for usage of APIs in
   // base/post_task.h which will be used by some net APIs like
   // URLRequestContext;
@@ -1023,9 +1036,7 @@
       DLOG(INFO) << "Finished suspending.";
       break;
     case kSbEventTypeResume:
-#if SB_API_VERSION >= 10
       DCHECK(SbSystemSupportsResume());
-#endif  // SB_API_VERSION >= 10
       DLOG(INFO) << "Got resume event.";
       app_status_ = kPausedAppStatus;
       ++app_resume_count_;
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 9292424..5ffbbc5 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -121,16 +121,14 @@
         'COBALT_OFFSCREEN_TARGET_CACHE_SIZE_IN_BYTES=<(offscreen_target_cache_size_in_bytes)',
         'COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES=<(software_surface_cache_size_in_bytes)',
         'COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES=<(mozjs_garbage_collection_threshold_in_bytes)',
-        'COBALT_MAX_CPU_USAGE_IN_BYTES=<(max_cobalt_cpu_usage)',
-        'COBALT_MAX_GPU_USAGE_IN_BYTES=<(max_cobalt_gpu_usage)',
         'COBALT_REDUCE_CPU_MEMORY_BY=<(reduce_cpu_memory_by)',
         'COBALT_REDUCE_GPU_MEMORY_BY=<(reduce_gpu_memory_by)',
       ],
       'dependencies': [
-        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/account/account.gyp:account',
         '<(DEPTH)/cobalt/audio/audio.gyp:audio',
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/css_parser/css_parser.gyp:css_parser',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
@@ -150,6 +148,7 @@
         '<(DEPTH)/cobalt/script/engine.gyp:engine',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/cobalt/sso/sso.gyp:sso',
+        '<(DEPTH)/cobalt/subtlecrypto/subtlecrypto.gyp:subtlecrypto',
         '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
         '<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event',
         '<(DEPTH)/cobalt/ui_navigation/ui_navigation.gyp:ui_navigation',
@@ -179,6 +178,12 @@
         '<(SHARED_INTERMEDIATE_DIR)',
       ],
       'conditions': [
+        ['max_cobalt_cpu_usage != -1', {
+          'defines': [ 'COBALT_MAX_CPU_USAGE_IN_BYTES' ],
+        }],
+        ['max_cobalt_gpu_usage != -1', {
+          'defines': [ 'COBALT_MAX_GPU_USAGE_IN_BYTES' ],
+        }],
         ['enable_about_scheme == 1', {
           'defines': [ 'ENABLE_ABOUT_SCHEME' ],
         }],
@@ -206,6 +211,10 @@
           'dependencies': [
             '<(DEPTH)/cobalt/updater/updater.gyp:updater',
           ],
+        }, {
+          'dependencies': [
+            '<@(cobalt_platform_dependencies)',
+          ],
         }],
       ],
     },
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index df35efe..2d9c0f1 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -114,6 +114,7 @@
         '../dom/intersection_observer_entry.idl',
         '../dom/keyboard_event.idl',
         '../dom/location.idl',
+        '../dom/lottie_player.idl',
         '../dom/media_error.idl',
         '../dom/media_query_list.idl',
         '../dom/media_source.idl',
@@ -205,6 +206,9 @@
         '../speech/speech_synthesis_utterance.idl',
         '../speech/speech_synthesis_voice.idl',
 
+        '../subtlecrypto/crypto_key.idl',
+        '../subtlecrypto/subtle_crypto.idl',
+
         '../web_animations/animatable.idl',
         '../web_animations/animation.idl',
         '../web_animations/animation_effect_read_only.idl',
@@ -285,6 +289,12 @@
         '../page_visibility/visibility_state.idl',
         '../speech/speech_recognition_error_code.idl',
         '../speech/speech_synthesis_error_code.idl',
+        '../subtlecrypto/aes_ctr_params.idl',
+        '../subtlecrypto/algorithm.idl',
+        '../subtlecrypto/import_key_algorithm_params.idl',
+        '../subtlecrypto/key_format.idl',
+        '../subtlecrypto/key_type.idl',
+        '../subtlecrypto/key_usage.idl',
         '../web_animations/animation_fill_mode.idl',
         '../web_animations/animation_playback_direction.idl',
         '../websocket/close_event_init.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 735d581..aa71610 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -38,6 +38,7 @@
 #include "cobalt/browser/switches.h"
 #include "cobalt/browser/user_agent_string.h"
 #include "cobalt/browser/webapi_extension.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/cssom/viewport_size.h"
 #include "cobalt/dom/csp_delegate_factory.h"
 #include "cobalt/dom/input_event_init.h"
@@ -600,7 +601,8 @@
     options.on_screen_keyboard_bridge = on_screen_keyboard_bridge_.get();
   }
   options.image_cache_capacity_multiplier_when_playing_video =
-      COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO;
+      configuration::Configuration::GetInstance()
+          ->CobaltImageCacheCapacityMultiplierWhenPlayingVideo();
   if (input_device_manager_) {
     options.camera_3d = input_device_manager_->camera_3d();
   }
@@ -1636,10 +1638,8 @@
       system_window_.get());
   InstantiateRendererModule();
 
-#if SB_API_VERSION >= 10
   options_.media_module_options.allow_resume_after_suspend =
       SbSystemSupportsResume();
-#endif  // SB_API_VERSION >= 10
   media_module_.reset(new media::MediaModule(system_window_.get(),
                                              GetResourceProvider(),
                                              options_.media_module_options));
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index 471e9ec..5a9c801 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -56,35 +56,31 @@
         '<(DEPTH)/cobalt/demos/demos.gyp:copy_demos',
       ],
     },
+    {
+      'target_name': 'snapshot_app_stats',
+      'type': '<(final_executable_type)',
+      'sources': [
+        'snapshot_app_stats.cc',
+      ],
+      'dependencies': [
+        'cobalt',
+        '<(DEPTH)/cobalt/browser/browser.gyp:browser',
+        '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
+      ],
+    },
+    {
+      'target_name': 'snapshot_app_stats_deploy',
+      'type': 'none',
+      'dependencies': [
+        'snapshot_app_stats',
+      ],
+      'variables': {
+        'executable_name': 'snapshot_app_stats',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
   ],
   'conditions': [
-    ['build_snapshot_app_stats', {
-      'targets': [
-        {
-          'target_name': 'snapshot_app_stats',
-          'type': '<(final_executable_type)',
-          'sources': [
-            'snapshot_app_stats.cc',
-          ],
-          'dependencies': [
-            'cobalt',
-            '<(DEPTH)/cobalt/browser/browser.gyp:browser',
-            '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
-          ],
-        },
-        {
-          'target_name': 'snapshot_app_stats_deploy',
-          'type': 'none',
-          'dependencies': [
-            'snapshot_app_stats',
-          ],
-          'variables': {
-            'executable_name': 'snapshot_app_stats',
-          },
-          'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
-        },
-      ]
-    }],
     ['final_executable_type == "shared_library" and sb_evergreen != 1', {
       'targets': [
         {
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
index e50ae16..07d4c08 100644
--- a/src/cobalt/browser/memory_settings/auto_mem.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -36,6 +36,7 @@
 #include "cobalt/browser/memory_settings/pretty_print.h"
 #include "cobalt/browser/memory_settings/scaling_function.h"
 #include "cobalt/browser/switches.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/loader/image/image_decoder.h"
 #include "cobalt/math/clamp.h"
 
@@ -557,12 +558,13 @@
           CalculateOffscreenTargetCacheSizeInBytes(ui_resolution));
   offscreen_target_cache_size_in_bytes_->set_memory_scaling_function(
       MakeLinearMemoryScaler(0.25, 1.0));
-#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
-  offscreen_target_cache_size_in_bytes_->set_memory_type(MemorySetting::kGPU);
-#else
-  offscreen_target_cache_size_in_bytes_->set_memory_type(
-      MemorySetting::kNotApplicable);
-#endif
+  if (std::string(configuration::Configuration::GetInstance()
+                      ->CobaltRasterizerType()) == "direct-gles") {
+    offscreen_target_cache_size_in_bytes_->set_memory_type(MemorySetting::kGPU);
+  } else {
+    offscreen_target_cache_size_in_bytes_->set_memory_type(
+        MemorySetting::kNotApplicable);
+  }
   EnsureValuePositive(offscreen_target_cache_size_in_bytes_.get());
 
   // Final stage: Check that all constraining functions are monotonically
diff --git a/src/cobalt/browser/memory_settings/auto_mem_settings.cc b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
index 9ef9a59..413cc86 100644
--- a/src/cobalt/browser/memory_settings/auto_mem_settings.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
@@ -26,6 +26,7 @@
 #include "base/strings/string_util.h"
 #include "cobalt/browser/memory_settings/constants.h"
 #include "cobalt/browser/switches.h"
+#include "cobalt/configuration/configuration.h"
 #include "starboard/blitter.h"
 
 namespace cobalt {
@@ -198,43 +199,52 @@
 
 AutoMemSettings GetDefaultBuildSettings() {
   AutoMemSettings settings(AutoMemSettings::kTypeBuild);
+  configuration::Configuration* config =
+      configuration::Configuration::GetInstance();
   settings.has_blitter = HasBlitter();
 
   settings.cobalt_encoded_image_cache_size_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(
-          COBALT_ENCODED_IMAGE_CACHE_SIZE_IN_BYTES);
+          config->CobaltEncodedImageCacheSizeInBytes());
   settings.cobalt_image_cache_size_in_bytes =
-      MakeValidIfGreaterThanOrEqualToZero(COBALT_IMAGE_CACHE_SIZE_IN_BYTES);
+      MakeValidIfGreaterThanOrEqualToZero(
+          config->CobaltImageCacheSizeInBytes());
   settings.javascript_garbage_collection_threshold_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(
-          COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES);
+          config->CobaltJsGarbageCollectionThresholdInBytes());
   settings.remote_typeface_cache_capacity_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(
-          COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES);
+          config->CobaltRemoteTypefaceCacheSizeInBytes());
   settings.skia_cache_size_in_bytes =
-      MakeValidIfGreaterThanOrEqualToZero(COBALT_SKIA_CACHE_SIZE_IN_BYTES);
-  settings.skia_texture_atlas_dimensions =
-      MakeDimensionsIfValid(TextureDimensions(
-          COBALT_SKIA_GLYPH_ATLAS_WIDTH, COBALT_SKIA_GLYPH_ATLAS_HEIGHT,
-          kSkiaGlyphAtlasTextureBytesPerPixel));
+      MakeValidIfGreaterThanOrEqualToZero(config->CobaltSkiaCacheSizeInBytes());
+  settings.skia_texture_atlas_dimensions = MakeDimensionsIfValid(
+      TextureDimensions(config->CobaltSkiaGlyphAtlasWidth(),
+                        config->CobaltSkiaGlyphAtlasHeight(),
+                        kSkiaGlyphAtlasTextureBytesPerPixel));
 
   // Render tree node cache settings for various rasterizers.
   settings.software_surface_cache_size_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(
-          COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES);
+          config->CobaltSoftwareSurfaceCacheSizeInBytes());
   settings.offscreen_target_cache_size_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(
-          COBALT_OFFSCREEN_TARGET_CACHE_SIZE_IN_BYTES);
+          config->CobaltOffscreenTargetCacheSizeInBytes());
 
+#if SB_API_VERSION < SB_FEATURE_GYP_CONFIGURATION_VERSION
+#if defined(COBALT_MAX_CPU_USAGE_IN_BYTES)
   settings.max_cpu_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(COBALT_MAX_CPU_USAGE_IN_BYTES);
+#endif  // defined(COBALT_MAX_CPU_USAGE_IN_BYTES)
+#if defined(COBALT_MAX_GPU_USAGE_IN_BYTES)
   settings.max_gpu_in_bytes =
       MakeValidIfGreaterThanOrEqualToZero(COBALT_MAX_GPU_USAGE_IN_BYTES);
+#endif  // defined(COBALT_MAX_GPU_USAGE_IN_BYTES)
+#endif  // SB_API_VERSION < SB_FEATURE_GYP_CONFIGURATION_VERSION
 
   settings.reduce_cpu_memory_by =
-      MakeValidIfGreaterThanOrEqualToZero(COBALT_REDUCE_CPU_MEMORY_BY);
+      MakeValidIfGreaterThanOrEqualToZero(config->CobaltReduceCpuMemoryBy());
   settings.reduce_gpu_memory_by =
-      MakeValidIfGreaterThanOrEqualToZero(COBALT_REDUCE_GPU_MEMORY_BY);
+      MakeValidIfGreaterThanOrEqualToZero(config->CobaltReduceGpuMemoryBy());
 
   return settings;
 }
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 8eff058..6586507 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -38,6 +38,7 @@
 #include "cobalt/browser/stack_size_constants.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/browser/web_module_stat_tracker.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/css_parser/parser.h"
 #include "cobalt/dom/blob.h"
 #include "cobalt/dom/csp_delegate_factory.h"
@@ -1304,7 +1305,8 @@
 WebModule::Options::Options()
     : name("WebModule"),
       layout_trigger(layout::LayoutManager::kOnDocumentMutation),
-      mesh_cache_capacity(COBALT_MESH_CACHE_SIZE_IN_BYTES) {}
+      mesh_cache_capacity(configuration::Configuration::GetInstance()
+                              ->CobaltMeshCacheSizeInBytes()) {}
 
 WebModule::WebModule(
     const GURL& initial_url, base::ApplicationState initial_application_state,
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 25018e7..850d937 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -88,6 +88,8 @@
         '<(DEPTH)/third_party/boringssl/boringssl_tool.gyp:*',
         '<(DEPTH)/net/net.gyp:net_unittests_deploy',
         '<(DEPTH)/sql/sql.gyp:sql_unittests_deploy',
+        '<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader_test_deploy',
+        '<(DEPTH)/starboard/loader_app/loader_app.gyp:loader_app'
       ],
       'conditions': [
         ['sb_evergreen != 1', {
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 1443ae3..2af62a2 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-253153
\ No newline at end of file
+260628
\ No newline at end of file
diff --git a/src/cobalt/build/build_config.h b/src/cobalt/build/build_config.h
deleted file mode 100644
index 92c69cc..0000000
--- a/src/cobalt/build/build_config.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef COBALT_BUILD_BUILD_CONFIG_H_
-#define COBALT_BUILD_BUILD_CONFIG_H_
-
-#if defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY) && \
-    COBALT_MEDIA_BUFFER_INITIAL_CAPACITY < 0
-#error cobalt_media_buffer_initial_capacity has to be greater than or equal to 0
-#endif  // defined(COBALT_MEDIA_BUFFER_INITIAL_CAPACITY) &&
-        // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY < 0
-
-#if defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT) && \
-    COBALT_MEDIA_BUFFER_ALLOCATION_UNIT < 0
-#error cobalt_media_buffer_allocation_unit has to be greater than or equal to 0
-#endif  // defined(COBALT_MEDIA_BUFFER_ALLOCATION_UNIT) &&
-        // COBALT_MEDIA_BUFFER_ALLOCATION_UNIT < 0
-
-#if defined(COBALT_MEDIA_BUFFER_ALIGNMENT) && COBALT_MEDIA_BUFFER_ALIGNMENT < 0
-#error "cobalt_media_buffer_alignment has to be greater than or equal to 0."
-#endif  // defined(COBALT_MEDIA_BUFFER_ALIGNMENT) &&
-        // COBALT_MEDIA_BUFFER_ALIGNMENT < 0
-
-#if defined(COBALT_MEDIA_BUFFER_PADDING) && COBALT_MEDIA_BUFFER_PADDING < 0
-#error "cobalt_media_buffer_padding has to be greater than or equal to 0."
-#endif  // defined(COBALT_MEDIA_BUFFER_PADDING) && COBALT_MEDIA_BUFFER_PADDING <
-        // 0
-
-#if defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET) && \
-    COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET < 8 * 1024 * 1024
-#error cobalt_media_buffer_progressive_budget has to be greater than or equal \
-           to 8 MB.
-#endif  // defined(COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET) &&
-        // COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET < 0
-
-#if defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) && \
-    COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET <= 0
-#error "cobalt_media_buffer_non_video_budget has to be greater than 0."
-#endif  // defined(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) &&
-        // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET < 0
-
-#if defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) && \
-    COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P <= 0
-#error "cobalt_media_buffer_video_budget_1080p has to be greater than 0."
-#endif  // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) &&
-        // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P < 0
-
-#if defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K) &&    \
-    defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) && \
-    COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K <              \
-        COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
-#error cobalt_media_buffer_video_budget_4k has to be greater than or equal to \
-           cobalt_media_buffer_video_budget_1080p.
-#endif  // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K) &&
-        // defined(COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P) &&
-        // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K <
-        // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
-
-#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P != 0
-#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P <  \
-    (COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P + \
-     COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET)
-#error cobalt_media_buffer_max_capacity_1080p has to be greater than the sum \
-           of cobalt_media_buffer_video_budget_1080p + \
-           cobalt_media_buffer_non_video_budget
-#endif  // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P <
-        // (COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P +
-        // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET)
-#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P < \
-    COBALT_MEDIA_BUFFER_INITIAL_CAPACITY
-#error cobalt_media_buffer_max_capacity_1080p has to be greater than \
-           cobalt_media_buffer_initial_capacity
-#endif  // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P <
-        // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY
-#endif  // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P != 0
-
-#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != 0
-#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K <  \
-    (COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K + \
-     COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET)
-#error cobalt_media_buffer_max_capacity_4k has to be greater than the sum of \
-           cobalt_media_buffer_video_budget_4k + \
-           cobalt_media_buffer_non_video_budget
-#endif  // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K <
-        // (COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K +
-        // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET)
-#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K < COBALT_MEDIA_BUFFER_INITIAL_CAPACITY
-#error cobalt_media_buffer_max_capacity_4k has to be greater than \
-           cobalt_media_buffer_initial_capacity
-#endif  // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K <
-        // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY
-#endif  // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != 0
-
-#endif  // COBALT_BUILD_BUILD_CONFIG_H_
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 773fdb5..b289868 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -229,9 +229,6 @@
     # "file:///cobalt/browser/splash_screen/". If '', no file is copied.
     'cobalt_splash_screen_file%': '',
 
-    # Some platforms have difficulty linking snapshot_app_stats
-    'build_snapshot_app_stats%': 1,
-
     # Set to "true" to enable v8 snapshot generation at Cobalt build time.
     'cobalt_v8_buildtime_snapshot%': '<(cobalt_v8_buildtime_snapshot)',
 
@@ -353,15 +350,15 @@
     'mozjs_garbage_collection_threshold_in_bytes%': 8 * 1024 * 1024,
 
     # Max Cobalt CPU usage specifies that the cobalt program should
-    # keep it's size below the specified size. A value of -1 causes this
-    # value to be assumed from the starboard API function:
-    # SbSystemGetTotalCPUMemory().
+    # keep it's size below the specified size.
+    # This setting is deprecated. Implement starboard API function
+    # SbSystemGetTotalCPUMemory() instead.
     'max_cobalt_cpu_usage%': -1,
 
     # Max Cobalt GPU usage specifies that the cobalt program should
-    # keep it's size below the specified size. A value of -1 causes this
-    # value to be assumed from the starboard API function:
-    # SbSystemGetTotalGPUMemory().
+    # keep it's size below the specified size.
+    # This setting is deprecated. Implement starboard API function
+    # SbSystemGetTotalGPUMemory() instead.
     'max_cobalt_gpu_usage%': -1,
 
     # When specified this value will reduce the cpu memory consumption by
@@ -390,6 +387,10 @@
     # reproducing bugs.
     'cobalt_gc_zeal%': 0,
 
+    # The cobalt_media_* variables defined below are deprecated. Their
+    # corresponding Starboard functions should be defined instead of setting
+    # their values through GYP.
+
     # This can be set to "memory" or "file".  When it is set to "memory", the
     # media buffers will be stored in main memory allocated by SbMemory
     # functions.  When it is set to "file", the media buffers will be stored in
@@ -397,7 +398,7 @@
     # SbSystemGetPath() with "kSbSystemPathCacheDirectory".  Note that when its
     # value is "file" the media stack will still allocate memory to cache the
     # the buffers in use.
-    'cobalt_media_buffer_storage_type%': 'memory',
+    'cobalt_media_buffer_storage_type%': '',
     # When either |cobalt_media_buffer_initial_capacity| or
     # |cobalt_media_buffer_allocation_unit| isn't zero, media buffers will be
     # allocated using a memory pool.  Set the following variable to 1 to
@@ -407,20 +408,20 @@
     # |cobalt_media_buffer_initial_capacity| bytes for media buffer on startup
     # and will not release any media buffer memory back to the system even if
     # there is no media buffers allocated.
-    'cobalt_media_buffer_pool_allocate_on_demand%': 1,
+    'cobalt_media_buffer_pool_allocate_on_demand%': -1,
     # The amount of memory that will be used to store media buffers allocated
     # during system startup.  To allocate a large chunk at startup helps with
     # reducing fragmentation and can avoid failures to allocate incrementally.
     # This can be set to 0.
-    'cobalt_media_buffer_initial_capacity%': 21 * 1024 * 1024,
+    'cobalt_media_buffer_initial_capacity%': -1,
     # The maximum amount of memory that will be used to store media buffers when
     # video resolution is no larger than 1080p. This must be larger than sum of
     # 1080p video budget and non-video budget.
-    'cobalt_media_buffer_max_capacity_1080p%': 50 * 1024 * 1024,
+    'cobalt_media_buffer_max_capacity_1080p%': -1,
     # The maximum amount of memory that will be used to store media buffers when
     # video resolution is 4k. If 0, then memory can grow without bound. This
     # must be larger than sum of 4k video budget and non-video budget.
-    'cobalt_media_buffer_max_capacity_4k%': 140 * 1024 * 1024,
+    'cobalt_media_buffer_max_capacity_4k%': -1,
 
     # When the media stack needs more memory to store media buffers, it will
     # allocate extra memory in units of |cobalt_media_buffer_allocation_unit|.
@@ -428,22 +429,22 @@
     # memory on demand.  When |cobalt_media_buffer_initial_capacity| and this
     # value are both set to 0, the media stack will allocate individual buffers
     # directly using SbMemory functions.
-    'cobalt_media_buffer_allocation_unit%': 1 * 1024 * 1024,
+    'cobalt_media_buffer_allocation_unit%': -1,
 
     # The media buffer will be allocated using the following alignment.  Set
     # this to a larger value may increase the memory consumption of media
     # buffers.
-    'cobalt_media_buffer_alignment%': 1,
+    'cobalt_media_buffer_alignment%': -1,
     # Extra bytes allocated at the end of a media buffer to ensure that the
     # buffer can be use optimally by specific instructions like SIMD.  Set to 0
     # to remove any padding.
-    'cobalt_media_buffer_padding%': 0,
+    'cobalt_media_buffer_padding%': -1,
 
     # The memory used when playing mp4 videos that is not in DASH format.  The
     # resolution of such videos shouldn't go beyond 1080p.  Its value should be
     # less than the sum of 'cobalt_media_buffer_non_video_budget' and
     # 'cobalt_media_buffer_video_budget_1080p' but not less than 8 MB.
-    'cobalt_media_buffer_progressive_budget%': 12 * 1024 * 1024,
+    'cobalt_media_buffer_progressive_budget%': -1,
 
     # Specifies the maximum amount of memory used by audio or text buffers of
     # media source before triggering a garbage collection.  A large value will
@@ -451,7 +452,7 @@
     # JavaScript app less likely to re-download audio data.  Note that the
     # JavaScript app may experience significant difficulty if this value is too
     # low.
-    'cobalt_media_buffer_non_video_budget%': 5 * 1024 * 1024,
+    'cobalt_media_buffer_non_video_budget%': -1,
 
     # Specifies the maximum amount of memory used by video buffers of media
     # source before triggering a garbage collection when the video resolution is
@@ -459,14 +460,14 @@
     # used by video buffers but will also make JavaScript app less likely to
     # re-download video data.  Note that the JavaScript app may experience
     # significant difficulty if this value is too low.
-    'cobalt_media_buffer_video_budget_1080p%': 30 * 1024 * 1024,
+    'cobalt_media_buffer_video_budget_1080p%': -1,
     # Specifies the maximum amount of memory used by video buffers of media
     # source before triggering a garbage collection when the video resolution is
     # lower than 4k (3840x2160).  A large value will cause more memory being
     # used by video buffers but will also make JavaScript app less likely to
     # re-download video data.  Note that the JavaScript app may experience
     # significant difficulty if this value is too low.
-    'cobalt_media_buffer_video_budget_4k%': 100 * 1024 * 1024,
+    'cobalt_media_buffer_video_budget_4k%': -1,
 
     # Specifies the duration threshold of media source garbage collection.  When
     # the accumulated duration in a source buffer exceeds this value, the media
@@ -477,7 +478,7 @@
     # system heap.
     # This should be set to 170 for most of the platforms.  But it can be
     # further reduced on systems with extremely low memory.
-    'cobalt_media_source_garbage_collection_duration_threshold_in_seconds%': 170,
+    'cobalt_media_source_garbage_collection_duration_threshold_in_seconds%': -1,
 
     'compiler_flags_host': [
       '-D__LB_HOST__',  # TODO: Is this still needed?
@@ -544,27 +545,89 @@
   'target_defaults': {
     'defines': [
       'COBALT',
-      'COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND=<(cobalt_media_buffer_pool_allocate_on_demand)',
-      'COBALT_MEDIA_BUFFER_INITIAL_CAPACITY=<(cobalt_media_buffer_initial_capacity)',
-      'COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P=<(cobalt_media_buffer_max_capacity_1080p)',
-      'COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K=<(cobalt_media_buffer_max_capacity_4k)',
-      'COBALT_MEDIA_BUFFER_ALLOCATION_UNIT=<(cobalt_media_buffer_allocation_unit)',
-      'COBALT_MEDIA_BUFFER_ALIGNMENT=<(cobalt_media_buffer_alignment)',
-      'COBALT_MEDIA_BUFFER_PADDING=<(cobalt_media_buffer_padding)',
-      'COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET=<(cobalt_media_buffer_progressive_budget)',
-      'COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=<(cobalt_media_buffer_non_video_budget)',
-      'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=<(cobalt_media_buffer_video_budget_1080p)',
-      'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=<(cobalt_media_buffer_video_budget_4k)',
-      'COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS=<(cobalt_media_source_garbage_collection_duration_threshold_in_seconds)',
     ],
     'conditions': [
+      ['cobalt_media_buffer_pool_allocate_on_demand != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND=<(cobalt_media_buffer_pool_allocate_on_demand)',
+        ],
+      }],
+      ['cobalt_media_buffer_initial_capacity != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_INITIAL_CAPACITY=<(cobalt_media_buffer_initial_capacity)',
+        ],
+      }],
+      ['cobalt_media_buffer_max_capacity_1080p != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P=<(cobalt_media_buffer_max_capacity_1080p)',
+        ],
+      }],
+      ['cobalt_media_buffer_max_capacity_4k != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K=<(cobalt_media_buffer_max_capacity_4k)',
+        ],
+      }],
+      ['cobalt_media_buffer_max_capacity_4k != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K=<(cobalt_media_buffer_max_capacity_4k)',
+        ],
+      }],
+      ['cobalt_media_buffer_max_capacity_4k != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K=<(cobalt_media_buffer_max_capacity_4k)',
+        ],
+      }],
+      ['cobalt_media_buffer_allocation_unit != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_ALLOCATION_UNIT=<(cobalt_media_buffer_allocation_unit)',
+        ],
+      }],
+      ['cobalt_media_buffer_alignment != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_ALIGNMENT=<(cobalt_media_buffer_alignment)',
+        ],
+      }],
+      ['cobalt_media_buffer_padding != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_PADDING=<(cobalt_media_buffer_padding)',
+        ],
+      }],
+      ['cobalt_media_buffer_progressive_budget != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET=<(cobalt_media_buffer_progressive_budget)',
+        ],
+      }],
+      ['cobalt_media_buffer_non_video_budget != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=<(cobalt_media_buffer_non_video_budget)',
+        ],
+      }],
+      ['cobalt_media_buffer_video_budget_1080p != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=<(cobalt_media_buffer_video_budget_1080p)',
+        ],
+      }],
+      ['cobalt_media_buffer_video_budget_4k != -1', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=<(cobalt_media_buffer_video_budget_4k)',
+        ],
+      }],
+      ['cobalt_media_source_garbage_collection_duration_threshold_in_seconds != -1', {
+        'defines': [
+          'COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS=<(cobalt_media_source_garbage_collection_duration_threshold_in_seconds)',
+        ],
+      }],
       ['cobalt_media_buffer_storage_type == "memory"', {
         'defines': [
           'COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY=1',
         ],
       }, {
-        'defines': [
-          'COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE=1',
+        'conditions': [
+          ['cobalt_media_buffer_storage_type != ""', {
+            'defines': [
+              'COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE=1',
+            ],
+          }],
         ],
       }],
       ['cobalt_gc_zeal == 1', {
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
index face159..415251b 100644
--- a/src/cobalt/build/config/base.gni
+++ b/src/cobalt/build/config/base.gni
@@ -413,125 +413,6 @@
   cobalt_use_media_source_2016 = true
 }
 
-# Note that the following media buffer related variables are only used when
-# |cobalt_use_media_source_2016| is set to true.
-
-# This can be set to "memory" or "file".  When it is set to "memory", the
-# media buffers will be stored in main memory allocated by SbMemory
-# functions.  When it is set to "file", the media buffers will be stored in
-# a temporary file in the system cache folder acquired by calling
-# SbSystemGetPath() with "kSbSystemPathCacheDirectory".  Note that when its
-# value is "file" the media stack will still allocate memory to cache the
-# the buffers in use.
-if (!defined(cobalt_media_buffer_storage_type)) {
-  cobalt_media_buffer_storage_type = "memory"
-}
-# When either |cobalt_media_buffer_initial_capacity| or
-# |cobalt_media_buffer_allocation_unit| isn't zero, media buffers will be
-# allocated using a memory pool.  Set the following variable to true to
-# allocate the media buffer pool memory on demand and return all memory to
-# the system when there is no media buffer allocated.  Setting the following
-# value to false results in that Cobalt will allocate
-# |cobalt_media_buffer_initial_capacity| bytes for media buffer on startup
-# and will not release any media buffer memory back to the system even if
-# there is no media buffers allocated.
-if (!defined(cobalt_media_buffer_pool_allocate_on_demand)) {
-  cobalt_media_buffer_pool_allocate_on_demand = true
-}
-# The amount of memory that will be used to store media buffers allocated
-# during system startup.  To allocate a large chunk at startup helps with
-# reducing fragmentation and can avoid failures to allocate incrementally.
-# This can be set to 0.
-if (!defined(cobalt_media_buffer_initial_capacity)) {
-  cobalt_media_buffer_initial_capacity = "(21 * 1024 * 1024)"
-}
-
-# The maximum amount of memory that will be used to store media buffers when
-# video resolution is no larger than 1080p. This must be larger than sum of
-# 1080p video budget and non-video budget.
-if (!defined(cobalt_media_buffer_max_capacity_1080p)) {
-  cobalt_media_buffer_max_capacity_1080p = "(50 * 1024 * 1024)",
-}
-
-# The maximum amount of memory that will be used to store media buffers when
-# video resolution is no larger than 4k. This must be larger than sum of 4k
-# video budget and non-video budget.
-if (!defined(cobalt_buffer_max_capacity_4k)) {
-  cobalt_media_buffer_max_capacity_4k = "(140 * 1024 * 1024)",
-}
-
-# When the media stack needs more memory to store media buffers, it will
-# allocate extra memory in units of |cobalt_media_buffer_allocation_unit|.
-# This can be set to 0, in which case the media stack will allocate extra
-# memory on demand.  When |cobalt_media_buffer_initial_capacity| and this
-# value are both set to 0, the media stack will allocate individual buffers
-# directly using SbMemory functions.
-if (!defined(cobalt_media_buffer_allocation_unit)) {
-  cobalt_media_buffer_allocation_unit = "(1 * 1024 * 1024)"
-}
-
-# The media buffer will be allocated using the following alignment.  Set
-# this to a larger value may increase the memory consumption of media
-# buffers.
-if (!defined(cobalt_media_buffer_alignment)) {
-  cobalt_media_buffer_alignment = 1
-}
-# Extra bytes allocated at the end of a media buffer to ensure that the
-# buffer can be use optimally by specific instructions like SIMD.  Set to 0
-# to remove any padding.
-if (!defined(cobalt_media_buffer_padding)) {
-  cobalt_media_buffer_padding = 0
-}
-
-# The memory used when playing mp4 videos that is not in DASH format.  The
-# resolution of such videos shouldn't go beyond 1080p.  Its value should be
-# less than the sum of 'cobalt_media_buffer_non_video_budget' and
-# 'cobalt_media_buffer_video_budget_1080p' but not less than 8 MB.
-if (!defined(cobalt_media_buffer_progressive_budget)) {
-  cobalt_media_buffer_progressive_budget = "(12 * 1024 * 1024)"
-}
-
-# Specifies the maximum amount of memory used by audio or text buffers of
-# media source before triggering a garbage collection.  A large value will
-# cause more memory being used by audio buffers but will also make
-# JavaScript app less likely to re-download audio data.  Note that the
-# JavaScript app may experience significant difficulty if this value is too
-# low.
-if (!defined(cobalt_media_buffer_non_video_budget)) {
-  cobalt_media_buffer_non_video_budget = "(5 * 1024 * 1024)"
-}
-
-# Specifies the maximum amount of memory used by video buffers of media
-# source before triggering a garbage collection when the video resolution is
-# lower than 1080p (1920x1080).  A large value will cause more memory being
-# used by video buffers but will also make JavaScript app less likely to
-# re-download video data.  Note that the JavaScript app may experience
-# significant difficulty if this value is too low.
-if (!defined(cobalt_media_buffer_video_budget_1080p)) {
-  cobalt_media_buffer_video_budget_1080p = "(30 * 1024 * 1024)"
-}
-# Specifies the maximum amount of memory used by video buffers of media
-# source before triggering a garbage collection when the video resolution is
-# lower than 4k (3840x2160).  A large value will cause more memory being
-# used by video buffers but will also make JavaScript app less likely to
-# re-download video data.  Note that the JavaScript app may experience
-# significant difficulty if this value is too low.
-if (!defined(cobalt_media_buffer_video_budget_4k)) {
-  cobalt_media_buffer_video_budget_4k = "(100 * 1024 * 1024)"
-}
-
-# Specifies the duration threshold of media source garbage collection.  When the
-# accumulated duration in a source buffer exceeds this value, the media source
-# implementation will try to eject existing buffers from the cache.
-# This is usually triggered when the video being played has a simple content and
-# the encoded data is small.  In such case this can limit how much is allocated
-# for the book keeping data of the media buffers and avoid OOM of system heap.
-# This should be set to 170 for most of the platforms.  But it can be further
-# reduced on systems with extremely low memory.
-if (!defined(cobalt_media_source_garbage_collection_duration_threshold_in_seconds)) {
-  cobalt_media_source_garbage_collection_duration_threshold_in_seconds = "(170)"
-}
-
 # For configurations other than Gold, set the flag that lets test data files be
 # copied and carried along with the build.
 # Clients must copy over all content; to avoid having to copy over extra data,
diff --git a/src/cobalt/configuration/configuration.cc b/src/cobalt/configuration/configuration.cc
new file mode 100644
index 0000000..ce19c77
--- /dev/null
+++ b/src/cobalt/configuration/configuration.cc
@@ -0,0 +1,343 @@
+// 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/configuration/configuration.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace cobalt {
+namespace configuration {
+
+Configuration* Configuration::configuration_ = nullptr;
+
+Configuration* Configuration::GetInstance() {
+  return base::Singleton<Configuration>::get();
+}
+
+Configuration::Configuration() {
+#if SB_API_VERSION < 11
+  configuration_api_ = nullptr;
+#else
+  configuration_api_ = static_cast<const CobaltExtensionConfigurationApi*>(
+      SbSystemGetExtension(kCobaltExtensionConfigurationName));
+  if (configuration_api_) {
+    // Verify it's the extension needed.
+    if (SbStringCompareAll(configuration_api_->name,
+                           kCobaltExtensionConfigurationName) != 0 ||
+        configuration_api_->version < 1) {
+      LOG(WARNING) << "Not using supplied cobalt configuration extension: "
+                   << "'" << configuration_api_->name << "' ("
+                   << configuration_api_->version << ")";
+      configuration_api_ = nullptr;
+    }
+  }
+#endif
+}
+
+const char* Configuration::CobaltUserOnExitStrategy() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltUserOnExitStrategy();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return "stop";
+#elif defined(COBALT_USER_ON_EXIT_STRATEGY)
+  return COBALT_USER_ON_EXIT_STRATEGY;
+#else
+  return "stop";
+#endif
+}
+
+bool Configuration::CobaltRenderDirtyRegionOnly() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltRenderDirtyRegionOnly();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return false;
+#elif defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+  return true;
+#else
+  return false;
+#endif
+}
+
+int Configuration::CobaltEglSwapInterval() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltEglSwapInterval();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 1;
+#elif defined(COBALT_EGL_SWAP_INTERVAL)
+  return COBALT_EGL_SWAP_INTERVAL;
+#else
+  return 1;
+#endif
+}
+
+const char* Configuration::CobaltFallbackSplashScreenUrl() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltFallbackSplashScreenUrl();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return "none";
+#elif defined(COBALT_FALLBACK_SPLASH_SCREEN_URL)
+  return COBALT_FALLBACK_SPLASH_SCREEN_URL;
+#else
+  return "none";
+#endif
+}
+
+bool Configuration::CobaltEnableQuic() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltEnableQuic();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return true;
+#elif defined(COBALT_ENABLE_QUIC)
+  return true;
+#else
+  return false;
+#endif
+}
+
+int Configuration::CobaltSkiaCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltSkiaCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 4 * 1024 * 1024;
+#elif defined(COBALT_SKIA_CACHE_SIZE_IN_BYTES)
+  return COBALT_SKIA_CACHE_SIZE_IN_BYTES;
+#else
+  return 4 * 1024 * 1024;
+#endif
+}
+
+int Configuration::CobaltOffscreenTargetCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltOffscreenTargetCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return -1;
+#elif defined(COBALT_OFFSCREEN_TARGET_CACHE_SIZE_IN_BYTES)
+  return COBALT_OFFSCREEN_TARGET_CACHE_SIZE_IN_BYTES;
+#else
+  return -1;
+#endif
+}
+
+int Configuration::CobaltEncodedImageCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltEncodedImageCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 1024 * 1024;
+#elif defined(COBALT_ENCODED_IMAGE_CACHE_SIZE_IN_BYTES)
+  return COBALT_ENCODED_IMAGE_CACHE_SIZE_IN_BYTES;
+#else
+  return 1024 * 1024;
+#endif
+}
+
+int Configuration::CobaltImageCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltImageCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return -1;
+#elif defined(COBALT_IMAGE_CACHE_SIZE_IN_BYTES)
+  return COBALT_IMAGE_CACHE_SIZE_IN_BYTES;
+#else
+  return -1;
+#endif
+}
+
+int Configuration::CobaltLocalTypefaceCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltLocalTypefaceCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 16 * 1024 * 1024;
+#elif defined(COBALT_LOCAL_TYPEFACE_CACHE_SIZE_IN_BYTES)
+  return COBALT_LOCAL_TYPEFACE_CACHE_SIZE_IN_BYTES;
+#else
+  return 16 * 1024 * 1024;
+#endif
+}
+
+int Configuration::CobaltRemoteTypefaceCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltRemoteTypefaceCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 4 * 1024 * 1024;
+#elif defined(COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES)
+  return COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES;
+#else
+  return 4 * 1024 * 1024;
+#endif
+}
+
+int Configuration::CobaltMeshCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltMeshCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 1024 * 1024;
+#elif defined(COBALT_MESH_CACHE_SIZE_IN_BYTES)
+  return COBALT_MESH_CACHE_SIZE_IN_BYTES;
+#else
+  return 1024 * 1024;
+#endif
+}
+
+int Configuration::CobaltSoftwareSurfaceCacheSizeInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltSoftwareSurfaceCacheSizeInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 8 * 1024 * 1024;
+#elif defined(COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES)
+  return COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES;
+#else
+  return 8 * 1024 * 1024;
+#endif
+}
+
+float Configuration::CobaltImageCacheCapacityMultiplierWhenPlayingVideo() {
+  if (configuration_api_) {
+    return configuration_api_
+        ->CobaltImageCacheCapacityMultiplierWhenPlayingVideo();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 1.0f;
+#elif defined(COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO)
+  return COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO;
+#else
+  return 1.0f;
+#endif
+}
+
+int Configuration::CobaltJsGarbageCollectionThresholdInBytes() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltJsGarbageCollectionThresholdInBytes();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 8 * 1024 * 1024;
+#elif defined(COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES)
+  return COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES;
+#else
+  return 8 * 1024 * 1024;
+#endif
+}
+
+int Configuration::CobaltSkiaGlyphAtlasWidth() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltSkiaGlyphAtlasWidth();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 2048;
+#elif defined(COBALT_SKIA_GLYPH_ATLAS_WIDTH)
+  return COBALT_SKIA_GLYPH_ATLAS_WIDTH;
+#else
+  return 2048;
+#endif
+}
+
+int Configuration::CobaltSkiaGlyphAtlasHeight() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltSkiaGlyphAtlasHeight();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return 2048;
+#elif defined(COBALT_SKIA_GLYPH_ATLAS_HEIGHT)
+  return COBALT_SKIA_GLYPH_ATLAS_HEIGHT;
+#else
+  return 2048;
+#endif
+}
+
+int Configuration::CobaltReduceCpuMemoryBy() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltReduceCpuMemoryBy();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return -1;
+#elif defined(COBALT_REDUCE_CPU_MEMORY_BY)
+  return COBALT_REDUCE_CPU_MEMORY_BY;
+#else
+  return -1;
+#endif
+}
+
+int Configuration::CobaltReduceGpuMemoryBy() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltReduceGpuMemoryBy();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return -1;
+#elif defined(COBALT_REDUCE_GPU_MEMORY_BY)
+  return COBALT_REDUCE_GPU_MEMORY_BY;
+#else
+  return -1;
+#endif
+}
+
+bool Configuration::CobaltGcZeal() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltGcZeal();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return false;
+#elif defined(COBALT_GC_ZEAL)
+  return true;
+#else
+  return false;
+#endif
+}
+
+const char* Configuration::CobaltRasterizerType() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltRasterizerType();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return "direct-gles";
+#elif defined(COBALT_FORCE_STUB_RASTERIZER)
+  return "stub";
+#elif defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
+  return "direct-gles";
+#else
+  return "hardware";
+#endif
+}
+
+bool Configuration::CobaltEnableJit() {
+  if (configuration_api_) {
+    return configuration_api_->CobaltEnableJit();
+  }
+#if SB_API_VERSION >= SB_FEATURE_GYP_CONFIGURATION_VERSION
+  return false;
+#elif defined(ENGINE_SUPPORTS_JIT)
+  return true;
+#elif defined(COBALT_DISABLE_JIT)
+  return false;
+#else
+  return false;
+#endif
+}
+
+}  // namespace configuration
+}  // namespace cobalt
diff --git a/src/cobalt/configuration/configuration.gyp b/src/cobalt/configuration/configuration.gyp
new file mode 100644
index 0000000..c3d4404
--- /dev/null
+++ b/src/cobalt/configuration/configuration.gyp
@@ -0,0 +1,38 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'configuration',
+      'type': 'static_library',
+      'sources': [
+        'configuration.cc',
+        'configuration.h',
+      ],
+      'conditions': [
+        ['rasterizer_type == "stub"', {
+          'defines': [
+            'COBALT_FORCE_STUB_RASTERIZER',
+          ],
+        }],
+        ['rasterizer_type == "direct-gles"', {
+          'defines': [
+            'COBALT_FORCE_DIRECT_GLES_RASTERIZER',
+          ],
+        }],
+      ],
+    },
+  ],
+}
\ No newline at end of file
diff --git a/src/cobalt/configuration/configuration.h b/src/cobalt/configuration/configuration.h
new file mode 100644
index 0000000..6ea8ba5
--- /dev/null
+++ b/src/cobalt/configuration/configuration.h
@@ -0,0 +1,76 @@
+// 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_CONFIGURATION_CONFIGURATION_H_
+#define COBALT_CONFIGURATION_CONFIGURATION_H_
+
+#include "base/macros.h"
+#include "cobalt/extension/configuration.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace cobalt {
+namespace configuration {
+
+// The Configuration changes certain Cobalt features as specified by the
+// platform. This class picks up values set in the
+// CobaltConfigurationExtensionApi if it is implemented by the platform and
+// will otherwise use default configurations.
+class Configuration {
+ public:
+  // The Configuration is a singleton initialized on the first call of
+  // GetInstance(). Calls to this function will return a pointer to the same
+  // instance as was previously initialized.
+  static Configuration* GetInstance();
+
+  const char* CobaltUserOnExitStrategy();
+  bool CobaltRenderDirtyRegionOnly();
+  int CobaltEglSwapInterval();
+  const char* CobaltFallbackSplashScreenUrl();
+  int CobaltSkiaCacheSizeInBytes();
+  bool CobaltEnableQuic();
+  int CobaltOffscreenTargetCacheSizeInBytes();
+  int CobaltEncodedImageCacheSizeInBytes();
+  int CobaltImageCacheSizeInBytes();
+  int CobaltLocalTypefaceCacheSizeInBytes();
+  int CobaltRemoteTypefaceCacheSizeInBytes();
+  int CobaltMeshCacheSizeInBytes();
+  int CobaltSoftwareSurfaceCacheSizeInBytes();
+  float CobaltImageCacheCapacityMultiplierWhenPlayingVideo();
+  int CobaltSkiaGlyphAtlasWidth();
+  int CobaltSkiaGlyphAtlasHeight();
+  int CobaltJsGarbageCollectionThresholdInBytes();
+  int CobaltReduceCpuMemoryBy();
+  int CobaltReduceGpuMemoryBy();
+  bool CobaltGcZeal();
+  const char* CobaltRasterizerType();
+  bool CobaltEnableJit();
+
+ private:
+  Configuration();
+
+  friend struct base::DefaultSingletonTraits<Configuration>;
+  const CobaltExtensionConfigurationApi* configuration_api_;
+  static Configuration* configuration_;
+
+  DISALLOW_COPY_AND_ASSIGN(Configuration);
+};
+
+}  // namespace configuration
+}  // namespace cobalt
+
+#endif  // COBALT_CONFIGURATION_CONFIGURATION_H_
diff --git a/src/cobalt/content/ssl/certs/5c44d531.0 b/src/cobalt/content/ssl/certs/5c44d531.0
deleted file mode 100644
index cedf596..0000000
--- a/src/cobalt/content/ssl/certs/5c44d531.0
+++ /dev/null
@@ -1,33 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
-TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
-dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX
-DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
-ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
-b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291
-qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp
-uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU
-Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE
-pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp
-5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M
-UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN
-GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy
-5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv
-6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK
-eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6
-B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/
-BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov
-L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG
-SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS
-CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen
-5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897
-IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK
-gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL
-+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL
-vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm
-bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk
-N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC
-Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z
-ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/5e98733a.0 b/src/cobalt/content/ssl/certs/5e98733a.0
new file mode 100644
index 0000000..7ac10cc
--- /dev/null
+++ b/src/cobalt/content/ssl/certs/5e98733a.0
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw
+gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL
+Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg
+MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw
+BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0
+MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1
+c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ
+bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ
+2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E
+T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j
+5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM
+C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T
+DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX
+wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A
+2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm
+nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8
+dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl
+N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj
+c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS
+5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS
+Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr
+hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/
+B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI
+AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw
+H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+
+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk
+2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol
+IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk
+5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY
+n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==
+-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/9f0f5fd6.0 b/src/cobalt/content/ssl/certs/9f0f5fd6.0
deleted file mode 100644
index 7f60447..0000000
--- a/src/cobalt/content/ssl/certs/9f0f5fd6.0
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET
-MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb
-BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz
-MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx
-FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g
-Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2
-fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl
-LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV
-WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF
-TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb
-5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc
-CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri
-wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ
-wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG
-m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4
-F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng
-WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0
-2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
-AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/
-0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw
-F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS
-g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj
-qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN
-h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/
-ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V
-btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj
-Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ
-8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW
-gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE=
------END CERTIFICATE-----
diff --git a/src/cobalt/cssom/embedded_resources/user_agent_style_sheet.css b/src/cobalt/cssom/embedded_resources/user_agent_style_sheet.css
index 02f4c89..abffe65 100644
--- a/src/cobalt/cssom/embedded_resources/user_agent_style_sheet.css
+++ b/src/cobalt/cssom/embedded_resources/user_agent_style_sheet.css
@@ -1,10 +1,10 @@
 /* The user agent style sheet is applied by default to all web page elements.
    Most of the specifications made here are specified and explained here:
-   https://www.w3.org/TR/html5/rendering.html#rendering
+   https://www.w3.org/TR/html50/rendering.html#rendering
 */
 
 /* Hidden Elements
-   https://www.w3.org/TR/html5/rendering.html#hidden-elements */
+   https://www.w3.org/TR/html50/rendering.html#hidden-elements */
 area, base, basefont, datalist, head, link,
 meta, noembed, noframes, param, rp, script, source, style, template, track,
 title {
@@ -12,12 +12,12 @@
 }
 
 /* The page
-   https://www.w3.org/TR/html5/rendering.html#the-page */
+   https://www.w3.org/TR/html50/rendering.html#the-page */
 html, body { display: block; }
 
 
 /* Flow content
-   https://www.w3.org/TR/html5/rendering.html#flow-content-0 */
+   https://www.w3.org/TR/html50/rendering.html#flow-content-0 */
 address, blockquote, center, div, figure, figcaption, footer, form,
 header, hr, legend, listing, p, plaintext, pre, xmp {
   display: block;
diff --git a/src/cobalt/debug/backend/content/dom_agent.js b/src/cobalt/debug/backend/content/dom_agent.js
index 7c8d70f..65bef03 100644
--- a/src/cobalt/debug/backend/content/dom_agent.js
+++ b/src/cobalt/debug/backend/content/dom_agent.js
@@ -309,10 +309,6 @@
   return node;
 }
 
-// TODO: Don't use an actual MutationObserver since the page under test can
-// disconnect it from the nodes being observed. Instead set _onNodeMutated() as
-// a callback on DebugBackend and hook it up to MutationObserverTaskManager to
-// always run when notifying actual MutationObservers.
 const _nodeObserver = new MutationObserver(_onNodeMutated);
 const _observerConfig = {
   attributes: true,
diff --git a/src/cobalt/demos/content/page-visibility-demo/page-visibility-demo.html b/src/cobalt/demos/content/page-visibility-demo/page-visibility-demo.html
index 890f0cd..afee11b 100644
--- a/src/cobalt/demos/content/page-visibility-demo/page-visibility-demo.html
+++ b/src/cobalt/demos/content/page-visibility-demo/page-visibility-demo.html
@@ -2,6 +2,15 @@
 <html>
 <head>
   <script type="text/javascript">
+
+    function get_new_tab_on_blur() {
+      return document.getElementById('new_tab_on_blur').innerHTML == "true";
+    }
+
+    function set_new_tab_on_blur(value) {
+      return document.getElementById('new_tab_on_blur').innerHTML = value ? "true": "false";
+    }
+
     function printVisibilityState() {
       console.log("  document.hasFocus() == " + document.hasFocus());
       console.log("  document.hidden == " + document.hidden);
@@ -11,6 +20,10 @@
 
     window.onblur = function() {
       console.log("window.onblur");
+      if (get_new_tab_on_blur()) {
+        set_new_tab_on_blur(false);
+        setTimeout(function() { window.open("about:blank", "_blank")}, 2000);
+      }
       printVisibilityState();
     };
 
@@ -19,6 +32,11 @@
       printVisibilityState();
     };
 
+    window.onload = function() {
+      console.log("window.onload");
+      printVisibilityState();
+    };
+
     document.onblur = function() {
       console.log("document.onblur");
       printVisibilityState();
@@ -33,7 +51,30 @@
       console.log("document.onvisibilitychange: " + document.visibilityState);
       printVisibilityState();
     };
+
+    document.onfreeze = function() {
+      console.log("document.onfreeze")
+      printVisibilityState();
+    };
+
+    document.onresume = function() {
+      console.log("document.onresume")
+      printVisibilityState();
+    };
+    document.onkeypress = function(e) {
+      console.log("document.onkeypress: keyCode == " + e.keyCode)
+      if (e.keyCode == 13) {
+        set_new_tab_on_blur(!get_new_tab_on_blur());
+      } else {
+        printVisibilityState();
+      }
+    }
   </script>
 </head>
-<body style="background-color:#ccc"></body>
+<body style="background-color:#ccc">
+
+Press Enter to toggle new_tab_on_blur :
+<span id="new_tab_on_blur">false</span>
+
+</body>
 </html>
diff --git a/src/cobalt/demos/content/script-tag-demo/script-tag-demo.html b/src/cobalt/demos/content/script-tag-demo/script-tag-demo.html
index 4767901..bcdc2ca 100644
--- a/src/cobalt/demos/content/script-tag-demo/script-tag-demo.html
+++ b/src/cobalt/demos/content/script-tag-demo/script-tag-demo.html
@@ -14,7 +14,7 @@
   <!--
     For details of the options for loading for scripts, see Prepare() in
     html_script_element.cc, and paragraph 15 in the spec:
-      https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script
+      https://www.w3.org/TR/html50/scripting-1.html#prepare-a-script
   -->
 
   <!-- Option 5: Otherwise(inline script). -->
diff --git a/src/cobalt/doc/performance_tuning.md b/src/cobalt/doc/performance_tuning.md
index e8336f3..740d0ec 100644
--- a/src/cobalt/doc/performance_tuning.md
+++ b/src/cobalt/doc/performance_tuning.md
@@ -42,15 +42,17 @@
 
 To enable V8, you must modify the `GetVariables()` method in your
 `gyp_configuration.py` file and ensure that the variables dictionary that is
-returned contains the following key/value pairs:
+returned contains the following key/value pair:
 
 ```
 {
   'javascript_engine': 'v8',
-  'cobalt_enable_jit': 1,
 }
 ```
 
+Additionally, you must implement the `CobaltExtensionConfigurationApi` such
+that the `CobaltEnableJit()` method returns `true`.
+
 Note also that use of V8 requires Starboard version 10 or higher.
 
 **Tags:** *startup, browse-to-watch, cpu memory, input latency.*
@@ -171,8 +173,8 @@
 more memory (to store compiled code) and can also actually slow down
 JavaScript execution (e.g. time must now be spent compiling code).  It is
 recommended that JIT support be left disabled, but you can experiment with
-it by setting the `cobalt_enable_jit` `gyp_configuration.gypi` variable to `1`
-to enable JIT, or `0` to disable it.
+it by implementing the CobaltExtensionConfigurationApi method
+`CobaltEnableJit()` to return `true` to enable JIT, or `false` to disable it.
 
 **Tags:** *gyp_configuration.gypi, startup, browse-to-watch, input latency,
            cpu memory.*
diff --git a/src/cobalt/dom/blob.h b/src/cobalt/dom/blob.h
index 439e61a..19f04b1 100644
--- a/src/cobalt/dom/blob.h
+++ b/src/cobalt/dom/blob.h
@@ -72,6 +72,7 @@
   const std::string& type() const { return type_; }
 
   DEFINE_WRAPPABLE_TYPE(Blob);
+  JSObjectType GetJSObjectType() override { return JSObjectType::kBlob; }
 
  private:
   static const BlobPropertyBag& EmptyBlobPropertyBag();
diff --git a/src/cobalt/dom/crypto.cc b/src/cobalt/dom/crypto.cc
index 77eccc5..a93cc8a 100644
--- a/src/cobalt/dom/crypto.cc
+++ b/src/cobalt/dom/crypto.cc
@@ -20,6 +20,12 @@
 namespace cobalt {
 namespace dom {
 
+// https://www.w3.org/TR/WebCryptoAPI/#Crypto-attribute-subtle
+scoped_refptr<subtlecrypto::SubtleCrypto> Crypto::subtle(
+    script::EnvironmentSettings* settings) const {
+  return new subtlecrypto::SubtleCrypto(settings);
+}
+
 // https://www.w3.org/TR/WebCryptoAPI/#dfn-Crypto-method-getRandomValues
 // static
 script::Handle<script::ArrayBufferView> Crypto::GetRandomValues(
diff --git a/src/cobalt/dom/crypto.h b/src/cobalt/dom/crypto.h
index a2745fc..47b86a9 100644
--- a/src/cobalt/dom/crypto.h
+++ b/src/cobalt/dom/crypto.h
@@ -16,8 +16,10 @@
 #define COBALT_DOM_CRYPTO_H_
 
 #include "cobalt/script/array_buffer_view.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/wrappable.h"
+#include "cobalt/subtlecrypto/subtle_crypto.h"
 
 namespace cobalt {
 namespace dom {
@@ -32,10 +34,12 @@
  public:
   // Web API:Crypto
   //
+  scoped_refptr<subtlecrypto::SubtleCrypto> subtle(
+      script::EnvironmentSettings* settings) const;
+
   static script::Handle<script::ArrayBufferView> GetRandomValues(
       const script::Handle<script::ArrayBufferView>& array,
       script::ExceptionState* exception_state);
-
   DEFINE_WRAPPABLE_TYPE(Crypto);
 };
 
diff --git a/src/cobalt/dom/crypto.idl b/src/cobalt/dom/crypto.idl
index e7f58ba..bf624b9 100644
--- a/src/cobalt/dom/crypto.idl
+++ b/src/cobalt/dom/crypto.idl
@@ -14,6 +14,8 @@
 
 // https://www.w3.org/TR/WebCryptoAPI/#crypto-interface
 
+[Exposed=Window]
 interface Crypto {
+  [CallWith=EnvironmentSettings] readonly attribute SubtleCrypto subtle;
   [RaisesException] ArrayBufferView getRandomValues(ArrayBufferView array);
 };
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index ab3e410..110ad0b 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -314,12 +314,12 @@
 }
 
 // Algorithm for body:
-//   https://www.w3.org/TR/html5/dom.html#dom-document-body
+//   https://www.w3.org/TR/html50/dom.html#dom-document-body
 scoped_refptr<HTMLBodyElement> Document::body() const {
   // The body element of a document is the first child of the html element that
   // is either a body element or a frameset element. If there is no such
   // element, it is null.
-  //   https://www.w3.org/TR/html5/dom.html#the-body-element-0
+  //   https://www.w3.org/TR/html50/dom.html#the-body-element-0
   HTMLHtmlElement* html_element = html().get();
   if (!html_element) {
     return NULL;
@@ -338,7 +338,7 @@
 }
 
 // Algorithm for set_body:
-//   https://www.w3.org/TR/html5/dom.html#dom-document-body
+//   https://www.w3.org/TR/html50/dom.html#dom-document-body
 void Document::set_body(const scoped_refptr<HTMLBodyElement>& body) {
   // 1. If the new value is not a body or frameset element, then throw a
   //    HierarchyRequestError exception and abort these steps.
@@ -370,11 +370,11 @@
 }
 
 // Algorithm for head:
-//   https://www.w3.org/TR/html5/dom.html#dom-document-head
+//   https://www.w3.org/TR/html50/dom.html#dom-document-head
 scoped_refptr<HTMLHeadElement> Document::head() const {
   // The head element of a document is the first head element that is a child of
   // the html element, if there is one, or null otherwise.
-  //   https://www.w3.org/TR/html5/dom.html#the-head-element-0
+  //   https://www.w3.org/TR/html50/dom.html#the-head-element-0
   HTMLHtmlElement* html_element = html().get();
   if (!html_element) {
     return NULL;
@@ -396,7 +396,7 @@
   return page_visibility_state()->HasWindowFocus();
 }
 
-// https://www.w3.org/TR/html5/editing.html#dom-document-activeelement
+// https://www.w3.org/TR/html50/editing.html#dom-document-activeelement
 scoped_refptr<Element> Document::active_element() const {
   // The activeElement attribute on Document objects must return the element in
   // the document that is focused. If no element in the Document is focused,
@@ -515,7 +515,7 @@
 scoped_refptr<HTMLHtmlElement> Document::html() const {
   // The html element of a document is the document's root element, if there is
   // one and it's an html element, or null otherwise.
-  //   https://www.w3.org/TR/html5/dom.html#the-html-element-0
+  //   https://www.w3.org/TR/html50/dom.html#the-html-element-0
   Element* root = document_element().get();
   if (!root) {
     return NULL;
@@ -867,8 +867,6 @@
 #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 void Document::SetPartialLayout(bool enabled) {
   partial_layout_is_enabled_ = enabled;
-  DLOG(INFO) << "Partial Layout is "
-             << (partial_layout_is_enabled_ ? "on" : "off");
 }
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 19eb1d3..b0bf4e6 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -190,7 +190,7 @@
   scoped_refptr<Element> GetElementById(const std::string& id) const;
 
   // Web API: HTML5 (partial interface)
-  //   https://www.w3.org/TR/html5/dom.html#the-document-object
+  //   https://www.w3.org/TR/html50/dom.html#the-document-object
   //
   const scoped_refptr<Location>& location() const;
 
@@ -202,7 +202,7 @@
 
   scoped_refptr<HTMLHeadElement> head() const;
 
-  // https://www.w3.org/TR/html5/editing.html#dom-document-hasfocus
+  // https://www.w3.org/TR/html50/editing.html#dom-document-hasfocus
   bool HasFocus() const;
 
   scoped_refptr<Element> active_element() const;
@@ -225,7 +225,7 @@
     return default_timeline_;
   }
 
-  // https://www.w3.org/TR/html5/dom.html#dom-document-cookie
+  // https://www.w3.org/TR/html50/dom.html#dom-document-cookie
   void set_cookie(const std::string& cookie,
                   script::ExceptionState* exception_state);
   std::string cookie(script::ExceptionState* exception_state) const;
@@ -236,7 +236,7 @@
 
   // Returns the document's ready state, i.e. whether the document's 'load'
   // event has fired yet or not.
-  // https://www.w3.org/TR/html5/dom.html#dom-document-readystate
+  // https://www.w3.org/TR/html50/dom.html#dom-document-readystate
   DocumentReadyState ready_state() const { return ready_state_; }
 
   // Custom, not in any spec: Node.
@@ -262,7 +262,7 @@
   scoped_refptr<HTMLHtmlElement> html() const;
 
   // List of scripts that will execute in order as soon as possible.
-  //   https://www.w3.org/TR/html5/scripting-1.html#list-of-scripts-that-will-execute-in-order-as-soon-as-possible
+  //   https://www.w3.org/TR/html50/scripting-1.html#list-of-scripts-that-will-execute-in-order-as-soon-as-possible
   std::deque<HTMLScriptElement*>* scripts_to_be_executed() {
     return &scripts_to_be_executed_;
   }
@@ -285,7 +285,7 @@
 
   // Returns whether the document has browsing context. Having the browsing
   // context means the document is shown on the screen.
-  //   https://www.w3.org/TR/html5/browsers.html#browsing-context
+  //   https://www.w3.org/TR/html50/browsers.html#browsing-context
   bool HasBrowsingContext() const { return !!window_; }
 
   void set_window(Window* window) { window_ = window; }
diff --git a/src/cobalt/dom/document.idl b/src/cobalt/dom/document.idl
index a765015..3791276 100644
--- a/src/cobalt/dom/document.idl
+++ b/src/cobalt/dom/document.idl
@@ -22,7 +22,7 @@
 
   // user interaction
   // Non-standard return type, should be WindowProxy.
-  // https://www.w3.org/TR/html5/single-page.html#dom-document-defaultview
+  // https://www.w3.org/TR/html50/single-page.html#dom-document-defaultview
   readonly attribute Window? defaultView;
 
   HTMLCollection getElementsByTagName(DOMString localName);
diff --git a/src/cobalt/dom/document_html5.idl b/src/cobalt/dom/document_html5.idl
index cd0b0fd..3d94101 100644
--- a/src/cobalt/dom/document_html5.idl
+++ b/src/cobalt/dom/document_html5.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/dom.html#the-document-object
+// https://www.w3.org/TR/html50/dom.html#the-document-object
 
 [OverrideBuiltins]
 partial /*sealed*/ interface Document {
diff --git a/src/cobalt/dom/document_ready_state.idl b/src/cobalt/dom/document_ready_state.idl
index 7cf6f90..57a44f1 100644
--- a/src/cobalt/dom/document_ready_state.idl
+++ b/src/cobalt/dom/document_ready_state.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/dom.html#the-document-object
+// https://www.w3.org/TR/html50/dom.html#the-document-object
 
 enum DocumentReadyState {
   "loading",
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index f0a1a2e..7ed14b7 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -210,6 +210,8 @@
         'local_storage_database.h',
         'location.cc',
         'location.h',
+        'lottie_player.cc',
+        'lottie_player.h',
         'media_query_list.cc',
         'media_query_list.h',
         'media_source.cc',
diff --git a/src/cobalt/dom/dom_exception.h b/src/cobalt/dom/dom_exception.h
index 5651525..28a8778 100644
--- a/src/cobalt/dom/dom_exception.h
+++ b/src/cobalt/dom/dom_exception.h
@@ -86,6 +86,7 @@
                     script::ExceptionState* exception_state);
 
   DEFINE_WRAPPABLE_TYPE(DOMException);
+  JSObjectType GetJSObjectType() override { return JSObjectType::kError; }
 
  private:
   ExceptionCode code_;
diff --git a/src/cobalt/dom/dom_string_map.cc b/src/cobalt/dom/dom_string_map.cc
index dfe531e..26db29e 100644
--- a/src/cobalt/dom/dom_string_map.cc
+++ b/src/cobalt/dom/dom_string_map.cc
@@ -28,7 +28,7 @@
 const size_t kDataPrefixLength = sizeof(kDataPrefix) - 1;
 
 // See "The algorithm for getting the list of name-value pairs" at
-// https://www.w3.org/TR/html5/dom.html#dom-dataset.
+// https://www.w3.org/TR/html50/dom.html#dom-dataset.
 base::Optional<std::string> TryConvertAttributeNameToPropertyName(
     const std::string& attribute_name) {
   // First five characters of attribute name should be "data-".
@@ -85,7 +85,7 @@
 }
 
 // See "The algorithm for setting names to certain values" at
-// https://www.w3.org/TR/html5/dom.html#dom-dataset.
+// https://www.w3.org/TR/html50/dom.html#dom-dataset.
 base::Optional<std::string> TryConvertPropertyNameToAttributeName(
     const std::string& property_name) {
   // Insert the string "data-" at the front of attribute name.
diff --git a/src/cobalt/dom/dom_string_map.h b/src/cobalt/dom/dom_string_map.h
index 05a00d5..7972b47 100644
--- a/src/cobalt/dom/dom_string_map.h
+++ b/src/cobalt/dom/dom_string_map.h
@@ -30,12 +30,12 @@
 
 // The DOMStringMap interface represents a set of name-value pairs. It exposes
 // these using the scripting language's native mechanisms for property access.
-//   https://www.w3.org/TR/html5/infrastructure.html#domstringmap
+//   https://www.w3.org/TR/html50/infrastructure.html#domstringmap
 //
 // NOTE: This implementation is targeted to support |HTMLElement.dataset|
 //       which performs bidirectional conversion of key names between hyphenated
 //       and camel-cased forms as per
-//       https://www.w3.org/TR/html5/dom.html#dom-dataset.
+//       https://www.w3.org/TR/html50/dom.html#dom-dataset.
 class DOMStringMap : public script::Wrappable,
                      public base::SupportsWeakPtr<DOMStringMap> {
  public:
diff --git a/src/cobalt/dom/dom_string_map.idl b/src/cobalt/dom/dom_string_map.idl
index e0ffa61..df0f23b 100644
--- a/src/cobalt/dom/dom_string_map.idl
+++ b/src/cobalt/dom/dom_string_map.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/infrastructure.html#domstringmap
+// https://www.w3.org/TR/html50/infrastructure.html#domstringmap
 
 [OverrideBuiltins]
 interface DOMStringMap {
diff --git a/src/cobalt/dom/dom_token_list.h b/src/cobalt/dom/dom_token_list.h
index eb755d1..4497ac3 100644
--- a/src/cobalt/dom/dom_token_list.h
+++ b/src/cobalt/dom/dom_token_list.h
@@ -56,6 +56,7 @@
   Element* element() { return element_; }
 
   DEFINE_WRAPPABLE_TYPE(DOMTokenList);
+  JSObjectType GetJSObjectType() override { return JSObjectType::kArray; }
   void TraceMembers(script::Tracer* tracer) override;
 
  private:
diff --git a/src/cobalt/dom/element.h b/src/cobalt/dom/element.h
index a0c50e2..a590a76 100644
--- a/src/cobalt/dom/element.h
+++ b/src/cobalt/dom/element.h
@@ -183,7 +183,7 @@
 
   // Used to ensure that the style attribute value reflects the style
   // declaration.
-  //   https://www.w3.org/TR/html5/dom.html#the-style-attribute
+  //   https://www.w3.org/TR/html50/dom.html#the-style-attribute
   virtual base::Optional<std::string> GetStyleAttribute() const;
   virtual void SetStyleAttribute(const std::string& value);
   virtual void RemoveStyleAttribute();
@@ -216,7 +216,7 @@
   ~Element() override;
 
   // Getting and setting boolean attribute.
-  //   https://www.w3.org/TR/html5/infrastructure.html#boolean-attribute
+  //   https://www.w3.org/TR/html50/infrastructure.html#boolean-attribute
   bool GetBooleanAttribute(const std::string& name) const;
   void SetBooleanAttribute(const std::string& name, bool value);
 
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc
index 5a9c775..71a1894 100644
--- a/src/cobalt/dom/eme/media_key_session.cc
+++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -44,13 +44,9 @@
       drm_system_(drm_system),
       drm_system_session_(drm_system->CreateSession(
           base::Bind(&MediaKeySession::OnSessionUpdateKeyStatuses,
-                     base::AsWeakPtr(this))
-#if SB_HAS(DRM_SESSION_CLOSED)
-              ,
+                     base::AsWeakPtr(this)),
           base::Bind(&MediaKeySession::OnSessionClosed,
-                     base::AsWeakPtr(this))
-#endif             // SB_HAS(DRM_SESSION_CLOSED)
-              )),  // NOLINT(whitespace/parens)
+                     base::AsWeakPtr(this)))),
       script_value_factory_(script_value_factory),
       uninitialized_(true),
       callable_(false),
@@ -58,8 +54,7 @@
       closed_callback_(closed_callback),
       ALLOW_THIS_IN_INITIALIZER_LIST(closed_promise_reference_(
           this, script_value_factory->CreateBasicPromise<void>())),
-      initiated_by_generate_request_(false) {
-}
+      initiated_by_generate_request_(false) {}
 
 // According to the step 3.1 of
 // https://www.w3.org/TR/encrypted-media/#dom-mediakeys-createsession,
@@ -215,11 +210,6 @@
   // 5.2. Use CDM to close the key session associated with session.
   drm_system_session_->Close();
 
-#if !SB_HAS(DRM_SESSION_CLOSED)
-  // 5.3.1. Run the Session Closed algorithm on the session.
-  OnSessionClosed();
-#endif  // !SB_HAS(DRM_SESSION_CLOSED)
-
   // 5.3.2. Resolve promise.
   promise->Resolve();
   return promise;
diff --git a/src/cobalt/dom/error_event.h b/src/cobalt/dom/error_event.h
index b1c2c63..dba71ee 100644
--- a/src/cobalt/dom/error_event.h
+++ b/src/cobalt/dom/error_event.h
@@ -28,7 +28,7 @@
 // Whenever an uncaught runtime script error occurs in one of the scripts
 // associated with a Document, the user agent must report the error for the
 // relevant script.
-//   https://www.w3.org/TR/html5/webappapis.html#errorevent
+//   https://www.w3.org/TR/html50/webappapis.html#errorevent
 class ErrorEvent : public Event {
  public:
   explicit ErrorEvent(const std::string& type)
diff --git a/src/cobalt/dom/error_event.idl b/src/cobalt/dom/error_event.idl
index 35f7c72..9cd324e 100644
--- a/src/cobalt/dom/error_event.idl
+++ b/src/cobalt/dom/error_event.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#errorevent
+// https://www.w3.org/TR/html50/webappapis.html#errorevent
 
 [Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
 interface ErrorEvent : Event {
diff --git a/src/cobalt/dom/error_event_init.idl b/src/cobalt/dom/error_event_init.idl
index 9210f70..80735ee 100644
--- a/src/cobalt/dom/error_event_init.idl
+++ b/src/cobalt/dom/error_event_init.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#erroreventinit
+// https://www.w3.org/TR/html50/webappapis.html#erroreventinit
 
 dictionary ErrorEventInit : EventInit {
   DOMString message = "";
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index eae5c6a..23411b5 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -127,7 +127,7 @@
   // Web API: GlobalEventHandlers (implements)
   // Many objects can have event handlers specified. These act as non-capture
   // event listeners for the object on which they are specified.
-  //   https://www.w3.org/TR/html5/webappapis.html#globaleventhandlers
+  //   https://www.w3.org/TR/html50/webappapis.html#globaleventhandlers
   //
   const EventListenerScriptValue* onblur() {
     return GetAttributeEventListener(base::Tokens::blur());
diff --git a/src/cobalt/dom/global_event_handlers.idl b/src/cobalt/dom/global_event_handlers.idl
index 6a9e7ef..c877347 100644
--- a/src/cobalt/dom/global_event_handlers.idl
+++ b/src/cobalt/dom/global_event_handlers.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#globaleventhandlers
+// https://www.w3.org/TR/html50/webappapis.html#globaleventhandlers
 
 [NoInterfaceObject]
 interface GlobalEventHandlers {
diff --git a/src/cobalt/dom/history.h b/src/cobalt/dom/history.h
index 1c01d2c..30d8606 100644
--- a/src/cobalt/dom/history.h
+++ b/src/cobalt/dom/history.h
@@ -21,8 +21,8 @@
 namespace dom {
 
 // The History object can be used to maintain browser session history.
-// https://www.w3.org/TR/html5/browsers.html#the-history-interface
-// https://www.w3.org/TR/html5/browsers.html#history-1
+// https://www.w3.org/TR/html50/browsers.html#the-history-interface
+// https://www.w3.org/TR/html50/browsers.html#history-1
 // This implementation is extremely basic.
 class History : public script::Wrappable {
  public:
diff --git a/src/cobalt/dom/history.idl b/src/cobalt/dom/history.idl
index 92fea49..da86104 100644
--- a/src/cobalt/dom/history.idl
+++ b/src/cobalt/dom/history.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/browsers.html#the-history-interface
+// https://www.w3.org/TR/html50/browsers.html#the-history-interface
 
 // The joint session history of a top-level browsing context is the union of
 // all the session histories of all browsing contexts of all the fully active
diff --git a/src/cobalt/dom/html_anchor_element.h b/src/cobalt/dom/html_anchor_element.h
index 872838a..0f78121 100644
--- a/src/cobalt/dom/html_anchor_element.h
+++ b/src/cobalt/dom/html_anchor_element.h
@@ -29,7 +29,7 @@
 
 // The HTML Anchor Element (<a>) defines a hyperlink to a location on the same
 // page or any other page on the Web.
-//   https://www.w3.org/TR/html5/text-level-semantics.html#htmlanchorelement
+//   https://www.w3.org/TR/html50/text-level-semantics.html#htmlanchorelement
 class HTMLAnchorElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_anchor_element.idl b/src/cobalt/dom/html_anchor_element.idl
index 6e74273..f0a92e7 100644
--- a/src/cobalt/dom/html_anchor_element.idl
+++ b/src/cobalt/dom/html_anchor_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/text-level-semantics.html#the-a-element
+// https://www.w3.org/TR/html50/text-level-semantics.html#the-a-element
 
 interface HTMLAnchorElement : HTMLElement {};
 HTMLAnchorElement implements URLUtils;
diff --git a/src/cobalt/dom/html_audio_element.h b/src/cobalt/dom/html_audio_element.h
index 38be787..f456ad3 100644
--- a/src/cobalt/dom/html_audio_element.h
+++ b/src/cobalt/dom/html_audio_element.h
@@ -21,7 +21,7 @@
 namespace dom {
 
 // The HTMLAudioElement is used to play audios.
-//   https://www.w3.org/TR/html5/embedded-content-0.html#the-audio-element
+//   https://www.w3.org/TR/html50/embedded-content-0.html#the-audio-element
 class HTMLAudioElement : public HTMLMediaElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_audio_element.idl b/src/cobalt/dom/html_audio_element.idl
index 2104e35..ba25aac 100644
--- a/src/cobalt/dom/html_audio_element.idl
+++ b/src/cobalt/dom/html_audio_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/embedded-content-0.html#the-video-element
+// https://www.w3.org/TR/html50/embedded-content-0.html#the-video-element
 
 interface HTMLAudioElement : HTMLMediaElement {};
diff --git a/src/cobalt/dom/html_body_element.h b/src/cobalt/dom/html_body_element.h
index f803ecf..fa439aa 100644
--- a/src/cobalt/dom/html_body_element.h
+++ b/src/cobalt/dom/html_body_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // The body element represents the content of the document.
-//   https://www.w3.org/TR/html5/sections.html#the-body-element
+//   https://www.w3.org/TR/html50/sections.html#the-body-element
 class HTMLBodyElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_body_element.idl b/src/cobalt/dom/html_body_element.idl
index 794853f..82a2bed 100644
--- a/src/cobalt/dom/html_body_element.idl
+++ b/src/cobalt/dom/html_body_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/sections.html#the-body-element
+// https://www.w3.org/TR/html50/sections.html#the-body-element
 
 interface HTMLBodyElement : HTMLElement {
   // CSSOM View Module
diff --git a/src/cobalt/dom/html_br_element.h b/src/cobalt/dom/html_br_element.h
index 9677619..f296715 100644
--- a/src/cobalt/dom/html_br_element.h
+++ b/src/cobalt/dom/html_br_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // The br element represents a line break.
-//   https://www.w3.org/TR/html5/text-level-semantics.html#the-br-element
+//   https://www.w3.org/TR/html50/text-level-semantics.html#the-br-element
 class HTMLBRElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_br_element.idl b/src/cobalt/dom/html_br_element.idl
index db1c666..f53bc47 100644
--- a/src/cobalt/dom/html_br_element.idl
+++ b/src/cobalt/dom/html_br_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/text-level-semantics.html#the-br-element
+// https://www.w3.org/TR/html50/text-level-semantics.html#the-br-element
 
 interface HTMLBRElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_collection.h b/src/cobalt/dom/html_collection.h
index 3598e15..45f092d 100644
--- a/src/cobalt/dom/html_collection.h
+++ b/src/cobalt/dom/html_collection.h
@@ -62,6 +62,7 @@
       script::PropertyEnumerator* enumerator) const = 0;
 
   DEFINE_WRAPPABLE_TYPE(HTMLCollection);
+  JSObjectType GetJSObjectType() override { return JSObjectType::kArray; }
 
  protected:
   HTMLCollection();
diff --git a/src/cobalt/dom/html_div_element.h b/src/cobalt/dom/html_div_element.h
index 8d66eff..19dfd28 100644
--- a/src/cobalt/dom/html_div_element.h
+++ b/src/cobalt/dom/html_div_element.h
@@ -27,7 +27,7 @@
 // The div element has no special meaning at all. It represents its children. It
 // can be used with the class, lang, and title attributes to mark up semantics
 // common to a group of consecutive elements.
-//   https://www.w3.org/TR/html5/grouping-content.html#the-div-element
+//   https://www.w3.org/TR/html50/grouping-content.html#the-div-element
 class HTMLDivElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_div_element.idl b/src/cobalt/dom/html_div_element.idl
index 8468fa2..52122a9 100644
--- a/src/cobalt/dom/html_div_element.idl
+++ b/src/cobalt/dom/html_div_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/grouping-content.html#the-div-element
+// https://www.w3.org/TR/html50/grouping-content.html#the-div-element
 
 interface HTMLDivElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 1fa7ece..ad0c2b8 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -56,6 +56,7 @@
 #include "cobalt/dom/html_title_element.h"
 #include "cobalt/dom/html_unknown_element.h"
 #include "cobalt/dom/html_video_element.h"
+#include "cobalt/dom/lottie_player.h"
 #include "cobalt/dom/rule_matching.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/loader/image/animated_image_tracker.h"
@@ -211,7 +212,7 @@
 }
 
 // Algorithm for Focus:
-//   https://www.w3.org/TR/html5/editing.html#dom-focus
+//   https://www.w3.org/TR/html50/editing.html#dom-focus
 void HTMLElement::Focus() {
   // 1. If the element is marked as locked for focus, then abort these steps.
   if (locked_for_focus_) {
@@ -235,7 +236,7 @@
 }
 
 // Algorithm for Blur:
-//   https://www.w3.org/TR/html5/editing.html#dom-blur
+//   https://www.w3.org/TR/html50/editing.html#dom-blur
 void HTMLElement::Blur() {
   // The blur() method, when invoked, should run the unfocusing steps for the
   // element on which the method was called instead. User agents may selectively
@@ -879,6 +880,8 @@
   return NULL;
 }
 
+scoped_refptr<LottiePlayer> HTMLElement::AsLottiePlayer() { return NULL; }
+
 void HTMLElement::ClearRuleMatchingState() {
   ClearRuleMatchingStateInternal(true /*invalidate_descendants*/);
 }
@@ -1228,7 +1231,7 @@
   // For example, this might happen because the element is removed from its
   // Document, or has a hidden attribute added. It would also happen to an input
   // element when the element gets disabled.
-  //   https://www.w3.org/TR/html5/editing.html#unfocusing-steps
+  //   https://www.w3.org/TR/html50/editing.html#unfocusing-steps
   Document* document = node_document();
   DCHECK(document);
   if (document->active_element() == this->AsElement()) {
@@ -1271,18 +1274,18 @@
 }
 
 // Algorithm for IsFocusable:
-//   https://www.w3.org/TR/html5/editing.html#focusable
+//   https://www.w3.org/TR/html50/editing.html#focusable
 bool HTMLElement::IsFocusable() {
   return HasTabindexFocusFlag() && IsBeingRendered();
 }
 
 // Algorithm for HasTabindexFocusFlag:
-//  https://www.w3.org/TR/html5/editing.html#specially-focusable
+//  https://www.w3.org/TR/html50/editing.html#specially-focusable
 bool HTMLElement::HasTabindexFocusFlag() const { return tabindex_.has_value(); }
 
 // An element is being rendered if it has any associated CSS layout boxes, SVG
 // layout boxes, or some equivalent in other styling languages.
-//   https://www.w3.org/TR/html5/rendering.html#being-rendered
+//   https://www.w3.org/TR/html50/rendering.html#being-rendered
 bool HTMLElement::IsBeingRendered() {
   Document* document = node_document();
   if (!document) {
@@ -1299,7 +1302,7 @@
 }
 
 // Algorithm for RunFocusingSteps:
-//   https://www.w3.org/TR/html5/editing.html#focusing-steps
+//   https://www.w3.org/TR/html50/editing.html#focusing-steps
 void HTMLElement::RunFocusingSteps() {
   // 1. If the element is not in a Document, or if the element's Document has
   // no browsing context, or if the element's Document's browsing context has no
@@ -1363,7 +1366,7 @@
 }
 
 // Algorithm for RunUnFocusingSteps:
-//   https://www.w3.org/TR/html5/editing.html#unfocusing-steps
+//   https://www.w3.org/TR/html50/editing.html#unfocusing-steps
 void HTMLElement::RunUnFocusingSteps() {
   // 1. Not needed by Cobalt.
 
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index c71d733..fae0c2b 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -68,6 +68,7 @@
 class HTMLTitleElement;
 class HTMLUnknownElement;
 class HTMLVideoElement;
+class LottiePlayer;
 
 // The enum PseudoElementType is used to track the type of pseudo element
 enum PseudoElementType {
@@ -80,7 +81,7 @@
 
 // The basic interface, from which all the HTML elements' interfaces inherit,
 // and which must be used by elements that have no additional requirements.
-//   https://www.w3.org/TR/html5/dom.html#htmlelement
+//   https://www.w3.org/TR/html50/dom.html#htmlelement
 class HTMLElement : public Element, public cssom::MutationObserver {
  public:
   typedef cssom::SelectorTree::Nodes SelectorTreeNodes;
@@ -226,6 +227,7 @@
   virtual scoped_refptr<HTMLTitleElement> AsHTMLTitleElement();
   virtual scoped_refptr<HTMLUnknownElement> AsHTMLUnknownElement();
   virtual scoped_refptr<HTMLVideoElement> AsHTMLVideoElement();
+  virtual scoped_refptr<LottiePlayer> AsLottiePlayer();
 
   // Returns the directionality of the element, which is based upon the
   // element's "dir" attribute if it was set, or that of the parent's if not
@@ -355,7 +357,7 @@
   }
 
   // Returns true if the element is the root element as defined in
-  // https://www.w3.org/TR/html5/semantics.html#the-root-element.
+  // https://www.w3.org/TR/html50/semantics.html#the-root-element.
   bool IsRootElement();
 
   // Returns true if this is a document element.
diff --git a/src/cobalt/dom/html_element.idl b/src/cobalt/dom/html_element.idl
index 6499184..6e9e25a 100644
--- a/src/cobalt/dom/html_element.idl
+++ b/src/cobalt/dom/html_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/dom.html#htmlelement
+// https://www.w3.org/TR/html50/dom.html#htmlelement
 
 interface HTMLElement : Element {
   attribute DOMString dir;
diff --git a/src/cobalt/dom/html_element_factory.cc b/src/cobalt/dom/html_element_factory.cc
index 6612cdd..a753273 100644
--- a/src/cobalt/dom/html_element_factory.cc
+++ b/src/cobalt/dom/html_element_factory.cc
@@ -35,6 +35,7 @@
 #include "cobalt/dom/html_title_element.h"
 #include "cobalt/dom/html_unknown_element.h"
 #include "cobalt/dom/html_video_element.h"
+#include "cobalt/dom/lottie_player.h"
 #include "nb/memory_scope.h"
 
 namespace cobalt {
@@ -160,6 +161,7 @@
   RegisterHTMLElementWithSingleTagName<HTMLStyleElement>();
   RegisterHTMLElementWithSingleTagName<HTMLTitleElement>();
   RegisterHTMLElementWithSingleTagName<HTMLVideoElement>();
+  RegisterHTMLElementWithSingleTagName<LottiePlayer>();
 
   // Register HTML elements that have multiple tag names in the map.
   RegisterHTMLElementWithMultipleTagName<HTMLHeadingElement>();
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index 8b54486..e088077 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -40,6 +40,7 @@
 #include "cobalt/dom/html_title_element.h"
 #include "cobalt/dom/html_unknown_element.h"
 #include "cobalt/dom/html_video_element.h"
+#include "cobalt/dom/lottie_player.h"
 #include "cobalt/dom/testing/stub_css_parser.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_script_runner.h"
@@ -173,6 +174,11 @@
   EXPECT_TRUE(html_element->AsHTMLVideoElement());
   EXPECT_EQ(html_element->GetInlineSourceLocation().file_path,
             "[object HTMLVideoElement]");
+  html_element = html_element_factory_.CreateHTMLElement(
+      document_, base::Token("lottie-player"));
+  EXPECT_TRUE(html_element->AsLottiePlayer());
+  EXPECT_EQ(html_element->GetInlineSourceLocation().file_path,
+            "[object LottiePlayer]");
 
   html_element =
       html_element_factory_.CreateHTMLElement(document_, base::Token("h1"));
diff --git a/src/cobalt/dom/html_head_element.h b/src/cobalt/dom/html_head_element.h
index f92dbf2..5b68dc1 100644
--- a/src/cobalt/dom/html_head_element.h
+++ b/src/cobalt/dom/html_head_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // The head element represents a collection of metadata for the Document.
-//   https://www.w3.org/TR/html5/document-metadata.html#the-head-element
+//   https://www.w3.org/TR/html50/document-metadata.html#the-head-element
 class HTMLHeadElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_head_element.idl b/src/cobalt/dom/html_head_element.idl
index b869f67..2197442 100644
--- a/src/cobalt/dom/html_head_element.idl
+++ b/src/cobalt/dom/html_head_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/document-metadata.html#the-head-element
+// https://www.w3.org/TR/html50/document-metadata.html#the-head-element
 
 interface HTMLHeadElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_heading_element.h b/src/cobalt/dom/html_heading_element.h
index 82bb5e9..2d7b044 100644
--- a/src/cobalt/dom/html_heading_element.h
+++ b/src/cobalt/dom/html_heading_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // These elements represent headings for their sections.
-//   https://www.w3.org/TR/html5/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
+//   https://www.w3.org/TR/html50/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
 class HTMLHeadingElement : public HTMLElement {
  public:
   static const int kTagNameCount;
diff --git a/src/cobalt/dom/html_heading_element.idl b/src/cobalt/dom/html_heading_element.idl
index 0e57963..c0d80c7 100644
--- a/src/cobalt/dom/html_heading_element.idl
+++ b/src/cobalt/dom/html_heading_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
+// https://www.w3.org/TR/html50/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
 
 interface HTMLHeadingElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_html_element.h b/src/cobalt/dom/html_html_element.h
index e34652f..71c3260 100644
--- a/src/cobalt/dom/html_html_element.h
+++ b/src/cobalt/dom/html_html_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // The html element represents the root of an HTML document.
-//   https://www.w3.org/TR/html5/semantics.html#the-html-element
+//   https://www.w3.org/TR/html50/semantics.html#the-html-element
 class HTMLHtmlElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_html_element.idl b/src/cobalt/dom/html_html_element.idl
index c840ce5..6ef7015 100644
--- a/src/cobalt/dom/html_html_element.idl
+++ b/src/cobalt/dom/html_html_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/semantics.html#the-html-element
+// https://www.w3.org/TR/html50/semantics.html#the-html-element
 
 interface HTMLHtmlElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_image_element.cc b/src/cobalt/dom/html_image_element.cc
index 511d83c..c4aab0b 100644
--- a/src/cobalt/dom/html_image_element.cc
+++ b/src/cobalt/dom/html_image_element.cc
@@ -78,7 +78,7 @@
 }
 
 // Algorithm for UpdateTheImageData:
-//   https://www.w3.org/TR/html5/embedded-content-0.html#update-the-image-data
+//   https://www.w3.org/TR/html50/embedded-content-0.html#update-the-image-data
 void HTMLImageElement::UpdateImageData() {
   DCHECK(base::MessageLoop::current());
   DCHECK(node_document());
diff --git a/src/cobalt/dom/html_image_element.h b/src/cobalt/dom/html_image_element.h
index cfc5a59..db22502 100644
--- a/src/cobalt/dom/html_image_element.h
+++ b/src/cobalt/dom/html_image_element.h
@@ -29,7 +29,7 @@
 class Document;
 
 // An img element represents an image.
-//   https://www.w3.org/TR/html5/embedded-content-0.html#the-img-element
+//   https://www.w3.org/TR/html50/embedded-content-0.html#the-img-element
 class HTMLImageElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_image_element.idl b/src/cobalt/dom/html_image_element.idl
index 9830631..04dfd11 100644
--- a/src/cobalt/dom/html_image_element.idl
+++ b/src/cobalt/dom/html_image_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/embedded-content-0.html#the-img-element
+// https://www.w3.org/TR/html50/embedded-content-0.html#the-img-element
 
 // TODO: Add the optional width and height parameters to the
 // constructor, as shown in the spec.
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc
index 37b9332..9ed7bea 100644
--- a/src/cobalt/dom/html_link_element.cc
+++ b/src/cobalt/dom/html_link_element.cc
@@ -124,7 +124,7 @@
 }
 
 // Algorithm for Obtain:
-//   https://www.w3.org/TR/html5/document-metadata.html#concept-link-obtain
+//   https://www.w3.org/TR/html50/document-metadata.html#concept-link-obtain
 void HTMLLinkElement::Obtain() {
   TRACK_MEMORY_SCOPE("DOM");
   TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::Obtain()");
diff --git a/src/cobalt/dom/html_link_element.h b/src/cobalt/dom/html_link_element.h
index 1beaa36..ba4ee01 100644
--- a/src/cobalt/dom/html_link_element.h
+++ b/src/cobalt/dom/html_link_element.h
@@ -34,7 +34,7 @@
 class Document;
 
 // The link element allows authors to link their document to other resources.
-//   https://www.w3.org/TR/html5/document-metadata.html#the-link-element
+//   https://www.w3.org/TR/html50/document-metadata.html#the-link-element
 class HTMLLinkElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_link_element.idl b/src/cobalt/dom/html_link_element.idl
index e177b18..825af73 100644
--- a/src/cobalt/dom/html_link_element.idl
+++ b/src/cobalt/dom/html_link_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/document-metadata.html#the-link-element
+// https://www.w3.org/TR/html50/document-metadata.html#the-link-element
 
 interface HTMLLinkElement : HTMLElement {
   attribute DOMString href;
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index f21b694..03c2b3d 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -43,7 +43,7 @@
 typedef media::WebMediaPlayerClient WebMediaPlayerClient;
 
 // The HTMLMediaElement is the base of HTMLAudioElement and HTMLVideoElement.
-//   https://www.w3.org/TR/html5/embedded-content-0.html#media-element
+//   https://www.w3.org/TR/html50/embedded-content-0.html#media-element
 class HTMLMediaElement : public HTMLElement, private WebMediaPlayerClient {
  public:
   HTMLMediaElement(Document* document, base::Token tag_name);
diff --git a/src/cobalt/dom/html_media_element.idl b/src/cobalt/dom/html_media_element.idl
index e26b596..4a1a509 100644
--- a/src/cobalt/dom/html_media_element.idl
+++ b/src/cobalt/dom/html_media_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/embedded-content-0.html#media-elements
+// https://www.w3.org/TR/html50/embedded-content-0.html#media-elements
 
 interface HTMLMediaElement : HTMLElement {
   // error state
diff --git a/src/cobalt/dom/html_meta_element.h b/src/cobalt/dom/html_meta_element.h
index 5c7e3e6..e55d1ef 100644
--- a/src/cobalt/dom/html_meta_element.h
+++ b/src/cobalt/dom/html_meta_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // The meta element allows authors to meta their document to other resources.
-//   https://www.w3.org/TR/html5/document-metadata.html#the-meta-element
+//   https://www.w3.org/TR/html50/document-metadata.html#the-meta-element
 class HTMLMetaElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_meta_element.idl b/src/cobalt/dom/html_meta_element.idl
index 2ee52e5..8b002ef 100644
--- a/src/cobalt/dom/html_meta_element.idl
+++ b/src/cobalt/dom/html_meta_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/document-metadata.html#the-meta-element
+// https://www.w3.org/TR/html50/document-metadata.html#the-meta-element
 
 interface HTMLMetaElement : HTMLElement {
   attribute DOMString name;
diff --git a/src/cobalt/dom/html_paragraph_element.h b/src/cobalt/dom/html_paragraph_element.h
index f54e813..4d0a17a 100644
--- a/src/cobalt/dom/html_paragraph_element.h
+++ b/src/cobalt/dom/html_paragraph_element.h
@@ -25,7 +25,7 @@
 class Document;
 
 // The p element represents a paragraph.
-//   https://www.w3.org/TR/html5/grouping-content.html#the-p-element
+//   https://www.w3.org/TR/html50/grouping-content.html#the-p-element
 class HTMLParagraphElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_paragraph_element.idl b/src/cobalt/dom/html_paragraph_element.idl
index 6431867..43d0f72 100644
--- a/src/cobalt/dom/html_paragraph_element.idl
+++ b/src/cobalt/dom/html_paragraph_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/grouping-content.html#the-p-element
+// https://www.w3.org/TR/html50/grouping-content.html#the-p-element
 
 interface HTMLParagraphElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index cdb642e..ca3bd43 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -122,7 +122,7 @@
 scoped_refptr<Node> HTMLScriptElement::Duplicate() const {
   // The cloning steps for script elements must set the "already started" flag
   // on the copy if it is set on the element being cloned.
-  //   https://www.w3.org/TR/html5/scripting-1.html#already-started
+  //   https://www.w3.org/TR/html50/scripting-1.html#already-started
   scoped_refptr<HTMLScriptElement> new_script = HTMLElement::Duplicate()
                                                     ->AsElement()
                                                     ->AsHTMLElement()
@@ -150,7 +150,7 @@
 }
 
 // Algorithm for Prepare:
-//   https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script
+//   https://www.w3.org/TR/html50/scripting-1.html#prepare-a-script
 void HTMLScriptElement::Prepare() {
   TRACK_MEMORY_SCOPE("DOM");
   // Custom, not in any spec.
@@ -450,7 +450,7 @@
 }
 
 // Algorithm for OnContentProduced:
-//   https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script
+//   https://www.w3.org/TR/html50/scripting-1.html#prepare-a-script
 void HTMLScriptElement::OnContentProduced(
     const loader::Origin& last_url_origin,
     std::unique_ptr<std::string> content) {
@@ -552,7 +552,7 @@
 }
 
 // Algorithm for OnLoadingComplete:
-//   https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script
+//   https://www.w3.org/TR/html50/scripting-1.html#prepare-a-script
 void HTMLScriptElement::OnLoadingComplete(
     const base::Optional<std::string>& error) {
   if (!error) return;
@@ -611,7 +611,7 @@
 }
 
 // Algorithm for Execute:
-//   https://www.w3.org/TR/html5/scripting-1.html#execute-the-script-block
+//   https://www.w3.org/TR/html50/scripting-1.html#execute-the-script-block
 void HTMLScriptElement::Execute(const std::string& content,
                                 const base::SourceLocation& script_location,
                                 bool is_external) {
@@ -621,7 +621,7 @@
   // When inserted using the document.write() method, script elements execute
   // (typically synchronously), but when inserted using innerHTML and
   // outerHTML attributes, they do not execute at all.
-  // https://www.w3.org/TR/html5/scripting-1.html#the-script-element.
+  // https://www.w3.org/TR/html50/scripting-1.html#the-script-element.
   if (!should_execute_) {
     return;
   }
diff --git a/src/cobalt/dom/html_script_element.h b/src/cobalt/dom/html_script_element.h
index 06977aa..bd2fc18 100644
--- a/src/cobalt/dom/html_script_element.h
+++ b/src/cobalt/dom/html_script_element.h
@@ -33,7 +33,7 @@
 
 // The script element allows authors to include dynamic script and data blocks
 // in their documents.
-//   https://www.w3.org/TR/html5/scripting-1.html#the-script-element
+//   https://www.w3.org/TR/html50/scripting-1.html#the-script-element
 class HTMLScriptElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_script_element.idl b/src/cobalt/dom/html_script_element.idl
index b2c707b..80ea31c 100644
--- a/src/cobalt/dom/html_script_element.idl
+++ b/src/cobalt/dom/html_script_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/scripting-1.html#the-script-element
+// https://www.w3.org/TR/html50/scripting-1.html#the-script-element
 
 interface HTMLScriptElement : HTMLElement {
   attribute DOMString src;
diff --git a/src/cobalt/dom/html_span_element.h b/src/cobalt/dom/html_span_element.h
index e70a4c4..a1ed8e7 100644
--- a/src/cobalt/dom/html_span_element.h
+++ b/src/cobalt/dom/html_span_element.h
@@ -25,7 +25,7 @@
 // The span element doesn't mean anything on its own, but can be useful
 // when used together with the global attributes, e.g. class, lang, or dir.
 // It represents its children.
-//    https://www.w3.org/TR/html5/grouping-content.html#the-span-element
+//    https://www.w3.org/TR/html50/grouping-content.html#the-span-element
 class HTMLSpanElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_span_element.idl b/src/cobalt/dom/html_span_element.idl
index b835e2d..79f622a 100644
--- a/src/cobalt/dom/html_span_element.idl
+++ b/src/cobalt/dom/html_span_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/text-level-semantics.html#the-span-element
+// https://www.w3.org/TR/html50/text-level-semantics.html#the-span-element
 
 interface HTMLSpanElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_style_element.h b/src/cobalt/dom/html_style_element.h
index 6ab5184..e6c60f7 100644
--- a/src/cobalt/dom/html_style_element.h
+++ b/src/cobalt/dom/html_style_element.h
@@ -29,7 +29,7 @@
 
 // The style element allows authors to embed style information in their
 // documents.
-//   https://www.w3.org/TR/html5/document-metadata.html#the-style-element
+//   https://www.w3.org/TR/html50/document-metadata.html#the-style-element
 class HTMLStyleElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_style_element.idl b/src/cobalt/dom/html_style_element.idl
index 0bd0bb4..3bb305c 100644
--- a/src/cobalt/dom/html_style_element.idl
+++ b/src/cobalt/dom/html_style_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/document-metadata.html#the-style-element
+// https://www.w3.org/TR/html50/document-metadata.html#the-style-element
 
 interface HTMLStyleElement : HTMLElement {
   attribute DOMString type;
diff --git a/src/cobalt/dom/html_title_element.h b/src/cobalt/dom/html_title_element.h
index b40c389..01ef944 100644
--- a/src/cobalt/dom/html_title_element.h
+++ b/src/cobalt/dom/html_title_element.h
@@ -29,7 +29,7 @@
 // for example in a user's history or bookmarks, or in search results. The
 // document's title is often different from its first heading, since the first
 // heading does not have to stand alone when taken out of context.
-//   https://www.w3.org/TR/html5/document-metadata.html#the-title-element
+//   https://www.w3.org/TR/html50/document-metadata.html#the-title-element
 class HTMLTitleElement : public HTMLElement {
  public:
   static const char kTagName[];
diff --git a/src/cobalt/dom/html_title_element.idl b/src/cobalt/dom/html_title_element.idl
index 475e797..9e93247 100644
--- a/src/cobalt/dom/html_title_element.idl
+++ b/src/cobalt/dom/html_title_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/document-metadata.html#the-title-element
+// https://www.w3.org/TR/html50/document-metadata.html#the-title-element
 
 interface HTMLTitleElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_unknown_element.h b/src/cobalt/dom/html_unknown_element.h
index b0640ab..50dcf50 100644
--- a/src/cobalt/dom/html_unknown_element.h
+++ b/src/cobalt/dom/html_unknown_element.h
@@ -24,7 +24,7 @@
 
 // The HTMLUnknownElement interface must be used for HTML elements that are not
 // defined by any applicable specifications.
-//   https://www.w3.org/TR/html5/dom.html#htmlunknownelement
+//   https://www.w3.org/TR/html50/dom.html#htmlunknownelement
 class HTMLUnknownElement : public HTMLElement {
  public:
   HTMLUnknownElement(Document* document, base::Token tag_name)
diff --git a/src/cobalt/dom/html_unknown_element.idl b/src/cobalt/dom/html_unknown_element.idl
index ec3abec..8046307 100644
--- a/src/cobalt/dom/html_unknown_element.idl
+++ b/src/cobalt/dom/html_unknown_element.idl
@@ -12,6 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/dom.html#htmlunknownelement
+// https://www.w3.org/TR/html50/dom.html#htmlunknownelement
 
 interface HTMLUnknownElement : HTMLElement {};
diff --git a/src/cobalt/dom/html_video_element.h b/src/cobalt/dom/html_video_element.h
index 436787f..1f5f64d 100644
--- a/src/cobalt/dom/html_video_element.h
+++ b/src/cobalt/dom/html_video_element.h
@@ -27,7 +27,7 @@
 namespace dom {
 
 // The HTMLVideoElement is used to play videos.
-//   https://www.w3.org/TR/html5/embedded-content-0.html#the-video-element
+//   https://www.w3.org/TR/html50/embedded-content-0.html#the-video-element
 class HTMLVideoElement : public HTMLMediaElement {
  public:
   typedef media::VideoFrameProvider VideoFrameProvider;
diff --git a/src/cobalt/dom/html_video_element.idl b/src/cobalt/dom/html_video_element.idl
index e3b06ab..d6decd5 100644
--- a/src/cobalt/dom/html_video_element.idl
+++ b/src/cobalt/dom/html_video_element.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/embedded-content-0.html#the-video-element
+// https://www.w3.org/TR/html50/embedded-content-0.html#the-video-element
 
 interface HTMLVideoElement : HTMLMediaElement {
   attribute unsigned long width;
diff --git a/src/cobalt/dom/location.cc b/src/cobalt/dom/location.cc
index fb25708..dfcbea6 100644
--- a/src/cobalt/dom/location.cc
+++ b/src/cobalt/dom/location.cc
@@ -31,7 +31,7 @@
       security_callback_(security_callback) {}
 
 // Algorithm for Replace:
-//   https://www.w3.org/TR/html5/browsers.html#dom-location-replace
+//   https://www.w3.org/TR/html50/browsers.html#dom-location-replace
 void Location::Replace(const std::string& url) {
   // When the replace(url) method is invoked, the UA must resolve the argument,
   // relative to the API base URL specified by the entry settings object, and if
@@ -46,7 +46,7 @@
   const GURL& old_url = url_utils_.url();
 
   // The following codes correspond to navigating the browsing context in HTML5.
-  //   https://www.w3.org/TR/html5/browsers.html#navigate
+  //   https://www.w3.org/TR/html50/browsers.html#navigate
   // Since navigation in Cobalt always goes through Location interface, it is
   // implemented here.
 
diff --git a/src/cobalt/dom/location.h b/src/cobalt/dom/location.h
index cc92ea6..d73dde0 100644
--- a/src/cobalt/dom/location.h
+++ b/src/cobalt/dom/location.h
@@ -30,7 +30,7 @@
 // document of their Document's browsing context, and allow the current entry of
 // the browsing context's session history to be changed, by adding or replacing
 // entries in the history object.
-//   https://www.w3.org/TR/html5/browsers.html#the-location-interface
+//   https://www.w3.org/TR/html50/browsers.html#the-location-interface
 class Location : public script::Wrappable {
  public:
   // If any navigation is triggered, all these callbacks should be provided,
diff --git a/src/cobalt/dom/location.idl b/src/cobalt/dom/location.idl
index c7908e9..0b96c5a 100644
--- a/src/cobalt/dom/location.idl
+++ b/src/cobalt/dom/location.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/browsers.html#the-location-interface
+// https://www.w3.org/TR/html50/browsers.html#the-location-interface
 
 [Unforgeable] interface Location {
   void assign(DOMString url);
diff --git a/src/cobalt/dom/lottie_player.cc b/src/cobalt/dom/lottie_player.cc
new file mode 100644
index 0000000..21138e2
--- /dev/null
+++ b/src/cobalt/dom/lottie_player.cc
@@ -0,0 +1,163 @@
+// 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/dom/lottie_player.h"
+
+#include <memory>
+#include <string>
+
+#include "base/message_loop/message_loop.h"
+#include "base/trace_event/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/csp_delegate.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/script/global_environment.h"
+#include "url/gurl.h"
+
+namespace cobalt {
+namespace dom {
+
+const char LottiePlayer::kTagName[] = "lottie-player";
+
+void LottiePlayer::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
+  if (!cached_image_loaded_callback_handler_) {
+    return;
+  }
+
+  // While we are still loading, treat this as an error.
+  OnLoadingError();
+}
+
+void LottiePlayer::OnSetAttribute(const std::string& name,
+                                  const std::string& value) {
+  if (name == "src") {
+    UpdateAnimationData();
+  } else {
+    HTMLElement::OnSetAttribute(name, value);
+  }
+}
+
+void LottiePlayer::OnRemoveAttribute(const std::string& name) {
+  if (name == "src") {
+    UpdateAnimationData();
+  } else {
+    HTMLElement::OnRemoveAttribute(name);
+  }
+}
+
+void LottiePlayer::UpdateAnimationData() {
+  DCHECK(base::MessageLoop::current());
+  DCHECK(node_document());
+  TRACE_EVENT0("cobalt::dom", "LottiePlayer::UpdateAnimationData()");
+
+  if (cached_image_loaded_callback_handler_) {
+    cached_image_loaded_callback_handler_.reset();
+    prevent_gc_until_load_complete_.reset();
+    node_document()->DecreaseLoadingCounter();
+  }
+
+  const std::string src = GetAttribute("src").value_or("");
+
+  if (!src.empty()) {
+    const GURL& base_url = node_document()->url_as_gurl();
+    const GURL selected_source = base_url.Resolve(src);
+    if (!selected_source.is_valid()) {
+      LOG(WARNING) << src << " cannot be resolved based on " << base_url << ".";
+      return;
+    }
+
+    auto image_cache = node_document()->html_element_context()->image_cache();
+    cached_image_ = image_cache->GetOrCreateCachedResource(selected_source,
+                                                           loader::Origin());
+
+    if (cached_image_->TryGetResource()) {
+      PreventGarbageCollectionUntilEventIsDispatched(base::Tokens::load());
+      return;
+    }
+  } else {
+    PreventGarbageCollectionUntilEventIsDispatched(base::Tokens::error());
+    return;
+  }
+
+  DCHECK(!prevent_gc_until_load_complete_);
+  prevent_gc_until_load_complete_.reset(
+      new script::GlobalEnvironment::ScopedPreventGarbageCollection(
+          html_element_context()->script_runner()->GetGlobalEnvironment(),
+          this));
+  node_document()->IncreaseLoadingCounter();
+  cached_image_loaded_callback_handler_.reset(
+      new loader::image::CachedImage::OnLoadedCallbackHandler(
+          cached_image_,
+          base::Bind(&LottiePlayer::OnLoadingSuccess, base::Unretained(this)),
+          base::Bind(&LottiePlayer::OnLoadingError, base::Unretained(this))));
+}
+
+void LottiePlayer::OnLoadingSuccess() {
+  TRACE_EVENT0("cobalt::dom", "LottiePlayer::OnLoadingSuccess()");
+  AllowGarbageCollectionAfterEventIsDispatched(
+      base::Tokens::load(), std::move(prevent_gc_until_load_complete_));
+  if (node_document()) {
+    node_document()->DecreaseLoadingCounterAndMaybeDispatchLoadEvent();
+  }
+  cached_image_loaded_callback_handler_.reset();
+
+  // Set up the Lottie objects in the box and render trees once the file has
+  // successfully loaded.
+  node_document()->RecordMutation();
+  InvalidateLayoutBoxRenderTreeNodes();
+}
+
+void LottiePlayer::OnLoadingError() {
+  TRACE_EVENT0("cobalt::dom", "LottiePlayer::OnLoadingError()");
+  AllowGarbageCollectionAfterEventIsDispatched(
+      base::Tokens::error(), std::move(prevent_gc_until_load_complete_));
+  if (node_document()) {
+    node_document()->DecreaseLoadingCounterAndMaybeDispatchLoadEvent();
+  }
+  cached_image_loaded_callback_handler_.reset();
+}
+
+void LottiePlayer::PreventGarbageCollectionUntilEventIsDispatched(
+    base::Token event_name) {
+  std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
+      prevent_gc_until_event_dispatch(
+          new script::GlobalEnvironment::ScopedPreventGarbageCollection(
+              html_element_context()->script_runner()->GetGlobalEnvironment(),
+              this));
+  AllowGarbageCollectionAfterEventIsDispatched(
+      event_name, std::move(prevent_gc_until_event_dispatch));
+}
+
+void LottiePlayer::AllowGarbageCollectionAfterEventIsDispatched(
+    base::Token event_name,
+    std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
+        scoped_prevent_gc) {
+  PostToDispatchEventNameAndRunCallback(
+      FROM_HERE, event_name,
+      base::Bind(&LottiePlayer::DestroyScopedPreventGC,
+                 base::AsWeakPtr<LottiePlayer>(this),
+                 base::Passed(&scoped_prevent_gc)));
+}
+
+void LottiePlayer::DestroyScopedPreventGC(
+    std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
+        scoped_prevent_gc) {
+  scoped_prevent_gc.reset();
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/lottie_player.h b/src/cobalt/dom/lottie_player.h
new file mode 100644
index 0000000..53b104c
--- /dev/null
+++ b/src/cobalt/dom/lottie_player.h
@@ -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.
+
+#ifndef COBALT_DOM_LOTTIE_PLAYER_H_
+#define COBALT_DOM_LOTTIE_PLAYER_H_
+
+#include <string>
+
+#include "cobalt/dom/html_element.h"
+#include "cobalt/loader/image/image_cache.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/global_environment.h"
+
+namespace cobalt {
+namespace dom {
+
+class Document;
+
+// Custom element that represents a Lottie web player, which embeds and plays
+// Lottie animations.
+// Although LottiePlayer does not inherit from HTMLImageElement, much of its
+// functionality is based off that of HTMLImageElement - in particular, loading
+// the animation pointed to by the "src" attribute.
+//   https://github.com/LottieFiles/lottie-player
+class LottiePlayer : public HTMLElement {
+ public:
+  static const char kTagName[];
+
+  explicit LottiePlayer(Document* document)
+      : HTMLElement(document, base::Token(kTagName)) {}
+
+  // Web API: LottiePlayer
+  //
+  std::string src() const { return GetAttribute("src").value_or(""); }
+  void set_src(const std::string& src) { SetAttribute("src", src); }
+
+  // Custom, not in any spec
+  //
+  // From HTMLElement
+  scoped_refptr<LottiePlayer> AsLottiePlayer() override { return this; }
+
+  const scoped_refptr<loader::image::CachedImage>& cached_image() {
+    return cached_image_;
+  }
+
+  DEFINE_WRAPPABLE_TYPE(LottiePlayer);
+
+ private:
+  ~LottiePlayer() override {}
+
+  // From Node.
+  void PurgeCachedBackgroundImagesOfNodeAndDescendants() override;
+
+  // From Element.
+  void OnSetAttribute(const std::string& name,
+                      const std::string& value) override;
+  void OnRemoveAttribute(const std::string& name) override;
+
+  void UpdateAnimationData();
+
+  void OnLoadingSuccess();
+  void OnLoadingError();
+
+  void PreventGarbageCollectionUntilEventIsDispatched(base::Token event_name);
+  void AllowGarbageCollectionAfterEventIsDispatched(
+      base::Token event_name,
+      std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
+          scoped_prevent_gc);
+  void DestroyScopedPreventGC(
+      std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
+          scoped_prevent_gc);
+
+  scoped_refptr<loader::image::CachedImage> cached_image_;
+  std::unique_ptr<loader::image::CachedImage::OnLoadedCallbackHandler>
+      cached_image_loaded_callback_handler_;
+
+  std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
+      prevent_gc_until_load_complete_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_LOTTIE_PLAYER_H_
diff --git a/src/cobalt/dom/lottie_player.idl b/src/cobalt/dom/lottie_player.idl
new file mode 100644
index 0000000..215bf21
--- /dev/null
+++ b/src/cobalt/dom/lottie_player.idl
@@ -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/license/sLICENSE-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.
+
+// https://github.com/LottieFiles/lottie-player
+
+interface LottiePlayer : HTMLElement {
+  attribute DOMString src;
+};
diff --git a/src/cobalt/dom/media_error.h b/src/cobalt/dom/media_error.h
index 45b2538..898af55 100644
--- a/src/cobalt/dom/media_error.h
+++ b/src/cobalt/dom/media_error.h
@@ -23,7 +23,7 @@
 namespace dom {
 
 // The MediaError represents a media element error with an error code.
-//   https://www.w3.org/TR/html5/embedded-content-0.html#mediaerror
+//   https://www.w3.org/TR/html50/embedded-content-0.html#mediaerror
 // kMediaErrEncrypted(MEDIA_ERR_ENCRYPTED) is introduced in EME 0.1b.
 //   https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-mediakeyerror
 class MediaError : public script::Wrappable {
diff --git a/src/cobalt/dom/media_error.idl b/src/cobalt/dom/media_error.idl
index d188e25..d6a184a 100644
--- a/src/cobalt/dom/media_error.idl
+++ b/src/cobalt/dom/media_error.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/embedded-content-0.html#mediaerror
+// https://www.w3.org/TR/html50/embedded-content-0.html#mediaerror
 
 interface MediaError {
   const unsigned short MEDIA_ERR_ABORTED = 1;
diff --git a/src/cobalt/dom/mime_type_array.idl b/src/cobalt/dom/mime_type_array.idl
index db93cd0..65e3403 100644
--- a/src/cobalt/dom/mime_type_array.idl
+++ b/src/cobalt/dom/mime_type_array.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#mimetypearray
+// https://www.w3.org/TR/html50/webappapis.html#mimetypearray
 
 // A MimeTypeArray object represents the MIME types explicitly supported by
 // plugins supported by the user agent, each of which is represented by a
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 3a4e119..10a417d 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -34,7 +34,7 @@
 // The Navigator object represents the identity and state of the user agent (the
 // client), and allows Web pages to register themselves as potential protocol
 // and content handlers.
-// https://www.w3.org/TR/html5/webappapis.html#navigator
+// https://www.w3.org/TR/html50/webappapis.html#navigator
 class Navigator : public script::Wrappable {
  public:
   Navigator(
diff --git a/src/cobalt/dom/navigator.idl b/src/cobalt/dom/navigator.idl
index ea4dfc6..abaee23 100644
--- a/src/cobalt/dom/navigator.idl
+++ b/src/cobalt/dom/navigator.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#the-navigator-object
+// https://www.w3.org/TR/html50/webappapis.html#the-navigator-object
 
 interface Navigator {};
 
diff --git a/src/cobalt/dom/navigator_id.idl b/src/cobalt/dom/navigator_id.idl
index 76b52ab..794beab 100644
--- a/src/cobalt/dom/navigator_id.idl
+++ b/src/cobalt/dom/navigator_id.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#navigatorid
+// https://www.w3.org/TR/html50/webappapis.html#navigatorid
 
 [NoInterfaceObject]
 interface NavigatorID {
diff --git a/src/cobalt/dom/navigator_language.idl b/src/cobalt/dom/navigator_language.idl
index 8a82e30..27c0ece 100644
--- a/src/cobalt/dom/navigator_language.idl
+++ b/src/cobalt/dom/navigator_language.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#navigatorlanguage
+// https://www.w3.org/TR/html50/webappapis.html#navigatorlanguage
 
 [NoInterfaceObject]
 interface NavigatorLanguage {
diff --git a/src/cobalt/dom/navigator_plugins.idl b/src/cobalt/dom/navigator_plugins.idl
index 3a73849..05283c9 100644
--- a/src/cobalt/dom/navigator_plugins.idl
+++ b/src/cobalt/dom/navigator_plugins.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#navigatorplugins
+// https://www.w3.org/TR/html50/webappapis.html#navigatorplugins
 
 [NoInterfaceObject]
 interface NavigatorPlugins {
diff --git a/src/cobalt/dom/navigator_storage_utils.idl b/src/cobalt/dom/navigator_storage_utils.idl
index 1bc430a..78693bd 100644
--- a/src/cobalt/dom/navigator_storage_utils.idl
+++ b/src/cobalt/dom/navigator_storage_utils.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#navigatorstorageutils
+// https://www.w3.org/TR/html50/webappapis.html#navigatorstorageutils
 
 [NoInterfaceObject]
 interface NavigatorStorageUtils {
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index c94c34c..0982fae 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -179,12 +179,12 @@
 }
 
 bool Node::Contains(const scoped_refptr<Node>& other_node) const {
-  const Node* child = first_child_.get();
-  while (child) {
-    if (child == other_node || child->Contains(other_node)) {
+  const Node* candidate = other_node.get();
+  while (candidate) {
+    if (this == candidate) {
       return true;
     }
-    child = child->next_sibling_.get();
+    candidate = candidate->parent_node();
   }
   return false;
 }
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index e1d7614..a739aac 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -240,6 +240,7 @@
   }
 
   DEFINE_WRAPPABLE_TYPE(Node);
+  JSObjectType GetJSObjectType() override { return JSObjectType::kNode; }
   void TraceMembers(script::Tracer* tracer) override;
 
  protected:
diff --git a/src/cobalt/dom/node_list.h b/src/cobalt/dom/node_list.h
index b6ffa58..dcd743e 100644
--- a/src/cobalt/dom/node_list.h
+++ b/src/cobalt/dom/node_list.h
@@ -44,6 +44,7 @@
   void AppendNode(const scoped_refptr<Node>& node);
 
   DEFINE_WRAPPABLE_TYPE(NodeList);
+  JSObjectType GetJSObjectType() override { return JSObjectType::kArray; }
   void TraceMembers(script::Tracer* tracer) override;
 
  protected:
diff --git a/src/cobalt/dom/node_test.cc b/src/cobalt/dom/node_test.cc
index 29f01cd..22a0780 100644
--- a/src/cobalt/dom/node_test.cc
+++ b/src/cobalt/dom/node_test.cc
@@ -158,8 +158,8 @@
 
   EXPECT_TRUE(root->Contains(child1));
   EXPECT_TRUE(root->Contains(child2));
+  EXPECT_TRUE(root->Contains(root));
 
-  EXPECT_FALSE(root->Contains(root));
   EXPECT_FALSE(root->Contains(node));
 }
 
diff --git a/src/cobalt/dom/plugin_array.idl b/src/cobalt/dom/plugin_array.idl
index 19bc996..43f418f 100644
--- a/src/cobalt/dom/plugin_array.idl
+++ b/src/cobalt/dom/plugin_array.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#pluginarray
+// https://www.w3.org/TR/html50/webappapis.html#pluginarray
 
 interface PluginArray {
   readonly attribute long length;
diff --git a/src/cobalt/dom/time_ranges.h b/src/cobalt/dom/time_ranges.h
index 2c1beb4..bc59d71 100644
--- a/src/cobalt/dom/time_ranges.h
+++ b/src/cobalt/dom/time_ranges.h
@@ -25,7 +25,7 @@
 
 // The TimeRanges interface is used to describe a series of time ranges. Each of
 // them has a start time and an end time.
-//   https://www.w3.org/TR/html5/embedded-content-0.html#timeranges
+//   https://www.w3.org/TR/html50/embedded-content-0.html#timeranges
 //
 // Note that in our implementation it is always normalized, i.e. all contained
 // ranges are sorted ascendantly and not overlapped.  The limits of the range
diff --git a/src/cobalt/dom/time_ranges.idl b/src/cobalt/dom/time_ranges.idl
index 38c2297..da7e069 100644
--- a/src/cobalt/dom/time_ranges.idl
+++ b/src/cobalt/dom/time_ranges.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/embedded-content-0.html#timeranges
+// https://www.w3.org/TR/html50/embedded-content-0.html#timeranges
 
 interface TimeRanges {
   readonly attribute unsigned long length;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 2dc70c4..98afc42 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -264,7 +264,7 @@
 
 const scoped_refptr<History>& Window::history() const { return history_; }
 
-// https://www.w3.org/TR/html5/browsers.html#dom-window-close
+// https://www.w3.org/TR/html50/browsers.html#dom-window-close
 void Window::Close() {
   LOG(INFO) << __func__;
   if (!window_close_callback_.is_null()) {
@@ -379,9 +379,10 @@
 std::string Window::Btoa(const std::string& string_to_encode,
                          script::ExceptionState* exception_state) {
   TRACE_EVENT0("cobalt::dom", "Window::Btoa()");
-  LOG(WARNING) << "In older Cobalt(<19), btoa() can not take a string"
-                  " containing NUL. Be careful that you don't need to stay "
-                  "compatible with old versions of Cobalt if you use btoa.";
+  LOG_ONCE(WARNING)
+      << "In older Cobalt(<19), btoa() can not take a string"
+         " containing NULL. Be careful that you don't need to stay "
+         "compatible with old versions of Cobalt if you use btoa.";
   auto output = ForgivingBase64Encode(string_to_encode);
   if (!output) {
     DOMException::Raise(DOMException::kInvalidCharacterErr, exception_state);
@@ -554,7 +555,7 @@
   // Runtime script errors: when the user agent is required to report an error
   // for a particular script, it must run these steps, after which the error is
   // either handled or not handled:
-  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
 
   // 1. If target is in error reporting mode, then abort these steps; the error
   //    is not handled.
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 40d0fbf..cacd9b5 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -103,7 +103,7 @@
 class WindowTimers;
 
 // The window object represents a window containing a DOM document.
-//   https://www.w3.org/TR/html5/browsers.html#the-window-object
+//   https://www.w3.org/TR/html50/browsers.html#the-window-object
 //
 // TODO: Properly handle viewport resolution change event.
 class Window : public EventTarget,
@@ -280,7 +280,7 @@
                             script::ExceptionState* exception_state);
 
   // Web API: WindowTimers (implements)
-  //   https://www.w3.org/TR/html5/webappapis.html#timers
+  //   https://www.w3.org/TR/html50/webappapis.html#timers
   //
   int SetTimeout(const WindowTimers::TimerCallbackArg& handler) {
     return SetTimeout(handler, 0);
@@ -363,7 +363,7 @@
   void SetApplicationState(base::ApplicationState state);
 
   // Performs the steps specified for runtime script errors:
-  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
   // Returns whether or not the script was handled.
   bool ReportScriptError(const script::ErrorReport& error_report);
 
diff --git a/src/cobalt/dom/window.idl b/src/cobalt/dom/window.idl
index c08ffde..7f0c8f4 100644
--- a/src/cobalt/dom/window.idl
+++ b/src/cobalt/dom/window.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/browsers.html#the-window-object
+// https://www.w3.org/TR/html50/browsers.html#the-window-object
 
 // Note: Cobalt only supports one browsing context and one Window object.
 [PrimaryGlobal]
diff --git a/src/cobalt/dom/window_event_handlers.idl b/src/cobalt/dom/window_event_handlers.idl
index 808b981..186e1ad 100644
--- a/src/cobalt/dom/window_event_handlers.idl
+++ b/src/cobalt/dom/window_event_handlers.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#windoweventhandlers
+// https://www.w3.org/TR/html50/webappapis.html#windoweventhandlers
 
 [NoInterfaceObject]
 interface WindowEventHandlers {
diff --git a/src/cobalt/dom/window_timers.idl b/src/cobalt/dom/window_timers.idl
index 98a1c38..9e7b435 100644
--- a/src/cobalt/dom/window_timers.idl
+++ b/src/cobalt/dom/window_timers.idl
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/html5/webappapis.html#windowtimers
+// https://www.w3.org/TR/html50/webappapis.html#windowtimers
 
 [NoInterfaceObject]
 interface WindowTimers {
   // TODO: Change the following methods back to their original form in the spec,
   // when "Function" and "any" are supported.
-  //   https://www.w3.org/TR/html5/webappapis.html#timers
+  //   https://www.w3.org/TR/html50/webappapis.html#timers
   long setTimeout(TimerCallback handler, optional long timeout);
   void clearTimeout(long handle);
   long setInterval(TimerCallback handler, optional long timeout);
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index 646a077..79736fd 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -428,7 +428,7 @@
 }
 
 // Misnested tags: <b><i></b></i>
-//   https://www.w3.org/TR/html5/syntax.html#misnested-tags:-b-i-/b-/i
+//   https://www.w3.org/TR/html50/syntax.html#misnested-tags:-b-i-/b-/i
 //
 // The current version DOES NOT handle the error as outlined in the link above.
 TEST_F(HTMLDecoderTest, CanParseMisnestedTags1) {
@@ -455,7 +455,7 @@
 }
 
 // Misnested tags: <b><p></b></p>
-//   https://www.w3.org/TR/html5/syntax.html#misnested-tags:-b-p-/b-/p
+//   https://www.w3.org/TR/html50/syntax.html#misnested-tags:-b-p-/b-/p
 //
 // The current version DOES NOT handle the error as outlined in the link above.
 TEST_F(HTMLDecoderTest, CanParseMisnestedTags2) {
diff --git a/src/cobalt/extension/configuration.h b/src/cobalt/extension/configuration.h
new file mode 100644
index 0000000..9d379c7
--- /dev/null
+++ b/src/cobalt/extension/configuration.h
@@ -0,0 +1,228 @@
+// 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_EXTENSION_CONFIGURATION_H_
+#define COBALT_EXTENSION_CONFIGURATION_H_
+
+#include <stdint.h>
+
+#include "starboard/configuration.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define kCobaltExtensionConfigurationName "dev.cobalt.extension.Configuration"
+
+typedef struct CobaltExtensionConfigurationApi {
+  // Name should be the string |kCobaltExtensionConfigurationName|.
+  // This helps to validate that the extension API is correct.
+  const char* name;
+
+  // This specifies the version of the API that is implemented.
+  uint32_t version;
+
+  // The fields below this point were added in version 1 or later.
+
+  // The functions below configure Cobalt. All correspond to some GYP variable,
+  // but the implementation of this functions will take precedence over the GYP
+  // variable.
+
+  // This variable defines what Cobalt's preferred strategy should be for
+  // handling internally triggered application exit requests (e.g. the user
+  // chooses to back out of the application).
+  //   'stop'    -- The application should call SbSystemRequestStop() on exit,
+  //                resulting in a complete shutdown of the application.
+  //   'suspend' -- The application should call SbSystemRequestSuspend() on
+  //                exit, resulting in the application being "minimized".
+  //   'noexit'  -- The application should never allow the user to trigger an
+  //                exit, this will be managed by the system.
+  const char* (*CobaltUserOnExitStrategy)();
+
+  // If set to |true|, will enable support for rendering only the regions of
+  // the display that are modified due to animations, instead of re-rendering
+  // the entire scene each frame.  This feature can reduce startup time where
+  // usually there is a small loading spinner animating on the screen.  On GLES
+  // renderers, Cobalt will attempt to implement this support by using
+  // eglSurfaceAttrib(..., EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED), otherwise
+  // the dirty region will be silently disabled.  On Blitter API platforms,
+  // if this is enabled, we explicitly create an extra offscreen full-size
+  // intermediate surface to render into.  Note that some GLES driver
+  // implementations may internally allocate an extra full screen surface to
+  // support this feature, and many have been noticed to not properly support
+  // this functionality (but they report that they do), and for these reasons
+  // this value is defaulted to |false|.
+  bool (*CobaltRenderDirtyRegionOnly)();
+
+  // Cobalt will call eglSwapInterval() and specify this value before calling
+  // eglSwapBuffers() each frame.
+  int (*CobaltEglSwapInterval)();
+
+  // The URL of default build time splash screen - see
+  // cobalt/doc/splash_screen.md for information about this.
+  const char* (*CobaltFallbackSplashScreenUrl)();
+
+  // If set to |true|, enables Quic.
+  bool (*CobaltEnableQuic)();
+
+  // Cache parameters
+
+  // The following set of parameters define how much memory is reserved for
+  // different Cobalt caches.  These caches affect CPU *and* GPU memory usage.
+  //
+  // The sum of the following caches effectively describes the maximum GPU
+  // texture memory usage (though it doesn't consider video textures and
+  // display color buffers):
+  //   - CobaltSkiaCacheSizeInBytes (GLES2 rasterizer only)
+  //   - CobaltImageCacheSizeInBytes
+  //   - CobaltSkiaGlyphAtlasWidth * CobaltSkiaGlyphAtlasHeight
+  //
+  // The other caches affect CPU memory usage.
+
+  // Determines the capacity of the skia cache.  The Skia cache is maintained
+  // within Skia and is used to cache the results of complicated effects such
+  // as shadows, so that Skia draw calls that are used repeatedly across
+  // frames can be cached into surfaces.  This setting is only relevant when
+  // using the hardware-accelerated Skia rasterizer (e.g. as opposed to the
+  // Blitter API).
+  int (*CobaltSkiaCacheSizeInBytes)();
+
+  // Determines the amount of GPU memory the offscreen target atlases will
+  // use. This is specific to the direct-GLES rasterizer and caches any render
+  // tree nodes which require skia for rendering. Two atlases will be allocated
+  // from this memory or multiple atlases of the frame size if the limit
+  // allows. It is recommended that enough memory be reserved for two RGBA
+  // atlases about a quarter of the frame size.
+  int (*CobaltOffscreenTargetCacheSizeInBytes)();
+
+  // Determines the capacity of the encoded image cache, which manages encoded
+  // images downloaded from a web page. These images are cached within CPU
+  // memory.  This not only reduces network traffic to download the encoded
+  // images, but also allows the downloaded images to be held during suspend.
+  // Note that there is also a cache for the decoded images whose capacity is
+  // specified in |CobaltImageCacheSizeInBytes|.  The decoded images are often
+  // cached in the GPU memory and will be released during suspend.
+  //
+  // If a system meets the following requirements:
+  // 1. Has a fast image decoder.
+  // 2. Has enough CPU memory, or has a unified memory architecture that allows
+  //    sharing of CPU and GPU memory.
+  // Then it may consider implementing |CobaltEncodedImageCacheSizeInBytes| to
+  // return a much bigger value, and set the return value of
+  // |CobaltImageCacheSizeInBytes| to a much smaller value. This allows the app
+  // to cache significantly more images.
+  //
+  // Setting this to 0 can disable the cache completely.
+  int (*CobaltEncodedImageCacheSizeInBytes)();
+
+  // Determines the capacity of the image cache, which manages image surfaces
+  // downloaded from a web page.  While it depends on the platform, often (and
+  // ideally) these images are cached within GPU memory.
+  // Set to -1 to automatically calculate the value at runtime, based on
+  // features like windows dimensions and the value of
+  // SbSystemGetTotalGPUMemory().
+  int (*CobaltImageCacheSizeInBytes)();
+
+  // Determines the capacity of the local font cache, which manages all fonts
+  // loaded from local files. Newly encountered sections of font files are
+  // lazily loaded into the cache, enabling subsequent requests to the same
+  // file sections to be handled via direct memory access. Once the limit is
+  // reached, further requests are handled via file stream.
+  // Setting the value to 0 disables memory caching and causes all font file
+  // accesses to be done using file streams.
+  int (*CobaltLocalTypefaceCacheSizeInBytes)();
+
+  // Determines the capacity of the remote font cache, which manages all
+  // fonts downloaded from a web page.
+  int (*CobaltRemoteTypefaceCacheSizeInBytes)();
+
+  // Determines the capacity of the mesh cache. Each mesh is held compressed
+  // in main memory, to be inflated into a GPU buffer when needed for
+  // projection.
+  int (*CobaltMeshCacheSizeInBytes)();
+
+  // Only relevant if you are using the Blitter API.
+  // Determines the capacity of the software surface cache, which is used to
+  // cache all surfaces that are rendered via a software rasterizer to avoid
+  // re-rendering them.
+  int (*CobaltSoftwareSurfaceCacheSizeInBytes)();
+
+  // Modifying this function's return value to be non-1.0f will result in the
+  // image cache capacity being cleared and then temporarily reduced for the
+  // duration that a video is playing.  This can be useful for some platforms
+  // if they are particularly constrained for (GPU) memory during video
+  // playback.  When playing a video, the image cache is reduced to:
+  // CobaltImageCacheSizeInBytes() *
+  //     CobaltImageCacheCapacityMultiplierWhenPlayingVideo().
+  float (*CobaltImageCacheCapacityMultiplierWhenPlayingVideo)();
+
+  // Determines the size in pixels of the glyph atlas where rendered glyphs are
+  // cached. The resulting memory usage is 2 bytes of GPU memory per pixel.
+  // When a value is used that is too small, thrashing may occur that will
+  // result in visible stutter. Such thrashing is more likely to occur when CJK
+  // language glyphs are rendered and when the size of the glyphs in pixels is
+  // larger, such as for higher resolution displays.
+  // The negative default values indicates to the engine that these settings
+  // should be automatically set.
+  int (*CobaltSkiaGlyphAtlasWidth)();
+  int (*CobaltSkiaGlyphAtlasHeight)();
+
+  // Determines the size of garbage collection threshold. After this many
+  // bytes have been allocated, the SpiderMonkey garbage collector will run.
+  // Lowering this has been found to reduce performance and decrease
+  // JavaScript memory usage. For example, we have measured on at least one
+  // platform that performance becomes 7% worse on average in certain cases
+  // when adjusting this number from 8MB to 1MB.
+  int (*CobaltJsGarbageCollectionThresholdInBytes)();
+
+  // When specified this value will reduce the cpu memory consumption by
+  // the specified amount. -1 disables the value.
+  int (*CobaltReduceCpuMemoryBy)();
+
+  // When specified this value will reduce the gpu memory consumption by
+  // the specified amount. -1 disables the value.
+  int (*CobaltReduceGpuMemoryBy)();
+
+  // Can be set to enable zealous garbage collection, if |javascript_engine|
+  // supports it.  Zealous garbage collection will cause garbage collection
+  // to occur much more frequently than normal, for the purpose of finding or
+  // reproducing bugs.
+  bool (*CobaltGcZeal)();
+
+  // Defines what kind of rasterizer will be used.  This can be adjusted to
+  // force a stub graphics implementation.
+  // It can be one of the following options:
+  //   'direct-gles' -- Uses a light wrapper over OpenGL ES to handle most
+  //                    draw elements. This will fall back to the skia hardware
+  //                    rasterizer for some render tree node types, but is
+  //                    generally faster on the CPU and GPU. This can handle
+  //                    360 rendering.
+  //   'hardware'    -- As much hardware acceleration of graphics commands as
+  //                    possible. This uses skia to wrap OpenGL ES commands.
+  //                    Required for 360 rendering.
+  //   'stub'        -- Stub graphics rasterization.  A rasterizer object will
+  //                    still be available and valid, but it will do nothing.
+  const char* (*CobaltRasterizerType)();
+
+  // Controls whether or not just in time code should be used.
+  // See "cobalt/doc/performance_tuning.md" for more information on when this
+  // should be used.
+  bool (*CobaltEnableJit)();
+} CobaltExtensionConfigurationApi;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_EXTENSION_CONFIGURATION_H_
diff --git a/src/cobalt/extension/extension.gyp b/src/cobalt/extension/extension.gyp
index bf63c0d..f8905dd 100644
--- a/src/cobalt/extension/extension.gyp
+++ b/src/cobalt/extension/extension.gyp
@@ -24,12 +24,18 @@
         'extension_test.cc',
       ],
       'dependencies': [
-        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/starboard/starboard.gyp:starboard',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
       ],
+      'conditions': [
+        ['sb_evergreen == 0', {
+          'dependencies': [
+            '<@(cobalt_platform_dependencies)',
+          ],
+        }],
+      ],
       'includes': [ '<(DEPTH)/cobalt/test/test.gypi' ],
     },
     {
diff --git a/src/cobalt/extension/extension_test.cc b/src/cobalt/extension/extension_test.cc
index b49922a..bad7015 100644
--- a/src/cobalt/extension/extension_test.cc
+++ b/src/cobalt/extension/extension_test.cc
@@ -14,6 +14,7 @@
 
 #include <cmath>
 
+#include "cobalt/extension/configuration.h"
 #include "cobalt/extension/graphics.h"
 #include "cobalt/extension/installation_manager.h"
 #include "cobalt/extension/platform_service.h"
@@ -111,6 +112,45 @@
   EXPECT_EQ(second_extension_api, extension_api)
       << "Extension struct should be a singleton";
 }
+
+TEST(ExtensionTest, Configuration) {
+  typedef CobaltExtensionConfigurationApi ExtensionApi;
+  const char* kExtensionName = kCobaltExtensionConfigurationName;
+
+  const ExtensionApi* extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  if (!extension_api) {
+    return;
+  }
+
+  EXPECT_STREQ(extension_api->name, kExtensionName);
+  EXPECT_TRUE(extension_api->version == 1);
+  EXPECT_TRUE(extension_api->CobaltUserOnExitStrategy != NULL);
+  EXPECT_TRUE(extension_api->CobaltRenderDirtyRegionOnly != NULL);
+  EXPECT_TRUE(extension_api->CobaltEglSwapInterval != NULL);
+  EXPECT_TRUE(extension_api->CobaltFallbackSplashScreenUrl != NULL);
+  EXPECT_TRUE(extension_api->CobaltEnableQuic != NULL);
+  EXPECT_TRUE(extension_api->CobaltSkiaCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltOffscreenTargetCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltEncodedImageCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltImageCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltLocalTypefaceCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltRemoteTypefaceCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltMeshCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltSoftwareSurfaceCacheSizeInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltImageCacheCapacityMultiplierWhenPlayingVideo != NULL);
+  EXPECT_TRUE(extension_api->CobaltSkiaGlyphAtlasWidth != NULL);
+  EXPECT_TRUE(extension_api->CobaltSkiaGlyphAtlasHeight != NULL);
+  EXPECT_TRUE(extension_api->CobaltJsGarbageCollectionThresholdInBytes != NULL);
+  EXPECT_TRUE(extension_api->CobaltReduceCpuMemoryBy != NULL);
+  EXPECT_TRUE(extension_api->CobaltReduceGpuMemoryBy != NULL);
+  EXPECT_TRUE(extension_api->CobaltGcZeal != NULL);
+
+  const ExtensionApi* second_extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  EXPECT_EQ(second_extension_api, extension_api)
+      << "Extension struct should be a singleton";
+}
 }  // namespace extension
 }  // namespace cobalt
 #endif  // SB_API_VERSION >= 11
diff --git a/src/cobalt/h5vcc/h5vcc.cc b/src/cobalt/h5vcc/h5vcc.cc
index 4fe6a24..9029775 100644
--- a/src/cobalt/h5vcc/h5vcc.cc
+++ b/src/cobalt/h5vcc/h5vcc.cc
@@ -27,15 +27,17 @@
   crash_log_ = new H5vccCrashLog();
   runtime_ =
       new H5vccRuntime(settings.event_dispatcher, settings.initial_deep_link);
-  settings_ = new H5vccSettings(settings.media_module);
+  settings_ = new H5vccSettings(settings.media_module, settings.network_module);
 #if defined(COBALT_ENABLE_SSO)
   sso_ = new H5vccSso();
 #endif
   storage_ = new H5vccStorage(settings.network_module);
-  system_ = new H5vccSystem();
   trace_event_ = new H5vccTraceEvent();
 #if SB_IS(EVERGREEN)
   updater_ = new H5vccUpdater(settings.updater_module);
+  system_ = new H5vccSystem(updater_);
+#else
+  system_ = new H5vccSystem();
 #endif
 }
 
diff --git a/src/cobalt/h5vcc/h5vcc.gyp b/src/cobalt/h5vcc/h5vcc.gyp
index 3e7b35d..d8b7f9e 100644
--- a/src/cobalt/h5vcc/h5vcc.gyp
+++ b/src/cobalt/h5vcc/h5vcc.gyp
@@ -57,6 +57,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/build/cobalt_build_id.gyp:cobalt_build_id',
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/net/net.gyp:net',
diff --git a/src/cobalt/h5vcc/h5vcc_settings.cc b/src/cobalt/h5vcc/h5vcc_settings.cc
index f836031..a8e8280 100644
--- a/src/cobalt/h5vcc/h5vcc_settings.cc
+++ b/src/cobalt/h5vcc/h5vcc_settings.cc
@@ -19,15 +19,27 @@
 namespace cobalt {
 namespace h5vcc {
 
-H5vccSettings::H5vccSettings(media::MediaModule* media_module)
-    : media_module_(media_module) {}
+H5vccSettings::H5vccSettings(media::MediaModule* media_module,
+                             cobalt::network::NetworkModule* network_module)
+    : media_module_(media_module), network_module_(network_module) {}
 
 bool H5vccSettings::Set(const std::string& name, int32 value) const {
   const char kMediaPrefix[] = "Media.";
+  const char kQUIC[] = "QUIC";
 
-  if (strncmp(name.c_str(), kMediaPrefix, sizeof(kMediaPrefix) - 1) == 0) {
+  if (SbStringCompare(name.c_str(), kMediaPrefix, sizeof(kMediaPrefix) - 1) ==
+      0) {
     return media_module_ ? media_module_->SetConfiguration(name, value) : false;
   }
+
+  if (SbStringCompare(name.c_str(), kQUIC, sizeof(kQUIC) - 1) == 0) {
+    if (value != 0 || !network_module_) {
+      return false;
+    } else {
+      network_module_->DisableQuic();
+      return true;
+    }
+  }
   return false;
 }
 
diff --git a/src/cobalt/h5vcc/h5vcc_settings.h b/src/cobalt/h5vcc/h5vcc_settings.h
index eca59e7..f0b2502 100644
--- a/src/cobalt/h5vcc/h5vcc_settings.h
+++ b/src/cobalt/h5vcc/h5vcc_settings.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "cobalt/media/media_module.h"
+#include "cobalt/network/network_module.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -28,7 +29,8 @@
 // version to avoid being abused.
 class H5vccSettings : public script::Wrappable {
  public:
-  explicit H5vccSettings(media::MediaModule* media_module);
+  explicit H5vccSettings(media::MediaModule* media_module,
+                         cobalt::network::NetworkModule* network_module);
 
   // Returns true when the setting is set successfully or if the setting has
   // already been set to the expected value.  Returns false when the setting is
@@ -39,6 +41,7 @@
 
  private:
   media::MediaModule* media_module_;
+  cobalt::network::NetworkModule* network_module_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(H5vccSettings);
 };
diff --git a/src/cobalt/h5vcc/h5vcc_system.cc b/src/cobalt/h5vcc/h5vcc_system.cc
index 7df5f43..511bb78 100644
--- a/src/cobalt/h5vcc/h5vcc_system.cc
+++ b/src/cobalt/h5vcc/h5vcc_system.cc
@@ -15,6 +15,7 @@
 #include "cobalt/h5vcc/h5vcc_system.h"
 
 #include "base/strings/stringprintf.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/version.h"
 #include "cobalt_build_id.h"  // NOLINT(build/include)
 #include "starboard/system.h"
@@ -22,7 +23,11 @@
 namespace cobalt {
 namespace h5vcc {
 
+#if SB_IS(EVERGREEN)
+H5vccSystem::H5vccSystem(H5vccUpdater* updater) : updater_(updater) {}
+#else
 H5vccSystem::H5vccSystem() {}
+#endif
 
 bool H5vccSystem::are_keys_reversed() const {
   return SbSystemHasCapability(kSbSystemCapabilityReversedEnterAndBack);
@@ -63,10 +68,18 @@
 uint32 H5vccSystem::user_on_exit_strategy() const {
   // Convert from the Cobalt gyp setting variable's enum options to the H5VCC
   // interface enum options.
-  std::string exit_strategy_str(COBALT_USER_ON_EXIT_STRATEGY);
+  std::string exit_strategy_str(
+      configuration::Configuration::GetInstance()->CobaltUserOnExitStrategy());
   if (exit_strategy_str == "stop") {
     return static_cast<UserOnExitStrategy>(kUserOnExitStrategyClose);
   } else if (exit_strategy_str == "suspend") {
+#if SB_IS(EVERGREEN)
+    // Note: The status string used here must be synced with the
+    // ComponentState::kUpdated status string defined in updater_module.cc.
+    if (updater_->GetUpdateStatus() == "Update installed, pending restart") {
+      return static_cast<UserOnExitStrategy>(kUserOnExitStrategyClose);
+    }
+#endif
     return static_cast<UserOnExitStrategy>(kUserOnExitStrategyMinimize);
   } else if (exit_strategy_str == "noexit") {
     return static_cast<UserOnExitStrategy>(kUserOnExitStrategyNoExit);
diff --git a/src/cobalt/h5vcc/h5vcc_system.h b/src/cobalt/h5vcc/h5vcc_system.h
index 204ddcc..7270e61 100644
--- a/src/cobalt/h5vcc/h5vcc_system.h
+++ b/src/cobalt/h5vcc/h5vcc_system.h
@@ -17,6 +17,10 @@
 
 #include <string>
 
+#include "starboard/configuration.h"
+#if SB_IS(EVERGREEN)
+#include "cobalt/h5vcc/h5vcc_updater.h"
+#endif
 #include "cobalt/media/media_module.h"
 #include "cobalt/script/wrappable.h"
 
@@ -25,7 +29,11 @@
 
 class H5vccSystem : public script::Wrappable {
  public:
+#if SB_IS(EVERGREEN)
+  explicit H5vccSystem(H5vccUpdater* updater);
+#else
   H5vccSystem();
+#endif
 
   bool are_keys_reversed() const;
   std::string build_id() const;
@@ -47,6 +55,9 @@
 
  private:
   std::string video_container_size_;
+#if SB_IS(EVERGREEN)
+  scoped_refptr<H5vccUpdater> updater_;
+#endif
   DISALLOW_COPY_AND_ASSIGN(H5vccSystem);
 };
 
diff --git a/src/cobalt/layout/block_level_replaced_box.cc b/src/cobalt/layout/block_level_replaced_box.cc
index 59f4442..16f9c7b 100644
--- a/src/cobalt/layout/block_level_replaced_box.cc
+++ b/src/cobalt/layout/block_level_replaced_box.cc
@@ -29,13 +29,13 @@
     const base::Optional<LayoutUnit>& maybe_intrinsic_height,
     const base::Optional<float>& maybe_intrinsic_ratio,
     UsedStyleProvider* used_style_provider,
-    base::Optional<bool> is_video_punched_out, const math::SizeF& content_size,
-    LayoutStatTracker* layout_stat_tracker)
+    base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode,
+    const math::SizeF& content_size, LayoutStatTracker* layout_stat_tracker)
     : ReplacedBox(css_computed_style_declaration, replace_image_cb,
                   set_bounds_cb, paragraph, text_position,
                   maybe_intrinsic_width, maybe_intrinsic_height,
-                  maybe_intrinsic_ratio, used_style_provider,
-                  is_video_punched_out, content_size, layout_stat_tracker) {}
+                  maybe_intrinsic_ratio, used_style_provider, replaced_box_mode,
+                  content_size, layout_stat_tracker) {}
 
 Box::Level BlockLevelReplacedBox::GetLevel() const { return kBlockLevel; }
 
diff --git a/src/cobalt/layout/block_level_replaced_box.h b/src/cobalt/layout/block_level_replaced_box.h
index 144c3af..8793d32 100644
--- a/src/cobalt/layout/block_level_replaced_box.h
+++ b/src/cobalt/layout/block_level_replaced_box.h
@@ -39,7 +39,7 @@
       const base::Optional<LayoutUnit>& maybe_intrinsic_height,
       const base::Optional<float>& maybe_intrinsic_ratio,
       UsedStyleProvider* used_style_provider,
-      base::Optional<bool> is_video_punched_out,
+      base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode,
       const math::SizeF& content_size, LayoutStatTracker* layout_stat_tracker);
 
   // From |Box|.
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index d632aa8..785aa23 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -29,6 +29,7 @@
 #include "cobalt/dom/html_br_element.h"
 #include "cobalt/dom/html_element.h"
 #include "cobalt/dom/html_video_element.h"
+#include "cobalt/dom/lottie_player.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/layout/base_direction.h"
 #include "cobalt/layout/block_formatting_block_container_box.h"
@@ -41,6 +42,7 @@
 #include "cobalt/layout/text_box.h"
 #include "cobalt/layout/used_style.h"
 #include "cobalt/layout/white_space_processing.h"
+#include "cobalt/loader/image/lottie_animation.h"
 #include "cobalt/media/base/video_frame_provider.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/web_animations/keyframe_effect_read_only.h"
@@ -71,6 +73,14 @@
   }
 }
 
+scoped_refptr<render_tree::Image> GetLottieAnimation(
+    scoped_refptr<loader::image::Image> lottie_animation) {
+  TRACE_EVENT0("cobalt::layout", "GetLottieAnimation()");
+  return base::polymorphic_downcast<loader::image::LottieAnimation*>(
+             lottie_animation.get())
+      ->animation();
+}
+
 }  // namespace
 
 BoxGenerator::BoxGenerator(
@@ -150,6 +160,13 @@
     return;
   }
 
+  scoped_refptr<dom::LottiePlayer> lottie_player =
+      html_element->AsLottiePlayer();
+  if (lottie_player) {
+    VisitLottiePlayer(lottie_player);
+    return;
+  }
+
   VisitNonReplacedElement(html_element);
 }
 
@@ -157,18 +174,18 @@
 
 class ReplacedBoxGenerator : public cssom::NotReachedPropertyValueVisitor {
  public:
-  ReplacedBoxGenerator(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
-                           css_computed_style_declaration,
-                       const ReplacedBox::ReplaceImageCB& replace_image_cb,
-                       const ReplacedBox::SetBoundsCB& set_bounds_cb,
-                       const scoped_refptr<Paragraph>& paragraph,
-                       int32 text_position,
-                       const base::Optional<LayoutUnit>& maybe_intrinsic_width,
-                       const base::Optional<LayoutUnit>& maybe_intrinsic_height,
-                       const base::Optional<float>& maybe_intrinsic_ratio,
-                       const BoxGenerator::Context* context,
-                       base::Optional<bool> is_video_punched_out,
-                       math::SizeF content_size)
+  ReplacedBoxGenerator(
+      const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
+          css_computed_style_declaration,
+      const ReplacedBox::ReplaceImageCB& replace_image_cb,
+      const ReplacedBox::SetBoundsCB& set_bounds_cb,
+      const scoped_refptr<Paragraph>& paragraph, int32 text_position,
+      const base::Optional<LayoutUnit>& maybe_intrinsic_width,
+      const base::Optional<LayoutUnit>& maybe_intrinsic_height,
+      const base::Optional<float>& maybe_intrinsic_ratio,
+      const BoxGenerator::Context* context,
+      base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode,
+      math::SizeF content_size)
       : css_computed_style_declaration_(css_computed_style_declaration),
         replace_image_cb_(replace_image_cb),
         set_bounds_cb_(set_bounds_cb),
@@ -178,7 +195,7 @@
         maybe_intrinsic_height_(maybe_intrinsic_height),
         maybe_intrinsic_ratio_(maybe_intrinsic_ratio),
         context_(context),
-        is_video_punched_out_(is_video_punched_out),
+        replaced_box_mode_(replaced_box_mode),
         content_size_(content_size) {}
 
   void VisitKeyword(cssom::KeywordValue* keyword) override;
@@ -196,7 +213,7 @@
   const base::Optional<LayoutUnit> maybe_intrinsic_height_;
   const base::Optional<float> maybe_intrinsic_ratio_;
   const BoxGenerator::Context* context_;
-  base::Optional<bool> is_video_punched_out_;
+  base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode_;
   math::SizeF content_size_;
 
   scoped_refptr<ReplacedBox> replaced_box_;
@@ -212,7 +229,7 @@
           css_computed_style_declaration_, replace_image_cb_, set_bounds_cb_,
           paragraph_, text_position_, maybe_intrinsic_width_,
           maybe_intrinsic_height_, maybe_intrinsic_ratio_,
-          context_->used_style_provider, is_video_punched_out_, content_size_,
+          context_->used_style_provider, replaced_box_mode_, content_size_,
           context_->layout_stat_tracker));
       break;
     // Generate an inline-level replaced box. There is no need to distinguish
@@ -226,7 +243,7 @@
           css_computed_style_declaration_, replace_image_cb_, set_bounds_cb_,
           paragraph_, text_position_, maybe_intrinsic_width_,
           maybe_intrinsic_height_, maybe_intrinsic_ratio_,
-          context_->used_style_provider, is_video_punched_out_, content_size_,
+          context_->used_style_provider, replaced_box_mode_, content_size_,
           context_->layout_stat_tracker));
       break;
     // The element generates no boxes and has no effect on layout.
@@ -324,12 +341,15 @@
 
   // If the optional is disengaged, then we don't know if punch out is enabled
   // or not.
-  base::Optional<bool> is_punch_out;
+  base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode;
   if (video_element->GetVideoFrameProvider()) {
     VideoFrameProvider::OutputMode output_mode =
         video_element->GetVideoFrameProvider()->GetOutputMode();
     if (output_mode != VideoFrameProvider::kOutputModeInvalid) {
-      is_punch_out = output_mode == VideoFrameProvider::kOutputModePunchOut;
+      replaced_box_mode =
+          (output_mode == VideoFrameProvider::kOutputModePunchOut)
+              ? ReplacedBox::ReplacedBoxMode::kPunchOutVideo
+              : ReplacedBox::ReplacedBoxMode::kVideo;
     }
   }
 
@@ -340,7 +360,7 @@
                        resource_provider)
           : ReplacedBox::ReplaceImageCB(),
       video_element->GetSetBoundsCB(), *paragraph_, text_position,
-      base::nullopt, base::nullopt, base::nullopt, context_, is_punch_out,
+      base::nullopt, base::nullopt, base::nullopt, context_, replaced_box_mode,
       video_element->GetVideoSize());
   video_element->computed_style()->display()->Accept(&replaced_box_generator);
 
@@ -404,6 +424,42 @@
   boxes_.push_back(br_text_box);
 }
 
+void BoxGenerator::VisitLottiePlayer(dom::LottiePlayer* lottie_player) {
+  int32 text_position =
+      (*paragraph_)
+          ->AppendCodePoint(Paragraph::kObjectReplacementCharacterCodePoint);
+
+  ReplacedBoxGenerator replaced_box_generator(
+      lottie_player->css_computed_style_declaration(),
+      lottie_player->cached_image()->TryGetResource()
+          ? base::Bind(GetLottieAnimation,
+                       lottie_player->cached_image()->TryGetResource())
+          : ReplacedBox::ReplaceImageCB(),
+      ReplacedBox::SetBoundsCB(), *paragraph_, text_position, base::nullopt,
+      base::nullopt, base::nullopt, context_,
+      ReplacedBox::ReplacedBoxMode::kLottie,
+      math::Size() /* only relevant to punch out video */);
+  lottie_player->computed_style()->display()->Accept(&replaced_box_generator);
+
+  scoped_refptr<ReplacedBox> replaced_box =
+      replaced_box_generator.replaced_box();
+  if (replaced_box.get() == NULL) {
+    // The element with "display: none" generates no boxes and has no effect
+    // on layout. Descendant elements do not generate any boxes either.
+    // This behavior cannot be overridden by setting the "display" property on
+    // the descendants.
+    //   https://www.w3.org/TR/CSS21/visuren.html#display-prop
+    return;
+  }
+
+#ifdef COBALT_BOX_DUMP_ENABLED
+  replaced_box->SetGeneratingNode(lottie_player);
+#endif  // COBALT_BOX_DUMP_ENABLED
+
+  replaced_box->SetUiNavItem(lottie_player->GetUiNavItem());
+  boxes_.push_back(replaced_box);
+}
+
 namespace {
 
 typedef dom::HTMLElement::DirState DirState;
diff --git a/src/cobalt/layout/box_generator.h b/src/cobalt/layout/box_generator.h
index deb13f5..948ea92 100644
--- a/src/cobalt/layout/box_generator.h
+++ b/src/cobalt/layout/box_generator.h
@@ -99,6 +99,7 @@
  private:
   void VisitVideoElement(dom::HTMLVideoElement* video_element);
   void VisitBrElement(dom::HTMLBRElement* br_element);
+  void VisitLottiePlayer(dom::LottiePlayer* lottie_player);
   void VisitNonReplacedElement(dom::HTMLElement* html_element);
 
   void AppendChildBoxToLine(const scoped_refptr<Box>& child_box);
diff --git a/src/cobalt/layout/inline_level_replaced_box.cc b/src/cobalt/layout/inline_level_replaced_box.cc
index e5f3b7f..b1400fc 100644
--- a/src/cobalt/layout/inline_level_replaced_box.cc
+++ b/src/cobalt/layout/inline_level_replaced_box.cc
@@ -29,13 +29,13 @@
     const base::Optional<LayoutUnit>& maybe_intrinsic_height,
     const base::Optional<float>& maybe_intrinsic_ratio,
     UsedStyleProvider* used_style_provider,
-    base::Optional<bool> is_video_punched_out, const math::SizeF& content_size,
-    LayoutStatTracker* layout_stat_tracker)
+    base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode,
+    const math::SizeF& content_size, LayoutStatTracker* layout_stat_tracker)
     : ReplacedBox(css_computed_style_declaration, replace_image_cb,
                   set_bounds_cb, paragraph, text_position,
                   maybe_intrinsic_width, maybe_intrinsic_height,
-                  maybe_intrinsic_ratio, used_style_provider,
-                  is_video_punched_out, content_size, layout_stat_tracker),
+                  maybe_intrinsic_ratio, used_style_provider, replaced_box_mode,
+                  content_size, layout_stat_tracker),
       is_hidden_by_ellipsis_(false),
       was_hidden_by_ellipsis_(false) {}
 
diff --git a/src/cobalt/layout/inline_level_replaced_box.h b/src/cobalt/layout/inline_level_replaced_box.h
index b50887c..401cda9 100644
--- a/src/cobalt/layout/inline_level_replaced_box.h
+++ b/src/cobalt/layout/inline_level_replaced_box.h
@@ -41,7 +41,7 @@
       const base::Optional<LayoutUnit>& maybe_intrinsic_height,
       const base::Optional<float>& maybe_intrinsic_ratio,
       UsedStyleProvider* used_style_provider,
-      base::Optional<bool> is_video_punched_out,
+      base::Optional<ReplacedBox::ReplacedBoxMode> replaced_box_mode,
       const math::SizeF& content_size, LayoutStatTracker* layout_stat_tracker);
 
   // From |Box|.
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 0542809..0292df1 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -35,6 +35,7 @@
 #include "cobalt/render_tree/color_rgba.h"
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/map_to_mesh_filter.h"
 #include "cobalt/render_tree/punch_through_video_node.h"
 #include "cobalt/render_tree/rect_node.h"
@@ -46,6 +47,7 @@
 using render_tree::CompositionNode;
 using render_tree::FilterNode;
 using render_tree::ImageNode;
+using render_tree::LottieNode;
 using render_tree::MapToMeshFilter;
 using render_tree::Node;
 using render_tree::PunchThroughVideoNode;
@@ -103,8 +105,8 @@
     const base::Optional<LayoutUnit>& maybe_intrinsic_height,
     const base::Optional<float>& maybe_intrinsic_ratio,
     UsedStyleProvider* used_style_provider,
-    base::Optional<bool> is_video_punched_out, const math::SizeF& content_size,
-    LayoutStatTracker* layout_stat_tracker)
+    base::Optional<ReplacedBoxMode> replaced_box_mode,
+    const math::SizeF& content_size, LayoutStatTracker* layout_stat_tracker)
     : Box(css_computed_style_declaration, used_style_provider,
           layout_stat_tracker),
       maybe_intrinsic_width_(maybe_intrinsic_width),
@@ -117,7 +119,7 @@
       set_bounds_cb_(set_bounds_cb),
       paragraph_(paragraph),
       text_position_(text_position),
-      is_video_punched_out_(is_video_punched_out),
+      replaced_box_mode_(replaced_box_mode),
       content_size_(content_size) {}
 
 WrapResult ReplacedBox::TryWrapAt(
@@ -282,6 +284,18 @@
   }
 }
 
+void AnimateLottie(const ReplacedBox::ReplaceImageCB& replace_image_cb,
+                   math::RectF destination_rect,
+                   LottieNode::Builder* node_builder,
+                   base::TimeDelta time_elapsed) {
+  scoped_refptr<render_tree::Image> animation = replace_image_cb.Run();
+  node_builder->animation =
+      base::polymorphic_downcast<render_tree::LottieAnimation*>(
+          animation.get());
+  node_builder->destination_rect = destination_rect;
+  node_builder->animation_time = time_elapsed;
+}
+
 }  // namespace
 
 void ReplacedBox::RenderAndAnimateContent(
@@ -295,7 +309,7 @@
     return;
   }
 
-  if (is_video_punched_out_ == base::nullopt) {
+  if (replaced_box_mode_ == base::nullopt) {
     // If we don't have a data stream associated with this video [yet], then
     // we don't yet know if it is punched out or not, and so render black.
     border_node_builder->AddChild(new RectNode(
@@ -306,20 +320,32 @@
     return;
   }
 
+  if (*replaced_box_mode_ == ReplacedBox::ReplacedBoxMode::kLottie) {
+    AnimateNode::Builder animate_node_builder;
+    scoped_refptr<LottieNode> lottie_node =
+        new LottieNode(nullptr, math::RectF());
+    animate_node_builder.Add(lottie_node,
+                             base::Bind(&AnimateLottie, replace_image_cb_,
+                                        math::RectF(content_box_size())));
+    border_node_builder->AddChild(
+        new AnimateNode(animate_node_builder, lottie_node));
+    return;
+  }
+
   const cssom::MapToMeshFunction* mtm_filter_function =
       cssom::MapToMeshFunction::ExtractFromFilterList(
           computed_style()->filter());
 
   if (mtm_filter_function && mtm_filter_function->mesh_spec().mesh_type() !=
                                  cssom::MapToMeshFunction::kRectangular) {
-    DCHECK(!*is_video_punched_out_)
+    DCHECK(*replaced_box_mode_ == ReplacedBox::ReplacedBoxMode::kVideo)
         << "We currently do not support punched out video with map-to-mesh "
            "filters.";
     RenderAndAnimateContentWithMapToMesh(border_node_builder,
                                          mtm_filter_function);
   } else {
 #if defined(FORCE_VIDEO_EXTERNAL_MESH)
-    if (!*is_video_punched_out_) {
+    if (*replaced_box_mode_ == ReplacedBox : ReplacedBoxMode::kVideo) {
       AnimateNode::Builder animate_node_builder;
       scoped_refptr<ImageNode> image_node = new ImageNode(nullptr);
       animate_node_builder.Add(
@@ -744,7 +770,7 @@
   scoped_refptr<CompositionNode> composition_node =
       new CompositionNode(composition_node_builder);
 
-  if (*is_video_punched_out_) {
+  if (*replaced_box_mode_ == ReplacedBox::ReplacedBoxMode::kPunchOutVideo) {
     LetterboxDimensions letterbox_dims =
         GetLetterboxDimensions(content_size_, content_box_size());
     AddLetterboxedPunchThroughVideoNodeToRenderTree(
diff --git a/src/cobalt/layout/replaced_box.h b/src/cobalt/layout/replaced_box.h
index 71ac2d8..7f80c97 100644
--- a/src/cobalt/layout/replaced_box.h
+++ b/src/cobalt/layout/replaced_box.h
@@ -34,7 +34,7 @@
 // The class represents a Replaced element in the layout tree. It is used to
 // render elements like embed, iframe or video. Currently it renders the element
 // as an image retrieved from a callback passed into its ctor.
-//   https://www.w3.org/TR/html5/rendering.html#replaced-elements
+//   https://www.w3.org/TR/html50/rendering.html#replaced-elements
 //
 // TODO: Make ReplacedBox support elements other than media element.
 class ReplacedBox : public Box {
@@ -42,6 +42,8 @@
   typedef base::Callback<scoped_refptr<render_tree::Image>()> ReplaceImageCB;
   typedef render_tree::PunchThroughVideoNode::SetBoundsCB SetBoundsCB;
 
+  enum class ReplacedBoxMode { kVideo, kPunchOutVideo, kLottie };
+
   ReplacedBox(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
                   css_computed_style_declaration,
               const ReplaceImageCB& replace_image_cb,
@@ -51,7 +53,7 @@
               const base::Optional<LayoutUnit>& maybe_intrinsic_height,
               const base::Optional<float>& maybe_intrinsic_ratio,
               UsedStyleProvider* used_style_provider,
-              base::Optional<bool> is_video_punched_out,
+              base::Optional<ReplacedBoxMode> replaced_box_mode,
               const math::SizeF& content_size,
               LayoutStatTracker* layout_stat_tracker);
 
@@ -119,7 +121,7 @@
 
   const scoped_refptr<Paragraph> paragraph_;
   int32 text_position_;
-  base::Optional<bool> is_video_punched_out_;
+  base::Optional<ReplacedBoxMode> replaced_box_mode_;
   math::SizeF content_size_;
 };
 
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index e0609a0..0464c1b 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -331,7 +331,7 @@
     ::testing::ValuesIn(EnumerateLayoutTests("the-dir-attribute")),
     GetTestName());
 
-// JavaScript HTML5 WebAPIs (https://www.w3.org/TR/html5/webappapis.html) test
+// JavaScript HTML5 WebAPIs (https://www.w3.org/TR/html50/webappapis.html) test
 // cases.
 INSTANTIATE_TEST_CASE_P(
     WebAppAPIsLayoutTests, Layout,
diff --git a/src/cobalt/layout_tests/testdata/bidi/containing-block-should-not-inherit-directionality-from-nested-block-box.html b/src/cobalt/layout_tests/testdata/bidi/containing-block-should-not-inherit-directionality-from-nested-block-box.html
index d09fef3..2b66f61 100644
--- a/src/cobalt/layout_tests/testdata/bidi/containing-block-should-not-inherit-directionality-from-nested-block-box.html
+++ b/src/cobalt/layout_tests/testdata/bidi/containing-block-should-not-inherit-directionality-from-nested-block-box.html
@@ -3,7 +3,7 @@
  | The directionality of an element is inherited from its parent element if it
  | is not explicitly set on the element itself. The parent does not have its
  | directionality impacted by its children.
- |   https://www.w3.org/TR/html5/dom.html#the-directionality
+ |   https://www.w3.org/TR/html50/dom.html#the-directionality
  -->
 <html>
 <head>
diff --git a/src/cobalt/layout_tests/testdata/bidi/directional-stack-should-be-restored-following-nested-paragraph.html b/src/cobalt/layout_tests/testdata/bidi/directional-stack-should-be-restored-following-nested-paragraph.html
index 610bfb4..7646773 100644
--- a/src/cobalt/layout_tests/testdata/bidi/directional-stack-should-be-restored-following-nested-paragraph.html
+++ b/src/cobalt/layout_tests/testdata/bidi/directional-stack-should-be-restored-following-nested-paragraph.html
@@ -3,7 +3,7 @@
  | The directionality of an element is inherited from its parent element if it
  | is not explicitly set on the element itself. The parent does not have its
  | directionality impacted by its children.
- |   https://www.w3.org/TR/html5/dom.html#the-directionality
+ |   https://www.w3.org/TR/html50/dom.html#the-directionality
  | All explicit embedding levels are determined from explicit directional
  | formatting characters using a directional status stack.
  |   http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions
diff --git a/src/cobalt/layout_tests/testdata/bidi/inline-container-blocks-should-not-impact-directionality-of-containing-block.html b/src/cobalt/layout_tests/testdata/bidi/inline-container-blocks-should-not-impact-directionality-of-containing-block.html
index 6028c64..d1f3416 100644
--- a/src/cobalt/layout_tests/testdata/bidi/inline-container-blocks-should-not-impact-directionality-of-containing-block.html
+++ b/src/cobalt/layout_tests/testdata/bidi/inline-container-blocks-should-not-impact-directionality-of-containing-block.html
@@ -3,7 +3,7 @@
  | The directionality of an element is inherited from its parent element if it
  | is not explicitly set on the element itself. The parent does not have its
  | directionality impacted by its children.
- |   https://www.w3.org/TR/html5/dom.html#the-directionality
+ |   https://www.w3.org/TR/html50/dom.html#the-directionality
  -->
 <html>
 <head>
diff --git a/src/cobalt/layout_tests/testdata/bidi/nested-block-boxes-should-inherit-directionality-from-containing-block.html b/src/cobalt/layout_tests/testdata/bidi/nested-block-boxes-should-inherit-directionality-from-containing-block.html
index 53a7047..223bdae 100644
--- a/src/cobalt/layout_tests/testdata/bidi/nested-block-boxes-should-inherit-directionality-from-containing-block.html
+++ b/src/cobalt/layout_tests/testdata/bidi/nested-block-boxes-should-inherit-directionality-from-containing-block.html
@@ -2,7 +2,7 @@
 <!--
  | The directionality of an element is inherited from its parent element if it
  | is not explicitly set on the element itself.
- |   https://www.w3.org/TR/html5/dom.html#the-directionality
+ |   https://www.w3.org/TR/html50/dom.html#the-directionality
  |   https://www.w3.org/International/questions/qa-html-dir#basedirection
  -->
 <html>
diff --git a/src/cobalt/layout_tests/testdata/bidi/paragraph-should-maintain-directional-stack-with-nested-inline-container-blocks.html b/src/cobalt/layout_tests/testdata/bidi/paragraph-should-maintain-directional-stack-with-nested-inline-container-blocks.html
index 7e9df7a..4aed6ac 100644
--- a/src/cobalt/layout_tests/testdata/bidi/paragraph-should-maintain-directional-stack-with-nested-inline-container-blocks.html
+++ b/src/cobalt/layout_tests/testdata/bidi/paragraph-should-maintain-directional-stack-with-nested-inline-container-blocks.html
@@ -2,7 +2,7 @@
 <!--
  | The directionality of an element is inherited from its parent element if it
  | is not explicitly set on the element itself.
- |   https://www.w3.org/TR/html5/dom.html#the-directionality
+ |   https://www.w3.org/TR/html50/dom.html#the-directionality
  | All explicit embedding levels are determined from explicit directional
  | formatting characters using a directional status stack.
  |   http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt
new file mode 100644
index 0000000..3e1afd6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt
@@ -0,0 +1,49 @@
+# WebCrypto API tests.
+#
+# Only HMAC signing is supported
+sign_verify/test_rsa_pkcs.https.html,DISABLE
+sign_verify/test_hmac.https.html,PASS
+sign_verify/test_rsa_pss.https.html,DISABLE
+sign_verify/test_ecdsa.https.html,DISABLE
+# TODO: Random generator not yet supported, should use SbSystemGetRandomData
+getRandomValues.any.html,DISABLE
+# TODO: This requires a significant update to testharness.js
+idlharness.https.any.html,DISABLE
+# Only AES-CTR is implemented
+encrypt_decrypt/test_aes_gcm.https.html,DISABLE
+encrypt_decrypt/test_aes_ctr.https.html,PASS
+encrypt_decrypt/test_aes_cbc.https.html,DISABLE
+encrypt_decrypt/test_rsa_oaep.https.html,DISABLE
+# No generateKey functions are supported
+generateKey/failures_ECDH.https.any.html,DISABLE
+generateKey/failures_AES-CBC.https.any.html,DISABLE
+generateKey/successes_HMAC.https.any.html,DISABLE
+generateKey/failures_AES-GCM.https.any.html,DISABLE
+generateKey/failures_RSA-PSS.https.any.html,DISABLE
+generateKey/successes_RSA-PSS.https.any.html,DISABLE
+generateKey/failures_AES-CTR.https.any.html,DISABLE
+generateKey/failures_RSASSA-PKCS1-v1_5.https.any.html,DISABLE
+generateKey/successes_ECDSA.https.any.html,DISABLE
+generateKey/successes_RSA-OAEP.https.any.html,DISABLE
+generateKey/failures_HMAC.https.any.html,DISABLE
+generateKey/successes_RSASSA-PKCS1-v1_5.https.any.html,DISABLE
+generateKey/successes_ECDH.https.any.html,DISABLE
+generateKey/successes_AES-CTR.https.any.html,DISABLE
+generateKey/successes_AES-KW.https.any.html,DISABLE
+generateKey/failures_RSA-OAEP.https.any.html,DISABLE
+generateKey/failures_ECDSA.https.any.html,DISABLE
+generateKey/successes_AES-CBC.https.any.html,DISABLE
+generateKey/failures_AES-KW.https.any.html,DISABLE
+generateKey/successes_AES-GCM.https.any.html,DISABLE
+# deriveBits is not supported
+derive_bits_keys/ecdh_bits.https.any.html,DISABLE
+derive_bits_keys/ecdh_keys.https.any.html,DISABLE
+derive_bits_keys/pbkdf2.https.any.html,DISABLE
+derive_bits_keys/hkdf.https.any.html,DISABLE
+# RSA and EC key import/export is not supported
+import_export/test_rsa_importKey.https.html,DISABLE
+import_export/test_ec_importKey.https.html,DISABLE
+import_export/test_symmetric_importKey.https.html,PASS
+digest/test_digest.https.html,PASS
+wrapKey_unwrapKey/test_wrapKey_unwrapKey.https.html,DISABLE
+secure_context/crypto-subtle-secure-context-available.https.sub.html,DISABLE
diff --git a/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window-when-set-via-attribute.html b/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window-when-set-via-attribute.html
index c74e872..3ed58ac 100644
--- a/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window-when-set-via-attribute.html
+++ b/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window-when-set-via-attribute.html
@@ -4,7 +4,7 @@
  | resource) have finished loading.  This test specifically sets the onload
  | event handler through the window onload attribute, as opposed to
  | addEventListener().
- |   https://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
+ |   https://www.w3.org/TR/html50/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
  -->
 <html>
 <head>
diff --git a/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window.html b/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window.html
index 7ee3304..e1c9b62 100644
--- a/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window.html
+++ b/src/cobalt/layout_tests/testdata/webappapis/6-1-5-2-onload-event-fires-on-window.html
@@ -2,7 +2,7 @@
 <!--
  | The onload event should fire on the window object when the document (and all
  | resource) have finished loading.
- |   https://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
+ |   https://www.w3.org/TR/html50/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
  -->
 <html>
 <head>
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 3d98bd2..1ec5888 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -433,6 +433,11 @@
     ::testing::ValuesIn(EnumerateWebPlatformTests("websockets")),
     GetTestName());
 
+INSTANTIATE_TEST_CASE_P(
+    web_crypto_api, WebPlatformTest,
+    ::testing::ValuesIn(EnumerateWebPlatformTests("WebCryptoAPI")),
+    GetTestName());
+
 #endif  // !defined(COBALT_WIN)
 
 }  // namespace layout_tests
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index f4384c4..d1e5a51 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -19,9 +19,11 @@
 
 #include "base/command_line.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/loader/image/dummy_gif_image_decoder.h"
 #include "cobalt/loader/image/image_decoder_starboard.h"
 #include "cobalt/loader/image/jpeg_image_decoder.h"
+#include "cobalt/loader/image/lottie_animation_decoder.h"
 #include "cobalt/loader/image/png_image_decoder.h"
 #include "cobalt/loader/image/stub_image_decoder.h"
 #include "cobalt/loader/image/webp_image_decoder.h"
@@ -56,6 +58,10 @@
     return ImageDecoder::kImageTypeJPEG;
   } else if (!memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6)) {
     return ImageDecoder::kImageTypeGIF;
+  } else if (!memcmp(header, "{", 1)) {
+    // TODO: Improve heuristics for determining whether the file contains valid
+    // Lottie JSON.
+    return ImageDecoder::kImageTypeJSON;
   } else if (!memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8)) {
     return ImageDecoder::kImageTypePNG;
   } else if (!memcmp(header, "RIFF", 4) && !memcmp(header + 8, "WEBPVP", 6)) {
@@ -262,6 +268,8 @@
       return "image/png";
     case ImageDecoder::kImageTypeGIF:
       return "image/gif";
+    case ImageDecoder::kImageTypeJSON:
+      return "application/json";
     case ImageDecoder::kImageTypeWebP:
       return "image/webp";
     case ImageDecoder::kImageTypeInvalid:
@@ -333,6 +341,9 @@
   } else if (image_type == ImageDecoder::kImageTypeGIF) {
     return std::unique_ptr<ImageDataDecoder>(
         new DummyGIFImageDecoder(resource_provider));
+  } else if (image_type == ImageDecoder::kImageTypeJSON) {
+    return std::unique_ptr<ImageDataDecoder>(
+        new LottieAnimationDecoder(resource_provider));
   } else {
     return std::unique_ptr<ImageDataDecoder>();
   }
@@ -381,8 +392,7 @@
 
 // static
 bool ImageDecoder::AllowDecodingToMultiPlane() {
-#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION && \
-    defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
+#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
   // Many image formats can produce native output in multi plane images in YUV
   // 420. Allowing these images to be decoded into multi plane image not only
   // reduces the space to store the decoded image to 37.5%, but also improves
@@ -394,7 +404,9 @@
   // This also applies to skia based "hardware" rasterizers as the rendering
   // of multi plane images in such cases are not optimized, but this may be
   // improved in future.
-  bool allow_image_decoding_to_multi_plane = SbGetGlesInterface() != nullptr;
+  bool allow_image_decoding_to_multi_plane =
+      std::string(configuration::Configuration::GetInstance()
+                      ->CobaltRasterizerType()) == "direct-gles";
 #elif SB_HAS(GLES2) && defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
   bool allow_image_decoding_to_multi_plane = true;
 #else  // SB_HAS(GLES2) && defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h
index b731b97..0e1c3dc 100644
--- a/src/cobalt/loader/image/image_decoder.h
+++ b/src/cobalt/loader/image/image_decoder.h
@@ -41,6 +41,7 @@
     kImageTypeInvalid,
     kImageTypeGIF,
     kImageTypeJPEG,
+    kImageTypeJSON,
     kImageTypePNG,
     kImageTypeWebP,
   };
diff --git a/src/cobalt/loader/image/lottie_animation.cc b/src/cobalt/loader/image/lottie_animation.cc
new file mode 100644
index 0000000..10aeff7
--- /dev/null
+++ b/src/cobalt/loader/image/lottie_animation.cc
@@ -0,0 +1,43 @@
+// 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/loader/image/lottie_animation.h"
+
+#include "base/trace_event/trace_event.h"
+#include "nb/memory_scope.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+LottieAnimation::LottieAnimation(
+    render_tree::ResourceProvider* resource_provider)
+    : resource_provider_(resource_provider) {
+  TRACE_EVENT0("cobalt::loader::image", "LottieAnimation::LottieAnimation()");
+}
+
+void LottieAnimation::AppendChunk(const uint8* data, size_t size) {
+  TRACE_EVENT0("cobalt::loader::image", "LottieAnimatino::AppendChunk()");
+  data_buffer_.insert(data_buffer_.end(), data, data + size);
+}
+
+void LottieAnimation::FinishReadingData() {
+  TRACE_EVENT0("cobalt::loader::image", "LottieAnimation::FinishReadingData()");
+  animation_ = resource_provider_->CreateLottieAnimation(
+      reinterpret_cast<char*>(data_buffer_.data()), data_buffer_.size());
+}
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
diff --git a/src/cobalt/loader/image/lottie_animation.h b/src/cobalt/loader/image/lottie_animation.h
new file mode 100644
index 0000000..21f41e4
--- /dev/null
+++ b/src/cobalt/loader/image/lottie_animation.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_LOADER_IMAGE_LOTTIE_ANIMATION_H_
+#define COBALT_LOADER_IMAGE_LOTTIE_ANIMATION_H_
+
+#include <vector>
+
+#include "base/cancelable_callback.h"
+#include "base/threading/thread.h"
+#include "base/trace_event/trace_event.h"
+#include "cobalt/loader/image/image.h"
+#include "cobalt/render_tree/resource_provider.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+class LottieAnimation : public Image {
+ public:
+  explicit LottieAnimation(render_tree::ResourceProvider* resource_provider);
+
+  const math::Size& GetSize() const override { return animation_->GetSize(); }
+
+  uint32 GetEstimatedSizeInBytes() const override {
+    return animation_->GetEstimatedSizeInBytes();
+  }
+
+  bool IsAnimated() const override {
+    // Even though this class represents an animation, IsAnimated() should
+    // return false because there is only one render_tree::Image object ever
+    // associated with an instance of this class.
+    return false;
+  }
+
+  bool IsOpaque() const override { return animation_->IsOpaque(); }
+
+  void AppendChunk(const uint8* data, size_t input_byte);
+
+  void FinishReadingData();
+
+  scoped_refptr<render_tree::Image> animation() { return animation_; }
+
+ private:
+  render_tree::ResourceProvider* resource_provider_;
+  std::vector<uint8> data_buffer_;
+  scoped_refptr<render_tree::Image> animation_;
+};
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // COBALT_LOADER_IMAGE_LOTTIE_ANIMATION_H_
diff --git a/src/cobalt/loader/image/lottie_animation_decoder.cc b/src/cobalt/loader/image/lottie_animation_decoder.cc
new file mode 100644
index 0000000..0408d8e
--- /dev/null
+++ b/src/cobalt/loader/image/lottie_animation_decoder.cc
@@ -0,0 +1,59 @@
+// 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/loader/image/lottie_animation_decoder.h"
+
+#include "base/trace_event/trace_event.h"
+#include "nb/memory_scope.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+LottieAnimationDecoder::LottieAnimationDecoder(
+    render_tree::ResourceProvider* resource_provider)
+    : ImageDataDecoder(resource_provider) {
+  TRACE_EVENT0("cobalt::loader::image",
+               "LottieAnimationDecoder::LottieAnimationDecoder()");
+  TRACK_MEMORY_SCOPE("Rendering");
+}
+
+size_t LottieAnimationDecoder::DecodeChunkInternal(const uint8* data,
+                                                   size_t input_byte) {
+  TRACE_EVENT0("cobalt::loader::image",
+               "LottieAnimationDecoder::DecodeChunkInternal()");
+  TRACK_MEMORY_SCOPE("Rendering");
+  if (state() == kWaitingForHeader) {
+    // TODO: Remove hard coded values.
+    lottie_animation_ = new LottieAnimation(resource_provider());
+    set_state(kReadLines);
+  }
+
+  if (state() == kReadLines) {
+    DCHECK(lottie_animation_);
+    lottie_animation_->AppendChunk(data, input_byte);
+  }
+
+  return input_byte;
+}
+
+scoped_refptr<Image> LottieAnimationDecoder::FinishInternal() {
+  set_state(kDone);
+  lottie_animation_->FinishReadingData();
+  return lottie_animation_;
+}
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
diff --git a/src/cobalt/loader/image/lottie_animation_decoder.h b/src/cobalt/loader/image/lottie_animation_decoder.h
new file mode 100644
index 0000000..313be2c
--- /dev/null
+++ b/src/cobalt/loader/image/lottie_animation_decoder.h
@@ -0,0 +1,49 @@
+// 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_LOADER_IMAGE_LOTTIE_ANIMATION_DECODER_H_
+#define COBALT_LOADER_IMAGE_LOTTIE_ANIMATION_DECODER_H_
+
+#include <string>
+
+#include "cobalt/loader/image/image_data_decoder.h"
+#include "cobalt/loader/image/lottie_animation.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+class LottieAnimationDecoder : public ImageDataDecoder {
+ public:
+  explicit LottieAnimationDecoder(
+      render_tree::ResourceProvider* resource_provider);
+
+  // From ImageDataDecoder
+  std::string GetTypeString() const override {
+    return "LottieAnimationDecoder";
+  }
+
+ private:
+  // From ImageDataDecoder
+  size_t DecodeChunkInternal(const uint8* data, size_t input_byte) override;
+  scoped_refptr<Image> FinishInternal() override;
+
+  scoped_refptr<LottieAnimation> lottie_animation_;
+};
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // COBALT_LOADER_IMAGE_LOTTIE_ANIMATION_DECODER_H_
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index d05d443..29e3d43 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -64,6 +64,10 @@
         'image/image.h',
         'image/jpeg_image_decoder.cc',
         'image/jpeg_image_decoder.h',
+        'image/lottie_animation.h',
+        'image/lottie_animation.cc',
+        'image/lottie_animation_decoder.cc',
+        'image/lottie_animation_decoder.h',
         'image/png_image_decoder.cc',
         'image/png_image_decoder.h',
         'image/stub_image_decoder.h',
@@ -102,6 +106,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/csp/csp.gyp:csp',
         '<(DEPTH)/cobalt/loader/origin.gyp:origin',
         '<(DEPTH)/cobalt/network/network.gyp:network',
@@ -126,19 +131,6 @@
             'about_fetcher.h',
           ]
         }],
-        ['enable_xhr_header_filtering == 1', {
-          'dependencies': [
-            '<@(cobalt_platform_dependencies)',
-          ],
-          'defines': [
-            'COBALT_ENABLE_XHR_HEADER_FILTERING',
-          ],
-          'direct_dependent_settings': {
-            'defines': [
-              'COBALT_ENABLE_XHR_HEADER_FILTERING',
-            ],
-          },
-        }],
       ],
     },
 
diff --git a/src/cobalt/media/base/decoder_buffer.cc b/src/cobalt/media/base/decoder_buffer.cc
index 0528852..34bb460 100644
--- a/src/cobalt/media/base/decoder_buffer.cc
+++ b/src/cobalt/media/base/decoder_buffer.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "cobalt/build/build_config.h"
 #include "starboard/media.h"
 #include "starboard/memory.h"
 
@@ -34,14 +33,9 @@
     : allocator_(allocator), type_(type) {
   if (size > 0) {
     DCHECK(allocator_);
-#if SB_API_VERSION >= 10
     int padding = SbMediaGetBufferPadding(DemuxerStreamTypeToSbMediaType(type));
     int alignment =
         SbMediaGetBufferAlignment(DemuxerStreamTypeToSbMediaType(type));
-#else   // SB_API_VERSION >= 10
-    int padding = COBALT_MEDIA_BUFFER_PADDING;
-    int alignment = COBALT_MEDIA_BUFFER_ALIGNMENT;
-#endif  // SB_API_VERSION >= 10
     allocations_ = allocator_->Allocate(size + padding, alignment,
                                         static_cast<intptr_t>(type));
     static bool logged_warning = false;
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc
index b94c858..74a68f1 100644
--- a/src/cobalt/media/base/drm_system.cc
+++ b/src/cobalt/media/base/drm_system.cc
@@ -29,23 +29,15 @@
 
 DrmSystem::Session::Session(
     DrmSystem* drm_system,
-    SessionUpdateKeyStatusesCallback update_key_statuses_callback
-#if SB_HAS(DRM_SESSION_CLOSED)
-    ,
-    SessionClosedCallback session_closed_callback
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-    )
+    SessionUpdateKeyStatusesCallback update_key_statuses_callback,
+    SessionClosedCallback session_closed_callback)
     : drm_system_(drm_system),
       update_key_statuses_callback_(update_key_statuses_callback),
-#if SB_HAS(DRM_SESSION_CLOSED)
       session_closed_callback_(session_closed_callback),
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
       closed_(false),
       weak_factory_(this) {
   DCHECK(!update_key_statuses_callback_.is_null());
-#if SB_HAS(DRM_SESSION_CLOSED)
   DCHECK(!session_closed_callback_.is_null());
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 }
 
 DrmSystem::Session::~Session() {
@@ -88,27 +80,20 @@
 }
 
 DrmSystem::DrmSystem(const char* key_system)
-    : wrapped_drm_system_(SbDrmCreateSystem(key_system, this,
-                                            OnSessionUpdateRequestGeneratedFunc,
-                                            OnSessionUpdatedFunc
-                                            ,
-                                            OnSessionKeyStatusesChangedFunc
-#if SB_API_VERSION >= 10
-                                            ,
-                                            OnServerCertificateUpdatedFunc
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
-                                            ,
-                                            OnSessionClosedFunc
-#endif                                           // SB_HAS(DRM_SESSION_CLOSED)
-                                            )),  // NOLINT(whitespace/parens)
+    : wrapped_drm_system_(SbDrmCreateSystem(
+          key_system, this, OnSessionUpdateRequestGeneratedFunc,
+          OnSessionUpdatedFunc, OnSessionKeyStatusesChangedFunc,
+          OnServerCertificateUpdatedFunc, OnSessionClosedFunc)),
       message_loop_(base::MessageLoop::current()->task_runner()),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
       weak_this_(weak_ptr_factory_.GetWeakPtr()) {
   ON_INSTANCE_CREATED(DrmSystem);
 
-  if (!is_valid()) {
-    SB_LOG(ERROR) << "Failed to initialize the underlying wrapped DrmSystem.";
+  if (is_valid()) {
+    LOG(INFO) << "Successfully created SbDrmSystem (" << wrapped_drm_system_
+              << "), key system: " << key_system << ".";
+  } else {
+    LOG(INFO) << "Failed to create SbDrmSystem, key system: " << key_system;
   }
 }
 
@@ -119,44 +104,22 @@
 }
 
 std::unique_ptr<DrmSystem::Session> DrmSystem::CreateSession(
-    SessionUpdateKeyStatusesCallback session_update_key_statuses_callback
-#if SB_HAS(DRM_SESSION_CLOSED)
-    ,
-    SessionClosedCallback session_closed_callback
-#endif   // SB_HAS(DRM_SESSION_CLOSED)
-    ) {  // NOLINT(whitespace/parens)
+    SessionUpdateKeyStatusesCallback session_update_key_statuses_callback,
+    SessionClosedCallback session_closed_callback) {
   DCHECK(message_loop_->BelongsToCurrentThread());
   return std::unique_ptr<DrmSystem::Session>(new Session(
-      this
-      ,
-      session_update_key_statuses_callback
-#if SB_HAS(DRM_SESSION_CLOSED)
-      ,
-      session_closed_callback
-#endif     // SB_HAS(DRM_SESSION_CLOSED)
-      ));  // NOLINT(whitespace/parens)
+      this, session_update_key_statuses_callback, session_closed_callback));
 }
 
-#if SB_API_VERSION >= 10
 bool DrmSystem::IsServerCertificateUpdatable() {
   DCHECK(message_loop_->BelongsToCurrentThread());
-  return SbDrmIsServerCertificateUpdatable(wrapped_drm_system_);
-}
-
-void DrmSystem::UpdateServerCertificate(
-    const uint8_t* certificate, int certificate_size,
-    ServerCertificateUpdatedCallback server_certificate_updated_callback) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  DCHECK(IsServerCertificateUpdatable());
-  int ticket = next_ticket_++;
-  ticket_to_server_certificate_updated_map_.insert(
-      std::make_pair(ticket, server_certificate_updated_callback));
-  SbDrmUpdateServerCertificate(wrapped_drm_system_, ticket, certificate,
-                               certificate_size);
-}
-#else   // SB_API_VERSION >= 10
-bool DrmSystem::IsServerCertificateUpdatable() {
-  DCHECK(message_loop_->BelongsToCurrentThread());
+  if (SbDrmIsServerCertificateUpdatable(wrapped_drm_system_)) {
+    LOG(INFO) << "SbDrmSystem (" << wrapped_drm_system_
+              << ") supports server certificate update";
+    return true;
+  }
+  LOG(INFO) << "SbDrmSystem (" << wrapped_drm_system_
+            << ") doesn't support server certificate update";
   return false;
 }
 
@@ -164,9 +127,18 @@
     const uint8_t* certificate, int certificate_size,
     ServerCertificateUpdatedCallback server_certificate_updated_callback) {
   DCHECK(message_loop_->BelongsToCurrentThread());
-  NOTREACHED();
+  DCHECK(IsServerCertificateUpdatable());
+
+  LOG(INFO) << "Updating server certificate of drm system ("
+            << wrapped_drm_system_
+            << "), certificate size: " << certificate_size;
+
+  int ticket = next_ticket_++;
+  ticket_to_server_certificate_updated_map_.insert(
+      std::make_pair(ticket, server_certificate_updated_callback));
+  SbDrmUpdateServerCertificate(wrapped_drm_system_, ticket, certificate,
+                               certificate_size);
 }
-#endif  // SB_API_VERSION >= 10
 
 void DrmSystem::GenerateSessionUpdateRequest(
     const base::WeakPtr<Session>& session, const std::string& type,
@@ -188,6 +160,11 @@
   ticket_to_session_update_request_map_.insert(
       std::make_pair(ticket, session_update_request));
 
+  LOG(INFO) << "Generate session update request of drm system ("
+            << wrapped_drm_system_ << "), type: " << type
+            << ", init data size: " << init_data_length
+            << ", ticket: " << ticket;
+
   SbDrmGenerateSessionUpdateRequest(wrapped_drm_system_, ticket, type.c_str(),
                                     init_data, init_data_length);
 }
@@ -205,15 +182,20 @@
   int ticket = next_ticket_++;
   ticket_to_session_update_map_.insert(std::make_pair(ticket, session_update));
 
+  LOG(INFO) << "Update session of drm system (" << wrapped_drm_system_
+            << "), key length: " << key_length << ", ticket: " << ticket
+            << ", session id: " << session_id;
+
   SbDrmUpdateSession(wrapped_drm_system_, ticket, key, key_length,
                      session_id.c_str(), session_id.size());
 }
 
 void DrmSystem::CloseSession(const std::string& session_id) {
   DCHECK(message_loop_->BelongsToCurrentThread());
-#if !SB_HAS(DRM_SESSION_CLOSED)
-  id_to_session_map_.erase(session_id);
-#endif  // !SB_HAS(DRM_SESSION_CLOSED)
+
+  LOG(INFO) << "Close session of drm system (" << wrapped_drm_system_
+            << "), session id: " << session_id;
+
   SbDrmCloseSession(wrapped_drm_system_, session_id.c_str(), session_id.size());
 }
 
@@ -225,6 +207,12 @@
 
   int ticket = ticket_and_optional_id.ticket;
   const base::Optional<std::string>& session_id = ticket_and_optional_id.id;
+
+  LOG(INFO) << "Receiving session update request notification from drm system ("
+            << wrapped_drm_system_ << "), status: " << status
+            << ", type: " << type << ", ticket: " << ticket
+            << ", session id: " << session_id.value_or("n/a");
+
   if (SbDrmTicketIsValid(ticket)) {
     // Called back as a result of |SbDrmGenerateSessionUpdateRequest|.
 
@@ -251,10 +239,17 @@
         id_to_session_map_.insert(
             std::make_pair(*session_id, session_update_request.session));
 
+        LOG(INFO) << "Calling session update request callback on drm system ("
+                  << wrapped_drm_system_ << ") with type: " << type
+                  << ", message size: " << message_size;
+
         session_update_request.generated_callback.Run(type, std::move(message),
                                                       message_size);
       } else {
         // Failure during request generation.
+        LOG(INFO) << "Calling session update request callback on drm system ("
+                  << wrapped_drm_system_ << "), status: " << status
+                  << ", error message: " << error_message;
         session_update_request.did_not_generate_callback.Run(status,
                                                              error_message);
       }
@@ -268,8 +263,9 @@
 
     // Spontaneous calls must refer to a valid session.
     if (!session_id) {
-      DLOG(FATAL) << "SbDrmSessionUpdateRequestFunc() should not be called "
-                     "with both invalid ticket and null session id.";
+      LOG(ERROR) << "SbDrmSessionUpdateRequestFunc() should not be called "
+                    "with both invalid ticket and null session id.";
+      NOTREACHED();
       return;
     }
 
@@ -283,6 +279,9 @@
 
     // As DrmSystem::Session may be released, need to check it before using it.
     if (session_iterator->second) {
+      LOG(INFO) << "Calling session update request callback on drm system ("
+                << wrapped_drm_system_ << "), type: " << type
+                << ", message size " << message_size;
       session_iterator->second->update_request_generated_callback().Run(
           type, std::move(message), message_size);
     }
@@ -293,6 +292,10 @@
                                  const std::string& error_message) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
+  LOG(INFO) << "Receiving session updated notification from drm system ("
+            << wrapped_drm_system_ << "), status: " << status
+            << ", ticket: " << ticket << ", error message: " << error_message;
+
   // Restore the context of |UpdateSession|.
   TicketToSessionUpdateMap::iterator session_update_iterator =
       ticket_to_session_update_map_.find(ticket);
@@ -318,6 +321,11 @@
     const std::vector<SbDrmKeyStatus>& key_statuses) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
+  LOG(INFO) << "Receiving session key status changed notification from drm"
+            << " system (" << wrapped_drm_system_
+            << "), session id: " << session_id
+            << ", number of key ids: " << key_ids.size();
+
   // Find the session by ID.
   IdToSessionMap::iterator session_iterator =
       id_to_session_map_.find(session_id);
@@ -333,10 +341,12 @@
   }
 }
 
-#if SB_HAS(DRM_SESSION_CLOSED)
 void DrmSystem::OnSessionClosed(const std::string& session_id) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
+  LOG(INFO) << "Receiving session closed notification from drm system ("
+            << wrapped_drm_system_ << "), session id: " << session_id;
+
   // Find the session by ID.
   IdToSessionMap::iterator session_iterator =
       id_to_session_map_.find(session_id);
@@ -351,13 +361,15 @@
   }
   id_to_session_map_.erase(session_iterator);
 }
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 
-#if SB_API_VERSION >= 10
 void DrmSystem::OnServerCertificateUpdated(int ticket, SbDrmStatus status,
                                            const std::string& error_message) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
+  LOG(INFO) << "Receiving server certificate updated notification from drm"
+            << " system (" << wrapped_drm_system_ << "), ticket: " << ticket
+            << ", status: " << status << ", error message: " << error_message;
+
   auto iter = ticket_to_server_certificate_updated_map_.find(ticket);
   if (iter == ticket_to_server_certificate_updated_map_.end()) {
     LOG(ERROR) << "Unknown ticket: " << ticket << ".";
@@ -366,25 +378,13 @@
   iter->second.Run(status, error_message);
   ticket_to_server_certificate_updated_map_.erase(iter);
 }
-#endif  // SB_API_VERSION >= 10
 
 // static
-#if SB_API_VERSION >= 10
 void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
     SbDrmSystem wrapped_drm_system, void* context, int ticket,
     SbDrmStatus status, SbDrmSessionRequestType type, const char* error_message,
     const void* session_id, int session_id_size, const void* content,
     int content_size, const char* url) {
-#else   // SB_API_VERSION >= 10
-void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
-    SbDrmSystem wrapped_drm_system, void* context, int ticket,
-    const void* session_id, int session_id_size, const void* content,
-    int content_size, const char* url) {
-  SbDrmStatus status =
-      session_id ? kSbDrmStatusSuccess : kSbDrmStatusUnknownError;
-  SbDrmSessionRequestType type = kSbDrmSessionRequestTypeLicenseRequest;
-  const char* error_message = NULL;
-#endif  // SB_API_VERSION >= 10
   DCHECK(context);
   DrmSystem* drm_system = static_cast<DrmSystem*>(context);
   DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
@@ -410,22 +410,12 @@
 }
 
 // static
-#if SB_API_VERSION >= 10
 void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
                                      void* context, int ticket,
                                      SbDrmStatus status,
                                      const char* error_message,
                                      const void* /*session_id*/,
                                      int /*session_id_size*/) {
-#else   // SB_API_VERSION >= 10
-void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
-                                     void* context, int ticket,
-                                     const void* /*session_id*/,
-                                     int /*session_id_size*/, bool succeeded) {
-  SbDrmStatus status =
-      succeeded ? kSbDrmStatusSuccess : kSbDrmStatusUnknownError;
-  const char* error_message = NULL;
-#endif  // SB_API_VERSION >= 10
   DCHECK(context);
   DrmSystem* drm_system = static_cast<DrmSystem*>(context);
   DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
@@ -468,7 +458,6 @@
                  session_id_copy, key_ids_copy, key_statuses_copy));
 }
 
-#if SB_API_VERSION >= 10
 // static
 void DrmSystem::OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system,
                                                void* context, int ticket,
@@ -483,9 +472,7 @@
                             drm_system->weak_this_, ticket, status,
                             error_message ? std::string(error_message) : ""));
 }
-#endif  // SB_API_VERSION >= 10
 
-#if SB_HAS(DRM_SESSION_CLOSED)
 // static
 void DrmSystem::OnSessionClosedFunc(SbDrmSystem wrapped_drm_system,
                                     void* context, const void* session_id,
@@ -504,7 +491,6 @@
       FROM_HERE, base::Bind(&DrmSystem::OnSessionClosed, drm_system->weak_this_,
                             session_id_copy));
 }
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index a80bf34..df7ae93 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -50,9 +50,7 @@
   typedef base::Callback<void(const std::vector<std::string>& key_ids,
                               const std::vector<SbDrmKeyStatus>& key_statuses)>
       SessionUpdateKeyStatusesCallback;
-#if SB_HAS(DRM_SESSION_CLOSED)
   typedef base::Callback<void()> SessionClosedCallback;
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
   typedef base::Callback<void(SbDrmStatus status,
                               const std::string& error_message)>
       ServerCertificateUpdatedCallback;
@@ -99,14 +97,9 @@
 
    private:
     // Private API for |DrmSystem|.
-    Session(DrmSystem* drm_system
-            ,
-            SessionUpdateKeyStatusesCallback update_key_statuses_callback
-#if SB_HAS(DRM_SESSION_CLOSED)
-            ,
-            SessionClosedCallback session_closed_callback
-#endif          // SB_HAS(SESSION_CLOSED)
-            );  // NOLINT(whitespace/parens)
+    Session(DrmSystem* drm_system,
+            SessionUpdateKeyStatusesCallback update_key_statuses_callback,
+            SessionClosedCallback session_closed_callback);
     void set_id(const std::string& id) { id_ = id; }
     const SessionUpdateRequestGeneratedCallback&
     update_request_generated_callback() const {
@@ -116,17 +109,13 @@
         const {
       return update_key_statuses_callback_;
     }
-#if SB_HAS(DRM_SESSION_CLOSED)
     const SessionClosedCallback& session_closed_callback() const {
       return session_closed_callback_;
     }
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 
     DrmSystem* const drm_system_;
     SessionUpdateKeyStatusesCallback update_key_statuses_callback_;
-#if SB_HAS(DRM_SESSION_CLOSED)
     SessionClosedCallback session_closed_callback_;
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
     bool closed_;
     base::Optional<std::string> id_;
     // Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|.
@@ -147,12 +136,8 @@
   bool is_valid() const { return SbDrmSystemIsValid(wrapped_drm_system_); }
 
   std::unique_ptr<Session> CreateSession(
-      SessionUpdateKeyStatusesCallback session_update_key_statuses_callback
-#if SB_HAS(DRM_SESSION_CLOSED)
-      ,
-      SessionClosedCallback session_closed_callback
-#endif    // SB_HAS(DRM_SESSION_CLOSED)
-      );  // NOLINT(whitespace/parens)
+      SessionUpdateKeyStatusesCallback session_update_key_statuses_callback,
+      SessionClosedCallback session_closed_callback);
 
   bool IsServerCertificateUpdatable();
   void UpdateServerCertificate(
@@ -213,16 +198,11 @@
   void OnSessionKeyStatusChanged(
       const std::string& session_id, const std::vector<std::string>& key_ids,
       const std::vector<SbDrmKeyStatus>& key_statuses);
-#if SB_API_VERSION >= 10
   void OnServerCertificateUpdated(int ticket, SbDrmStatus status,
                                   const std::string& error_message);
-#endif  // SB_API_VERSION >= 10
-#if SB_HAS(DRM_SESSION_CLOSED)
   void OnSessionClosed(const std::string& session_id);
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
-// Called on any thread, parameters need to be copied immediately.
 
-#if SB_API_VERSION >= 10
+  // Called on any thread, parameters need to be copied immediately.
   static void OnSessionUpdateRequestGeneratedFunc(
       SbDrmSystem wrapped_drm_system, void* context, int ticket,
       SbDrmStatus status, SbDrmSessionRequestType type,
@@ -234,33 +214,19 @@
                                    const char* error_message,
                                    const void* session_id,
                                    int session_id_length);
-#else   // SB_API_VERSION >= 10
-  static void OnSessionUpdateRequestGeneratedFunc(
-      SbDrmSystem wrapped_drm_system, void* context, int ticket,
-      const void* session_id, int session_id_size, const void* content,
-      int content_size, const char* url);
-  static void OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
-                                   void* context, int ticket,
-                                   const void* session_id,
-                                   int session_id_length, bool succeeded);
-#endif  // SB_API_VERSION >= 10
 
   static void OnSessionKeyStatusesChangedFunc(
       SbDrmSystem wrapped_drm_system, void* context, const void* session_id,
       int session_id_size, int number_of_keys, const SbDrmKeyId* key_ids,
       const SbDrmKeyStatus* key_statuses);
 
-#if SB_HAS(DRM_SESSION_CLOSED)
   static void OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, void* context,
                                   const void* session_id, int session_id_size);
-#endif  // SB_HAS(DRM_SESSION_CLOSED)
 
-#if SB_API_VERSION >= 10
   static void OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system,
                                              void* context, int ticket,
                                              SbDrmStatus status,
                                              const char* error_message);
-#endif  // SB_API_VERSION >= 10
 
   const SbDrmSystem wrapped_drm_system_;
   scoped_refptr<base::SingleThreadTaskRunner> const message_loop_;
diff --git a/src/cobalt/media/base/fake_demuxer_stream.cc b/src/cobalt/media/base/fake_demuxer_stream.cc
index 5a13d0f..e9eaf41 100644
--- a/src/cobalt/media/base/fake_demuxer_stream.cc
+++ b/src/cobalt/media/base/fake_demuxer_stream.cc
@@ -15,6 +15,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/bind_to_current_loop.h"
 #include "cobalt/media/base/decoder_buffer.h"
 #include "cobalt/media/base/decrypt_config.h"
@@ -23,8 +25,6 @@
 #include "cobalt/media/base/timestamp_constants.h"
 #include "cobalt/media/base/video_frame.h"
 #include "starboard/types.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -64,7 +64,7 @@
   current_timestamp_ = base::TimeDelta::FromMilliseconds(kStartTimestampMs);
   duration_ = base::TimeDelta::FromMilliseconds(kDurationMs);
   splice_timestamp_ = kNoTimestamp;
-  next_coded_size_ = gfx::Size(kStartWidth, kStartHeight);
+  next_coded_size_ = math::Size(kStartWidth, kStartHeight);
   next_read_num_ = 0;
 }
 
@@ -159,7 +159,7 @@
 }
 
 void FakeDemuxerStream::UpdateVideoDecoderConfig() {
-  const gfx::Rect kVisibleRect(kStartWidth, kStartHeight);
+  const math::Rect kVisibleRect(kStartWidth, kStartHeight);
   video_decoder_config_.Initialize(
       kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12,
       COLOR_SPACE_UNSPECIFIED, next_coded_size_, kVisibleRect, next_coded_size_,
diff --git a/src/cobalt/media/base/fake_demuxer_stream.h b/src/cobalt/media/base/fake_demuxer_stream.h
index 0a4f671..d50e7f6 100644
--- a/src/cobalt/media/base/fake_demuxer_stream.h
+++ b/src/cobalt/media/base/fake_demuxer_stream.h
@@ -96,7 +96,7 @@
   base::TimeDelta duration_;
   base::TimeDelta splice_timestamp_;
 
-  gfx::Size next_coded_size_;
+  math::Size next_coded_size_;
   VideoDecoderConfig video_decoder_config_;
 
   ReadCB read_cb_;
diff --git a/src/cobalt/media/base/mock_filters.h b/src/cobalt/media/base/mock_filters.h
index 2711a8a..067dbd3 100644
--- a/src/cobalt/media/base/mock_filters.h
+++ b/src/cobalt/media/base/mock_filters.h
@@ -50,7 +50,7 @@
   MOCK_METHOD2(OnAddTextTrack,
                void(const TextTrackConfig&, const AddTextTrackDoneCB&));
   MOCK_METHOD0(OnWaitingForDecryptionKey, void());
-  MOCK_METHOD1(OnVideoNaturalSizeChange, void(const gfx::Size&));
+  MOCK_METHOD1(OnVideoNaturalSizeChange, void(const math::Size&));
   MOCK_METHOD1(OnVideoOpacityChange, void(bool));
 };
 
@@ -214,7 +214,7 @@
   MOCK_METHOD1(OnStatisticsUpdate, void(const PipelineStatistics&));
   MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
   MOCK_METHOD0(OnWaitingForDecryptionKey, void());
-  MOCK_METHOD1(OnVideoNaturalSizeChange, void(const gfx::Size&));
+  MOCK_METHOD1(OnVideoNaturalSizeChange, void(const math::Size&));
   MOCK_METHOD1(OnVideoOpacityChange, void(bool));
   MOCK_METHOD1(OnDurationChange, void(base::TimeDelta));
 };
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index 4268acd..739b302 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -21,6 +21,8 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/demuxer.h"
 #include "cobalt/media/base/media_export.h"
 #include "cobalt/media/base/pipeline_status.h"
@@ -28,8 +30,6 @@
 #include "cobalt/media/base/video_frame_provider.h"
 #include "starboard/drm.h"
 #include "starboard/window.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -56,7 +56,7 @@
 
   // Return true if the punch through box should be rendered.  Return false if
   // no punch through box should be rendered.
-  typedef base::Callback<bool(const gfx::Rect&)> SetBoundsCB;
+  typedef base::Callback<bool(const math::Rect&)> SetBoundsCB;
 
   // Call to get the SbDecodeTargetGraphicsContextProvider for SbPlayerCreate().
   typedef base::Callback<SbDecodeTargetGraphicsContextProvider*()>
@@ -202,7 +202,7 @@
   // Gets the natural size of the video output in pixel units.  If there is no
   // video or the video has not been rendered yet, the width and height will
   // be 0.
-  virtual void GetNaturalVideoSize(gfx::Size* out_size) const = 0;
+  virtual void GetNaturalVideoSize(math::Size* out_size) const = 0;
 
   // Return true if loading progress has been made since the last time this
   // method was called.
diff --git a/src/cobalt/media/base/pipeline_metadata.h b/src/cobalt/media/base/pipeline_metadata.h
index a90e0a3..87f4791 100644
--- a/src/cobalt/media/base/pipeline_metadata.h
+++ b/src/cobalt/media/base/pipeline_metadata.h
@@ -6,8 +6,8 @@
 #define COBALT_MEDIA_BASE_PIPELINE_METADATA_H_
 
 #include "base/time/time.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/video_rotation.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -19,7 +19,7 @@
 
   bool has_audio;
   bool has_video;
-  gfx::Size natural_size;
+  math::Size natural_size;
   VideoRotation video_rotation;
   base::Time timeline_offset;
 };
diff --git a/src/cobalt/media/base/renderer_client.h b/src/cobalt/media/base/renderer_client.h
index 5af89b0..4fe4919 100644
--- a/src/cobalt/media/base/renderer_client.h
+++ b/src/cobalt/media/base/renderer_client.h
@@ -6,8 +6,8 @@
 #define COBALT_MEDIA_BASE_RENDERER_CLIENT_H_
 
 #include "base/time/time.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/pipeline_status.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -33,7 +33,7 @@
 
   // Executed for the first video frame and whenever natural size changes.
   // Only used if media stream contains video track.
-  virtual void OnVideoNaturalSizeChange(const gfx::Size& size) = 0;
+  virtual void OnVideoNaturalSizeChange(const math::Size& size) = 0;
 
   // Executed for the first video frame and whenever opacity changes.
   // Only used if media stream contains video track.
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 32bad7e..b848c56 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -26,6 +26,7 @@
 #include "base/task_runner.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/audio_decoder_config.h"
 #include "cobalt/media/base/bind_to_current_loop.h"
 #include "cobalt/media/base/channel_layout.h"
@@ -41,7 +42,6 @@
 #include "cobalt/media/base/starboard_player.h"
 #include "cobalt/media/base/video_decoder_config.h"
 #include "starboard/configuration_constants.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -126,7 +126,7 @@
 #if SB_HAS(PLAYER_WITH_URL)
   TimeDelta GetMediaStartDate() const override;
 #endif  // SB_HAS(PLAYER_WITH_URL)
-  void GetNaturalVideoSize(gfx::Size* out_size) const override;
+  void GetNaturalVideoSize(math::Size* out_size) const override;
 
   bool DidLoadingProgress() const override;
   PipelineStatistics GetStatistics() const override;
@@ -163,9 +163,7 @@
   // StarboardPlayer::Host implementation.
   void OnNeedData(DemuxerStream::Type type) override;
   void OnPlayerStatus(SbPlayerState state) override;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   void OnPlayerError(SbPlayerError error, const std::string& message) override;
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
   // Used to make a delayed call to OnNeedData() if |audio_read_delayed_| is
   // true. If |audio_read_delayed_| is false, that means the delayed call has
@@ -207,7 +205,7 @@
   mutable bool did_loading_progress_;
 
   // Video's natural width and height.  Set by filters.
-  gfx::Size natural_size_;
+  math::Size natural_size_;
 
   // Current volume level (from 0.0f to 1.0f).  This value is set immediately
   // via SetVolume() and a task is dispatched on the message loop to notify the
@@ -605,7 +603,7 @@
     player_->GetVideoResolution(&frame_width, &frame_height);
     if (frame_width != natural_size_.width() ||
         frame_height != natural_size_.height()) {
-      natural_size_ = gfx::Size(frame_width, frame_height);
+      natural_size_ = math::Size(frame_width, frame_height);
       content_size_change_cb_.Run();
     }
   }
@@ -677,7 +675,7 @@
 }
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
-void SbPlayerPipeline::GetNaturalVideoSize(gfx::Size* out_size) const {
+void SbPlayerPipeline::GetNaturalVideoSize(math::Size* out_size) const {
   CHECK(out_size);
   base::AutoLock auto_lock(lock_);
   *out_size = natural_size_;
@@ -969,32 +967,6 @@
   DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
   DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
 
-#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
-    defined(SB_HAS_AUDIOLESS_VIDEO)
-  if (!kSbHasAudiolessVideo) {
-#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // defined(SB_HAS_AUDIOLESS_VIDEO)
-    if (audio_stream == NULL) {
-      LOG(INFO) << "The video has to contain an audio track.";
-      ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
-                           "The video has to contain an audio track.");
-      return;
-    }
-#if SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION || \
-    defined(SB_HAS_AUDIOLESS_VIDEO)
-  }
-#endif  // SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION ||
-        // defined(SB_HAS_AUDIOLESS_VIDEO)
-
-#if SB_API_VERSION < 10
-  if (video_stream == NULL) {
-    LOG(INFO) << "The video has to contain a video track.";
-    ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
-                         "The video has to contain a video track.");
-    return;
-  }
-#endif  // SB_API_VERSION < 10
-
   if (audio_stream == NULL && video_stream == NULL) {
     LOG(INFO) << "The video has to contain an audio track or a video track.";
     ResetAndRunIfNotNull(
@@ -1200,7 +1172,7 @@
         player_->GetVideoResolution(&frame_width, &frame_height);
         bool natural_size_changed = (frame_width != natural_size_.width() ||
                                      frame_height != natural_size_.height());
-        natural_size_ = gfx::Size(frame_width, frame_height);
+        natural_size_ = math::Size(frame_width, frame_height);
         if (natural_size_changed) {
           content_size_change_cb_.Run();
         }
@@ -1218,16 +1190,9 @@
       break;
     case kSbPlayerStateDestroyed:
       break;
-#if !SB_HAS(PLAYER_ERROR_MESSAGE)
-    case kSbPlayerStateError:
-      ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE,
-                           "Pipeline player state error.");
-      break;
-#endif  // !SB_HAS(PLAYER_ERROR_MESSAGE)
   }
 }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 void SbPlayerPipeline::OnPlayerError(SbPlayerError error,
                                      const std::string& message) {
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -1256,11 +1221,9 @@
     case kSbPlayerErrorDecode:
       ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE, message);
       break;
-#if SB_API_VERSION >= 10
     case kSbPlayerErrorCapabilityChanged:
       ResetAndRunIfNotNull(&error_cb_, PLAYBACK_CAPABILITY_CHANGED, message);
       break;
-#endif  // SB_API_VERSION >= 10
 #if SB_API_VERSION >= 11
     case kSbPlayerErrorMax:
       NOTREACHED();
@@ -1268,7 +1231,6 @@
 #endif  // SB_API_VERSION >= 11
   }
 }
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 void SbPlayerPipeline::DelayedNeedData() {
   DCHECK(task_runner_->BelongsToCurrentThread());
diff --git a/src/cobalt/media/base/sbplayer_set_bounds_helper.cc b/src/cobalt/media/base/sbplayer_set_bounds_helper.cc
index 1783cd5..e561b13 100644
--- a/src/cobalt/media/base/sbplayer_set_bounds_helper.cc
+++ b/src/cobalt/media/base/sbplayer_set_bounds_helper.cc
@@ -36,7 +36,7 @@
   }
 }
 
-bool SbPlayerSetBoundsHelper::SetBounds(const gfx::Rect& rect) {
+bool SbPlayerSetBoundsHelper::SetBounds(const math::Rect& rect) {
   base::AutoLock auto_lock(lock_);
   rect_ = rect;
   if (player_) {
diff --git a/src/cobalt/media/base/sbplayer_set_bounds_helper.h b/src/cobalt/media/base/sbplayer_set_bounds_helper.h
index 074ff7b..caee2bc 100644
--- a/src/cobalt/media/base/sbplayer_set_bounds_helper.h
+++ b/src/cobalt/media/base/sbplayer_set_bounds_helper.h
@@ -18,7 +18,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
-#include "ui/gfx/rect.h"
+#include "cobalt/math/rect.h"
 
 namespace cobalt {
 namespace media {
@@ -31,12 +31,12 @@
   SbPlayerSetBoundsHelper() {}
 
   void SetPlayer(StarboardPlayer* player);
-  bool SetBounds(const gfx::Rect& rect);
+  bool SetBounds(const math::Rect& rect);
 
  private:
   base::Lock lock_;
   StarboardPlayer* player_ = nullptr;
-  base::Optional<gfx::Rect> rect_;
+  base::Optional<math::Rect> rect_;
 
   DISALLOW_COPY_AND_ASSIGN(SbPlayerSetBoundsHelper);
 };
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 987edbc..bf50269 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -56,7 +56,6 @@
   }
 }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 void StarboardPlayer::CallbackHelper::OnPlayerError(
     SbPlayer player, SbPlayerError error, const std::string& message) {
   base::AutoLock auto_lock(lock_);
@@ -64,7 +63,6 @@
     player_->OnPlayerError(player, error, message);
   }
 }
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 void StarboardPlayer::CallbackHelper::OnDeallocateSample(
     const void* sample_buffer) {
@@ -245,7 +243,7 @@
   WriteBufferInternal(type, buffer);
 }
 
-void StarboardPlayer::SetBounds(int z_index, const gfx::Rect& rect) {
+void StarboardPlayer::SetBounds(int z_index, const math::Rect& rect) {
   base::AutoLock auto_lock(lock_);
 
   set_bounds_z_index_ = z_index;
@@ -291,12 +289,7 @@
   DCHECK(SbPlayerIsValid(player_));
 
   ++ticket_;
-#if SB_API_VERSION < 10
-  SbPlayerSeek(player_, SB_TIME_TO_SB_MEDIA_TIME(time.InMicroseconds()),
-               ticket_);
-#else  // SB_API_VERSION < 10
   SbPlayerSeek2(player_, time.InMicroseconds(), ticket_);
-#endif  // SB_API_VERSION < 10
 
   SbPlayerSetPlaybackRate(player_, playback_rate_);
 }
@@ -582,9 +575,6 @@
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
   player_ = SbPlayerCreate(
       window_, video_codec, audio_codec,
-#if SB_API_VERSION < 10
-      SB_PLAYER_NO_DURATION,
-#endif  // SB_API_VERSION < 10
       drm_system_, has_audio ? &audio_sample_info_ : NULL,
 #if SB_API_VERSION >= 11
       max_video_capabilities_.length() > 0 ? max_video_capabilities_.c_str()
@@ -592,9 +582,7 @@
 #endif  // SB_API_VERSION >= 11
       &StarboardPlayer::DeallocateSampleCB, &StarboardPlayer::DecoderStatusCB,
       &StarboardPlayer::PlayerStatusCB,
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
       &StarboardPlayer::PlayerErrorCB,
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
       this, output_mode_,
       get_decode_target_graphics_context_provider_func_.Run());
 
@@ -695,15 +683,6 @@
   SbPlayerWriteSample2(player_, sample_type, &sample_info, 1);
 #else  // SB_API_VERSION >= 11
   video_sample_info_.is_key_frame = buffer->is_key_frame();
-#if SB_API_VERSION < 10
-  SbPlayerWriteSample(
-      player_, sample_type,
-      allocations.buffers(), allocations.buffer_sizes(),
-      allocations.number_of_buffers(),
-      SB_TIME_TO_SB_MEDIA_TIME(buffer->timestamp().InMicroseconds()),
-      type == DemuxerStream::VIDEO ? &video_sample_info_ : NULL,
-      drm_info.subsample_count > 0 ? &drm_info : NULL);
-#else   // SB_API_VERSION < 10
   DCHECK_GT(SbPlayerGetMaximumNumberOfSamplesPerWrite(player_, sample_type), 0);
   SbPlayerSampleInfo sample_info = {
       allocations.buffers()[0], allocations.buffer_sizes()[0],
@@ -711,7 +690,6 @@
       type == DemuxerStream::VIDEO ? &video_sample_info_ : NULL,
       drm_info.subsample_count > 0 ? &drm_info : NULL};
   SbPlayerWriteSample2(player_, sample_type, &sample_info, 1);
-#endif  // SB_API_VERSION < 10
 #endif  // SB_API_VERSION >= 11
 }
 
@@ -742,14 +720,6 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
-#if SB_API_VERSION < 10
-  SbPlayerInfo info;
-  SbPlayerGetInfo(player_, &info);
-  if (media_time) {
-    *media_time = base::TimeDelta::FromMicroseconds(
-        SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts));
-  }
-#else   // SB_API_VERSION < 10
   SbPlayerInfo2 info;
   SbPlayerGetInfo2(player_, &info);
 
@@ -757,7 +727,6 @@
     *media_time =
         base::TimeDelta::FromMicroseconds(info.current_media_timestamp);
   }
-#endif  // SB_API_VERSION < 10
   if (video_frames_decoded) {
     *video_frames_decoded = info.total_video_frames;
   }
@@ -852,13 +821,7 @@
     if (ticket_ == SB_PLAYER_INITIAL_TICKET) {
       ++ticket_;
     }
-#if SB_API_VERSION < 10
-    SbPlayerSeek(player_,
-                 SB_TIME_TO_SB_MEDIA_TIME(preroll_timestamp_.InMicroseconds()),
-                 ticket_);
-#else   // SB_API_VERSION < 10
     SbPlayerSeek2(player_, preroll_timestamp_.InMicroseconds(), ticket_);
-#endif  // SB_API_VERSION < 10
     SetVolume(volume_);
     SbPlayerSetPlaybackRate(player_, playback_rate_);
     return;
@@ -866,7 +829,6 @@
   host_->OnPlayerStatus(state);
 }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 void StarboardPlayer::OnPlayerError(SbPlayer player, SbPlayerError error,
                                     const std::string& message) {
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -876,7 +838,6 @@
   }
   host_->OnPlayerError(error, message);
 }
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 void StarboardPlayer::OnDeallocateSample(const void* sample_buffer) {
 #if SB_HAS(PLAYER_WITH_URL)
@@ -917,7 +878,6 @@
                             helper->callback_helper_, player, state, ticket));
 }
 
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
 // static
 void StarboardPlayer::PlayerErrorCB(SbPlayer player, void* context,
                                     SbPlayerError error, const char* message) {
@@ -927,7 +887,6 @@
                             helper->callback_helper_, player, error,
                             message ? std::string(message) : ""));
 }
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
 // static
 void StarboardPlayer::DeallocateSampleCB(SbPlayer player, void* context,
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index 6c824fd..2b00770 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -47,10 +47,8 @@
    public:
     virtual void OnNeedData(DemuxerStream::Type type) = 0;
     virtual void OnPlayerStatus(SbPlayerState state) = 0;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     virtual void OnPlayerError(SbPlayerError error,
                                const std::string& message) = 0;
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
 
    protected:
     ~Host() {}
@@ -96,7 +94,7 @@
   void WriteBuffer(DemuxerStream::Type type,
                    const scoped_refptr<DecoderBuffer>& buffer);
 
-  void SetBounds(int z_index, const gfx::Rect& rect);
+  void SetBounds(int z_index, const math::Rect& rect);
 
   void PrepareForSeek();
   void Seek(base::TimeDelta time);
@@ -139,10 +137,8 @@
     void OnDecoderStatus(SbPlayer player, SbMediaType type,
                          SbPlayerDecoderState state, int ticket);
     void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket);
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
     void OnPlayerError(SbPlayer player, SbPlayerError error,
                        const std::string& message);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
     void OnDeallocateSample(const void* sample_buffer);
 
     void ResetPlayer();
@@ -187,20 +183,16 @@
   void OnDecoderStatus(SbPlayer player, SbMediaType type,
                        SbPlayerDecoderState state, int ticket);
   void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket);
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   void OnPlayerError(SbPlayer player, SbPlayerError error,
                      const std::string& message);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   void OnDeallocateSample(const void* sample_buffer);
 
   static void DecoderStatusCB(SbPlayer player, void* context, SbMediaType type,
                               SbPlayerDecoderState state, int ticket);
   static void PlayerStatusCB(SbPlayer player, void* context,
                              SbPlayerState state, int ticket);
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
   static void PlayerErrorCB(SbPlayer player, void* context, SbPlayerError error,
                             const char* message);
-#endif  // SB_HAS(PLAYER_ERROR_MESSAGE)
   static void DeallocateSampleCB(SbPlayer player, void* context,
                                  const void* sample_buffer);
 
@@ -250,7 +242,7 @@
 
   // Stores the |z_index| and |rect| parameters of the latest SetBounds() call.
   base::Optional<int> set_bounds_z_index_;
-  base::Optional<gfx::Rect> set_bounds_rect_;
+  base::Optional<math::Rect> set_bounds_rect_;
   State state_ = kPlaying;
   SbPlayer player_;
   uint32 cached_video_frames_decoded_;
diff --git a/src/cobalt/media/base/surface_manager.h b/src/cobalt/media/base/surface_manager.h
index c6de496..bfb1592 100644
--- a/src/cobalt/media/base/surface_manager.h
+++ b/src/cobalt/media/base/surface_manager.h
@@ -7,8 +7,8 @@
 
 #include "base/basictypes.h"
 #include "base/callback.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/media_export.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -30,12 +30,12 @@
   // existing surface will be returned. The client should ensure that the
   // previous consumer is no longer using the surface.
   virtual void CreateFullscreenSurface(
-      const gfx::Size& video_natural_size,
+      const math::Size& video_natural_size,
       const SurfaceCreatedCB& surface_created_cb) = 0;
 
   // Call this when the natural size of the fullscreen video changes. The
   // surface will be resized to match the aspect ratio.
-  virtual void NaturalSizeChanged(const gfx::Size& size) = 0;
+  virtual void NaturalSizeChanged(const math::Size& size) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SurfaceManager);
diff --git a/src/cobalt/media/base/test_helpers.cc b/src/cobalt/media/base/test_helpers.cc
index 9d3ede5..b0a3dc0 100644
--- a/src/cobalt/media/base/test_helpers.cc
+++ b/src/cobalt/media/base/test_helpers.cc
@@ -12,12 +12,12 @@
 #include "base/test/test_timeouts.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "cobalt/math/rect.h"
 #include "cobalt/media/base/audio_buffer.h"
 #include "cobalt/media/base/bind_to_current_loop.h"
 #include "cobalt/media/base/decoder_buffer.h"
 #include "cobalt/media/base/media_util.h"
 #include "starboard/types.h"
-#include "ui/gfx/rect.h"
 
 using ::testing::_;
 using ::testing::StrictMock;
@@ -126,10 +126,10 @@
   run_loop_->Quit();
 }
 
-static VideoDecoderConfig GetTestConfig(VideoCodec codec, gfx::Size coded_size,
+static VideoDecoderConfig GetTestConfig(VideoCodec codec, math::Size coded_size,
                                         bool is_encrypted) {
-  gfx::Rect visible_rect(coded_size.width(), coded_size.height());
-  gfx::Size natural_size = coded_size;
+  math::Rect visible_rect(coded_size.width(), coded_size.height());
+  math::Size natural_size = coded_size;
 
   return VideoDecoderConfig(
       codec, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12,
@@ -138,8 +138,8 @@
       is_encrypted ? AesCtrEncryptionScheme() : Unencrypted());
 }
 
-static const gfx::Size kNormalSize(320, 240);
-static const gfx::Size kLargeSize(640, 480);
+static const math::Size kNormalSize(320, 240);
+static const math::Size kLargeSize(640, 480);
 
 // static
 VideoDecoderConfig TestVideoConfig::Invalid() {
@@ -167,10 +167,10 @@
 }
 
 // static
-gfx::Size TestVideoConfig::NormalCodedSize() { return kNormalSize; }
+math::Size TestVideoConfig::NormalCodedSize() { return kNormalSize; }
 
 // static
-gfx::Size TestVideoConfig::LargeCodedSize() { return kLargeSize; }
+math::Size TestVideoConfig::LargeCodedSize() { return kLargeSize; }
 
 // static
 AudioParameters TestAudioParameters::Normal() {
diff --git a/src/cobalt/media/base/test_helpers.h b/src/cobalt/media/base/test_helpers.h
index d107ad3..f152d29 100644
--- a/src/cobalt/media/base/test_helpers.h
+++ b/src/cobalt/media/base/test_helpers.h
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/non_thread_safe.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/audio_parameters.h"
 #include "cobalt/media/base/channel_layout.h"
 #include "cobalt/media/base/media_log.h"
@@ -21,7 +22,6 @@
 #include "cobalt/media/base/video_decoder_config.h"
 #include "starboard/types.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "ui/gfx/size.h"
 
 namespace base {
 class RunLoop;
@@ -93,8 +93,8 @@
   static VideoDecoderConfig LargeEncrypted();
 
   // Returns coded size for Normal and Large config.
-  static gfx::Size NormalCodedSize();
-  static gfx::Size LargeCodedSize();
+  static math::Size NormalCodedSize();
+  static math::Size LargeCodedSize();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestVideoConfig);
diff --git a/src/cobalt/media/base/video_decoder.h b/src/cobalt/media/base/video_decoder.h
index 570e034..2b98f55 100644
--- a/src/cobalt/media/base/video_decoder.h
+++ b/src/cobalt/media/base/video_decoder.h
@@ -10,10 +10,10 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/decode_status.h"
 #include "cobalt/media/base/media_export.h"
 #include "cobalt/media/base/pipeline_status.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
diff --git a/src/cobalt/media/base/video_decoder_config.cc b/src/cobalt/media/base/video_decoder_config.cc
index d399d36..dcdd75d 100644
--- a/src/cobalt/media/base/video_decoder_config.cc
+++ b/src/cobalt/media/base/video_decoder_config.cc
@@ -62,8 +62,8 @@
 
 VideoDecoderConfig::VideoDecoderConfig(
     VideoCodec codec, VideoCodecProfile profile, VideoPixelFormat format,
-    ColorSpace color_space, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    ColorSpace color_space, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     const std::vector<uint8_t>& extra_data,
     const EncryptionScheme& encryption_scheme) {
   Initialize(codec, profile, format, color_space, coded_size, visible_rect,
@@ -75,9 +75,9 @@
 void VideoDecoderConfig::Initialize(VideoCodec codec, VideoCodecProfile profile,
                                     VideoPixelFormat format,
                                     ColorSpace color_space,
-                                    const gfx::Size& coded_size,
-                                    const gfx::Rect& visible_rect,
-                                    const gfx::Size& natural_size,
+                                    const math::Size& coded_size,
+                                    const math::Rect& visible_rect,
+                                    const math::Size& natural_size,
                                     const std::vector<uint8_t>& extra_data,
                                     const EncryptionScheme& encryption_scheme) {
   codec_ = codec;
diff --git a/src/cobalt/media/base/video_decoder_config.h b/src/cobalt/media/base/video_decoder_config.h
index ae8cf34..f78920c 100644
--- a/src/cobalt/media/base/video_decoder_config.h
+++ b/src/cobalt/media/base/video_decoder_config.h
@@ -10,6 +10,8 @@
 
 #include "base/basictypes.h"
 #include "base/optional.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/color_space.h"
 #include "cobalt/media/base/encryption_scheme.h"
 #include "cobalt/media/base/hdr_metadata.h"
@@ -18,8 +20,6 @@
 #include "cobalt/media/base/video_types.h"
 #include "cobalt/media/formats/webm/webm_colour_parser.h"
 #include "starboard/types.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -37,8 +37,9 @@
   // |extra_data|, otherwise the memory is copied.
   VideoDecoderConfig(VideoCodec codec, VideoCodecProfile profile,
                      VideoPixelFormat format, ColorSpace color_space,
-                     const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-                     const gfx::Size& natural_size,
+                     const math::Size& coded_size,
+                     const math::Rect& visible_rect,
+                     const math::Size& natural_size,
                      const std::vector<uint8_t>& extra_data,
                      const EncryptionScheme& encryption_scheme);
 
@@ -47,8 +48,8 @@
   // Resets the internal state of this object.
   void Initialize(VideoCodec codec, VideoCodecProfile profile,
                   VideoPixelFormat format, ColorSpace color_space,
-                  const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-                  const gfx::Size& natural_size,
+                  const math::Size& coded_size, const math::Rect& visible_rect,
+                  const math::Size& natural_size,
                   const std::vector<uint8_t>& extra_data,
                   const EncryptionScheme& encryption_scheme);
 
@@ -81,14 +82,14 @@
 
   // Width and height of video frame immediately post-decode. Not all pixels
   // in this region are valid.
-  gfx::Size coded_size() const { return coded_size_; }
+  math::Size coded_size() const { return coded_size_; }
 
   // Region of |coded_size_| that is visible.
-  gfx::Rect visible_rect() const { return visible_rect_; }
+  math::Rect visible_rect() const { return visible_rect_; }
 
   // Final visible width and height of a video frame with aspect ratio taken
   // into account.
-  gfx::Size natural_size() const { return natural_size_; }
+  math::Size natural_size() const { return natural_size_; }
 
   // Optional byte data required to initialize video decoders, such as H.264
   // AVCC data.
@@ -124,9 +125,9 @@
   // TODO(servolk): Deprecated, use color_space_info_ instead.
   ColorSpace color_space_;
 
-  gfx::Size coded_size_;
-  gfx::Rect visible_rect_;
-  gfx::Size natural_size_;
+  math::Size coded_size_;
+  math::Rect visible_rect_;
+  math::Size natural_size_;
 
   std::vector<uint8_t> extra_data_;
 
diff --git a/src/cobalt/media/base/video_decoder_config_unittest.cc b/src/cobalt/media/base/video_decoder_config_unittest.cc
index 79630ec..5bbc146 100644
--- a/src/cobalt/media/base/video_decoder_config_unittest.cc
+++ b/src/cobalt/media/base/video_decoder_config_unittest.cc
@@ -13,9 +13,9 @@
 namespace media {
 
 static const VideoPixelFormat kVideoFormat = PIXEL_FORMAT_YV12;
-static const gfx::Size kCodedSize(320, 240);
-static const gfx::Rect kVisibleRect(320, 240);
-static const gfx::Size kNaturalSize(320, 240);
+static const math::Size kCodedSize(320, 240);
+static const math::Rect kVisibleRect(320, 240);
+static const math::Size kNaturalSize(320, 240);
 
 TEST(VideoDecoderConfigTest, Invalid_UnsupportedPixelFormat) {
   VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
@@ -26,7 +26,7 @@
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorZero) {
-  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
+  math::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
   VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
                             COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
                             natural_size, EmptyExtraData(), Unencrypted());
@@ -34,7 +34,7 @@
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorZero) {
-  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
+  math::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
   VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
                             COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
                             natural_size, EmptyExtraData(), Unencrypted());
@@ -42,7 +42,7 @@
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorNegative) {
-  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
+  math::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
   VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
                             COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
                             natural_size, EmptyExtraData(), Unencrypted());
@@ -50,7 +50,7 @@
 }
 
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorNegative) {
-  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
+  math::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
   VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
                             COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
                             natural_size, EmptyExtraData(), Unencrypted());
@@ -60,7 +60,7 @@
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorTooLarge) {
   int width = kVisibleRect.size().width();
   int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width);
-  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
+  math::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
   VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
                             COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
                             natural_size, EmptyExtraData(), Unencrypted());
@@ -70,7 +70,7 @@
 TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorTooLarge) {
   // Denominator is large enough that the natural size height will be zero.
   int den = 2 * kVisibleRect.size().width() + 1;
-  gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
+  math::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den);
   EXPECT_EQ(0, natural_size.width());
   VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
                             COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
diff --git a/src/cobalt/media/base/video_frame.cc b/src/cobalt/media/base/video_frame.cc
index 7acd769..af86dda 100644
--- a/src/cobalt/media/base/video_frame.cc
+++ b/src/cobalt/media/base/video_frame.cc
@@ -16,11 +16,11 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
+#include "cobalt/math/geometry/point.h"
 #include "cobalt/media/base/limits.h"
 #include "cobalt/media/base/timestamp_constants.h"
 #include "cobalt/media/base/video_util.h"
 #include "starboard/memory.h"
-#include "ui/gfx/geometry/point.h"
 
 namespace cobalt {
 namespace media {
@@ -97,9 +97,9 @@
 // static
 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
                                StorageType storage_type,
-                               const gfx::Size& coded_size,
-                               const gfx::Rect& visible_rect,
-                               const gfx::Size& natural_size) {
+                               const math::Size& coded_size,
+                               const math::Rect& visible_rect,
+                               const math::Size& natural_size) {
   // Check maximum limits for all formats.
   int coded_size_area = coded_size.GetCheckedArea().ValueOrDefault(INT_MAX);
   int natural_size_area = natural_size.GetCheckedArea().ValueOrDefault(INT_MAX);
@@ -133,19 +133,18 @@
 }
 
 // static
-scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
-                                                  const gfx::Size& coded_size,
-                                                  const gfx::Rect& visible_rect,
-                                                  const gfx::Size& natural_size,
-                                                  base::TimeDelta timestamp) {
+scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
+    base::TimeDelta timestamp) {
   return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
                              timestamp, false);
 }
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::CreateZeroInitializedFrame(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     base::TimeDelta timestamp) {
   return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
                              timestamp, true);
@@ -156,8 +155,8 @@
     VideoPixelFormat format,
     const gpu::MailboxHolder (&mailbox_holders)[kMaxPlanes],
     const ReleaseMailboxCB& mailbox_holder_release_cb,
-    const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-    const gfx::Size& natural_size, base::TimeDelta timestamp) {
+    const math::Size& coded_size, const math::Rect& visible_rect,
+    const math::Size& natural_size, base::TimeDelta timestamp) {
   if (format != PIXEL_FORMAT_ARGB && format != PIXEL_FORMAT_XRGB &&
       format != PIXEL_FORMAT_UYVY && format != PIXEL_FORMAT_NV12 &&
       format != PIXEL_FORMAT_I420) {
@@ -179,9 +178,9 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size, uint8_t* data,
-    size_t data_size, base::TimeDelta timestamp) {
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
+    uint8_t* data, size_t data_size, base::TimeDelta timestamp) {
   return WrapExternalStorage(format, STORAGE_UNOWNED_MEMORY, coded_size,
                              visible_rect, natural_size, data, data_size,
                              timestamp, base::SharedMemory::NULLHandle(), 0);
@@ -189,10 +188,10 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size, uint8_t* data,
-    size_t data_size, base::SharedMemoryHandle handle, size_t data_offset,
-    base::TimeDelta timestamp) {
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
+    uint8_t* data, size_t data_size, base::SharedMemoryHandle handle,
+    size_t data_offset, base::TimeDelta timestamp) {
   return WrapExternalStorage(format, STORAGE_SHMEM, coded_size, visible_rect,
                              natural_size, data, data_size, timestamp, handle,
                              data_offset);
@@ -200,8 +199,8 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     int32_t y_stride, int32_t u_stride, int32_t v_stride, uint8_t* y_data,
     uint8_t* u_data, uint8_t* v_data, base::TimeDelta timestamp) {
   const StorageType storage = STORAGE_UNOWNED_MEMORY;
@@ -225,8 +224,8 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvGpuMemoryBuffers(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     int32_t y_stride, int32_t u_stride, int32_t v_stride, uint8_t* y_data,
     uint8_t* u_data, uint8_t* v_data,
     const gfx::GpuMemoryBufferHandle& y_handle,
@@ -256,8 +255,8 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvaData(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     int32_t y_stride, int32_t u_stride, int32_t v_stride, int32_t a_stride,
     uint8_t* y_data, uint8_t* u_data, uint8_t* v_data, uint8_t* a_data,
     base::TimeDelta timestamp) {
@@ -291,8 +290,8 @@
 #if defined(OS_LINUX)
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     const std::vector<int>& dmabuf_fds, base::TimeDelta timestamp) {
   const StorageType storage = STORAGE_DMABUFS;
   if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
@@ -337,9 +336,9 @@
     return NULL;
   }
 
-  const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
-  const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
-  const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
+  const math::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
+  const math::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
+  const math::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
   const StorageType storage = STORAGE_UNOWNED_MEMORY;
 
   if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
@@ -360,7 +359,7 @@
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
     const scoped_refptr<VideoFrame>& frame, VideoPixelFormat format,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size) {
+    const math::Rect& visible_rect, const math::Size& natural_size) {
   // Frames with textures need mailbox info propagated, and there's no support
   // for that here yet, see http://crbug/362521.
   CHECK(!frame->HasTextures());
@@ -416,24 +415,24 @@
 // static
 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
   scoped_refptr<VideoFrame> frame =
-      new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_UNKNOWN, gfx::Size(),
-                     gfx::Rect(), gfx::Size(), kNoTimestamp);
+      new VideoFrame(PIXEL_FORMAT_UNKNOWN, STORAGE_UNKNOWN, math::Size(),
+                     math::Rect(), math::Size(), kNoTimestamp);
   frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
   return frame;
 }
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
-    const gfx::Size& size, uint8_t y, uint8_t u, uint8_t v,
+    const math::Size& size, uint8_t y, uint8_t u, uint8_t v,
     base::TimeDelta timestamp) {
   scoped_refptr<VideoFrame> frame =
-      CreateFrame(PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, timestamp);
+      CreateFrame(PIXEL_FORMAT_YV12, size, math::Rect(size), size, timestamp);
   FillYUV(frame.get(), y, u, v);
   return frame;
 }
 
 // static
-scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
+scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const math::Size& size) {
   const uint8_t kBlackY = 0x00;
   const uint8_t kBlackUV = 0x80;
   const base::TimeDelta kZero;
@@ -442,13 +441,13 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
-    const gfx::Size& size) {
+    const math::Size& size) {
   const uint8_t kBlackY = 0x00;
   const uint8_t kBlackUV = 0x00;
   const uint8_t kTransparentA = 0x00;
   const base::TimeDelta kZero;
   scoped_refptr<VideoFrame> frame =
-      CreateFrame(PIXEL_FORMAT_YV12A, size, gfx::Rect(size), size, kZero);
+      CreateFrame(PIXEL_FORMAT_YV12A, size, math::Rect(size), size, kZero);
   FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
   return frame;
 }
@@ -495,7 +494,7 @@
 
 // static
 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
-                                  const gfx::Size& coded_size) {
+                                  const math::Size& coded_size) {
   size_t total = 0;
   for (size_t i = 0; i < NumPlanes(format); ++i)
     total += PlaneSize(format, i, coded_size).GetArea();
@@ -503,8 +502,8 @@
 }
 
 // static
-gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format, size_t plane,
-                                const gfx::Size& coded_size) {
+math::Size VideoFrame::PlaneSize(VideoPixelFormat format, size_t plane,
+                                 const math::Size& coded_size) {
   DCHECK(IsValidPlane(plane, format));
 
   int width = coded_size.width();
@@ -517,11 +516,11 @@
     height = RoundUp(height, 2);
   }
 
-  const gfx::Size subsample = SampleSize(format, plane);
+  const math::Size subsample = SampleSize(format, plane);
   DCHECK_EQ(width % subsample.width(), 0);
   DCHECK_EQ(height % subsample.height(), 0);
-  return gfx::Size(BytesPerElement(format, plane) * width / subsample.width(),
-                   height / subsample.height());
+  return math::Size(BytesPerElement(format, plane) * width / subsample.width(),
+                    height / subsample.height());
 }
 
 // static
@@ -638,11 +637,11 @@
   DCHECK(IsMappable());
 
   // Calculate an offset that is properly aligned for all planes.
-  const gfx::Size alignment = CommonAlignment(format_);
+  const math::Size alignment = CommonAlignment(format_);
   const gfx::Point offset(RoundDown(visible_rect_.x(), alignment.width()),
                           RoundDown(visible_rect_.y(), alignment.height()));
 
-  const gfx::Size subsample = SampleSize(format_, plane);
+  const math::Size subsample = SampleSize(format_, plane);
   DCHECK_EQ(offset.x() % subsample.width(), 0);
   DCHECK_EQ(offset.y() % subsample.height(), 0);
   return data(plane) +
@@ -757,8 +756,8 @@
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
     VideoPixelFormat format, StorageType storage_type,
-    const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-    const gfx::Size& natural_size, uint8_t* data, size_t data_size,
+    const math::Size& coded_size, const math::Rect& visible_rect,
+    const math::Size& natural_size, uint8_t* data, size_t data_size,
     base::TimeDelta timestamp, base::SharedMemoryHandle handle,
     size_t data_offset) {
   DCHECK(IsStorageTypeMappable(storage_type));
@@ -801,9 +800,10 @@
 }
 
 VideoFrame::VideoFrame(VideoPixelFormat format, StorageType storage_type,
-                       const gfx::Size& coded_size,
-                       const gfx::Rect& visible_rect,
-                       const gfx::Size& natural_size, base::TimeDelta timestamp)
+                       const math::Size& coded_size,
+                       const math::Rect& visible_rect,
+                       const math::Size& natural_size,
+                       base::TimeDelta timestamp)
     : format_(format),
       storage_type_(storage_type),
       coded_size_(coded_size),
@@ -838,9 +838,9 @@
 // static
 std::string VideoFrame::ConfigToString(const VideoPixelFormat format,
                                        const StorageType storage_type,
-                                       const gfx::Size& coded_size,
-                                       const gfx::Rect& visible_rect,
-                                       const gfx::Size& natural_size) {
+                                       const math::Size& coded_size,
+                                       const math::Rect& visible_rect,
+                                       const math::Size& natural_size) {
   return base::StringPrintf(
       "format:%s storage_type:%s coded_size:%s visible_rect:%s natural_size:%s",
       VideoPixelFormatToString(format).c_str(),
@@ -855,12 +855,12 @@
 }
 
 // static
-gfx::Size VideoFrame::DetermineAlignedSize(VideoPixelFormat format,
-                                           const gfx::Size& dimensions) {
-  const gfx::Size alignment = CommonAlignment(format);
-  const gfx::Size adjusted =
-      gfx::Size(RoundUp(dimensions.width(), alignment.width()),
-                RoundUp(dimensions.height(), alignment.height()));
+math::Size VideoFrame::DetermineAlignedSize(VideoPixelFormat format,
+                                            const math::Size& dimensions) {
+  const math::Size alignment = CommonAlignment(format);
+  const math::Size adjusted =
+      math::Size(RoundUp(dimensions.width(), alignment.width()),
+                 RoundUp(dimensions.height(), alignment.height()));
   DCHECK((adjusted.width() % alignment.width() == 0) &&
          (adjusted.height() % alignment.height() == 0));
   return adjusted;
@@ -879,9 +879,10 @@
 }
 
 VideoFrame::VideoFrame(VideoPixelFormat format, StorageType storage_type,
-                       const gfx::Size& coded_size,
-                       const gfx::Rect& visible_rect,
-                       const gfx::Size& natural_size, base::TimeDelta timestamp,
+                       const math::Size& coded_size,
+                       const math::Rect& visible_rect,
+                       const math::Size& natural_size,
+                       base::TimeDelta timestamp,
                        base::SharedMemoryHandle handle,
                        size_t shared_memory_offset)
     : VideoFrame(format, storage_type, coded_size, visible_rect, natural_size,
@@ -892,9 +893,9 @@
 }
 
 VideoFrame::VideoFrame(VideoPixelFormat format, StorageType storage_type,
-                       const gfx::Size& coded_size,
-                       const gfx::Rect& visible_rect,
-                       const gfx::Size& natural_size,
+                       const math::Size& coded_size,
+                       const math::Rect& visible_rect,
+                       const math::Size& natural_size,
                        const gpu::MailboxHolder (&mailbox_holders)[kMaxPlanes],
                        const ReleaseMailboxCB& mailbox_holder_release_cb,
                        base::TimeDelta timestamp)
@@ -906,8 +907,8 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
-    VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     base::TimeDelta timestamp, bool zero_initialize_memory) {
   if (!IsYuvPlanar(format)) {
     NOTIMPLEMENTED();
@@ -918,7 +919,7 @@
   // ourselves), we can pad the requested |coded_size| if necessary if the
   // request does not line up on sample boundaries. See discussion at
   // http://crrev.com/1240833003
-  const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size);
+  const math::Size new_coded_size = DetermineAlignedSize(format, coded_size);
   const StorageType storage = STORAGE_OWNED_MEMORY;
   if (!IsValidConfig(format, storage, new_coded_size, visible_rect,
                      natural_size)) {
@@ -935,13 +936,13 @@
 }
 
 // static
-gfx::Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) {
+math::Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) {
   DCHECK(IsValidPlane(plane, format));
 
   switch (plane) {
     case kYPlane:  // and kARGBPlane:
     case kAPlane:
-      return gfx::Size(1, 1);
+      return math::Size(1, 1);
 
     case kUPlane:  // and kUVPlane:
     case kVPlane:
@@ -950,13 +951,13 @@
         case PIXEL_FORMAT_YUV444P9:
         case PIXEL_FORMAT_YUV444P10:
         case PIXEL_FORMAT_YUV444P12:
-          return gfx::Size(1, 1);
+          return math::Size(1, 1);
 
         case PIXEL_FORMAT_YV16:
         case PIXEL_FORMAT_YUV422P9:
         case PIXEL_FORMAT_YUV422P10:
         case PIXEL_FORMAT_YUV422P12:
-          return gfx::Size(2, 1);
+          return math::Size(2, 1);
 
         case PIXEL_FORMAT_YV12:
         case PIXEL_FORMAT_I420:
@@ -967,7 +968,7 @@
         case PIXEL_FORMAT_YUV420P9:
         case PIXEL_FORMAT_YUV420P10:
         case PIXEL_FORMAT_YUV420P12:
-          return gfx::Size(2, 2);
+          return math::Size(2, 2);
 
         case PIXEL_FORMAT_UNKNOWN:
         case PIXEL_FORMAT_UYVY:
@@ -983,7 +984,7 @@
       }
   }
   NOTREACHED();
-  return gfx::Size();
+  return math::Size();
 }
 
 // static
@@ -1033,15 +1034,15 @@
 }
 
 // static
-gfx::Size VideoFrame::CommonAlignment(VideoPixelFormat format) {
+math::Size VideoFrame::CommonAlignment(VideoPixelFormat format) {
   int max_sample_width = 0;
   int max_sample_height = 0;
   for (size_t plane = 0; plane < NumPlanes(format); ++plane) {
-    const gfx::Size sample_size = SampleSize(format, plane);
+    const math::Size sample_size = SampleSize(format, plane);
     max_sample_width = std::max(max_sample_width, sample_size.width());
     max_sample_height = std::max(max_sample_height, sample_size.height());
   }
-  return gfx::Size(max_sample_width, max_sample_height);
+  return math::Size(max_sample_width, max_sample_height);
 }
 
 void VideoFrame::AllocateYUV(bool zero_initialize_memory) {
diff --git a/src/cobalt/media/base/video_frame.h b/src/cobalt/media/base/video_frame.h
index 87b79a3..3130185 100644
--- a/src/cobalt/media/base/video_frame.h
+++ b/src/cobalt/media/base/video_frame.h
@@ -15,14 +15,14 @@
 #include "base/memory/shared_memory.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
+#include "cobalt/math/gpu_memory_buffer.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/color_space.h"
 #include "cobalt/media/base/video_frame_metadata.h"
 #include "cobalt/media/base/video_types.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "starboard/types.h"
-#include "ui/gfx/gpu_memory_buffer.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 #if defined(OS_MACOSX)
 #include <CoreVideo/CVPixelBuffer.h>
@@ -94,9 +94,9 @@
   // Call prior to CreateFrame to ensure validity of frame configuration. Called
   // automatically by VideoDecoderConfig::IsValidConfig().
   static bool IsValidConfig(VideoPixelFormat format, StorageType storage_type,
-                            const gfx::Size& coded_size,
-                            const gfx::Rect& visible_rect,
-                            const gfx::Size& natural_size);
+                            const math::Size& coded_size,
+                            const math::Rect& visible_rect,
+                            const math::Size& natural_size);
 
   // Creates a new YUV frame in system memory with given parameters (|format|
   // must be YUV). Buffers for the frame are allocated but not initialized. The
@@ -104,16 +104,16 @@
   // check the returned VideoFrame instead.
   // TODO(mcasas): implement the RGB version of this factory method.
   static scoped_refptr<VideoFrame> CreateFrame(VideoPixelFormat format,
-                                               const gfx::Size& coded_size,
-                                               const gfx::Rect& visible_rect,
-                                               const gfx::Size& natural_size,
+                                               const math::Size& coded_size,
+                                               const math::Rect& visible_rect,
+                                               const math::Size& natural_size,
                                                base::TimeDelta timestamp);
 
   // Offers the same functionality as CreateFrame, and additionally zeroes out
   // the initial allocated buffers.
   static scoped_refptr<VideoFrame> CreateZeroInitializedFrame(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       base::TimeDelta timestamp);
 
   // Wraps a set of native textures with a VideoFrame.
@@ -123,38 +123,38 @@
       VideoPixelFormat format,
       const gpu::MailboxHolder (&mailbox_holder)[kMaxPlanes],
       const ReleaseMailboxCB& mailbox_holders_release_cb,
-      const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-      const gfx::Size& natural_size, base::TimeDelta timestamp);
+      const math::Size& coded_size, const math::Rect& visible_rect,
+      const math::Size& natural_size, base::TimeDelta timestamp);
 
   // Wraps packed image data residing in a memory buffer with a VideoFrame.
   // The image data resides in |data| and is assumed to be packed tightly in a
   // buffer of logical dimensions |coded_size| with the appropriate bit depth
   // and plane count as given by |format|. Returns NULL on failure.
   static scoped_refptr<VideoFrame> WrapExternalData(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       uint8_t* data, size_t data_size, base::TimeDelta timestamp);
 
   // Same as WrapExternalData() with SharedMemoryHandle and its offset.
   static scoped_refptr<VideoFrame> WrapExternalSharedMemory(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       uint8_t* data, size_t data_size, base::SharedMemoryHandle handle,
       size_t shared_memory_offset, base::TimeDelta timestamp);
 
   // Wraps external YUV data of the given parameters with a VideoFrame.
   // The returned VideoFrame does not own the data passed in.
   static scoped_refptr<VideoFrame> WrapExternalYuvData(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       int32_t y_stride, int32_t u_stride, int32_t v_stride, uint8_t* y_data,
       uint8_t* u_data, uint8_t* v_data, base::TimeDelta timestamp);
 
   // Wraps external YUV data with the given parameters with a VideoFrame.
   // The returned VideoFrame does not own the GpuMemoryBuffers passed in.
   static scoped_refptr<VideoFrame> WrapExternalYuvGpuMemoryBuffers(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       int32_t y_stride, int32_t u_stride, int32_t v_stride, uint8_t* y_data,
       uint8_t* u_data, uint8_t* v_data,
       const gfx::GpuMemoryBufferHandle& y_handle,
@@ -164,8 +164,8 @@
   // Wraps external YUVA data of the given parameters with a VideoFrame.
   // The returned VideoFrame does not own the data passed in.
   static scoped_refptr<VideoFrame> WrapExternalYuvaData(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       int32_t y_stride, int32_t u_stride, int32_t v_stride, int32_t a_stride,
       uint8_t* y_data, uint8_t* u_data, uint8_t* v_data, uint8_t* a_data,
       base::TimeDelta timestamp);
@@ -182,8 +182,8 @@
   // mapped via mmap() for CPU access.
   // Returns NULL on failure.
   static scoped_refptr<VideoFrame> WrapExternalDmabufs(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       const std::vector<int>& dmabuf_fds, base::TimeDelta timestamp);
 #endif
 
@@ -204,37 +204,37 @@
   // frame->visible_rect().
   static scoped_refptr<VideoFrame> WrapVideoFrame(
       const scoped_refptr<VideoFrame>& frame, VideoPixelFormat format,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size);
+      const math::Rect& visible_rect, const math::Size& natural_size);
 
   // Creates a frame which indicates end-of-stream.
   static scoped_refptr<VideoFrame> CreateEOSFrame();
 
   // Allocates YV12 frame based on |size|, and sets its data to the YUV(y,u,v).
-  static scoped_refptr<VideoFrame> CreateColorFrame(const gfx::Size& size,
+  static scoped_refptr<VideoFrame> CreateColorFrame(const math::Size& size,
                                                     uint8_t y, uint8_t u,
                                                     uint8_t v,
                                                     base::TimeDelta timestamp);
 
   // Allocates YV12 frame based on |size|, and sets its data to the YUV
   // equivalent of RGB(0,0,0).
-  static scoped_refptr<VideoFrame> CreateBlackFrame(const gfx::Size& size);
+  static scoped_refptr<VideoFrame> CreateBlackFrame(const math::Size& size);
 
   // Allocates YV12A frame based on |size|, and sets its data to the YUVA
   // equivalent of RGBA(0,0,0,0).
   static scoped_refptr<VideoFrame> CreateTransparentFrame(
-      const gfx::Size& size);
+      const math::Size& size);
 
   static size_t NumPlanes(VideoPixelFormat format);
 
   // Returns the required allocation size for a (tightly packed) frame of the
   // given coded size and format.
   static size_t AllocationSize(VideoPixelFormat format,
-                               const gfx::Size& coded_size);
+                               const math::Size& coded_size);
 
-  // Returns the plane gfx::Size (in bytes) for a plane of the given coded size
+  // Returns the plane math::Size (in bytes) for a plane of the given coded size
   // and format.
-  static gfx::Size PlaneSize(VideoPixelFormat format, size_t plane,
-                             const gfx::Size& coded_size);
+  static math::Size PlaneSize(VideoPixelFormat format, size_t plane,
+                              const math::Size& coded_size);
 
   // Returns horizontal bits per pixel for given |plane| and |format|.
   static int PlaneHorizontalBitsPerPixel(VideoPixelFormat format, size_t plane);
@@ -275,9 +275,9 @@
   VideoPixelFormat format() const { return format_; }
   StorageType storage_type() const { return storage_type_; }
 
-  const gfx::Size& coded_size() const { return coded_size_; }
-  const gfx::Rect& visible_rect() const { return visible_rect_; }
-  const gfx::Size& natural_size() const { return natural_size_; }
+  const math::Size& coded_size() const { return coded_size_; }
+  const math::Rect& visible_rect() const { return visible_rect_; }
+  const math::Size& natural_size() const { return natural_size_; }
 
   int stride(size_t plane) const;
 
@@ -378,24 +378,24 @@
   // Derived classes should create their own factory/wrapping methods, and use
   // this constructor to do basic initialization.
   VideoFrame(VideoPixelFormat format, StorageType storage_type,
-             const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-             const gfx::Size& natural_size, base::TimeDelta timestamp);
+             const math::Size& coded_size, const math::Rect& visible_rect,
+             const math::Size& natural_size, base::TimeDelta timestamp);
 
   virtual ~VideoFrame();
 
   // Creates a summary of the configuration settings provided as parameters.
   static std::string ConfigToString(const VideoPixelFormat format,
                                     const VideoFrame::StorageType storage_type,
-                                    const gfx::Size& coded_size,
-                                    const gfx::Rect& visible_rect,
-                                    const gfx::Size& natural_size);
+                                    const math::Size& coded_size,
+                                    const math::Rect& visible_rect,
+                                    const math::Size& natural_size);
 
   // Returns true if |plane| is a valid plane index for the given |format|.
   static bool IsValidPlane(size_t plane, VideoPixelFormat format);
 
   // Returns |dimensions| adjusted to appropriate boundaries based on |format|.
-  static gfx::Size DetermineAlignedSize(VideoPixelFormat format,
-                                        const gfx::Size& dimensions);
+  static math::Size DetermineAlignedSize(VideoPixelFormat format,
+                                         const math::Size& dimensions);
 
   void set_data(size_t plane, uint8_t* ptr);
   void set_stride(size_t plane, int stride);
@@ -403,38 +403,38 @@
  private:
   // Clients must use the static factory/wrapping methods to create a new frame.
   VideoFrame(VideoPixelFormat format, StorageType storage_type,
-             const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-             const gfx::Size& natural_size, base::TimeDelta timestamp,
+             const math::Size& coded_size, const math::Rect& visible_rect,
+             const math::Size& natural_size, base::TimeDelta timestamp,
              base::SharedMemoryHandle handle, size_t shared_memory_offset);
   VideoFrame(VideoPixelFormat format, StorageType storage_type,
-             const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-             const gfx::Size& natural_size,
+             const math::Size& coded_size, const math::Rect& visible_rect,
+             const math::Size& natural_size,
              const gpu::MailboxHolder (&mailbox_holders)[kMaxPlanes],
              const ReleaseMailboxCB& mailbox_holder_release_cb,
              base::TimeDelta timestamp);
 
   static scoped_refptr<VideoFrame> WrapExternalStorage(
       VideoPixelFormat format, StorageType storage_type,
-      const gfx::Size& coded_size, const gfx::Rect& visible_rect,
-      const gfx::Size& natural_size, uint8_t* data, size_t data_size,
+      const math::Size& coded_size, const math::Rect& visible_rect,
+      const math::Size& natural_size, uint8_t* data, size_t data_size,
       base::TimeDelta timestamp, base::SharedMemoryHandle handle,
       size_t data_offset);
 
   static scoped_refptr<VideoFrame> CreateFrameInternal(
-      VideoPixelFormat format, const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+      VideoPixelFormat format, const math::Size& coded_size,
+      const math::Rect& visible_rect, const math::Size& natural_size,
       base::TimeDelta timestamp, bool zero_initialize_memory);
 
   // Returns the pixel size of each subsample for a given |plane| and |format|.
   // E.g. 2x2 for the U-plane in PIXEL_FORMAT_I420.
-  static gfx::Size SampleSize(VideoPixelFormat format, size_t plane);
+  static math::Size SampleSize(VideoPixelFormat format, size_t plane);
 
   // Returns the number of bytes per element for given |plane| and |format|.
   static int BytesPerElement(VideoPixelFormat format, size_t plane);
 
   // Return the alignment for the whole frame, calculated as the max of the
   // alignment for each individual plane.
-  static gfx::Size CommonAlignment(VideoPixelFormat format);
+  static math::Size CommonAlignment(VideoPixelFormat format);
 
   void AllocateYUV(bool zero_initialize_memory);
 
@@ -449,16 +449,16 @@
   // planes, in the case that the visible portion of the image does not line up
   // on a sample boundary, |coded_size_| must be rounded up appropriately and
   // the pixel data provided for the odd pixels.
-  const gfx::Size coded_size_;
+  const math::Size coded_size_;
 
   // Width, height, and offsets of the visible portion of the video frame. Must
   // be a subrect of |coded_size_|. Can be odd with respect to the sample
   // boundaries, e.g. for formats with subsampled chroma.
-  const gfx::Rect visible_rect_;
+  const math::Rect visible_rect_;
 
   // Width and height of the visible portion of the video frame
   // (|visible_rect_.size()|) with aspect ratio taken into account.
-  const gfx::Size natural_size_;
+  const math::Size natural_size_;
 
   // Array of strides for each plane, typically greater or equal to the width
   // of the surface divided by the horizontal sampling period.  Note that
diff --git a/src/cobalt/media/base/video_resolution.h b/src/cobalt/media/base/video_resolution.h
index a207476..9e3c899 100644
--- a/src/cobalt/media/base/video_resolution.h
+++ b/src/cobalt/media/base/video_resolution.h
@@ -17,8 +17,8 @@
 #ifndef COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
 #define COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
 
+#include "cobalt/math/size.h"
 #include "starboard/media.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -46,7 +46,7 @@
   return kVideoResolutionInvalid;
 }
 
-inline VideoResolution GetVideoResolution(const gfx::Size& size) {
+inline VideoResolution GetVideoResolution(const math::Size& size) {
   return GetVideoResolution(size.width(), size.height());
 }
 
diff --git a/src/cobalt/media/base/video_util.cc b/src/cobalt/media/base/video_util.cc
index a6ce644..1877645 100644
--- a/src/cobalt/media/base/video_util.cc
+++ b/src/cobalt/media/base/video_util.cc
@@ -14,18 +14,18 @@
 namespace cobalt {
 namespace media {
 
-gfx::Size GetNaturalSize(const gfx::Size& visible_size,
-                         int aspect_ratio_numerator,
-                         int aspect_ratio_denominator) {
+math::Size GetNaturalSize(const math::Size& visible_size,
+                          int aspect_ratio_numerator,
+                          int aspect_ratio_denominator) {
   if (aspect_ratio_denominator == 0 || aspect_ratio_numerator < 0 ||
       aspect_ratio_denominator < 0)
-    return gfx::Size();
+    return math::Size();
 
   double aspect_ratio =
       aspect_ratio_numerator / static_cast<double>(aspect_ratio_denominator);
 
-  return gfx::Size(round(visible_size.width() * aspect_ratio),
-                   visible_size.height());
+  return math::Size(round(visible_size.width() * aspect_ratio),
+                    visible_size.height());
 }
 
 void RotatePlaneByPixels(const uint8_t* src, uint8_t* dest, int width,
diff --git a/src/cobalt/media/base/video_util.h b/src/cobalt/media/base/video_util.h
index 54fdc3b..9a75ff7 100644
--- a/src/cobalt/media/base/video_util.h
+++ b/src/cobalt/media/base/video_util.h
@@ -6,10 +6,10 @@
 #define COBALT_MEDIA_BASE_VIDEO_UTIL_H_
 
 #include "base/memory/ref_counted.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/media_export.h"
 #include "starboard/types.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -17,9 +17,9 @@
 class VideoFrame;
 
 // Computes the size of |visible_size| for a given aspect ratio.
-MEDIA_EXPORT gfx::Size GetNaturalSize(const gfx::Size& visible_size,
-                                      int aspect_ratio_numerator,
-                                      int aspect_ratio_denominator);
+MEDIA_EXPORT math::Size GetNaturalSize(const math::Size& visible_size,
+                                       int aspect_ratio_numerator,
+                                       int aspect_ratio_denominator);
 
 // Fills |frame| containing YUV data to the given color values.
 MEDIA_EXPORT void FillYUV(VideoFrame* frame, uint8_t y, uint8_t u, uint8_t v);
@@ -32,7 +32,7 @@
 // |view_area| are black. The size and position of |view_area|
 // must be even to align correctly with the color planes.
 // Only YV12 format video frames are currently supported.
-MEDIA_EXPORT void LetterboxYUV(VideoFrame* frame, const gfx::Rect& view_area);
+MEDIA_EXPORT void LetterboxYUV(VideoFrame* frame, const math::Rect& view_area);
 
 // Rotates |src| plane by |rotation| degree with possible flipping vertically
 // and horizontally.
@@ -53,22 +53,22 @@
 // Return the largest centered rectangle with the same aspect ratio of |content|
 // that fits entirely inside of |bounds|.  If |content| is empty, its aspect
 // ratio would be undefined; and in this case an empty Rect would be returned.
-MEDIA_EXPORT gfx::Rect ComputeLetterboxRegion(const gfx::Rect& bounds,
-                                              const gfx::Size& content);
+MEDIA_EXPORT math::Rect ComputeLetterboxRegion(const math::Rect& bounds,
+                                               const math::Size& content);
 
 // Return a scaled |size| whose area is less than or equal to |target|, where
 // one of its dimensions is equal to |target|'s.  The aspect ratio of |size| is
 // preserved as closely as possible.  If |size| is empty, the result will be
 // empty.
-MEDIA_EXPORT gfx::Size ScaleSizeToFitWithinTarget(const gfx::Size& size,
-                                                  const gfx::Size& target);
+MEDIA_EXPORT math::Size ScaleSizeToFitWithinTarget(const math::Size& size,
+                                                   const math::Size& target);
 
 // Return a scaled |size| whose area is greater than or equal to |target|, where
 // one of its dimensions is equal to |target|'s.  The aspect ratio of |size| is
 // preserved as closely as possible.  If |size| is empty, the result will be
 // empty.
-MEDIA_EXPORT gfx::Size ScaleSizeToEncompassTarget(const gfx::Size& size,
-                                                  const gfx::Size& target);
+MEDIA_EXPORT math::Size ScaleSizeToEncompassTarget(const math::Size& size,
+                                                   const math::Size& target);
 
 // Returns |size| with only one of its dimensions increased such that the result
 // matches the aspect ratio of |target|.  This is different from
@@ -76,13 +76,13 @@
 // ratio of |target| rather than that of |size|.  2) Only one of the dimensions
 // of |size| may change, and it may only be increased (padded).  If either
 // |size| or |target| is empty, the result will be empty.
-MEDIA_EXPORT gfx::Size PadToMatchAspectRatio(const gfx::Size& size,
-                                             const gfx::Size& target);
+MEDIA_EXPORT math::Size PadToMatchAspectRatio(const math::Size& size,
+                                              const math::Size& target);
 
 // Copy an RGB bitmap into the specified |region_in_frame| of a YUV video frame.
 // Fills the regions outside |region_in_frame| with black.
 MEDIA_EXPORT void CopyRGBToVideoFrame(const uint8_t* source, int stride,
-                                      const gfx::Rect& region_in_frame,
+                                      const math::Rect& region_in_frame,
                                       VideoFrame* frame);
 
 // Converts a frame with YV12A format into I420 by dropping alpha channel.
diff --git a/src/cobalt/media/base/video_util_unittest.cc b/src/cobalt/media/base/video_util_unittest.cc
index 490af8c..e485aad 100644
--- a/src/cobalt/media/base/video_util_unittest.cc
+++ b/src/cobalt/media/base/video_util_unittest.cc
@@ -16,7 +16,7 @@
 
 // Initialize a plane's visible rect with value circularly from 0 to 255.
 void FillPlaneWithPattern(uint8_t* data, int stride,
-                          const gfx::Size& visible_size) {
+                          const math::Size& visible_size) {
   DCHECK(data && visible_size.width() <= stride);
 
   uint32_t val = 0;
@@ -32,8 +32,8 @@
 // |VideoFrame::CreateColorFrame()| where the entrire VideoFrame is filled
 // with a given color.
 scoped_refptr<media::VideoFrame> CreateFrameWithPatternFilled(
-    media::VideoPixelFormat format, const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+    media::VideoPixelFormat format, const math::Size& coded_size,
+    const math::Rect& visible_rect, const math::Size& natural_size,
     base::TimeDelta timestamp) {
   scoped_refptr<media::VideoFrame> frame(media::VideoFrame::CreateFrame(
       format, coded_size, visible_rect, natural_size, timestamp));
@@ -58,10 +58,10 @@
 // visible region and padding the remaining area.
 bool VerifyPlanCopyWithPadding(const uint8_t* src, size_t src_stride,
                                // Size of visible region.
-                               const gfx::Size& src_size, const uint8_t* dst,
+                               const math::Size& src_size, const uint8_t* dst,
                                size_t dst_stride,
                                // Coded size of |dst|.
-                               const gfx::Size& dst_size) {
+                               const math::Size& dst_size) {
   if (!src || !dst) return false;
 
   const size_t src_width = src_size.width();
@@ -156,9 +156,9 @@
   }
 
   void CreateDestinationFrame(int width, int height) {
-    gfx::Size size(width, height);
+    math::Size size(width, height);
     destination_frame_ = VideoFrame::CreateFrame(
-        PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, base::TimeDelta());
+        PIXEL_FORMAT_YV12, size, math::Rect(size), size, base::TimeDelta());
   }
 
  private:
@@ -177,32 +177,32 @@
 };
 
 TEST_F(VideoUtilTest, GetNaturalSize) {
-  gfx::Size visible_size(320, 240);
+  math::Size visible_size(320, 240);
 
   // Test 0 sizes.
-  EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(gfx::Size(0, 0), 1, 1));
-  EXPECT_EQ(gfx::Size(0, 1), GetNaturalSize(gfx::Size(0, 1), 1, 1));
-  EXPECT_EQ(gfx::Size(1, 0), GetNaturalSize(gfx::Size(1, 0), 1, 1));
+  EXPECT_EQ(math::Size(0, 0), GetNaturalSize(math::Size(0, 0), 1, 1));
+  EXPECT_EQ(math::Size(0, 1), GetNaturalSize(math::Size(0, 1), 1, 1));
+  EXPECT_EQ(math::Size(1, 0), GetNaturalSize(math::Size(1, 0), 1, 1));
 
   // Test abnormal ratios.
-  EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(visible_size, 0, 0));
-  EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(visible_size, 1, 0));
-  EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(visible_size, 1, -1));
-  EXPECT_EQ(gfx::Size(0, 0), GetNaturalSize(visible_size, -1, 1));
+  EXPECT_EQ(math::Size(0, 0), GetNaturalSize(visible_size, 0, 0));
+  EXPECT_EQ(math::Size(0, 0), GetNaturalSize(visible_size, 1, 0));
+  EXPECT_EQ(math::Size(0, 0), GetNaturalSize(visible_size, 1, -1));
+  EXPECT_EQ(math::Size(0, 0), GetNaturalSize(visible_size, -1, 1));
 
   // Test normal sizes and ratios.
-  EXPECT_EQ(gfx::Size(0, 240), GetNaturalSize(visible_size, 0, 1));
-  EXPECT_EQ(gfx::Size(320, 240), GetNaturalSize(visible_size, 1, 1));
-  EXPECT_EQ(gfx::Size(640, 240), GetNaturalSize(visible_size, 2, 1));
-  EXPECT_EQ(gfx::Size(160, 240), GetNaturalSize(visible_size, 1, 2));
-  EXPECT_EQ(gfx::Size(427, 240), GetNaturalSize(visible_size, 4, 3));
-  EXPECT_EQ(gfx::Size(240, 240), GetNaturalSize(visible_size, 3, 4));
-  EXPECT_EQ(gfx::Size(569, 240), GetNaturalSize(visible_size, 16, 9));
-  EXPECT_EQ(gfx::Size(180, 240), GetNaturalSize(visible_size, 9, 16));
+  EXPECT_EQ(math::Size(0, 240), GetNaturalSize(visible_size, 0, 1));
+  EXPECT_EQ(math::Size(320, 240), GetNaturalSize(visible_size, 1, 1));
+  EXPECT_EQ(math::Size(640, 240), GetNaturalSize(visible_size, 2, 1));
+  EXPECT_EQ(math::Size(160, 240), GetNaturalSize(visible_size, 1, 2));
+  EXPECT_EQ(math::Size(427, 240), GetNaturalSize(visible_size, 4, 3));
+  EXPECT_EQ(math::Size(240, 240), GetNaturalSize(visible_size, 3, 4));
+  EXPECT_EQ(math::Size(569, 240), GetNaturalSize(visible_size, 16, 9));
+  EXPECT_EQ(math::Size(180, 240), GetNaturalSize(visible_size, 9, 16));
 
   // Test some random ratios.
-  EXPECT_EQ(gfx::Size(495, 240), GetNaturalSize(visible_size, 17, 11));
-  EXPECT_EQ(gfx::Size(207, 240), GetNaturalSize(visible_size, 11, 17));
+  EXPECT_EQ(math::Size(495, 240), GetNaturalSize(visible_size, 17, 11));
+  EXPECT_EQ(math::Size(207, 240), GetNaturalSize(visible_size, 11, 17));
 }
 
 namespace {
@@ -374,82 +374,84 @@
 // Tests the ComputeLetterboxRegion function.  Also, because of shared code
 // internally, this also tests ScaleSizeToFitWithinTarget().
 TEST_F(VideoUtilTest, ComputeLetterboxRegion) {
+  EXPECT_EQ(math::Rect(166, 0, 667, 500),
+            ComputeLetterboxRegion(math::Rect(0, 0, 1000, 500),
+                                   math::Size(640, 480)));
+  EXPECT_EQ(math::Rect(0, 312, 500, 375),
+            ComputeLetterboxRegion(math::Rect(0, 0, 500, 1000),
+                                   math::Size(640, 480)));
+  EXPECT_EQ(math::Rect(55, 0, 889, 500),
+            ComputeLetterboxRegion(math::Rect(0, 0, 1000, 500),
+                                   math::Size(1920, 1080)));
   EXPECT_EQ(
-      gfx::Rect(166, 0, 667, 500),
-      ComputeLetterboxRegion(gfx::Rect(0, 0, 1000, 500), gfx::Size(640, 480)));
-  EXPECT_EQ(
-      gfx::Rect(0, 312, 500, 375),
-      ComputeLetterboxRegion(gfx::Rect(0, 0, 500, 1000), gfx::Size(640, 480)));
-  EXPECT_EQ(gfx::Rect(55, 0, 889, 500),
-            ComputeLetterboxRegion(gfx::Rect(0, 0, 1000, 500),
-                                   gfx::Size(1920, 1080)));
-  EXPECT_EQ(
-      gfx::Rect(0, 12, 100, 75),
-      ComputeLetterboxRegion(gfx::Rect(0, 0, 100, 100), gfx::Size(400, 300)));
-  EXPECT_EQ(gfx::Rect(0, 250000000, 2000000000, 1500000000),
-            ComputeLetterboxRegion(gfx::Rect(0, 0, 2000000000, 2000000000),
-                                   gfx::Size(40000, 30000)));
-  EXPECT_TRUE(ComputeLetterboxRegion(gfx::Rect(0, 0, 2000000000, 2000000000),
-                                     gfx::Size(0, 0))
+      math::Rect(0, 12, 100, 75),
+      ComputeLetterboxRegion(math::Rect(0, 0, 100, 100), math::Size(400, 300)));
+  EXPECT_EQ(math::Rect(0, 250000000, 2000000000, 1500000000),
+            ComputeLetterboxRegion(math::Rect(0, 0, 2000000000, 2000000000),
+                                   math::Size(40000, 30000)));
+  EXPECT_TRUE(ComputeLetterboxRegion(math::Rect(0, 0, 2000000000, 2000000000),
+                                     math::Size(0, 0))
                   .IsEmpty());
 }
 
 TEST_F(VideoUtilTest, ScaleSizeToEncompassTarget) {
   EXPECT_EQ(
-      gfx::Size(1000, 750),
-      ScaleSizeToEncompassTarget(gfx::Size(640, 480), gfx::Size(1000, 500)));
+      math::Size(1000, 750),
+      ScaleSizeToEncompassTarget(math::Size(640, 480), math::Size(1000, 500)));
   EXPECT_EQ(
-      gfx::Size(1333, 1000),
-      ScaleSizeToEncompassTarget(gfx::Size(640, 480), gfx::Size(500, 1000)));
+      math::Size(1333, 1000),
+      ScaleSizeToEncompassTarget(math::Size(640, 480), math::Size(500, 1000)));
+  EXPECT_EQ(math::Size(1000, 563),
+            ScaleSizeToEncompassTarget(math::Size(1920, 1080),
+                                       math::Size(1000, 500)));
   EXPECT_EQ(
-      gfx::Size(1000, 563),
-      ScaleSizeToEncompassTarget(gfx::Size(1920, 1080), gfx::Size(1000, 500)));
-  EXPECT_EQ(gfx::Size(133, 100), ScaleSizeToEncompassTarget(
-                                     gfx::Size(400, 300), gfx::Size(100, 100)));
-  EXPECT_EQ(gfx::Size(266666667, 200000000),
-            ScaleSizeToEncompassTarget(gfx::Size(40000, 30000),
-                                       gfx::Size(200000000, 200000000)));
-  EXPECT_TRUE(ScaleSizeToEncompassTarget(gfx::Size(0, 0),
-                                         gfx::Size(2000000000, 2000000000))
+      math::Size(133, 100),
+      ScaleSizeToEncompassTarget(math::Size(400, 300), math::Size(100, 100)));
+  EXPECT_EQ(math::Size(266666667, 200000000),
+            ScaleSizeToEncompassTarget(math::Size(40000, 30000),
+                                       math::Size(200000000, 200000000)));
+  EXPECT_TRUE(ScaleSizeToEncompassTarget(math::Size(0, 0),
+                                         math::Size(2000000000, 2000000000))
                   .IsEmpty());
 }
 
 TEST_F(VideoUtilTest, PadToMatchAspectRatio) {
-  EXPECT_EQ(gfx::Size(640, 480),
-            PadToMatchAspectRatio(gfx::Size(640, 480), gfx::Size(640, 480)));
-  EXPECT_EQ(gfx::Size(640, 480),
-            PadToMatchAspectRatio(gfx::Size(640, 480), gfx::Size(4, 3)));
-  EXPECT_EQ(gfx::Size(960, 480),
-            PadToMatchAspectRatio(gfx::Size(640, 480), gfx::Size(1000, 500)));
-  EXPECT_EQ(gfx::Size(640, 1280),
-            PadToMatchAspectRatio(gfx::Size(640, 480), gfx::Size(500, 1000)));
-  EXPECT_EQ(gfx::Size(2160, 1080),
-            PadToMatchAspectRatio(gfx::Size(1920, 1080), gfx::Size(1000, 500)));
-  EXPECT_EQ(gfx::Size(400, 400),
-            PadToMatchAspectRatio(gfx::Size(400, 300), gfx::Size(100, 100)));
-  EXPECT_EQ(gfx::Size(400, 400),
-            PadToMatchAspectRatio(gfx::Size(300, 400), gfx::Size(100, 100)));
-  EXPECT_EQ(gfx::Size(40000, 40000),
-            PadToMatchAspectRatio(gfx::Size(40000, 30000),
-                                  gfx::Size(2000000000, 2000000000)));
-  EXPECT_TRUE(PadToMatchAspectRatio(gfx::Size(40000, 30000), gfx::Size(0, 0))
+  EXPECT_EQ(math::Size(640, 480),
+            PadToMatchAspectRatio(math::Size(640, 480), math::Size(640, 480)));
+  EXPECT_EQ(math::Size(640, 480),
+            PadToMatchAspectRatio(math::Size(640, 480), math::Size(4, 3)));
+  EXPECT_EQ(math::Size(960, 480),
+            PadToMatchAspectRatio(math::Size(640, 480), math::Size(1000, 500)));
+  EXPECT_EQ(math::Size(640, 1280),
+            PadToMatchAspectRatio(math::Size(640, 480), math::Size(500, 1000)));
+  EXPECT_EQ(
+      math::Size(2160, 1080),
+      PadToMatchAspectRatio(math::Size(1920, 1080), math::Size(1000, 500)));
+  EXPECT_EQ(math::Size(400, 400),
+            PadToMatchAspectRatio(math::Size(400, 300), math::Size(100, 100)));
+  EXPECT_EQ(math::Size(400, 400),
+            PadToMatchAspectRatio(math::Size(300, 400), math::Size(100, 100)));
+  EXPECT_EQ(math::Size(40000, 40000),
+            PadToMatchAspectRatio(math::Size(40000, 30000),
+                                  math::Size(2000000000, 2000000000)));
+  EXPECT_TRUE(PadToMatchAspectRatio(math::Size(40000, 30000), math::Size(0, 0))
                   .IsEmpty());
 }
 
 TEST_F(VideoUtilTest, LetterboxYUV) {
   int width = 40;
   int height = 30;
-  gfx::Size size(width, height);
+  math::Size size(width, height);
   scoped_refptr<VideoFrame> frame(VideoFrame::CreateFrame(
-      PIXEL_FORMAT_YV12, size, gfx::Rect(size), size, base::TimeDelta()));
+      PIXEL_FORMAT_YV12, size, math::Rect(size), size, base::TimeDelta()));
 
   for (int left_margin = 0; left_margin <= 10; left_margin += 10) {
     for (int right_margin = 0; right_margin <= 10; right_margin += 10) {
       for (int top_margin = 0; top_margin <= 10; top_margin += 10) {
         for (int bottom_margin = 0; bottom_margin <= 10; bottom_margin += 10) {
-          gfx::Rect view_area(left_margin, top_margin,
-                              width - left_margin - right_margin,
-                              height - top_margin - bottom_margin);
+          math::Rect view_area(left_margin, top_margin,
+                               width - left_margin - right_margin,
+                               height - top_margin - bottom_margin);
           FillYUV(frame.get(), 0x1, 0x2, 0x3);
           LetterboxYUV(frame.get(), view_area);
           for (int x = 0; x < width; x++) {
@@ -477,32 +479,32 @@
 }
 
 TEST_F(VideoUtilTest, I420CopyWithPadding) {
-  gfx::Size visible_size(40, 30);
+  math::Size visible_size(40, 30);
   scoped_refptr<VideoFrame> src_frame = CreateFrameWithPatternFilled(
-      PIXEL_FORMAT_I420, visible_size, gfx::Rect(visible_size), visible_size,
+      PIXEL_FORMAT_I420, visible_size, math::Rect(visible_size), visible_size,
       base::TimeDelta());
   // Expect to return false when copying to an empty buffer.
   EXPECT_FALSE(I420CopyWithPadding(*src_frame, NULL));
 
   scoped_refptr<VideoFrame> dst_frame = CreateFrameWithPatternFilled(
-      PIXEL_FORMAT_I420, visible_size, gfx::Rect(visible_size), visible_size,
+      PIXEL_FORMAT_I420, visible_size, math::Rect(visible_size), visible_size,
       base::TimeDelta());
   EXPECT_TRUE(I420CopyWithPadding(*src_frame, dst_frame.get()));
   EXPECT_TRUE(VerifyCopyWithPadding(*src_frame, *dst_frame));
 
-  gfx::Size coded_size(60, 40);
+  math::Size coded_size(60, 40);
   dst_frame = CreateFrameWithPatternFilled(PIXEL_FORMAT_I420, coded_size,
-                                           gfx::Rect(visible_size), coded_size,
+                                           math::Rect(visible_size), coded_size,
                                            base::TimeDelta());
   EXPECT_TRUE(I420CopyWithPadding(*src_frame, dst_frame.get()));
   EXPECT_TRUE(VerifyCopyWithPadding(*src_frame, *dst_frame));
 
-  gfx::Size odd_size(39, 31);
+  math::Size odd_size(39, 31);
   src_frame = CreateFrameWithPatternFilled(PIXEL_FORMAT_I420, odd_size,
-                                           gfx::Rect(odd_size), odd_size,
+                                           math::Rect(odd_size), odd_size,
                                            base::TimeDelta());
   dst_frame = CreateFrameWithPatternFilled(PIXEL_FORMAT_I420, coded_size,
-                                           gfx::Rect(odd_size), coded_size,
+                                           math::Rect(odd_size), coded_size,
                                            base::TimeDelta());
   EXPECT_TRUE(I420CopyWithPadding(*src_frame, dst_frame.get()));
   EXPECT_TRUE(VerifyCopyWithPadding(*src_frame, *dst_frame));
diff --git a/src/cobalt/media/base/yuv_convert_unittest.cc b/src/cobalt/media/base/yuv_convert_unittest.cc
index 7d8790a..5b7cb31 100644
--- a/src/cobalt/media/base/yuv_convert_unittest.cc
+++ b/src/cobalt/media/base/yuv_convert_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "build/build_config.h"
+#include "cobalt/math/rect.h"
 #include "cobalt/media/base/djb2.h"
 #include "cobalt/media/base/simd/convert_rgb_to_yuv.h"
 #include "cobalt/media/base/simd/convert_yuv_to_rgb.h"
@@ -20,7 +21,6 @@
 #include "starboard/memory.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect.h"
 
 // Size of raw image.
 static const int kSourceWidth = 640;
@@ -445,7 +445,7 @@
   // The API currently only supports down-scaling, so we don't test up-scaling.
   const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp;
   std::unique_ptr<uint8_t[]> rgb_scaled_bytes(new uint8_t[size_of_rgb_scaled]);
-  gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight);
+  math::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight);
 
   // We can't compare with the full-frame scaler because it uses slightly
   // different sampling coordinates.
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index f6e2021..13de0b0 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -40,38 +40,19 @@
 }
 
 bool IsMemoryPoolEnabled() {
-#if SB_API_VERSION >= 10
   return SbMediaIsBufferUsingMemoryPool();
-#elif COBALT_MEDIA_BUFFER_INITIAL_CAPACITY > 0 || \
-      COBALT_MEDIA_BUFFER_ALLOCATION_UNIT > 0
-  return true;
-#endif  // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY == 0 &&
-        // COBALT_MEDIA_BUFFER_ALLOCATION_UNIT == 0
-  return false;
 }
 
 bool IsMemoryPoolAllocatedOnDemand() {
-#if SB_API_VERSION >= 10
   return SbMediaIsBufferPoolAllocateOnDemand();
-#else  // SB_API_VERSION >= 10
-  return COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND;
-#endif  // SB_API_VERSION >= 10
 }
 
 int GetInitialBufferCapacity() {
-#if SB_API_VERSION >= 10
   return SbMediaGetInitialBufferCapacity();
-#else   // SB_API_VERSION >= 10
-  return COBALT_MEDIA_BUFFER_INITIAL_CAPACITY;
-#endif  // SB_API_VERSION >= 10
 }
 
 int GetBufferAllocationUnit() {
-#if SB_API_VERSION >= 10
   return SbMediaGetBufferAllocationUnit();
-#else   // SB_API_VERSION >= 10
-  return COBALT_MEDIA_BUFFER_ALLOCATION_UNIT;
-#endif  // SB_API_VERSION >= 10
 }
 
 }  // namespace
@@ -93,14 +74,10 @@
 
   TRACK_MEMORY_SCOPE("Media");
 
-#if SB_API_VERSION >= 10
   // We cannot call SbMediaGetMaxBufferCapacity because |video_codec_| is not
   // set yet. Use 0 (unbounded) until |video_codec_| is updated in
   // UpdateVideoConfig().
   int max_capacity = 0;
-#else   // SB_API_VERSION >= 10
-  int max_capacity = COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P;
-#endif  // SB_API_VERSION >= 10
   reuse_allocator_.reset(new ReuseAllocator(
       &fallback_allocator_, initial_capacity_, allocation_unit_, max_capacity));
   DLOG(INFO) << "Allocated " << initial_capacity_
@@ -136,7 +113,6 @@
   if (!reuse_allocator_) {
     DCHECK(is_memory_pool_allocated_on_demand_);
 
-#if SB_API_VERSION >= 10
     int max_capacity = 0;
     if (video_codec_ != kSbMediaVideoCodecNone) {
       DCHECK_GT(resolution_width_, 0);
@@ -145,13 +121,6 @@
       max_capacity = SbMediaGetMaxBufferCapacity(
           video_codec_, resolution_width_, resolution_height_, bits_per_pixel_);
     }
-#else   // SB_API_VERSION >= 10
-    VideoResolution resolution =
-        GetVideoResolution(math::Size(resolution_width_, resolution_height_));
-    int max_capacity = resolution <= kVideoResolution1080p
-                           ? COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P
-                           : COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K;
-#endif  // SB_API_VERSION >= 10
     reuse_allocator_.reset(new ReuseAllocator(&fallback_allocator_,
                                               initial_capacity_,
                                               allocation_unit_, max_capacity));
@@ -225,16 +194,8 @@
     return;
   }
 
-#if SB_API_VERSION >= 10
   reuse_allocator_->IncreaseMaxCapacityIfNecessary(SbMediaGetMaxBufferCapacity(
       video_codec_, resolution_width_, resolution_height_, bits_per_pixel_));
-#else   // SB_API_VERSION >= 10
-  VideoResolution resolution = GetVideoResolution(config.visible_rect().size());
-  if (reuse_allocator_->max_capacity() && resolution > kVideoResolution1080p) {
-    reuse_allocator_->IncreaseMaxCapacityIfNecessary(
-        COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K);
-  }
-#endif  // SB_API_VERSION >= 10
   DLOG(INFO) << "Max capacity of decoder buffer allocator after increasing is "
              << reuse_allocator_->GetCapacity();
 }
diff --git a/src/cobalt/media/filters/h264_parser.cc b/src/cobalt/media/filters/h264_parser.cc
index bd390ad..29284b9 100644
--- a/src/cobalt/media/filters/h264_parser.cc
+++ b/src/cobalt/media/filters/h264_parser.cc
@@ -10,10 +10,10 @@
 
 #include "base/basictypes.h"
 #include "base/logging.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/decrypt_config.h"
 #include "starboard/memory.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -34,7 +34,7 @@
 
 // Based on T-REC-H.264 7.4.2.1.1, "Sequence parameter set data semantics",
 // available from http://www.itu.int/rec/T-REC-H.264.
-base::Optional<gfx::Size> H264SPS::GetCodedSize() const {
+base::Optional<math::Size> H264SPS::GetCodedSize() const {
   // Interlaced frames are twice the height of each field.
   const int mb_unit = 16;
   int map_unit = frame_mbs_only_flag ? 16 : 32;
@@ -51,16 +51,16 @@
     return base::nullopt;
   }
 
-  return gfx::Size(mb_unit * (pic_width_in_mbs_minus1 + 1),
-                   map_unit * (pic_height_in_map_units_minus1 + 1));
+  return math::Size(mb_unit * (pic_width_in_mbs_minus1 + 1),
+                    map_unit * (pic_height_in_map_units_minus1 + 1));
 }
 
 // Also based on section 7.4.2.1.1.
-base::Optional<gfx::Rect> H264SPS::GetVisibleRect() const {
-  base::Optional<gfx::Size> coded_size = GetCodedSize();
+base::Optional<math::Rect> H264SPS::GetVisibleRect() const {
+  base::Optional<math::Size> coded_size = GetCodedSize();
   if (!coded_size) return base::nullopt;
 
-  if (!frame_cropping_flag) return gfx::Rect(coded_size.value());
+  if (!frame_cropping_flag) return math::Rect(coded_size.value());
 
   int crop_unit_x;
   int crop_unit_y;
@@ -102,9 +102,9 @@
     return base::nullopt;
   }
 
-  return gfx::Rect(crop_left, crop_top,
-                   coded_size->width() - crop_left - crop_right,
-                   coded_size->height() - crop_top - crop_bottom);
+  return math::Rect(crop_left, crop_top,
+                    coded_size->width() - crop_left - crop_right,
+                    coded_size->height() - crop_top - crop_bottom);
 }
 
 H264PPS::H264PPS() { SbMemorySet(this, 0, sizeof(*this)); }
diff --git a/src/cobalt/media/filters/h264_parser.h b/src/cobalt/media/filters/h264_parser.h
index 5cd443b..5358b3e 100644
--- a/src/cobalt/media/filters/h264_parser.h
+++ b/src/cobalt/media/filters/h264_parser.h
@@ -15,13 +15,13 @@
 
 #include "base/basictypes.h"
 #include "base/optional.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/media_export.h"
 #include "cobalt/media/base/ranges.h"
 #include "cobalt/media/base/video_codecs.h"
 #include "cobalt/media/filters/h264_bit_reader.h"
 #include "starboard/types.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -174,8 +174,8 @@
   // Helpers to compute frequently-used values. These methods return
   // base::nullopt if they encounter integer overflow. They do not verify that
   // the results are in-spec for the given profile or level.
-  base::Optional<gfx::Size> GetCodedSize() const;
-  base::Optional<gfx::Rect> GetVisibleRect() const;
+  base::Optional<math::Size> GetCodedSize() const;
+  base::Optional<math::Rect> GetVisibleRect() const;
 };
 
 struct MEDIA_EXPORT H264PPS {
diff --git a/src/cobalt/media/filters/h264_parser_fuzzertest.cc b/src/cobalt/media/filters/h264_parser_fuzzertest.cc
index c4a9e0e..832ef24 100644
--- a/src/cobalt/media/filters/h264_parser_fuzzertest.cc
+++ b/src/cobalt/media/filters/h264_parser_fuzzertest.cc
@@ -4,10 +4,10 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "base/optional.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/filters/h264_parser.h"
 #include "starboard/types.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 static volatile size_t volatile_sink;
 
@@ -41,10 +41,10 @@
         if (!sps) break;
         // Also test the SPS helper methods. We make sure that the results are
         // used so that the calls are not optimized away.
-        base::Optional<gfx::Size> coded_size = sps->GetCodedSize();
-        volatile_sink = coded_size.value_or(gfx::Size()).ToString().length();
-        base::Optional<gfx::Rect> visible_rect = sps->GetVisibleRect();
-        volatile_sink = visible_rect.value_or(gfx::Rect()).ToString().length();
+        base::Optional<math::Size> coded_size = sps->GetCodedSize();
+        volatile_sink = coded_size.value_or(math::Size()).ToString().length();
+        base::Optional<math::Rect> visible_rect = sps->GetVisibleRect();
+        volatile_sink = visible_rect.value_or(math::Rect()).ToString().length();
         break;
       }
 
diff --git a/src/cobalt/media/filters/h264_parser_unittest.cc b/src/cobalt/media/filters/h264_parser_unittest.cc
index ab71188..20ae2c5 100644
--- a/src/cobalt/media/filters/h264_parser_unittest.cc
+++ b/src/cobalt/media/filters/h264_parser_unittest.cc
@@ -11,11 +11,11 @@
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/strings/string_number_conversions.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/test_data_util.h"
 #include "cobalt/media/filters/h264_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 
 namespace cobalt {
 namespace media {
@@ -57,7 +57,7 @@
 
 TEST_F(H264SPSTest, GetCodedSize) {
   std::unique_ptr<H264SPS> sps = MakeSPS_BBB480p();
-  EXPECT_EQ(gfx::Size(848, 480), sps->GetCodedSize());
+  EXPECT_EQ(math::Size(848, 480), sps->GetCodedSize());
 
   // Overflow.
   sps->pic_width_in_mbs_minus1 = std::numeric_limits<int>::max();
@@ -66,7 +66,7 @@
 
 TEST_F(H264SPSTest, GetVisibleRect) {
   std::unique_ptr<H264SPS> sps = MakeSPS_BBB480p();
-  EXPECT_EQ(gfx::Rect(0, 0, 848, 480), sps->GetVisibleRect());
+  EXPECT_EQ(math::Rect(0, 0, 848, 480), sps->GetVisibleRect());
 
   // Add some cropping.
   sps->frame_cropping_flag = true;
@@ -74,14 +74,14 @@
   sps->frame_crop_right_offset = 2;
   sps->frame_crop_top_offset = 3;
   sps->frame_crop_bottom_offset = 4;
-  EXPECT_EQ(gfx::Rect(2, 6, 848 - 6, 480 - 14), sps->GetVisibleRect());
+  EXPECT_EQ(math::Rect(2, 6, 848 - 6, 480 - 14), sps->GetVisibleRect());
 
   // Not quite invalid.
   sps->frame_crop_left_offset = 422;
   sps->frame_crop_right_offset = 1;
   sps->frame_crop_top_offset = 0;
   sps->frame_crop_bottom_offset = 0;
-  EXPECT_EQ(gfx::Rect(844, 0, 2, 480), sps->GetVisibleRect());
+  EXPECT_EQ(math::Rect(844, 0, 2, 480), sps->GetVisibleRect());
 
   // Invalid crop.
   sps->frame_crop_left_offset = 423;
diff --git a/src/cobalt/media/filters/shell_avc_parser.cc b/src/cobalt/media/filters/shell_avc_parser.cc
index f120ffa..baca931 100644
--- a/src/cobalt/media/filters/shell_avc_parser.cc
+++ b/src/cobalt/media/filters/shell_avc_parser.cc
@@ -286,10 +286,10 @@
   // checks out, write output structure
   int visible_width = width - (crop_left + crop_right);
   int visible_height = height - (crop_top + crop_bottom);
-  record_out->coded_size = gfx::Size(width, height),
+  record_out->coded_size = math::Size(width, height),
   record_out->visible_rect =
-      gfx::Rect(crop_left, crop_top, visible_width, visible_height),
-  record_out->natural_size = gfx::Size(visible_width, visible_height);
+      math::Rect(crop_left, crop_top, visible_width, visible_height),
+  record_out->natural_size = math::Size(visible_width, visible_height);
   record_out->num_ref_frames = num_ref_frames;
   return true;
 }
diff --git a/src/cobalt/media/filters/shell_avc_parser.h b/src/cobalt/media/filters/shell_avc_parser.h
index 3b72e18..758a7a2 100644
--- a/src/cobalt/media/filters/shell_avc_parser.h
+++ b/src/cobalt/media/filters/shell_avc_parser.h
@@ -38,9 +38,9 @@
   virtual ~ShellAVCParser();
 
   struct ShellSPSRecord {
-    gfx::Size coded_size;
-    gfx::Rect visible_rect;
-    gfx::Size natural_size;
+    math::Size coded_size;
+    math::Rect visible_rect;
+    math::Size natural_size;
     uint32 num_ref_frames;
   };
   static bool ParseSPS(const uint8* sps, size_t sps_size,
diff --git a/src/cobalt/media/filters/shell_demuxer.cc b/src/cobalt/media/filters/shell_demuxer.cc
index cf044a7..3d0a8cf 100644
--- a/src/cobalt/media/filters/shell_demuxer.cc
+++ b/src/cobalt/media/filters/shell_demuxer.cc
@@ -369,7 +369,6 @@
                                video_demuxer_stream_->GetTotalBufferSize();
     size_t total_buffer_count = audio_demuxer_stream_->GetTotalBufferCount() +
                                 video_demuxer_stream_->GetTotalBufferCount();
-#if SB_API_VERSION >= 10
     int progressive_budget = SbMediaGetProgressiveBufferBudget(
         MediaVideoCodecToSbMediaVideoCodec(VideoConfig().codec()),
         VideoConfig().visible_rect().size().width(),
@@ -377,11 +376,6 @@
         VideoConfig().webm_color_metadata().BitsPerChannel);
     int progressive_duration_cap_in_seconds =
         SbMediaGetBufferGarbageCollectionDurationThreshold() / kSbTimeSecond;
-#else   // SB_API_VERSION >= 10
-    int progressive_budget = COBALT_MEDIA_BUFFER_PROGRESSIVE_BUDGET;
-    int progressive_duration_cap_in_seconds =
-        COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS;
-#endif  // SB_API_VERSION >= 10
     const int kEstimatedBufferCountPerSeconds = 70;
     int progressive_buffer_count_cap =
         progressive_duration_cap_in_seconds * kEstimatedBufferCountPerSeconds;
diff --git a/src/cobalt/media/filters/source_buffer_state_unittest.cc b/src/cobalt/media/filters/source_buffer_state_unittest.cc
index b68ca67..1093ee3 100644
--- a/src/cobalt/media/filters/source_buffer_state_unittest.cc
+++ b/src/cobalt/media/filters/source_buffer_state_unittest.cc
@@ -33,8 +33,8 @@
 }
 
 VideoDecoderConfig CreateVideoConfig(VideoCodec codec, int w, int h) {
-  gfx::Size size(w, h);
-  gfx::Rect visible_rect(size);
+  math::Size size(w, h);
+  math::Rect visible_rect(size);
   return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN,
                             PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709, size,
                             visible_rect, size, EmptyExtraData(),
diff --git a/src/cobalt/media/filters/source_buffer_stream.cc b/src/cobalt/media/filters/source_buffer_stream.cc
index e177b15..c4208ba 100644
--- a/src/cobalt/media/filters/source_buffer_stream.cc
+++ b/src/cobalt/media/filters/source_buffer_stream.cc
@@ -158,11 +158,7 @@
       range_for_next_append_(ranges_.end()),
       last_output_buffer_timestamp_(kNoDecodeTimestamp()),
       max_interbuffer_distance_(kNoTimestamp),
-#if SB_API_VERSION >= 10
       memory_limit_(SbMediaGetAudioBufferBudget()) {
-#else   // SB_API_VERSION >= 10
-      memory_limit_(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) {
-#endif  // SB_API_VERSION >= 10
 
   DCHECK(audio_config.IsValidConfig());
   audio_configs_.push_back(audio_config);
@@ -180,16 +176,8 @@
       max_interbuffer_distance_(kNoTimestamp) {
   DCHECK(video_config.IsValidConfig());
   video_configs_.push_back(video_config);
-#if SB_API_VERSION >= 10
   // This gets updated via taking a max in UpdateMediaBufferMembers.
   memory_limit_ = 0;
-#else   // SB_API_VERSION >= 10
-  VideoResolution resolution =
-      GetVideoResolution(video_config.visible_rect().size());
-  memory_limit_ = resolution <= kVideoResolution1080p
-                      ? COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
-                      : COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
-#endif  // SB_API_VERSION >= 10
   UpdateMediaBufferMembers(video_config);
 }
 
@@ -204,13 +192,8 @@
       range_for_next_append_(ranges_.end()),
       last_output_buffer_timestamp_(kNoDecodeTimestamp()),
       max_interbuffer_distance_(kNoTimestamp),
-#if SB_API_VERSION >= 10
       memory_limit_(SbMediaGetAudioBufferBudget()) {
 }
-#else   // SB_API_VERSION >= 10
-      memory_limit_(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) {
-}
-#endif  // SB_API_VERSION >= 10
 
 SourceBufferStream::~SourceBufferStream() {
   while (!ranges_.empty()) {
@@ -753,13 +736,8 @@
 
   size_t bytes_to_free = 0;
 
-#if SB_API_VERSION >= 10
   int garbage_collection_duration_threshold_in_seconds =
       SbMediaGetBufferGarbageCollectionDurationThreshold() / kSbTimeSecond;
-#else   // SB_API_VERSION >= 10
-  int garbage_collection_duration_threshold_in_seconds =
-      COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS;
-#endif  // SB_API_VERSION >= 10
   // Check if we're under or at the memory/duration limit.
   const auto kGcDurationThresholdInMilliseconds =
       garbage_collection_duration_threshold_in_seconds * 1000;
@@ -1564,7 +1542,6 @@
 void SourceBufferStream::UpdateMediaBufferMembers(
     const VideoDecoderConfig& config) {
   DVLOG(3) << "UpdateMediaBufferMembers.";
-#if SB_API_VERSION >= 10
   resolution_width_ = config.visible_rect().size().width();
   resolution_height_ = config.visible_rect().size().height();
   bits_per_pixel_ = config.webm_color_metadata().BitsPerChannel;
@@ -1574,13 +1551,6 @@
       memory_limit_,
       static_cast<size_t>(SbMediaGetVideoBufferBudget(
           codec_, resolution_width_, resolution_height_, bits_per_pixel_)));
-#else   // SB_API_VERSION >= 10
-  VideoResolution resolution = GetVideoResolution(config.visible_rect().size());
-  // TODO: Reduce the memory limit when there is no more 4k samples cached.
-  if (resolution > kVideoResolution1080p) {
-    memory_limit_ = COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
-  }
-#endif  // SB_API_VERSION >= 10
 }
 
 bool SourceBufferStream::UpdateVideoConfig(const VideoDecoderConfig& config) {
diff --git a/src/cobalt/media/filters/source_buffer_stream.h b/src/cobalt/media/filters/source_buffer_stream.h
index 69af330..57f194b 100644
--- a/src/cobalt/media/filters/source_buffer_stream.h
+++ b/src/cobalt/media/filters/source_buffer_stream.h
@@ -503,13 +503,11 @@
   // Stores the largest distance between two adjacent buffers in this stream.
   base::TimeDelta max_interbuffer_distance_;
 
-#if SB_API_VERSION >= 10
   bool using_memory_pool_;
   int resolution_width_ = kSbMediaVideoResolutionDimensionInvalid;
   int resolution_height_ = kSbMediaVideoResolutionDimensionInvalid;
   int bits_per_pixel_ = kSbMediaBitsPerPixelInvalid;
   SbMediaVideoCodec codec_ = kSbMediaVideoCodecNone;
-#endif  // SB_API_VERSION >= 10
 
   // The maximum amount of data in bytes the stream will keep in memory.
   size_t memory_limit_;
diff --git a/src/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc b/src/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc
index 641a380..81951e0 100644
--- a/src/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc
+++ b/src/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc
@@ -26,9 +26,9 @@
 namespace {
 
 VideoDecoderConfig CreateFakeVideoConfig() {
-  gfx::Size coded_size(320, 240);
-  gfx::Rect visible_rect(0, 0, 320, 240);
-  gfx::Size natural_size(320, 240);
+  math::Size coded_size(320, 240);
+  math::Rect visible_rect(0, 0, 320, 240);
+  math::Size natural_size(320, 240);
   return VideoDecoderConfig(kCodecH264, H264PROFILE_MAIN, PIXEL_FORMAT_I420,
                             COLOR_SPACE_UNSPECIFIED, coded_size, visible_rect,
                             natural_size, EmptyExtraData(), Unencrypted());
diff --git a/src/cobalt/media/formats/mp2t/es_parser_h264.cc b/src/cobalt/media/formats/mp2t/es_parser_h264.cc
index 8034c4c..ede1283 100644
--- a/src/cobalt/media/formats/mp2t/es_parser_h264.cc
+++ b/src/cobalt/media/formats/mp2t/es_parser_h264.cc
@@ -9,6 +9,8 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/optional.h"
+#include "cobalt/math/geometry/rect.h"
+#include "cobalt/math/geometry/size.h"
 #include "media/base/encryption_scheme.h"
 #include "media/base/media_util.h"
 #include "media/base/stream_parser_buffer.h"
@@ -17,8 +19,6 @@
 #include "media/filters/h264_parser.h"
 #include "media/formats/common/offset_byte_queue.h"
 #include "media/formats/mp2t/mp2t_common.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/size.h"
 
 namespace cobalt {
 namespace media {
@@ -249,10 +249,10 @@
   int sar_width = (sps->sar_width == 0) ? 1 : sps->sar_width;
   int sar_height = (sps->sar_height == 0) ? 1 : sps->sar_height;
 
-  base::Optional<gfx::Size> coded_size = sps->GetCodedSize();
+  base::Optional<math::Size> coded_size = sps->GetCodedSize();
   if (!coded_size) return false;
 
-  base::Optional<gfx::Rect> visible_rect = sps->GetVisibleRect();
+  base::Optional<math::Rect> visible_rect = sps->GetVisibleRect();
   if (!visible_rect) return false;
 
   if (visible_rect->width() > std::numeric_limits<int>::max() / sar_width) {
@@ -260,8 +260,8 @@
              << visible_rect->width() << " sar_width=" << sar_width;
     return false;
   }
-  gfx::Size natural_size((visible_rect->width() * sar_width) / sar_height,
-                         visible_rect->height());
+  math::Size natural_size((visible_rect->width() * sar_width) / sar_height,
+                          visible_rect->height());
   if (natural_size.width() == 0) return false;
 
   VideoDecoderConfig video_decoder_config(
diff --git a/src/cobalt/media/formats/mp4/mp4_stream_parser.cc b/src/cobalt/media/formats/mp4/mp4_stream_parser.cc
index a184c8a..50892db 100644
--- a/src/cobalt/media/formats/mp4/mp4_stream_parser.cc
+++ b/src/cobalt/media/formats/mp4/mp4_stream_parser.cc
@@ -415,19 +415,19 @@
       }
 
       // TODO: Recover correct crop box
-      gfx::Size coded_size(entry.width, entry.height);
-      gfx::Rect visible_rect(coded_size);
+      math::Size coded_size(entry.width, entry.height);
+      math::Rect visible_rect(coded_size);
 
       // If PASP is available, use the coded size and PASP to calculate the
       // natural size. Otherwise, use the size in track header for natural size.
-      gfx::Size natural_size(visible_rect.size());
+      math::Size natural_size(visible_rect.size());
       if (entry.pixel_aspect.h_spacing != 1 ||
           entry.pixel_aspect.v_spacing != 1) {
         natural_size =
             GetNaturalSize(visible_rect.size(), entry.pixel_aspect.h_spacing,
                            entry.pixel_aspect.v_spacing);
       } else if (track->header.width && track->header.height) {
-        natural_size = gfx::Size(track->header.width, track->header.height);
+        natural_size = math::Size(track->header.width, track->header.height);
       }
 
       uint32_t video_track_id = track->header.track_id;
diff --git a/src/cobalt/media/formats/mp4/mp4_stream_parser_unittest.cc b/src/cobalt/media/formats/mp4/mp4_stream_parser_unittest.cc
index 1cb9d2b..b2127ad 100644
--- a/src/cobalt/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/src/cobalt/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -419,7 +419,7 @@
 
   EXPECT_MEDIA_LOG(VideoCodecLog("avc1.64001E"));
   EXPECT_TRUE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512));
-  EXPECT_EQ(gfx::Size(639, 360), video_decoder_config_.natural_size());
+  EXPECT_EQ(math::Size(639, 360), video_decoder_config_.natural_size());
 }
 
 TEST_F(MP4StreamParserTest, NaturalSizeWithPASP) {
@@ -434,7 +434,7 @@
 
   EXPECT_MEDIA_LOG(VideoCodecLog("avc1.64001E"));
   EXPECT_TRUE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512));
-  EXPECT_EQ(gfx::Size(639, 360), video_decoder_config_.natural_size());
+  EXPECT_EQ(math::Size(639, 360), video_decoder_config_.natural_size());
 }
 
 TEST_F(MP4StreamParserTest, DemuxingAC3) {
diff --git a/src/cobalt/media/formats/webm/webm_video_client.cc b/src/cobalt/media/formats/webm/webm_video_client.cc
index f825979..962de68 100644
--- a/src/cobalt/media/formats/webm/webm_video_client.cc
+++ b/src/cobalt/media/formats/webm/webm_video_client.cc
@@ -82,11 +82,11 @@
 
   if (display_unit_ == -1) display_unit_ = 0;
 
-  gfx::Size coded_size(pixel_width_, pixel_height_);
-  gfx::RectF visible_rect_float(crop_top_, crop_left_,
-                                pixel_width_ - (crop_left_ + crop_right_),
-                                pixel_height_ - (crop_top_ + crop_bottom_));
-  gfx::Rect visible_rect = math::Rect::RoundFromRectF(visible_rect_float);
+  math::Size coded_size(pixel_width_, pixel_height_);
+  math::RectF visible_rect_float(crop_top_, crop_left_,
+                                 pixel_width_ - (crop_left_ + crop_right_),
+                                 pixel_height_ - (crop_top_ + crop_bottom_));
+  math::Rect visible_rect = math::Rect::RoundFromRectF(visible_rect_float);
   if (display_unit_ == 0) {
     if (display_width_ <= 0) display_width_ = visible_rect.width();
     if (display_height_ <= 0) display_height_ = visible_rect.height();
@@ -97,7 +97,7 @@
         << "Unsupported display unit type " << display_unit_;
     return false;
   }
-  gfx::Size natural_size = gfx::Size(display_width_, display_height_);
+  math::Size natural_size = math::Size(display_width_, display_height_);
 
   config->Initialize(video_codec, profile, format, COLOR_SPACE_HD_REC709,
                      coded_size, visible_rect, natural_size, codec_private,
diff --git a/src/cobalt/media/formats/webm/webm_video_client_unittest.cc b/src/cobalt/media/formats/webm/webm_video_client_unittest.cc
index 02ffefb..3dccf80 100644
--- a/src/cobalt/media/formats/webm/webm_video_client_unittest.cc
+++ b/src/cobalt/media/formats/webm/webm_video_client_unittest.cc
@@ -15,7 +15,7 @@
 namespace media {
 
 namespace {
-const gfx::Size kCodedSize(321, 243);
+const math::Size kCodedSize(321, 243);
 
 struct CodecTestParams {
   VideoCodecProfile profile;
@@ -67,7 +67,7 @@
 
   VideoDecoderConfig expected_config(
       kCodecVP9, profile, PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709, kCodedSize,
-      gfx::Rect(kCodedSize), kCodedSize, codec_private, Unencrypted());
+      math::Rect(kCodedSize), kCodedSize, codec_private, Unencrypted());
 
   EXPECT_TRUE(config.Matches(expected_config))
       << "Config (" << config.AsHumanReadableString()
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index 79ce44b..9592599 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -18,12 +18,12 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "cobalt/math/rect.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/ranges.h"
 #include "cobalt/media/base/video_frame_provider.h"
 #include "cobalt/media/filters/chunk_demuxer.h"
 #include "cobalt/media/player/buffered_data_source.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
 #include "url/gurl.h"
 
 namespace cobalt {
@@ -35,7 +35,7 @@
  public:
   // Return true if the punch through box should be rendered.  Return false if
   // no punch through box should be rendered.
-  typedef base::Callback<bool(const gfx::Rect&)> SetBoundsCB;
+  typedef base::Callback<bool(const math::Rect&)> SetBoundsCB;
 
   enum NetworkState {
     kNetworkStateEmpty,
@@ -122,7 +122,7 @@
   virtual bool HasAudio() const = 0;
 
   // Dimension of the video.
-  virtual gfx::Size GetNaturalSize() const = 0;
+  virtual math::Size GetNaturalSize() const = 0;
 
   // Getters of playback state.
   virtual bool IsPaused() const = 0;
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 2d91c81..6f18dc7 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -433,10 +433,10 @@
   return pipeline_->HasAudio();
 }
 
-gfx::Size WebMediaPlayerImpl::GetNaturalSize() const {
+math::Size WebMediaPlayerImpl::GetNaturalSize() const {
   DCHECK_EQ(main_loop_, base::MessageLoop::current());
 
-  gfx::Size size;
+  math::Size size;
   pipeline_->GetNaturalVideoSize(&size);
   return size;
 }
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index 55a00c9..a1a51be 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -58,6 +58,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
+#include "cobalt/math/size.h"
 #include "cobalt/media/base/decoder_buffer.h"
 #include "cobalt/media/base/demuxer.h"
 #include "cobalt/media/base/eme_constants.h"
@@ -66,7 +67,6 @@
 #include "cobalt/media/base/video_frame_provider.h"
 #include "cobalt/media/player/web_media_player.h"
 #include "cobalt/media/player/web_media_player_delegate.h"
-#include "ui/gfx/size.h"
 #include "url/gurl.h"
 
 #if defined(OS_STARBOARD)
@@ -147,7 +147,7 @@
   bool HasAudio() const override;
 
   // Dimensions of the video.
-  gfx::Size GetNaturalSize() const override;
+  math::Size GetNaturalSize() const override;
 
   // Getters of playback state.
   bool IsPaused() const override;
diff --git a/src/cobalt/media/sandbox/media_source_demuxer.cc b/src/cobalt/media/sandbox/media_source_demuxer.cc
index e73ff44..8016616 100644
--- a/src/cobalt/media/sandbox/media_source_demuxer.cc
+++ b/src/cobalt/media/sandbox/media_source_demuxer.cc
@@ -250,10 +250,10 @@
   // Try to load it as an ivf first.
   if (LoadIVF(content, Bind(&MediaSourceDemuxer::AppendBuffer,
                             base::Unretained(this)))) {
-    config_.Initialize(::media::kCodecVP9, ::media::VP9PROFILE_MAIN,
-                       ::media::VideoFrame::YV12,
-                       ::media::COLOR_SPACE_HD_REC709, gfx::Size(1, 1),
-                       gfx::Rect(1, 1), gfx::Size(1, 1), NULL, 0, false, false);
+    config_.Initialize(
+        ::media::kCodecVP9, ::media::VP9PROFILE_MAIN, ::media::VideoFrame::YV12,
+        ::media::COLOR_SPACE_HD_REC709, math::Size(1, 1), math::Rect(1, 1),
+        math::Size(1, 1), NULL, 0, false, false);
     valid_ = descs_.size() > 0;
     return;
   }
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index fc1d461..3952dd7 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -70,7 +70,13 @@
         '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
         '<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event',
         '<(DEPTH)/url/url.gyp:url',
-        '<@(cobalt_platform_dependencies)',
+      ],
+      'conditions': [
+        ['sb_evergreen == 0', {
+          'dependencies': [
+            '<@(cobalt_platform_dependencies)',
+          ],
+        }],
       ],
     },
     {
diff --git a/src/cobalt/network/network.gyp b/src/cobalt/network/network.gyp
index c06c9e9..9b79c3a 100644
--- a/src/cobalt/network/network.gyp
+++ b/src/cobalt/network/network.gyp
@@ -52,6 +52,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/network_bridge/network_bridge.gyp:network_bridge',
         '<(DEPTH)/cobalt/storage/storage.gyp:storage',
         '<(DEPTH)/net/net.gyp:net',
@@ -76,8 +77,12 @@
           ],
         }],
         ['enable_configure_request_job_factory == 1', {
-          'dependencies': [
-            '<@(cobalt_platform_dependencies)',
+          'conditions': [
+            ['sb_evergreen == 0', {
+              'dependencies': [
+                '<@(cobalt_platform_dependencies)',
+              ],
+            }],
           ],
           'defines': [
             'ENABLE_CONFIGURE_REQUEST_JOB_FACTORY',
diff --git a/src/cobalt/network/network_delegate.cc b/src/cobalt/network/network_delegate.cc
index 79c1b3a..9c3f936 100644
--- a/src/cobalt/network/network_delegate.cc
+++ b/src/cobalt/network/network_delegate.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/network/network_delegate.h"
 
+#include <set>
+
 #include "cobalt/network/local_network.h"
 #include "cobalt/network/socket_address_parser.h"
 #include "net/base/net_errors.h"
@@ -38,9 +40,9 @@
       url.SchemeIs("data")) {
     return net::OK;
   } else if (https_requirement_ == kHTTPSOptional) {
-    DLOG(WARNING)
-        << "Page must be served over secure scheme, it will fail to load "
-           "in production builds of Cobalt.";
+    DLOG_ONCE(WARNING)
+        << "Pages must be served over secure scheme, otherwise it will fail to "
+           "load in production builds of Cobalt.";
     return net::OK;
   }
 
diff --git a/src/cobalt/network/network_module.cc b/src/cobalt/network/network_module.cc
index d23c4fa..65c15ed 100644
--- a/src/cobalt/network/network_module.cc
+++ b/src/cobalt/network/network_module.cc
@@ -90,6 +90,12 @@
                             custom_proxy_rules));
 }
 
+void NetworkModule::DisableQuic() {
+  task_runner()->PostTask(
+      FROM_HERE, base::Bind(&URLRequestContext::DisableQuic,
+                            base::Unretained(url_request_context_.get())));
+}
+
 void NetworkModule::Initialize(const std::string& user_agent_string,
                                base::EventDispatcher* event_dispatcher) {
   thread_.reset(new base::Thread("NetworkModule"));
diff --git a/src/cobalt/network/network_module.h b/src/cobalt/network/network_module.h
index 27f25de..7d29dca 100644
--- a/src/cobalt/network/network_module.h
+++ b/src/cobalt/network/network_module.h
@@ -103,6 +103,8 @@
 #endif
   void SetProxy(const std::string& custom_proxy_rules);
 
+  void DisableQuic();
+
  private:
   void Initialize(const std::string& user_agent_string,
                   base::EventDispatcher* event_dispatcher);
diff --git a/src/cobalt/network/url_request_context.cc b/src/cobalt/network/url_request_context.cc
index 671de56..9e8bd4a 100644
--- a/src/cobalt/network/url_request_context.cc
+++ b/src/cobalt/network/url_request_context.cc
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/network/job_factory_config.h"
 #include "cobalt/network/network_delegate.h"
 #include "cobalt/network/persistent_cookie_store.h"
@@ -37,9 +38,9 @@
 #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/third_party/quic/platform/api/quic_flags.h"
 #include "net/url_request/data_protocol_handler.h"
 #include "net/url_request/url_request_job_factory_impl.h"
 
@@ -126,11 +127,12 @@
       std::make_unique<net::HttpServerPropertiesImpl>());
 
   net::HttpNetworkSession::Params params;
-#if defined(COBALT_ENABLE_QUIC)
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  params.enable_quic = !command_line->HasSwitch(switches::kDisableQuic);
-  params.use_quic_for_unknown_origins = params.enable_quic;
-#endif
+
+  if (configuration::Configuration::GetInstance()->CobaltEnableQuic()) {
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    params.enable_quic = !command_line->HasSwitch(switches::kDisableQuic);
+    params.use_quic_for_unknown_origins = params.enable_quic;
+  }
 #if defined(ENABLE_IGNORE_CERTIFICATE_ERRORS)
   params.ignore_certificate_errors = ignore_certificate_errors;
   if (ignore_certificate_errors) {
@@ -188,6 +190,11 @@
       std::make_unique<ProxyConfigService>(proxy_config));
 }
 
+void URLRequestContext::DisableQuic() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  storage_.http_network_session()->DisableQuic();
+}
+
 #if defined(ENABLE_DEBUGGER)
 void URLRequestContext::OnQuicToggle(const std::string& /*message*/) {
   DCHECK(storage_.http_network_session());
diff --git a/src/cobalt/network/url_request_context.h b/src/cobalt/network/url_request_context.h
index dfa77fb..614d1f8 100644
--- a/src/cobalt/network/url_request_context.h
+++ b/src/cobalt/network/url_request_context.h
@@ -17,7 +17,9 @@
 
 #include <string>
 
+#include "base/basictypes.h"
 #include "base/macros.h"
+#include "base/threading/thread_checker.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/log/net_log.h"
 #include "net/url_request/url_request_context.h"
@@ -45,7 +47,10 @@
 
   void SetProxy(const std::string& custom_proxy_rules);
 
+  void DisableQuic();
+
  private:
+  THREAD_CHECKER(thread_checker_);
   net::URLRequestContextStorage storage_;
   scoped_refptr<net::CookieMonster::PersistentCookieStore>
       persistent_cookie_store_;
diff --git a/src/cobalt/render_tree/animations/animate_node.cc b/src/cobalt/render_tree/animations/animate_node.cc
index 53ebb60..7e7c460 100644
--- a/src/cobalt/render_tree/animations/animate_node.cc
+++ b/src/cobalt/render_tree/animations/animate_node.cc
@@ -75,6 +75,7 @@
   void Visit(CompositionNode* composition) override { VisitNode(composition); }
   void Visit(FilterNode* text) override { VisitNode(text); }
   void Visit(ImageNode* image) override { VisitNode(image); }
+  void Visit(LottieNode* lottie) override { VisitNode(lottie); }
   void Visit(MatrixTransform3DNode* transform) override {
     VisitNode(transform);
   }
@@ -249,6 +250,7 @@
   void Visit(CompositionNode* composition) override { VisitNode(composition); }
   void Visit(FilterNode* text) override { VisitNode(text); }
   void Visit(ImageNode* image) override { VisitNode(image); }
+  void Visit(LottieNode* lottie) override { VisitNode(lottie); }
   void Visit(MatrixTransform3DNode* transform) override {
     VisitNode(transform);
   }
@@ -404,6 +406,7 @@
   void Visit(CompositionNode* composition) override { VisitNode(composition); }
   void Visit(FilterNode* text) override { VisitNode(text); }
   void Visit(ImageNode* image) override { VisitNode(image); }
+  void Visit(LottieNode* lottie) override { VisitNode(lottie); }
   void Visit(MatrixTransform3DNode* transform) override {
     VisitNode(transform);
   }
diff --git a/src/cobalt/render_tree/child_iterator.h b/src/cobalt/render_tree/child_iterator.h
index a0c551d..bb90ae4 100644
--- a/src/cobalt/render_tree/child_iterator.h
+++ b/src/cobalt/render_tree/child_iterator.h
@@ -19,6 +19,7 @@
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/node.h"
diff --git a/src/cobalt/render_tree/dump_render_tree_to_string.cc b/src/cobalt/render_tree/dump_render_tree_to_string.cc
index 72c9e54..b601059 100644
--- a/src/cobalt/render_tree/dump_render_tree_to_string.cc
+++ b/src/cobalt/render_tree/dump_render_tree_to_string.cc
@@ -20,6 +20,7 @@
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/node_visitor.h"
@@ -48,6 +49,7 @@
   void Visit(CompositionNode* composition) override;
   void Visit(FilterNode* text) override;
   void Visit(ImageNode* image) override;
+  void Visit(LottieNode* lottie) override;
   void Visit(MatrixTransform3DNode* transform) override;
   void Visit(MatrixTransformNode* transform) override;
   void Visit(PunchThroughVideoNode* punch_through) override;
@@ -134,6 +136,11 @@
   result_ << "\n";
 }
 
+void DebugTreePrinter::Visit(LottieNode* lottie) {
+  AddNamedNodeString(lottie, "LottieNode");
+  result_ << "\n";
+}
+
 void DebugTreePrinter::Visit(MatrixTransform3DNode* transform) {
   AddNamedNodeString(transform, "MatrixTransform3DNode");
   result_ << "\n";
diff --git a/src/cobalt/render_tree/lottie_animation.h b/src/cobalt/render_tree/lottie_animation.h
new file mode 100644
index 0000000..5d6ab17
--- /dev/null
+++ b/src/cobalt/render_tree/lottie_animation.h
@@ -0,0 +1,42 @@
+// 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_RENDER_TREE_LOTTIE_ANIMATION_H_
+#define COBALT_RENDER_TREE_LOTTIE_ANIMATION_H_
+
+#include "base/time/time.h"
+#include "cobalt/render_tree/image.h"
+
+namespace cobalt {
+namespace render_tree {
+
+// The LottieAnimation type is an abstract base class that represents a stored
+// Lottie animation. When constructing a render tree, external Lottie animations
+// can be introduced by adding an LottieNode and associating it with a specific
+// LottieAnimation object.
+class LottieAnimation : public Image {
+ public:
+  virtual void SetAnimationTime(base::TimeDelta animation_time) = 0;
+
+ protected:
+  virtual ~LottieAnimation() {}
+
+  // Allow the reference counting system access to our destructor.
+  friend class base::RefCountedThreadSafe<LottieAnimation>;
+};
+
+}  // namespace render_tree
+}  // namespace cobalt
+
+#endif  // COBALT_RENDER_TREE_LOTTIE_ANIMATION_H_
diff --git a/src/cobalt/render_tree/lottie_node.cc b/src/cobalt/render_tree/lottie_node.cc
new file mode 100644
index 0000000..9b75b6f
--- /dev/null
+++ b/src/cobalt/render_tree/lottie_node.cc
@@ -0,0 +1,31 @@
+// 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/render_tree/lottie_node.h"
+
+#include "cobalt/render_tree/node_visitor.h"
+
+namespace cobalt {
+namespace render_tree {
+
+LottieNode::Builder::Builder(const scoped_refptr<LottieAnimation>& animation,
+                             const math::RectF destination_rect)
+    : animation(animation), destination_rect(destination_rect) {}
+
+void LottieNode::Accept(NodeVisitor* visitor) { visitor->Visit(this); }
+
+math::RectF LottieNode::GetBounds() const { return data_.destination_rect; }
+
+}  // namespace render_tree
+}  // namespace cobalt
diff --git a/src/cobalt/render_tree/lottie_node.h b/src/cobalt/render_tree/lottie_node.h
new file mode 100644
index 0000000..c7dc889
--- /dev/null
+++ b/src/cobalt/render_tree/lottie_node.h
@@ -0,0 +1,72 @@
+// 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_RENDER_TREE_LOTTIE_NODE_H_
+#define COBALT_RENDER_TREE_LOTTIE_NODE_H_
+
+#include "base/compiler_specific.h"
+#include "cobalt/base/type_id.h"
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/lottie_animation.h"
+#include "cobalt/render_tree/node.h"
+
+namespace cobalt {
+namespace render_tree {
+
+// A Lottie animation.
+class LottieNode : public Node {
+ public:
+  struct Builder {
+    Builder(const Builder&) = default;
+
+    Builder(const scoped_refptr<LottieAnimation>& animation,
+            const math::RectF destination_rect);
+
+    bool operator==(const Builder& other) const {
+      return animation == other.animation &&
+             destination_rect == other.destination_rect &&
+             animation_time == other.animation_time;
+    }
+
+    // Information about the contents of the original animation file.
+    scoped_refptr<LottieAnimation> animation;
+
+    // The destination rectangle into which the animation will be rasterized.
+    math::RectF destination_rect;
+
+    // The time that the animation should currently be playing at.
+    base::TimeDelta animation_time;
+  };
+
+  // Forwarding constructor to the set of Builder constructors.
+  template <typename... Args>
+  LottieNode(Args&&... args) : data_(std::forward<Args>(args)...) {}
+
+  void Accept(NodeVisitor* visitor) override;
+  math::RectF GetBounds() const override;
+
+  base::TypeId GetTypeId() const override {
+    return base::GetTypeId<LottieNode>();
+  }
+
+  const Builder& data() const { return data_; }
+
+ private:
+  const Builder data_;
+};
+
+}  // namespace render_tree
+}  // namespace cobalt
+
+#endif  // COBALT_RENDER_TREE_LOTTIE_NODE_H_
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
index c81fb73..31fce01 100644
--- a/src/cobalt/render_tree/mock_resource_provider.h
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -24,6 +24,7 @@
 #include "cobalt/render_tree/font_provider.h"
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/lottie_animation.h"
 #include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/render_tree/typeface.h"
@@ -81,6 +82,9 @@
                      render_tree::FontProvider* font_provider,
                      render_tree::FontVector* maybe_used_fonts));
 
+  MOCK_METHOD2(CreateLottieAnimationMock,
+               LottieAnimation*(const char* data, size_t length));
+
   MOCK_METHOD2(CreateMeshMock,
                render_tree::Mesh*(std::vector<render_tree::Mesh::Vertex>*,
                                   render_tree::Mesh::DrawMode));
@@ -161,6 +165,12 @@
     return scoped_refptr<render_tree::GlyphBuffer>(
         CreateGlyphBufferMock(utf8_string, font.get()));
   }
+  scoped_refptr<LottieAnimation> CreateLottieAnimation(const char* data,
+                                                       size_t length) {
+    return scoped_refptr<LottieAnimation>(
+        CreateLottieAnimationMock(data, length));
+  }
+
   virtual scoped_refptr<Mesh> CreateMesh(
       std::unique_ptr<std::vector<Mesh::Vertex> > vertices,
       Mesh::DrawMode draw_mode) {
diff --git a/src/cobalt/render_tree/node_visitor.h b/src/cobalt/render_tree/node_visitor.h
index e60ae62..abe184c 100644
--- a/src/cobalt/render_tree/node_visitor.h
+++ b/src/cobalt/render_tree/node_visitor.h
@@ -22,6 +22,7 @@
 class CompositionNode;
 class FilterNode;
 class ImageNode;
+class LottieNode;
 class MatrixTransform3DNode;
 class MatrixTransformNode;
 class PunchThroughVideoNode;
@@ -43,6 +44,7 @@
   virtual void Visit(CompositionNode* composition) = 0;
   virtual void Visit(FilterNode* text) = 0;
   virtual void Visit(ImageNode* image) = 0;
+  virtual void Visit(LottieNode* lottie) = 0;
   virtual void Visit(MatrixTransform3DNode* transform) = 0;
   virtual void Visit(MatrixTransformNode* transform) = 0;
   virtual void Visit(PunchThroughVideoNode* punch_through) = 0;
diff --git a/src/cobalt/render_tree/node_visitor_test.cc b/src/cobalt/render_tree/node_visitor_test.cc
index 8ad3786..adbac19 100644
--- a/src/cobalt/render_tree/node_visitor_test.cc
+++ b/src/cobalt/render_tree/node_visitor_test.cc
@@ -27,6 +27,7 @@
 #include "cobalt/render_tree/font.h"
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/punch_through_video_node.h"
@@ -48,6 +49,7 @@
 using cobalt::render_tree::GlyphBuffer;
 using cobalt::render_tree::Image;
 using cobalt::render_tree::ImageNode;
+using cobalt::render_tree::LottieNode;
 using cobalt::render_tree::MatrixTransform3DNode;
 using cobalt::render_tree::MatrixTransformNode;
 using cobalt::render_tree::NodeVisitor;
@@ -64,6 +66,7 @@
   MOCK_METHOD1(Visit, void(CompositionNode* composition));
   MOCK_METHOD1(Visit, void(FilterNode* image));
   MOCK_METHOD1(Visit, void(ImageNode* image));
+  MOCK_METHOD1(Visit, void(LottieNode* lottie));
   MOCK_METHOD1(Visit, void(MatrixTransform3DNode* matrix_transform_3d_node));
   MOCK_METHOD1(Visit, void(MatrixTransformNode* matrix_transform_node));
   MOCK_METHOD1(Visit, void(PunchThroughVideoNode* punch_through_video_node));
diff --git a/src/cobalt/render_tree/render_tree.gyp b/src/cobalt/render_tree/render_tree.gyp
index 8f74058..ddced7b 100644
--- a/src/cobalt/render_tree/render_tree.gyp
+++ b/src/cobalt/render_tree/render_tree.gyp
@@ -45,8 +45,11 @@
         'font_provider.h',
         'glyph.h',
         'glyph_buffer.h',
+        'lottie_animation.h',
         'image_node.cc',
         'image_node.h',
+        'lottie_node.cc',
+        'lottie_node.h',
         'map_to_mesh_filter.h',
         'matrix_transform_3d_node.cc',
         'matrix_transform_3d_node.h',
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index deb0bf8..8820eed 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -25,6 +25,7 @@
 #include "cobalt/render_tree/font_provider.h"
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/lottie_animation.h"
 #include "cobalt/render_tree/mesh.h"
 #include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/typeface.h"
@@ -206,6 +207,12 @@
                              render_tree::FontProvider* font_provider,
                              render_tree::FontVector* maybe_used_fonts) = 0;
 
+  // This function will wrap the given Lottie animation data into a
+  // LottieAnimation that can be used in a render tree, and return it to the
+  // caller.
+  virtual scoped_refptr<LottieAnimation> CreateLottieAnimation(
+      const char* data, size_t length) = 0;
+
   // Consumes a list of vertices and returns a Mesh instance.
   virtual scoped_refptr<Mesh> CreateMesh(
       std::unique_ptr<std::vector<Mesh::Vertex> > vertices,
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index 90d0f75..5935554 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -24,6 +24,7 @@
 #include "cobalt/render_tree/font.h"
 #include "cobalt/render_tree/font_provider.h"
 #include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/lottie_animation.h"
 #include "cobalt/render_tree/mesh.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "third_party/ots/include/opentype-sanitiser.h"
@@ -411,6 +412,13 @@
         glyph_bounds.height())));
   }
 
+  scoped_refptr<LottieAnimation> CreateLottieAnimation(const char* data,
+                                                       size_t length) override {
+    SB_UNREFERENCED_PARAMETER(data);
+    SB_UNREFERENCED_PARAMETER(length);
+    return scoped_refptr<LottieAnimation>(NULL);
+  }
+
   // Create a mesh which can map replaced boxes to 3D shapes.
   scoped_refptr<render_tree::Mesh> CreateMesh(
       std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
diff --git a/src/cobalt/renderer/backend/egl/display.cc b/src/cobalt/renderer/backend/egl/display.cc
index af436ad..9c43a94 100644
--- a/src/cobalt/renderer/backend/egl/display.cc
+++ b/src/cobalt/renderer/backend/egl/display.cc
@@ -17,6 +17,7 @@
 
 #include "cobalt/renderer/backend/egl/display.h"
 
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/backend/egl/render_target.h"
 #include "cobalt/renderer/egl_and_gles.h"
 
@@ -49,16 +50,18 @@
 DisplayRenderTargetEGL::DisplayRenderTargetEGL(EGLDisplay display,
                                                EGLSurface surface)
     : display_(display), surface_(surface) {
-#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-  // Configure the surface to preserve contents on swap.
-  EGLBoolean surface_attrib_set = EGL_CALL_SIMPLE(eglSurfaceAttrib(
-      display_, surface_, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
-  // NOTE: Must check eglGetError() to clear any error flags and also check
-  // the return value of eglSurfaceAttrib since some implementations may not
-  // set the error condition.
-  content_preserved_on_swap_ = EGL_CALL_SIMPLE(eglGetError()) == EGL_SUCCESS &&
-                               surface_attrib_set == EGL_TRUE;
-#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+  if (configuration::Configuration::GetInstance()
+          ->CobaltRenderDirtyRegionOnly()) {
+    // Configure the surface to preserve contents on swap.
+    EGLBoolean surface_attrib_set = EGL_CALL_SIMPLE(eglSurfaceAttrib(
+        display_, surface_, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
+    // NOTE: Must check eglGetError() to clear any error flags and also check
+    // the return value of eglSurfaceAttrib since some implementations may not
+    // set the error condition.
+    content_preserved_on_swap_ =
+        EGL_CALL_SIMPLE(eglGetError()) == EGL_SUCCESS &&
+        surface_attrib_set == EGL_TRUE;
+  }
 
   // Query and cache information about the surface now that we have created it.
   EGLint egl_surface_width;
diff --git a/src/cobalt/renderer/backend/egl/egl_backend.gyp b/src/cobalt/renderer/backend/egl/egl_backend.gyp
index 5c90301..cf2da10 100644
--- a/src/cobalt/renderer/backend/egl/egl_backend.gyp
+++ b/src/cobalt/renderer/backend/egl/egl_backend.gyp
@@ -48,6 +48,7 @@
         'COBALT_EGL_SWAP_INTERVAL=<(cobalt_egl_swap_interval)',
       ],
       'dependencies': [
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
       ],
 
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc
index 94a6269..f5524ea 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -23,6 +23,7 @@
 #include "base/debug/leak_annotations.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/graphics_system.h"
 #include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
@@ -32,6 +33,10 @@
 #include "cobalt/renderer/backend/egl/utils.h"
 #include "cobalt/renderer/egl_and_gles.h"
 
+#if defined(GLES3_SUPPORTED) && SB_API_VERSION >= SB_GLES3_DEPRECATED_VERSION
+#error "Support for gles3 features has been deprecated."
+#endif
+
 namespace cobalt {
 namespace renderer {
 namespace backend {
@@ -55,7 +60,7 @@
       display_(display),
       config_(config),
       is_current_(false) {
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   context_ = CreateGLES3Context(display, config, resource_context->context());
 #else
   // Create an OpenGL ES 2.0 context.
@@ -533,7 +538,9 @@
   // current implementation of eglSwapBuffers() does nothing for PBuffers,
   // so only check for framebuffer render targets.
   if (surface->GetPlatformHandle() == 0) {
-    EGL_CALL_SIMPLE(eglSwapInterval(display_, COBALT_EGL_SWAP_INTERVAL));
+    EGL_CALL_SIMPLE(eglSwapInterval(
+        display_,
+        configuration::Configuration::GetInstance()->CobaltEglSwapInterval()));
     EGL_CALL_SIMPLE(eglSwapBuffers(display_, surface->GetSurface()));
     EGLint swap_err = EGL_CALL_SIMPLE(eglGetError());
     if (swap_err != EGL_SUCCESS) {
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.cc b/src/cobalt/renderer/backend/egl/graphics_system.cc
index 16e9633..5761596 100644
--- a/src/cobalt/renderer/backend/egl/graphics_system.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_system.cc
@@ -24,12 +24,8 @@
 #include "base/trace_event/trace_event.h"
 #endif
 #include "starboard/common/optional.h"
-#if defined(GLES3_SUPPORTED)
-#include "cobalt/renderer/backend/egl/texture_data_pbo.h"
-#else
-#include "cobalt/renderer/backend/egl/texture_data_cpu.h"
-#endif
 
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/backend/egl/display.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 #include "cobalt/renderer/backend/egl/texture.h"
@@ -40,6 +36,15 @@
 
 #include "cobalt/renderer/egl_and_gles.h"
 
+#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION >= SB_GLES3_DEPRECATED_VERSION
+#error "Support for gles3 features has been deprecated."
+#endif
+#include "cobalt/renderer/backend/egl/texture_data_pbo.h"
+#else
+#include "cobalt/renderer/backend/egl/texture_data_cpu.h"
+#endif
+
 namespace cobalt {
 namespace renderer {
 namespace backend {
@@ -151,13 +156,13 @@
 
   // Setup our configuration to support RGBA and compatibility with PBuffer
   // objects (for offscreen rendering).
+  EGLint egl_bit =
+      configuration::Configuration::GetInstance()->CobaltRenderDirtyRegionOnly()
+          ? EGL_WINDOW_BIT | EGL_PBUFFER_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT
+          : EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
   EGLint attribute_list[] = {
     EGL_SURFACE_TYPE,  // this must be first
-    EGL_WINDOW_BIT | EGL_PBUFFER_BIT
-#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-        | EGL_SWAP_BEHAVIOR_PRESERVED_BIT
-#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-    ,
+    egl_bit,
     EGL_RED_SIZE,
     8,
     EGL_GREEN_SIZE,
@@ -167,32 +172,34 @@
     EGL_ALPHA_SIZE,
     8,
     EGL_RENDERABLE_TYPE,
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
     EGL_OPENGL_ES3_BIT,
 #else
     EGL_OPENGL_ES2_BIT,
-#endif  // #if defined(GLES3_SUPPORTED)
+#endif  // #if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION &&
+        // defined(GLES3_SUPPORTED)
     EGL_NONE
   };
 
   base::Optional<ChooseConfigResult> choose_config_results =
       ChooseConfig(display_, attribute_list, system_window);
 
-#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-  // Try to allow preservation of the frame contents between swap calls --
-  // this will allow rendering of only parts of the frame that have changed.
-  DCHECK_EQ(EGL_SURFACE_TYPE, attribute_list[0]);
-  EGLint& surface_type_value = attribute_list[1];
+  if (configuration::Configuration::GetInstance()
+          ->CobaltRenderDirtyRegionOnly()) {
+    // Try to allow preservation of the frame contents between swap calls --
+    // this will allow rendering of only parts of the frame that have changed.
+    DCHECK_EQ(EGL_SURFACE_TYPE, attribute_list[0]);
+    EGLint& surface_type_value = attribute_list[1];
 
-  // Swap buffer preservation may not be supported. If we failed to find a
-  // config with the EGL_SWAP_BEHAVIOR_PRESERVED_BIT attribute, try again
-  // without it.
-  if (!choose_config_results) {
-    surface_type_value &= ~EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
-    choose_config_results =
-        ChooseConfig(display_, attribute_list, system_window);
+    // Swap buffer preservation may not be supported. If we failed to find a
+    // config with the EGL_SWAP_BEHAVIOR_PRESERVED_BIT attribute, try again
+    // without it.
+    if (!choose_config_results) {
+      surface_type_value &= ~EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
+      choose_config_results =
+          ChooseConfig(display_, attribute_list, system_window);
+    }
   }
-#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 
   DCHECK(choose_config_results);
 
@@ -200,7 +207,7 @@
   system_window_ = system_window;
   window_surface_ = choose_config_results->window_surface;
 
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   resource_context_.emplace(display_, config_);
 #endif
 }
@@ -239,7 +246,7 @@
 // that data from graphics contexts created through this method, we must
 // enable sharing between them and the resource context, which is why we
 // must pass it in here.
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   ResourceContext* resource_context = &(resource_context_.value());
 #else
   ResourceContext* resource_context = NULL;
@@ -250,7 +257,7 @@
 
 std::unique_ptr<TextureDataEGL> GraphicsSystemEGL::AllocateTextureData(
     const math::Size& size, GLenum format) {
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   std::unique_ptr<TextureDataEGL> texture_data(
       new TextureDataPBO(&(resource_context_.value()), size, format));
 #else
@@ -267,7 +274,7 @@
 std::unique_ptr<RawTextureMemoryEGL>
 GraphicsSystemEGL::AllocateRawTextureMemory(size_t size_in_bytes,
                                             size_t alignment) {
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   return std::unique_ptr<RawTextureMemoryEGL>(new RawTextureMemoryPBO(
       &(resource_context_.value()), size_in_bytes, alignment));
 #else
diff --git a/src/cobalt/renderer/backend/egl/texture_data_pbo.cc b/src/cobalt/renderer/backend/egl/texture_data_pbo.cc
index 6b7cd26..52c8f6f 100644
--- a/src/cobalt/renderer/backend/egl/texture_data_pbo.cc
+++ b/src/cobalt/renderer/backend/egl/texture_data_pbo.cc
@@ -13,6 +13,9 @@
 // limitations under the License.
 
 #if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION >= SB_GLES3_DEPRECATED_VERSION
+#error "Support for gles3 features has been deprecated."
+#endif
 
 #include "cobalt/renderer/backend/egl/texture_data_pbo.h"
 
diff --git a/src/cobalt/renderer/backend/egl/texture_data_pbo.h b/src/cobalt/renderer/backend/egl/texture_data_pbo.h
index 84b2dc5..fe78690 100644
--- a/src/cobalt/renderer/backend/egl/texture_data_pbo.h
+++ b/src/cobalt/renderer/backend/egl/texture_data_pbo.h
@@ -16,6 +16,9 @@
 #define COBALT_RENDERER_BACKEND_EGL_TEXTURE_DATA_PBO_H_
 
 #if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION >= SB_GLES3_DEPRECATED_VERSION
+#error "Support for gles3 features has been deprecated."
+#endif
 
 #include <GLES3/gl3.h>
 
diff --git a/src/cobalt/renderer/backend/starboard/default_graphics_system.cc b/src/cobalt/renderer/backend/starboard/default_graphics_system.cc
index f193937..3d646e9 100644
--- a/src/cobalt/renderer/backend/starboard/default_graphics_system.cc
+++ b/src/cobalt/renderer/backend/starboard/default_graphics_system.cc
@@ -13,10 +13,12 @@
 // limitations under the License.
 
 #include <memory>
+#include <string>
 
 #include "cobalt/renderer/backend/default_graphics_system.h"
 
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/backend/blitter/graphics_system.h"
 #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(GLES2)
 #include "cobalt/renderer/backend/egl/graphics_system.h"
@@ -31,12 +33,13 @@
 std::unique_ptr<GraphicsSystem> CreateDefaultGraphicsSystem(
     system_window::SystemWindow* system_window) {
 #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
-  if (SbGetGlesInterface()) {
-    return std::unique_ptr<GraphicsSystem>(
-        new GraphicsSystemEGL(system_window));
-  } else {
+  if (std::string(configuration::Configuration::GetInstance()
+                      ->CobaltRasterizerType()) == "stub") {
     SB_UNREFERENCED_PARAMETER(system_window);
     return std::unique_ptr<GraphicsSystem>(new GraphicsSystemStub());
+  } else {
+    return std::unique_ptr<GraphicsSystem>(
+        new GraphicsSystemEGL(system_window));
   }
 #else  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
 #if SB_HAS(GLES2)
diff --git a/src/cobalt/renderer/backend/starboard/platform_backend.gyp b/src/cobalt/renderer/backend/starboard/platform_backend.gyp
index a10077c..d4e7606 100644
--- a/src/cobalt/renderer/backend/starboard/platform_backend.gyp
+++ b/src/cobalt/renderer/backend/starboard/platform_backend.gyp
@@ -18,25 +18,17 @@
       'target_name': 'renderer_platform_backend',
       'type': 'static_library',
 
-      'conditions': [
-        ['rasterizer_type == "stub"', {
-          'sources': [
-            'default_graphics_system_stub.cc',
-          ],
-        }, {
-          'includes': [
-            '../../renderer_parameters_setup.gypi',
-          ],
+      'includes': [
+        '../../renderer_parameters_setup.gypi',
+      ],
 
-          'dependencies': [
-            '../blitter/blitter_backend.gyp:blitter_backend',
-            '../egl/egl_backend.gyp:egl_backend',
-          ],
+      'dependencies': [
+        '../blitter/blitter_backend.gyp:blitter_backend',
+        '../egl/egl_backend.gyp:egl_backend',
+      ],
 
-          'sources': [
-            'default_graphics_system.cc',
-          ]
-        }],
+      'sources': [
+        'default_graphics_system.cc',
       ],
     },
   ],
diff --git a/src/cobalt/renderer/egl_and_gles.h b/src/cobalt/renderer/egl_and_gles.h
index 6dc8ceb..9f808b8 100644
--- a/src/cobalt/renderer/egl_and_gles.h
+++ b/src/cobalt/renderer/egl_and_gles.h
@@ -88,6 +88,15 @@
 }  // namespace cobalt
 #endif  // SB_API_VERSION >= 11
 
+// The following *_CALL, *_CALL_SIMPLE macros provide a mechanism to
+// transparently use the Starboard OpenGL ES and EGL interfaces if available,
+// while also providing error-checking. The *_CALL macros should be used when
+// the return value of the function to invoke is not desired, otherwise use
+// *_CALL_SIMPLE to propagate the returned value. For example:
+//
+// eglMakeCurrent(...) becomes EGL_CALL(eglMakeCurrent(...)).
+// ... = eglGetError() becomes ... = EGL_CALL_SIMPLE(eglGetError()).
+
 #define EGL_CALL(x)    \
   do {                 \
     EGL_CALL_PREFIX x; \
diff --git a/src/cobalt/renderer/get_default_rasterizer_for_platform.cc b/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
index 6e9ca04..98d2337 100644
--- a/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
+++ b/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
@@ -16,6 +16,7 @@
 
 #include "cobalt/renderer/get_default_rasterizer_for_platform.h"
 
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/backend/graphics_context.h"
 #include "cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h"
 #include "cobalt/renderer/rasterizer/blitter/software_rasterizer.h"
@@ -32,7 +33,6 @@
 
 namespace {
 
-#if COBALT_FORCE_STUB_RASTERIZER
 std::unique_ptr<rasterizer::Rasterizer> CreateStubRasterizer(
     backend::GraphicsContext* graphics_context,
     const RendererModule::Options& options) {
@@ -40,7 +40,6 @@
   return std::unique_ptr<rasterizer::Rasterizer>(
       new rasterizer::stub::Rasterizer());
 }
-#endif  // COBALT_FORCE_STUB_RASTERIZER
 
 #if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION || SB_HAS(GLES2)
 std::unique_ptr<rasterizer::Rasterizer> CreateGLESSoftwareRasterizer(
@@ -105,15 +104,18 @@
 }  // namespace
 
 RasterizerInfo GetDefaultRasterizerForPlatform() {
-#if COBALT_FORCE_STUB_RASTERIZER
-  return {"stub", base::Bind(&CreateStubRasterizer)};
-#elif SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
+  std::string rasterizer_type =
+      configuration::Configuration::GetInstance()->CobaltRasterizerType();
+  if (rasterizer_type == "stub") {
+    return {"stub", base::Bind(&CreateStubRasterizer)};
+  }
+#if SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
   if (SbGetGlesInterface()) {
-#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
-    return {"gles", base::Bind(&CreateGLESHardwareRasterizer)};
-#else
-    return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
-#endif
+    if (rasterizer_type == "direct-gles") {
+      return {"gles", base::Bind(&CreateGLESHardwareRasterizer)};
+    } else {
+      return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
+    }
   } else {
     SB_LOG(ERROR)
         << "GLES2 must be available.";
@@ -122,18 +124,18 @@
   }
 #else   // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
 #if SB_HAS(GLES2)
-#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
-  return {"gles", base::Bind(&CreateGLESHardwareRasterizer)};
-#else
-  return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
-#endif
+  if (rasterizer_type == "direct-gles") {
+    return {"gles", base::Bind(&CreateGLESHardwareRasterizer)};
+  } else {
+    return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
+  }
 #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."
   return {"", NULL};
 #endif
-#endif  // #if COBALT_FORCE_STUB_RASTERIZER
+#endif  // SB_API_VERSION >= SB_ALL_RENDERERS_REQUIRED_VERSION
 }
 
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index 2200e63..dfdc1e9 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -20,6 +20,7 @@
 #include "base/bind.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/render_tree/resource_provider_stub.h"
 #include "cobalt/renderer/backend/blitter/graphics_context.h"
 #include "cobalt/renderer/backend/blitter/render_target.h"
@@ -62,9 +63,7 @@
 #if defined(ENABLE_DEBUGGER)
   void OnToggleHighlightSoftwareDraws(const std::string& message);
 #endif
-#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   void SetupLastFrameSurface(int width, int height);
-#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   THREAD_CHECKER(thread_checker_);
 
   backend::GraphicsContextBlitter* context_;
@@ -159,11 +158,12 @@
       base::polymorphic_downcast<backend::RenderTargetBlitter*>(
           render_target.get());
 
-#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
-  if (!SbBlitterIsSurfaceValid(current_frame_)) {
-    SetupLastFrameSurface(width, height);
+  if (configuration::Configuration::GetInstance()
+          ->CobaltRenderDirtyRegionOnly()) {
+    if (!SbBlitterIsSurfaceValid(current_frame_)) {
+      SetupLastFrameSurface(width, height);
+    }
   }
-#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 
   SbBlitterRenderTarget visitor_render_target = kSbBlitterInvalidRenderTarget;
   if (SbBlitterIsSurfaceValid(current_frame_)) {
@@ -272,13 +272,11 @@
   return resource_provider_.get();
 }
 
-#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 void HardwareRasterizer::Impl::SetupLastFrameSurface(int width, int height) {
   current_frame_ =
       SbBlitterCreateRenderTargetSurface(context_->GetSbBlitterDevice(), width,
                                          height, kSbBlitterSurfaceFormatRGBA8);
 }
-#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 
 HardwareRasterizer::HardwareRasterizer(
     backend::GraphicsContext* graphics_context, int skia_atlas_width,
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache_test.cc b/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache_test.cc
index 3aeac46..d12f4b8 100644
--- a/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache_test.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient_cache_test.cc
@@ -20,6 +20,7 @@
 #include "cobalt/render_tree/brush.h"
 #include "cobalt/render_tree/color_rgba.h"
 #include "starboard/blitter.h"
+#include "starboard/configuration_constants.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -112,7 +113,7 @@
 
   virtual void SetUp() {
     pixel_format = kSbBlitterInvalidPixelDataFormat;
-    switch (SB_PREFERRED_RGBA_BYTE_ORDER) {
+    switch (kSbPreferredRgbaByteOrder) {
       case SB_PREFERRED_RGBA_BYTE_ORDER_RGBA:
         pixel_format = kSbBlitterPixelDataFormatRGBA8;
         break;
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index af03b7b..e2a96c4 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -55,6 +55,7 @@
 
       'dependencies': [
         '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
         '<(DEPTH)/cobalt/renderer/rasterizer/common/common.gyp:common',
         '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
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 296ff78..30acbf7 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -275,6 +275,11 @@
           image_node->data().destination_rect)));
 }
 
+void RenderTreeNodeVisitor::Visit(render_tree::LottieNode* lottie_node) {
+  // Use Skottie to render Lottie animations.
+  RenderWithSoftwareRenderer(lottie_node);
+}
+
 void RenderTreeNodeVisitor::Visit(
     render_tree::MatrixTransform3DNode* matrix_transform_3d_node) {
   TRACE_EVENT0_IF_ENABLED("Visit(MatrixTransform3DNode)");
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 a35e804..b349c8c 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
@@ -26,6 +26,7 @@
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/node_visitor.h"
@@ -74,6 +75,7 @@
   void Visit(render_tree::CompositionNode* composition_node) override;
   void Visit(render_tree::FilterNode* filter_node) override;
   void Visit(render_tree::ImageNode* image_node) override;
+  void Visit(render_tree::LottieNode* lottie_node) override;
   void Visit(
       render_tree::MatrixTransform3DNode* matrix_transform_3d_node) override;
   void Visit(render_tree::MatrixTransformNode* matrix_transform_node) override;
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 56f77ff..a242f7a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -187,6 +187,12 @@
   return skia_resource_provider_->CreateGlyphBuffer(utf8_string, font);
 }
 
+scoped_refptr<render_tree::LottieAnimation>
+ResourceProvider::CreateLottieAnimation(const char* data, size_t length) {
+  NOTIMPLEMENTED();
+  return scoped_refptr<render_tree::LottieAnimation>(NULL);
+}
+
 scoped_refptr<render_tree::Mesh> ResourceProvider::CreateMesh(
     std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
     render_tree::Mesh::DrawMode draw_mode) {
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index 1e736b9..869ad35 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -104,6 +104,9 @@
       const std::string& utf8_string,
       const scoped_refptr<render_tree::Font>& font) override;
 
+  scoped_refptr<render_tree::LottieAnimation> CreateLottieAnimation(
+      const char* data, size_t length) override;
+
   scoped_refptr<render_tree::Mesh> CreateMesh(
       std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
       render_tree::Mesh::DrawMode draw_mode) override;
diff --git a/src/cobalt/renderer/rasterizer/common/find_node.cc b/src/cobalt/renderer/rasterizer/common/find_node.cc
index 3c1a6cf..4a04217 100644
--- a/src/cobalt/renderer/rasterizer/common/find_node.cc
+++ b/src/cobalt/renderer/rasterizer/common/find_node.cc
@@ -45,6 +45,9 @@
   void Visit(render_tree::ImageNode* image_node) override {
     VisitNode(image_node);
   }
+  void Visit(render_tree::LottieNode* lottie_node) override {
+    VisitNode(lottie_node);
+  }
   void Visit(render_tree::MatrixTransform3DNode* transform_3d_node) override {
     VisitNode(transform_3d_node);
   }
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index d6bbdae..84303d5 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -612,6 +612,11 @@
                     : DrawObjectManager::kBlendSrcAlpha);
 }
 
+void RenderTreeNodeVisitor::Visit(render_tree::LottieNode* lottie_node) {
+  // Use Skottie to render Lottie animations.
+  FallbackRasterize(lottie_node);
+}
+
 void RenderTreeNodeVisitor::Visit(
     render_tree::PunchThroughVideoNode* video_node) {
   if (!IsVisible(video_node->GetBounds())) {
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
index 69fd81e..a6d45d6 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
@@ -26,6 +26,7 @@
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/node_visitor.h"
@@ -77,6 +78,7 @@
   void Visit(render_tree::MatrixTransformNode* transform_node) override;
   void Visit(render_tree::FilterNode* filter_node) override;
   void Visit(render_tree::ImageNode* image_node) override;
+  void Visit(render_tree::LottieNode* lottie_node) override;
   void Visit(render_tree::PunchThroughVideoNode* video_node) override;
   void Visit(render_tree::RectNode* rect_node) override;
   void Visit(render_tree::RectShadowNode* shadow_node) override;
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 361bdfd..6329907 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -16,7 +16,10 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
+#include "base/path_service.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/math/size_f.h"
@@ -33,6 +36,8 @@
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_animation.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/punch_through_video_node.h"
@@ -92,6 +97,8 @@
 using cobalt::render_tree::ImageData;
 using cobalt::render_tree::ImageDataDescriptor;
 using cobalt::render_tree::ImageNode;
+using cobalt::render_tree::LottieAnimation;
+using cobalt::render_tree::LottieNode;
 using cobalt::render_tree::LinearGradientBrush;
 using cobalt::render_tree::MapToMeshFilter;
 using cobalt::render_tree::MatrixTransform3DNode;
@@ -3969,6 +3976,7 @@
 
   return new MatrixTransform3DNode(map_to_mesh_filter, projection * model_view);
 }
+
 }  // namespace
 
 TEST_F(PixelTest, MapToMeshRGBTest) {
@@ -4058,6 +4066,57 @@
   TestTree(new CompositionNode(std::move(composition_node_builder)));
 }
 
+#if !SB_HAS(BLITTER)
+
+namespace {
+
+base::FilePath GetTestFilePath(const char* file_name) {
+  base::FilePath data_directory;
+  CHECK(base::PathService::Get(base::DIR_TEST_DATA, &data_directory));
+  return data_directory.Append(FILE_PATH_LITERAL("cobalt"))
+      .Append(FILE_PATH_LITERAL("renderer"))
+      .Append(FILE_PATH_LITERAL("rasterizer"))
+      .Append(FILE_PATH_LITERAL("testdata"))
+      .Append(FILE_PATH_LITERAL(file_name));
+}
+
+std::vector<uint8> GetFileData(const base::FilePath& file_path) {
+  int64 size;
+  std::vector<uint8> image_data;
+
+  bool success = base::GetFileSize(file_path, &size);
+
+  CHECK(success) << "Could not get file size.";
+  CHECK_GT(size, 0);
+
+  image_data.resize(static_cast<size_t>(size));
+
+  int num_of_bytes =
+      base::ReadFile(file_path, reinterpret_cast<char*>(&image_data[0]),
+                     static_cast<int>(size));
+
+  CHECK_EQ(num_of_bytes, static_cast<int>(image_data.size()))
+      << "Could not read '" << file_path.value() << "'.";
+  return image_data;
+}
+
+}  // namespace
+
+TEST_F(PixelTest, SimpleLottieAnimationTest) {
+  std::vector<uint8> animation_data =
+      GetFileData(GetTestFilePath("circle.json"));
+  scoped_refptr<LottieAnimation> animation =
+      GetResourceProvider()->CreateLottieAnimation(
+          reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+  LottieNode::Builder node_builder =
+      LottieNode::Builder(animation, RectF(output_surface_size()));
+  node_builder.animation_time = base::TimeDelta::FromSecondsD(0.25);
+  scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+  TestTree(lottie_node);
+}
+
+#endif  // !SB_HAS(BLITTER)
+
 }  // namespace rasterizer
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/rasterizer.gyp b/src/cobalt/renderer/rasterizer/rasterizer.gyp
index 2f0b2cf..055eea3 100644
--- a/src/cobalt/renderer/rasterizer/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/rasterizer.gyp
@@ -21,20 +21,13 @@
       'target_name': 'rasterizer',
       'type': 'none',
 
-      'conditions': [
-        ['rasterizer_type == "stub"', {
-          'dependencies': [
-            '<(DEPTH)/cobalt/renderer/rasterizer/stub/rasterizer.gyp:rasterizer',
-          ],
-        }, {
-          'dependencies': [
-            '<(DEPTH)/cobalt/renderer/rasterizer/skia/rasterizer.gyp:hardware_rasterizer',
-            '<(DEPTH)/cobalt/renderer/rasterizer/egl/rasterizer.gyp:software_rasterizer',
-            '<(DEPTH)/cobalt/renderer/rasterizer/egl/rasterizer.gyp:hardware_rasterizer',
-            '<(DEPTH)/cobalt/renderer/rasterizer/blitter/rasterizer.gyp:hardware_rasterizer',
-            '<(DEPTH)/cobalt/renderer/rasterizer/blitter/rasterizer.gyp:software_rasterizer',
-          ]
-        }],
+      'dependencies': [
+        '<(DEPTH)/cobalt/renderer/rasterizer/stub/rasterizer.gyp:rasterizer',
+        '<(DEPTH)/cobalt/renderer/rasterizer/skia/rasterizer.gyp:hardware_rasterizer',
+        '<(DEPTH)/cobalt/renderer/rasterizer/egl/rasterizer.gyp:software_rasterizer',
+        '<(DEPTH)/cobalt/renderer/rasterizer/egl/rasterizer.gyp:hardware_rasterizer',
+        '<(DEPTH)/cobalt/renderer/rasterizer/blitter/rasterizer.gyp:hardware_rasterizer',
+        '<(DEPTH)/cobalt/renderer/rasterizer/blitter/rasterizer.gyp:software_rasterizer',
       ],
     },
   ],
diff --git a/src/cobalt/renderer/rasterizer/skia/common.gyp b/src/cobalt/renderer/rasterizer/skia/common.gyp
index 0bb0473..f0c9b66 100644
--- a/src/cobalt/renderer/rasterizer/skia/common.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/common.gyp
@@ -36,6 +36,8 @@
         'render_tree_node_visitor.h',
         'scratch_surface_cache.cc',
         'scratch_surface_cache.h',
+        'skottie_animation.cc',
+        'skottie_animation.h',
         'text_shaper.cc',
         'text_shaper.h',
         'typeface.cc',
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index ad4768c..df953ee 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -33,6 +33,7 @@
 #include "cobalt/renderer/rasterizer/skia/hardware_mesh.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
 #include "cobalt/renderer/rasterizer/skia/typeface.h"
 #include "third_party/ots/include/opentype-sanitiser.h"
 #include "third_party/ots/include/ots-memory-stream.h"
@@ -214,9 +215,7 @@
       }
 #endif  // SB_API_VERSION >= 7
     } break;
-#if SB_API_VERSION >= 10
     case kSbDecodeTargetFormat3Plane10BitYUVI420:
-#endif
     case kSbDecodeTargetFormat3PlaneYUVI420: {
       DCHECK_LT(plane, 3);
 #if SB_API_VERSION >= 7 && defined(GL_RED_EXT)
@@ -242,11 +241,9 @@
     case kSbDecodeTargetFormat3PlaneYUVI420: {
       return render_tree::kMultiPlaneImageFormatYUV3PlaneBT709;
     } break;
-#if SB_API_VERSION >= 10
     case kSbDecodeTargetFormat3Plane10BitYUVI420: {
       return render_tree::kMultiPlaneImageFormatYUV3Plane10BitBT2020;
     } break;
-#endif
     default: { NOTREACHED(); }
   }
   return render_tree::kMultiPlaneImageFormatYUV2PlaneBT709;
@@ -546,6 +543,14 @@
                                    font_provider, maybe_used_fonts);
 }
 
+scoped_refptr<render_tree::LottieAnimation>
+HardwareResourceProvider::CreateLottieAnimation(const char* data,
+                                                size_t length) {
+  TRACE_EVENT0("cobalt::renderer",
+               "HardwareResourceProvider::CreateLottieAnimation()");
+  return base::WrapRefCounted(new SkottieAnimation(data, length));
+}
+
 scoped_refptr<render_tree::Mesh> HardwareResourceProvider::CreateMesh(
     std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
     render_tree::Mesh::DrawMode draw_mode) {
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 9c0c3a5..b5d71c2 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -118,6 +118,9 @@
                      render_tree::FontProvider* font_provider,
                      render_tree::FontVector* maybe_used_fonts) override;
 
+  scoped_refptr<render_tree::LottieAnimation> CreateLottieAnimation(
+      const char* data, size_t length) override;
+
   scoped_refptr<render_tree::Mesh> CreateMesh(
       std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
       render_tree::Mesh::DrawMode draw_mode) override;
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
index 314b5d8..10e2d38 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -33,6 +33,7 @@
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/rect_node.h"
 #include "cobalt/render_tree/rect_shadow_node.h"
@@ -46,6 +47,7 @@
 #include "cobalt/renderer/rasterizer/skia/image.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/effects/SkNV122RGBShader.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/effects/SkYUV2RGBShader.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
 #include "cobalt/renderer/rasterizer/skia/software_image.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
 #include "third_party/skia/include/core/SkClipOp.h"
@@ -709,6 +711,24 @@
 #endif
 }
 
+void RenderTreeNodeVisitor::Visit(render_tree::LottieNode* lottie_node) {
+#if ENABLE_RENDER_TREE_VISITOR_TRACING && !FILTER_RENDER_TREE_VISITOR_TRACING
+  TRACE_EVENT0("cobalt::renderer", "Visit(LottieNode)");
+#endif
+  skia::SkottieAnimation* animation =
+      base::polymorphic_downcast<skia::SkottieAnimation*>(
+          lottie_node->data().animation.get());
+  animation->SetAnimationTime(lottie_node->data().animation_time);
+
+  sk_sp<skottie::Animation> skottie = animation->GetSkottieAnimation();
+  SkCanvas* render_target = draw_state_.render_target;
+  math::RectF bounding_rect = lottie_node->data().destination_rect;
+  const SkRect sk_bounding_rect =
+      SkRect::MakeXYWH(bounding_rect.x(), bounding_rect.y(),
+                       bounding_rect.width(), bounding_rect.height());
+  skottie->render(render_target, &sk_bounding_rect);
+}
+
 void RenderTreeNodeVisitor::Visit(
     render_tree::MatrixTransform3DNode* matrix_transform_3d_node) {
 #if ENABLE_RENDER_TREE_VISITOR_TRACING && !FILTER_RENDER_TREE_VISITOR_TRACING
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h
index f229f41..64b494a 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.h
@@ -25,6 +25,7 @@
 #include "cobalt/render_tree/filter_node.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/lottie_node.h"
 #include "cobalt/render_tree/matrix_transform_3d_node.h"
 #include "cobalt/render_tree/matrix_transform_node.h"
 #include "cobalt/render_tree/node_visitor.h"
@@ -108,6 +109,7 @@
   void Visit(render_tree::CompositionNode* composition_node) override;
   void Visit(render_tree::FilterNode* filter_node) override;
   void Visit(render_tree::ImageNode* image_node) override;
+  void Visit(render_tree::LottieNode* lottie_node) override;
   void Visit(
       render_tree::MatrixTransform3DNode* matrix_transform_3d_node) override;
   void Visit(render_tree::MatrixTransformNode* matrix_transform_node) override;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h b/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h
index 0c3da7c..6ebf240 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/config/SkUserConfig.h
@@ -19,6 +19,8 @@
 
 #if defined(STARBOARD)
 #include "starboard/configuration.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/types.h"
 #endif
 
 /*  SkTypes.h, the root of the public header files, does the following trick:
@@ -107,11 +109,26 @@
 // would like these formats to match.
 // Always use OpenGL byte-order (RGBA).
 
-#if defined(STARBOARD) && \
-    SB_PREFERRED_RGBA_BYTE_ORDER == SB_PREFERRED_RGBA_BYTE_ORDER_BGRA
+#if defined(STARBOARD) && SB_API_VERSION >= SB_FEATURE_RUNTIME_CONFIGS_VERSION
+const uint8_t r32_or_bendian_a32_shift =
+    kSbPreferredRgbaByteOrder == SB_PREFERRED_RGBA_BYTE_ORDER_BGRA ? 16 : 0;
 
 #ifdef SK_CPU_BENDIAN
 #define SK_R32_SHIFT 24
+#define SK_G32_SHIFT (16 - r32_or_bendian_a32_shift)
+#define SK_B32_SHIFT 8
+#define SK_A32_SHIFT r32_or_bendian_a32_shift
+#else
+#define SK_R32_SHIFT r32_or_bendian_a32_shift
+#define SK_G32_SHIFT 8
+#define SK_B32_SHIFT (16 - r32_or_bendian_a32_shift)
+#define SK_A32_SHIFT 24
+#endif
+
+#elif defined(STARBOARD) && \
+    kSbPreferredRgbaByteOrder == SB_PREFERRED_RGBA_BYTE_ORDER_BGRA
+#ifdef SK_CPU_BENDIAN
+#define SK_R32_SHIFT 24
 #define SK_G32_SHIFT 0
 #define SK_B32_SHIFT 8
 #define SK_A32_SHIFT 16
@@ -125,7 +142,7 @@
 #else
 
 // Default to RGBA otherwise.  Skia only supports either BGRA or RGBA, so if
-// SB_PREFERRED_RGBA_BYTE_ORDER is neither, we default it to RGBA and we will
+// kSbPreferredRgbaByteOrder is neither, we default it to RGBA and we will
 // have to do color conversions at runtime.
 #ifdef SK_CPU_BENDIAN
 #define SK_R32_SHIFT 24
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
index e61806a..091f7bd 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
@@ -21,14 +21,6 @@
       'export_dependent_settings': [
         'skia_library',
       ],
-      'conditions': [
-        # Skia should normally be optimized for speed. However, the direct-gles
-        # rasterizer rarely uses skia and normally caches its output anyway, so
-        # optimize skia for size in its case.
-        ['rasterizer_type != "direct-gles"', {
-          'variables': { 'optimize_target_for_speed': 1 },
-        }],
-      ],
     },
 
     {
@@ -40,14 +32,6 @@
         'skia_library.gypi',
         'skia_sksl.gypi',
       ],
-      'conditions': [
-        # Skia should normally be optimized for speed. However, the direct-gles
-        # rasterizer rarely uses skia and normally caches its output anyway, so
-        # optimize skia for size in its case.
-        ['rasterizer_type != "direct-gles"', {
-          'variables': { 'optimize_target_for_speed': 1 },
-        }],
-      ],
     },
 
     {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi b/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
index 1de6a84..d6dc670 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
@@ -10,6 +10,7 @@
   'dependencies': [
     '<(DEPTH)/base/base.gyp:base',
     '<(DEPTH)/cobalt/base/base.gyp:base',
+    '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
     '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
     '<(DEPTH)/third_party/freetype2/freetype2_cobalt.gyp:freetype2',
     '<(DEPTH)/third_party/libxml/libxml.gyp:libxml',
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia_common.gypi b/src/cobalt/renderer/rasterizer/skia/skia/skia_common.gypi
index b5d0cc0..892ebab 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia_common.gypi
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia_common.gypi
@@ -33,12 +33,10 @@
     # Android.
     'default_font_cache_limit%': '(1*1024*1024)',
   },
+  'dependencies': [
+    '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
+  ],
   'conditions': [
-    ['gl_type != "none"', {
-      'dependencies': [
-        '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
-      ],
-    }],
     ['target_arch == "win"', {
       'variables': {
         'skia_export_defines': [
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/gpu/gl/GrGLMakeNativeInterface_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/gpu/gl/GrGLMakeNativeInterface_cobalt.cc
index 805f29d..ca27552 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/gpu/gl/GrGLMakeNativeInterface_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/gpu/gl/GrGLMakeNativeInterface_cobalt.cc
@@ -74,7 +74,7 @@
   REGISTER_GL_FUNCTION(GetShaderInfoLog);
   REGISTER_GL_FUNCTION(GetShaderiv);
   REGISTER_GL_FUNCTION(GetString);
-#if defined(GLES3_SUPPORTED)
+#if SB_API_VERSION < SB_GLES3_DEPRECATED_VERSION && defined(GLES3_SUPPORTED)
   REGISTER_GL_FUNCTION(GetStringi);
 #endif
   REGISTER_GL_FUNCTION(GetUniformLocation);
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 883ccd1..51f953c 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
@@ -25,6 +25,7 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFreeType_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
@@ -35,8 +36,10 @@
     const char* system_font_config_directory,
     const char* system_font_files_directory,
     const SkTArray<SkString, true>& default_families)
-    : local_typeface_stream_manager_("Font.LocalTypefaceCache",
-                                     COBALT_LOCAL_TYPEFACE_CACHE_SIZE_IN_BYTES),
+    : local_typeface_stream_manager_(
+          "Font.LocalTypefaceCache",
+          cobalt::configuration::Configuration::GetInstance()
+              ->CobaltLocalTypefaceCacheSizeInBytes()),
       default_family_(NULL) {
   TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::SkFontMgr_Cobalt()");
 
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
new file mode 100644
index 0000000..a337dd2
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
@@ -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.
+
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace skia {
+
+SkottieAnimation::SkottieAnimation(const char* data, size_t length) {
+  skottie::Animation::Builder builder;
+  skottie_animation_ = builder.make(data, length);
+  animation_size_ = math::Size(skottie_animation_->size().width(),
+                               skottie_animation_->size().height());
+  json_size_in_bytes_ = builder.getStats().fJsonSize;
+}
+
+void SkottieAnimation::SetAnimationTime(base::TimeDelta animation_time) {
+  skottie_animation_->seekFrameTime(animation_time.InSecondsF());
+}
+
+}  // namespace skia
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
new file mode 100644
index 0000000..16aed6e
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
@@ -0,0 +1,55 @@
+// 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_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
+
+#include "cobalt/render_tree/lottie_animation.h"
+#include "third_party/skia/modules/skottie/include/Skottie.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace skia {
+
+// A subclass of render_tree::LottieAnnimation that holds information about
+// the Skottie animation object associated with the given animation data.
+class SkottieAnimation : public render_tree::LottieAnimation {
+ public:
+  SkottieAnimation(const char* data, size_t length);
+
+  const math::Size& GetSize() const override { return animation_size_; }
+
+  uint32 GetEstimatedSizeInBytes() const override {
+    return json_size_in_bytes_;
+  }
+
+  bool IsOpaque() const override { return false; }
+
+  void SetAnimationTime(base::TimeDelta animation_time) override;
+
+  sk_sp<skottie::Animation> GetSkottieAnimation() { return skottie_animation_; }
+
+ private:
+  sk_sp<skottie::Animation> skottie_animation_;
+  math::Size animation_size_;
+  uint32 json_size_in_bytes_;
+};
+
+}  // namespace skia
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
index 590e7bc..78f5dc8 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -23,6 +23,7 @@
 #include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
 #include "cobalt/renderer/rasterizer/skia/software_image.h"
 #include "cobalt/renderer/rasterizer/skia/software_mesh.h"
 #include "cobalt/renderer/rasterizer/skia/typeface.h"
@@ -245,6 +246,14 @@
                                    font_provider, maybe_used_fonts);
 }
 
+scoped_refptr<render_tree::LottieAnimation>
+SoftwareResourceProvider::CreateLottieAnimation(const char* data,
+                                                size_t length) {
+  TRACE_EVENT0("cobalt::renderer",
+               "SoftwareResourceProvider::CreateLottieAnimation()");
+  return base::WrapRefCounted(new SkottieAnimation(data, length));
+}
+
 scoped_refptr<render_tree::Mesh> SoftwareResourceProvider::CreateMesh(
     std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
     render_tree::Mesh::DrawMode draw_mode) {
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index 3c9c23c..c906967 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -108,6 +108,9 @@
                      render_tree::FontProvider* font_provider,
                      render_tree::FontVector* maybe_used_fonts) override;
 
+  scoped_refptr<render_tree::LottieAnimation> CreateLottieAnimation(
+      const char* data, size_t length) override;
+
   scoped_refptr<render_tree::Mesh> CreateMesh(
       std::unique_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
       render_tree::Mesh::DrawMode draw_mode) override;
diff --git a/src/cobalt/renderer/rasterizer/testdata/SimpleLottieAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/SimpleLottieAnimationTest-expected.png
new file mode 100644
index 0000000..7f1e07e
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SimpleLottieAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/circle.json b/src/cobalt/renderer/rasterizer/testdata/circle.json
new file mode 100644
index 0000000..ded3575
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/circle.json
@@ -0,0 +1,144 @@
+{
+	"v": "5.5.4",
+	"fr": 60,
+	"ip": 0,
+	"op": 51,
+	"w": 250,
+	"h": 250,
+	"nm": "pie_chart 2",
+	"ddd": 0,
+	"assets": [],
+	"layers": [{
+		"ddd": 0,
+		"ind": 3,
+		"ty": 4,
+		"nm": "chart_1",
+		"sr": 1,
+		"ks": {
+			"o": {
+				"a": 0,
+				"k": 100,
+				"ix": 11
+			},
+			"r": {
+				"a": 0,
+				"k": 0,
+				"ix": 10
+			},
+			"p": {
+				"a": 0,
+				"k": [125, 125, 0],
+				"ix": 2
+			},
+			"a": {
+				"a": 0,
+				"k": [0, 0, 0],
+				"ix": 1
+			},
+			"s": {
+				"a": 0,
+				"k": [60, 60, 100],
+				"ix": 6
+			}
+		},
+		"ao": 0,
+		"shapes": [{
+			"ind": 0,
+			"ty": "sh",
+			"ix": 1,
+			"ks": {
+				"a": 0,
+				"k": {
+					"i": [
+						[-96.926, 0],
+						[0, -96.926],
+						[96.926, 0],
+						[0, 96.926]
+					],
+					"o": [
+						[96.926, 0],
+						[0, 96.926],
+						[-96.926, 0],
+						[0, -96.926]
+					],
+					"v": [
+						[0, -175.5],
+						[175.5, 0],
+						[0, 175.5],
+						[-175.5, 0]
+					],
+					"c": true
+				},
+				"ix": 2
+			},
+			"nm": "Path 1",
+			"mn": "ADBE Vector Shape - Group",
+			"hd": false
+		}, {
+			"ty": "tm",
+			"s": {
+				"a": 1,
+				"k": [{
+					"i": {
+						"x": [0],
+						"y": [1]
+					},
+					"o": {
+						"x": [0.251],
+						"y": [0]
+					},
+					"t": 0,
+					"s": [0],
+					"e": [100]
+				}, {
+					"t": 45
+				}],
+				"ix": 1
+			},
+			"e": {
+				"a": 0,
+				"k": 0,
+				"ix": 2
+			},
+			"o": {
+				"a": 0,
+				"k": 0,
+				"ix": 3
+			},
+			"m": 1,
+			"ix": 2,
+			"nm": "Trim Paths 1",
+			"mn": "ADBE Vector Filter - Trim",
+			"hd": false
+		}, {
+			"ty": "st",
+			"c": {
+				"a": 0,
+				"k": [0.113344013691, 0.864957630634, 0.241092547774, 1],
+				"ix": 3
+			},
+			"o": {
+				"a": 0,
+				"k": 100,
+				"ix": 4
+			},
+			"w": {
+				"a": 0,
+				"k": 49,
+				"ix": 5
+			},
+			"lc": 1,
+			"lj": 1,
+			"ml": 4,
+			"bm": 0,
+			"nm": "Stroke 1",
+			"mn": "ADBE Vector Graphic - Stroke",
+			"hd": false
+		}],
+		"ip": 0,
+		"op": 194,
+		"st": 0,
+		"bm": 0
+	}],
+	"markers": []
+}
\ No newline at end of file
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index 1fc8987..be4eabb 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -13,12 +13,14 @@
 // limitations under the License.
 
 #include <memory>
+#include <string>
 
 #include "cobalt/renderer/renderer_module.h"
 
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/browser/memory_settings/calculations.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/renderer/backend/default_graphics_system.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
 #include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
@@ -37,18 +39,18 @@
   skia_glyph_texture_atlas_dimensions.SetSize(2048, 2048);
   skia_cache_size_in_bytes = 4 * 1024 * 1024;
 
-#if SB_HAS(GLES2)
-#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
-  software_surface_cache_size_in_bytes = 0;
-  offscreen_target_cache_size_in_bytes = 4 * 1024 * 1024;
-#else
-  software_surface_cache_size_in_bytes = 0;
-  offscreen_target_cache_size_in_bytes = 0;
-#endif
-#else
-  software_surface_cache_size_in_bytes = 8 * 1024 * 1024;
-  offscreen_target_cache_size_in_bytes = 0;
-#endif
+  std::string rasterizer_type =
+      configuration::Configuration::GetInstance()->CobaltRasterizerType();
+  if (rasterizer_type == "direct-gles") {
+    software_surface_cache_size_in_bytes = 0;
+    offscreen_target_cache_size_in_bytes = 4 * 1024 * 1024;
+  } else if (rasterizer_type == "hardware") {
+    software_surface_cache_size_in_bytes = 0;
+    offscreen_target_cache_size_in_bytes = 0;
+  } else {
+    software_surface_cache_size_in_bytes = 8 * 1024 * 1024;
+    offscreen_target_cache_size_in_bytes = 0;
+  }
 
   // Call into platform-specific code for setting up render module options.
   SetPerPlatformDefaultOptions();
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h
index 93b10a6..946211b 100644
--- a/src/cobalt/script/global_environment.h
+++ b/src/cobalt/script/global_environment.h
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "cobalt/script/error_report.h"
 #include "cobalt/script/script_value.h"
@@ -35,7 +36,8 @@
 class SourceCode;
 
 // Manages a handle to a JavaScript engine's global object.
-class GlobalEnvironment : public base::RefCounted<GlobalEnvironment> {
+class GlobalEnvironment : public base::RefCounted<GlobalEnvironment>,
+                          public base::SupportsWeakPtr<GlobalEnvironment> {
  public:
   typedef base::Callback<bool(const ErrorReport& error_report)>
       ReportErrorCallback;
@@ -126,17 +128,19 @@
    public:
     ScopedPreventGarbageCollection(GlobalEnvironment* global_environment,
                                    Wrappable* wrappable)
-        : global_environment(global_environment), wrappable(wrappable) {
+        : global_environment(global_environment->AsWeakPtr()), wrappable(wrappable) {
       global_environment->PreventGarbageCollection(
           base::WrapRefCounted(wrappable));
     }
 
     ~ScopedPreventGarbageCollection() {
-      global_environment->AllowGarbageCollection(wrappable);
+      if (global_environment) {
+        global_environment->AllowGarbageCollection(wrappable);
+      }
     }
 
    private:
-    GlobalEnvironment* global_environment;
+    base::WeakPtr<GlobalEnvironment> global_environment;
     Wrappable* wrappable;
   };
 
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index 55d934c..13631b6 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -80,6 +80,7 @@
         '<(DEPTH)/cobalt/script/shared/stub_script_debugger.cc',
       ],
       'dependencies': [
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/script/script.gyp:script',
         '<(DEPTH)/third_party/mozjs-45/mozjs-45.gyp:mozjs-45_lib',
         'embed_mozjs_resources_as_header_files',
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc
index 0c688a5..27e0036 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -259,7 +259,12 @@
 }
 
 std::string GetJavaScriptEngineNameAndVersion() {
-  return std::string("mozjs/") + MOZILLA_VERSION;
+  return std::string("mozjs/") + MOZILLA_VERSION
+#if defined(ENGINE_SUPPORTS_JIT)
+      + "-jit";
+#else
+      + "-jitless";
+#endif
 }
 
 }  // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 20cb37d..c93c502 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -21,6 +21,7 @@
 #include "base/lazy_instance.h"
 #include "base/strings/stringprintf.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/script/mozjs-45/conversion_helpers.h"
 #include "cobalt/script/mozjs-45/embedded_resources.h"
 #include "cobalt/script/mozjs-45/mozjs_exception_state.h"
@@ -158,14 +159,14 @@
   // Set a pointer to this class inside the JSContext.
   JS_SetContextPrivate(context_, this);
 
-#if defined(COBALT_GC_ZEAL)
-  // Set zeal level to "Collect when every frequency allocations." See
-  // https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS_SetGCZeal
-  // for other valid options.
-  const uint8_t kZealLevel = 2;
-  const uint32_t kZealFrequency = 600;
-  JS_SetGCZeal(context_, kZealLevel, kZealFrequency);
-#endif
+  if (configuration::Configuration::GetInstance()->CobaltGcZeal()) {
+    // Set zeal level to "Collect when every frequency allocations." See
+    // https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS_SetGCZeal
+    // for other valid options.
+    const uint8_t kZealLevel = 2;
+    const uint32_t kZealFrequency = 600;
+    JS_SetGCZeal(context_, kZealLevel, kZealFrequency);
+  }
 
   JS_SetGCParameterForThread(context_, JSGC_MAX_CODE_CACHE_BYTES,
                              kMaxCodeCacheBytes);
@@ -595,7 +596,7 @@
   // an uncaught exception, that would be the object that was thrown; in the
   // case of a JavaScript error that would be an Error object. If there is no
   // corresponding object, then the null value must be used instead.
-  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
   if (exception.isObject()) {
     error_report.error.reset(new MozjsValueHandleHolder(context_, exception));
   }
@@ -614,7 +615,7 @@
   }
 
   // If the error is not handled, then the error may be reported to the user.
-  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors-in-documents
+  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors-in-documents
   std::string new_error_message = base::StringPrintf(
       "%s:%u:%u: %s", error_report.filename.c_str(), error_report.line_number,
       error_report.column_number, error_report.message.c_str());
diff --git a/src/cobalt/script/script_value_factory_instantiations.h b/src/cobalt/script/script_value_factory_instantiations.h
index 3f9e63b..dc0cab0 100644
--- a/src/cobalt/script/script_value_factory_instantiations.h
+++ b/src/cobalt/script/script_value_factory_instantiations.h
@@ -24,6 +24,10 @@
 namespace cobalt {
 namespace script {
 
+// TODO: Move dom::BufferSource to script::BufferSource and include here
+using BufferSource = script::UnionType2<script::Handle<script::ArrayBufferView>,
+                                        script::Handle<script::ArrayBuffer>>;
+
 // Explicit template instantiations for all Promise types that will be used
 // in Cobalt.
 #define PROMISE_TEMPLATE_INSTANTIATION(TYPE) \
@@ -44,6 +48,7 @@
 PROMISE_TEMPLATE_INSTANTIATION(std::string);
 PROMISE_TEMPLATE_INSTANTIATION(scoped_refptr<Wrappable>);
 PROMISE_TEMPLATE_INSTANTIATION(Sequence<scoped_refptr<Wrappable>>);
+PROMISE_TEMPLATE_INSTANTIATION(BufferSource);
 
 #undef PROMISE_TEMPLATE_INSTANTIATION
 
diff --git a/src/cobalt/script/v8c/isolate_fellowship.cc b/src/cobalt/script/v8c/isolate_fellowship.cc
index b4df087..aa21331 100644
--- a/src/cobalt/script/v8c/isolate_fellowship.cc
+++ b/src/cobalt/script/v8c/isolate_fellowship.cc
@@ -37,29 +37,32 @@
 // according to the update_snapshot_time gyp target.
 const char kIsolateFellowshipBuildTime[] = __DATE__ " " __TIME__;
 
-const char* kV8CommandLineFlags[] = {"--optimize_for_size",
-                                     // Starboard disallow rwx memory access.
-                                     "--write_protect_code_memory",
-                                     // Cobalt's TraceMembers and
-                                     // ScriptValue::*Reference do not currently
-                                     // support incremental tracing.
-                                     "--noincremental_marking_wrappers",
-                                     "--noexpose_wasm",
-                                     "--novalidate_asm",
-#if defined(COBALT_GC_ZEAL)
-                                     "--gc_interval=1200",
-#endif
-#if !defined(ENGINE_SUPPORTS_JIT)
-                                     "--jitless"
-#endif
-};
-
 // Configure v8's global command line flag options for Cobalt.
 // It can be called more than once, but make sure it is called before any
 // v8 instance is created.
 void V8FlagsInit() {
+  std::vector<std::string> kV8CommandLineFlags = {
+    "--optimize_for_size",
+    // Starboard disallow rwx memory access.
+    "--write_protect_code_memory",
+    // Cobalt's TraceMembers and
+    // ScriptValue::*Reference do not currently
+    // support incremental tracing.
+    "--noincremental_marking_wrappers",
+    "--noexpose_wasm",
+    "--novalidate_asm",
+#if !defined(ENGINE_SUPPORTS_JIT)
+    "--jitless"
+#endif
+  };
+
+  if (configuration::Configuration::GetInstance()->CobaltGcZeal()) {
+    kV8CommandLineFlags.push_back("--gc_interval=1200");
+  }
+
   for (auto flag_str : kV8CommandLineFlags) {
-    v8::V8::SetFlagsFromString(flag_str, SbStringGetLength(flag_str));
+    v8::V8::SetFlagsFromString(flag_str.c_str(),
+                               SbStringGetLength(flag_str.c_str()));
   }
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -110,7 +113,7 @@
   // DCHECK here.
   delete[] startup_data.data;
   startup_data = {nullptr, 0};
-#endif
+#endif  // !defined(COBALT_V8_BUILDTIME_SNAPSHOT)
 }
 
 #if !defined(COBALT_V8_BUILDTIME_SNAPSHOT)
diff --git a/src/cobalt/script/v8c/isolate_fellowship.h b/src/cobalt/script/v8c/isolate_fellowship.h
index f1930b8..5f36845 100644
--- a/src/cobalt/script/v8c/isolate_fellowship.h
+++ b/src/cobalt/script/v8c/isolate_fellowship.h
@@ -16,6 +16,7 @@
 #define COBALT_SCRIPT_V8C_ISOLATE_FELLOWSHIP_H_
 
 #include "base/memory/singleton.h"
+#include "cobalt/configuration/configuration.h"
 #include "cobalt/script/v8c/cobalt_platform.h"
 #include "v8/include/libplatform/libplatform.h"
 #include "v8/include/v8-platform.h"
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp
index 3429ad8..edc2f22 100644
--- a/src/cobalt/script/v8c/v8c.gyp
+++ b/src/cobalt/script/v8c/v8c.gyp
@@ -77,6 +77,7 @@
         'wrapper_private.h',
       ],
       'dependencies': [
+        '<(DEPTH)/cobalt/configuration/configuration.gyp:configuration',
         '<(DEPTH)/cobalt/script/script.gyp:script',
         '<(DEPTH)/v8/src/v8.gyp:v8',
         '<(DEPTH)/v8/src/v8.gyp:v8_libplatform',
diff --git a/src/cobalt/script/v8c/v8c_engine.cc b/src/cobalt/script/v8c/v8c_engine.cc
index 866b39f..0194537 100644
--- a/src/cobalt/script/v8c/v8c_engine.cc
+++ b/src/cobalt/script/v8c/v8c_engine.cc
@@ -195,7 +195,12 @@
 }
 
 std::string GetJavaScriptEngineNameAndVersion() {
-  return std::string("v8/") + v8::V8::GetVersion();
+  return std::string("v8/") + v8::V8::GetVersion()
+#if defined(ENGINE_SUPPORTS_JIT)
+      + "-jit";
+#else
+      + "-jitless";
+#endif
 }
 
 }  // namespace script
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.cc b/src/cobalt/script/v8c/v8c_heap_tracer.cc
index 1277526..594bf77 100644
--- a/src/cobalt/script/v8c/v8c_heap_tracer.cc
+++ b/src/cobalt/script/v8c/v8c_heap_tracer.cc
@@ -53,23 +53,25 @@
   // manually decide to trace the from the global object.
   MaybeAddToFrontier(
       V8cGlobalEnvironment::GetFromIsolate(isolate_)->global_wrappable());
-
-  for (Traceable* traceable : roots_) {
-    MaybeAddToFrontier(traceable);
-  }
-  for (v8::TracedGlobal<v8::Value>* traced_global : globals_) {
-    RegisterEmbedderReference(*traced_global);
-  }
 }
 
 bool V8cHeapTracer::AdvanceTracing(double deadline_in_ms) {
   TRACE_EVENT0("cobalt::script", "V8cHeapTracer::AdvanceTracing");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  double start_time = platform_->MonotonicallyIncreasingTime();
   if (disabled_) {
     return true;
   }
-  double start_time = platform_->MonotonicallyIncreasingTime();
+
+  // Objects that we want to keep alive.
+  for (Traceable* traceable : roots_) {
+    MaybeAddToFrontier(traceable);
+  }
+  for (v8::TracedGlobal<v8::Value>* traced_global : globals_) {
+    RegisterEmbedderReference(*traced_global);
+  }
+
   while (platform_->MonotonicallyIncreasingTime() - start_time <
          deadline_in_ms) {
     if (frontier_.empty()) {
diff --git a/src/cobalt/script/v8c/v8c_script_debugger.cc b/src/cobalt/script/v8c/v8c_script_debugger.cc
index 11a4f1d..5b3b743 100644
--- a/src/cobalt/script/v8c/v8c_script_debugger.cc
+++ b/src/cobalt/script/v8c/v8c_script_debugger.cc
@@ -306,6 +306,35 @@
 }
 
 // v8_inspector::V8InspectorClient implementation.
+std::unique_ptr<v8_inspector::StringBuffer> V8cScriptDebugger::valueSubtype(
+    v8::Local<v8::Value> value) {
+  if (value->IsNullOrUndefined() || !value->IsObject()) {
+    return nullptr;
+  }
+
+  v8::Local<v8::Object> object = value.As<v8::Object>();
+  if (!WrapperPrivate::HasWrapperPrivate(object)) {
+    return nullptr;
+  }
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  DCHECK(wrapper_private);
+  Wrappable::JSObjectType type =
+      wrapper_private->raw_wrappable()->GetJSObjectType();
+  if (type == Wrappable::JSObjectType::kNode) {
+    return v8_inspector::StringBuffer::create(ToStringView("node"));
+  } else if (type == Wrappable::JSObjectType::kArray) {
+    return v8_inspector::StringBuffer::create(ToStringView("array"));
+  } else if (type == Wrappable::JSObjectType::kError) {
+    return v8_inspector::StringBuffer::create(ToStringView("error"));
+  } else if (type == Wrappable::JSObjectType::kBlob) {
+    return v8_inspector::StringBuffer::create(ToStringView("blob"));
+  }
+  return nullptr;
+}
+
+// v8_inspector::V8InspectorClient implementation.
 void V8cScriptDebugger::consoleAPIMessage(
     int contextGroupId, v8::Isolate::MessageErrorLevel level,
     const v8_inspector::StringView& message,
diff --git a/src/cobalt/script/v8c/v8c_script_debugger.h b/src/cobalt/script/v8c/v8c_script_debugger.h
index d242c3f..ac71e77 100644
--- a/src/cobalt/script/v8c/v8c_script_debugger.h
+++ b/src/cobalt/script/v8c/v8c_script_debugger.h
@@ -71,6 +71,8 @@
   void runMessageLoopOnPause(int contextGroupId) override;
   void quitMessageLoopOnPause() override;
   void runIfWaitingForDebugger(int contextGroupId) override;
+  std::unique_ptr<v8_inspector::StringBuffer> valueSubtype(
+      v8::Local<v8::Value> value) override;
   v8::Local<v8::Context> ensureDefaultContextInGroup(
       int contextGroupId) override;
   void consoleAPIMessage(int contextGroupId,
diff --git a/src/cobalt/script/wrappable.h b/src/cobalt/script/wrappable.h
index c6a6fc2..e546bcd 100644
--- a/src/cobalt/script/wrappable.h
+++ b/src/cobalt/script/wrappable.h
@@ -55,6 +55,14 @@
     }
   };
 
+  enum class JSObjectType {
+    kObject,
+    kNode,
+    kArray,
+    kError,
+    kBlob,
+  };
+
   // Used for RTTI on wrappable types. This is implemented within the
   // DEFINE_WRAPPABLE_TYPE macro, defined below, which should be added to the
   // class definition of each wrappable type.
@@ -66,6 +74,7 @@
   // own any |Traceable|s must override |TraceMembers| and trace them.
   void TraceMembers(Tracer* /*tracer*/) override {}
   bool IsWrappable() const final { return true; }
+  virtual JSObjectType GetJSObjectType() { return JSObjectType::kObject; };
 
  protected:
   virtual ~Wrappable() { }
diff --git a/src/cobalt/site/docs/reference/starboard/gyp-configuration.md b/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
index a3470b0..02be346 100644
--- a/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
+++ b/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
@@ -6,7 +6,6 @@
 | Variables |
 | :--- |
 | **`abort_on_allocation_failure`**<br><br> Halt execution on failure to allocate memory.<br><br>The default value is `1`. |
-| **`build_snapshot_app_stats`**<br><br> Some platforms have difficulty linking snapshot_app_stats<br><br>The default value is `1`. |
 | **`cobalt`**<br><br> Whether Cobalt is being built.<br><br>The default value is `1`. |
 | **`cobalt_config`**<br><br> Contains the current build configuration.<br><br>The default value is `'gold'`. |
 | **`cobalt_egl_swap_interval`**<br><br> Cobalt will call eglSwapInterval() and specify this value before calling eglSwapBuffers() each frame.<br><br>The default value is `1`. |
@@ -60,7 +59,7 @@
 | **`enable_xhr_header_filtering`**<br><br> Set to 1 to enable filtering of HTTP headers before sending.<br><br>The default value is `0`. |
 | **`fallback_splash_screen_url`**<br><br> The URL of default build time splash screen - see cobalt/doc/splash_screen.md for information about this.<br><br>The default value is `'none'`. |
 | **`final_executable_type`**<br><br> The variables allow changing the target type on platforms where the native code may require an additional packaging step (ex. Android).<br><br>The default value is `'executable'`. |
-| **`gl_type`**<br><br> The source of EGL and GLES headers and libraries. Valid values (case and everything sensitive!):<ul><li><code>none</code> - No EGL + GLES implementation is available on this platform.<li><code>system_gles3</code> - Use the system implementation of EGL + GLES3. The headers and libraries must be on the system include and link paths.<li><code>system_gles2</code> - Use the system implementation of EGL + GLES2. The headers and libraries must be on the system include and link paths.<li><code>glimp</code> - Cobalt's own EGL + GLES2 implementation. This requires a valid Glimp implementation for the platform.<li><code>angle</code> - A DirectX-to-OpenGL adaptation layer. This requires a valid ANGLE implementation for the platform.</li></ul> Choosing an unsupported value will result in a GYP error: "cobalt/renderer/egl_and_gles/egl_and_gles_<gl_type>.gyp not found"<br><br>The default value is `'system_gles2'`. |
+| **`gl_type`**<br><br> The source of EGL and GLES headers and libraries. Valid values (case and everything sensitive!):<ul><li><code>none</code> - No EGL + GLES implementation is available on this platform.<li><code>system_gles3</code> - Deprecated. Use system_gles2 instead.<br><br>Use the system implementation of EGL + GLES3. The headers and libraries must be on the system include and link paths.<li><code>system_gles2</code> - Use the system implementation of EGL + GLES2. The headers and libraries must be on the system include and link paths.<li><code>glimp</code> - Cobalt's own EGL + GLES2 implementation. This requires a valid Glimp implementation for the platform.<li><code>angle</code> - A DirectX-to-OpenGL adaptation layer. This requires a valid ANGLE implementation for the platform.</li></ul> Choosing an unsupported value will result in a GYP error: "cobalt/renderer/egl_and_gles/egl_and_gles_<gl_type>.gyp not found"<br><br>The default value is `'system_gles2'`. |
 | **`gtest_target_type`**<br><br> The variables allow changing the target type on platforms where the native code may require an additional packaging step (ex. Android).<br><br>The default value is `'executable'`. |
 | **`host_os`**<br><br> Contains the name of the hosting OS. The value is defined by the gyp wrapper script.<br><br>The default value is `'win'`. |
 | **`image_cache_capacity_multiplier_when_playing_video`**<br><br> Modifying this value to be non-1.0f will result in the image cache capacity being cleared and then temporarily reduced for the duration that a video is playing.  This can be useful for some platforms if they are particularly constrained for (GPU) memory during video playback.  When playing a video, the image cache is reduced to: image_cache_size_in_bytes * image_cache_capacity_multiplier_when_playing_video.<br><br>The default value is `'1.0f'`. |
@@ -68,8 +67,8 @@
 | **`in_app_dial`**<br><br> Set to 1 to build with DIAL support.<br><br>The default value is `0`. |
 | **`linker_flags_<config>`**<br><br>The following configurations are supported: <ul><li><code>[default]</code></li><li><code>debug</code></li><li><code>devel</code></li><li><code>gold</code></li><li><code>host</code></li><li><code>qa</ul> |
 | **`local_font_cache_size_in_bytes`**<br><br> Determines the capacity of the local font cache, which manages all fonts loaded from local files. Newly encountered sections of font files are lazily loaded into the cache, enabling subsequent requests to the same file sections to be handled via direct memory access. Once the limit is reached, further requests are handled via file stream. Setting the value to 0 disables memory caching and causes all font file accesses to be done using file streams.<br><br>The default value is `16 * 1024 * 1024`. |
-| **`max_cobalt_cpu_usage`**<br><br> Max Cobalt CPU usage specifies that the cobalt program should keep it's size below the specified size. A value of -1 causes this value to be assumed from the starboard API function: SbSystemGetTotalCPUMemory().<br><br>The default value is `-1`. |
-| **`max_cobalt_gpu_usage`**<br><br> Max Cobalt GPU usage specifies that the cobalt program should keep it's size below the specified size. A value of -1 causes this value to be assumed from the starboard API function: SbSystemGetTotalGPUMemory().<br><br>The default value is `-1`. |
+| **`max_cobalt_cpu_usage`**<br><br> This setting is deprecated. Implement starboard API function SbSystemGetTotalCPUMemory() instead.<br><br> Max Cobalt CPU usage specifies that the cobalt program should keep it's size below the specified size.<br><br>The default value is `-1`. |
+| **`max_cobalt_gpu_usage`**<br><br> This setting is deprecated. Implement starboard API function SbSystemGetTotalGPUMemory() instead.<br><br> Max Cobalt GPU usage specifies that the cobalt program should keep it's size below the specified size. A value of -1 causes this value to be assumed from the starboard API function: SbSystemGetTotalGPUMemory().<br><br>The default value is `-1`. |
 | **`mesh_cache_size_in_bytes`**<br><br> Determines the capacity of the mesh cache. Each mesh is held compressed in main memory, to be inflated into a GPU buffer when needed for projection. When set to 'auto', will be adjusted according to whether the enable_map_to_mesh is true or not.  If enable_map_to_mesh is false, then the mesh cache size will be set to 0.<br><br>The default value is `'auto'`. |
 | **`mozjs_garbage_collection_threshold_in_bytes`**<br><br> Determines the size of garbage collection threshold. After this many bytes have been allocated, the SpiderMonkey garbage collector will run. Lowering this has been found to reduce performance and decrease JavaScript memory usage. For example, we have measured on at least one platform that performance becomes 7% worse on average in certain cases when adjusting this number from 8MB to 1MB.<br><br>The default value is `8 * 1024 * 1024`. |
 | **`offscreen_target_cache_size_in_bytes`**<br><br> Determines the amount of GPU memory the offscreen target atlases will use. This is specific to the direct-GLES rasterizer and caches any render tree nodes which require skia for rendering. Two atlases will be allocated from this memory or multiple atlases of the frame size if the limit allows. It is recommended that enough memory be reserved for two RGBA atlases about a quarter of the frame size.<br><br>The default value is `-1`. |
diff --git a/src/cobalt/site/docs/starboard/porting.md b/src/cobalt/site/docs/starboard/porting.md
index 4ce6cb9..dfd630b 100644
--- a/src/cobalt/site/docs/starboard/porting.md
+++ b/src/cobalt/site/docs/starboard/porting.md
@@ -283,9 +283,7 @@
         *   `target_os` - Set to `linux` if your platform is Linux-based.
             Otherwise, remove this variable.
         *   `gl_type` - Set to `system_gles2` if you are using the system EGL
-            + GLES2 implementation or to `system_gles3` if you are using the
-            system EGL + GLES3 implementation. Otherwise, set the value to
-            `none`.
+            + GLES2 implementation and otherwise set the value to `none`.
         *   `in_app_dial` - Enables (or disables) the DIAL server that runs
             inside Cobalt. (This server only runs when Cobalt is running.)
             The [DIAL protocol](http://www.dial-multiscreen.org/home) enables
diff --git a/src/cobalt/speech/speech_synthesis_utterance.cc b/src/cobalt/speech/speech_synthesis_utterance.cc
index 62c024e..373e7ce 100644
--- a/src/cobalt/speech/speech_synthesis_utterance.cc
+++ b/src/cobalt/speech/speech_synthesis_utterance.cc
@@ -45,21 +45,30 @@
       rate_(utterance->rate_),
       pitch_(utterance->pitch_),
       pending_speak_(false) {
-  set_onstart(EventListenerScriptValue::Reference(this, *utterance->onstart())
-                  .referenced_value());
-  set_onend(EventListenerScriptValue::Reference(this, *utterance->onend())
-                .referenced_value());
-  set_onerror(EventListenerScriptValue::Reference(this, *utterance->onerror())
-                  .referenced_value());
-  set_onpause(EventListenerScriptValue::Reference(this, *utterance->onpause())
-                  .referenced_value());
-  set_onresume(EventListenerScriptValue::Reference(this, *utterance->onresume())
-                   .referenced_value());
-  set_onmark(EventListenerScriptValue::Reference(this, *utterance->onmark())
-                 .referenced_value());
-  set_onboundary(
-      EventListenerScriptValue::Reference(this, *utterance->onboundary())
-          .referenced_value());
+  using ListenerSetterFunctionType = base::Callback<void(
+      SpeechSynthesisUtterance*,
+      const cobalt::dom::EventTarget::EventListenerScriptValue&)>;
+  auto ListenerCopyHelper = [this](
+                                const EventListenerScriptValue* script_value,
+                                const ListenerSetterFunctionType& setter_func) {
+    if (script_value) {
+      setter_func.Run(this, *script_value);
+    }
+  };
+  ListenerCopyHelper(utterance->onstart(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onstart));
+  ListenerCopyHelper(utterance->onend(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onend));
+  ListenerCopyHelper(utterance->onerror(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onerror));
+  ListenerCopyHelper(utterance->onpause(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onpause));
+  ListenerCopyHelper(utterance->onresume(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onresume));
+  ListenerCopyHelper(utterance->onmark(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onmark));
+  ListenerCopyHelper(utterance->onboundary(),
+                     base::Bind(&SpeechSynthesisUtterance::set_onboundary));
 }
 
 void SpeechSynthesisUtterance::DispatchErrorCancelledEvent() {
diff --git a/src/cobalt/subtlecrypto/aes_ctr_params.idl b/src/cobalt/subtlecrypto/aes_ctr_params.idl
new file mode 100644
index 0000000..2659879
--- /dev/null
+++ b/src/cobalt/subtlecrypto/aes_ctr_params.idl
@@ -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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#aes-ctr-params
+
+dictionary AesCtrParams : Algorithm {
+  // The initial value of the counter block.
+  BufferSource counter;
+  // The length, in bits, of the rightmost part of the counter block
+  // that is incremented.
+  unsigned short length;
+};
diff --git a/src/cobalt/subtlecrypto/algorithm.idl b/src/cobalt/subtlecrypto/algorithm.idl
new file mode 100644
index 0000000..62b6d8e
--- /dev/null
+++ b/src/cobalt/subtlecrypto/algorithm.idl
@@ -0,0 +1,21 @@
+// 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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary
+
+typedef (Algorithm or DOMString) AlgorithmIdentifier;
+
+dictionary Algorithm {
+  required DOMString name;
+};
diff --git a/src/cobalt/subtlecrypto/crypto_impl.cc b/src/cobalt/subtlecrypto/crypto_impl.cc
new file mode 100644
index 0000000..3379ec8
--- /dev/null
+++ b/src/cobalt/subtlecrypto/crypto_impl.cc
@@ -0,0 +1,137 @@
+// 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/subtlecrypto/crypto_impl.h"
+
+#include <openssl/cipher.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include <algorithm>
+#include <map>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace subtlecrypto {
+
+namespace {
+
+using EVP_Func = const EVP_MD *(*)(void);
+
+template <typename TCtx, size_t HashLen, EVP_Func evp_func,
+          int (*fInit)(TCtx *), int (*fUpdate)(TCtx *, const void *, size_t),
+          int (*fFinal)(unsigned char *, TCtx *)>
+class HashImpl : public Hash {
+  TCtx ctx;
+
+ public:
+  HashImpl() { fInit(&ctx); }
+  void Update(const ByteVector &data) override {
+    if (!data.empty()) {
+      fUpdate(&ctx, static_cast<const unsigned char *>(data.data()),
+              data.size());
+    }
+  }
+  ByteVector Finish() override {
+    ByteVector out(HashLen);
+    fFinal(out.data(), &ctx);
+    return out;
+  }
+  ByteVector CalculateHMAC(const ByteVector &data,
+                           const ByteVector &key) override {
+    ByteVector ret(HashLen);
+    unsigned int ret_len = static_cast<unsigned int>(ret.size());
+    auto result = HMAC(evp_func(), key.data(), key.size(), data.data(),
+                       data.size(), ret.data(), &ret_len);
+    if (ret_len == HashLen && result) {
+      return ret;
+    }
+    return {};
+  }
+};
+
+using Sha1Hash = HashImpl<SHA_CTX, SHA_DIGEST_LENGTH, EVP_sha1, SHA1_Init,
+                          SHA1_Update, SHA1_Final>;
+using Sha256Hash = HashImpl<SHA256_CTX, SHA256_DIGEST_LENGTH, EVP_sha256,
+                            SHA256_Init, SHA256_Update, SHA256_Final>;
+using Sha384Hash = HashImpl<SHA512_CTX, SHA384_DIGEST_LENGTH, EVP_sha384,
+                            SHA384_Init, SHA384_Update, SHA384_Final>;
+using Sha512Hash = HashImpl<SHA512_CTX, SHA512_DIGEST_LENGTH, EVP_sha512,
+                            SHA512_Init, SHA512_Update, SHA512_Final>;
+using HashPtr = std::unique_ptr<Hash>;
+
+}  // namespace
+
+std::unique_ptr<Hash> Hash::CreateByName(const std::string &name) {
+  std::string tmp_name(name);
+  transform(tmp_name.begin(), tmp_name.end(), tmp_name.begin(), ::toupper);
+  if (tmp_name == "SHA-1") return std::make_unique<Sha1Hash>();
+  if (tmp_name == "SHA-256") return std::make_unique<Sha256Hash>();
+  if (tmp_name == "SHA-384") return std::make_unique<Sha384Hash>();
+  if (tmp_name == "SHA-512") return std::make_unique<Sha512Hash>();
+  return nullptr;
+}
+
+ByteVector CalculateAES_CTR(const ByteVector &data, const ByteVector &key,
+                            const ByteVector &iv) {
+  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+  DCHECK(ctx);
+  auto error = [&ctx](const char *msg) -> ByteVector {
+    DLOG(ERROR) << msg;
+    EVP_CIPHER_CTX_free(ctx);
+    return {};
+  };
+
+  if (iv.size() != 16) {
+    return error("Invalid initialization vector size, AES requires 128-bit IV");
+  }
+  const char *algo = nullptr;
+  switch (key.size()) {
+    case 16:
+      algo = "aes-128-ctr";
+      break;
+    case 24:
+      algo = "aes-192-ctr";
+      break;
+    case 32:
+      algo = "aes-256-ctr";
+      break;
+  }
+  if (!algo) {
+    return error("Invalid key size, 128, 192 or 256 bits required");
+  }
+
+  if (!EVP_EncryptInit_ex(ctx, EVP_get_cipherbyname(algo), NULL, key.data(),
+                          iv.data())) {
+    return error("EVP_EncryptInit_ex failed");
+  }
+  int buf_len = 0, out_len = 0;
+  ByteVector out(data.size() + 1);
+  if (!EVP_EncryptUpdate(ctx, out.data(), &buf_len, data.data(),
+                         static_cast<int>(data.size()))) {
+    return error("EncryptUpdate failed");
+  }
+  out_len += buf_len;
+  if (!EVP_EncryptFinal_ex(ctx, &out[buf_len], &buf_len)) {
+    return error("EVP_EncryptFinal_ex failed");
+  }
+  out_len += buf_len;
+  out.resize(out_len);
+  EVP_CIPHER_CTX_free(ctx);
+  return out;
+}
+
+}  // namespace subtlecrypto
+}  // namespace cobalt
diff --git a/src/cobalt/subtlecrypto/crypto_impl.h b/src/cobalt/subtlecrypto/crypto_impl.h
new file mode 100644
index 0000000..0e01b85
--- /dev/null
+++ b/src/cobalt/subtlecrypto/crypto_impl.h
@@ -0,0 +1,49 @@
+// 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_SUBTLECRYPTO_CRYPTO_IMPL_H_
+#define COBALT_SUBTLECRYPTO_CRYPTO_IMPL_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace cobalt {
+namespace subtlecrypto {
+
+using ByteVector = std::vector<uint8_t>;
+
+class Hash {
+ public:
+  virtual ~Hash() {}
+  // Hash input data
+  virtual void Update(const ByteVector &data) = 0;
+  // Finish hash, output is hash-length vector
+  virtual ByteVector Finish() = 0;
+  // Calculate HMAC with given key
+  virtual ByteVector CalculateHMAC(const ByteVector &data,
+                                   const ByteVector &key) = 0;
+  static std::unique_ptr<Hash> CreateByName(const std::string &name);
+};
+
+// Calculate AES-CTR, selecting the correct algorithm based on |key| length.
+// Returns an empty vector in case of failure.
+ByteVector CalculateAES_CTR(const ByteVector &data, const ByteVector &key,
+                            const ByteVector &iv);
+
+}  // namespace subtlecrypto
+}  // namespace cobalt
+
+#endif  // COBALT_SUBTLECRYPTO_CRYPTO_IMPL_H_
diff --git a/src/cobalt/subtlecrypto/crypto_impl_test.cc b/src/cobalt/subtlecrypto/crypto_impl_test.cc
new file mode 100644
index 0000000..cea8588
--- /dev/null
+++ b/src/cobalt/subtlecrypto/crypto_impl_test.cc
@@ -0,0 +1,154 @@
+// 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/subtlecrypto/crypto_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace subtlecrypto {
+namespace {
+
+ByteVector empty_ref{218, 57,  163, 238, 94, 107, 75,  13,  50, 85,
+                     191, 239, 149, 96,  24, 144, 175, 216, 7,  9};
+
+using testing::ContainerEq;
+
+TEST(HashImplTest, CreateByName) {
+  EXPECT_TRUE(Hash::CreateByName("SHA-1"));
+  EXPECT_TRUE(Hash::CreateByName("sha-1"));
+  EXPECT_TRUE(Hash::CreateByName("SHA-256"));
+  EXPECT_TRUE(Hash::CreateByName("sha-512"));
+  EXPECT_FALSE(Hash::CreateByName("md5"));
+  EXPECT_FALSE(Hash::CreateByName("SHA1"));
+  EXPECT_FALSE(Hash::CreateByName("BlowFish"));
+}
+
+TEST(HashImplTest, NullHash) {
+  auto hash = Hash::CreateByName("SHA-1");
+  auto out = hash->Finish();
+  EXPECT_THAT(out, ContainerEq(empty_ref));
+}
+
+TEST(HashImplTest, EmptyHash) {
+  auto hash = Hash::CreateByName("SHA-1");
+  hash->Update({});
+  auto out = hash->Finish();
+  EXPECT_THAT(out, ContainerEq(empty_ref));
+}
+
+TEST(HashImplTest, BasicHash) {
+  auto hash = Hash::CreateByName("SHA-1");
+  hash->Update({1, 2, 3});
+  auto out = hash->Finish();
+  EXPECT_THAT(out, testing::ContainerEq(ByteVector{
+                       112, 55, 128, 113, 152, 194, 42,  125, 43,  8,
+                       7,   55, 29,  118, 55,  121, 168, 79,  223, 207}));
+}
+
+TEST(HashImplTest, LongHash) {
+  auto hash = Hash::CreateByName("SHA-512");
+  hash->Update({'c', 'o', 'b', 'a', 'l', 't'});
+  auto out = hash->Finish();
+  EXPECT_THAT(
+      out, ContainerEq(ByteVector{
+               151, 169, 180, 36,  213, 102, 227, 224, 165, 138, 100, 210, 91,
+               158, 228, 19,  41,  40,  161, 153, 173, 66,  171, 83,  20,  245,
+               230, 110, 197, 4,   211, 110, 253, 105, 233, 138, 42,  70,  242,
+               210, 45,  70,  247, 103, 5,   145, 228, 142, 197, 56,  73,  160,
+               210, 95,  81,  144, 51,  173, 207, 21,  183, 234, 15,  226}));
+}
+
+TEST(HMACImplTest, SimpleHmac) {
+  auto hash = Hash::CreateByName("SHA-1");
+  auto out = hash->CalculateHMAC({1, 2, 3}, {3, 4, 5});
+  EXPECT_THAT(out, ContainerEq(ByteVector{253, 95,  161, 139, 138, 122, 106,
+                                          143, 19,  9,   188, 104, 215, 248,
+                                          12,  112, 14,  38,  175, 198}));
+}
+
+TEST(HMACImplTest, LongHmac) {
+  auto hash = Hash::CreateByName("SHA-256");
+  auto out = hash->CalculateHMAC({1, 2, 3}, {3, 4, 5});
+  EXPECT_THAT(out, ContainerEq(ByteVector{
+                       12,  25,  8,   57,  149, 183, 80,  251, 83, 218, 165,
+                       55,  132, 139, 243, 70,  107, 18,  185, 64, 103, 13,
+                       251, 57,  248, 156, 237, 253, 103, 133, 26, 39}));
+}
+
+TEST(HMACImplTest, EmptyHmac) {
+  auto hash = Hash::CreateByName("SHA-1");
+  auto out = hash->CalculateHMAC({}, {3, 4, 5});
+  EXPECT_THAT(out, ContainerEq(ByteVector{137, 104, 209, 15,  163, 43,  226,
+                                          236, 10,  29,  196, 233, 152, 219,
+                                          179, 217, 186, 247, 111, 43}));
+}
+
+TEST(HMACImplTest, EmptyHmacKey) {
+  auto hash = Hash::CreateByName("SHA-1");
+  auto out = hash->CalculateHMAC({1, 2, 3}, {});
+  EXPECT_THAT(out, ContainerEq(ByteVector{104, 1,   10,  108, 24,  33,  145,
+                                          220, 224, 70,  243, 58,  196, 6,
+                                          109, 163, 178, 181, 185, 37}));
+}
+
+ByteVector key_128 = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
+ByteVector key_192 = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3,
+                      4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
+ByteVector key_256 = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
+                      0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
+
+ByteVector iv_128 = {7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7};
+
+TEST(AESImplTest, Simple) {
+  auto out = CalculateAES_CTR({1, 2}, key_128, iv_128);
+  EXPECT_THAT(out, ContainerEq(ByteVector{76, 8}));
+
+  auto modified_key{key_128};
+  modified_key[0] = 42;
+  out = CalculateAES_CTR({1, 2}, modified_key, iv_128);
+  EXPECT_THAT(out, ContainerEq(ByteVector{185, 59}));
+
+  auto modified_iv{key_128};
+  modified_iv[0] = 42;
+  out = CalculateAES_CTR({1, 2}, key_128, modified_iv);
+  EXPECT_THAT(out, ContainerEq(ByteVector{249, 68}));
+
+  out = CalculateAES_CTR({1, 2}, key_192, iv_128);
+  EXPECT_THAT(out, ContainerEq(ByteVector{225, 207}));
+
+  out = CalculateAES_CTR({1, 2}, key_256, iv_128);
+  EXPECT_THAT(out, ContainerEq(ByteVector{18, 233}));
+}
+
+TEST(AESImplTest, Long) {
+  auto sizes = {1, 2, 16, 17, 31, 32, 33, 1025, 16385, 1028 * 1024 + 1};
+  for (auto& size : sizes) {
+    ByteVector data(size, 1);
+    auto out = CalculateAES_CTR(data, key_128, iv_128);
+    EXPECT_EQ(out.size(), size);
+  }
+}
+
+TEST(AESImplTest, InvalidParams) {
+  auto out = CalculateAES_CTR({1, 2}, {1}, iv_128);
+  EXPECT_TRUE(out.empty());
+
+  out = CalculateAES_CTR({1, 2}, key_128, {1, 2});
+  EXPECT_TRUE(out.empty());
+}
+
+}  // namespace
+}  // namespace subtlecrypto
+}  // namespace cobalt
diff --git a/src/cobalt/subtlecrypto/crypto_key.cc b/src/cobalt/subtlecrypto/crypto_key.cc
new file mode 100644
index 0000000..465603e
--- /dev/null
+++ b/src/cobalt/subtlecrypto/crypto_key.cc
@@ -0,0 +1,47 @@
+// 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/subtlecrypto/crypto_key.h"
+#include <algorithm>
+
+namespace cobalt {
+namespace subtlecrypto {
+
+const script::Sequence<KeyUsage>& CryptoKey::usages() { return usages_; }
+
+bool CryptoKey::set_usages(const script::Sequence<KeyUsage>& usages) {
+  usages_.clear();
+  // TODO: Key usage checking should actually be done per algorithm.
+  for (auto& usage : usages) {
+    switch (usage) {
+      case KeyUsage::kKeyUsageEncrypt:
+      case KeyUsage::kKeyUsageDecrypt:
+      case KeyUsage::kKeyUsageSign:
+      case KeyUsage::kKeyUsageVerify:
+        usages_.push_back(usage);
+      default:
+        break;
+    }
+  }
+  // If all elements were copied, all were valid.
+  return usages_.size() == usages.size();
+}
+
+bool CryptoKey::usage(const KeyUsage& usage) const {
+  return std::count(usages_.begin(), usages_.end(), usage) > 0;
+}
+
+}  // namespace subtlecrypto
+
+}  // namespace cobalt
diff --git a/src/cobalt/subtlecrypto/crypto_key.h b/src/cobalt/subtlecrypto/crypto_key.h
new file mode 100644
index 0000000..02a0e85
--- /dev/null
+++ b/src/cobalt/subtlecrypto/crypto_key.h
@@ -0,0 +1,70 @@
+// 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_SUBTLECRYPTO_CRYPTO_KEY_H_
+#define COBALT_SUBTLECRYPTO_CRYPTO_KEY_H_
+
+#include <string>
+#include <vector>
+
+#include "cobalt/script/union_type.h"
+#include "cobalt/script/value_handle.h"
+
+#include "cobalt/subtlecrypto/algorithm.h"
+#include "cobalt/subtlecrypto/key_type.h"
+#include "cobalt/subtlecrypto/key_usage.h"
+
+namespace cobalt {
+namespace subtlecrypto {
+
+class CryptoKey : public base::SupportsWeakPtr<CryptoKey>,
+                  public script::Wrappable {
+ public:
+  using AlgorithmIdentifier = script::UnionType2<Algorithm, std::string>;
+  using KeyData = std::vector<uint8_t>;
+
+  explicit CryptoKey(const std::string& algorithm) : algorithm_(algorithm) {}
+
+  // Hardcoded to secret key, as RSA/DSA keys are not supported
+  KeyType type() const { return KeyType::kKeyTypeSecret; }
+  bool extractable() const { return false; }
+
+  const AlgorithmIdentifier algorithm() const {
+    return AlgorithmIdentifier(algorithm_);
+  }
+  const std::string& get_algorithm() const { return algorithm_; }
+
+  void set_key(const KeyData& data) { key_ = data; }
+  const KeyData& get_key() const { return key_; }
+
+  void set_hash(const std::string& hash) { hash_ = hash; }
+  const std::string& get_hash() const { return hash_; }
+
+  const script::Sequence<KeyUsage>& usages();
+  bool set_usages(const script::Sequence<KeyUsage>& usages);
+  bool usage(const KeyUsage& usage) const;
+
+  DEFINE_WRAPPABLE_TYPE(CryptoKey);
+
+ private:
+  KeyData key_;
+  script::Sequence<KeyUsage> usages_;
+  std::string algorithm_;
+  std::string hash_;
+};
+
+}  // namespace subtlecrypto
+}  // namespace cobalt
+
+#endif  // COBALT_SUBTLECRYPTO_CRYPTO_KEY_H_
diff --git a/src/cobalt/subtlecrypto/crypto_key.idl b/src/cobalt/subtlecrypto/crypto_key.idl
new file mode 100644
index 0000000..365cd6b
--- /dev/null
+++ b/src/cobalt/subtlecrypto/crypto_key.idl
@@ -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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface
+
+[Exposed=Window]
+interface CryptoKey {
+    readonly attribute KeyType type;
+    readonly attribute boolean extractable;
+    readonly attribute AlgorithmIdentifier algorithm;
+    readonly attribute sequence<DOMString> usages;
+};
diff --git a/src/cobalt/subtlecrypto/import_key_algorithm_params.idl b/src/cobalt/subtlecrypto/import_key_algorithm_params.idl
new file mode 100644
index 0000000..6e913dc
--- /dev/null
+++ b/src/cobalt/subtlecrypto/import_key_algorithm_params.idl
@@ -0,0 +1,25 @@
+// 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.
+
+
+// Union of HmacImportParams and RsaHashedImportParams
+// https://www.w3.org/TR/WebCryptoAPI/#hmac-importparams
+// https://www.w3.org/TR/WebCryptoAPI/#dfn-RsaHashedImportParams
+
+dictionary ImportKeyAlgorithmParams : Algorithm {
+  // The inner hash function to use.
+  DOMString hash;
+  // The length (in bits) of the key.
+  unsigned long length;
+};
diff --git a/src/cobalt/subtlecrypto/key_format.idl b/src/cobalt/subtlecrypto/key_format.idl
new file mode 100644
index 0000000..b12e7f0
--- /dev/null
+++ b/src/cobalt/subtlecrypto/key_format.idl
@@ -0,0 +1,26 @@
+// 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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#dfn-KeyFormat
+
+enum KeyFormat {
+    // An unformatted sequence of bytes. Intended for secret keys.
+    "raw",
+    // The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
+    "spki",
+    // The DER encoding of the PrivateKeyInfo structure from RFC 5208.
+    "pkcs8",
+    // The key is a JsonWebKey dictionary encoded as a JavaScript object
+    "jwk"
+};
diff --git a/src/cobalt/subtlecrypto/key_type.idl b/src/cobalt/subtlecrypto/key_type.idl
new file mode 100644
index 0000000..f36458d
--- /dev/null
+++ b/src/cobalt/subtlecrypto/key_type.idl
@@ -0,0 +1,21 @@
+// 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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-types
+
+enum KeyType {
+    "public",
+    "private",
+    "secret"
+};
diff --git a/src/cobalt/subtlecrypto/key_usage.idl b/src/cobalt/subtlecrypto/key_usage.idl
new file mode 100644
index 0000000..7cec4d4
--- /dev/null
+++ b/src/cobalt/subtlecrypto/key_usage.idl
@@ -0,0 +1,26 @@
+// 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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-types
+
+enum KeyUsage {
+    "encrypt",
+    "decrypt",
+    "sign",
+    "verify",
+    "deriveKey",
+    "deriveBits",
+    "wrapKey",
+    "unwrapKey"
+};
diff --git a/src/cobalt/subtlecrypto/subtle_crypto.cc b/src/cobalt/subtlecrypto/subtle_crypto.cc
new file mode 100644
index 0000000..0634745
--- /dev/null
+++ b/src/cobalt/subtlecrypto/subtle_crypto.cc
@@ -0,0 +1,416 @@
+// 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 <algorithm>
+#include <set>
+#include <string>
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/dom_settings.h"
+
+#include "cobalt/subtlecrypto/crypto_impl.h"
+#include "cobalt/subtlecrypto/subtle_crypto.h"
+
+namespace cobalt {
+namespace subtlecrypto {
+
+namespace {
+
+// TODO: Test performance for large input data, and evaluate
+// removing copies of inputs to a vector.
+const ByteVector to_vector(const dom::BufferSource &data) {
+  const uint8_t *buff;
+  int buf_len;
+  cobalt::dom::GetBufferAndSize(data, &buff, &buf_len);
+  return ByteVector(buff, buff + buf_len);
+}
+
+std::string algo_name(const Algorithm &algo) {
+  return algo.has_name() ? algo.name() : "";
+}
+
+std::string to_string(SubtleCrypto::AlgorithmIdentifier algorithm) {
+  return algorithm.IsType<std::string>()
+             ? algorithm.AsType<std::string>()
+             : algo_name(algorithm.AsType<Algorithm>());
+}
+
+template <typename T, typename W>
+std::string getName(const W &algorithm) {
+  if (algorithm.template IsType<T>()) {
+    return algo_name(algorithm.template AsType<T>());
+  }
+  return "";
+}
+
+template <typename Promise>
+Promise reject(Promise &&promise,
+               const scoped_refptr<script::ScriptException> &result) {
+  promise->Reject(result);
+  return promise;
+}
+
+template <typename Promise>
+Promise reject(Promise &&promise, dom::DOMException::ExceptionCode error) {
+  return reject(promise, new dom::DOMException(error));
+}
+
+template <typename Promise>
+Promise reject(Promise &&promise, const std::string &dom_error) {
+  return reject(promise, new dom::DOMException(dom_error, dom_error));
+}
+
+}  // namespace
+
+using PromiseBool = SubtleCrypto::PromiseBool;
+using PromiseArray = SubtleCrypto::PromiseArray;
+using PromiseWrappable = SubtleCrypto::PromiseWrappable;
+
+SubtleCrypto::SubtleCrypto(script::EnvironmentSettings *environment) {
+  dom::DOMSettings *dom_settings =
+      base::polymorphic_downcast<dom::DOMSettings *>(environment);
+  global_env_ = dom_settings->global_environment();
+  script_value_factory_ = global_env_->script_value_factory();
+}
+
+SubtleCrypto::~SubtleCrypto() {}
+
+PromiseWrappable SubtleCrypto::CreateKeyPromise() {
+  DCHECK(script_value_factory_);
+  return script_value_factory_
+      ->CreateInterfacePromise<scoped_refptr<CryptoKeyPtr>>();
+}
+
+dom::BufferSource SubtleCrypto::CreateBufferSource(const ByteVector &input) {
+  DCHECK(global_env_);
+  auto arrayBuffer =
+      script::ArrayBuffer::New(global_env_, input.data(), input.size());
+  return dom::BufferSource(arrayBuffer);
+}
+
+PromiseArray SubtleCrypto::Decrypt(EncryptionAlgorithm algorithm,
+                                   const CryptoKeyPtr &key,
+                                   const BufferSource &data) {
+  // 1. Let algorithm and key be the algorithm and key parameters passed to the
+  // decrypt method, respectively.
+  // 2. Let data be the result of getting a copy of the bytes held by the data
+  // parameter passed to the decrypt method.
+  // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with
+  // alg set to algorithm and op set to "decrypt".
+  std::string normalizedAlgorithm = getName<AesCtrParams>(algorithm);
+  if (normalizedAlgorithm.empty())
+    normalizedAlgorithm = getName<Algorithm>(algorithm);
+  // 5. Let promise be a new Promise.
+  auto promise = CreatePromise();
+  // 4. If an error occurred, return a Promise rejected with
+  // normalizedAlgorithm.
+  if (normalizedAlgorithm != key->get_algorithm()) {
+    return reject(promise, DOMException::kInvalidAccessErr);
+  }
+  // 9. If the [[usages]] internal slot of key does not contain an entry that is
+  // "decrypt", then throw an InvalidAccessError.
+  if (!key->usage(KeyUsage::kKeyUsageDecrypt)) {
+    return reject(promise, DOMException::kInvalidAccessErr);
+  }
+  if (key->get_key().size() == 0) {
+    return reject(promise, DOMException::kInvalidAccessErr);
+  }
+  if (normalizedAlgorithm == "AES-CTR") {
+    auto ctr_params = algorithm.AsType<AesCtrParams>();
+    if (!(ctr_params.has_length() && ctr_params.has_counter())) {
+      return reject(promise, DOMException::kValidationErr);
+    }
+    auto iv = to_vector(ctr_params.counter());
+    auto counter_len = ctr_params.length();
+    // 25.7.1 If the counter member of normalizedAlgorithm does not have length
+    // 16 bytes, then throw an OperationError.
+    // 25.7.2 If the length member of normalizedAlgorithm is zero or is greater
+    // than 128, then throw an OperationError.
+    if (counter_len < 1 || counter_len > 128 || iv.size() != 16) {
+      return reject(promise, "OperationError");
+    }
+    // 10. Let plaintext be the result of performing the decrypt operation
+    // specified by normalizedAlgorithm using key and algorithm and with
+    // data as ciphertext.
+    auto out = CalculateAES_CTR(to_vector(data), key->get_key(), iv);
+    if (out.empty()) {
+      reject(promise, "Internal error");
+    }
+    // 11. Resolve promise with plaintext.
+    promise->Resolve(CreateBufferSource(out));
+  } else {
+    // 7. If the following steps or referenced procedures say to throw an error,
+    // reject promise with the returned error and then terminate the algorithm.
+    return reject(promise, DOMException::kNotSupportedErr);
+  }
+  // 6. Return promise and asynchronously perform the remaining steps.
+  return promise;
+}
+
+// TODO: Consider sharing the implementation with Decrypt, as the flow is
+// very similar for symmetric algos.
+PromiseArray SubtleCrypto::Encrypt(EncryptionAlgorithm algorithm,
+                                   const CryptoKeyPtr &key,
+                                   const BufferSource &data) {
+  // 1. Let algorithm and key be the algorithm and key parameters passed to the
+  // encrypt method, respectively.
+  // 2. Let data be the result of getting a copy of the bytes held by the data
+  // parameter passed to the encrypt method.
+  // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with
+  // alg set to algorithm and op set to "encrypt".
+  std::string normalizedAlgorithm = getName<AesCtrParams>(algorithm);
+  if (normalizedAlgorithm.empty())
+    normalizedAlgorithm = getName<Algorithm>(algorithm);
+  // 5. Let promise be a new Promise.
+  auto promise = CreatePromise();
+  // 4. If an error occurred, return a Promise rejected with
+  // normalizedAlgorithm.
+  if (normalizedAlgorithm != key->get_algorithm()) {
+    return reject(promise, DOMException::kInvalidAccessErr);
+  }
+  // 9. If the [[usages]] internal slot of key does not contain an entry that is
+  // "encrypt", then throw an InvalidAccessError.
+  if (!key->usage(KeyUsage::kKeyUsageEncrypt)) {
+    return reject(promise, DOMException::kInvalidAccessErr);
+  }
+  if (key->get_key().size() == 0) {
+    return reject(promise, DOMException::kInvalidAccessErr);
+  }
+  if (normalizedAlgorithm == "AES-CTR") {
+    auto ctr_params = algorithm.AsType<AesCtrParams>();
+    if (!(ctr_params.has_length() && ctr_params.has_counter())) {
+      return reject(promise, DOMException::kValidationErr);
+    }
+    auto iv = to_vector(ctr_params.counter());
+    auto counter_len = ctr_params.length();
+    // 25.7.1 If the counter member of normalizedAlgorithm does not have length
+    // 16 bytes, then throw an OperationError.
+    // 25.7.2 If the length member of normalizedAlgorithm is zero or is greater
+    // than 128, then throw an OperationError.
+    if (counter_len < 1 || counter_len > 128 || iv.size() != 16) {
+      return reject(promise, "OperationError");
+    }
+    // 10. Let ciphertext be the result of performing the encrypt operation
+    // specified by normalizedAlgorithm using algorithm and key and with data
+    // as plaintext.
+    auto out = CalculateAES_CTR(to_vector(data), key->get_key(), iv);
+    if (out.empty()) {
+      reject(promise, "Internal error");
+    }
+    // 11. Resolve promise with ciphertext.
+    promise->Resolve(CreateBufferSource(out));
+  } else {
+    // 7. If the following steps or referenced procedures say to throw an error,
+    // reject promise with the returned error and then terminate the algorithm.
+    return reject(promise, DOMException::kNotSupportedErr);
+  }
+  // 6. Return promise and asynchronously perform the remaining steps.
+  return promise;
+}
+
+PromiseArray SubtleCrypto::Sign(AlgorithmIdentifier algorithm,
+                                const CryptoKeyPtr &key,
+                                const BufferSource &data) {
+  // 1. Let algorithm and key be the algorithm and key parameters passed to the
+  // sign method, respectively.
+  // 2. Let data be the result of getting a copy of the bytes held by the data
+  // parameter passed to the sign method.
+  // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with
+  // alg set to algorithm and op set to "sign".
+  std::string normalizedAlgorithm = getName<Algorithm>(algorithm);
+  // 5. Let promise be a new Promise.
+  auto promise = CreatePromise();
+  if (normalizedAlgorithm == "HMAC") {
+    auto hash = Hash::CreateByName(key->get_hash());
+    // 9. If the [[usages]] internal slot of key does not contain an entry that
+    // is "sign", then throw an InvalidAccessError.
+    if (!key->usage(KeyUsage::kKeyUsageSign) || !hash) {
+      return reject(promise, DOMException::kInvalidAccessErr);
+    }
+    // 10. Let result be the result of performing the sign operation specified
+    // by normalizedAlgorithm using key and algorithm and with data as message.
+    auto out = hash->CalculateHMAC(to_vector(data), key->get_key());
+    // 11. Resolve promise with result.
+    promise->Resolve(CreateBufferSource(out));
+  } else {
+    // 7. If the following steps or referenced procedures say to throw an error,
+    // reject promise with the returned error and then terminate the algorithm.
+    return reject(promise, DOMException::kNotSupportedErr);
+  }
+  // 6. Return promise and asynchronously perform the remaining steps.
+  return promise;
+}
+
+PromiseBool SubtleCrypto::Verify(AlgorithmIdentifier algorithm,
+                                 const CryptoKeyPtr &key,
+                                 const BufferSource &signature,
+                                 const BufferSource &data) {
+  // 1. Let algorithm and key be the algorithm and key parameters passed to the
+  // verify method, respectively.
+  // 2. Let signature be the result of getting a copy of the bytes held by the
+  // signature parameter passed to the verify method.
+  // 3. Let data be the result of getting a copy of the bytes held by the data
+  // parameter passed to the verify method.
+  // 4. Let normalizedAlgorithm be the result of normalizing an algorithm, with
+  // alg set to algorithm and op set to "verify".
+  std::string normalizedAlgorithm = getName<Algorithm>(algorithm);
+  // 6. Let promise be a new Promise.
+  auto promise = CreatePromise<PromiseBool, bool>();
+  if (normalizedAlgorithm == "HMAC") {
+    auto hash = Hash::CreateByName(key->get_hash());
+    // 10. If the [[usages]] internal slot of key does not contain an entry that
+    // is "verify", then throw an InvalidAccessError.
+    if (!key->usage(KeyUsage::kKeyUsageVerify) || !hash) {
+      return reject(promise, DOMException::kInvalidAccessErr);
+    }
+    // 11. Let result be the result of performing the verify operation
+    // specified by normalizedAlgorithm using key, algorithm and signature
+    // and with data as message.
+    auto out = hash->CalculateHMAC(to_vector(data), key->get_key());
+    bool signature_matches = to_vector(signature) == out;
+    // 12. Resolve promise with result.
+    promise->Resolve(signature_matches);
+  } else {
+    // 8. If the following steps or referenced procedures say to throw an error,
+    // reject promise with the returned error and then terminate the algorithm.
+    return reject(promise, DOMException::kNotSupportedErr);
+  }
+  // 7. Return promise and asynchronously perform the remaining steps.
+  return promise;
+}
+
+PromiseArray SubtleCrypto::Digest(AlgorithmIdentifier algorithm,
+                                  const BufferSource &data) {
+  // 1. Let algorithm be the algorithm parameter passed to the digest method.
+  // 2. Let data be the result of getting a copy of the bytes held by the data
+  // parameter passed to the digest method.
+  // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with
+  // alg set to algorithm and op set to "digest".
+  auto normalizedAlgorithm = to_string(algorithm);
+  // 5. Let promise be a new Promise.
+  auto promise = CreatePromise();
+  auto hash = Hash::CreateByName(normalizedAlgorithm);
+  if (!hash) {
+    // 4. If an error occurred, return a Promise rejected with
+    // normalizedAlgorithm.
+    return reject(promise, DOMException::kNotSupportedErr);
+  }
+  // 8. Let result be the result of performing the digest operation specified by
+  // normalizedAlgorithm using algorithm, with data as message.
+  hash->Update(to_vector(data));
+  auto out = hash->Finish();
+  // 9. Resolve promise with result.
+  promise->Resolve(CreateBufferSource(out));
+  // 6. Return promise and asynchronously perform the remaining steps.
+  return promise;
+}
+
+PromiseArray SubtleCrypto::GenerateKey(AlgorithmIdentifier algorithm,
+                                       bool extractable,
+                                       const KeyUsages &keyUsages) {
+  NOTIMPLEMENTED();
+  return reject(CreatePromise(), DOMException::kNotSupportedErr);
+}
+
+PromiseArray SubtleCrypto::DeriveKey(AlgorithmIdentifier algorithm,
+                                     const CryptoKeyPtr &key,
+                                     AlgorithmIdentifier derivedKeyType,
+                                     bool extractable,
+                                     const KeyUsages &keyUsages) {
+  NOTIMPLEMENTED();
+  return reject(CreatePromise(), DOMException::kNotSupportedErr);
+}
+
+PromiseArray SubtleCrypto::DeriveBits(AlgorithmIdentifier algorithm,
+                                      const CryptoKeyPtr &key,
+                                      const uint32_t length) {
+  NOTIMPLEMENTED();
+  return reject(CreatePromise(), DOMException::kNotSupportedErr);
+}
+
+PromiseWrappable SubtleCrypto::ImportKey(
+    KeyFormat format, const BufferSource &keyData,
+    script::UnionType2<ImportKeyAlgorithmParams, std::string> algorithm,
+    bool extractable, const KeyUsages &keyUsages) {
+  // 1. Let format, algorithm, extractable and usages, be the format, algorithm,
+  // extractable and keyUsages parameters passed to the importKey method,
+  // respectively.
+  // 2. Let keyData be the result of getting a copy of the bytes held by the
+  // keyData parameter passed to the importKey method.
+  // 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with
+  // alg set to algorithm and op set to "importKey".
+  std::string normalizedAlgorithm =
+      getName<ImportKeyAlgorithmParams>(algorithm);
+  // 5. Let promise be a new Promise.
+  auto promise = CreateKeyPromise();
+
+  if (format != kKeyFormatRaw) {
+    NOTIMPLEMENTED();
+    return reject(promise, new DOMException(DOMException::kNotSupportedErr));
+  }
+
+  if (!(normalizedAlgorithm == "AES-CTR" || normalizedAlgorithm == "HMAC")) {
+    return reject(promise, new DOMException(DOMException::kNotSupportedErr));
+  }
+
+  // 8. Let result be the CryptoKey object that results from performing the
+  // import key operation specified by normalizedAlgorithm using keyData,
+  // algorithm, format, extractable and usages.
+  scoped_refptr<CryptoKey> cryptoKey = new CryptoKey(normalizedAlgorithm);
+
+  // 11. Set the [[usages]] internal slot of result to the normalized value of
+  // usages.
+  if (!cryptoKey->set_usages(keyUsages)) {
+    return reject(promise, new DOMException("Invalid key", "not allowed"));
+  }
+  // 25.7.1 Let data be the octet string contained in keyData.
+  cryptoKey->set_key(to_vector(keyData));
+  if (algorithm.IsType<ImportKeyAlgorithmParams>()) {
+    ImportKeyAlgorithmParams algo_params =
+        algorithm.AsType<ImportKeyAlgorithmParams>();
+    // 29.6.2 Set hash to equal the hash member of normalizedAlgorithm.
+    if (algo_params.has_hash()) {
+      cryptoKey->set_hash(algo_params.hash());
+    }
+  }
+  // 12. Resolve promise with result.
+  promise->Resolve(cryptoKey);
+  // 6. Return promise and asynchronously perform the remaining steps.
+  return promise;
+}
+
+PromiseArray SubtleCrypto::ExportKey(KeyFormat format,
+                                     const CryptoKeyPtr &key) {
+  NOTIMPLEMENTED();
+  return reject(CreatePromise(), DOMException::kNotSupportedErr);
+}
+
+PromiseArray SubtleCrypto::WrapKey(KeyFormat format, const CryptoKeyPtr &key,
+                                   const CryptoKeyPtr &wrappingKey,
+                                   AlgorithmIdentifier algorithm) {
+  NOTIMPLEMENTED();
+  return reject(CreatePromise(), DOMException::kNotSupportedErr);
+}
+
+PromiseWrappable SubtleCrypto::UnwrapKey(
+    KeyFormat format, const BufferSource &wrappedKey,
+    const CryptoKeyPtr &unwrappingKey, AlgorithmIdentifier unwrapAlgorithm,
+    AlgorithmIdentifier unwrappedKeyAlgorithm, bool extractacble,
+    const KeyUsages &keyUsages) {
+  NOTIMPLEMENTED();
+  return reject(CreateKeyPromise(), DOMException::kNotSupportedErr);
+}
+
+}  // namespace subtlecrypto
+}  // namespace cobalt
diff --git a/src/cobalt/subtlecrypto/subtle_crypto.h b/src/cobalt/subtlecrypto/subtle_crypto.h
new file mode 100644
index 0000000..62f2714
--- /dev/null
+++ b/src/cobalt/subtlecrypto/subtle_crypto.h
@@ -0,0 +1,113 @@
+// 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_SUBTLECRYPTO_SUBTLE_CRYPTO_H_
+#define COBALT_SUBTLECRYPTO_SUBTLE_CRYPTO_H_
+
+#include <string>
+#include <vector>
+
+#include "cobalt/dom/buffer_source.h"
+#include "cobalt/dom/dom_exception.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/exception_state.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/promise.h"
+
+#include "cobalt/subtlecrypto/aes_ctr_params.h"
+#include "cobalt/subtlecrypto/algorithm.h"
+#include "cobalt/subtlecrypto/crypto_key.h"
+#include "cobalt/subtlecrypto/import_key_algorithm_params.h"
+#include "cobalt/subtlecrypto/key_format.h"
+#include "cobalt/subtlecrypto/key_usage.h"
+
+namespace cobalt {
+
+namespace subtlecrypto {
+
+class SubtleCrypto : public base::SupportsWeakPtr<SubtleCrypto>,
+                     public script::Wrappable {
+ public:
+  using KeyUsages = script::Sequence<KeyUsage>;
+  using AlgorithmIdentifier = script::UnionType2<Algorithm, std::string>;
+  using EncryptionAlgorithm = script::UnionType2<AesCtrParams, Algorithm>;
+  using CryptoKeyPtr = scoped_refptr<CryptoKey>;
+  using BufferSource = dom::BufferSource;
+  using DOMException = dom::DOMException;
+
+  using PromiseArray = script::Handle<script::Promise<dom::BufferSource>>;
+  using PromiseBool = script::Handle<script::Promise<bool>>;
+  using PromiseWrappable =
+      script::Handle<script::Promise<scoped_refptr<script::Wrappable>>>;
+
+  explicit SubtleCrypto(script::EnvironmentSettings* environment);
+  ~SubtleCrypto() override;
+
+  PromiseArray Decrypt(EncryptionAlgorithm algorithm, const CryptoKeyPtr& key,
+                       const dom::BufferSource& data);
+  PromiseArray Encrypt(EncryptionAlgorithm algorithm, const CryptoKeyPtr& key,
+                       const dom::BufferSource& data);
+  PromiseArray Sign(AlgorithmIdentifier algorithm, const CryptoKeyPtr& key,
+                    const dom::BufferSource& data);
+  PromiseBool Verify(AlgorithmIdentifier algorithm, const CryptoKeyPtr& key,
+                     const dom::BufferSource& signature,
+                     const dom::BufferSource& data);
+  PromiseArray Digest(AlgorithmIdentifier algorithm,
+                      const dom::BufferSource& data);
+  PromiseArray GenerateKey(AlgorithmIdentifier algorithm, bool extractable,
+                           const KeyUsages& keyUsages);
+  PromiseArray DeriveKey(AlgorithmIdentifier algorithm, const CryptoKeyPtr& key,
+                         AlgorithmIdentifier derivedKeyType, bool extractable,
+                         const KeyUsages& keyUsages);
+  PromiseArray DeriveBits(AlgorithmIdentifier algorithm,
+                          const CryptoKeyPtr& key, const uint32_t length);
+  PromiseWrappable ImportKey(
+      KeyFormat format, const dom::BufferSource& keyData,
+      script::UnionType2<ImportKeyAlgorithmParams, std::string> algorithm,
+      bool extractable, const KeyUsages& keyUsages);
+  PromiseArray ExportKey(KeyFormat format, const CryptoKeyPtr& key);
+  PromiseArray WrapKey(KeyFormat format, const CryptoKeyPtr& key,
+                       const CryptoKeyPtr& wrappingKey,
+                       AlgorithmIdentifier algorithm);
+  PromiseWrappable UnwrapKey(KeyFormat format,
+                             const dom::BufferSource& wrappedKey,
+                             const CryptoKeyPtr& unwrappingKey,
+                             AlgorithmIdentifier unwrapAlgorithm,
+                             AlgorithmIdentifier unwrappedKeyAlgorithm,
+                             bool extractacble, const KeyUsages& keyUsages);
+
+  DEFINE_WRAPPABLE_TYPE(SubtleCrypto);
+
+ private:
+  SubtleCrypto(const SubtleCrypto&) = delete;
+  SubtleCrypto& operator=(const SubtleCrypto&) = delete;
+
+  cobalt::script::GlobalEnvironment* global_env_ = nullptr;
+  cobalt::script::ScriptValueFactory* script_value_factory_ = nullptr;
+
+  template <typename Promise = PromiseArray, typename T = dom::BufferSource>
+  Promise CreatePromise() {
+    DCHECK(script_value_factory_);
+    return script_value_factory_->CreateBasicPromise<T>();
+  }
+
+  PromiseWrappable CreateKeyPromise();
+  dom::BufferSource CreateBufferSource(const std::vector<uint8_t>& input);
+};
+
+}  // namespace subtlecrypto
+
+}  // namespace cobalt
+
+#endif  // COBALT_SUBTLECRYPTO_SUBTLE_CRYPTO_H_
diff --git a/src/cobalt/subtlecrypto/subtle_crypto.idl b/src/cobalt/subtlecrypto/subtle_crypto.idl
new file mode 100644
index 0000000..3294ea8
--- /dev/null
+++ b/src/cobalt/subtlecrypto/subtle_crypto.idl
@@ -0,0 +1,50 @@
+// 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.
+
+// https://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface
+
+[
+    Constructor,
+    ConstructorCallWith=EnvironmentSettings,
+    Exposed=(Window)
+] interface SubtleCrypto {
+    Promise<any> encrypt((AesCtrParams or Algorithm) algorithm, CryptoKey key, BufferSource data);
+    Promise<any> decrypt((AesCtrParams or Algorithm) algorithm, CryptoKey key, BufferSource data);
+
+    Promise<any> sign(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data);
+    Promise<boolean> verify(AlgorithmIdentifier algorithm, CryptoKey key,
+        BufferSource signature, BufferSource data);
+
+    Promise<any> digest(AlgorithmIdentifier algorithm, BufferSource data);
+
+    Promise<any> generateKey(AlgorithmIdentifier algorithm, boolean extractable,
+        sequence<KeyUsage> keyUsages);
+
+    Promise<any> deriveKey(AlgorithmIdentifier algorithm, CryptoKey baseKey,
+        AlgorithmIdentifier derivedKeyType, boolean extractable, sequence<KeyUsage> keyUsages);
+    Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm, CryptoKey baseKey,
+        unsigned long length);
+
+    Promise<CryptoKey> importKey(KeyFormat format, BufferSource keyData,
+        (ImportKeyAlgorithmParams or DOMString) algorithm, boolean extractable,
+        sequence<KeyUsage> keyUsages);
+
+    Promise<any> exportKey(KeyFormat format, CryptoKey key);
+
+    Promise<any> wrapKey(KeyFormat format, CryptoKey key, CryptoKey wrappingKey,
+        AlgorithmIdentifier wrapAlgorithm);
+    Promise<CryptoKey> unwrapKey(KeyFormat format, BufferSource wrappedKey, CryptoKey unwrappingKey,
+        AlgorithmIdentifier unwrapAlgorithm, AlgorithmIdentifier unwrappedKeyAlgorithm,
+        boolean extractable, sequence<KeyUsage> keyUsages);
+};
diff --git a/src/cobalt/subtlecrypto/subtlecrypto.gyp b/src/cobalt/subtlecrypto/subtlecrypto.gyp
new file mode 100644
index 0000000..240bc98
--- /dev/null
+++ b/src/cobalt/subtlecrypto/subtlecrypto.gyp
@@ -0,0 +1,74 @@
+# 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': {
+    'sb_pedantic_warnings': 1,
+  },
+  'targets': [
+    {
+      'target_name': 'crypto_impl',
+      'type': 'static_library',
+      'sources': [
+        'crypto_impl.cc',
+        'crypto_impl.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+        '<(DEPTH)/third_party/boringssl/boringssl.gyp:crypto',
+      ]
+    },
+    {
+      'target_name': 'crypto_impl_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        'crypto_impl_test.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+        'crypto_impl',
+      ],
+      'includes': [ '<(DEPTH)/cobalt/test/test.gypi' ],
+    },
+    {
+      'target_name': 'subtlecrypto',
+      'type': 'static_library',
+      'sources': [
+        'subtle_crypto.cc',
+        'subtle_crypto.h',
+        'crypto_key.cc',
+        'crypto_key.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
+        'crypto_impl',
+      ],
+    },
+    {
+      'target_name': 'crypto_impl_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'crypto_impl_test',
+      ],
+      'variables': {
+        'executable_name': 'crypto_impl_test',
+      },
+      'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+    },
+  ]
+}
+
diff --git a/src/cobalt/system_window/system_window.cc b/src/cobalt/system_window/system_window.cc
index 40eb174..8678ee5 100644
--- a/src/cobalt/system_window/system_window.cc
+++ b/src/cobalt/system_window/system_window.cc
@@ -109,13 +109,11 @@
   // Use the current time unless it was overridden.
   SbTimeMonotonic timestamp = 0;
 
-#if SB_API_VERSION >= 10
   bool use_input_timestamp =
       SbSystemHasCapability(kSbSystemCapabilitySetsInputTimestamp);
   if (use_input_timestamp) {
     timestamp = data.timestamp;
   }
-#endif
 
   if (timestamp == 0) {
     timestamp = SbTimeGetMonotonicNow();
diff --git a/src/cobalt/updater/BRANDING b/src/cobalt/updater/BRANDING
new file mode 100644
index 0000000..49eb4d5
--- /dev/null
+++ b/src/cobalt/updater/BRANDING
@@ -0,0 +1,4 @@
+COMPANY_FULLNAME=Google LLC
+COMPANY_SHORTNAME=Google
+PRODUCT_FULLNAME=GoogleUpdater
+COPYRIGHT=Copyright 2019 The Chromium Authors. All rights reserved.
diff --git a/src/cobalt/updater/BUILD.gn b/src/cobalt/updater/BUILD.gn
new file mode 100644
index 0000000..6de5b53
--- /dev/null
+++ b/src/cobalt/updater/BUILD.gn
@@ -0,0 +1,102 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chrome_build.gni")
+import("//build/config/sanitizers/sanitizers.gni")
+import("//build/util/process_version.gni")
+import("//testing/test.gni")
+
+group("updater") {
+  if (is_win) {
+    deps = [
+      "//chrome/updater/win",
+    ]
+  }
+  if (is_mac) {
+    deps = [
+      "//chrome/updater/mac",
+    ]
+  }
+}
+
+# Conditional build is needed, otherwise the analyze script on Linux
+# requires all targets and it is going to include the targets below.
+if (is_win || is_mac) {
+  source_set("common") {
+    sources = [
+      "configurator.cc",
+      "configurator.h",
+      "crash_client.cc",
+      "crash_client.h",
+      "crash_reporter.cc",
+      "crash_reporter.h",
+      "installer.cc",
+      "installer.h",
+      "patcher.cc",
+      "patcher.h",
+      "prefs.cc",
+      "prefs.h",
+      "unzipper.cc",
+      "unzipper.h",
+      "updater.cc",
+      "updater.h",
+      "updater_constants.cc",
+      "updater_constants.h",
+      "util.cc",
+      "util.h",
+    ]
+
+    deps = [
+      ":version_header",
+      "//base",
+      "//components/crash/core/common:crash_key",
+      "//components/prefs",
+      "//components/update_client",
+      "//components/version_info",
+      "//courgette",
+      "//third_party/crashpad/crashpad/client",
+      "//third_party/crashpad/crashpad/handler",
+      "//third_party/zlib/google:zip",
+      "//url",
+    ]
+  }
+
+  process_version("version_header") {
+    sources = [
+      "//chrome/VERSION",
+      "BRANDING",
+    ]
+    template_file = "updater_version.h.in"
+    output = "$target_gen_dir/updater_version.h"
+  }
+
+  source_set("updater_tests") {
+    testonly = true
+
+    sources = [
+      "updater_unittest.cc",
+    ]
+
+    deps = [
+      ":common",
+      ":updater",
+      "//base/test:test_support",
+      "//testing/gtest",
+    ]
+
+    if (is_win) {
+      deps += [ "//chrome/updater/win:updater_tests" ]
+
+      data_deps = [
+        "//chrome/updater/win:updater",
+      ]
+    }
+
+    if (is_mac) {
+      data_deps = [
+        "//chrome/updater/mac:updater",
+      ]
+    }
+  }
+}
diff --git a/src/cobalt/updater/README.md b/src/cobalt/updater/README.md
new file mode 100644
index 0000000..67d0c46
--- /dev/null
+++ b/src/cobalt/updater/README.md
@@ -0,0 +1,4 @@
+This is the code for the client updater that will soon be used by
+desktop Chrome, on macOS and Windows.
+
+Please join chrome-updates-dev@chromium.org for topics related to this project.
diff --git a/src/cobalt/updater/configurator.cc b/src/cobalt/updater/configurator.cc
new file mode 100644
index 0000000..d89d0ca
--- /dev/null
+++ b/src/cobalt/updater/configurator.cc
@@ -0,0 +1,195 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cobalt/updater/configurator.h"
+
+#include <set>
+
+#include "base/version.h"
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/updater/network_fetcher.h"
+#include "cobalt/updater/patcher.h"
+#include "cobalt/updater/prefs.h"
+#include "cobalt/updater/unzipper.h"
+#include "cobalt/updater/updater_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/update_client/network.h"
+#include "components/update_client/patcher.h"
+#include "components/update_client/protocol_handler.h"
+#include "components/update_client/unzipper.h"
+
+#include "url/gurl.h"
+
+namespace {
+
+// Default time constants.
+const int kDelayOneMinute = 60;
+const int kDelayOneHour = kDelayOneMinute * 60;
+
+#if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
+const std::set<std::string> valid_channels = {"dev"};
+#elif defined(COBALT_BUILD_TYPE_QA)
+// Find more information about these test channels in the Evergreen test plan.
+const std::set<std::string> valid_channels = {
+    "qa",
+    // A normal test channel that serves a valid update
+    "test",
+    // Test an update with mismatched sabi
+    "tmsabi",
+    // Test an update that does nothing
+    "tnoop",
+    // Test an update that crashes
+    "tcrash",
+    // Test an update that fails verification
+    "tfailv",
+    // Test a series of continuous updates with two channels
+    "tseries1",
+    "tseries2",
+    // Test an update that's larger than the available storage on the device
+    "tistore",
+};
+#elif defined(COBALT_BUILD_TYPE_GOLD)
+const std::set<std::string> valid_channels = {"prod", "dogfood"};
+#endif
+
+}  // namespace
+
+namespace cobalt {
+namespace updater {
+
+Configurator::Configurator(network::NetworkModule* network_module)
+    : pref_service_(CreatePrefService()),
+      unzip_factory_(base::MakeRefCounted<UnzipperFactory>()),
+      network_fetcher_factory_(
+          base::MakeRefCounted<NetworkFetcherFactoryCobalt>(network_module)),
+      patch_factory_(base::MakeRefCounted<PatcherFactory>()) {}
+Configurator::~Configurator() = default;
+
+int Configurator::InitialDelay() const { return 0; }
+
+int Configurator::NextCheckDelay() const { return 5 * kDelayOneHour; }
+
+int Configurator::OnDemandDelay() const { return 0; }
+
+int Configurator::UpdateDelay() const { return 0; }
+
+std::vector<GURL> Configurator::UpdateUrl() const {
+  return std::vector<GURL>{GURL(kUpdaterJSONDefaultUrl)};
+}
+
+std::vector<GURL> Configurator::PingUrl() const { return UpdateUrl(); }
+
+std::string Configurator::GetProdId() const { return "cobalt"; }
+
+base::Version Configurator::GetBrowserVersion() const {
+  return base::Version("1.0.0.0");  // version_info::GetVersion();
+}
+
+std::string Configurator::GetBrand() const { return {}; }
+
+std::string Configurator::GetLang() const { return {}; }
+
+std::string Configurator::GetOSLongName() const {
+  return "Starboard";  // version_info::GetOSType();
+}
+
+base::flat_map<std::string, std::string> Configurator::ExtraRequestParams()
+    const {
+  base::flat_map<std::string, std::string> params;
+  params.insert(std::make_pair("SABI", SB_SABI_JSON_ID));
+  params.insert(std::make_pair("sbversion", std::to_string(SB_API_VERSION)));
+  params.insert(std::make_pair(
+      "jsengine", script::GetJavaScriptEngineNameAndVersion()));
+  params.insert(std::make_pair("updaterchannelchanged",
+                               IsChannelChanged() ? "True" : "False"));
+  return params;
+}
+
+std::string Configurator::GetDownloadPreference() const { return {}; }
+
+scoped_refptr<update_client::NetworkFetcherFactory>
+Configurator::GetNetworkFetcherFactory() {
+  return network_fetcher_factory_;
+}
+
+scoped_refptr<update_client::UnzipperFactory>
+Configurator::GetUnzipperFactory() {
+  return unzip_factory_;
+}
+
+scoped_refptr<update_client::PatcherFactory> Configurator::GetPatcherFactory() {
+  return patch_factory_;
+}
+
+bool Configurator::EnabledDeltas() const { return false; }
+
+bool Configurator::EnabledComponentUpdates() const { return false; }
+
+bool Configurator::EnabledBackgroundDownloader() const { return false; }
+
+// TODO: enable cup signing
+bool Configurator::EnabledCupSigning() const { return false; }
+
+PrefService* Configurator::GetPrefService() const {
+  return pref_service_.get();
+}
+
+update_client::ActivityDataService* Configurator::GetActivityDataService()
+    const {
+  return nullptr;
+}
+
+bool Configurator::IsPerUserInstall() const { return true; }
+
+std::vector<uint8_t> Configurator::GetRunActionKeyHash() const { return {}; }
+
+std::string Configurator::GetAppGuid() const {
+  return "{6D4E53F3-CC64-4CB8-B6BD-AB0B8F300E1C}";
+}
+
+std::unique_ptr<update_client::ProtocolHandlerFactory>
+Configurator::GetProtocolHandlerFactory() const {
+  return std::make_unique<update_client::ProtocolHandlerFactoryJSON>();
+}
+
+update_client::RecoveryCRXElevator Configurator::GetRecoveryCRXElevator()
+    const {
+  return {};
+}
+
+// The updater channel is get and set by main web module thread and update
+// client thread. The getter and set use a lock to prevent synchronization
+// issue.
+std::string Configurator::GetChannel() const {
+  base::AutoLock auto_lock(const_cast<base::Lock&>(updater_channel_lock_));
+  return updater_channel_;
+}
+
+void Configurator::SetChannel(const std::string& updater_channel) {
+  base::AutoLock auto_lock(updater_channel_lock_);
+  updater_channel_ = updater_channel;
+}
+
+bool Configurator::IsChannelValid(const std::string& channel) {
+  if (!valid_channels.count(channel)) {
+    SetUpdaterStatus(std::string("Invalid channel requested"));
+    return false;
+  }
+  return true;
+}
+
+// The updater status is get by main web module thread and set by the updater
+// thread. The getter and set use a lock to prevent synchronization issue.
+std::string Configurator::GetUpdaterStatus() const {
+  base::AutoLock auto_lock(const_cast<base::Lock&>(updater_status_lock_));
+  return updater_status_;
+}
+
+void Configurator::SetUpdaterStatus(const std::string& status) {
+  base::AutoLock auto_lock(updater_status_lock_);
+  updater_status_ = status;
+}
+
+}  // namespace updater
+}  // namespace cobalt
diff --git a/src/cobalt/updater/configurator.h b/src/cobalt/updater/configurator.h
new file mode 100644
index 0000000..8012b2f
--- /dev/null
+++ b/src/cobalt/updater/configurator.h
@@ -0,0 +1,106 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COBALT_UPDATER_CONFIGURATOR_H_
+#define COBALT_UPDATER_CONFIGURATOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "cobalt/network/network_module.h"
+#include "components/update_client/configurator.h"
+
+class GURL;
+class PrefService;
+
+namespace base {
+class Version;
+}  // namespace base
+
+namespace service_manager {
+class Connector;
+}  // namespace service_manager
+
+namespace update_client {
+class ActivityDataService;
+class NetworkFetcherFactory;
+class ProtocolHandlerFactory;
+}  // namespace update_client
+
+namespace cobalt {
+namespace updater {
+
+class Configurator : public update_client::Configurator {
+ public:
+  explicit Configurator(network::NetworkModule* network_module);
+
+  // Configurator for update_client::Configurator.
+  int InitialDelay() const override;
+  int NextCheckDelay() const override;
+  int OnDemandDelay() const override;
+  int UpdateDelay() const override;
+  std::vector<GURL> UpdateUrl() const override;
+  std::vector<GURL> PingUrl() const override;
+  std::string GetProdId() const override;
+  base::Version GetBrowserVersion() const override;
+  std::string GetChannel() const override;
+  std::string GetBrand() const override;
+  std::string GetLang() const override;
+  std::string GetOSLongName() const override;
+  base::flat_map<std::string, std::string> ExtraRequestParams() const override;
+  std::string GetDownloadPreference() const override;
+  scoped_refptr<update_client::NetworkFetcherFactory> GetNetworkFetcherFactory()
+      override;
+  scoped_refptr<update_client::UnzipperFactory> GetUnzipperFactory() override;
+  scoped_refptr<update_client::PatcherFactory> GetPatcherFactory() override;
+  bool EnabledDeltas() const override;
+  bool EnabledComponentUpdates() const override;
+  bool EnabledBackgroundDownloader() const override;
+  bool EnabledCupSigning() const override;
+  PrefService* GetPrefService() const override;
+  update_client::ActivityDataService* GetActivityDataService() const override;
+  bool IsPerUserInstall() const override;
+  std::vector<uint8_t> GetRunActionKeyHash() const override;
+  std::string GetAppGuid() const override;
+  std::unique_ptr<update_client::ProtocolHandlerFactory>
+  GetProtocolHandlerFactory() const override;
+  update_client::RecoveryCRXElevator GetRecoveryCRXElevator() const override;
+
+  void SetChannel(const std::string& updater_channel) override;
+
+  void MarkChannelChanged() { is_channel_changed = true; }
+  bool IsChannelChanged() const override { return is_channel_changed; }
+  bool IsChannelValid(const std::string& channel);
+
+  std::string GetUpdaterStatus() const;
+  void SetUpdaterStatus(const std::string& status);
+
+ private:
+  friend class base::RefCountedThreadSafe<Configurator>;
+  ~Configurator() override;
+
+  std::unique_ptr<PrefService> pref_service_;
+  scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
+  scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
+  scoped_refptr<update_client::PatcherFactory> patch_factory_;
+  std::string updater_channel_;
+  base::Lock updater_channel_lock_;
+  bool is_channel_changed = false;
+  std::string updater_status_;
+  base::Lock updater_status_lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(Configurator);
+};
+
+}  // namespace updater
+}  // namespace cobalt
+
+#endif  // COBALT_UPDATER_CONFIGURATOR_H_
diff --git a/src/cobalt/updater/crash_client.cc b/src/cobalt/updater/crash_client.cc
new file mode 100644
index 0000000..d989e2a
--- /dev/null
+++ b/src/cobalt/updater/crash_client.cc
@@ -0,0 +1,161 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cobalt/updater/crash_client.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "cobalt/updater/util.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/client/prune_crash_reports.h"
+#include "third_party/crashpad/crashpad/client/settings.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include "base/win/wrapped_window_proc.h"
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+
+int __cdecl HandleWinProcException(EXCEPTION_POINTERS* exception_pointers) {
+  crashpad::CrashpadClient::DumpAndCrash(exception_pointers);
+  return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#endif
+
+}  // namespace
+
+namespace cobalt {
+namespace updater {
+
+CrashClient::CrashClient() = default;
+CrashClient::~CrashClient() = default;
+
+// static
+CrashClient* CrashClient::GetInstance() {
+  static base::NoDestructor<CrashClient> crash_client;
+  return crash_client.get();
+}
+
+bool CrashClient::InitializeDatabaseOnly() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::FilePath handler_path;
+  base::PathService::Get(base::FILE_EXE, &handler_path);
+
+  base::FilePath database_path;
+  if (!GetProductDirectory(&database_path)) {
+    LOG(ERROR) << "Failed to get the database path.";
+    return false;
+  }
+
+  database_ = crashpad::CrashReportDatabase::Initialize(database_path);
+  if (!database_) {
+    LOG(ERROR) << "Failed to initialize Crashpad database.";
+    return false;
+  }
+
+  return true;
+}
+
+bool CrashClient::InitializeCrashReporting() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!InitializeDatabaseOnly())
+    return false;
+
+#if defined(OS_WIN)
+  // Catch exceptions thrown from a window procedure.
+  base::win::WinProcExceptionFilter exception_filter =
+      base::win::SetWinProcExceptionFilter(&HandleWinProcException);
+  LOG_IF(DFATAL, exception_filter) << "Exception filter already present";
+#endif  // OS_WIN
+
+  std::vector<crashpad::CrashReportDatabase::Report> reports_completed;
+  const crashpad::CrashReportDatabase::OperationStatus status_completed =
+      database_->GetCompletedReports(&reports_completed);
+  if (status_completed == crashpad::CrashReportDatabase::kNoError) {
+    VLOG(1) << "Found " << reports_completed.size()
+            << " completed crash reports";
+    for (const auto& report : reports_completed) {
+      VLOG(1) << "Crash since last run: ID \"" << report.id << "\", created at "
+              << report.creation_time << ", " << report.upload_attempts
+              << " upload attempts, file path \"" << report.file_path
+              << "\", unique ID \"" << report.uuid.ToString()
+              << "\"; uploaded: " << (report.uploaded ? "yes" : "no");
+    }
+  } else {
+    LOG(ERROR) << "Failed to fetch completed crash reports: "
+               << status_completed;
+  }
+
+  std::vector<crashpad::CrashReportDatabase::Report> reports_pending;
+  const crashpad::CrashReportDatabase::OperationStatus status_pending =
+      database_->GetPendingReports(&reports_pending);
+  if (status_pending == crashpad::CrashReportDatabase::kNoError) {
+    VLOG(1) << "Found " << reports_pending.size() << " pending crash reports";
+    for (const auto& report : reports_pending) {
+      VLOG(1) << "Crash since last run: (pending), created at "
+              << report.creation_time << ", " << report.upload_attempts
+              << " upload attempts, file path \"" << report.file_path
+              << "\", unique ID \"" << report.uuid.ToString() << "\"";
+    }
+  } else {
+    LOG(ERROR) << "Failed to fetch pending crash reports: " << status_pending;
+  }
+
+  // TODO(sorin): fix before shipping to users, crbug.com/940098.
+  crashpad::Settings* crashpad_settings = database_->GetSettings();
+  DCHECK(crashpad_settings);
+  crashpad_settings->SetUploadsEnabled(true);
+
+  return true;
+}
+
+// static
+std::string CrashClient::GetClientId() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetInstance()->sequence_checker_);
+  DCHECK(GetInstance()->database_) << "Crash reporting not initialized";
+  crashpad::Settings* settings = GetInstance()->database_->GetSettings();
+  DCHECK(settings);
+
+  crashpad::UUID uuid;
+  if (!settings->GetClientID(&uuid)) {
+    LOG(ERROR) << "Unable to retrieve client ID from Crashpad database";
+    return {};
+  }
+
+  std::string uuid_string = uuid.ToString();
+  base::ReplaceSubstringsAfterOffset(&uuid_string, 0, "-", "");
+  return uuid_string;
+}
+
+// static
+bool CrashClient::IsUploadEnabled() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetInstance()->sequence_checker_);
+  DCHECK(GetInstance()->database_) << "Crash reporting not initialized";
+  crashpad::Settings* settings = GetInstance()->database_->GetSettings();
+  DCHECK(settings);
+
+  bool upload_enabled = false;
+  if (!settings->GetUploadsEnabled(&upload_enabled)) {
+    LOG(ERROR) << "Unable to verify if crash uploads are enabled or not";
+    return false;
+  }
+  return upload_enabled;
+}
+
+}  // namespace updater
+}  // namespace cobalt
diff --git a/src/cobalt/updater/crash_client.h b/src/cobalt/updater/crash_client.h
new file mode 100644
index 0000000..46bcebc
--- /dev/null
+++ b/src/cobalt/updater/crash_client.h
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COBALT_UPDATER_CRASH_CLIENT_H_
+#define COBALT_UPDATER_CRASH_CLIENT_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+
+namespace base {
+template <typenam