Import Cobalt 23.lts.1.309088
diff --git a/cobalt/bindings/path_generator.py b/cobalt/bindings/path_generator.py
index 192ff59..ee91538 100644
--- a/cobalt/bindings/path_generator.py
+++ b/cobalt/bindings/path_generator.py
@@ -63,22 +63,7 @@
 
     rel_idl_path = os.path.relpath(idl_path, self.interfaces_root)
     components = os.path.dirname(rel_idl_path).split(os.sep)
-
-    # Check if this IDL's path lies in our interfaces root.  If it does not,
-    # we treat it as an extension IDL.
-    real_interfaces_root = os.path.realpath(self.interfaces_root)
-    real_idl_path = os.path.realpath(os.path.dirname(idl_path))
-    interfaces_root_is_in_components_path = (
-        os.path.commonprefix([real_interfaces_root,
-                              real_idl_path]) == real_interfaces_root)
-
-    if interfaces_root_is_in_components_path:
-      return [os.path.basename(self.interfaces_root)] + components
-    else:
-      # If our IDL path lies outside of the cobalt/ directory, assume it is
-      # an externally defined web extension and assign it the 'webapi_extension'
-      # namespace.
-      return [os.path.basename(self.interfaces_root), 'webapi_extension']
+    return [os.path.basename(self.interfaces_root)] + components
 
   def Namespace(self, interface_name):
     """Get the interface's namespace."""
diff --git a/cobalt/bindings/testing/BUILD.gn b/cobalt/bindings/testing/BUILD.gn
index 523ca00..b97f61c 100644
--- a/cobalt/bindings/testing/BUILD.gn
+++ b/cobalt/bindings/testing/BUILD.gn
@@ -94,6 +94,7 @@
   "promise_interface.idl",
   "put_forwards_interface.idl",
   "sequence_user.idl",
+  "serialize_script_value_interface.idl",
   "single_operation_interface.idl",
   "static_properties_interface.idl",
   "stringifier_anonymous_operation_interface.idl",
@@ -185,6 +186,7 @@
     "promise_test.cc",
     "put_forwards_test.cc",
     "sequence_bindings_test.cc",
+    "serialize_script_value_test.cc",
     "stack_trace_test.cc",
     "static_properties_bindings_test.cc",
     "stringifier_bindings_test.cc",
diff --git a/cobalt/bindings/testing/serialize_script_value_interface.h b/cobalt/bindings/testing/serialize_script_value_interface.h
new file mode 100644
index 0000000..782edb2
--- /dev/null
+++ b/cobalt/bindings/testing/serialize_script_value_interface.h
@@ -0,0 +1,63 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BINDINGS_TESTING_SERIALIZE_SCRIPT_VALUE_INTERFACE_H_
+#define COBALT_BINDINGS_TESTING_SERIALIZE_SCRIPT_VALUE_INTERFACE_H_
+
+#include <memory>
+#include <utility>
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/typed_arrays.h"
+#include "cobalt/script/value_handle.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/web/context.h"
+#include "cobalt/web/environment_settings.h"
+#include "v8/include/v8.h"
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class SerializeScriptValueInterface : public script::Wrappable {
+ public:
+  SerializeScriptValueInterface() = default;
+
+  size_t serialized_size() { return data_buffer_->size; }
+
+  void SerializeTest(const script::ValueHandleHolder& value) {
+    data_buffer_ = std::move(script::SerializeScriptValue(value));
+    isolate_ = script::GetIsolate(value);
+  }
+
+  const script::ValueHandleHolder* DeserializeTest() {
+    deserialized_.reset(
+        script::DeserializeScriptValue(isolate_, *data_buffer_));
+    return deserialized_.get();
+  }
+
+  DEFINE_WRAPPABLE_TYPE(SerializeScriptValueInterface);
+
+ private:
+  std::unique_ptr<script::DataBuffer> data_buffer_;
+  std::unique_ptr<script::ValueHandleHolder> deserialized_;
+  v8::Isolate* isolate_;
+};
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
+
+#endif  // COBALT_BINDINGS_TESTING_SERIALIZE_SCRIPT_VALUE_INTERFACE_H_
diff --git a/starboard/shared/test_webapi_extension/my_new_interface.idl b/cobalt/bindings/testing/serialize_script_value_interface.idl
similarity index 75%
rename from starboard/shared/test_webapi_extension/my_new_interface.idl
rename to cobalt/bindings/testing/serialize_script_value_interface.idl
index 8f49995..9fd6a06 100644
--- a/starboard/shared/test_webapi_extension/my_new_interface.idl
+++ b/cobalt/bindings/testing/serialize_script_value_interface.idl
@@ -1,4 +1,4 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-interface MyNewInterface {
-  attribute DOMString foo;
-
-  void SetMyNewEnum(MyNewEnum value);
-  MyNewEnum GetMyNewEnum();
+[ Constructor ]
+interface SerializeScriptValueInterface {
+  void serializeTest(any value);
+  any deserializeTest();
 };
diff --git a/cobalt/bindings/testing/serialize_script_value_test.cc b/cobalt/bindings/testing/serialize_script_value_test.cc
new file mode 100644
index 0000000..96e0dc6
--- /dev/null
+++ b/cobalt/bindings/testing/serialize_script_value_test.cc
@@ -0,0 +1,55 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/strings/string_number_conversions.h"
+#include "cobalt/bindings/testing/bindings_test_base.h"
+#include "cobalt/bindings/testing/serialize_script_value_interface.h"
+
+using ::testing::ContainsRegex;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class SerializeScriptValueTest
+    : public InterfaceBindingsTest<SerializeScriptValueInterface> {
+ public:
+  void ExpectTrue(const std::string& script) {
+    std::string result;
+    EvaluateScript("`${" + script + "}`", &result);
+    EXPECT_EQ("true", result)
+        << "Expect \"" + script + "\" to evaluate to true.";
+  }
+};
+
+TEST_F(SerializeScriptValueTest, Serialize) {
+  // Stores serialized result that is then used by |deserializeTest()|.
+  EvaluateScript(R"(
+      test.serializeTest({a: ['something'], b: new Uint8Array([42])});
+    )");
+  // Sanity check the serialized size.
+  EXPECT_EQ(32, test_mock().serialized_size());
+  ExpectTrue("!(test.deserializeTest() instanceof Array)");
+  ExpectTrue("test.deserializeTest() instanceof Object");
+  ExpectTrue("test.deserializeTest().a instanceof Array");
+  ExpectTrue("1 === test.deserializeTest().a.length");
+  ExpectTrue("'something' === test.deserializeTest().a[0]");
+  ExpectTrue("test.deserializeTest().b instanceof Uint8Array");
+  ExpectTrue("!(test.deserializeTest().b instanceof Uint16Array)");
+  ExpectTrue("42 === test.deserializeTest().b[0]");
+}
+
+}  // namespace testing
+}  // namespace bindings
+}  // namespace cobalt
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index d95d150..b80da96 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -21,6 +21,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -28,7 +29,6 @@
 #include "base/path_service.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/cobalt_paths.h"
@@ -144,15 +144,6 @@
     "activated or not.  While activated, input will constantly and randomly be "
     "generated and passed directly into the main web module.";
 
-const char kSetMediaConfigCommand[] = "set_media_config";
-const char kSetMediaConfigCommandShortHelp[] =
-    "Sets media module configuration.";
-const char kSetMediaConfigCommandLongHelp[] =
-    "This can be called in the form of set_media_config('name=value'), where "
-    "name is a string and value is an int.  Refer to the implementation of "
-    "MediaModule::SetConfiguration() on individual platform for settings "
-    "supported on the particular platform.";
-
 const char kScreenshotCommand[] = "screenshot";
 const char kScreenshotCommandShortHelp[] = "Takes a screenshot.";
 const char kScreenshotCommandLongHelp[] =
@@ -268,10 +259,6 @@
           kFuzzerToggleCommand,
           base::Bind(&BrowserModule::OnFuzzerToggle, base::Unretained(this)),
           kFuzzerToggleCommandShortHelp, kFuzzerToggleCommandLongHelp)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(set_media_config_command_handler_(
-          kSetMediaConfigCommand,
-          base::Bind(&BrowserModule::OnSetMediaConfig, base::Unretained(this)),
-          kSetMediaConfigCommandShortHelp, kSetMediaConfigCommandLongHelp)),
       ALLOW_THIS_IN_INITIALIZER_LIST(screenshot_command_handler_(
           kScreenshotCommand,
           base::Bind(&OnScreenshotMessage, base::Unretained(this)),
@@ -450,9 +437,10 @@
   switch (application_state_) {
     case base::kApplicationStateStarted:
       Blur(0);
-    // Intentional fall-through.
+      FALLTHROUGH;
     case base::kApplicationStateBlurred:
       Conceal(0);
+      FALLTHROUGH;
     case base::kApplicationStateConcealed:
       Freeze(0);
       break;
@@ -1011,30 +999,6 @@
   }
 }
 
-void BrowserModule::OnSetMediaConfig(const std::string& config) {
-  if (base::MessageLoop::current() != self_message_loop_) {
-    self_message_loop_->task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(&BrowserModule::OnSetMediaConfig, weak_this_, config));
-    return;
-  }
-
-  std::vector<std::string> tokens = base::SplitString(
-      config, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  int value;
-  if (tokens.size() != 2 || !base::StringToInt(tokens[1], &value)) {
-    LOG(WARNING) << "Media configuration '" << config << "' is not in the"
-                 << " form of '<string name>=<int value>'.";
-    return;
-  }
-  if (media_module_->SetConfiguration(tokens[0], value)) {
-    LOG(INFO) << "Successfully setting " << tokens[0] << " to " << value;
-  } else {
-    LOG(WARNING) << "Failed to set " << tokens[0] << " to " << value;
-  }
-}
-
 void BrowserModule::OnDisableMediaCodecs(const std::string& codecs) {
   disabled_media_codecs_ = codecs;
   can_play_type_handler_->SetDisabledMediaCodecs(codecs);
@@ -2059,8 +2023,11 @@
 
 scoped_refptr<script::Wrappable> BrowserModule::CreateH5vcc(
     script::EnvironmentSettings* settings) {
+  DCHECK(web_module_);
+
   h5vcc::H5vcc::Settings h5vcc_settings;
-  h5vcc_settings.media_module = media_module_.get();
+  h5vcc_settings.set_media_source_setting_func = base::Bind(
+      &WebModule::SetMediaSourceSetting, base::Unretained(web_module_.get()));
   h5vcc_settings.network_module = network_module_;
 #if SB_IS(EVERGREEN)
   h5vcc_settings.updater_module = updater_module_;
diff --git a/cobalt/browser/browser_module.h b/cobalt/browser/browser_module.h
index b618caa..6730fa2 100644
--- a/cobalt/browser/browser_module.h
+++ b/cobalt/browser/browser_module.h
@@ -355,10 +355,6 @@
   // Toggles the input fuzzer on/off.  Ignores the parameter.
   void OnFuzzerToggle(const std::string&);
 
-  // Use the config in the form of '<string name>=<int value>' to call
-  // MediaModule::SetConfiguration().
-  void OnSetMediaConfig(const std::string& config);
-
   // Sets the disabled media codecs in the debug console and in
   // the CanPlayTypeHandler instance.
   // Future requests to play videos with these codecs will report that these
@@ -615,10 +611,6 @@
   debug::console::ConsoleCommandManager::CommandHandler
       fuzzer_toggle_command_handler_;
 
-  // Command handler object for setting media module config.
-  debug::console::ConsoleCommandManager::CommandHandler
-      set_media_config_command_handler_;
-
   // Command handler object for screenshot command from the debug console.
   debug::console::ConsoleCommandManager::CommandHandler
       screenshot_command_handler_;
diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc
index 8311b31..ecc0f56 100644
--- a/cobalt/browser/web_module.cc
+++ b/cobalt/browser/web_module.cc
@@ -48,6 +48,7 @@
 #include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/keyboard_event_init.h"
 #include "cobalt/dom/local_storage_database.h"
+#include "cobalt/dom/media_source_settings.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
 #include "cobalt/dom/navigation_type.h"
 #include "cobalt/dom/navigator.h"
@@ -215,6 +216,8 @@
   void SetSize(cssom::ViewportSize viewport_size);
   void UpdateCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
   void SetMediaModule(media::MediaModule* media_module);
+  void SetMediaSourceSetting(const std::string& name, int value,
+                             bool* succeeded);
   void SetImageCacheCapacity(int64_t bytes);
   void SetRemoteTypefaceCacheCapacity(int64_t bytes);
 
@@ -396,6 +399,9 @@
   // Object to register and retrieve MediaSource object with a string key.
   std::unique_ptr<dom::MediaSource::Registry> media_source_registry_;
 
+  // Object to hold WebModule wide settings for MediaSource related objects.
+  dom::MediaSourceSettingsImpl media_source_settings_;
+
   // The Window object wraps all DOM-related components.
   scoped_refptr<dom::Window> window_;
 
@@ -580,8 +586,8 @@
 
   web_context_->setup_environment_settings(new dom::DOMSettings(
       debugger_hooks_, kDOMMaxElementDepth, media_source_registry_.get(),
-      data.can_play_type_handler, memory_info, &mutation_observer_task_manager_,
-      data.options.dom_settings_options));
+      &media_source_settings_, data.can_play_type_handler, memory_info,
+      &mutation_observer_task_manager_, data.options.dom_settings_options));
   DCHECK(web_context_->environment_settings());
   // From algorithm to setup up a window environment settings object:
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-window-environment-settings-object
@@ -706,7 +712,7 @@
 
   web_context_->global_environment()->SetReportEvalCallback(
       base::Bind(&web::CspDelegate::ReportEval,
-                 base::Unretained(window_->document()->csp_delegate())));
+                 base::Unretained(window_->csp_delegate())));
 
   web_context_->global_environment()->SetReportErrorCallback(
       base::Bind(&WebModule::Impl::ReportScriptError, base::Unretained(this)));
@@ -1013,12 +1019,10 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(is_running_);
   DCHECK(window_);
-  DCHECK(window_->document());
-  DCHECK(window_->document()->csp_delegate());
+  DCHECK(window_->csp_delegate());
 
   std::string eval_disabled_message;
-  bool allow_eval =
-      window_->document()->csp_delegate()->AllowEval(&eval_disabled_message);
+  bool allow_eval = window_->csp_delegate()->AllowEval(&eval_disabled_message);
   if (allow_eval) {
     web_context_->global_environment()->EnableEval();
   } else {
@@ -1096,6 +1100,12 @@
   window_->set_web_media_player_factory(media_module);
 }
 
+void WebModule::Impl::SetMediaSourceSetting(const std::string& name, int value,
+                                            bool* succeeded) {
+  DCHECK(succeeded);
+  *succeeded = media_source_settings_.Set(name, value);
+}
+
 void WebModule::Impl::SetApplicationState(base::ApplicationState state,
                                           SbTimeMonotonic timestamp) {
   window_->SetApplicationState(state, timestamp);
@@ -1576,6 +1586,12 @@
   impl_->SetMediaModule(media_module);
 }
 
+bool WebModule::SetMediaSourceSetting(const std::string& name, int value) {
+  bool succeeded = false;
+  SetMediaSourceSettingInternal(name, value, &succeeded);
+  return succeeded;
+}
+
 void WebModule::SetImageCacheCapacity(int64_t bytes) {
   POST_TO_ENSURE_IMPL_ON_THREAD(SetImageCacheCapacity, bytes);
   impl_->SetImageCacheCapacity(bytes);
@@ -1709,5 +1725,12 @@
   impl_->SetUnloadEventTimingInfo(start_time, end_time);
 }
 
+void WebModule::SetMediaSourceSettingInternal(const std::string& name,
+                                              int value, bool* succeeded) {
+  POST_AND_BLOCK_TO_ENSURE_IMPL_ON_THREAD(SetMediaSourceSettingInternal, name,
+                                          value, succeeded);
+  impl_->SetMediaSourceSetting(name, value, succeeded);
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/cobalt/browser/web_module.h b/cobalt/browser/web_module.h
index b7c21e5..3e442c8 100644
--- a/cobalt/browser/web_module.h
+++ b/cobalt/browser/web_module.h
@@ -338,6 +338,7 @@
 
   void UpdateCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
   void SetMediaModule(media::MediaModule* media_module);
+  bool SetMediaSourceSetting(const std::string& name, int value);
   void SetImageCacheCapacity(int64_t bytes);
   void SetRemoteTypefaceCacheCapacity(int64_t bytes);
 
@@ -458,10 +459,11 @@
 
   void ClearAllIntervalsAndTimeouts();
 
-  void CancelSynchronousLoads();
-
   void GetIsReadyToFreeze(volatile bool* is_ready_to_freeze);
 
+  void SetMediaSourceSettingInternal(const std::string& name, int value,
+                                     bool* succeeded);
+
   // The message loop this object is running on.
   base::MessageLoop* message_loop() const {
     DCHECK(web_agent_);
diff --git a/cobalt/build/build_number.py b/cobalt/build/build_number.py
index b0307e3..7ec534e 100644
--- a/cobalt/build/build_number.py
+++ b/cobalt/build/build_number.py
@@ -44,28 +44,15 @@
 
 def GetRevinfo():
   """Get absolute state of all git repos."""
-  try:
-    repo_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'
-                                        ]).strip().decode('utf-8')
-  except subprocess.CalledProcessError:
-    logging.info('Could not get repo root. Trying again in src/')
-    try:
-      repo_root = subprocess.check_output(
-          ['git', '-C', 'src', 'rev-parse',
-           '--show-toplevel']).strip().decode('utf-8')
-    except subprocess.CalledProcessError as e:
-      logging.warning('Failed to get revision information: %s', e)
-      return {}
-
   # First make sure we can add the cobalt_src repo.
   try:
-    repos = CheckRevInfo('.', cwd=repo_root)
+    repos = CheckRevInfo('.', cwd=paths.REPOSITORY_ROOT)
   except subprocess.CalledProcessError as e:
     logging.warning('Failed to get revision information: %s', e)
     return {}
 
   for rel_path in _SUBREPO_PATHS:
-    path = os.path.join(repo_root, rel_path)
+    path = os.path.join(paths.REPOSITORY_ROOT, rel_path)
     try:
       repos.update(CheckRevInfo(rel_path, cwd=path))
     except subprocess.CalledProcessError as e:
diff --git a/cobalt/debug/backend/debug_module.cc b/cobalt/debug/backend/debug_module.cc
index 982f8f3..56e8b26 100644
--- a/cobalt/debug/backend/debug_module.cc
+++ b/cobalt/debug/backend/debug_module.cc
@@ -123,7 +123,7 @@
       script::ScriptDebugger::CreateDebugger(data.global_environment, this);
   script_runner_.reset(new DebugScriptRunner(
       data.global_environment, script_debugger_.get(),
-      data.window ? data.window->document()->csp_delegate() : nullptr));
+      data.window ? data.window->csp_delegate() : nullptr));
   debug_dispatcher_.reset(
       new DebugDispatcher(script_debugger_.get(), script_runner_.get()));
   debug_backend_ = WrapRefCounted(new DebugBackend(
diff --git a/cobalt/demos/content/BUILD.gn b/cobalt/demos/content/BUILD.gn
index e87713e..c23edf1 100644
--- a/cobalt/demos/content/BUILD.gn
+++ b/cobalt/demos/content/BUILD.gn
@@ -133,7 +133,6 @@
       "performance-spike/assets/banner720.jpg",
       "performance-spike/assets/banner720baked.jpg",
       "performance-spike/assets/icons.ttf",
-      "performance-spike/assets/profile-alecmce.jpg",
       "performance-spike/css/default.css",
       "performance-spike/css/icons-content.css",
       "performance-spike/css/icons.css",
diff --git a/cobalt/demos/content/watchdog-demo/index.html b/cobalt/demos/content/watchdog-demo/index.html
index 0213470..c5f17f8 100644
--- a/cobalt/demos/content/watchdog-demo/index.html
+++ b/cobalt/demos/content/watchdog-demo/index.html
@@ -59,7 +59,7 @@
           let ret = 'void';
 
           if (watchdogFunction == 'register') {
-            ret = h5vcc.crashLog.register('test-name', 'test-description', 'started', 5000000, 0, 'none');
+            ret = h5vcc.crashLog.register('test-name', 'test-description', 'started', 3000, 0, 'none');
           } else if (watchdogFunction == 'unregister') {
             ret = h5vcc.crashLog.unregister('test-name');
           } else if (watchdogFunction == 'ping') {
diff --git a/cobalt/dom/BUILD.gn b/cobalt/dom/BUILD.gn
index 74caf14..e16397b 100644
--- a/cobalt/dom/BUILD.gn
+++ b/cobalt/dom/BUILD.gn
@@ -187,6 +187,8 @@
     "media_query_list.h",
     "media_source.cc",
     "media_source.h",
+    "media_source_settings.cc",
+    "media_source_settings.h",
     "memory_info.cc",
     "memory_info.h",
     "mime_type_array.cc",
@@ -259,10 +261,14 @@
     "screenshot.h",
     "screenshot_manager.cc",
     "screenshot_manager.h",
+    "serialized_algorithm_runner.cc",
+    "serialized_algorithm_runner.h",
     "serializer.cc",
     "serializer.h",
     "source_buffer.cc",
     "source_buffer.h",
+    "source_buffer_algorithm.cc",
+    "source_buffer_algorithm.h",
     "source_buffer_list.cc",
     "source_buffer_list.h",
     "source_buffer_metrics.cc",
@@ -382,6 +388,7 @@
     "local_storage_database_test.cc",
     "location_test.cc",
     "media_query_list_test.cc",
+    "media_source_settings_test.cc",
     "mutation_observer_test.cc",
     "named_node_map_test.cc",
     "navigator_licenses_test.cc",
diff --git a/cobalt/dom/document.cc b/cobalt/dom/document.cc
index 22323c1..4e665a5 100644
--- a/cobalt/dom/document.cc
+++ b/cobalt/dom/document.cc
@@ -60,8 +60,6 @@
 #include "cobalt/dom/wheel_event.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/script/global_environment.h"
-#include "cobalt/web/csp_delegate.h"
-#include "cobalt/web/csp_delegate_factory.h"
 #include "cobalt/web/custom_event.h"
 #include "cobalt/web/dom_exception.h"
 #include "cobalt/web/message_event.h"
@@ -71,6 +69,17 @@
 
 namespace cobalt {
 namespace dom {
+namespace {
+csp::SecurityCallback CreateSecurityCallback(
+    web::CspDelegate* csp_delegate, web::CspDelegate::ResourceType type) {
+  csp::SecurityCallback callback;
+  if (csp_delegate) {
+    callback = base::Bind(&web::CspDelegate::CanLoad,
+                          base::Unretained(csp_delegate), type);
+  }
+  return callback;
+}
+}  // namespace
 
 Document::Document(HTMLElementContext* html_element_context,
                    const Options& options)
@@ -113,20 +122,11 @@
     SetViewport(*options.viewport_size);
   }
 
-  std::unique_ptr<web::CspViolationReporter> violation_reporter(
-      new web::CspViolationReporter(this, options.post_sender));
-  csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
-      options.csp_enforcement_mode, std::move(violation_reporter), options.url,
-      options.require_csp, options.csp_policy_changed_callback,
-      options.csp_insecure_allowed_token);
-
   cookie_jar_ = options.cookie_jar;
 
   location_ = new Location(
       options.url, options.hashchange_callback, options.navigation_callback,
-      base::Bind(&web::CspDelegate::CanLoad,
-                 base::Unretained(csp_delegate_.get()),
-                 web::CspDelegate::kLocation),
+      CreateSecurityCallback(csp_delegate(), web::CspDelegate::kLocation),
       base::Bind(&Document::SetNavigationType, base::Unretained(this)));
 
   font_cache_.reset(new FontCache(
@@ -138,15 +138,11 @@
   if (HasBrowsingContext()) {
     if (html_element_context_->remote_typeface_cache()) {
       html_element_context_->remote_typeface_cache()->set_security_callback(
-          base::Bind(&web::CspDelegate::CanLoad,
-                     base::Unretained(csp_delegate_.get()),
-                     web::CspDelegate::kFont));
+          CreateSecurityCallback(csp_delegate(), web::CspDelegate::kFont));
     }
-
     if (html_element_context_->image_cache()) {
-      html_element_context_->image_cache()->set_security_callback(base::Bind(
-          &web::CspDelegate::CanLoad, base::Unretained(csp_delegate_.get()),
-          web::CspDelegate::kImage));
+      html_element_context_->image_cache()->set_security_callback(
+          CreateSecurityCallback(csp_delegate(), web::CspDelegate::kImage));
     }
 
     ready_state_ = kDocumentReadyStateLoading;
@@ -640,7 +636,7 @@
 
 void Document::NotifyUrlChanged(const GURL& url) {
   location_->set_url(url);
-  csp_delegate_->NotifyUrlChanged(url);
+  csp_delegate()->NotifyUrlChanged(url);
 }
 
 void Document::OnFocusChange() {
diff --git a/cobalt/dom/document.h b/cobalt/dom/document.h
index b50a8d9..2fc0246 100644
--- a/cobalt/dom/document.h
+++ b/cobalt/dom/document.h
@@ -55,8 +55,6 @@
 #include "cobalt/network_bridge/net_poster.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/wrappable.h"
-#include "cobalt/web/csp_delegate.h"
-#include "cobalt/web/csp_delegate_type.h"
 #include "cobalt/web/event.h"
 #include "starboard/time.h"
 #include "url/gurl.h"
@@ -105,15 +103,9 @@
                  public ApplicationLifecycleState::Observer {
  public:
   struct Options {
-    Options()
-        : window(NULL),
-          cookie_jar(NULL),
-          csp_enforcement_mode(web::kCspEnforcementEnable) {}
+    Options() : window(NULL), cookie_jar(NULL) {}
     explicit Options(const GURL& url_value)
-        : url(url_value),
-          window(NULL),
-          cookie_jar(NULL),
-          csp_enforcement_mode(web::kCspEnforcementEnable) {}
+        : url(url_value), window(NULL), cookie_jar(NULL) {}
     Options(const GURL& url_value, Window* window,
             const base::Closure& hashchange_callback,
             const scoped_refptr<base::BasicClock>& navigation_start_clock_value,
@@ -121,11 +113,7 @@
             const scoped_refptr<cssom::CSSStyleSheet> user_agent_style_sheet,
             const base::Optional<cssom::ViewportSize>& viewport_size,
             network_bridge::CookieJar* cookie_jar,
-            const network_bridge::PostSender& post_sender,
-            csp::CSPHeaderPolicy require_csp,
-            web::CspEnforcementType csp_enforcement_mode,
-            const base::Closure& csp_policy_changed_callback,
-            int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0)
+            int dom_max_element_depth = 0)
         : url(url_value),
           window(window),
           hashchange_callback(hashchange_callback),
@@ -134,11 +122,6 @@
           user_agent_style_sheet(user_agent_style_sheet),
           viewport_size(viewport_size),
           cookie_jar(cookie_jar),
-          post_sender(post_sender),
-          require_csp(require_csp),
-          csp_enforcement_mode(csp_enforcement_mode),
-          csp_policy_changed_callback(csp_policy_changed_callback),
-          csp_insecure_allowed_token(csp_insecure_allowed_token),
           dom_max_element_depth(dom_max_element_depth) {}
 
     GURL url;
@@ -149,11 +132,6 @@
     scoped_refptr<cssom::CSSStyleSheet> user_agent_style_sheet;
     base::Optional<cssom::ViewportSize> viewport_size;
     network_bridge::CookieJar* cookie_jar;
-    network_bridge::PostSender post_sender;
-    csp::CSPHeaderPolicy require_csp;
-    web::CspEnforcementType csp_enforcement_mode;
-    base::Closure csp_policy_changed_callback;
-    int csp_insecure_allowed_token;
     int dom_max_element_depth;
   };
 
@@ -386,7 +364,13 @@
     return navigation_start_clock_;
   }
 
-  web::CspDelegate* csp_delegate() const { return csp_delegate_.get(); }
+  // Virtual for testing.
+  virtual web::CspDelegate* csp_delegate() const {
+    if (window_) {
+      return window_->csp_delegate();
+    }
+    return nullptr;
+  }
 
   // Triggers a synchronous layout.
   scoped_refptr<render_tree::Node> DoSynchronousLayoutAndGetRenderTree();
@@ -594,8 +578,6 @@
 
   // Viewport size.
   base::Optional<cssom::ViewportSize> viewport_size_;
-  // Content Security Policy enforcement for this document.
-  std::unique_ptr<web::CspDelegate> csp_delegate_;
   network_bridge::CookieJar* cookie_jar_;
   // Associated location object.
   scoped_refptr<Location> location_;
diff --git a/cobalt/dom/document_test.cc b/cobalt/dom/document_test.cc
index 434fc9b..8909802 100644
--- a/cobalt/dom/document_test.cc
+++ b/cobalt/dom/document_test.cc
@@ -34,6 +34,7 @@
 #include "cobalt/dom/location.h"
 #include "cobalt/dom/mouse_event.h"
 #include "cobalt/dom/node_list.h"
+#include "cobalt/dom/testing/fake_document.h"
 #include "cobalt/dom/testing/html_collection_testing.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/text.h"
@@ -90,26 +91,30 @@
 //////////////////////////////////////////////////////////////////////////
 
 TEST_F(DocumentTest, Create) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   ASSERT_TRUE(document);
 
   EXPECT_EQ(Node::kDocumentNode, document->node_type());
   EXPECT_EQ("#document", document->node_name());
 
   GURL url("http://a valid url");
-  document = new Document(&html_element_context_, Document::Options(url));
+  document =
+      new testing::FakeDocument(&html_element_context_, Document::Options(url));
   EXPECT_EQ(url.spec(), document->url());
   EXPECT_EQ(url.spec(), document->document_uri());
   EXPECT_EQ(url, document->url_as_gurl());
 }
 
 TEST_F(DocumentTest, IsNotXMLDocument) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_FALSE(document->IsXMLDocument());
 }
 
 TEST_F(DocumentTest, DocumentElement) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_EQ(NULL, document->document_element().get());
 
   scoped_refptr<Text> text = new Text(document, "test_text");
@@ -121,7 +126,8 @@
 }
 
 TEST_F(DocumentTest, CreateElement) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   scoped_refptr<Element> element = document->CreateElement("element");
 
   EXPECT_EQ(Node::kElementNode, element->node_type());
@@ -137,7 +143,8 @@
 }
 
 TEST_F(DocumentTest, CreateTextNode) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   scoped_refptr<Text> text = document->CreateTextNode("test_text");
 
   EXPECT_EQ(Node::kTextNode, text->node_type());
@@ -147,7 +154,8 @@
 }
 
 TEST_F(DocumentTest, CreateComment) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   scoped_refptr<Comment> comment = document->CreateComment("test_comment");
 
   EXPECT_EQ(Node::kCommentNode, comment->node_type());
@@ -159,7 +167,8 @@
 TEST_F(DocumentTest, CreateEventEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Create an Event, the name is case insensitive.
   scoped_refptr<web::Event> event =
@@ -183,7 +192,8 @@
 TEST_F(DocumentTest, CreateEventCustomEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Create an Event, the name is case insensitive.
   scoped_refptr<web::Event> event =
@@ -196,7 +206,8 @@
 TEST_F(DocumentTest, CreateEventUIEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Create an Event, the name is case insensitive.
   scoped_refptr<web::Event> event =
@@ -214,7 +225,8 @@
 TEST_F(DocumentTest, CreateEventKeyboardEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Create an Event, the name is case insensitive.
   scoped_refptr<web::Event> event =
@@ -232,7 +244,8 @@
 TEST_F(DocumentTest, CreateEventMessageEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Create an Event, the name is case insensitive.
   scoped_refptr<web::Event> event =
@@ -245,7 +258,8 @@
 TEST_F(DocumentTest, CreateEventMouseEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Create an Event, the name is case insensitive.
   scoped_refptr<web::Event> event =
@@ -263,7 +277,8 @@
 TEST_F(DocumentTest, CreateEventEventNotSupported) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   EXPECT_CALL(exception_state, SetException(_))
       .WillOnce(SaveArg<0>(&exception));
@@ -278,17 +293,20 @@
 }
 
 TEST_F(DocumentTest, GetElementsByClassName) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   testing::TestGetElementsByClassName(document);
 }
 
 TEST_F(DocumentTest, GetElementsByTagName) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   testing::TestGetElementsByTagName(document);
 }
 
 TEST_F(DocumentTest, GetElementById) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   // Construct a tree:
   // document
@@ -321,17 +339,20 @@
 }
 
 TEST_F(DocumentTest, Implementation) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_TRUE(document->implementation());
 }
 
 TEST_F(DocumentTest, Location) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_TRUE(document->location());
 }
 
 TEST_F(DocumentTest, StyleSheets) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   scoped_refptr<HTMLElement> element1 =
       html_element_context_.html_element_factory()->CreateHTMLElement(
@@ -376,7 +397,8 @@
 }
 
 TEST_F(DocumentTest, StyleSheetsAddedToFront) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
 
   scoped_refptr<HTMLElement> element1 =
       html_element_context_.html_element_factory()->CreateHTMLElement(
@@ -421,7 +443,8 @@
 }
 
 TEST_F(DocumentTest, HtmlElement) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_FALSE(document->html());
 
   scoped_refptr<Node> div =
@@ -435,7 +458,8 @@
 }
 
 TEST_F(DocumentTest, HeadElement) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_FALSE(document->head());
 
   scoped_refptr<Node> html =
@@ -453,7 +477,8 @@
 }
 
 TEST_F(DocumentTest, BodyElement) {
-  scoped_refptr<Document> document = new Document(&html_element_context_);
+  scoped_refptr<Document> document =
+      new testing::FakeDocument(&html_element_context_);
   EXPECT_FALSE(document->body());
 
   scoped_refptr<Node> html =
diff --git a/cobalt/dom/dom_settings.cc b/cobalt/dom/dom_settings.cc
index 6c9e66d..d07474b 100644
--- a/cobalt/dom/dom_settings.cc
+++ b/cobalt/dom/dom_settings.cc
@@ -28,6 +28,7 @@
 DOMSettings::DOMSettings(
     const base::DebuggerHooks& debugger_hooks, const int max_dom_element_depth,
     MediaSourceRegistry* media_source_registry,
+    const MediaSourceSettings* media_source_settings,
     media::CanPlayTypeHandler* can_play_type_handler,
     const media::DecoderBufferMemoryInfo* decoder_buffer_memory_info,
     MutationObserverTaskManager* mutation_observer_task_manager,
@@ -36,6 +37,7 @@
       max_dom_element_depth_(max_dom_element_depth),
       microphone_options_(options.microphone_options),
       media_source_registry_(media_source_registry),
+      media_source_settings_(media_source_settings),
       can_play_type_handler_(can_play_type_handler),
       decoder_buffer_memory_info_(decoder_buffer_memory_info),
       mutation_observer_task_manager_(mutation_observer_task_manager) {}
diff --git a/cobalt/dom/dom_settings.h b/cobalt/dom/dom_settings.h
index 8eb437a..33a5c0a 100644
--- a/cobalt/dom/dom_settings.h
+++ b/cobalt/dom/dom_settings.h
@@ -15,8 +15,10 @@
 #ifndef COBALT_DOM_DOM_SETTINGS_H_
 #define COBALT_DOM_DOM_SETTINGS_H_
 
+#include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "cobalt/base/debugger_hooks.h"
+#include "cobalt/dom/media_source_settings.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/decoder_buffer_memory_info.h"
@@ -55,6 +57,7 @@
   DOMSettings(const base::DebuggerHooks& debugger_hooks,
               const int max_dom_element_depth,
               MediaSourceRegistry* media_source_registry,
+              const MediaSourceSettings* media_source_settings,
               media::CanPlayTypeHandler* can_play_type_handler,
               const media::DecoderBufferMemoryInfo* decoder_buffer_memory_info,
               MutationObserverTaskManager* mutation_observer_task_manager,
@@ -71,6 +74,9 @@
   MediaSourceRegistry* media_source_registry() const {
     return media_source_registry_;
   }
+  const MediaSourceSettings* media_source_settings() const {
+    return media_source_settings_;
+  }
   void set_decoder_buffer_memory_info(
       const media::DecoderBufferMemoryInfo* decoder_buffer_memory_info) {
     decoder_buffer_memory_info_ = decoder_buffer_memory_info;
@@ -96,6 +102,7 @@
   const int max_dom_element_depth_;
   const speech::Microphone::Options microphone_options_;
   MediaSourceRegistry* media_source_registry_;
+  const MediaSourceSettings* media_source_settings_;
   media::CanPlayTypeHandler* can_play_type_handler_;
   const media::DecoderBufferMemoryInfo* decoder_buffer_memory_info_;
   MutationObserverTaskManager* mutation_observer_task_manager_;
diff --git a/cobalt/dom/element_test.cc b/cobalt/dom/element_test.cc
index 0128e48..bfdd04d 100644
--- a/cobalt/dom/element_test.cc
+++ b/cobalt/dom/element_test.cc
@@ -31,6 +31,7 @@
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/named_node_map.h"
 #include "cobalt/dom/node_list.h"
+#include "cobalt/dom/testing/fake_document.h"
 #include "cobalt/dom/testing/html_collection_testing.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/text.h"
@@ -70,7 +71,7 @@
                             NULL, dom_stat_tracker_.get(), "",
                             base::kApplicationStateStarted, NULL, NULL) {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
-  document_ = new Document(&html_element_context_);
+  document_ = new testing::FakeDocument(&html_element_context_);
   xml_document_ = new XMLDocument(&html_element_context_);
 }
 
diff --git a/cobalt/dom/event_queue.cc b/cobalt/dom/event_queue.cc
index 1bd5282..ca851e8 100644
--- a/cobalt/dom/event_queue.cc
+++ b/cobalt/dom/event_queue.cc
@@ -44,6 +44,23 @@
   events_.push_back(event);
 }
 
+void EventQueue::EnqueueAndMaybeDispatchImmediately(
+    const scoped_refptr<web::Event>& event) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  bool was_empty = events_.empty();
+
+  Enqueue(event);
+
+  if (was_empty) {
+    // We can only dispatch the event immediately if there aren't any existing
+    // events in the queue, because dom activities (including events) have to
+    // happen in order, and any existing events in the queue may mean that these
+    // events have to be dispatched after other activities not tracked here.
+    DispatchEvents();
+  }
+}
+
 void EventQueue::CancelAllEvents() {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
diff --git a/cobalt/dom/event_queue.h b/cobalt/dom/event_queue.h
index 624bd8e..6aad863 100644
--- a/cobalt/dom/event_queue.h
+++ b/cobalt/dom/event_queue.h
@@ -42,7 +42,13 @@
   // The EventTarget is guaranteed to be valid during the life time and should
   // usually be the owner.
   explicit EventQueue(web::EventTarget* event_target);
+
   void Enqueue(const scoped_refptr<web::Event>& event);
+  // Try to dispatch the event immediately if there are no existing events in
+  // the queue, otherwise it has the same behavior as Enqueue(), which enqueues
+  // the event and the event will be dispatched after the existing events.
+  void EnqueueAndMaybeDispatchImmediately(
+      const scoped_refptr<web::Event>& event);
   void CancelAllEvents();
 
   void TraceMembers(script::Tracer* tracer) override;
diff --git a/cobalt/dom/event_queue_test.cc b/cobalt/dom/event_queue_test.cc
index e8fc35f..be4a970 100644
--- a/cobalt/dom/event_queue_test.cc
+++ b/cobalt/dom/event_queue_test.cc
@@ -90,6 +90,42 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(EventQueueTest, EnqueueAndMaybeDispatchImmediatelyTest) {
+  scoped_refptr<web::EventTarget> event_target =
+      new web::EventTarget(&environment_settings_);
+  scoped_refptr<web::Event> event = new web::Event(base::Token("event"));
+  std::unique_ptr<MockEventListener> event_listener =
+      MockEventListener::Create();
+  EventQueue event_queue(event_target.get());
+
+  event->set_target(event_target);
+  event_target->AddEventListener(
+      "event", FakeScriptValue<web::EventListener>(event_listener.get()),
+      false);
+
+  {
+    ::testing::InSequence s;
+    ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
+                                            event_target);
+    // The event should be dispatched immediately as the queue is empty, so the
+    // expectation should be set before being enqueued.
+    event_queue.EnqueueAndMaybeDispatchImmediately(event);
+  }
+
+  {
+    ::testing::InSequence s;
+    event_queue.Enqueue(event);
+    // The event won't be dispatched immediately as the queue isn't empty, so
+    // the expectations can be set after being enqueued.
+    event_queue.EnqueueAndMaybeDispatchImmediately(event);
+    ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
+                                            event_target);
+    ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
+                                            event_target);
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
 TEST_F(EventQueueTest, CancelAllEventsTest) {
   scoped_refptr<web::EventTarget> event_target =
       new web::EventTarget(&environment_settings_);
diff --git a/cobalt/dom/html_element_test.cc b/cobalt/dom/html_element_test.cc
index e8936f9..9426a09 100644
--- a/cobalt/dom/html_element_test.cc
+++ b/cobalt/dom/html_element_test.cc
@@ -31,6 +31,7 @@
 #include "cobalt/dom/html_div_element.h"
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/named_node_map.h"
+#include "cobalt/dom/testing/fake_document.h"
 #include "cobalt/dom/testing/mock_layout_boxes.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_window.h"
@@ -86,7 +87,7 @@
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
                               "", base::kApplicationStateStarted, NULL, NULL),
-        document_(new Document(&html_element_context_)) {}
+        document_(new testing::FakeDocument(&html_element_context_)) {}
   ~HTMLElementTest() override {}
 
   // This creates simple DOM tree with mock layout boxes for all elements except
diff --git a/cobalt/dom/media_source.cc b/cobalt/dom/media_source.cc
index 290996f..f9f2c05 100644
--- a/cobalt/dom/media_source.cc
+++ b/cobalt/dom/media_source.cc
@@ -52,7 +52,9 @@
 #include "base/compiler_specific.h"
 #include "base/guid.h"
 #include "base/logging.h"
+#include "base/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/web/dom_exception.h"
@@ -63,19 +65,80 @@
 namespace cobalt {
 namespace dom {
 
+namespace {
+
 using ::media::CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR;
 using ::media::CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR;
 using ::media::PIPELINE_OK;
 using ::media::PipelineStatus;
 
+auto GetMediaSettings(script::EnvironmentSettings* settings) {
+  DOMSettings* dom_settings =
+      base::polymorphic_downcast<DOMSettings*>(settings);
+  DCHECK(dom_settings);
+  DCHECK(dom_settings->media_source_settings());
+  return dom_settings->media_source_settings();
+}
+
+// If the system has more processors than the specified value, SourceBuffer
+// append and remove algorithm will be offloaded to a non-web thread to reduce
+// the load on the web thread.
+// The default value is 2.  Set to a reasonably high value (say 1024) will
+// disable algorithm offloading completely.
+bool IsAlgorithmOffloadEnabled(script::EnvironmentSettings* settings) {
+  int min_process_count_to_offload =
+      GetMediaSettings(settings)
+          ->GetMinimumProcessorCountToOffloadAlgorithm()
+          .value_or(2);
+  DCHECK_GE(min_process_count_to_offload, 0);
+  return SbSystemGetNumberOfProcessors() >= min_process_count_to_offload;
+}
+
+// If this function returns true, SourceBuffer will reduce asynchronous
+// behaviors.  For example, queued events will be dispatached immediately when
+// possible.
+// The default value is true.
+bool IsAsynchronousReductionEnabled(script::EnvironmentSettings* settings) {
+  return GetMediaSettings(settings)->IsAsynchronousReductionEnabled().value_or(
+      true);
+}
+
+// If the size of a job that is part of an algorithm is less than or equal to
+// the return value of this function, the implementation will run the job
+// immediately instead of scheduling it to run later to reduce latency.
+// NOTE: This only works when IsAsynchronousReductionEnabled() returns true,
+//       and it is currently only enabled for buffer append.
+// The default value is 16 KB.  Set to 0 will disable immediate job completely.
+int GetMinSizeForImmediateJob(script::EnvironmentSettings* settings) {
+  const int kDefaultMinSize = 16 * 1024;
+  auto min_size =
+      GetMediaSettings(settings)->GetMinSizeForImmediateJob().value_or(
+          kDefaultMinSize);
+  DCHECK_GE(min_size, 0);
+  return min_size;
+}
+
+}  // namespace
+
 MediaSource::MediaSource(script::EnvironmentSettings* settings)
     : web::EventTarget(settings),
+      algorithm_offload_enabled_(IsAlgorithmOffloadEnabled(settings)),
+      asynchronous_reduction_enabled_(IsAsynchronousReductionEnabled(settings)),
+      min_size_for_immediate_job_(GetMinSizeForImmediateJob(settings)),
+      default_algorithm_runner_(asynchronous_reduction_enabled_),
       chunk_demuxer_(NULL),
       ready_state_(kMediaSourceReadyStateClosed),
       ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
       source_buffers_(new SourceBufferList(settings, &event_queue_)),
       active_source_buffers_(new SourceBufferList(settings, &event_queue_)),
-      live_seekable_range_(new TimeRanges) {}
+      live_seekable_range_(new TimeRanges) {
+  LOG(INFO) << "Algorithm offloading is "
+            << (algorithm_offload_enabled_ ? "enabled" : "disabled");
+  LOG(INFO) << "Asynchronous reduction is "
+            << (asynchronous_reduction_enabled_ ? "enabled" : "disabled");
+  LOG(INFO) << "Min size of immediate job is set to "
+            << min_size_for_immediate_job_;
+}
 
 MediaSource::~MediaSource() { SetReadyState(kMediaSourceReadyStateClosed); }
 
@@ -179,8 +242,9 @@
   ChunkDemuxer::Status status = chunk_demuxer_->AddId(guid, type);
   switch (status) {
     case ChunkDemuxer::kOk:
-      source_buffer =
-          new SourceBuffer(settings, guid, this, chunk_demuxer_, &event_queue_);
+      source_buffer = new SourceBuffer(settings, guid, this,
+                                       asynchronous_reduction_enabled_,
+                                       chunk_demuxer_, &event_queue_);
       break;
     case ChunkDemuxer::kNotSupported:
       web::DOMException::Raise(web::DOMException::kNotSupportedErr,
@@ -310,8 +374,28 @@
   }
 
   DCHECK(IsClosed());
+  DCHECK(!algorithm_process_thread_);
+
   attached_element_ = base::AsWeakPtr(media_element);
   has_max_video_capabilities_ = media_element->HasMaxVideoCapabilities();
+
+  if (algorithm_offload_enabled_) {
+    algorithm_process_thread_.reset(new base::Thread("MSEAlgorithm"));
+    if (!algorithm_process_thread_->Start()) {
+      LOG(WARNING) << "Starting algorithm process thread failed, disable"
+                      " algorithm offloading";
+      algorithm_process_thread_.reset();
+    }
+  }
+
+  if (algorithm_process_thread_) {
+    LOG(INFO) << "Algorithm offloading enabled.";
+    offload_algorithm_runner_.reset(new OffloadAlgorithmRunner(
+        algorithm_process_thread_->message_loop()->task_runner(),
+        base::MessageLoop::current()->task_runner()));
+  } else {
+    LOG(INFO) << "Algorithm offloading disabled.";
+  }
   return true;
 }
 
@@ -490,6 +574,18 @@
   return has_max_video_capabilities_;
 }
 
+SerializedAlgorithmRunner* MediaSource::GetAlgorithmRunner(int job_size) {
+  if (!offload_algorithm_runner_) {
+    return &default_algorithm_runner_;
+  }
+  if (asynchronous_reduction_enabled_ &&
+      job_size <= min_size_for_immediate_job_) {
+    // Append without posting new tasks is only supported on the default runner.
+    return &default_algorithm_runner_;
+  }
+  return offload_algorithm_runner_.get();
+}
+
 void MediaSource::TraceMembers(script::Tracer* tracer) {
   web::EventTarget::TraceMembers(tracer);
 
@@ -536,6 +632,12 @@
   attached_element_.reset();
 
   ScheduleEvent(base::Tokens::sourceclose());
+
+  if (algorithm_process_thread_) {
+    algorithm_process_thread_->Stop();
+    algorithm_process_thread_.reset();
+  }
+  offload_algorithm_runner_.reset();
 }
 
 bool MediaSource::IsUpdating() const {
diff --git a/cobalt/dom/media_source.h b/cobalt/dom/media_source.h
index de7bccb..bfe0774 100644
--- a/cobalt/dom/media_source.h
+++ b/cobalt/dom/media_source.h
@@ -45,16 +45,19 @@
 #ifndef COBALT_DOM_MEDIA_SOURCE_H_
 #define COBALT_DOM_MEDIA_SOURCE_H_
 
+#include <memory>
 #include <string>
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/threading/thread.h"
 #include "cobalt/base/token.h"
 #include "cobalt/dom/audio_track.h"
 #include "cobalt/dom/event_queue.h"
 #include "cobalt/dom/html_media_element.h"
 #include "cobalt/dom/media_source_end_of_stream_error.h"
 #include "cobalt/dom/media_source_ready_state.h"
+#include "cobalt/dom/serialized_algorithm_runner.h"
 #include "cobalt/dom/source_buffer.h"
 #include "cobalt/dom/source_buffer_list.h"
 #include "cobalt/dom/time_ranges.h"
@@ -123,6 +126,7 @@
   void SetSourceBufferActive(SourceBuffer* source_buffer, bool is_active);
   HTMLMediaElement* GetMediaElement() const;
   bool MediaElementHasMaxVideoCapabilities() const;
+  SerializedAlgorithmRunner* GetAlgorithmRunner(int job_size);
 
   DEFINE_WRAPPABLE_TYPE(MediaSource);
   void TraceMembers(script::Tracer* tracer) override;
@@ -132,6 +136,23 @@
   bool IsUpdating() const;
   void ScheduleEvent(base::Token event_name);
 
+  // Set to true to offload SourceBuffer buffer append and removal algorithms to
+  // a non-web thread.
+  const bool algorithm_offload_enabled_;
+  // Set to true to reduce asynchronous behaviors.  For example, queued events
+  // will be dispatached immediately when possible.
+  const bool asynchronous_reduction_enabled_;
+  // Only used when |asynchronous_reduction_enabled_| is set true, where any
+  // buffer append job smaller than its value will happen immediately instead of
+  // being scheduled asynchronously.
+  const int min_size_for_immediate_job_;
+
+  // The default algorithm runner runs all steps on the web thread.
+  DefaultAlgorithmRunner default_algorithm_runner_;
+  // The offload algorithm runner offloads some steps to a non-web thread.
+  std::unique_ptr<OffloadAlgorithmRunner> offload_algorithm_runner_;
+  std::unique_ptr<base::Thread> algorithm_process_thread_;
+
   ChunkDemuxer* chunk_demuxer_;
   MediaSourceReadyState ready_state_;
   EventQueue event_queue_;
diff --git a/cobalt/dom/media_source_settings.cc b/cobalt/dom/media_source_settings.cc
new file mode 100644
index 0000000..a330f83
--- /dev/null
+++ b/cobalt/dom/media_source_settings.cc
@@ -0,0 +1,64 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/media_source_settings.h"
+
+#include <cstring>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace dom {
+
+bool MediaSourceSettingsImpl::Set(const std::string& name, int value) {
+  const char kPrefix[] = "MediaSource.";
+  if (name.compare(0, strlen(kPrefix), kPrefix) != 0) {
+    return false;
+  }
+
+  base::AutoLock auto_lock(lock_);
+  if (name == "MediaSource.SourceBufferEvictExtraInBytes") {
+    if (value >= 0) {
+      source_buffer_evict_extra_in_bytes_ = value;
+      LOG(INFO) << name << ": set to " << value;
+      return true;
+    }
+  } else if (name == "MediaSource.MinimumProcessorCountToOffloadAlgorithm") {
+    if (value >= 0) {
+      minimum_processor_count_to_offload_algorithm_ = value;
+      LOG(INFO) << name << ": set to " << value;
+      return true;
+    }
+  } else if (name == "MediaSource.EnableAsynchronousReduction") {
+    if (value == 0 || value == 1) {
+      is_asynchronous_reduction_enabled_ = value != 0;
+      LOG(INFO) << name << ": set to " << value;
+      return true;
+    }
+  } else if (name == "MediaSource.MinSizeForImmediateJob") {
+    if (value >= 0) {
+      min_size_for_immediate_job_ = value;
+      LOG(INFO) << name << ": set to " << value;
+      return true;
+    }
+  } else {
+    LOG(WARNING) << "Ignore unknown setting with name \"" << name << "\"";
+    return false;
+  }
+  LOG(WARNING) << name << ": ignore invalid value " << value;
+  return false;
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/cobalt/dom/media_source_settings.h b/cobalt/dom/media_source_settings.h
new file mode 100644
index 0000000..8d1b0be
--- /dev/null
+++ b/cobalt/dom/media_source_settings.h
@@ -0,0 +1,87 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_MEDIA_SOURCE_SETTINGS_H_
+#define COBALT_DOM_MEDIA_SOURCE_SETTINGS_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "base/synchronization/lock.h"
+
+namespace cobalt {
+namespace dom {
+
+// Holds WebModule wide settings for MediaSource related objects.  Their default
+// values are set in MediaSource related implementations, and the default values
+// will be overridden if the return values of the member functions are
+// non-empty.
+// Please refer to where these functions are called for the particular
+// MediaSource behaviors being controlled by them.
+class MediaSourceSettings {
+ public:
+  virtual base::Optional<int> GetSourceBufferEvictExtraInBytes() const = 0;
+  virtual base::Optional<int> GetMinimumProcessorCountToOffloadAlgorithm()
+      const = 0;
+  virtual base::Optional<bool> IsAsynchronousReductionEnabled() const = 0;
+  virtual base::Optional<int> GetMinSizeForImmediateJob() const = 0;
+
+ protected:
+  MediaSourceSettings() = default;
+  ~MediaSourceSettings() = default;
+
+  MediaSourceSettings(const MediaSourceSettings&) = delete;
+  MediaSourceSettings& operator=(const MediaSourceSettings&) = delete;
+};
+
+// Allows setting the values of MediaSource settings via a name and an int
+// value.
+// This class is thread safe.
+class MediaSourceSettingsImpl : public MediaSourceSettings {
+ public:
+  base::Optional<int> GetSourceBufferEvictExtraInBytes() const override {
+    base::AutoLock auto_lock(lock_);
+    return source_buffer_evict_extra_in_bytes_;
+  }
+  base::Optional<int> GetMinimumProcessorCountToOffloadAlgorithm()
+      const override {
+    base::AutoLock auto_lock(lock_);
+    return minimum_processor_count_to_offload_algorithm_;
+  }
+  base::Optional<bool> IsAsynchronousReductionEnabled() const override {
+    base::AutoLock auto_lock(lock_);
+    return is_asynchronous_reduction_enabled_;
+  }
+  base::Optional<int> GetMinSizeForImmediateJob() const override {
+    base::AutoLock auto_lock(lock_);
+    return min_size_for_immediate_job_;
+  }
+
+  // Returns true when the setting associated with `name` is set to `value`.
+  // Returns false when `name` is not associated with any settings, or if
+  // `value` contains an invalid value.
+  bool Set(const std::string& name, int value);
+
+ private:
+  mutable base::Lock lock_;
+  base::Optional<int> source_buffer_evict_extra_in_bytes_;
+  base::Optional<int> minimum_processor_count_to_offload_algorithm_;
+  base::Optional<bool> is_asynchronous_reduction_enabled_;
+  base::Optional<int> min_size_for_immediate_job_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_MEDIA_SOURCE_SETTINGS_H_
diff --git a/cobalt/dom/media_source_settings_test.cc b/cobalt/dom/media_source_settings_test.cc
new file mode 100644
index 0000000..27d9e9e
--- /dev/null
+++ b/cobalt/dom/media_source_settings_test.cc
@@ -0,0 +1,107 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/media_source_settings.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+namespace {
+
+TEST(MediaSourceSettingsImplTest, Empty) {
+  MediaSourceSettingsImpl impl;
+
+  EXPECT_FALSE(impl.GetSourceBufferEvictExtraInBytes());
+  EXPECT_FALSE(impl.GetMinimumProcessorCountToOffloadAlgorithm());
+  EXPECT_FALSE(impl.IsAsynchronousReductionEnabled());
+  EXPECT_FALSE(impl.GetMinSizeForImmediateJob());
+}
+
+TEST(MediaSourceSettingsImplTest, SunnyDay) {
+  MediaSourceSettingsImpl impl;
+
+  ASSERT_TRUE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", 100));
+  ASSERT_TRUE(
+      impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 101));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 1));
+  ASSERT_TRUE(impl.Set("MediaSource.MinSizeForImmediateJob", 103));
+
+  EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 100);
+  EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 101);
+  EXPECT_TRUE(impl.IsAsynchronousReductionEnabled().value());
+  EXPECT_EQ(impl.GetMinSizeForImmediateJob().value(), 103);
+}
+
+TEST(MediaSourceSettingsImplTest, RainyDay) {
+  MediaSourceSettingsImpl impl;
+
+  ASSERT_FALSE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", -100));
+  ASSERT_FALSE(
+      impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", -101));
+  ASSERT_FALSE(impl.Set("MediaSource.EnableAsynchronousReduction", 2));
+  ASSERT_FALSE(impl.Set("MediaSource.MinSizeForImmediateJob", -103));
+
+  EXPECT_FALSE(impl.GetSourceBufferEvictExtraInBytes());
+  EXPECT_FALSE(impl.GetMinimumProcessorCountToOffloadAlgorithm());
+  EXPECT_FALSE(impl.IsAsynchronousReductionEnabled());
+  EXPECT_FALSE(impl.GetMinSizeForImmediateJob());
+}
+
+TEST(MediaSourceSettingsImplTest, ZeroValuesWork) {
+  MediaSourceSettingsImpl impl;
+
+  ASSERT_TRUE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", 0));
+  ASSERT_TRUE(
+      impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 0));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 0));
+  ASSERT_TRUE(impl.Set("MediaSource.MinSizeForImmediateJob", 0));
+
+  EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 0);
+  EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 0);
+  EXPECT_FALSE(impl.IsAsynchronousReductionEnabled().value());
+  EXPECT_EQ(impl.GetMinSizeForImmediateJob().value(), 0);
+}
+
+TEST(MediaSourceSettingsImplTest, Updatable) {
+  MediaSourceSettingsImpl impl;
+
+  ASSERT_TRUE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", 0));
+  ASSERT_TRUE(
+      impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 0));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 0));
+  ASSERT_TRUE(impl.Set("MediaSource.MinSizeForImmediateJob", 0));
+
+  ASSERT_TRUE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", 1));
+  ASSERT_TRUE(
+      impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 1));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 1));
+  ASSERT_TRUE(impl.Set("MediaSource.MinSizeForImmediateJob", 1));
+
+  EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 1);
+  EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 1);
+  EXPECT_TRUE(impl.IsAsynchronousReductionEnabled().value());
+  EXPECT_EQ(impl.GetMinSizeForImmediateJob().value(), 1);
+}
+
+TEST(MediaSourceSettingsImplTest, InvalidSettingNames) {
+  MediaSourceSettingsImpl impl;
+
+  ASSERT_FALSE(impl.Set("MediaSource.Invalid", 0));
+  ASSERT_FALSE(impl.Set("Invalid.SourceBufferEvictExtraInBytes", 0));
+}
+
+}  // namespace
+}  // namespace dom
+}  // namespace cobalt
diff --git a/cobalt/dom/rule_matching_test.cc b/cobalt/dom/rule_matching_test.cc
index e9573e0..e11362e 100644
--- a/cobalt/dom/rule_matching_test.cc
+++ b/cobalt/dom/rule_matching_test.cc
@@ -32,6 +32,7 @@
 #include "cobalt/dom/node.h"
 #include "cobalt/dom/node_descendants_iterator.h"
 #include "cobalt/dom/node_list.h"
+#include "cobalt/dom/testing/fake_document.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/dom_parser/parser.h"
@@ -58,7 +59,7 @@
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, dom_stat_tracker_.get(), "",
                               base::kApplicationStateStarted, NULL, NULL),
-        document_(new Document(&html_element_context_)),
+        document_(new testing::FakeDocument(&html_element_context_)),
         root_(document_->CreateElement("html")->AsHTMLElement()),
         head_(document_->CreateElement("head")->AsHTMLElement()),
         body_(document_->CreateElement("body")->AsHTMLElement()) {
diff --git a/cobalt/dom/serialized_algorithm_runner.cc b/cobalt/dom/serialized_algorithm_runner.cc
new file mode 100644
index 0000000..07dece0
--- /dev/null
+++ b/cobalt/dom/serialized_algorithm_runner.cc
@@ -0,0 +1,95 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/serialized_algorithm_runner.h"
+
+#include "base/message_loop/message_loop.h"
+
+namespace cobalt {
+namespace dom {
+
+void DefaultAlgorithmRunner::Start(const scoped_refptr<HandleBase>& handle) {
+  DCHECK(handle);
+  TRACE_EVENT0("cobalt::dom", "DefaultAlgorithmRunner::Start()");
+
+  if (asynchronous_reduction_enabled_) {
+    Process(handle);
+    return;
+  }
+
+  auto task_runner = base::MessageLoop::current()->task_runner();
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(&DefaultAlgorithmRunner::Process,
+                                       base::Unretained(this), handle));
+}
+
+void DefaultAlgorithmRunner::Process(const scoped_refptr<HandleBase>& handle) {
+  DCHECK(handle);
+  TRACE_EVENT0("cobalt::dom", "DefaultAlgorithmRunner::Process()");
+
+  auto task_runner = base::MessageLoop::current()->task_runner();
+
+  bool finished = false;
+  handle->Process(&finished);
+
+  if (finished) {
+    handle->Finalize();
+    return;
+  }
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(&DefaultAlgorithmRunner::Process,
+                                       base::Unretained(this), handle));
+}
+
+OffloadAlgorithmRunner::OffloadAlgorithmRunner(
+    const scoped_refptr<TaskRunner>& process_task_runner,
+    const scoped_refptr<TaskRunner>& finalize_task_runner)
+    : process_task_runner_(process_task_runner),
+      finalize_task_runner_(finalize_task_runner) {
+  DCHECK(process_task_runner_);
+  DCHECK(finalize_task_runner_);
+  DCHECK_NE(process_task_runner_, finalize_task_runner_);
+}
+
+void OffloadAlgorithmRunner::Start(const scoped_refptr<HandleBase>& handle) {
+  DCHECK(handle);
+
+  TRACE_EVENT0("cobalt::dom", "OffloadAlgorithmRunner::Start()");
+
+  process_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&OffloadAlgorithmRunner::Process,
+                                base::Unretained(this), handle));
+}
+
+void OffloadAlgorithmRunner::Process(const scoped_refptr<HandleBase>& handle) {
+  DCHECK(handle);
+  DCHECK(process_task_runner_->BelongsToCurrentThread());
+
+  TRACE_EVENT0("cobalt::dom", "OffloadAlgorithmRunner::Process()");
+
+  bool finished = false;
+  handle->Process(&finished);
+
+  if (finished) {
+    finalize_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&HandleBase::Finalize, handle));
+    return;
+  }
+  process_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&OffloadAlgorithmRunner::Process,
+                                base::Unretained(this), handle));
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/cobalt/dom/serialized_algorithm_runner.h b/cobalt/dom/serialized_algorithm_runner.h
new file mode 100644
index 0000000..d4c2b39
--- /dev/null
+++ b/cobalt/dom/serialized_algorithm_runner.h
@@ -0,0 +1,206 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_SERIALIZED_ALGORITHM_RUNNER_H_
+#define COBALT_DOM_SERIALIZED_ALGORITHM_RUNNER_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/trace_event/trace_event.h"
+#include "starboard/common/mutex.h"
+
+namespace cobalt {
+namespace dom {
+
+// This class defines a common interface to run algorithms, and an algorithm is
+// any class with the following methods:
+//   void Process(bool* finished);
+//   void Abort();
+//   void Finalize();
+//
+// For example, with a class:
+// class Download {
+//   void Process(bool* finished) {
+//     // Download some data
+//     // *finished = whether download is finished.
+//   }
+//   void Abort() {
+//     // Cancel the download, and neither Process() nor Finalize() will be
+//     // called again.
+//   }
+//   void Finalize() {
+//     // Queue an event to notify that the download has finished.
+//   }
+// };
+//
+// This class will keep calling Process() until |finished| becomes false, and
+// then call Finalize().  It guarantees that all calls won't be overlapped and
+// the member functions of the algorithm don't have to synchronize between them.
+class SerializedAlgorithmRunner {
+ public:
+  // Abstract the non-template part of the Handle class, and shouldn't be used
+  // directly.
+  class HandleBase : public base::RefCountedThreadSafe<HandleBase> {
+   public:
+    virtual ~HandleBase() {}
+
+    virtual void Process(bool* finished) = 0;
+    virtual void Finalize() = 0;
+  };
+
+  // A handle object for a running algorithm instance, to allow for aborting and
+  // access the algorithm.
+  template <typename SerializedAlgorithm>
+  class Handle : public HandleBase {
+   public:
+    explicit Handle(std::unique_ptr<SerializedAlgorithm> algorithm);
+
+    // Abort the algorithm and no more processing will happen on return.  It is
+    // possible that Process() has already finished asynchronously, in which
+    // case this function will call Finalize() instead (if it hasn't been called
+    // yet).
+    void Abort();
+    SerializedAlgorithm* algorithm() const { return algorithm_.get(); }
+
+   private:
+    void Process(bool* finished) override;
+    void Finalize() override;
+
+    // The |mutex_| is necessary as `Abort()` can be called from any thread.
+    starboard::Mutex mutex_;
+    std::unique_ptr<SerializedAlgorithm> algorithm_;
+    bool aborted_ = false;
+    bool finished_ = false;
+    bool finalized_ = false;
+  };
+
+  virtual ~SerializedAlgorithmRunner() {}
+
+  virtual void Start(const scoped_refptr<HandleBase>& handle) = 0;
+};
+
+template <typename SerializedAlgorithm>
+SerializedAlgorithmRunner::Handle<SerializedAlgorithm>::Handle(
+    std::unique_ptr<SerializedAlgorithm> algorithm)
+    : algorithm_(std::move(algorithm)) {
+  DCHECK(algorithm_);
+}
+
+template <typename SerializedAlgorithm>
+void SerializedAlgorithmRunner::Handle<SerializedAlgorithm>::Abort() {
+  TRACE_EVENT0("cobalt::dom", "SerializedAlgorithmRunner::Handle::Abort()");
+
+  starboard::ScopedLock scoped_lock(mutex_);
+
+  DCHECK(!aborted_);  // Abort() cannot be called twice.
+
+  if (finished_) {
+    // If the algorithm has finished, just call Finalize() to treat it as
+    // finished instead of aborted.
+    if (!finalized_) {
+      algorithm_->Finalize();
+    }
+  } else {
+    algorithm_->Abort();
+  }
+
+  algorithm_.reset();
+  aborted_ = true;
+}
+
+template <typename SerializedAlgorithm>
+void SerializedAlgorithmRunner::Handle<SerializedAlgorithm>::Process(
+    bool* finished) {
+  TRACE_EVENT0("cobalt::dom", "SerializedAlgorithmRunner::Handle::Process()");
+
+  DCHECK(finished);
+
+  starboard::ScopedLock scoped_lock(mutex_);
+
+  DCHECK(!finished_);
+  DCHECK(!finalized_);
+
+  if (aborted_) {
+    return;
+  }
+
+  DCHECK(algorithm_);
+  algorithm_->Process(&finished_);
+  *finished = finished_;
+}
+
+template <typename SerializedAlgorithm>
+void SerializedAlgorithmRunner::Handle<SerializedAlgorithm>::Finalize() {
+  TRACE_EVENT0("cobalt::dom", "SerializedAlgorithmRunner::Handle::Finalize()");
+  starboard::ScopedLock scoped_lock(mutex_);
+
+  DCHECK(finished_);
+  DCHECK(!finalized_);
+
+  if (aborted_) {
+    return;
+  }
+
+  DCHECK(algorithm_);
+
+  algorithm_->Finalize();
+  algorithm_.reset();
+  finalized_ = true;
+}
+
+// This class runs algorithm on the task runner associated with the thread where
+// Start() is called.
+class DefaultAlgorithmRunner : public SerializedAlgorithmRunner {
+ public:
+  explicit DefaultAlgorithmRunner(bool asynchronous_reduction_enabled)
+      : asynchronous_reduction_enabled_(asynchronous_reduction_enabled) {}
+
+ private:
+  void Start(const scoped_refptr<HandleBase>& handle) override;
+  void Process(const scoped_refptr<HandleBase>& handle);
+
+  const bool asynchronous_reduction_enabled_;
+};
+
+// This class runs algorithms on two task runners, it can be used to offload
+// processing to another task runner.
+//
+// This class will keep calling the Process() member function of the algorithm
+// on the process task runner, and then call Finalize() on the finalize task
+// runner when |finished| becomes true.
+class OffloadAlgorithmRunner : public SerializedAlgorithmRunner {
+ public:
+  typedef base::SingleThreadTaskRunner TaskRunner;
+
+  OffloadAlgorithmRunner(const scoped_refptr<TaskRunner>& process_task_runner,
+                         const scoped_refptr<TaskRunner>& finalize_task_runner);
+
+ private:
+  void Start(const scoped_refptr<HandleBase>& handle) override;
+  void Process(const scoped_refptr<HandleBase>& handle);
+
+  scoped_refptr<TaskRunner> process_task_runner_;
+  scoped_refptr<TaskRunner> finalize_task_runner_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_SERIALIZED_ALGORITHM_RUNNER_H_
diff --git a/cobalt/dom/source_buffer.cc b/cobalt/dom/source_buffer.cc
index a8b78e9..f49d151 100644
--- a/cobalt/dom/source_buffer.cc
+++ b/cobalt/dom/source_buffer.cc
@@ -45,9 +45,12 @@
 #include "cobalt/dom/source_buffer.h"
 
 #include <algorithm>
+#include <limits>
+#include <utility>
 
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
@@ -84,26 +87,34 @@
   return base::TimeDelta::FromSecondsD(time);
 }
 
+// The return value will be used in `SourceBuffer::EvictCodedFrames()` to allow
+// it to evict extra data from the SourceBuffer, so it can reduce the overall
+// memory used by the underlying Demuxer implementation.
+// The default value is 0, i.e. do not evict extra bytes.
 size_t GetEvictExtraInBytes(script::EnvironmentSettings* settings) {
   DOMSettings* dom_settings =
       base::polymorphic_downcast<DOMSettings*>(settings);
-  if (dom_settings && dom_settings->decoder_buffer_memory_info()) {
-    return dom_settings->decoder_buffer_memory_info()
-        ->GetSourceBufferEvictExtraInBytes();
-  }
-  return 0;
+  DCHECK(dom_settings);
+  DCHECK(dom_settings->media_source_settings());
+  int bytes = dom_settings->media_source_settings()
+                  ->GetSourceBufferEvictExtraInBytes()
+                  .value_or(0);
+  DCHECK_GE(bytes, 0);
+  return std::max<int>(bytes, 0);
 }
 
 }  // namespace
 
 SourceBuffer::SourceBuffer(script::EnvironmentSettings* settings,
                            const std::string& id, MediaSource* media_source,
+                           bool asynchronous_reduction_enabled,
                            ChunkDemuxer* chunk_demuxer, EventQueue* event_queue)
     : web::EventTarget(settings),
       id_(id),
+      asynchronous_reduction_enabled_(asynchronous_reduction_enabled),
       evict_extra_in_bytes_(GetEvictExtraInBytes(settings)),
-      chunk_demuxer_(chunk_demuxer),
       media_source_(media_source),
+      chunk_demuxer_(chunk_demuxer),
       event_queue_(event_queue),
       audio_tracks_(
           new AudioTrackList(settings, media_source->GetMediaElement())),
@@ -115,9 +126,11 @@
   DCHECK(chunk_demuxer);
   DCHECK(event_queue);
 
+  LOG(INFO) << "Evict extra in bytes is set to " << evict_extra_in_bytes_;
+
   chunk_demuxer_->SetTracksWatcher(
       id_,
-      base::Bind(&SourceBuffer::InitSegmentReceived, base::Unretained(this)));
+      base::Bind(&SourceBuffer::OnInitSegmentReceived, base::Unretained(this)));
   chunk_demuxer_->SetParseWarningCallback(
       id, base::BindRepeating([](::media::SourceBufferParseWarning warning) {
         LOG(WARNING) << "Encountered SourceBufferParseWarning "
@@ -132,7 +145,7 @@
                              exception_state);
     return;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return;
@@ -168,6 +181,12 @@
   return time_ranges;
 }
 
+double SourceBuffer::timestamp_offset(
+    script::ExceptionState* exception_state) const {
+  starboard::ScopedLock scoped_lock(timestamp_offset_mutex_);
+  return timestamp_offset_;
+}
+
 void SourceBuffer::set_timestamp_offset(
     double offset, script::ExceptionState* exception_state) {
   if (media_source_ == NULL) {
@@ -175,7 +194,7 @@
                              exception_state);
     return;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return;
@@ -189,6 +208,9 @@
     return;
   }
 
+  // We don't have to acquire |timestamp_offset_mutex_|, as no algorithms are
+  // running asynchronously at this moment, which is guaranteed by the check of
+  // updating() above.
   timestamp_offset_ = offset;
 
   chunk_demuxer_->SetGroupStartTimestampIfInSequenceMode(
@@ -202,7 +224,7 @@
                              exception_state);
     return;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return;
@@ -223,7 +245,7 @@
                              exception_state);
     return;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return;
@@ -274,20 +296,21 @@
     return;
   }
 
-  if (pending_remove_start_ != -1) {
-    DCHECK(updating_);
-    web::DOMException::Raise(web::DOMException::kInvalidStateErr,
-                             exception_state);
-    return;
+  if (active_algorithm_handle_) {
+    if (!active_algorithm_handle_->algorithm()->SupportExplicitAbort()) {
+      web::DOMException::Raise(web::DOMException::kInvalidStateErr,
+                               exception_state);
+      return;
+    }
+    active_algorithm_handle_->Abort();
+    active_algorithm_handle_ = nullptr;
   }
 
-  AbortIfUpdating();
-
   base::TimeDelta timestamp_offset = DoubleToTimeDelta(timestamp_offset_);
   chunk_demuxer_->ResetParserState(id_, DoubleToTimeDelta(append_window_start_),
                                    DoubleToTimeDelta(append_window_end_),
                                    &timestamp_offset);
-  timestamp_offset_ = timestamp_offset.InSecondsF();
+  UpdateTimestampOffset(timestamp_offset);
 
   set_append_window_start(0, exception_state);
   set_append_window_end(std::numeric_limits<double>::infinity(),
@@ -303,7 +326,7 @@
                              exception_state);
     return;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return;
@@ -322,14 +345,25 @@
 
   media_source_->OpenIfInEndedState();
 
-  updating_ = true;
-
   ScheduleEvent(base::Tokens::updatestart());
 
-  pending_remove_start_ = start;
-  pending_remove_end_ = end;
-  remove_timer_.Start(FROM_HERE, base::TimeDelta(), this,
-                      &SourceBuffer::OnRemoveTimer);
+  DCHECK_GE(start, 0);
+  DCHECK_LT(start, end);
+
+  std::unique_ptr<SourceBufferAlgorithm> algorithm(
+      new SourceBufferRemoveAlgorithm(
+          chunk_demuxer_, id_, DoubleToTimeDelta(start), DoubleToTimeDelta(end),
+          base::Bind(asynchronous_reduction_enabled_
+                         ? &SourceBuffer::ScheduleAndMaybeDispatchImmediately
+                         : &SourceBuffer::ScheduleEvent,
+                     base::Unretained(this)),
+          base::Bind(&SourceBuffer::OnAlgorithmFinalized,
+                     base::Unretained(this))));
+  active_algorithm_handle_ =
+      new SerializedAlgorithmRunner::Handle<SourceBufferAlgorithm>(
+          std::move(algorithm));
+  media_source_->GetAlgorithmRunner(std::numeric_limits<int>::max())
+      ->Start(active_algorithm_handle_);
 }
 
 void SourceBuffer::set_track_defaults(
@@ -340,7 +374,7 @@
                              exception_state);
     return;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return;
@@ -354,10 +388,9 @@
     return;
   }
 
-  if (pending_remove_start_ != -1) {
-    CancelRemove();
-  } else {
-    AbortIfUpdating();
+  if (active_algorithm_handle_) {
+    active_algorithm_handle_->Abort();
+    active_algorithm_handle_ = nullptr;
   }
 
   DCHECK(media_source_);
@@ -402,8 +435,15 @@
   tracer->Trace(video_tracks_);
 }
 
-void SourceBuffer::InitSegmentReceived(std::unique_ptr<MediaTracks> tracks) {
+void SourceBuffer::OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks) {
   if (!first_initialization_segment_received_) {
+    // This can be called from non-web thread when the append is async.
+    if (!web_task_runner_->BelongsToCurrentThread()) {
+      web_task_runner_->PostTask(
+          FROM_HERE, base::Bind(&SourceBuffer::OnInitSegmentReceived, this,
+                                base::Passed(&tracks)));
+      return;
+    }
     media_source_->SetSourceBufferActive(this, true);
     first_initialization_segment_received_ = true;
   }
@@ -417,6 +457,12 @@
   event_queue_->Enqueue(event);
 }
 
+void SourceBuffer::ScheduleAndMaybeDispatchImmediately(base::Token event_name) {
+  scoped_refptr<web::Event> event = new web::Event(event_name);
+  event->set_target(this);
+  event_queue_->EnqueueAndMaybeDispatchImmediately(event);
+}
+
 bool SourceBuffer::PrepareAppend(size_t new_data_size,
                                  script::ExceptionState* exception_state) {
   TRACE_EVENT1("cobalt::dom", "SourceBuffer::PrepareAppend()", "new_data_size",
@@ -426,7 +472,7 @@
                              exception_state);
     return false;
   }
-  if (updating_) {
+  if (updating()) {
     web::DOMException::Raise(web::DOMException::kInvalidStateErr,
                              exception_state);
     return false;
@@ -441,7 +487,10 @@
 
   media_source_->OpenIfInEndedState();
 
-  if (!EvictCodedFrames(new_data_size)) {
+  double current_time = media_source_->GetMediaElement()->current_time(NULL);
+  if (!chunk_demuxer_->EvictCodedFrames(
+          id_, base::TimeDelta::FromSecondsD(current_time),
+          new_data_size + evict_extra_in_bytes_)) {
     web::DOMException::Raise(web::DOMException::kQuotaExceededErr,
                              exception_state);
     return false;
@@ -450,15 +499,6 @@
   return true;
 }
 
-bool SourceBuffer::EvictCodedFrames(size_t new_data_size) {
-  DCHECK(media_source_);
-  DCHECK(media_source_->GetMediaElement());
-  double current_time = media_source_->GetMediaElement()->current_time(NULL);
-  return chunk_demuxer_->EvictCodedFrames(
-      id_, base::TimeDelta::FromSecondsD(current_time),
-      new_data_size + evict_extra_in_bytes_);
-}
-
 void SourceBuffer::AppendBufferInternal(
     const unsigned char* data, size_t size,
     script::ExceptionState* exception_state) {
@@ -471,8 +511,8 @@
   metrics_.EndTracking(0);
 
   DCHECK(data || size == 0);
+
   if (data) {
-    DCHECK_EQ(pending_append_data_offset_, 0u);
     if (pending_append_data_capacity_ < size) {
       pending_append_data_.reset();
       pending_append_data_.reset(new uint8_t[size]);
@@ -480,124 +520,43 @@
     }
     memcpy(pending_append_data_.get(), data, size);
   }
-  pending_append_data_size_ = size;
-  pending_append_data_offset_ = 0;
-
-  updating_ = true;
 
   ScheduleEvent(base::Tokens::updatestart());
 
-  append_timer_.Start(FROM_HERE, base::TimeDelta(), this,
-                      &SourceBuffer::OnAppendTimer);
+  std::unique_ptr<SourceBufferAlgorithm> algorithm(
+      new SourceBufferAppendAlgorithm(
+          media_source_, chunk_demuxer_, id_, pending_append_data_.get(), size,
+          DoubleToTimeDelta(append_window_start_),
+          DoubleToTimeDelta(append_window_end_),
+          DoubleToTimeDelta(timestamp_offset_),
+          base::Bind(&SourceBuffer::UpdateTimestampOffset,
+                     base::Unretained(this)),
+          base::Bind(asynchronous_reduction_enabled_
+                         ? &SourceBuffer::ScheduleAndMaybeDispatchImmediately
+                         : &SourceBuffer::ScheduleEvent,
+                     base::Unretained(this)),
+          base::Bind(&SourceBuffer::OnAlgorithmFinalized,
+                     base::Unretained(this)),
+          &metrics_));
+  active_algorithm_handle_ =
+      new SerializedAlgorithmRunner::Handle<SourceBufferAlgorithm>(
+          std::move(algorithm));
+  media_source_->GetAlgorithmRunner(size)->Start(active_algorithm_handle_);
 }
 
-void SourceBuffer::OnAppendTimer() {
-  TRACE_EVENT0("cobalt::dom", "SourceBuffer::OnAppendTimer()");
-  const size_t kMaxAppendSize = 128 * 1024;
+void SourceBuffer::OnAlgorithmFinalized() {
+  DCHECK(active_algorithm_handle_);
+  active_algorithm_handle_ = nullptr;
+}
 
-  DCHECK(updating_);
-
-  DCHECK_GE(pending_append_data_size_, pending_append_data_offset_);
-  size_t append_size = pending_append_data_size_ - pending_append_data_offset_;
-  append_size = std::min(append_size, kMaxAppendSize);
-
-  uint8_t dummy;
-  const uint8* data_to_append =
-      append_size > 0 ? pending_append_data_.get() + pending_append_data_offset_
-                      : &dummy;
-
-  base::TimeDelta timestamp_offset = DoubleToTimeDelta(timestamp_offset_);
-  metrics_.StartTracking();
-  bool success = chunk_demuxer_->AppendData(
-      id_, data_to_append, append_size, DoubleToTimeDelta(append_window_start_),
-      DoubleToTimeDelta(append_window_end_), &timestamp_offset);
-
-  if (timestamp_offset != DoubleToTimeDelta(timestamp_offset_)) {
+void SourceBuffer::UpdateTimestampOffset(base::TimeDelta timestamp_offset) {
+  starboard::ScopedLock scoped_lock(timestamp_offset_mutex_);
+  // The check avoids overwriting |timestamp_offset_| when there is a small
+  // difference between its float and its int64_t representation .
+  if (DoubleToTimeDelta(timestamp_offset_) != timestamp_offset) {
     timestamp_offset_ = timestamp_offset.InSecondsF();
   }
-
-  if (!success) {
-    metrics_.EndTracking(0);
-    pending_append_data_size_ = 0;
-    pending_append_data_offset_ = 0;
-    AppendError();
-  } else {
-    metrics_.EndTracking(append_size);
-    pending_append_data_offset_ += append_size;
-
-    if (pending_append_data_offset_ < pending_append_data_size_) {
-      append_timer_.Start(FROM_HERE, base::TimeDelta(), this,
-                          &SourceBuffer::OnAppendTimer);
-      return;
-    }
-
-    updating_ = false;
-    pending_append_data_size_ = 0;
-    pending_append_data_offset_ = 0;
-
-    ScheduleEvent(base::Tokens::update());
-    ScheduleEvent(base::Tokens::updateend());
-  }
 }
 
-void SourceBuffer::AppendError() {
-  base::TimeDelta timestamp_offset = DoubleToTimeDelta(timestamp_offset_);
-  chunk_demuxer_->ResetParserState(id_, DoubleToTimeDelta(append_window_start_),
-                                   DoubleToTimeDelta(append_window_end_),
-                                   &timestamp_offset);
-  timestamp_offset_ = timestamp_offset.InSecondsF();
-
-  updating_ = false;
-
-  ScheduleEvent(base::Tokens::error());
-  ScheduleEvent(base::Tokens::updateend());
-  media_source_->EndOfStreamAlgorithm(kMediaSourceEndOfStreamErrorDecode);
-}
-
-void SourceBuffer::OnRemoveTimer() {
-  TRACE_EVENT0("cobalt::dom", "SourceBuffer::OnRemoveTimer()");
-  DCHECK(updating_);
-  DCHECK_GE(pending_remove_start_, 0);
-  DCHECK_LT(pending_remove_start_, pending_remove_end_);
-
-  chunk_demuxer_->Remove(id_, DoubleToTimeDelta(pending_remove_start_),
-                         DoubleToTimeDelta(pending_remove_end_));
-
-  updating_ = false;
-  pending_remove_start_ = -1;
-  pending_remove_end_ = -1;
-
-  ScheduleEvent(base::Tokens::update());
-  ScheduleEvent(base::Tokens::updateend());
-}
-
-void SourceBuffer::CancelRemove() {
-  DCHECK(updating_);
-  DCHECK_NE(pending_remove_start_, -1);
-  remove_timer_.Stop();
-  pending_remove_start_ = -1;
-  pending_remove_end_ = -1;
-  updating_ = false;
-}
-
-void SourceBuffer::AbortIfUpdating() {
-  if (!updating_) {
-    return;
-  }
-
-  DCHECK_EQ(pending_remove_start_, -1);
-
-  append_timer_.Stop();
-  pending_append_data_size_ = 0;
-  pending_append_data_offset_ = 0;
-
-  updating_ = false;
-
-  ScheduleEvent(base::Tokens::abort());
-  ScheduleEvent(base::Tokens::updateend());
-}
-
-void SourceBuffer::RemoveMediaTracks() { NOTREACHED(); }
-
 }  // namespace dom
 }  // namespace cobalt
diff --git a/cobalt/dom/source_buffer.h b/cobalt/dom/source_buffer.h
index 9f68856..65bbe48 100644
--- a/cobalt/dom/source_buffer.h
+++ b/cobalt/dom/source_buffer.h
@@ -53,10 +53,13 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
+#include "base/single_thread_task_runner.h"
 #include "base/timer/timer.h"
 #include "cobalt/base/token.h"
 #include "cobalt/dom/audio_track_list.h"
 #include "cobalt/dom/event_queue.h"
+#include "cobalt/dom/serialized_algorithm_runner.h"
+#include "cobalt/dom/source_buffer_algorithm.h"
 #include "cobalt/dom/source_buffer_append_mode.h"
 #include "cobalt/dom/source_buffer_metrics.h"
 #include "cobalt/dom/time_ranges.h"
@@ -67,6 +70,7 @@
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/web/event_target.h"
+#include "starboard/common/mutex.h"
 #include "third_party/chromium/media/base/media_tracks.h"
 #include "third_party/chromium/media/filters/chunk_demuxer.h"
 
@@ -85,8 +89,8 @@
   // Custom, not in any spec.
   //
   SourceBuffer(script::EnvironmentSettings* settings, const std::string& id,
-               MediaSource* media_source, ChunkDemuxer* chunk_demuxer,
-               EventQueue* event_queue);
+               MediaSource* media_source, bool asynchronous_reduction_enabled,
+               ChunkDemuxer* chunk_demuxer, EventQueue* event_queue);
 
   // Web API: SourceBuffer
   //
@@ -95,12 +99,10 @@
   }
   void set_mode(SourceBufferAppendMode mode,
                 script::ExceptionState* exception_state);
-  bool updating() const { return updating_; }
+  bool updating() const { return active_algorithm_handle_ != nullptr; }
   scoped_refptr<TimeRanges> buffered(
       script::ExceptionState* exception_state) const;
-  double timestamp_offset(script::ExceptionState* exception_state) const {
-    return timestamp_offset_;
-  }
+  double timestamp_offset(script::ExceptionState* exception_state) const;
   void set_timestamp_offset(double offset,
                             script::ExceptionState* exception_state);
   scoped_refptr<AudioTrackList> audio_tracks() const { return audio_tracks_; }
@@ -140,22 +142,16 @@
  private:
   typedef ::media::MediaTracks MediaTracks;
 
-  void InitSegmentReceived(std::unique_ptr<MediaTracks> tracks);
+  void OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks);
   void ScheduleEvent(base::Token event_name);
+  void ScheduleAndMaybeDispatchImmediately(base::Token event_name);
   bool PrepareAppend(size_t new_data_size,
                      script::ExceptionState* exception_state);
-  bool EvictCodedFrames(size_t new_data_size);
   void AppendBufferInternal(const unsigned char* data, size_t size,
                             script::ExceptionState* exception_state);
-  void OnAppendTimer();
-  void AppendError();
 
-  void OnRemoveTimer();
-
-  void CancelRemove();
-  void AbortIfUpdating();
-
-  void RemoveMediaTracks();
+  void OnAlgorithmFinalized();
+  void UpdateTimestampOffset(base::TimeDelta timestamp_offset_);
 
   const TrackDefault* GetTrackDefault(
       const std::string& track_type,
@@ -167,30 +163,32 @@
       const std::string& byte_stream_track_id) const;
 
   const std::string id_;
+  const bool asynchronous_reduction_enabled_;
   const size_t evict_extra_in_bytes_;
-  ChunkDemuxer* chunk_demuxer_;
+
   MediaSource* media_source_;
+  ChunkDemuxer* chunk_demuxer_;
+  scoped_refptr<base::SingleThreadTaskRunner> web_task_runner_ =
+      base::MessageLoop::current()->task_runner();
   scoped_refptr<TrackDefaultList> track_defaults_ = new TrackDefaultList(NULL);
   EventQueue* event_queue_;
 
-  SourceBufferAppendMode mode_ = kSourceBufferAppendModeSegments;
-  bool updating_ = false;
-  double timestamp_offset_ = 0;
+  bool first_initialization_segment_received_ = false;
   scoped_refptr<AudioTrackList> audio_tracks_;
   scoped_refptr<VideoTrackList> video_tracks_;
+
+  SourceBufferAppendMode mode_ = kSourceBufferAppendModeSegments;
+
+  starboard::Mutex timestamp_offset_mutex_;
+  double timestamp_offset_ = 0;
+
+  scoped_refptr<SerializedAlgorithmRunner::Handle<SourceBufferAlgorithm>>
+      active_algorithm_handle_;
   double append_window_start_ = 0;
   double append_window_end_ = std::numeric_limits<double>::infinity();
 
-  base::OneShotTimer append_timer_;
-  bool first_initialization_segment_received_ = false;
   std::unique_ptr<uint8_t[]> pending_append_data_;
   size_t pending_append_data_capacity_ = 0;
-  size_t pending_append_data_size_ = 0;
-  size_t pending_append_data_offset_ = 0;
-
-  base::OneShotTimer remove_timer_;
-  double pending_remove_start_ = -1;
-  double pending_remove_end_ = -1;
 
   SourceBufferMetrics metrics_;
 };
diff --git a/cobalt/dom/source_buffer_algorithm.cc b/cobalt/dom/source_buffer_algorithm.cc
new file mode 100644
index 0000000..50eec01
--- /dev/null
+++ b/cobalt/dom/source_buffer_algorithm.cc
@@ -0,0 +1,152 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/source_buffer_algorithm.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "cobalt/dom/media_source.h"
+
+namespace cobalt {
+namespace dom {
+
+SourceBufferAppendAlgorithm::SourceBufferAppendAlgorithm(
+    MediaSource* media_source, ::media::ChunkDemuxer* chunk_demuxer,
+    const std::string& id, const uint8_t* buffer, size_t size_in_bytes,
+    base::TimeDelta append_window_start, base::TimeDelta append_window_end,
+    base::TimeDelta timestamp_offset,
+    UpdateTimestampOffsetCB&& update_timestamp_offset_cb,
+    ScheduleEventCB&& schedule_event_cb, base::OnceClosure&& finalized_cb,
+    SourceBufferMetrics* metrics)
+    : media_source_(media_source),
+      chunk_demuxer_(chunk_demuxer),
+      id_(id),
+      buffer_(buffer),
+      bytes_remaining_(size_in_bytes),
+      append_window_start_(append_window_start),
+      append_window_end_(append_window_end),
+      timestamp_offset_(timestamp_offset),
+      update_timestamp_offset_cb_(std::move(update_timestamp_offset_cb)),
+      schedule_event_cb_(std::move(schedule_event_cb)),
+      finalized_cb_(std::move(finalized_cb)),
+      metrics_(metrics) {
+  DCHECK(media_source_);
+  DCHECK(chunk_demuxer_);
+  if (bytes_remaining_ > 0) {
+    DCHECK(buffer);
+  }
+  DCHECK(update_timestamp_offset_cb_);
+  DCHECK(schedule_event_cb_);
+  DCHECK(metrics_);
+  TRACE_EVENT1("cobalt::dom", "SourceBufferAppendAlgorithm ctor",
+               "bytes_remaining", bytes_remaining_);
+}
+
+void SourceBufferAppendAlgorithm::Process(bool* finished) {
+  DCHECK(finished);
+  DCHECK(!*finished);
+
+  const size_t kMaxAppendSize = 128 * 1024;
+
+  size_t append_size = std::min(bytes_remaining_, kMaxAppendSize);
+
+  TRACE_EVENT1("cobalt::dom", "SourceBufferAppendAlgorithm::Process()",
+               "append_size", append_size);
+
+  metrics_->StartTracking();
+  succeeded_ = chunk_demuxer_->AppendData(
+      id_, buffer_, append_size, append_window_start_, append_window_end_,
+      &timestamp_offset_);
+  update_timestamp_offset_cb_.Run(timestamp_offset_);
+  metrics_->EndTracking(succeeded_ ? append_size : 0);
+
+  if (succeeded_) {
+    buffer_ += append_size;
+    bytes_remaining_ -= append_size;
+
+    if (bytes_remaining_ == 0) {
+      *finished = true;
+      return;
+    }
+
+    return;
+  }
+
+  chunk_demuxer_->ResetParserState(id_, append_window_start_,
+                                   append_window_end_, &timestamp_offset_);
+  update_timestamp_offset_cb_.Run(timestamp_offset_);
+  *finished = true;
+}
+
+void SourceBufferAppendAlgorithm::Abort() {
+  TRACE_EVENT0("cobalt::dom", "SourceBufferAppendAlgorithm::Abort()");
+  schedule_event_cb_.Run(base::Tokens::abort());
+  schedule_event_cb_.Run(base::Tokens::updateend());
+}
+
+void SourceBufferAppendAlgorithm::Finalize() {
+  TRACE_EVENT1("cobalt::dom", "SourceBufferAppendAlgorithm::Finalize()",
+               "succeeded", succeeded_);
+  if (succeeded_) {
+    schedule_event_cb_.Run(base::Tokens::update());
+    schedule_event_cb_.Run(base::Tokens::updateend());
+  } else {
+    schedule_event_cb_.Run(base::Tokens::error());
+    schedule_event_cb_.Run(base::Tokens::updateend());
+    media_source_->EndOfStreamAlgorithm(kMediaSourceEndOfStreamErrorDecode);
+  }
+
+  std::move(finalized_cb_).Run();
+}
+
+SourceBufferRemoveAlgorithm::SourceBufferRemoveAlgorithm(
+    ::media::ChunkDemuxer* chunk_demuxer, const std::string& id,
+    base::TimeDelta pending_remove_start, base::TimeDelta pending_remove_end,
+    ScheduleEventCB&& schedule_event_cb, base::OnceClosure&& finalized_cb)
+    : chunk_demuxer_(chunk_demuxer),
+      id_(id),
+      pending_remove_start_(pending_remove_start),
+      pending_remove_end_(pending_remove_end),
+      schedule_event_cb_(std::move(schedule_event_cb)),
+      finalized_cb_(std::move(finalized_cb)) {
+  TRACE_EVENT0("cobalt::dom", "SourceBufferRemoveAlgorithm ctor");
+  DCHECK(chunk_demuxer_);
+  DCHECK(schedule_event_cb_);
+}
+
+void SourceBufferRemoveAlgorithm::Process(bool* finished) {
+  TRACE_EVENT0("cobalt::dom", "SourceBufferRemoveAlgorithm::Process()");
+  chunk_demuxer_->Remove(id_, pending_remove_start_, pending_remove_end_);
+  *finished = true;
+}
+
+void SourceBufferRemoveAlgorithm::Abort() {
+  TRACE_EVENT0("cobalt::dom", "SourceBufferRemoveAlgorithm::Abort()");
+  // SourceBufferRemoveAlgorithm cannot be cancelled explicitly by the web app.
+  // This function will only be called when the SourceBuffer is being removed
+  // from the MediaSource and no events should be fired.
+}
+
+void SourceBufferRemoveAlgorithm::Finalize() {
+  TRACE_EVENT0("cobalt::dom", "SourceBufferRemoveAlgorithm::Finalize()");
+  schedule_event_cb_.Run(base::Tokens::update());
+  schedule_event_cb_.Run(base::Tokens::updateend());
+  std::move(finalized_cb_).Run();
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/cobalt/dom/source_buffer_algorithm.h b/cobalt/dom/source_buffer_algorithm.h
new file mode 100644
index 0000000..d9e0f16
--- /dev/null
+++ b/cobalt/dom/source_buffer_algorithm.h
@@ -0,0 +1,119 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_SOURCE_BUFFER_ALGORITHM_H_
+#define COBALT_DOM_SOURCE_BUFFER_ALGORITHM_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/time/time.h"
+#include "cobalt/base/tokens.h"
+#include "cobalt/dom/source_buffer_metrics.h"
+#include "third_party/chromium/media/filters/chunk_demuxer.h"
+
+namespace cobalt {
+namespace dom {
+
+class MediaSource;  // Necessary to avoid circular inclusion.
+
+// Base class of all SourceBuffer algorithms.  Its user should call `Process()`
+// multiple times until |finished| is set to true on return, and then call
+// `Finalize()` exactly once.  `Abort()` can be called at any time to abort any
+// processing.
+class SourceBufferAlgorithm {
+ public:
+  typedef base::RepeatingCallback<void(base::TimeDelta)>
+      UpdateTimestampOffsetCB;
+  typedef base::RepeatingCallback<void(base::Token)> ScheduleEventCB;
+
+  virtual ~SourceBufferAlgorithm() {}
+
+  // Returns false to indicate that the algorithm cannot be explicitly aborted
+  // by calling `Abort()` on the Handle.  The algorithm may still be aborted
+  // when the SourceBuffer is being removed from the MediaSource.
+  virtual bool SupportExplicitAbort() = 0;
+
+  virtual void Process(bool* finished) = 0;
+  virtual void Abort() = 0;
+  virtual void Finalize() = 0;
+};
+
+// Implements the steps 12 to 18 of SourceBuffer stream append loop algorithm as
+// specified at
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer-stream-append-loop.
+class SourceBufferAppendAlgorithm : public SourceBufferAlgorithm {
+ public:
+  SourceBufferAppendAlgorithm(
+      MediaSource* media_source, ::media::ChunkDemuxer* chunk_demuxer,
+      const std::string& id, const uint8_t* buffer, size_t size_in_bytes,
+      base::TimeDelta append_window_start, base::TimeDelta append_window_end,
+      base::TimeDelta timestamp_offset,
+      UpdateTimestampOffsetCB&& update_timestamp_offset_cb,
+      ScheduleEventCB&& schedule_event_cb, base::OnceClosure&& finalized_cb,
+      SourceBufferMetrics* metrics);
+
+ private:
+  bool SupportExplicitAbort() override { return true; }
+  void Process(bool* finished) override;
+  void Abort() override;
+  void Finalize() override;
+
+  MediaSource* const media_source_;
+  ::media::ChunkDemuxer* const chunk_demuxer_;
+  const std::string id_;
+  const uint8_t* buffer_;
+  size_t bytes_remaining_;
+  const base::TimeDelta append_window_start_;
+  const base::TimeDelta append_window_end_;
+
+  base::TimeDelta timestamp_offset_;
+  const UpdateTimestampOffsetCB update_timestamp_offset_cb_;
+  const ScheduleEventCB schedule_event_cb_;
+  base::OnceClosure finalized_cb_;
+  SourceBufferMetrics* const metrics_;
+
+  bool succeeded_ = false;
+};
+
+// Implements the steps 6 to 9 of SourceBuffer range removal algorithm as
+// specified at
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer-range-removal.
+class SourceBufferRemoveAlgorithm : public SourceBufferAlgorithm {
+ public:
+  SourceBufferRemoveAlgorithm(::media::ChunkDemuxer* chunk_demuxer,
+                              const std::string& id,
+                              base::TimeDelta pending_remove_start,
+                              base::TimeDelta pending_remove_end,
+                              ScheduleEventCB&& schedule_event_cb,
+                              base::OnceClosure&& finalized_cb);
+
+ private:
+  bool SupportExplicitAbort() override { return false; }
+  void Process(bool* finished) override;
+  void Abort() override;
+  void Finalize() override;
+
+  ::media::ChunkDemuxer* const chunk_demuxer_;
+  const std::string id_;
+  const base::TimeDelta pending_remove_start_;
+  const base::TimeDelta pending_remove_end_;
+  const ScheduleEventCB schedule_event_cb_;
+  base::OnceClosure finalized_cb_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_SOURCE_BUFFER_ALGORITHM_H_
diff --git a/cobalt/dom/testing/BUILD.gn b/cobalt/dom/testing/BUILD.gn
index 49453ae..846388b 100644
--- a/cobalt/dom/testing/BUILD.gn
+++ b/cobalt/dom/testing/BUILD.gn
@@ -17,6 +17,7 @@
   has_pedantic_warnings = true
 
   sources = [
+    "fake_document.h",
     "html_collection_testing.h",
     "mock_layout_boxes.h",
     "stub_css_parser.cc",
diff --git a/cobalt/dom/testing/fake_document.h b/cobalt/dom/testing/fake_document.h
new file mode 100644
index 0000000..3ab3d94
--- /dev/null
+++ b/cobalt/dom/testing/fake_document.h
@@ -0,0 +1,70 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_TESTING_FAKE_DOCUMENT_H_
+#define COBALT_DOM_TESTING_FAKE_DOCUMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "cobalt/dom/document.h"
+
+#include "cobalt/web/csp_delegate_factory.h"
+
+namespace cobalt {
+namespace dom {
+namespace testing {
+
+class FakeDocument : public dom::Document {
+ public:
+  explicit FakeDocument(dom::HTMLElementContext* html_element_context)
+      : dom::Document(html_element_context) {
+    web::WindowOrWorkerGlobalScope::Options options(
+        base::ApplicationState::kApplicationStateStarted);
+    std::unique_ptr<web::CspViolationReporter> violation_reporter(
+        new web::CspViolationReporter(nullptr, options.post_sender));
+    csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
+        options.csp_enforcement_mode, std::move(violation_reporter),
+        environment_settings()->creation_url(), options.require_csp,
+        options.csp_policy_changed_callback,
+        options.csp_insecure_allowed_token);
+  }
+
+  FakeDocument(dom::HTMLElementContext* html_element_context,
+               dom::Document::Options doc_options)
+      : dom::Document(html_element_context, doc_options) {
+    web::WindowOrWorkerGlobalScope::Options options(
+        base::ApplicationState::kApplicationStateStarted);
+    std::unique_ptr<web::CspViolationReporter> violation_reporter(
+        new web::CspViolationReporter(nullptr, options.post_sender));
+    csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
+        options.csp_enforcement_mode, std::move(violation_reporter),
+        environment_settings()->creation_url(), options.require_csp,
+        options.csp_policy_changed_callback,
+        options.csp_insecure_allowed_token);
+  }
+
+  web::CspDelegate* csp_delegate() const override {
+    return csp_delegate_.get();
+  }
+
+ private:
+  std::unique_ptr<web::CspDelegate> csp_delegate_;
+};
+
+}  // namespace testing
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_TESTING_FAKE_DOCUMENT_H_
diff --git a/cobalt/dom/testing/stub_environment_settings.h b/cobalt/dom/testing/stub_environment_settings.h
index 01d01f3..a70eef3 100644
--- a/cobalt/dom/testing/stub_environment_settings.h
+++ b/cobalt/dom/testing/stub_environment_settings.h
@@ -26,7 +26,7 @@
  public:
   explicit StubEnvironmentSettings(const Options& options = Options())
       : DOMSettings(null_debugger_hooks_, 0, nullptr, nullptr, nullptr, nullptr,
-                    options) {}
+                    nullptr, options) {}
   ~StubEnvironmentSettings() override {}
 
  private:
diff --git a/cobalt/dom/testing/test_with_javascript.h b/cobalt/dom/testing/test_with_javascript.h
index 58df232..30204af 100644
--- a/cobalt/dom/testing/test_with_javascript.h
+++ b/cobalt/dom/testing/test_with_javascript.h
@@ -50,15 +50,16 @@
   Window* window() { return stub_window_->window().get(); }
 
   bool EvaluateScript(const std::string& js_code, std::string* result) {
-    DCHECK(global_environment());
-    scoped_refptr<script::SourceCode> source_code =
-        script::SourceCode::CreateSourceCode(
-            js_code, base::SourceLocation(__FILE__, __LINE__, 1));
+    return global_environment()->EvaluateScript(
+        CreateSourceCodeAndPrepareEval(js_code), result);
+  }
 
-    global_environment()->EnableEval();
-    global_environment()->SetReportEvalCallback(base::Closure());
-    bool succeeded = global_environment()->EvaluateScript(source_code, result);
-    return succeeded;
+  bool EvaluateScript(
+      const std::string& js_code,
+      const scoped_refptr<script::Wrappable>& owning_object,
+      base::Optional<script::ValueHandleHolder::Reference>* result = NULL) {
+    return global_environment()->EvaluateScript(
+        CreateSourceCodeAndPrepareEval(js_code), owning_object, result);
   }
 
   ::testing::StrictMock<script::testing::MockExceptionState>*
@@ -73,6 +74,15 @@
   base::EventDispatcher* event_dispatcher() { return &event_dispatcher_; }
 
  private:
+  scoped_refptr<script::SourceCode> CreateSourceCodeAndPrepareEval(
+      const std::string& js_code) {
+    DCHECK(global_environment());
+    global_environment()->EnableEval();
+    global_environment()->SetReportEvalCallback(base::Closure());
+    return script::SourceCode::CreateSourceCode(
+        js_code, base::SourceLocation(__FILE__, __LINE__, 1));
+  }
+
   std::unique_ptr<StubWindow> stub_window_;
   ::testing::StrictMock<script::testing::MockExceptionState> exception_state_;
   base::EventDispatcher event_dispatcher_;
diff --git a/cobalt/dom/user_agent_data_test.cc b/cobalt/dom/user_agent_data_test.cc
index dc3c350..376fcda 100644
--- a/cobalt/dom/user_agent_data_test.cc
+++ b/cobalt/dom/user_agent_data_test.cc
@@ -40,7 +40,6 @@
 
     // Inject H5vcc interface to make it also accessible via Window
     h5vcc::H5vcc::Settings h5vcc_settings;
-    h5vcc_settings.media_module = NULL;
     h5vcc_settings.network_module = NULL;
 #if SB_IS(EVERGREEN)
     h5vcc_settings.updater_module = NULL;
diff --git a/cobalt/dom/window.cc b/cobalt/dom/window.cc
index 89178fe..aca75dc 100644
--- a/cobalt/dom/window.cc
+++ b/cobalt/dom/window.cc
@@ -129,8 +129,12 @@
     bool log_tts)
     // 'window' object EventTargets require special handling for onerror events,
     // see EventTarget constructor for more details.
-    : web::WindowOrWorkerGlobalScope(settings, dom_stat_tracker,
-                                     initial_application_state),
+    : web::WindowOrWorkerGlobalScope(
+          settings, dom_stat_tracker,
+          web::WindowOrWorkerGlobalScope::Options(
+              initial_application_state, post_sender, require_csp,
+              csp_enforcement_mode, csp_policy_changed_callback,
+              csp_insecure_allowed_token)),
       viewport_size_(view_size),
       is_resize_event_pending_(false),
       is_reporting_script_error_(false),
@@ -155,9 +159,7 @@
               base::Bind(&Window::FireHashChangeEvent, base::Unretained(this)),
               performance_->timing()->GetNavigationStartClock(),
               navigation_callback, ParseUserAgentStyleSheet(css_parser),
-              view_size, cookie_jar, post_sender, require_csp,
-              csp_enforcement_mode, csp_policy_changed_callback,
-              csp_insecure_allowed_token, dom_max_element_depth)))),
+              view_size, cookie_jar, dom_max_element_depth)))),
       document_loader_(nullptr),
       history_(new History()),
       navigator_(new Navigator(environment_settings(), captions)),
@@ -172,7 +174,6 @@
       ALLOW_THIS_IN_INITIALIZER_LIST(
           session_storage_(new Storage(this, Storage::kSessionStorage, NULL))),
       screen_(new Screen(view_size)),
-      preflight_cache_(new loader::CORSPreflightCache()),
       ran_animation_frame_callbacks_callback_(
           ran_animation_frame_callbacks_callback),
       window_close_callback_(window_close_callback),
diff --git a/cobalt/dom/window.h b/cobalt/dom/window.h
index 6485969..033ac82 100644
--- a/cobalt/dom/window.h
+++ b/cobalt/dom/window.h
@@ -45,7 +45,6 @@
 #include "cobalt/dom/test_runner.h"
 #endif  // ENABLE_TEST_RUNNER
 #include "cobalt/input/camera_3d.h"
-#include "cobalt/loader/cors_preflight_cache.h"
 #include "cobalt/loader/decoder.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/font/remote_typeface_cache.h"
@@ -361,10 +360,6 @@
   void CacheSplashScreen(const std::string& content,
                          const base::Optional<std::string>& topic);
 
-  const scoped_refptr<loader::CORSPreflightCache> get_preflight_cache() {
-    return preflight_cache_;
-  }
-
   // Custom on screen keyboard.
   const scoped_refptr<OnScreenKeyboard>& on_screen_keyboard() const;
   void ReleaseOnScreenKeyboard();
@@ -443,9 +438,6 @@
 
   scoped_refptr<Screen> screen_;
 
-  // Global preflight cache.
-  scoped_refptr<loader::CORSPreflightCache> preflight_cache_;
-
   const base::Closure ran_animation_frame_callbacks_callback_;
   const CloseCallback window_close_callback_;
   const base::Closure window_minimize_callback_;
diff --git a/cobalt/h5vcc/BUILD.gn b/cobalt/h5vcc/BUILD.gn
index 9c5a145..fd7216f 100644
--- a/cobalt/h5vcc/BUILD.gn
+++ b/cobalt/h5vcc/BUILD.gn
@@ -79,7 +79,6 @@
     "//cobalt/cache",
     "//cobalt/configuration",
     "//cobalt/dom",
-    "//cobalt/media",
     "//cobalt/network",
     "//cobalt/persistent_storage:persistent_settings",
     "//cobalt/script",
diff --git a/cobalt/h5vcc/h5vcc.cc b/cobalt/h5vcc/h5vcc.cc
index d3b8198..806f7a1 100644
--- a/cobalt/h5vcc/h5vcc.cc
+++ b/cobalt/h5vcc/h5vcc.cc
@@ -27,12 +27,12 @@
   c_val_ = new dom::CValView();
   crash_log_ = new H5vccCrashLog();
   runtime_ = new H5vccRuntime(settings.event_dispatcher);
-  settings_ =
-      new H5vccSettings(settings.media_module, settings.network_module,
+  settings_ = new H5vccSettings(
+      settings.set_media_source_setting_func, settings.network_module,
 #if SB_IS(EVERGREEN)
-                        settings.updater_module,
+      settings.updater_module,
 #endif
-                        settings.user_agent_data, settings.global_environment);
+      settings.user_agent_data, settings.global_environment);
 #if defined(COBALT_ENABLE_SSO)
   sso_ = new H5vccSso();
 #endif
diff --git a/cobalt/h5vcc/h5vcc.h b/cobalt/h5vcc/h5vcc.h
index c74b9e6..0dce20d 100644
--- a/cobalt/h5vcc/h5vcc.h
+++ b/cobalt/h5vcc/h5vcc.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "cobalt/base/event_dispatcher.h"
 #include "cobalt/dom/c_val_view.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
@@ -45,8 +46,7 @@
  public:
   struct Settings {
     Settings()
-        : media_module(NULL),
-          network_module(NULL),
+        : network_module(NULL),
 #if SB_IS(EVERGREEN)
           updater_module(NULL),
 #endif
@@ -55,7 +55,7 @@
           user_agent_data(NULL),
           global_environment(NULL) {
     }
-    media::MediaModule* media_module;
+    H5vccSettings::SetMediaSourceSettingFunc set_media_source_setting_func;
     network::NetworkModule* network_module;
 #if SB_IS(EVERGREEN)
     updater::UpdaterModule* updater_module;
diff --git a/cobalt/h5vcc/h5vcc_crash_log.cc b/cobalt/h5vcc/h5vcc_crash_log.cc
index 7a502b4..564feda 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.cc
+++ b/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -161,8 +161,8 @@
       default:
         replace = watchdog::NONE;
     }
-    return watchdog->Register(name, description, monitor_state, time_interval,
-                              time_wait, replace);
+    return watchdog->Register(name, description, monitor_state,
+                              time_interval * 1000, time_wait * 1000, replace);
   }
   return false;
 }
diff --git a/cobalt/h5vcc/h5vcc_crash_log.idl b/cobalt/h5vcc/h5vcc_crash_log.idl
index 45f6df3..2bbf4df 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.idl
+++ b/cobalt/h5vcc/h5vcc_crash_log.idl
@@ -34,9 +34,9 @@
   //   description, information on the Watchdog client.
   //   watchdog_state, application state to continue monitoring client up to.
   //     Inclusive.
-  //   time_interval, maximum number of microseconds allowed between pings
-  //     before triggering a Watchdog violation. Min value of 1000000.
-  //   time_wait, number of microseconds to initially wait before Watchdog
+  //   time_interval, maximum number of milliseconds allowed between pings
+  //     before triggering a Watchdog violation. Min value of 1000.
+  //   time_wait, number of milliseconds to initially wait before Watchdog
   //     violations can be triggered. Reapplies after client resumes from idle
   //     state due to application state changes.
   //   watchdog_replace, behavior with previously registered Watchdog clients
@@ -51,12 +51,13 @@
 
   // Returns true if Watchdog client was pinged. Name determines the Watchdog
   // client to ping with ping_info which can either be empty or contain relevant
-  // metadata. ping_info has a max length of 1024.
+  // metadata. ping_info has a max length of 128.
   boolean ping(DOMString name, DOMString ping_info);
 
   // Returns a json string containing the Watchdog violations since the last
-  // call. Clears internal cache of Watchdog violations to prevent duplicates.
-  // Timestamps are stored as strings due to int size constraints.
+  // call, up to the 200 most recent. Clears internal cache of Watchdog
+  // violations to prevent duplicates. Timestamps are stored as strings due to
+  // int size constraints.
   // Example json:
   // {
   //   "test-name":{
@@ -67,18 +68,18 @@
   //         "pingInfos":[
   //           {
   //             "info":"test-ping",
-  //             "timestampMicroseconds":"1658972623547006"
+  //             "timestampMilliseconds":"1658972623547"
   //           }
   //         ],
   //         "registeredClients":[
   //           "test-name"
   //         ],
-  //         "timeIntervalMicroseconds":"5000000",
-  //         "timeWaitMicroseconds":"0",
-  //         "timestampLastPingedMicroseconds":"1658972623547006",
-  //         "timestampRegisteredMicroseconds":"1658972621890834",
-  //         "timestampViolationMicroseconds":"1658972629489771",
-  //         "violationDurationMicroseconds":"942764"
+  //         "timeIntervalMilliseconds":"5000",
+  //         "timeWaitMilliseconds":"0",
+  //         "timestampLastPingedMilliseconds":"1658972623547",
+  //         "timestampRegisteredMilliseconds":"1658972621890",
+  //         "timestampViolationMilliseconds":"1658972629489",
+  //         "violationDurationMilliseconds":"942"
   //       }
   //     ]
   //   }
diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc
index 3417192..1735270 100644
--- a/cobalt/h5vcc/h5vcc_settings.cc
+++ b/cobalt/h5vcc/h5vcc_settings.cc
@@ -19,14 +19,15 @@
 namespace cobalt {
 namespace h5vcc {
 
-H5vccSettings::H5vccSettings(media::MediaModule* media_module,
-                             cobalt::network::NetworkModule* network_module,
+H5vccSettings::H5vccSettings(
+    const SetMediaSourceSettingFunc& set_media_source_setting_func,
+    cobalt::network::NetworkModule* network_module,
 #if SB_IS(EVERGREEN)
-                             cobalt::updater::UpdaterModule* updater_module,
+    cobalt::updater::UpdaterModule* updater_module,
 #endif
-                             web::NavigatorUAData* user_agent_data,
-                             script::GlobalEnvironment* global_environment)
-    : media_module_(media_module),
+    web::NavigatorUAData* user_agent_data,
+    script::GlobalEnvironment* global_environment)
+    : set_media_source_setting_func_(set_media_source_setting_func),
       network_module_(network_module),
 #if SB_IS(EVERGREEN)
       updater_module_(updater_module),
@@ -36,7 +37,6 @@
 }
 
 bool H5vccSettings::Set(const std::string& name, int32 value) const {
-  const char kMediaPrefix[] = "Media.";
   const char kNavigatorUAData[] = "NavigatorUAData";
   const char kQUIC[] = "QUIC";
 
@@ -44,8 +44,9 @@
   const char kUpdaterMinFreeSpaceBytes[] = "Updater.MinFreeSpaceBytes";
 #endif
 
-  if (name.compare(kMediaPrefix) == 0) {
-    return media_module_ ? media_module_->SetConfiguration(name, value) : false;
+  if (set_media_source_setting_func_ &&
+      set_media_source_setting_func_.Run(name, value)) {
+    return true;
   }
 
   if (name.compare(kNavigatorUAData) == 0 && value == 1) {
diff --git a/cobalt/h5vcc/h5vcc_settings.h b/cobalt/h5vcc/h5vcc_settings.h
index 2257d6b..a73e59f 100644
--- a/cobalt/h5vcc/h5vcc_settings.h
+++ b/cobalt/h5vcc/h5vcc_settings.h
@@ -17,7 +17,6 @@
 
 #include <string>
 
-#include "cobalt/media/media_module.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/wrappable.h"
@@ -35,13 +34,16 @@
 // version to avoid being abused.
 class H5vccSettings : public script::Wrappable {
  public:
-  explicit H5vccSettings(media::MediaModule* media_module,
-                         cobalt::network::NetworkModule* network_module,
+  typedef base::Callback<bool(const std::string& name, int value)>
+      SetMediaSourceSettingFunc;
+
+  H5vccSettings(const SetMediaSourceSettingFunc& set_media_source_setting_func,
+                cobalt::network::NetworkModule* network_module,
 #if SB_IS(EVERGREEN)
-                         cobalt::updater::UpdaterModule* updater_module,
+                cobalt::updater::UpdaterModule* updater_module,
 #endif
-                         web::NavigatorUAData* user_agent_data,
-                         script::GlobalEnvironment* global_environment);
+                web::NavigatorUAData* user_agent_data,
+                script::GlobalEnvironment* global_environment);
 
   // 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
@@ -51,7 +53,7 @@
   DEFINE_WRAPPABLE_TYPE(H5vccSettings);
 
  private:
-  media::MediaModule* media_module_;
+  const SetMediaSourceSettingFunc set_media_source_setting_func_;
   cobalt::network::NetworkModule* network_module_ = nullptr;
 #if SB_IS(EVERGREEN)
   cobalt::updater::UpdaterModule* updater_module_ = nullptr;
diff --git a/cobalt/h5vcc/h5vcc_storage.cc b/cobalt/h5vcc/h5vcc_storage.cc
index b5c3af0..d734a0e 100644
--- a/cobalt/h5vcc/h5vcc_storage.cc
+++ b/cobalt/h5vcc/h5vcc_storage.cc
@@ -248,12 +248,14 @@
                      quota.image() + quota.font() + quota.splash() +
                      quota.uncompiled_js() + quota.compiled_js();
 
-  // TODO(b/235529738): Calculate correct max_quota_size that subtracts
-  // non-cache memory used in the kSbSystemPathCacheDirectory.
   uint32_t max_quota_size = 24 * 1024 * 1024;
 #if SB_API_VERSION >= 14
   max_quota_size = kSbMaxSystemPathCacheDirectorySize;
 #endif
+  // Assume the non-http-cache memory in kSbSystemPathCacheDirectory
+  // is less than 1 mb and subtract this from the max_quota_size.
+  max_quota_size -= (1 << 20);
+
   if (quota_total != max_quota_size) {
     return SetQuotaResponse(starboard::FormatString(
         "H5vccStorageResourceTypeQuotaDictionary input parameter field values "
@@ -311,12 +313,13 @@
           ->GetMaxCacheStorageInBytes(disk_cache::kCompiledScript)
           .value());
 
-  // TODO(b/235529738): Calculate correct max_quota_size that subtracts
-  // non-cache memory used in the kSbSystemPathCacheDirectory.
   uint32_t max_quota_size = 24 * 1024 * 1024;
 #if SB_API_VERSION >= 14
   max_quota_size = kSbMaxSystemPathCacheDirectorySize;
 #endif
+  // Assume the non-http-cache memory in kSbSystemPathCacheDirectory
+  // is less than 1 mb and subtract this from the max_quota_size.
+  max_quota_size -= (1 << 20);
 
   quota.set_total(max_quota_size);
 
diff --git a/cobalt/h5vcc/h5vcc_system.h b/cobalt/h5vcc/h5vcc_system.h
index 6af4279..5b5d086 100644
--- a/cobalt/h5vcc/h5vcc_system.h
+++ b/cobalt/h5vcc/h5vcc_system.h
@@ -21,7 +21,6 @@
 #if SB_IS(EVERGREEN)
 #include "cobalt/h5vcc/h5vcc_updater.h"
 #endif
-#include "cobalt/media/media_module.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
diff --git a/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt b/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt
index 3e1afd6..287a65d 100644
--- a/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt
+++ b/cobalt/layout_tests/testdata/web-platform-tests/WebCryptoAPI/web_platform_tests.txt
@@ -1,6 +1,7 @@
 # WebCrypto API tests.
 #
 # Only HMAC signing is supported
+sign_verify/hmac.https.worker.html,PASS
 sign_verify/test_rsa_pkcs.https.html,DISABLE
 sign_verify/test_hmac.https.html,PASS
 sign_verify/test_rsa_pss.https.html,DISABLE
diff --git a/cobalt/media/base/sbplayer_pipeline.cc b/cobalt/media/base/sbplayer_pipeline.cc
index 79e9a6a..dc3a212 100644
--- a/cobalt/media/base/sbplayer_pipeline.cc
+++ b/cobalt/media/base/sbplayer_pipeline.cc
@@ -1255,11 +1255,9 @@
       SbTime time_ahead_of_playback =
           timestamp_of_last_written_audio_ - last_media_time_;
       if (time_ahead_of_playback > (kAudioLimit + kMediaTimeCheckInterval)) {
-        SbTime delay_time = (time_ahead_of_playback - kAudioLimit) /
-                            std::max(playback_rate_.value(), 1.0f);
         task_runner_->PostDelayedTask(
             FROM_HERE, base::Bind(&SbPlayerPipeline::DelayedNeedData, this),
-            base::TimeDelta::FromMicroseconds(delay_time));
+            base::TimeDelta::FromMicroseconds(kMediaTimeCheckInterval));
         audio_read_delayed_ = true;
         return;
       }
diff --git a/cobalt/media/base/starboard_player.cc b/cobalt/media/base/starboard_player.cc
index 7c5eb1d..ae7defd 100644
--- a/cobalt/media/base/starboard_player.cc
+++ b/cobalt/media/base/starboard_player.cc
@@ -37,7 +37,7 @@
 
 namespace {
 
-base::Statistics<SbTime, int, 1024> s_player_presenting_delays_;
+base::Statistics<SbTime, int, 1024> s_startup_latency;
 
 }  // namespace
 
@@ -983,7 +983,6 @@
   if (set_drm_system_ready_cb_time_ == -1) {
     first_events_str =
         starboard::FormatString("%-40s0 us", "SbPlayerCreate() called");
-
   } else if (set_drm_system_ready_cb_time_ < player_creation_time_) {
     first_events_str = starboard::FormatString(
         "%-40s0 us\n%-40s%" PRId64 " us", "set_drm_system_ready_cb called",
@@ -996,9 +995,10 @@
         set_drm_system_ready_cb_time_ - player_creation_time_);
   }
 
-  SbTime player_initialization_time_delta =
-      sb_player_state_initialized_time_ -
+  SbTime first_event_time =
       std::max(player_creation_time_, set_drm_system_ready_cb_time_);
+  SbTime player_initialization_time_delta =
+      sb_player_state_initialized_time_ - first_event_time;
   SbTime player_preroll_time_delta =
       sb_player_state_prerolling_time_ - sb_player_state_initialized_time_;
   SbTime first_audio_sample_time_delta = std::max(
@@ -1008,27 +1008,29 @@
   SbTime player_presenting_time_delta =
       sb_player_state_presenting_time_ -
       std::max(first_audio_sample_time_, first_video_sample_time_);
+  SbTime startup_latency = sb_player_state_presenting_time_ - first_event_time;
 
-  s_player_presenting_delays_.AddSample(player_presenting_time_delta, 1);
+  s_startup_latency.AddSample(startup_latency, 1);
 
   // clang-format off
   LOG(INFO) << starboard::FormatString(
-      "\nSbPlayer startup latencies\n"
+      "\nSbPlayer startup latencies: %" PRId64 " us\n"
       "  Event name                              time since last event\n"
       "  %s\n"  // |first_events_str| populated above
       "  kSbPlayerStateInitialized received      %" PRId64 " us\n"
       "  kSbPlayerStatePrerolling received       %" PRId64 " us\n"
       "  First media sample(s) written [a/v]     %" PRId64 "/%" PRId64 " us\n"
       "  kSbPlayerStatePresenting received       %" PRId64 " us\n"
-      "  kSbPlayerStatePresenting delay statistics (us):\n"
+      "  Startup latency statistics (us):\n"
       "    min: %" PRId64 ", median: %" PRId64 ", average: %" PRId64
       ", max: %" PRId64,
+      startup_latency,
       first_events_str.c_str(), player_initialization_time_delta,
       player_preroll_time_delta, first_audio_sample_time_delta,
       first_video_sample_time_delta, player_presenting_time_delta,
-      s_player_presenting_delays_.min(),
-      s_player_presenting_delays_.GetMedian(),
-      s_player_presenting_delays_.average(), s_player_presenting_delays_.max());
+      s_startup_latency.min(),
+      s_startup_latency.GetMedian(),
+      s_startup_latency.average(), s_startup_latency.max());
   // clang-format on
 }
 
diff --git a/cobalt/media/decoder_buffer_allocator.cc b/cobalt/media/decoder_buffer_allocator.cc
index 6f3a1a1..57b9974 100644
--- a/cobalt/media/decoder_buffer_allocator.cc
+++ b/cobalt/media/decoder_buffer_allocator.cc
@@ -188,10 +188,6 @@
   return max_buffer_capacity_;
 }
 
-size_t DecoderBufferAllocator::GetSourceBufferEvictExtraInBytes() const {
-  return source_buffer_evict_extra_in_bytes_;
-}
-
 void DecoderBufferAllocator::EnsureReuseAllocatorIsCreated() {
   mutex_.DCheckAcquired();
 
diff --git a/cobalt/media/decoder_buffer_allocator.h b/cobalt/media/decoder_buffer_allocator.h
index 2e08a9f..31c3df4 100644
--- a/cobalt/media/decoder_buffer_allocator.h
+++ b/cobalt/media/decoder_buffer_allocator.h
@@ -47,11 +47,6 @@
   size_t GetAllocatedMemory() const override;
   size_t GetCurrentMemoryCapacity() const override;
   size_t GetMaximumMemoryCapacity() const override;
-  size_t GetSourceBufferEvictExtraInBytes() const override;
-
-  void SetSourceBufferEvictExtraInBytes(size_t evict_extra_in_bytes) {
-    source_buffer_evict_extra_in_bytes_ = evict_extra_in_bytes;
-  }
 
  private:
   void EnsureReuseAllocatorIsCreated();
@@ -66,7 +61,6 @@
   std::unique_ptr<nb::BidirectionalFitReuseAllocator> reuse_allocator_;
 
   int max_buffer_capacity_ = 0;
-  size_t source_buffer_evict_extra_in_bytes_ = 0;
 
   // Monitor memory allocation and use when |using_memory_pool_| is false
   starboard::atomic_int32_t sbmemory_bytes_used_;
diff --git a/cobalt/media/decoder_buffer_memory_info.h b/cobalt/media/decoder_buffer_memory_info.h
index 2e4813b..730b303 100644
--- a/cobalt/media/decoder_buffer_memory_info.h
+++ b/cobalt/media/decoder_buffer_memory_info.h
@@ -28,14 +28,6 @@
   virtual size_t GetAllocatedMemory() const = 0;
   virtual size_t GetCurrentMemoryCapacity() const = 0;
   virtual size_t GetMaximumMemoryCapacity() const = 0;
-  // The return value will be used in `SourceBuffer::EvictCodedFrames()` so
-  // it will evict extra data from the SourceBuffer, to reduce the overall
-  // memory the underlying Demuxer implementation may use.
-  // NOTE: This is not the best place to pass such information.  Since it is a
-  // tentative workaround that will be reverted once we confirm the new
-  // DecoderBufferAllocator implementation works in production, adding this
-  // here allows us to reduce interfaces passed from media to dom.
-  virtual size_t GetSourceBufferEvictExtraInBytes() const = 0;
 };
 
 class StubDecoderBufferMemoryInfo : public DecoderBufferMemoryInfo {
@@ -45,7 +37,6 @@
   size_t GetAllocatedMemory() const override { return 0; }
   size_t GetCurrentMemoryCapacity() const override { return 0; }
   size_t GetMaximumMemoryCapacity() const override { return 0; }
-  size_t GetSourceBufferEvictExtraInBytes() const override { return 0; }
 };
 
 }  // namespace media
diff --git a/cobalt/media/media_module.cc b/cobalt/media/media_module.cc
index 7a63e4b..11bcbd7 100644
--- a/cobalt/media/media_module.cc
+++ b/cobalt/media/media_module.cc
@@ -183,14 +183,6 @@
 
 }  // namespace
 
-bool MediaModule::SetConfiguration(const std::string& name, int32 value) {
-  if (name == "source_buffer_evict_extra_in_bytes" && value >= 0) {
-    decoder_buffer_allocator_.SetSourceBufferEvictExtraInBytes(value);
-    return true;
-  }
-  return false;
-}
-
 std::unique_ptr<WebMediaPlayer> MediaModule::CreateWebMediaPlayer(
     WebMediaPlayerClient* client) {
   TRACK_MEMORY_SCOPE("Media");
diff --git a/cobalt/media/media_module.h b/cobalt/media/media_module.h
index 0a90162..e79db38 100644
--- a/cobalt/media/media_module.h
+++ b/cobalt/media/media_module.h
@@ -61,11 +61,6 @@
         system_window_(system_window),
         resource_provider_(resource_provider) {}
 
-  // 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
-  // invalid or not set to the expected value.
-  bool SetConfiguration(const std::string& name, int32 value);
-
   const DecoderBufferAllocator* GetDecoderBufferAllocator() const {
     return &decoder_buffer_allocator_;
   }
diff --git a/cobalt/network/url_request_context.cc b/cobalt/network/url_request_context.cc
index 34cc482..eb46dcf 100644
--- a/cobalt/network/url_request_context.cc
+++ b/cobalt/network/url_request_context.cc
@@ -181,14 +181,20 @@
   } else {
     using_http_cache_ = true;
 
-    // TODO: Set max size of cache in Starboard.
-    const int cache_size_mb = 24;
+    int max_cache_bytes = 24 * 1024 * 1024;
+#if SB_API_VERSION >= 14
+    max_cache_bytes = kSbMaxSystemPathCacheDirectorySize;
+#endif
+    // Assume the non-http-cache memory in kSbSystemPathCacheDirectory
+    // is less than 1 mb and subtract this from the max_cache_bytes.
+    max_cache_bytes -= (1 << 20);
+
     auto http_cache = std::make_unique<net::HttpCache>(
         storage_.http_network_session(),
         std::make_unique<net::HttpCache::DefaultBackend>(
             net::DISK_CACHE, net::CACHE_BACKEND_COBALT,
             base::FilePath(std::string(path.data())),
-            /* max_bytes */ 1024 * 1024 * cache_size_mb),
+            /* max_bytes */ max_cache_bytes),
         true);
     if (persistent_settings != nullptr) {
       auto cache_enabled = persistent_settings->GetPersistentSettingAsBool(
diff --git a/cobalt/script/BUILD.gn b/cobalt/script/BUILD.gn
index df196e6..fb5b577 100644
--- a/cobalt/script/BUILD.gn
+++ b/cobalt/script/BUILD.gn
@@ -60,6 +60,8 @@
     "//cobalt/base",
     "//nb",
     "//starboard:starboard_headers_only",
+    "//third_party/v8",
+    "//third_party/v8:v8_libplatform",
     "//url",
   ]
 }
@@ -74,6 +76,8 @@
     ":script",
     "//base",
     "//cobalt/base",
+    "//third_party/v8",
+    "//third_party/v8:v8_libplatform",
   ]
 }
 
diff --git a/cobalt/script/fake_global_environment.h b/cobalt/script/fake_global_environment.h
index d1d1cfb..51760fa 100644
--- a/cobalt/script/fake_global_environment.h
+++ b/cobalt/script/fake_global_environment.h
@@ -60,6 +60,10 @@
               const scoped_refptr<Wrappable>& impl,
               const std::string& local_object_name) override {}
   ScriptValueFactory* script_value_factory() { return NULL; }
+  v8::Isolate* isolate() const override { return nullptr; }
+  v8::Local<v8::Context> context() const override {
+    return v8::Local<v8::Context>();
+  }
 };
 
 }  // namespace script
diff --git a/cobalt/script/global_environment.h b/cobalt/script/global_environment.h
index 8681b94..4e569f3 100644
--- a/cobalt/script/global_environment.h
+++ b/cobalt/script/global_environment.h
@@ -27,6 +27,7 @@
 #include "cobalt/script/stack_frame.h"
 #include "cobalt/script/value_handle.h"
 #include "cobalt/script/wrappable.h"
+#include "v8/include/v8.h"
 
 namespace cobalt {
 namespace script {
@@ -138,6 +139,9 @@
   // should live longer than any ScriptValueFactory pointer.
   virtual ScriptValueFactory* script_value_factory() = 0;
 
+  virtual v8::Isolate* isolate() const = 0;
+  virtual v8::Local<v8::Context> context() const = 0;
+
   class ScopedPreventGarbageCollection {
    public:
     ScopedPreventGarbageCollection(GlobalEnvironment* global_environment,
diff --git a/cobalt/script/v8c/cobalt_platform.h b/cobalt/script/v8c/cobalt_platform.h
index 7c58d8e..c6d4399 100644
--- a/cobalt/script/v8c/cobalt_platform.h
+++ b/cobalt/script/v8c/cobalt_platform.h
@@ -17,6 +17,8 @@
 
 #include <map>
 #include <memory>
+#include <utility>
+#include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
@@ -60,7 +62,7 @@
     return default_platform_->OnCriticalMemoryPressure(length);
   }
 
-  int NumberOfWorkerThreads() {
+  int NumberOfWorkerThreads() override {
     return default_platform_->NumberOfWorkerThreads();
   }
 
@@ -68,16 +70,17 @@
       v8::Isolate* isolate) override;
 
   std::unique_ptr<v8::JobHandle> PostJob(
-       v8::TaskPriority priority, std::unique_ptr<v8::JobTask> job_task) override {
-     return v8::platform::NewDefaultJobHandle(
-         this, priority, std::move(job_task), NumberOfWorkerThreads());
+      v8::TaskPriority priority,
+      std::unique_ptr<v8::JobTask> job_task) override {
+    return v8::platform::NewDefaultJobHandle(
+        this, priority, std::move(job_task), NumberOfWorkerThreads());
   }
   void CallOnWorkerThread(std::unique_ptr<v8::Task> task) override {
     default_platform_->CallOnWorkerThread(std::move(task));
   }
 
-  virtual void CallDelayedOnWorkerThread(std::unique_ptr<v8::Task> task,
-                                         double delay_in_seconds) {
+  void CallDelayedOnWorkerThread(std::unique_ptr<v8::Task> task,
+                                 double delay_in_seconds) override {
     default_platform_->CallDelayedOnWorkerThread(std::move(task),
                                                  delay_in_seconds);
   }
diff --git a/cobalt/script/v8c/v8c_global_environment.h b/cobalt/script/v8c/v8c_global_environment.h
index fd4b772..a422397 100644
--- a/cobalt/script/v8c/v8c_global_environment.h
+++ b/cobalt/script/v8c/v8c_global_environment.h
@@ -102,8 +102,8 @@
 
   ScriptValueFactory* script_value_factory() override;
 
-  v8::Isolate* isolate() const { return isolate_; }
-  v8::Local<v8::Context> context() const {
+  v8::Isolate* isolate() const override { return isolate_; }
+  v8::Local<v8::Context> context() const override {
     return v8::Local<v8::Context>::New(isolate_, context_);
   }
 
diff --git a/cobalt/script/v8c/v8c_value_handle.cc b/cobalt/script/v8c/v8c_value_handle.cc
index 9e33866..d95d653 100644
--- a/cobalt/script/v8c/v8c_value_handle.cc
+++ b/cobalt/script/v8c/v8c_value_handle.cc
@@ -12,10 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <memory>
 #include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
 
 #include "cobalt/script/v8c/v8c_value_handle.h"
 
+#include "base/memory/ref_counted.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
 
@@ -103,5 +108,54 @@
   return v8c::ConvertSimpleObjectToMap(value, exception_state);
 }
 
+v8::Isolate* GetIsolate(const ValueHandleHolder& value) {
+  const script::v8c::V8cValueHandleHolder* v8_value_handle_holder =
+      base::polymorphic_downcast<const script::v8c::V8cValueHandleHolder*>(
+          &value);
+  return v8_value_handle_holder->isolate();
+}
+
+v8::Local<v8::Value> GetV8Value(const ValueHandleHolder& value) {
+  const script::v8c::V8cValueHandleHolder* v8_value_handle_holder =
+      base::polymorphic_downcast<const script::v8c::V8cValueHandleHolder*>(
+          &value);
+  return v8_value_handle_holder->v8_value();
+}
+
+ValueHandleHolder* DeserializeScriptValue(v8::Isolate* isolate,
+                                          const DataBuffer& data_buffer) {
+  v8::EscapableHandleScope scope(isolate);
+  v8::TryCatch try_catch(isolate);
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+  v8::ValueDeserializer deserializer(isolate, data_buffer.ptr.get(),
+                                     data_buffer.size);
+  v8::Local<v8::Value> value;
+  if (!deserializer.ReadValue(context).ToLocal(&value)) {
+    return nullptr;
+  }
+  script::v8c::V8cExceptionState exception_state(isolate);
+  auto* holder = new script::v8c::V8cValueHandleHolder();
+  FromJSValue(isolate, scope.Escape(value), script::v8c::kNoConversionFlags,
+              &exception_state, holder);
+  return holder;
+}
+
+std::unique_ptr<DataBuffer> SerializeScriptValue(
+    const ValueHandleHolder& value) {
+  v8::Isolate* isolate = GetIsolate(value);
+  script::v8c::EntryScope entry_scope(isolate);
+  v8::Local<v8::Value> v8_value = GetV8Value(value);
+  v8::ValueSerializer serializer(isolate);
+  bool wrote_value;
+  if (!serializer.WriteValue(isolate->GetCurrentContext(), v8_value)
+           .To(&wrote_value) ||
+      !wrote_value) {
+    return nullptr;
+  }
+  std::pair<uint8_t*, size_t> pair = serializer.Release();
+  return std::make_unique<DataBuffer>(std::move(pair.first), pair.second);
+}
+
 }  // namespace script
 }  // namespace cobalt
diff --git a/cobalt/script/value_handle.h b/cobalt/script/value_handle.h
index ef3b444..f0c8cce 100644
--- a/cobalt/script/value_handle.h
+++ b/cobalt/script/value_handle.h
@@ -15,11 +15,14 @@
 #ifndef COBALT_SCRIPT_VALUE_HANDLE_H_
 #define COBALT_SCRIPT_VALUE_HANDLE_H_
 
+#include <memory>
 #include <string>
 #include <unordered_map>
 
+#include "base/memory/ref_counted.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/script_value.h"
+#include "v8/include/v8.h"
 
 namespace cobalt {
 namespace script {
@@ -35,6 +38,18 @@
 
 typedef ScriptValue<ValueHandle> ValueHandleHolder;
 
+struct BufferDeleter {
+  void operator()(uint8_t* buffer) { SbMemoryDeallocate(buffer); }
+};
+using DataBufferPtr = std::unique_ptr<uint8_t[], BufferDeleter>;
+
+struct DataBuffer {
+  DataBufferPtr ptr;
+  size_t size;
+
+  DataBuffer(uint8_t* ptr, size_t size) : ptr(DataBufferPtr(ptr)), size(size) {}
+};
+
 // Converts a "simple" object to a map of the object's properties. "Simple"
 // means that the object's property names are strings and its property values
 // must be a boolean, number or string. Note that this is implemented on a per
@@ -47,6 +62,13 @@
 std::unordered_map<std::string, std::string> ConvertSimpleObjectToMap(
     const ValueHandleHolder& value, ExceptionState* exception_state);
 
+v8::Isolate* GetIsolate(const ValueHandleHolder& value);
+v8::Local<v8::Value> GetV8Value(const ValueHandleHolder& value);
+ValueHandleHolder* DeserializeScriptValue(v8::Isolate* isolate,
+                                          const DataBuffer& data_buffer);
+std::unique_ptr<DataBuffer> SerializeScriptValue(
+    const ValueHandleHolder& value);
+
 }  // namespace script
 }  // namespace cobalt
 
diff --git a/cobalt/site/docs/communication.md b/cobalt/site/docs/communication.md
index e96bb45..d8d4d40 100644
--- a/cobalt/site/docs/communication.md
+++ b/cobalt/site/docs/communication.md
@@ -10,7 +10,7 @@
 
 If you have identified a bug in Cobalt common code - meaning that you don't
 expect that it is related to a platform or port implementation quirk, then you
-should file a bug on our public issue tracker. 
+should file a bug on our public issue tracker.
 
 Additionally, if you have specific feature requests, you can also file them on
 our public tracker, but they should be very concrete and well-specified. If
@@ -26,7 +26,7 @@
 address (say, your work email address).*
 
 
-## Asking Techincal Questions
+## Asking Technical Questions
 
 For programming related technical questions about Starboard or Cobalt, we
 encourage you to search through and post questions on [Stack
diff --git a/cobalt/site/docs/development/setup-android.md b/cobalt/site/docs/development/setup-android.md
index ac393b0..2c8bdb5 100644
--- a/cobalt/site/docs/development/setup-android.md
+++ b/cobalt/site/docs/development/setup-android.md
@@ -106,10 +106,10 @@
 ## Basic Build, Install, and Run (command-line based)
 
 1.  Complete the Preliminary Setup above
-1.  Generate the cobalt.apk by building the "cobalt_deploy" target
+1.  Generate the cobalt.apk by building the "cobalt_install" target
 
     ```
-    ninja -C out/android-x86_gold cobalt_deploy
+    ninja -C out/android-x86_gold cobalt_install
     ```
 
     Output can be found in the corresponding `out/android-x86_gold` directory.
diff --git a/cobalt/site/docs/development/setup-docker.md b/cobalt/site/docs/development/setup-docker.md
index afc7065..d7b2beb 100644
--- a/cobalt/site/docs/development/setup-docker.md
+++ b/cobalt/site/docs/development/setup-docker.md
@@ -65,9 +65,9 @@
    base os image and version. Defaults to Debian 10. `ubuntu:bionic` and
    `ubuntu:xenial` are other tested examples.
 
-**PLATFORM**: Cobalt build platform, passed to GYP
+**PLATFORM**: Cobalt build platform, passed to GN
 
-**CONFIG**: Cobalt build config, passed to GYP. Defaults to `debug`
+**CONFIG**: Cobalt build config, passed to GN. Defaults to `debug`
 
 **TARGET**: Build target, passed to `ninja`
 
@@ -90,4 +90,4 @@
 ```
 
 and try to build Cobalt with the <a
-href="https://cobalt.googlesource.com/cobalt/+/refs/heads/22.lts.stable/src/README.md#building-and-running-the-code">usual gyp / ninja flow.</a>
+href="https://cobalt.googlesource.com/cobalt/+/refs/heads/22.lts.stable/src/README.md#building-and-running-the-code">usual GN / ninja flow.</a>
diff --git a/cobalt/site/docs/development/setup-linux.md b/cobalt/site/docs/development/setup-linux.md
index b296507..8f570d6 100644
--- a/cobalt/site/docs/development/setup-linux.md
+++ b/cobalt/site/docs/development/setup-linux.md
@@ -31,7 +31,7 @@
     $ export NVM_DIR=~/.nvm
     $ export NODE_VERSION=12.17.0
 
-    $ curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash
+    $ curl --silent -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
 
     $ . $NVM_DIR/nvm.sh \
         && nvm install --lts \
diff --git a/cobalt/speech/sandbox/speech_sandbox.cc b/cobalt/speech/sandbox/speech_sandbox.cc
index 56f46bd..78e3353 100644
--- a/cobalt/speech/sandbox/speech_sandbox.cc
+++ b/cobalt/speech/sandbox/speech_sandbox.cc
@@ -77,7 +77,7 @@
     const dom::DOMSettings::Options& dom_settings_options) {
   std::unique_ptr<script::EnvironmentSettings> environment_settings(
       new dom::DOMSettings(null_debugger_hooks_, kDOMMaxElementDepth, NULL,
-                           NULL, NULL, NULL, dom_settings_options));
+                           NULL, NULL, NULL, NULL, dom_settings_options));
   DCHECK(environment_settings);
 
   speech_recognition_ = new SpeechRecognition(environment_settings.get());
diff --git a/cobalt/test/document_loader.h b/cobalt/test/document_loader.h
index bb6ac15..6e6b7da 100644
--- a/cobalt/test/document_loader.h
+++ b/cobalt/test/document_loader.h
@@ -29,6 +29,7 @@
 #include "cobalt/dom/dom_parser.h"
 #include "cobalt/dom/dom_stat_tracker.h"
 #include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/testing/fake_document.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
@@ -78,7 +79,7 @@
     dom::Document::Options options(url);
     options.navigation_start_clock = new base::SystemMonotonicClock();
     options.viewport_size = cssom::ViewportSize(1920, 1080);
-    document_ = new dom::Document(&html_element_context_, options);
+    document_ = new dom::testing::FakeDocument(&html_element_context_, options);
     document_->AddObserver(this);
     document_loader_.reset(new loader::Loader(
         base::Bind(&loader::FetcherFactory::CreateFetcher,
diff --git a/cobalt/watchdog/watchdog.cc b/cobalt/watchdog/watchdog.cc
index a8dd794..0a46c90 100644
--- a/cobalt/watchdog/watchdog.cc
+++ b/cobalt/watchdog/watchdog.cc
@@ -38,14 +38,14 @@
 const char kWatchdogViolationsJson[] = "watchdog.json";
 // The frequency in microseconds of monitor loops.
 const int64_t kWatchdogMonitorFrequency = 1000000;
-// The maximum number of most recent repeated Watchdog violations.
-const int64_t kWatchdogMaxViolations = 100;
+// The maximum number of Watchdog violations.
+const int kWatchdogMaxViolations = 200;
 // The minimum number of microseconds between writes.
 const int64_t kWatchdogWriteWaitTime = 300000000;
 // The maximum number of most recent ping infos.
-const int64_t kWatchdogMaxPingInfos = 20;
+const int kWatchdogMaxPingInfos = 20;
 // The maximum length of each ping info.
-const int64_t kWatchdogMaxPingInfoLength = 1024;
+const int kWatchdogMaxPingInfoLength = 128;
 
 // Persistent setting name and default setting for the boolean that controls
 // whether or not Watchdog is enabled. When disabled, Watchdog behaves like a
@@ -162,14 +162,13 @@
     SB_CHECK(SbMutexAcquire(&(static_cast<Watchdog*>(context))->mutex_) ==
              kSbMutexAcquired);
 
+    // Shutdown
     if (!((static_cast<Watchdog*>(context))->is_monitoring_)) {
       SB_CHECK(SbMutexRelease(&(static_cast<Watchdog*>(context))->mutex_));
       break;
     }
 
-    int64_t current_time = SbTimeToPosix(SbTimeGetNow());
     SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
-    base::Value registered_clients(base::Value::Type::LIST);
 
     // Iterates through client map to monitor all registered clients.
     bool watchdog_violation = false;
@@ -196,94 +195,7 @@
       if (time_delta > client->time_interval_microseconds &&
           time_wait > client->time_wait_microseconds) {
         watchdog_violation = true;
-
-        // Gets violation dictionary with key client name from violations_map_.
-        if (static_cast<Watchdog*>(context)->violations_map_ == nullptr)
-          InitializeViolationsMap(context);
-        base::Value* violation_dict =
-            (static_cast<Watchdog*>(context)->violations_map_)
-                ->FindKey(client->name);
-
-        // Checks if new unique violation.
-        bool new_violation = false;
-        if (violation_dict == nullptr) {
-          new_violation = true;
-        } else {
-          // Compares against last_pinged_timestamp_microsecond of last
-          // violation.
-          base::Value* violations = violation_dict->FindKey("violations");
-          int index = violations->GetList().size() - 1;
-          std::string timestamp_last_pinged_microseconds =
-              violations->GetList()[index]
-                  .FindKey("timestampLastPingedMicroseconds")
-                  ->GetString();
-          if (timestamp_last_pinged_microseconds !=
-              std::to_string(client->time_last_pinged_microseconds))
-            new_violation = true;
-        }
-
-        // New unique violation.
-        if (new_violation) {
-          // Creates new violation.
-          base::Value violation(base::Value::Type::DICTIONARY);
-          violation.SetKey("pingInfos", client->ping_infos.Clone());
-          violation.SetKey("monitorState",
-                           base::Value(std::string(GetApplicationStateString(
-                               client->monitor_state))));
-          violation.SetKey(
-              "timeIntervalMicroseconds",
-              base::Value(std::to_string(client->time_interval_microseconds)));
-          violation.SetKey(
-              "timeWaitMicroseconds",
-              base::Value(std::to_string(client->time_wait_microseconds)));
-          violation.SetKey("timestampRegisteredMicroseconds",
-                           base::Value(std::to_string(
-                               client->time_registered_microseconds)));
-          violation.SetKey("timestampLastPingedMicroseconds",
-                           base::Value(std::to_string(
-                               client->time_last_pinged_microseconds)));
-          violation.SetKey("timestampViolationMicroseconds",
-                           base::Value(std::to_string(current_time)));
-          violation.SetKey(
-              "violationDurationMicroseconds",
-              base::Value(std::to_string(time_delta -
-                                         client->time_interval_microseconds)));
-          if (registered_clients.GetList().empty()) {
-            for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
-              registered_clients.GetList().emplace_back(base::Value(it.first));
-            }
-          }
-          violation.SetKey("registeredClients", registered_clients.Clone());
-
-          // Adds new violation to violations_map_.
-          if (violation_dict == nullptr) {
-            base::Value dict(base::Value::Type::DICTIONARY);
-            dict.SetKey("description", base::Value(client->description));
-            base::Value list(base::Value::Type::LIST);
-            list.GetList().emplace_back(violation.Clone());
-            dict.SetKey("violations", list.Clone());
-            (static_cast<Watchdog*>(context)->violations_map_)
-                ->SetKey(client->name, dict.Clone());
-          } else {
-            base::Value* violations = violation_dict->FindKey("violations");
-            violations->GetList().emplace_back(violation.Clone());
-            if (violations->GetList().size() > kWatchdogMaxViolations)
-              violations->GetList().erase(violations->GetList().begin());
-          }
-          // Consecutive non-unique violation.
-        } else {
-          // Updates consecutive violation in violations_map_.
-          base::Value* violations = violation_dict->FindKey("violations");
-          int index = violations->GetList().size() - 1;
-          int64_t violation_duration =
-              std::stoll(violations->GetList()[index]
-                             .FindKey("violationDurationMicroseconds")
-                             ->GetString());
-          violations->GetList()[index].SetKey(
-              "violationDurationMicroseconds",
-              base::Value(std::to_string(violation_duration + time_delta)));
-        }
-        static_cast<Watchdog*>(context)->pending_write_ = true;
+        UpdateViolationsMap(context, client, time_delta);
 
         // Resets time last updated.
         client->time_last_updated_monotonic_microseconds =
@@ -300,9 +212,103 @@
   return nullptr;
 }
 
+void Watchdog::UpdateViolationsMap(void* context, Client* client,
+                                   SbTimeMonotonic time_delta) {
+  // Gets violation dictionary with key client name from violations_map_.
+  if (static_cast<Watchdog*>(context)->violations_map_ == nullptr)
+    InitializeViolationsMap(context);
+  base::Value* violation_dict =
+      (static_cast<Watchdog*>(context)->violations_map_)->FindKey(client->name);
+
+  // Checks if new unique violation.
+  bool new_violation = false;
+  if (violation_dict == nullptr) {
+    new_violation = true;
+  } else {
+    // Compares against last_pinged_timestamp_microsecond of last violation.
+    base::Value* violations = violation_dict->FindKey("violations");
+    int last_index = violations->GetList().size() - 1;
+    std::string timestamp_last_pinged_milliseconds =
+        violations->GetList()[last_index]
+            .FindKey("timestampLastPingedMilliseconds")
+            ->GetString();
+    if (timestamp_last_pinged_milliseconds !=
+        std::to_string(client->time_last_pinged_microseconds / 1000))
+      new_violation = true;
+  }
+
+  // New unique violation.
+  if (new_violation) {
+    // Creates new violation.
+    base::Value violation(base::Value::Type::DICTIONARY);
+    violation.SetKey("pingInfos", client->ping_infos.Clone());
+    violation.SetKey("monitorState",
+                     base::Value(std::string(
+                         GetApplicationStateString(client->monitor_state))));
+    violation.SetKey(
+        "timeIntervalMilliseconds",
+        base::Value(std::to_string(client->time_interval_microseconds / 1000)));
+    violation.SetKey(
+        "timeWaitMilliseconds",
+        base::Value(std::to_string(client->time_wait_microseconds / 1000)));
+    violation.SetKey("timestampRegisteredMilliseconds",
+                     base::Value(std::to_string(
+                         client->time_registered_microseconds / 1000)));
+    violation.SetKey("timestampLastPingedMilliseconds",
+                     base::Value(std::to_string(
+                         client->time_last_pinged_microseconds / 1000)));
+    violation.SetKey(
+        "timestampViolationMilliseconds",
+        base::Value(std::to_string(SbTimeToPosix(SbTimeGetNow()) / 1000)));
+    violation.SetKey(
+        "violationDurationMilliseconds",
+        base::Value(std::to_string(
+            (time_delta - client->time_interval_microseconds) / 1000)));
+    base::Value registered_clients(base::Value::Type::LIST);
+    for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
+      registered_clients.GetList().emplace_back(base::Value(it.first));
+    }
+    violation.SetKey("registeredClients", registered_clients.Clone());
+
+    // Adds new violation to violations_map_.
+    if (violation_dict == nullptr) {
+      base::Value dict(base::Value::Type::DICTIONARY);
+      dict.SetKey("description", base::Value(client->description));
+      base::Value list(base::Value::Type::LIST);
+      list.GetList().emplace_back(violation.Clone());
+      dict.SetKey("violations", list.Clone());
+      (static_cast<Watchdog*>(context)->violations_map_)
+          ->SetKey(client->name, dict.Clone());
+    } else {
+      base::Value* violations = violation_dict->FindKey("violations");
+      violations->GetList().emplace_back(violation.Clone());
+    }
+    static_cast<Watchdog*>(context)->violations_count_++;
+    if (static_cast<Watchdog*>(context)->violations_count_ >
+        kWatchdogMaxViolations)
+      EvictWatchdogViolation(context);
+    // Consecutive non-unique violation.
+  } else {
+    // Updates consecutive violation in violations_map_.
+    base::Value* violations = violation_dict->FindKey("violations");
+    int last_index = violations->GetList().size() - 1;
+    int64_t violation_duration =
+        std::stoll(violations->GetList()[last_index]
+                       .FindKey("violationDurationMilliseconds")
+                       ->GetString());
+    violations->GetList()[last_index].SetKey(
+        "violationDurationMilliseconds",
+        base::Value(std::to_string(violation_duration + (time_delta / 1000))));
+  }
+
+  static_cast<Watchdog*>(context)->pending_write_ = true;
+}
+
 void Watchdog::InitializeViolationsMap(void* context) {
   // Loads the previous Watchdog violations file containing violations before
   // app start, if it exists, to populate violations_map_.
+  static_cast<Watchdog*>(context)->violations_count_ = 0;
+
   starboard::ScopedFile read_file(
       (static_cast<Watchdog*>(context)->GetWatchdogFilePath()).c_str(),
       kSbFileOpenOnly | kSbFileRead);
@@ -314,13 +320,56 @@
     static_cast<Watchdog*>(context)->violations_map_ =
         base::JSONReader::Read(watchdog_json);
   }
+
   if (static_cast<Watchdog*>(context)->violations_map_ == nullptr) {
     SB_LOG(INFO) << "[Watchdog] No previous violations JSON.";
     static_cast<Watchdog*>(context)->violations_map_ =
         std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+  } else {
+    for (const auto& it :
+         (static_cast<Watchdog*>(context)->violations_map_)->DictItems()) {
+      base::Value& violation_dict = it.second;
+      base::Value* violations = violation_dict.FindKey("violations");
+      static_cast<Watchdog*>(context)->violations_count_ +=
+          violations->GetList().size();
+    }
   }
 }
 
+void Watchdog::EvictWatchdogViolation(void* context) {
+  // Evicts a violation in violations_map_ prioritizing first the most frequent
+  // violations (largest violations count by client name) and second the oldest
+  // violation.
+  std::string evicted_name = "";
+  int evicted_count = 0;
+  int64_t evicted_timestamp = 0;
+
+  for (const auto& it :
+       (static_cast<Watchdog*>(context)->violations_map_)->DictItems()) {
+    std::string name = it.first;
+    base::Value& violation_dict = it.second;
+    base::Value* violations = violation_dict.FindKey("violations");
+    int count = violations->GetList().size();
+    int64_t timestamp =
+        std::stoll(violations->GetList()[0]
+                       .FindKey("timestampViolationMilliseconds")
+                       ->GetString());
+
+    if ((evicted_name == "") || (count > evicted_count) ||
+        ((count == evicted_count) && (timestamp < evicted_timestamp))) {
+      evicted_name = name;
+      evicted_count = count;
+      evicted_timestamp = timestamp;
+    }
+  }
+
+  base::Value* violation_dict =
+      (static_cast<Watchdog*>(context)->violations_map_)->FindKey(evicted_name);
+  base::Value* violations = violation_dict->FindKey("violations");
+  violations->GetList().erase(violations->GetList().begin());
+  static_cast<Watchdog*>(context)->violations_count_--;
+}
+
 void Watchdog::MaybeWriteWatchdogViolations(void* context) {
   if (SbTimeGetMonotonicNow() >
       static_cast<Watchdog*>(context)->time_last_written_microseconds_ +
@@ -345,9 +394,9 @@
   if (is_disabled_) return true;
 
   // Validates parameters.
-  if (time_interval < 1000000 || time_wait < 0) {
+  if (time_interval < kWatchdogMonitorFrequency || time_wait < 0) {
     SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
-    if (time_interval < 1000000) {
+    if (time_interval < kWatchdogMonitorFrequency) {
       SB_DLOG(ERROR) << "[Watchdog] Time interval less than min: "
                      << kWatchdogMonitorFrequency;
     } else {
@@ -450,8 +499,8 @@
     if (info != "") {
       // Creates new ping_info.
       base::Value ping_info(base::Value::Type::DICTIONARY);
-      ping_info.SetKey("timestampMicroseconds",
-                       base::Value(std::to_string(current_time)));
+      ping_info.SetKey("timestampMilliseconds",
+                       base::Value(std::to_string(current_time / 1000)));
       ping_info.SetKey("info", base::Value(info));
 
       client->ping_infos.GetList().emplace_back(ping_info.Clone());
@@ -491,6 +540,7 @@
       base::DictionaryValue** dict = nullptr;
       violations_map_->GetAsDictionary(dict);
       if (dict) (*dict)->Clear();
+      violations_count_ = 0;
     }
     starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
     SB_LOG(INFO) << "[Watchdog] Reading violations:\n" << watchdog_json;
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index 7ab5d96..1532f79 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -97,7 +97,10 @@
   std::string GetWatchdogFilePath();
   void WriteWatchdogViolations();
   static void* Monitor(void* context);
+  static void UpdateViolationsMap(void* context, Client* client,
+                                  SbTimeMonotonic time_delta);
   static void InitializeViolationsMap(void* context);
+  static void EvictWatchdogViolation(void* context);
   static void MaybeWriteWatchdogViolations(void* context);
   static void MaybeTriggerCrash(void* context);
 
@@ -128,6 +131,8 @@
   std::unordered_map<std::string, std::unique_ptr<Client>> client_map_;
   // Dictionary of lists of Watchdog violations represented as dictionaries.
   std::unique_ptr<base::Value> violations_map_;
+  // Number of violations in violations_map_;
+  int violations_count_;
   // Monitor thread.
   SbThread watchdog_thread_;
   // Flag to stop monitor thread.
diff --git a/cobalt/web/agent.cc b/cobalt/web/agent.cc
index d017e6d..f52e113 100644
--- a/cobalt/web/agent.cc
+++ b/cobalt/web/agent.cc
@@ -18,6 +18,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/observer_list.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/script_loader_factory.h"
@@ -51,6 +52,11 @@
   explicit Impl(const Agent::Options& options);
   virtual ~Impl();
 
+  void AddEnvironmentSettingsChangeObserver(
+      EnvironmentSettingsChangeObserver* observer) final;
+  void RemoveEnvironmentSettingsChangeObserver(
+      EnvironmentSettingsChangeObserver* observer) final;
+
   // Context
   //
   void set_message_loop(base::MessageLoop* message_loop) {
@@ -88,6 +94,9 @@
   const std::string& name() const final { return name_; };
   void setup_environment_settings(
       EnvironmentSettings* environment_settings) final {
+    for (auto& observer : environment_settings_change_observers_) {
+      observer.OnEnvironmentSettingsChanged(!!environment_settings);
+    }
     environment_settings_.reset(environment_settings);
     if (environment_settings_) environment_settings_->set_context(this);
   }
@@ -208,6 +217,9 @@
   scoped_refptr<worker::ServiceWorkerObject> active_service_worker_;
   scoped_refptr<worker::ServiceWorkerRegistrationObject>
       containing_service_worker_registration_;
+
+  base::ObserverList<Context::EnvironmentSettingsChangeObserver>::Unchecked
+      environment_settings_change_observers_;
 };
 
 Impl::Impl(const Agent::Options& options) : name_(options.name) {
@@ -269,6 +281,7 @@
   }
 
   setup_environment_settings(nullptr);
+  environment_settings_change_observers_.Clear();
   blob_registry_.reset();
   script_runner_.reset();
   execution_state_.reset();
@@ -280,6 +293,16 @@
 
 Impl::~Impl() { ShutDownJavaScriptEngine(); }
 
+void Impl::AddEnvironmentSettingsChangeObserver(
+    Context::EnvironmentSettingsChangeObserver* observer) {
+  environment_settings_change_observers_.AddObserver(observer);
+}
+
+void Impl::RemoveEnvironmentSettingsChangeObserver(
+    Context::EnvironmentSettingsChangeObserver* observer) {
+  environment_settings_change_observers_.RemoveObserver(observer);
+}
+
 void Impl::InjectGlobalObjectAttributes(
     const Agent::Options::InjectedGlobalObjectAttributes& attributes) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/cobalt/web/context.h b/cobalt/web/context.h
index f48182f..360dbe2 100644
--- a/cobalt/web/context.h
+++ b/cobalt/web/context.h
@@ -44,6 +44,15 @@
 class Context {
  public:
   virtual ~Context() {}
+
+  class EnvironmentSettingsChangeObserver {
+   public:
+    virtual void OnEnvironmentSettingsChanged(bool context_valid) = 0;
+
+   protected:
+    virtual ~EnvironmentSettingsChangeObserver() = default;
+  };
+
   virtual base::MessageLoop* message_loop() const = 0;
   virtual void ShutDownJavaScriptEngine() = 0;
   virtual loader::FetcherFactory* fetcher_factory() const = 0;
@@ -82,6 +91,11 @@
   virtual std::string GetUserAgent() const = 0;
   virtual std::string GetPreferredLanguage() const = 0;
 
+  virtual void AddEnvironmentSettingsChangeObserver(
+      EnvironmentSettingsChangeObserver* observer) = 0;
+  virtual void RemoveEnvironmentSettingsChangeObserver(
+      EnvironmentSettingsChangeObserver* observer) = 0;
+
   // https://w3c.github.io/ServiceWorker/#dfn-control
   virtual bool is_controlled_by(worker::ServiceWorkerObject* worker) const = 0;
 
diff --git a/cobalt/web/csp_violation_reporter.cc b/cobalt/web/csp_violation_reporter.cc
index 268ac95..2cb1951 100644
--- a/cobalt/web/csp_violation_reporter.cc
+++ b/cobalt/web/csp_violation_reporter.cc
@@ -21,7 +21,6 @@
 #include "base/json/json_writer.h"
 #include "base/values.h"
 #include "cobalt/dom/document.h"
-#include "cobalt/dom/html_element_context.h"
 #include "cobalt/network/net_poster.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/web/security_policy_violation_event.h"
@@ -83,12 +82,12 @@
 }
 
 void GatherSecurityPolicyViolationEventData(
-    const dom::Document* document, const csp::ViolationInfo& violation_info,
+    WindowOrWorkerGlobalScope* global, const csp::ViolationInfo& violation_info,
     ViolationEvent* event_data) {
-  event_data->document_uri =
-      StripUrlForUseInReport(document->url_as_gurl(), document->url_as_gurl());
-  event_data->blocked_uri = StripUrlForUseInReport(document->url_as_gurl(),
-                                                   violation_info.blocked_url);
+  GURL base_url = global->environment_settings()->base_url();
+  event_data->document_uri = StripUrlForUseInReport(base_url, base_url);
+  event_data->blocked_uri =
+      StripUrlForUseInReport(base_url, violation_info.blocked_url);
   // TODO: Implement Document referrer, if needed.
   event_data->referrer = "";
   event_data->violated_directive = violation_info.directive_text;
@@ -96,14 +95,14 @@
   event_data->original_policy = violation_info.header;
 
   script::GlobalEnvironment* global_environment =
-      document->html_element_context()->script_runner()->GetGlobalEnvironment();
+      global->environment_settings()->context()->global_environment();
   const std::vector<script::StackFrame>& stack_trace =
       global_environment->GetStackTrace(1);
   if (stack_trace.size() > 0) {
     event_data->line_number = stack_trace[0].line_number;
     event_data->column_number = stack_trace[0].column_number;
-    event_data->source_file = StripUrlForUseInReport(
-        document->url_as_gurl(), GURL(stack_trace[0].source_url));
+    event_data->source_file =
+        StripUrlForUseInReport(base_url, GURL(stack_trace[0].source_url));
   }
 
   // TODO: Set the status code if the document origin is non-secure.
@@ -113,14 +112,15 @@
 }  // namespace
 
 CspViolationReporter::CspViolationReporter(
-    dom::Document* document, const network_bridge::PostSender& post_sender)
+    web::WindowOrWorkerGlobalScope* global,
+    const network_bridge::PostSender& post_sender)
     : post_sender_(post_sender),
       message_loop_(base::MessageLoop::current()),
-      document_(document) {}
+      global_(global) {}
 
 CspViolationReporter::~CspViolationReporter() {}
 
-// https://www.w3.org/TR/CSP2/#violation-reports
+// https://www.w3.org/TR/CSP3/#report-violation
 void CspViolationReporter::Report(const csp::ViolationInfo& violation_info) {
   DCHECK(message_loop_);
   if (!message_loop_->task_runner()->BelongsToCurrentThread()) {
@@ -132,14 +132,24 @@
 
   LOG(INFO) << violation_info.console_message;
   ViolationEvent violation_data;
-  GatherSecurityPolicyViolationEventData(document_, violation_info,
+  GatherSecurityPolicyViolationEventData(global_, violation_info,
                                          &violation_data);
-  document_->DispatchEvent(new SecurityPolicyViolationEvent(
-      violation_data.document_uri, violation_data.referrer,
-      violation_data.blocked_uri, violation_data.violated_directive,
-      violation_data.effective_directive, violation_data.original_policy,
-      violation_data.source_file, violation_data.status_code,
-      violation_data.line_number, violation_data.column_number));
+  if (global_->IsWindow()) {
+    global_->AsWindow()->document()->DispatchEvent(
+        new SecurityPolicyViolationEvent(
+            violation_data.document_uri, violation_data.referrer,
+            violation_data.blocked_uri, violation_data.violated_directive,
+            violation_data.effective_directive, violation_data.original_policy,
+            violation_data.source_file, violation_data.status_code,
+            violation_data.line_number, violation_data.column_number));
+  } else {
+    global_->DispatchEvent(new SecurityPolicyViolationEvent(
+        violation_data.document_uri, violation_data.referrer,
+        violation_data.blocked_uri, violation_data.violated_directive,
+        violation_data.effective_directive, violation_data.original_policy,
+        violation_data.source_file, violation_data.status_code,
+        violation_data.line_number, violation_data.column_number));
+  }
 
   if (violation_info.endpoints.empty() || post_sender_.is_null()) {
     return;
@@ -190,7 +200,7 @@
     return;
   }
   violation_reports_sent_.insert(report_hash);
-  const GURL& origin_url = document_->url_as_gurl();
+  const GURL& origin_url = global_->environment_settings()->base_url();
   for (std::vector<std::string>::const_iterator it = endpoints.begin();
        it != endpoints.end(); ++it) {
     GURL resolved_endpoint = origin_url.Resolve(*it);
diff --git a/cobalt/web/csp_violation_reporter.h b/cobalt/web/csp_violation_reporter.h
index c7aa1a5..b2b9a98 100644
--- a/cobalt/web/csp_violation_reporter.h
+++ b/cobalt/web/csp_violation_reporter.h
@@ -25,11 +25,8 @@
 #include "cobalt/network_bridge/net_poster.h"
 
 namespace cobalt {
-namespace dom {
-class Document;
-}  // namespace dom
-
 namespace web {
+class WindowOrWorkerGlobalScope;
 
 // Responsible for reporting CSP violations, i.e. posting JSON
 // reports to any reporting endpoints described by the policy.
@@ -37,12 +34,12 @@
 // and passed in to its constructor. Generally it should not be called directly.
 class CspViolationReporter {
  public:
-  CspViolationReporter(dom::Document* document,
+  CspViolationReporter(WindowOrWorkerGlobalScope* global,
                        const network_bridge::PostSender& post_sender);
   virtual ~CspViolationReporter();
 
   // Used as a callback from ContentSecurityPolicy to dispatch security
-  // violation events on the Document and send the reports.
+  // violation events on the Document or Global Object and send the reports.
   // Report() can be called from any thread and it will re-post itself
   // to the message loop it was created on, which should be the Document's
   // message loop.
@@ -63,7 +60,7 @@
   // We must send violations on the document's message loop.
   base::MessageLoop* message_loop_;
 
-  dom::Document* document_;
+  WindowOrWorkerGlobalScope* global_;
 
   DISALLOW_COPY_AND_ASSIGN(CspViolationReporter);
 };
diff --git a/cobalt/web/message_event.cc b/cobalt/web/message_event.cc
index 0f153b6..a6e8888 100644
--- a/cobalt/web/message_event.cc
+++ b/cobalt/web/message_event.cc
@@ -20,15 +20,20 @@
 
 #include "base/basictypes.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/web/context.h"
 #include "cobalt/web/environment_settings.h"
 #include "starboard/common/string.h"
+#include "starboard/memory.h"
+#include "v8/include/v8.h"
 
 namespace {
-const char* const kResponseTypes[] = {"text", "blob", "arraybuffer"};
+const char* const kResponseTypes[] = {"text", "blob", "arraybuffer", "any"};
 
 COMPILE_ASSERT(arraysize(kResponseTypes) ==
-                   cobalt::web::MessageEvent::kResponseTypeCodeMax,
+                   cobalt::web::MessageEvent::kResponseTypeMax,
                enum_and_array_size_mismatch);
 
 }  // namespace
@@ -36,67 +41,73 @@
 namespace cobalt {
 namespace web {
 
+// static
 std::string MessageEvent::GetResponseTypeAsString(
-    const MessageEvent::ResponseTypeCode code) {
-  DCHECK_LT(code, kResponseTypeCodeMax);
-  if ((code >= kText) && (code < kResponseTypeCodeMax)) {
-    return kResponseTypes[code];
-  }
-  return std::string();
+    const MessageEvent::ResponseType response_type) {
+  DCHECK_GE(response_type, kText);
+  DCHECK_LT(response_type, kResponseTypeMax);
+  return kResponseTypes[response_type];
 }
 
-MessageEvent::ResponseTypeCode MessageEvent::GetResponseTypeCode(
+// static
+MessageEvent::ResponseType MessageEvent::GetResponseType(
     base::StringPiece to_match) {
   for (std::size_t i = 0; i != arraysize(kResponseTypes); ++i) {
     if (strncmp(kResponseTypes[i], to_match.data(), to_match.size()) == 0) {
-      return MessageEvent::ResponseTypeCode(i);
+      return MessageEvent::ResponseType(i);
     }
   }
-  return kResponseTypeCodeMax;
+  return kResponseTypeMax;
 }
 
-MessageEvent::ResponseType MessageEvent::data(
+MessageEvent::Response MessageEvent::data(
     script::EnvironmentSettings* settings) const {
-  const char* data_pointer = NULL;
-  int data_length = 0;
-  if (data_) {
-    data_pointer = data_->data();
-    data_length = data_->size();
+  if (!data_ && !data_io_buffer_) {
+    return Response("");
   }
 
-  auto* global_environment =
-      settings ? base::polymorphic_downcast<web::EnvironmentSettings*>(settings)
-                     ->context()
-                     ->global_environment()
-               : nullptr;
-  script::Handle<script::ArrayBuffer> response_buffer;
-  if (response_type_ != kText) {
+  script::GlobalEnvironment* global_environment = nullptr;
+  if (response_type_ == kBlob || response_type_ == kArrayBuffer ||
+      response_type_ == kAny) {
+    DCHECK(settings);
+    global_environment =
+        base::polymorphic_downcast<web::EnvironmentSettings*>(settings)
+            ->context()
+            ->global_environment();
     DCHECK(global_environment);
-    auto buffer_copy =
-        script::ArrayBuffer::New(global_environment, data_pointer, data_length);
-    response_buffer = std::move(buffer_copy);
   }
 
   switch (response_type_) {
     case kText: {
       // TODO: Find a way to remove two copies of data here.
-      std::string string_response(data_pointer, data_length);
-      return ResponseType(string_response);
+      std::string string_response(data_io_buffer_->data(),
+                                  data_io_buffer_->size());
+      return Response(string_response);
     }
-    case kBlob: {
-      DCHECK(settings);
-      scoped_refptr<web::Blob> blob = new web::Blob(settings, response_buffer);
-      return ResponseType(blob);
-    }
+    case kBlob:
     case kArrayBuffer: {
-      return ResponseType(response_buffer);
+      auto buffer_copy = script::ArrayBuffer::New(
+          global_environment, data_io_buffer_->data(), data_io_buffer_->size());
+      script::Handle<script::ArrayBuffer> response_buffer =
+          std::move(buffer_copy);
+      if (response_type_ == kBlob) {
+        DCHECK(settings);
+        scoped_refptr<web::Blob> blob =
+            new web::Blob(settings, response_buffer);
+        return Response(blob);
+      }
+      return Response(response_buffer);
     }
-    case kResponseTypeCodeMax:
+    case kAny: {
+      v8::Isolate* isolate = global_environment->isolate();
+      script::v8c::EntryScope entry_scope(isolate);
+      return Response(script::Handle<script::ValueHandle>(
+          std::move(script::DeserializeScriptValue(isolate, *data_))));
+    }
+    default:
       NOTREACHED() << "Invalid response type.";
-      return ResponseType();
+      return Response("");
   }
-
-  return ResponseType();
 }
 
 }  // namespace web
diff --git a/cobalt/web/message_event.h b/cobalt/web/message_event.h
index 88a838a..b39a198 100644
--- a/cobalt/web/message_event.h
+++ b/cobalt/web/message_event.h
@@ -16,58 +16,63 @@
 #ifndef COBALT_WEB_MESSAGE_EVENT_H_
 #define COBALT_WEB_MESSAGE_EVENT_H_
 
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_piece.h"
 #include "cobalt/base/token.h"
 #include "cobalt/script/array_buffer.h"
 #include "cobalt/script/union_type.h"
+#include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/wrappable.h"
 #include "cobalt/web/blob.h"
 #include "cobalt/web/event.h"
 #include "cobalt/web/message_event_init.h"
 #include "net/base/io_buffer.h"
+#include "v8/include/v8.h"
 
 namespace cobalt {
 namespace web {
 
 class MessageEvent : public web::Event {
  public:
-  typedef script::UnionType3<std::string, scoped_refptr<web::Blob>,
-                             script::Handle<script::ArrayBuffer> >
-      ResponseType;
-  // These response codes are ordered in the likelihood of being used.
+  typedef script::UnionType4<std::string, scoped_refptr<web::Blob>,
+                             script::Handle<script::ArrayBuffer>,
+                             script::Handle<script::ValueHandle>>
+      Response;
+  // These response types are ordered in the likelihood of being used.
   // Keeping them in expected order will help make code faster.
-  enum ResponseTypeCode { kText, kBlob, kArrayBuffer, kResponseTypeCodeMax };
-
-  explicit MessageEvent(const std::string& type) : Event(type) {}
-  MessageEvent(const std::string& type, const web::MessageEventInit& init_dict)
-      : Event(type, init_dict),
-        data_(new net::IOBufferWithSize(init_dict.data().size())) {
-    init_dict.data().copy(data_->data(), data_->size());
-  }
-
-  MessageEvent(base::Token type, ResponseTypeCode response_type,
-               const scoped_refptr<net::IOBufferWithSize>& data)
-      : Event(type), response_type_(response_type), data_(data) {}
+  enum ResponseType { kText, kBlob, kArrayBuffer, kAny, kResponseTypeMax };
 
   // Creates an event with its "initialized flag" unset.
   explicit MessageEvent(UninitializedFlag uninitialized_flag)
       : Event(uninitialized_flag) {}
 
-  ResponseType data(script::EnvironmentSettings* settings = nullptr) const;
+  explicit MessageEvent(const std::string& type) : Event(type) {}
+  MessageEvent(base::Token type, std::unique_ptr<script::DataBuffer> data)
+      : Event(type), response_type_(kAny), data_(std::move(data)) {}
+  MessageEvent(base::Token type, ResponseType response_type,
+               const scoped_refptr<net::IOBufferWithSize>& data)
+      : Event(type), response_type_(response_type), data_io_buffer_(data) {}
+  MessageEvent(const std::string& type, const web::MessageEventInit& init_dict)
+      : Event(type, init_dict),
+        response_type_(kAny),
+        data_(script::SerializeScriptValue(*(init_dict.data()))) {}
+
+  Response data(script::EnvironmentSettings* settings = nullptr) const;
 
   // These helper functions are custom, and not in any spec.
-  static std::string GetResponseTypeAsString(const ResponseTypeCode code);
-  // This function returns kResponseTypeCodeMax if no match is found.
-  static ResponseTypeCode GetResponseTypeCode(base::StringPiece to_match);
+  static std::string GetResponseTypeAsString(const ResponseType response_type);
+  static MessageEvent::ResponseType GetResponseType(base::StringPiece to_match);
 
   DEFINE_WRAPPABLE_TYPE(MessageEvent)
 
  private:
-  ResponseTypeCode response_type_ = kText;
-  scoped_refptr<net::IOBufferWithSize> data_;
+  ResponseType response_type_ = kText;
+  scoped_refptr<net::IOBufferWithSize> data_io_buffer_;
+  std::unique_ptr<script::DataBuffer> data_;
 };
 
 }  // namespace web
diff --git a/cobalt/web/message_event.idl b/cobalt/web/message_event.idl
index 9f722bd..0e15ab7 100644
--- a/cobalt/web/message_event.idl
+++ b/cobalt/web/message_event.idl
@@ -19,5 +19,5 @@
   Exposed = (Window,Worker),
   Constructor(DOMString type, optional MessageEventInit eventInitDict)
 ] interface MessageEvent : Event {
-  [CallWith = EnvironmentSettings] readonly attribute DOMString data;
+  [CallWith = EnvironmentSettings] readonly attribute any data;
 };
diff --git a/cobalt/web/message_event_init.idl b/cobalt/web/message_event_init.idl
index 9de818e..9ccf914 100644
--- a/cobalt/web/message_event_init.idl
+++ b/cobalt/web/message_event_init.idl
@@ -15,5 +15,5 @@
 // https://www.w3.org/TR/html50/webappapis.html#erroreventinit
 
 dictionary MessageEventInit : EventInit {
-  DOMString data = "";
+  any data = null;
 };
diff --git a/cobalt/web/message_event_test.cc b/cobalt/web/message_event_test.cc
index 4feaab2..d8f6d6d 100644
--- a/cobalt/web/message_event_test.cc
+++ b/cobalt/web/message_event_test.cc
@@ -20,6 +20,8 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "cobalt/dom/testing/test_with_javascript.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/value_handle.h"
 #include "cobalt/web/message_event_init.h"
 #include "cobalt/web/testing/gtest_workarounds.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -46,14 +48,17 @@
   EXPECT_FALSE(event->IsBeingDispatched());
   EXPECT_FALSE(event->propagation_stopped());
   EXPECT_FALSE(event->immediate_propagation_stopped());
-  MessageEvent::ResponseType event_data = event->data();
+  MessageEvent::Response event_data = event->data();
   EXPECT_TRUE(event_data.IsType<std::string>());
   EXPECT_EQ("", event_data.AsType<std::string>());
 }
 
-TEST(MessageEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
+TEST_F(MessageEventTestWithJavaScript,
+       ConstructorWithEventTypeAndDefaultInitDict) {
   MessageEventInit init;
-  init.set_data("data_value");
+  base::Optional<script::ValueHandleHolder::Reference> reference;
+  EvaluateScript("'data_value'", window(), &reference);
+  init.set_data(&(reference->referenced_value()));
   scoped_refptr<MessageEvent> event = new MessageEvent("mytestevent", init);
 
   EXPECT_EQ("mytestevent", event->type());
@@ -66,33 +71,34 @@
   EXPECT_FALSE(event->IsBeingDispatched());
   EXPECT_FALSE(event->propagation_stopped());
   EXPECT_FALSE(event->immediate_propagation_stopped());
-  MessageEvent::ResponseType event_data = event->data();
-  EXPECT_TRUE(event_data.IsType<std::string>());
-  EXPECT_EQ("data_value", event_data.AsType<std::string>());
+  MessageEvent::Response event_data =
+      event->data(window()->environment_settings());
+  EXPECT_TRUE(event_data.IsType<script::Handle<script::ValueHandle>>());
+  auto script_value =
+      event_data.AsType<script::Handle<script::ValueHandle>>().GetScriptValue();
+  auto* isolate = script::GetIsolate(*script_value);
+  script::v8c::EntryScope entry_scope(isolate);
+  v8::Local<v8::Value> v8_value = script::GetV8Value(*script_value);
+  std::string actual =
+      *(v8::String::Utf8Value(isolate, v8_value.As<v8::String>()));
+  EXPECT_EQ("data_value", actual);
 }
 
 TEST_F(MessageEventTestWithJavaScript,
        ConstructorWithEventTypeAndErrorInitDict) {
   std::string result;
-  bool success = EvaluateScript(
-      "var event = new MessageEvent('dog', "
-      "    {'cancelable':true, "
-      "     'data':'data_value'});"
-      "if (event.type == 'dog' &&"
-      "    event.bubbles == false &&"
-      "    event.cancelable == true &&"
-      "    event.data == 'data_value') "
-      "    event.data;",
-      &result);
+  EXPECT_TRUE(
+      EvaluateScript("var event = new MessageEvent('dog', "
+                     "    {'cancelable':true, "
+                     "     'data':'data_value'});"
+                     "if (event.type == 'dog' &&"
+                     "    event.bubbles == false &&"
+                     "    event.cancelable == true &&"
+                     "    event.data == 'data_value') "
+                     "    event.data;",
+                     &result))
+      << "Failed to evaluate script.";
   EXPECT_EQ("data_value", result);
-
-  if (!success) {
-    DLOG(ERROR) << "Failed to evaluate test: "
-                << "\"" << result << "\"";
-  } else {
-    LOG(INFO) << "Test result : "
-              << "\"" << result << "\"";
-  }
 }
 
 }  // namespace web
diff --git a/cobalt/web/message_port.cc b/cobalt/web/message_port.cc
index bc56140..519e682 100644
--- a/cobalt/web/message_port.cc
+++ b/cobalt/web/message_port.cc
@@ -14,7 +14,9 @@
 
 #include "cobalt/web/message_port.h"
 
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -33,35 +35,63 @@
 namespace web {
 
 MessagePort::MessagePort(web::EventTarget* event_target)
-    : event_target_(event_target) {}
+    : event_target_(event_target) {
+  if (!event_target_) {
+    return;
+  }
+  Context* context = event_target_->environment_settings()->context();
+  base::MessageLoop* message_loop = context->message_loop();
+  if (!message_loop) {
+    return;
+  }
+  message_loop->task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&Context::AddEnvironmentSettingsChangeObserver,
+                     base::Unretained(context), base::Unretained(this)));
+  remove_environment_settings_change_observer_ =
+      base::BindOnce(&Context::RemoveEnvironmentSettingsChangeObserver,
+                     base::Unretained(context), base::Unretained(this));
+}
 
-MessagePort::~MessagePort() { event_target_ = nullptr; }
+MessagePort::~MessagePort() { Close(); }
 
-void MessagePort::PostMessage(const std::string& messages) {
+void MessagePort::Close() {
+  if (!event_target_) {
+    return;
+  }
+  if (remove_environment_settings_change_observer_) {
+    std::move(remove_environment_settings_change_observer_).Run();
+  }
+  event_target_ = nullptr;
+}
+
+void MessagePort::PostMessage(const script::ValueHandleHolder& message) {
+  PostMessageSerialized(std::move(SerializeScriptValue(message)));
+}
+
+void MessagePort::PostMessageSerialized(
+    std::unique_ptr<script::DataBuffer> data_buffer) {
+  if (!event_target_ || !data_buffer) {
+    return;
+  }
   // TODO: Forward the location of the origating API call to the PostTask call.
   base::MessageLoop* message_loop =
       event_target_->environment_settings()->context()->message_loop();
-  if (message_loop) {
-    //   https://html.spec.whatwg.org/multipage/workers.html#handler-worker-onmessage
-    // TODO: Update MessageEvent to support more types. (b/227665847)
-    // TODO: Remove dependency of MessageEvent on net iobuffer (b/227665847)
-    message_loop->task_runner()->PostTask(
-        FROM_HERE,
-        base::Bind(
-            [](web::EventTarget* event_target, const std::string& messages) {
-              scoped_refptr<net::IOBufferWithSize> buf = base::WrapRefCounted(
-                  new net::IOBufferWithSize(messages.length()));
-              memcpy(buf->data(), messages.data(), messages.length());
-              if (event_target) {
-                event_target->DispatchEvent(new web::MessageEvent(
-                    base::Tokens::message(), web::MessageEvent::kText, buf));
-              }
-              LOG_IF(WARNING, !event_target)
-                  << "MessagePort event not dispatched "
-                     "because there is no EventTarget.";
-            },
-            base::Unretained(event_target_), messages));
+  if (!message_loop) {
+    return;
   }
+  //   https://html.spec.whatwg.org/multipage/workers.html#handler-worker-onmessage
+  // TODO: Update MessageEvent to support more types. (b/227665847)
+  // TODO: Remove dependency of MessageEvent on net iobuffer (b/227665847)
+  message_loop->task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MessagePort::DispatchMessage, AsWeakPtr(),
+                                std::move(data_buffer)));
+}
+
+void MessagePort::DispatchMessage(
+    std::unique_ptr<script::DataBuffer> data_buffer) {
+  event_target_->DispatchEvent(
+      new web::MessageEvent(base::Tokens::message(), std::move(data_buffer)));
 }
 
 }  // namespace web
diff --git a/cobalt/web/message_port.h b/cobalt/web/message_port.h
index 86ff17b..183e35a 100644
--- a/cobalt/web/message_port.h
+++ b/cobalt/web/message_port.h
@@ -15,8 +15,10 @@
 #ifndef COBALT_WEB_MESSAGE_PORT_H_
 #define COBALT_WEB_MESSAGE_PORT_H_
 
+#include <memory>
 #include <string>
 
+#include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "cobalt/base/tokens.h"
@@ -24,6 +26,7 @@
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/value_handle.h"
 #include "cobalt/script/wrappable.h"
+#include "cobalt/web/context.h"
 #include "cobalt/web/event_target.h"
 #include "cobalt/web/event_target_listener_info.h"
 #include "cobalt/web/message_event.h"
@@ -32,21 +35,29 @@
 namespace web {
 
 class MessagePort : public script::Wrappable,
-                    public base::SupportsWeakPtr<MessagePort> {
+                    public base::SupportsWeakPtr<MessagePort>,
+                    public Context::EnvironmentSettingsChangeObserver {
  public:
   explicit MessagePort(web::EventTarget* event_target);
   ~MessagePort();
   MessagePort(const MessagePort&) = delete;
   MessagePort& operator=(const MessagePort&) = delete;
 
+  void OnEnvironmentSettingsChanged(bool context_valid) override {
+    if (!context_valid) {
+      Close();
+    }
+  }
+
   // This may help for adding support of 'object'
   // void postMessage(any message, object transfer);
   // -> void PostMessage(const script::ValueHandleHolder& message,
   //                     script::Sequence<script::ValueHandle*> transfer) {}
-  void PostMessage(const std::string& message);
+  void PostMessage(const script::ValueHandleHolder& message);
+  void PostMessageSerialized(std::unique_ptr<script::DataBuffer> data_buffer);
 
   void Start() {}
-  void Close() {}
+  void Close();
 
   const web::EventTargetListenerInfo::EventListenerScriptValue* onmessage()
       const {
@@ -57,9 +68,11 @@
   void set_onmessage(
       const web::EventTargetListenerInfo::EventListenerScriptValue&
           event_listener) {
-    if (event_target_)
-      event_target_->SetAttributeEventListener(base::Tokens::message(),
-                                               event_listener);
+    if (!event_target_) {
+      return;
+    }
+    event_target_->SetAttributeEventListener(base::Tokens::message(),
+                                             event_listener);
   }
 
   const web::EventTargetListenerInfo::EventListenerScriptValue* onmessageerror()
@@ -71,16 +84,21 @@
   void set_onmessageerror(
       const web::EventTargetListenerInfo::EventListenerScriptValue&
           event_listener) {
-    if (event_target_)
-      event_target_->SetAttributeEventListener(base::Tokens::messageerror(),
-                                               event_listener);
+    if (!event_target_) {
+      return;
+    }
+    event_target_->SetAttributeEventListener(base::Tokens::messageerror(),
+                                             event_listener);
   }
 
   DEFINE_WRAPPABLE_TYPE(MessagePort);
 
  private:
+  void DispatchMessage(std::unique_ptr<script::DataBuffer> data_buffer);
+
   // The event target to dispatch events to.
   web::EventTarget* event_target_ = nullptr;
+  base::OnceClosure remove_environment_settings_change_observer_;
 };
 
 }  // namespace web
diff --git a/cobalt/web/message_port.idl b/cobalt/web/message_port.idl
index acacae8..86b9528 100644
--- a/cobalt/web/message_port.idl
+++ b/cobalt/web/message_port.idl
@@ -16,7 +16,7 @@
 // https://html.spec.whatwg.org/dev/web-messaging.html#message-ports
 interface MessagePort /* : EventTarget */ {
   // [RaisesException]
-  void postMessage(USVString message);
+  void postMessage(any message);
   // void postMessage(USVString message, sequence<USVString> transfer);
   // TODO: Support sequence<object>: b/218501774
   // void postMessage(any message, sequence<object> transfer);
diff --git a/cobalt/web/testing/stub_web_context.h b/cobalt/web/testing/stub_web_context.h
index f709d0e..dabc4b6 100644
--- a/cobalt/web/testing/stub_web_context.h
+++ b/cobalt/web/testing/stub_web_context.h
@@ -58,6 +58,11 @@
   }
   ~StubWebContext() final { blob_registry_.reset(); }
 
+  void AddEnvironmentSettingsChangeObserver(
+      Context::EnvironmentSettingsChangeObserver* observer) final {}
+  void RemoveEnvironmentSettingsChangeObserver(
+      Context::EnvironmentSettingsChangeObserver* observer) final {}
+
   // WebInstance
   //
   base::MessageLoop* message_loop() const final {
diff --git a/cobalt/web/window_or_worker_global_scope.cc b/cobalt/web/window_or_worker_global_scope.cc
index 26cfeff..f5a3fc3 100644
--- a/cobalt/web/window_or_worker_global_scope.cc
+++ b/cobalt/web/window_or_worker_global_scope.cc
@@ -14,7 +14,10 @@
 
 #include "cobalt/web/window_or_worker_global_scope.h"
 
+#include <utility>
+
 #include "cobalt/script/environment_settings.h"
+#include "cobalt/web/csp_delegate_factory.h"
 #include "cobalt/web/event_target.h"
 
 namespace cobalt {
@@ -28,12 +31,21 @@
 namespace web {
 WindowOrWorkerGlobalScope::WindowOrWorkerGlobalScope(
     script::EnvironmentSettings* settings, StatTracker* stat_tracker,
-    base::ApplicationState initial_state)
+    Options options)
     // Global scope object EventTargets require special handling for onerror
     // events, see EventTarget constructor for more details.
     : EventTarget(settings, kUnpackOnErrorEvents),
       crypto_(new Crypto()),
-      window_timers_(this, stat_tracker, debugger_hooks(), initial_state) {}
+      window_timers_(this, stat_tracker, debugger_hooks(),
+                     options.initial_state),
+      preflight_cache_(new loader::CORSPreflightCache()) {
+  std::unique_ptr<web::CspViolationReporter> violation_reporter(
+      new web::CspViolationReporter(this, options.post_sender));
+  csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
+      options.csp_enforcement_mode, std::move(violation_reporter),
+      environment_settings()->creation_url(), options.require_csp,
+      options.csp_policy_changed_callback, options.csp_insecure_allowed_token);
+}
 
 bool WindowOrWorkerGlobalScope::IsWindow() {
   return GetWrappableType() == base::GetTypeId<dom::Window>();
diff --git a/cobalt/web/window_or_worker_global_scope.h b/cobalt/web/window_or_worker_global_scope.h
index 6f079ca..b2c8ea4 100644
--- a/cobalt/web/window_or_worker_global_scope.h
+++ b/cobalt/web/window_or_worker_global_scope.h
@@ -15,13 +15,17 @@
 #ifndef COBALT_WEB_WINDOW_OR_WORKER_GLOBAL_SCOPE_H_
 #define COBALT_WEB_WINDOW_OR_WORKER_GLOBAL_SCOPE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "cobalt/loader/cors_preflight_cache.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/web/crypto.h"
+#include "cobalt/web/csp_delegate.h"
+#include "cobalt/web/csp_delegate_type.h"
 #include "cobalt/web/event_target.h"
 #include "cobalt/web/event_target_listener_info.h"
 #include "cobalt/web/navigator_base.h"
@@ -42,9 +46,35 @@
 // interfaces.
 class WindowOrWorkerGlobalScope : public EventTarget {
  public:
+  struct Options {
+    explicit Options(base::ApplicationState initial_state)
+        : initial_state(initial_state),
+          csp_enforcement_mode(web::kCspEnforcementEnable) {}
+
+    Options(base::ApplicationState initial_state,
+            const network_bridge::PostSender& post_sender,
+            csp::CSPHeaderPolicy require_csp,
+            web::CspEnforcementType csp_enforcement_mode,
+            base::Closure csp_policy_changed_callback,
+            int csp_insecure_allowed_token = 0)
+        : initial_state(initial_state),
+          post_sender(post_sender),
+          require_csp(require_csp),
+          csp_enforcement_mode(csp_enforcement_mode),
+          csp_policy_changed_callback(csp_policy_changed_callback),
+          csp_insecure_allowed_token(csp_insecure_allowed_token) {}
+
+    base::ApplicationState initial_state;
+    network_bridge::PostSender post_sender;
+    csp::CSPHeaderPolicy require_csp;
+    web::CspEnforcementType csp_enforcement_mode;
+    base::Closure csp_policy_changed_callback;
+    int csp_insecure_allowed_token;
+  };
+
   explicit WindowOrWorkerGlobalScope(script::EnvironmentSettings* settings,
                                      StatTracker* stat_tracker,
-                                     base::ApplicationState initial_state);
+                                     Options options);
   WindowOrWorkerGlobalScope(const WindowOrWorkerGlobalScope&) = delete;
   WindowOrWorkerGlobalScope& operator=(const WindowOrWorkerGlobalScope&) =
       delete;
@@ -68,6 +98,12 @@
     return nullptr;
   }
 
+  web::CspDelegate* csp_delegate() const { return csp_delegate_.get(); }
+
+  const scoped_refptr<loader::CORSPreflightCache> get_preflight_cache() {
+    return preflight_cache_;
+  }
+
   DEFINE_WRAPPABLE_TYPE(WindowOrWorkerGlobalScope);
 
   // Web API: WindowTimers (implements)
@@ -104,7 +140,10 @@
   web::WindowTimers window_timers_;
 
  private:
+  std::unique_ptr<web::CspDelegate> csp_delegate_;
   NavigatorBase* navigator_base_ = nullptr;
+  // Global preflight cache.
+  scoped_refptr<loader::CORSPreflightCache> preflight_cache_;
 };
 
 }  // namespace web
diff --git a/cobalt/websocket/web_socket.cc b/cobalt/websocket/web_socket.cc
index 13f2004..82db7a7 100644
--- a/cobalt/websocket/web_socket.cc
+++ b/cobalt/websocket/web_socket.cc
@@ -153,9 +153,9 @@
   return (all_protocols.size() == sub_protocols.size());
 }
 
-bool IsValidBinaryType(cobalt::web::MessageEvent::ResponseTypeCode code) {
-  return (code == cobalt::web::MessageEvent::kBlob) ||
-         (code == cobalt::web::MessageEvent::kArrayBuffer);
+bool IsValidBinaryType(cobalt::web::MessageEvent::ResponseType response_type) {
+  return (response_type == cobalt::web::MessageEvent::kBlob) ||
+         (response_type == cobalt::web::MessageEvent::kArrayBuffer);
 }
 
 }  // namespace
@@ -220,12 +220,12 @@
   // "arraybuffer", then set the IDL attribute to this new value.
   // Otherwise, throw a SyntaxError exception."
   base::StringPiece binary_type_string_piece(binary_type);
-  web::MessageEvent::ResponseTypeCode response_code =
-      web::MessageEvent::GetResponseTypeCode(binary_type_string_piece);
-  if (!IsValidBinaryType(response_code)) {
+  web::MessageEvent::ResponseType response_type =
+      web::MessageEvent::GetResponseType(binary_type_string_piece);
+  if (!IsValidBinaryType(response_type)) {
     web::DOMException::Raise(web::DOMException::kSyntaxErr, exception_state);
   } else {
-    binary_type_ = response_code;
+    binary_type_ = response_type;
   }
 }
 
@@ -416,12 +416,12 @@
 
 void WebSocket::OnReceivedData(bool is_text_frame,
                                scoped_refptr<net::IOBufferWithSize> data) {
-  web::MessageEvent::ResponseTypeCode response_type_code = binary_type_;
+  web::MessageEvent::ResponseType response_type = binary_type_;
   if (is_text_frame) {
-    response_type_code = web::MessageEvent::kText;
+    response_type = web::MessageEvent::kText;
   }
   this->DispatchEvent(
-      new web::MessageEvent(base::Tokens::message(), response_type_code, data));
+      new web::MessageEvent(base::Tokens::message(), response_type, data));
 }
 
 void WebSocket::OnWriteDone(uint64_t bytes_written) {
@@ -562,18 +562,13 @@
 }
 
 web::CspDelegate* WebSocket::csp_delegate() const {
-  DCHECK(settings_);
-  if (!settings_) {
-    return NULL;
-  }
-  dom::DOMSettings* dom_settings =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings_);
-  if (dom_settings && dom_settings->window() &&
-      dom_settings->window()->document()) {
-    return dom_settings->window()->document()->csp_delegate();
-  } else {
-    return NULL;
-  }
+  DCHECK(environment_settings());
+  DCHECK(environment_settings()->context());
+  DCHECK(environment_settings()->context()->GetWindowOrWorkerGlobalScope());
+  return environment_settings()
+      ->context()
+      ->GetWindowOrWorkerGlobalScope()
+      ->csp_delegate();
 }
 
 void WebSocket::Connect(const GURL& url,
diff --git a/cobalt/websocket/web_socket.h b/cobalt/websocket/web_socket.h
index 74909b7..2827dbb 100644
--- a/cobalt/websocket/web_socket.h
+++ b/cobalt/websocket/web_socket.h
@@ -201,7 +201,7 @@
   // https://www.w3.org/TR/websockets/#dom-websocket-readystate
   uint16 ready_state_;
   // https://www.w3.org/TR/websockets/#dom-websocket-binarytype
-  web::MessageEvent::ResponseTypeCode binary_type_;
+  web::MessageEvent::ResponseType binary_type_;
   // https://www.w3.org/TR/websockets/#dom-websocket-extensions
   std::string extensions_;
   // https://www.w3.org/TR/websockets/#dom-websocket-protocol
diff --git a/cobalt/websocket/web_socket.idl b/cobalt/websocket/web_socket.idl
index d4e642c..76bb69d 100644
--- a/cobalt/websocket/web_socket.idl
+++ b/cobalt/websocket/web_socket.idl
@@ -40,7 +40,7 @@
                                optional DOMString reason);
 
   // messaging
-           attribute EventHandler onmessage;
+  attribute EventHandler onmessage;
   [RaisesException] attribute DOMString binaryType;
   [RaisesException] void send(DOMString data);
   [RaisesException] void send(ArrayBuffer data);
diff --git a/cobalt/worker/client.idl b/cobalt/worker/client.idl
index fe68a12..8d2e4a9 100644
--- a/cobalt/worker/client.idl
+++ b/cobalt/worker/client.idl
@@ -19,7 +19,7 @@
   readonly attribute FrameType frameType;
   readonly attribute DOMString id;
   readonly attribute ClientType type;
-  void postMessage(USVString message);
+  void postMessage(any message);
   // TODO: Support sequence<object>: b/218501774
   // void postMessage(any message, sequence<object> transfer);
   // TODO: Support overloads with dictionary parameter: b/218506730
diff --git a/cobalt/worker/dedicated_worker.cc b/cobalt/worker/dedicated_worker.cc
index 69b0300..0dff482 100644
--- a/cobalt/worker/dedicated_worker.cc
+++ b/cobalt/worker/dedicated_worker.cc
@@ -98,7 +98,7 @@
 // void postMessage(any message, object transfer);
 // -> void PostMessage(const script::ValueHandleHolder& message,
 //                     script::Sequence<script::ValueHandle*> transfer) {}
-void DedicatedWorker::PostMessage(const std::string& message) {
+void DedicatedWorker::PostMessage(const script::ValueHandleHolder& message) {
   DCHECK(worker_);
   worker_->PostMessage(message);
 }
diff --git a/cobalt/worker/dedicated_worker.h b/cobalt/worker/dedicated_worker.h
index 90c9547..b1264d8 100644
--- a/cobalt/worker/dedicated_worker.h
+++ b/cobalt/worker/dedicated_worker.h
@@ -47,7 +47,7 @@
   // void postMessage(any message, object transfer);
   // -> void PostMessage(const script::ValueHandleHolder& message,
   //                     script::Sequence<script::ValueHandle*> transfer) {}
-  void PostMessage(const std::string& message);
+  void PostMessage(const script::ValueHandleHolder& message);
 
   const EventListenerScriptValue* onmessage() const {
     return GetAttributeEventListener(base::Tokens::message());
diff --git a/cobalt/worker/dedicated_worker_global_scope.cc b/cobalt/worker/dedicated_worker_global_scope.cc
index 420dd3a..a03e8a0 100644
--- a/cobalt/worker/dedicated_worker_global_scope.cc
+++ b/cobalt/worker/dedicated_worker_global_scope.cc
@@ -72,7 +72,8 @@
   }
 }
 
-void DedicatedWorkerGlobalScope::PostMessage(const std::string& message) {
+void DedicatedWorkerGlobalScope::PostMessage(
+    const script::ValueHandleHolder& message) {
   base::polymorphic_downcast<worker::WorkerSettings*>(environment_settings())
       ->message_port()
       ->PostMessage(message);
diff --git a/cobalt/worker/dedicated_worker_global_scope.h b/cobalt/worker/dedicated_worker_global_scope.h
index 6b140f5..3793608 100644
--- a/cobalt/worker/dedicated_worker_global_scope.h
+++ b/cobalt/worker/dedicated_worker_global_scope.h
@@ -52,7 +52,7 @@
   void set_name(const std::string& name) { name_ = name; }
   std::string name() { return name_; }
 
-  void PostMessage(const std::string& message);
+  void PostMessage(const script::ValueHandleHolder& message);
   void Close() {}
 
   const web::EventTargetListenerInfo::EventListenerScriptValue* onmessage() {
diff --git a/cobalt/worker/dedicated_worker_global_scope.idl b/cobalt/worker/dedicated_worker_global_scope.idl
index 7f3c6a7..1b42584 100644
--- a/cobalt/worker/dedicated_worker_global_scope.idl
+++ b/cobalt/worker/dedicated_worker_global_scope.idl
@@ -20,7 +20,7 @@
 ] interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
   [Replaceable] readonly attribute DOMString name;
 
-  void postMessage(USVString message);
+  void postMessage(any message);
   // TODO: Support sequence<object>: b/218501774
   // void postMessage(any message, sequence<object> transfer);
   // TODO: Support overloads with dictionary parameter: b/218506730
diff --git a/cobalt/worker/service_worker.h b/cobalt/worker/service_worker.h
index 1e39dfe..a281746 100644
--- a/cobalt/worker/service_worker.h
+++ b/cobalt/worker/service_worker.h
@@ -43,7 +43,7 @@
 
   // Web API: ServiceWorker
   //
-  void PostMessage(const std::string& message) {
+  void PostMessage(const script::ValueHandleHolder& message) {
     DCHECK(message_port_);
     if (worker_->worker_global_scope()) message_port_->PostMessage(message);
   }
diff --git a/cobalt/worker/service_worker.idl b/cobalt/worker/service_worker.idl
index f76c706..b1b9602 100644
--- a/cobalt/worker/service_worker.idl
+++ b/cobalt/worker/service_worker.idl
@@ -17,7 +17,7 @@
 [Exposed = (Window, ServiceWorker)] interface ServiceWorker : EventTarget {
 
   // Todo: Implement postMessage b/219986442
-  void postMessage(USVString message);
+  void postMessage(any message);
   // void postMessage(any message, sequence<object> transfer);
   // void postMessage(any message, optional PostMessageOptions options = {});
 
diff --git a/cobalt/worker/worker.cc b/cobalt/worker/worker.cc
index 495a95d..b8e283b 100644
--- a/cobalt/worker/worker.cc
+++ b/cobalt/worker/worker.cc
@@ -31,6 +31,11 @@
 #include "cobalt/worker/worker_options.h"
 #include "cobalt/worker/worker_settings.h"
 
+#include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/v8c_value_handle.h"
+#include "v8/include/v8.h"
+
 namespace cobalt {
 namespace worker {
 
@@ -313,23 +318,20 @@
   // TODO(b/226640425): Implement this when Message Ports can be entangled.
 }
 
-// void postMessage(any message, object transfer);
-// -> void PostMessage(const script::ValueHandleHolder& message,
-//                     script::Sequence<script::ValueHandle*> transfer) {}
-void Worker::PostMessage(const std::string& message) {
+void Worker::PostMessage(const script::ValueHandleHolder& message) {
   DCHECK(message_loop());
+  auto serialized_message = script::SerializeScriptValue(message);
   if (base::MessageLoop::current() != message_loop()) {
     // Block until the worker thread is ready to execute code to handle the
     // event.
     execution_ready_.Wait();
     message_loop()->task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&Worker::PostMessage, base::Unretained(this), message));
-    return;
+        FROM_HERE, base::BindOnce(&web::MessagePort::PostMessageSerialized,
+                                  message_port()->AsWeakPtr(),
+                                  std::move(serialized_message)));
   } else {
     DCHECK(execution_ready_.IsSignaled());
-    DCHECK(message_port());
-    if (message_port()) message_port()->PostMessage(message);
+    message_port()->PostMessageSerialized(std::move(serialized_message));
   }
 }
 
diff --git a/cobalt/worker/worker.h b/cobalt/worker/worker.h
index fe78f28..2c488ec 100644
--- a/cobalt/worker/worker.h
+++ b/cobalt/worker/worker.h
@@ -17,6 +17,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/macros.h"
@@ -83,7 +84,7 @@
     return web_agent_ ? web_agent_->message_loop() : nullptr;
   }
 
-  void PostMessage(const std::string& message);
+  void PostMessage(const script::ValueHandleHolder& message);
 
   // From base::MessageLoop::DestructionObserver.
   void WillDestroyCurrentMessageLoop() override;
diff --git a/cobalt/worker/worker.idl b/cobalt/worker/worker.idl
index b9db12b..e05669f 100644
--- a/cobalt/worker/worker.idl
+++ b/cobalt/worker/worker.idl
@@ -23,7 +23,7 @@
 interface Worker : EventTarget {
   void terminate();
 
-  void postMessage(USVString message);
+  void postMessage(any message);
   // TODO: Support sequence<object>: b/218501774
   // void postMessage(any message, sequence<object> transfer);
   // TODO: Support overloads with dictionary parameter: b/218506730
diff --git a/cobalt/worker/worker_global_scope.cc b/cobalt/worker/worker_global_scope.cc
index edc69fb..3316581 100644
--- a/cobalt/worker/worker_global_scope.cc
+++ b/cobalt/worker/worker_global_scope.cc
@@ -185,9 +185,11 @@
 WorkerGlobalScope::WorkerGlobalScope(script::EnvironmentSettings* settings)
     : web::WindowOrWorkerGlobalScope(
           settings, /*stat_tracker=*/NULL,
-          // TODO (b/233788170): once application state is
-          // available, update this to use the actual state.
-          base::ApplicationState::kApplicationStateStarted),
+          // Using default options for CSP
+          web::WindowOrWorkerGlobalScope::Options(
+              // TODO (b/233788170): once application state is
+              // available, update this to use the actual state.
+              base::ApplicationState::kApplicationStateStarted)),
       location_(new WorkerLocation(settings->creation_url())),
       navigator_(new WorkerNavigator(settings)) {
   set_navigator_base(navigator_);
diff --git a/cobalt/xhr/xml_http_request.cc b/cobalt/xhr/xml_http_request.cc
index 6f16335..56de1ee 100644
--- a/cobalt/xhr/xml_http_request.cc
+++ b/cobalt/xhr/xml_http_request.cc
@@ -168,6 +168,18 @@
 // 5. If request's redirect count is twenty, return a network error.
 const int kRedirectLimit = 20;
 
+std::string ClipUrl(const GURL& url, size_t length) {
+  const std::string& spec = url.possibly_invalid_spec();
+  if (spec.size() < length) {
+    return spec;
+  }
+
+  size_t remain = length - 5;
+  size_t head = remain / 2;
+  size_t tail = remain - head;
+
+  return spec.substr(0, head) + "[...]" + spec.substr(spec.size() - tail);
+}
 }  // namespace
 
 bool XMLHttpRequestImpl::verbose_ = false;
@@ -417,7 +429,7 @@
     return;
   }
 
-  web::CspDelegate* csp = this->csp_delegate();
+  web::CspDelegate* csp = csp_delegate();
   if (csp && !csp->CanLoad(web::CspDelegate::kXhr, request_url_, false)) {
     web::DOMException::Raise(web::DOMException::kSecurityErr, exception_state);
     return;
@@ -1044,7 +1056,7 @@
     return;
   }
   // This is a redirect. Re-check the CSP.
-  web::CspDelegate* csp = this->csp_delegate();
+  web::CspDelegate* csp = csp_delegate();
   if (csp &&
       !csp->CanLoad(web::CspDelegate::kXhr, new_url, true /* is_redirect */)) {
     HandleRequestError(XMLHttpRequest::kNetworkError);
@@ -1099,11 +1111,10 @@
 }
 
 web::CspDelegate* XMLHttpRequestImpl::csp_delegate() const {
-  // TODO (b/239733363): csp_delegate is currently available through window.
-  // Refactor to make it available outside of window then implement this. At
-  // that point, there should be no more need to override this function in
-  // DOMXMLHttpRequestImpl.
-  return NULL;
+  DCHECK(settings_);
+  DCHECK(settings_->context());
+  DCHECK(settings_->context()->GetWindowOrWorkerGlobalScope());
+  return settings_->context()->GetWindowOrWorkerGlobalScope()->csp_delegate();
 }
 
 void XMLHttpRequestImpl::TerminateRequest() {
@@ -1236,6 +1247,7 @@
 
 void XMLHttpRequestImpl::StartRequest(const std::string& request_body) {
   TRACK_MEMORY_SCOPE("XHR");
+  LOG(INFO) << "Fetching: " << ClipUrl(request_url_, 200);
 
   response_array_buffer_reference_.reset();
 
@@ -1283,9 +1295,35 @@
     url_fetcher_->AddExtraRequestHeader("Origin:" + origin_.SerializedOrigin());
   }
   bool dopreflight = false;
-  // TODO (b/239733363): Include CORS functionality once preflight cache is
-  // accessible outside of window. At that point, there should be no more need
-  // to override this function in DOMXMLHttpRequestImpl.
+  if (is_cross_origin_) {
+    corspreflight_.reset(new cobalt::loader::CORSPreflight(
+        request_url_, method_, network_module,
+        base::Bind(&DOMXMLHttpRequestImpl::CORSPreflightSuccessCallback,
+                   base::Unretained(this)),
+        origin_.SerializedOrigin(),
+        base::Bind(&DOMXMLHttpRequestImpl::CORSPreflightErrorCallback,
+                   base::Unretained(this)),
+        settings_->context()
+            ->GetWindowOrWorkerGlobalScope()
+            ->get_preflight_cache()));
+    corspreflight_->set_headers(request_headers_);
+    // For cross-origin requests, don't send or save auth data / cookies unless
+    // withCredentials was set.
+    // To make a cross-origin request, add origin, referrer source, credentials,
+    // omit credentials flag, force preflight flag
+    if (!with_credentials_) {
+      const uint32 kDisableCookiesLoadFlags =
+          net::LOAD_NORMAL | net::LOAD_DO_NOT_SAVE_COOKIES |
+          net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA;
+      url_fetcher_->SetLoadFlags(kDisableCookiesLoadFlags);
+    } else {
+      // For credentials mode: If the withCredentials attribute value is true,
+      // "include", and "same-origin" otherwise.
+      corspreflight_->set_credentials_mode_is_include(true);
+    }
+    corspreflight_->set_force_preflight(upload_listener_);
+    dopreflight = corspreflight_->Send();
+  }
   DLOG_IF(INFO, verbose()) << __FUNCTION__ << *xhr_;
   if (!dopreflight) {
     DCHECK(settings_->context()->network_module());
@@ -1413,99 +1451,6 @@
       request_url_.spec());
 }
 
-web::CspDelegate* DOMXMLHttpRequestImpl::csp_delegate() const {
-  DCHECK(settings_);
-  if (settings_->window() && settings_->window()->document()) {
-    return settings_->window()->document()->csp_delegate();
-  } else {
-    return NULL;
-  }
-}
-
-void DOMXMLHttpRequestImpl::StartRequest(const std::string& request_body) {
-  TRACK_MEMORY_SCOPE("XHR");
-
-  response_array_buffer_reference_.reset();
-
-  network::NetworkModule* network_module =
-      settings_->context()->fetcher_factory()->network_module();
-  url_fetcher_ = net::URLFetcher::Create(request_url_, method_, xhr_);
-  ++url_fetcher_generation_;
-  url_fetcher_->SetRequestContext(network_module->url_request_context_getter());
-  if (fetch_callback_) {
-    response_body_ = new URLFetcherResponseWriter::Buffer(
-        URLFetcherResponseWriter::Buffer::kString);
-    response_body_->DisablePreallocate();
-  } else {
-    response_body_ = new URLFetcherResponseWriter::Buffer(
-        response_type_ == XMLHttpRequest::kArrayBuffer
-            ? URLFetcherResponseWriter::Buffer::kArrayBuffer
-            : URLFetcherResponseWriter::Buffer::kString);
-  }
-  std::unique_ptr<net::URLFetcherResponseWriter> download_data_writer(
-      new URLFetcherResponseWriter(response_body_));
-  url_fetcher_->SaveResponseWithWriter(std::move(download_data_writer));
-  // Don't retry, let the caller deal with it.
-  url_fetcher_->SetAutomaticallyRetryOn5xx(false);
-  url_fetcher_->SetExtraRequestHeaders(request_headers_.ToString());
-
-  // We want to do cors check and preflight during redirects
-  url_fetcher_->SetStopOnRedirect(true);
-
-  if (request_body.size()) {
-    // If applicable, the request body Content-Type is already set in
-    // request_headers.
-    url_fetcher_->SetUploadData("", request_body);
-  }
-
-  // We let data url fetch resources freely but with no response headers.
-  is_data_url_ = is_data_url_ || request_url_.SchemeIs("data");
-  is_cross_origin_ = (is_redirect_ && is_cross_origin_) ||
-                     (origin_ != loader::Origin(request_url_) && !is_data_url_);
-  is_redirect_ = false;
-  // If the CORS flag is set, httpRequest’s method is neither `GET` nor `HEAD`
-  // or httpRequest’s mode is "websocket", then append `Origin`/httpRequest’s
-  // origin, serialized and UTF-8 encoded, to httpRequest’s header list.
-  if (is_cross_origin_ ||
-      (method_ != net::URLFetcher::GET && method_ != net::URLFetcher::HEAD)) {
-    url_fetcher_->AddExtraRequestHeader("Origin:" + origin_.SerializedOrigin());
-  }
-  bool dopreflight = false;
-  if (is_cross_origin_) {
-    corspreflight_.reset(new cobalt::loader::CORSPreflight(
-        request_url_, method_, network_module,
-        base::Bind(&DOMXMLHttpRequestImpl::CORSPreflightSuccessCallback,
-                   base::Unretained(this)),
-        origin_.SerializedOrigin(),
-        base::Bind(&DOMXMLHttpRequestImpl::CORSPreflightErrorCallback,
-                   base::Unretained(this)),
-        settings_->window()->get_preflight_cache()));
-    corspreflight_->set_headers(request_headers_);
-    // For cross-origin requests, don't send or save auth data / cookies unless
-    // withCredentials was set.
-    // To make a cross-origin request, add origin, referrer source, credentials,
-    // omit credentials flag, force preflight flag
-    if (!with_credentials_) {
-      const uint32 kDisableCookiesLoadFlags =
-          net::LOAD_NORMAL | net::LOAD_DO_NOT_SAVE_COOKIES |
-          net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA;
-      url_fetcher_->SetLoadFlags(kDisableCookiesLoadFlags);
-    } else {
-      // For credentials mode: If the withCredentials attribute value is true,
-      // "include", and "same-origin" otherwise.
-      corspreflight_->set_credentials_mode_is_include(true);
-    }
-    corspreflight_->set_force_preflight(upload_listener_);
-    dopreflight = corspreflight_->Send();
-  }
-  DLOG_IF(INFO, verbose()) << __FUNCTION__ << *xhr_;
-  if (!dopreflight) {
-    DCHECK(settings_->context()->network_module());
-    StartURLFetcher(settings_->context()->network_module()->max_network_delay(),
-                    url_fetcher_generation_);
-  }
-}
-
 // https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#document-response-entity-body
 scoped_refptr<dom::Document>
 DOMXMLHttpRequestImpl::GetDocumentResponseEntityBody() {
diff --git a/cobalt/xhr/xml_http_request.h b/cobalt/xhr/xml_http_request.h
index e3510f0..046e90f 100644
--- a/cobalt/xhr/xml_http_request.h
+++ b/cobalt/xhr/xml_http_request.h
@@ -446,12 +446,7 @@
 
   void GetLoadTimingInfoAndCreateResourceTiming() override;
 
- protected:
-  web::CspDelegate* csp_delegate() const override;
-
  private:
-  void StartRequest(const std::string& request_body) override;
-
   scoped_refptr<dom::Document> GetDocumentResponseEntityBody();
 
   void XMLDecoderLoadCompleteCallback(
diff --git a/docker-compose.yml b/docker-compose.yml
index 1f5f4e3..08f9df6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -40,6 +40,18 @@
   IS_DOCKER: 1
   PYTHONPATH: /code
 
+x-shared-docsite-definitions: &shared-docsite-definitions
+  <<: *build-common-definitions
+  build:
+    context: ./docker/docsite
+    dockerfile: Dockerfile
+    args:
+      - USER=root
+      - UID=2000
+      - GID=2000
+  environment:
+    <<: *shared-build-env
+
 x-shared-unittest-definitions: &shared-unittest-definitions
   stdin_open: true
   tty: true
@@ -125,6 +137,18 @@
       - base-bionic
     scale: 0
 
+  docsite:
+    <<: *shared-docsite-definitions
+    image: cobalt-build-docsite
+    ports:
+      # The docsite webapp runs on port 4000.
+      - "4000:4000"
+
+  docsite-deploy:
+    <<: *shared-docsite-definitions
+    image: cobalt-build-docsite-deploy
+    entrypoint: ["/code/third_party/repo-publishing-toolkit-local/preview-site.sh", "deploy"]
+
   stub:
     <<: *build-common-definitions
     build:
@@ -148,7 +172,7 @@
       <<: *shared-build-env
       PLATFORM: linux-x64x11
       CONFIG: ${CONFIG:-debug}
-      TARGET: ${TARGET:-cobalt_deploy}
+      TARGET: ${TARGET:-cobalt_install}
 
   linux-x64x11-bionic:
     <<: *common-definitions
diff --git a/docker/docsite/Dockerfile b/docker/docsite/Dockerfile
index fc278cc..02b4681 100644
--- a/docker/docsite/Dockerfile
+++ b/docker/docsite/Dockerfile
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM ruby:2.7-buster
+ARG FROM_IMAGE
+FROM ${FROM_IMAGE:-cobalt-build-base}
 
 ARG USER
 ARG UID
@@ -20,14 +21,13 @@
 
 RUN apt update -qqy \
     && apt install -qqy --no-install-recommends \
+        build-essential \
         bundler \
         doxygen \
         git \
-        nodejs \
-        python \
-        # Required for GN build.
-        python3 \
-        python3-requests \
+        python-pygments \
+        ruby2.5-dev \
+        ruby-execjs \
     && apt-get clean autoclean \
     && apt-get autoremove -y --purge \
     && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
@@ -37,27 +37,19 @@
 
 RUN bundle install --gemfile=/app/Gemfile
 
-# === Get GN via CIPD
-ARG GN_SHA256SUM="af7b2dcb3905bca56655e12131b365f1cba8e159db80d2022330c4f522fab2ef  /tmp/gn.zip"
-ARG GN_HASH=r3styzkFvKVmVeEhMbNl8cuo4VnbgNICIzDE9SL6su8C
-
-RUN curl --location --silent --output /tmp/gn.zip \
-    "https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/${GN_HASH}" \
-    && echo ${GN_SHA256SUM} | sha256sum --check \
-    && unzip /tmp/gn.zip -d /usr/local/bin \
-    && rm /tmp/gn.zip
-
 # We create and use a non-root user explicitly so that the generated and
 # modified files maintain the same permissions as the user that launched the
 # Docker container.
 RUN addgroup --group --gid "${GID}" defaultgroup \
-    && adduser --disabled-password --gecos '' --uid "${UID}" --gid "${GID}" ${USER:-defaultuser}
+    && adduser --disabled-password --gecos '' --uid "${UID}" --gid "${GID}" defaultuser
 
-# Allow the new uses to run gn and create a descriptive out directory.
+# The new user must be able to execute gn.
 RUN chmod a+x /usr/local/bin/gn
+
+# Create an out directory with a descriptive name for gn.
 RUN mkdir /project_out_dir \
     && chown ${USER:-defaultuser}:defaultgroup /project_out_dir
 
 USER ${USER:-defaultuser}
 
-CMD /cobalt/third_party/repo-publishing-toolkit-local/preview-site.sh
+CMD /code/third_party/repo-publishing-toolkit-local/preview-site.sh run
diff --git a/docker/docsite/README.md b/docker/docsite/README.md
index 06ea6fc..58177d1 100644
--- a/docker/docsite/README.md
+++ b/docker/docsite/README.md
@@ -4,25 +4,14 @@
 the current working directory, which should be the root of the Cobalt
 repository, if that environment variable is not set.
 
-When the `docsite` service is run, the container will then host the updated
-cobalt.dev site locally on port `4000`.
+When the `docsite` service is run, the container will generate the site and host
+the updated cobalt.dev site locally on port `4000`.
 
-When the `docsite-build-only` service is run, the container will build and
+When the `docsite-deploy` service is run, the container will build and
 output the site content in the directory `out/deploy/www` at the root of the
 Cobalt directory, and copy the app.yaml deployable into `out/deploy`.
 
 # How To Run
-docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g) SERVICE
+docker-compose build --no-cache --build-arg USER=defaultuser --build-arg UID=$(id -u) --build-arg GID=$(id -g) SERVICE
 
 docker-compose up SERVICE
-
-# Notes
-
-A separate docker-compose.yml was created due to the root docker-compose.yml
-being unable to locate the Gemfile in the Docker build context.
-
-The Gemfile was copied from third_party/repo-publishing-toolkit/Gemfile because
-we need to run `bundle install` with elevated permissions but if we let the
-`preview-site.sh`, which normally installs gems, run with elevated permissions
-the resulting files added or modified would not be accessible to the user who
-ran the Docker container.
diff --git a/docker/docsite/docker-compose.yml b/docker/docsite/docker-compose.yml
deleted file mode 100644
index 02e0aa5..0000000
--- a/docker/docsite/docker-compose.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2021 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-version: '2.4'
-
-services:
-  docsite:
-    build:
-      context: .
-      dockerfile: Dockerfile
-    container_name: docsite
-    environment:
-      - IS_DOCKER=1
-      - PYTHONPATH=/cobalt
-    ports:
-      - "4000:4000"
-    volumes:
-      # We use ../../ when the environment variable COBALT_SRC is not set since
-      # this file is located two directories under the root of the repository.
-      - ${COBALT_SRC:-../../}:/cobalt/
-
-  docsite-build-only:
-    container_name: docsite-build-only
-    environment:
-      # SITE_DST specifies where the files required to deploy the site should be
-      # placed, and by default this is under the "out" directory.
-      - SITE_DST=${SITE_DST:-/cobalt/out/deploy/www}
-    extends:
-      service: docsite
diff --git a/docker/linux/base/Dockerfile b/docker/linux/base/Dockerfile
index 84368e4..50dd9e0 100644
--- a/docker/linux/base/Dockerfile
+++ b/docker/linux/base/Dockerfile
@@ -27,6 +27,7 @@
         python3-pip \
         python3-requests \
         python3-setuptools \
+        python3-six \
         git ccache \
         curl xz-utils \
     && /opt/clean-after-apt.sh
diff --git a/net/disk_cache/cobalt/cobalt_backend_impl.cc b/net/disk_cache/cobalt/cobalt_backend_impl.cc
index 76c845a..8f58f6d 100644
--- a/net/disk_cache/cobalt/cobalt_backend_impl.cc
+++ b/net/disk_cache/cobalt/cobalt_backend_impl.cc
@@ -54,13 +54,34 @@
 }
 
 void ReadDiskCacheSize(
-    cobalt::persistent_storage::PersistentSettings* settings) {
+    cobalt::persistent_storage::PersistentSettings* settings,
+    int64_t max_bytes) {
+  auto total_size = 0;
+  disk_cache::ResourceTypeMetadata kTypeMetadataNew[disk_cache::kTypeCount];
+
   for (int i = 0; i < disk_cache::kTypeCount; i++) {
     auto metadata = disk_cache::kTypeMetadata[i];
     uint32_t bucket_size =
         static_cast<uint32_t>(settings->GetPersistentSettingAsDouble(
             metadata.directory, metadata.max_size_bytes));
-    disk_cache::kTypeMetadata[i] = {metadata.directory, bucket_size};
+    kTypeMetadataNew[i] = {metadata.directory, bucket_size};
+
+    total_size += bucket_size;
+  }
+
+  // Check if PersistentSettings values are valid and can replace the disk_cache::kTypeMetadata.
+  if (total_size <= max_bytes) {
+    std::copy(std::begin(kTypeMetadataNew), std::end(kTypeMetadataNew), std::begin(disk_cache::kTypeMetadata));
+    return;
+  }
+
+  // PersistentSettings values are invalid and will be replaced by the default values in
+  // disk_cache::kTypeMetadata.
+  for (int i = 0; i < disk_cache::kTypeCount; i++) {
+    auto metadata = disk_cache::kTypeMetadata[i];
+    settings->SetPersistentSetting(
+            metadata.directory,
+            std::make_unique<base::Value>(static_cast<double>(metadata.max_size_bytes)));
   }
 }
 
@@ -76,7 +97,7 @@
   persistent_settings_ =
       std::make_unique<cobalt::persistent_storage::PersistentSettings>(
           kPersistentSettingsJson, base::MessageLoop::current()->task_runner());
-  ReadDiskCacheSize(persistent_settings_.get());
+  ReadDiskCacheSize(persistent_settings_.get(), max_bytes);
 
   // Initialize disk backend for each resource type.
   int64_t total_size = 0;
diff --git a/net/disk_cache/cobalt/resource_type.h b/net/disk_cache/cobalt/resource_type.h
index b6b5cc7..60421ba 100644
--- a/net/disk_cache/cobalt/resource_type.h
+++ b/net/disk_cache/cobalt/resource_type.h
@@ -37,13 +37,13 @@
   uint32_t max_size_bytes;
 };
 
-static uint32_t kInitialBytes = 3 * 1024 * 1024;
+static uint32_t kInitialBytes = static_cast<uint32_t> (3 * 1024 * 1024);
 // These values are updated on start up in application.cc, using the
 // persisted values saved in settings.json.
 static ResourceTypeMetadata kTypeMetadata[] = {
     {"other", kInitialBytes},         {"html", kInitialBytes},
     {"css", kInitialBytes},           {"image", kInitialBytes},
-    {"font", kInitialBytes},          {"splash", kInitialBytes},
+    {"font", kInitialBytes},          {"splash", 2 * 1024 * 1024},
     {"uncompiled_js", kInitialBytes}, {"compiled_js", kInitialBytes},
 };
 
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index da934ee..9f13b95 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -13,6 +13,7 @@
 #include <algorithm>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -61,6 +62,15 @@
 
 namespace {
 
+#if defined(STARBOARD)
+// Default allowlist based off MIME types associated with top
+// resource types defined in resource_type.h.
+static const char* const kMimeTypesCacheAllowlist[] = {
+    "text/html", "text/css",      "image/gif",  "image/jpeg",
+    "image/png", "image/svg+xml", "image/webp", "font/otf",
+    "font/ttf",  "font/woff",     "font/woff2", "text/javascript"};
+#endif
+
 constexpr TimeDelta kStaleRevalidateTimeout = TimeDelta::FromSeconds(60);
 
 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6
@@ -3070,7 +3080,27 @@
   // (even though the cert status contains the actual errors) and no SSL
   // blocking page is shown.  An alternative would be to reverse-map the cert
   // status to a net error and replay the net error.
-  if ((response_.headers->HasHeaderValue("cache-control", "no-store")) ||
+
+#if defined(STARBOARD)
+  // Only allow caching for specific mime types.
+  std::string mime_type;
+  response_.headers->GetMimeType(&mime_type);
+  // TODO(b/243727663): Empty mime types should not get cached either.
+  bool is_allowed_mime_type = mime_type.empty();
+  if (!is_allowed_mime_type) {
+    for (auto allowed_type : kMimeTypesCacheAllowlist) {
+      if (mime_type.compare(allowed_type) == 0) {
+        is_allowed_mime_type = true;
+        break;
+      }
+    }
+  }
+#else
+  bool is_allowed_mime_type = true;
+#endif
+
+  if (!is_allowed_mime_type ||
+      (response_.headers->HasHeaderValue("cache-control", "no-store")) ||
       IsCertStatusError(response_.ssl_info.cert_status)) {
     bool stopped = StopCachingImpl(false);
     DCHECK(stopped);
diff --git a/net/test/embedded_test_server/default_handlers.cc b/net/test/embedded_test_server/default_handlers.cc
index 06eeb1d..f8ca3c5 100644
--- a/net/test/embedded_test_server/default_handlers.cc
+++ b/net/test/embedded_test_server/default_handlers.cc
@@ -102,7 +102,12 @@
 
   http_response->AddCustomHeader("Vary", vary);
   http_response->set_content(content);
+#if defined(STARBOARD)
+  // Cobalt does not currently support text/plain caching.
+  http_response->set_content_type("text/html");
+#else
   http_response->set_content_type("text/plain");
+#endif
   http_response->AddCustomHeader("Cache-Control", cache_control);
   return std::move(http_response);
 }
diff --git a/starboard/android/shared/audio_renderer_passthrough.cc b/starboard/android/shared/audio_renderer_passthrough.cc
index b2f7671..7056e8d 100644
--- a/starboard/android/shared/audio_renderer_passthrough.cc
+++ b/starboard/android/shared/audio_renderer_passthrough.cc
@@ -349,7 +349,16 @@
   SbTime playback_time =
       seek_to_time_ + playback_head_position * kSbTimeSecond /
                           audio_sample_info_.samples_per_second;
-  if (paused_ || playback_rate_ == 0.0) {
+
+  // When underlying AudioTrack is paused, we use returned playback time
+  // directly. Note that we should not use |paused_| or |playback_rate_| here.
+  // As we sync audio sink state on |audio_track_thread_|, when |paused_| is set
+  // to false, the underlying AudioTrack may still be paused. In that case, the
+  // returned playback time and last frame consumed time would be out of date.
+  // For example, when resume the playback, if we call GetAudioTimestamp()
+  // before calling AudioTrack.Play(), the returned playback time and last frame
+  // consumed time would be the same as at when we pause the video.
+  if (audio_track_paused_) {
     return playback_time;
   }
 
@@ -358,6 +367,10 @@
   SB_LOG_IF(WARNING, now < updated_at)
       << "now (" << now << ") is not greater than updated_at (" << updated_at
       << ").";
+  SB_LOG_IF(WARNING, now - updated_at > kSbTimeSecond)
+      << "Elapsed time (" << now - updated_at
+      << ") is greater than 1s. (playback_time " << playback_time << ")";
+
   playback_time += std::max<SbTime>(now - updated_at, 0);
 
   return playback_time;
@@ -469,11 +482,13 @@
   if (previous_state.playing() != current_state.playing()) {
     if (current_state.playing()) {
       audio_track_bridge_->Play();
+      audio_track_paused_ = false;
       SB_LOG(INFO) << "Played on AudioTrack thread.";
       ScopedLock scoped_lock(mutex_);
       stop_called_ = false;
     } else {
       audio_track_bridge_->Pause();
+      audio_track_paused_ = true;
       SB_LOG(INFO) << "Paused on AudioTrack thread.";
     }
   }
diff --git a/starboard/android/shared/audio_renderer_passthrough.h b/starboard/android/shared/audio_renderer_passthrough.h
index 58a073c..2b96d52 100644
--- a/starboard/android/shared/audio_renderer_passthrough.h
+++ b/starboard/android/shared/audio_renderer_passthrough.h
@@ -15,6 +15,7 @@
 #ifndef STARBOARD_ANDROID_SHARED_AUDIO_RENDERER_PASSTHROUGH_H_
 #define STARBOARD_ANDROID_SHARED_AUDIO_RENDERER_PASSTHROUGH_H_
 
+#include <atomic>
 #include <memory>
 #include <queue>
 
@@ -137,6 +138,8 @@
   JobToken update_status_and_write_data_token_;
   int64_t total_frames_written_on_audio_track_thread_ = 0;
 
+  std::atomic_bool audio_track_paused_{true};
+
   std::unique_ptr<JobThread> audio_track_thread_;
   std::unique_ptr<AudioTrackBridge> audio_track_bridge_;
 };
diff --git a/starboard/contrib/linux/stadia/main.cc b/starboard/contrib/linux/stadia/main.cc
index ae44e9f..77edff2 100644
--- a/starboard/contrib/linux/stadia/main.cc
+++ b/starboard/contrib/linux/stadia/main.cc
@@ -33,10 +33,12 @@
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
   if (starboard::shared::starboard::CommandLine(argc, argv)
-          .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
-  } else {
+          .HasSwitch(starboard::shared::starboard::kStartHandlerAtLaunch) &&
+      !starboard::shared::starboard::CommandLine(argc, argv)
+           .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
     third_party::crashpad::wrapper::InstallCrashpadHandler(false);
+  } else {
+    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
   }
 #endif
 
diff --git a/starboard/elf_loader/exported_symbols.cc b/starboard/elf_loader/exported_symbols.cc
index f5282ef..d7c3684 100644
--- a/starboard/elf_loader/exported_symbols.cc
+++ b/starboard/elf_loader/exported_symbols.cc
@@ -60,11 +60,42 @@
 namespace elf_loader {
 
 ExportedSymbols::ExportedSymbols() {
+  REGISTER_SYMBOL(kSbDefaultMmapThreshold);
+  REGISTER_SYMBOL(kSbFileAltSepChar);
+  REGISTER_SYMBOL(kSbFileAltSepString);
+  REGISTER_SYMBOL(kSbFileMaxName);
+  REGISTER_SYMBOL(kSbFileMaxOpen);
+  REGISTER_SYMBOL(kSbFileMaxPath);
+  REGISTER_SYMBOL(kSbFileSepChar);
+  REGISTER_SYMBOL(kSbFileSepString);
+  REGISTER_SYMBOL(kSbHasAc3Audio);
+  REGISTER_SYMBOL(kSbHasMediaWebmVp9Support);
+  REGISTER_SYMBOL(kSbHasThreadPrioritySupport);
+  REGISTER_SYMBOL(kSbMallocAlignment);
+#if SB_API_VERSION >= 14
+  REGISTER_SYMBOL(kSbMaxSystemPathCacheDirectorySize);
+#endif  // SB_API_VERSION >= 14
+  REGISTER_SYMBOL(kSbMaxThreadLocalKeys);
+  REGISTER_SYMBOL(kSbMaxThreadNameLength);
+  REGISTER_SYMBOL(kSbMaxThreads);
+  REGISTER_SYMBOL(kSbMediaMaxAudioBitrateInBitsPerSecond);
+  REGISTER_SYMBOL(kSbMediaMaxVideoBitrateInBitsPerSecond);
+  REGISTER_SYMBOL(kSbMediaVideoFrameAlignment);
+  REGISTER_SYMBOL(kSbMemoryLogPath);
+  REGISTER_SYMBOL(kSbMemoryPageSize);
+  REGISTER_SYMBOL(kSbNetworkReceiveBufferSize);
+  REGISTER_SYMBOL(kSbPathSepChar);
+  REGISTER_SYMBOL(kSbPathSepString);
+  REGISTER_SYMBOL(kSbPreferredRgbaByteOrder);
+  REGISTER_SYMBOL(kSbUserMaxSignedIn);
+  REGISTER_SYMBOL(SbAccessibilityGetCaptionSettings);
   REGISTER_SYMBOL(SbAccessibilityGetDisplaySettings);
   REGISTER_SYMBOL(SbAccessibilityGetTextToSpeechSettings);
+  REGISTER_SYMBOL(SbAccessibilitySetCaptionsEnabled);
   REGISTER_SYMBOL(SbAudioSinkCreate);
   REGISTER_SYMBOL(SbAudioSinkDestroy);
   REGISTER_SYMBOL(SbAudioSinkGetMaxChannels);
+  REGISTER_SYMBOL(SbAudioSinkGetMinBufferSizeInFrames);
   REGISTER_SYMBOL(SbAudioSinkGetNearestSupportedSampleFrequency);
   REGISTER_SYMBOL(SbAudioSinkIsAudioFrameStorageTypeSupported);
   REGISTER_SYMBOL(SbAudioSinkIsAudioSampleTypeSupported);
@@ -90,6 +121,7 @@
   REGISTER_SYMBOL(SbConditionVariableSignal);
   REGISTER_SYMBOL(SbConditionVariableWait);
   REGISTER_SYMBOL(SbConditionVariableWaitTimed);
+  REGISTER_SYMBOL(SbCPUFeaturesGet);
   REGISTER_SYMBOL(SbDecodeTargetGetInfo);
   REGISTER_SYMBOL(SbDecodeTargetRelease);
   REGISTER_SYMBOL(SbDirectoryCanOpen);
@@ -109,9 +141,12 @@
   REGISTER_SYMBOL(SbDrmDestroySystem);
   REGISTER_SYMBOL(SbDrmGenerateSessionUpdateRequest);
   REGISTER_SYMBOL(SbDrmGetMetrics);
+  REGISTER_SYMBOL(SbDrmIsServerCertificateUpdatable);
+  REGISTER_SYMBOL(SbDrmUpdateServerCertificate);
   REGISTER_SYMBOL(SbDrmUpdateSession);
   REGISTER_SYMBOL(SbEventCancel);
   REGISTER_SYMBOL(SbEventSchedule);
+  REGISTER_SYMBOL(SbFileAtomicReplace);
   REGISTER_SYMBOL(SbFileCanOpen);
   REGISTER_SYMBOL(SbFileClose);
   REGISTER_SYMBOL(SbFileDelete);
@@ -125,19 +160,8 @@
   REGISTER_SYMBOL(SbFileSeek);
   REGISTER_SYMBOL(SbFileTruncate);
   REGISTER_SYMBOL(SbFileWrite);
-  REGISTER_SYMBOL(SbMediaGetAudioConfiguration);
-  REGISTER_SYMBOL(SbMediaGetAudioOutputCount);
-#if SB_API_VERSION < 13
-  REGISTER_SYMBOL(SbMediaIsSupported);
-#endif  // SB_API_VERSION < 13
-  REGISTER_SYMBOL(SbMemoryAllocate);
-  REGISTER_SYMBOL(SbMemoryAllocateAligned);
-  REGISTER_SYMBOL(SbMemoryAllocateNoReport);
-  REGISTER_SYMBOL(SbMemoryDeallocate);
-  REGISTER_SYMBOL(SbMemoryDeallocateAligned);
-  REGISTER_SYMBOL(SbMemoryDeallocateNoReport);
-  REGISTER_SYMBOL(SbMemoryReallocate);
-  REGISTER_SYMBOL(SbMemorySetReporter);
+  REGISTER_SYMBOL(SbGetEglInterface);
+  REGISTER_SYMBOL(SbGetGlesInterface);
   REGISTER_SYMBOL(SbImageDecode);
   REGISTER_SYMBOL(SbImageIsDecodeSupported);
   REGISTER_SYMBOL(SbLog);
@@ -148,23 +172,64 @@
   REGISTER_SYMBOL(SbLogRawDumpStack);
   REGISTER_SYMBOL(SbLogRawFormat);
   REGISTER_SYMBOL(SbMediaCanPlayMimeAndKeySystem);
+  REGISTER_SYMBOL(SbMediaGetAudioBufferBudget);
+  REGISTER_SYMBOL(SbMediaGetAudioConfiguration);
+  REGISTER_SYMBOL(SbMediaGetAudioOutputCount);
+  REGISTER_SYMBOL(SbMediaGetBufferAlignment);
+  REGISTER_SYMBOL(SbMediaGetBufferAllocationUnit);
+  REGISTER_SYMBOL(SbMediaGetBufferGarbageCollectionDurationThreshold);
+  REGISTER_SYMBOL(SbMediaGetBufferPadding);
+  REGISTER_SYMBOL(SbMediaGetBufferStorageType);
+  REGISTER_SYMBOL(SbMediaGetInitialBufferCapacity);
+  REGISTER_SYMBOL(SbMediaGetMaxBufferCapacity);
+  REGISTER_SYMBOL(SbMediaGetProgressiveBufferBudget);
+  REGISTER_SYMBOL(SbMediaGetVideoBufferBudget);
+  REGISTER_SYMBOL(SbMediaIsBufferPoolAllocateOnDemand);
+  REGISTER_SYMBOL(SbMediaIsBufferUsingMemoryPool);
+#if SB_API_VERSION < 13
+  REGISTER_SYMBOL(SbMediaIsSupported);
+#endif  // SB_API_VERSION < 13
+  REGISTER_SYMBOL(SbMediaSetAudioWriteDuration);
+  REGISTER_SYMBOL(SbMemoryAllocate);
+  REGISTER_SYMBOL(SbMemoryAllocateAligned);
   REGISTER_SYMBOL(SbMemoryAllocateAlignedUnchecked);
+  REGISTER_SYMBOL(SbMemoryAllocateNoReport);
   REGISTER_SYMBOL(SbMemoryAllocateUnchecked);
 #if SB_API_VERSION < 13
   REGISTER_SYMBOL(SbMemoryCompare);
   REGISTER_SYMBOL(SbMemoryCopy);
+#endif
+  REGISTER_SYMBOL(SbMemoryDeallocate);
+  REGISTER_SYMBOL(SbMemoryDeallocateAligned);
+  REGISTER_SYMBOL(SbMemoryDeallocateNoReport);
+#if SB_API_VERSION < 13
   REGISTER_SYMBOL(SbMemoryFindByte);
 #endif
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+  REGISTER_SYMBOL(SbMemoryFlush);
+#endif  // SB_CAN(MAP_EXECUTABLE_MEMORY)
   REGISTER_SYMBOL(SbMemoryFree);
   REGISTER_SYMBOL(SbMemoryFreeAligned);
   REGISTER_SYMBOL(SbMemoryGetStackBounds);
+  REGISTER_SYMBOL(SbMemoryMap);
 #if SB_API_VERSION < 13
   REGISTER_SYMBOL(SbMemoryMove);
 #endif
+  REGISTER_SYMBOL(SbMemoryProtect);
+  REGISTER_SYMBOL(SbMemoryReallocate);
   REGISTER_SYMBOL(SbMemoryReallocateUnchecked);
 #if SB_API_VERSION < 13
   REGISTER_SYMBOL(SbMemorySet);
 #endif
+  REGISTER_SYMBOL(SbMemorySetReporter);
+  REGISTER_SYMBOL(SbMemoryUnmap);
+  REGISTER_SYMBOL(SbMicrophoneClose);
+  REGISTER_SYMBOL(SbMicrophoneCreate);
+  REGISTER_SYMBOL(SbMicrophoneDestroy);
+  REGISTER_SYMBOL(SbMicrophoneGetAvailable);
+  REGISTER_SYMBOL(SbMicrophoneIsSampleRateSupported);
+  REGISTER_SYMBOL(SbMicrophoneOpen);
+  REGISTER_SYMBOL(SbMicrophoneRead);
   REGISTER_SYMBOL(SbMutexAcquire);
   REGISTER_SYMBOL(SbMutexAcquireTry);
   REGISTER_SYMBOL(SbMutexCreate);
@@ -174,10 +239,15 @@
   REGISTER_SYMBOL(SbPlayerCreate);
   REGISTER_SYMBOL(SbPlayerDestroy);
   REGISTER_SYMBOL(SbPlayerGetCurrentFrame);
+  REGISTER_SYMBOL(SbPlayerGetInfo2);
+  REGISTER_SYMBOL(SbPlayerGetMaximumNumberOfSamplesPerWrite);
+  REGISTER_SYMBOL(SbPlayerGetPreferredOutputMode);
+  REGISTER_SYMBOL(SbPlayerSeek2);
   REGISTER_SYMBOL(SbPlayerSetBounds);
   REGISTER_SYMBOL(SbPlayerSetPlaybackRate);
   REGISTER_SYMBOL(SbPlayerSetVolume);
   REGISTER_SYMBOL(SbPlayerWriteEndOfStream);
+  REGISTER_SYMBOL(SbPlayerWriteSample2);
   REGISTER_SYMBOL(SbSocketAccept);
   REGISTER_SYMBOL(SbSocketBind);
   REGISTER_SYMBOL(SbSocketClearLastError);
@@ -190,6 +260,7 @@
   REGISTER_SYMBOL(SbSocketGetLocalAddress);
   REGISTER_SYMBOL(SbSocketIsConnected);
   REGISTER_SYMBOL(SbSocketIsConnectedAndIdle);
+  REGISTER_SYMBOL(SbSocketIsIpv6Supported);
   REGISTER_SYMBOL(SbSocketJoinMulticastGroup);
   REGISTER_SYMBOL(SbSocketListen);
   REGISTER_SYMBOL(SbSocketReceiveFrom);
@@ -209,6 +280,17 @@
   REGISTER_SYMBOL(SbSocketWaiterWait);
   REGISTER_SYMBOL(SbSocketWaiterWaitTimed);
   REGISTER_SYMBOL(SbSocketWaiterWakeUp);
+#if SB_API_VERSION == 12
+  REGISTER_SYMBOL(SbSpeechRecognizerCancel);
+  REGISTER_SYMBOL(SbSpeechRecognizerCreate);
+  REGISTER_SYMBOL(SbSpeechRecognizerDestroy);
+  REGISTER_SYMBOL(SbSpeechRecognizerIsSupported);
+  REGISTER_SYMBOL(SbSpeechRecognizerStart);
+  REGISTER_SYMBOL(SbSpeechRecognizerStop);
+#endif  // SB_API_VERSION == 12
+  REGISTER_SYMBOL(SbSpeechSynthesisCancel);
+  REGISTER_SYMBOL(SbSpeechSynthesisIsSupported);
+  REGISTER_SYMBOL(SbSpeechSynthesisSpeak);
   REGISTER_SYMBOL(SbStorageCloseRecord);
   REGISTER_SYMBOL(SbStorageDeleteRecord);
   REGISTER_SYMBOL(SbStorageGetRecordSize);
@@ -255,9 +337,11 @@
 #endif
   REGISTER_SYMBOL(SbSystemGetDeviceType);
   REGISTER_SYMBOL(SbSystemGetErrorString);
+  REGISTER_SYMBOL(SbSystemGetExtension);
   REGISTER_SYMBOL(SbSystemGetLastError);
   REGISTER_SYMBOL(SbSystemGetLocaleId);
   REGISTER_SYMBOL(SbSystemGetNumberOfProcessors);
+  REGISTER_SYMBOL(SbSystemGetPath);
   REGISTER_SYMBOL(SbSystemGetProperty);
   REGISTER_SYMBOL(SbSystemGetRandomData);
   REGISTER_SYMBOL(SbSystemGetRandomUInt64);
@@ -269,26 +353,35 @@
   REGISTER_SYMBOL(SbSystemHasCapability);
   REGISTER_SYMBOL(SbSystemHideSplashScreen);
   REGISTER_SYMBOL(SbSystemIsDebuggerAttached);
+#if SB_API_VERSION >= 13
+  REGISTER_SYMBOL(SbSystemNetworkIsDisconnected);
+#endif  // SB_API_VERSION >= 13
   REGISTER_SYMBOL(SbSystemRaisePlatformError);
 #if SB_API_VERSION >= 13
   REGISTER_SYMBOL(SbSystemRequestBlur);
   REGISTER_SYMBOL(SbSystemRequestConceal);
   REGISTER_SYMBOL(SbSystemRequestFocus);
   REGISTER_SYMBOL(SbSystemRequestFreeze);
+#endif  // SB_API_VERSION >= 13
+#if SB_API_VERSION < 13
+  REGISTER_SYMBOL(SbSystemRequestPause);
+#endif  // SB_API_VERSION < 13
+#if SB_API_VERSION >= 13
   REGISTER_SYMBOL(SbSystemRequestReveal);
   REGISTER_SYMBOL(SbSystemRequestStop);
-#else
-  REGISTER_SYMBOL(SbSystemRequestPause);
+#endif  // SB_API_VERSION >= 13
+#if SB_API_VERSION < 13
   REGISTER_SYMBOL(SbSystemRequestStop);
   REGISTER_SYMBOL(SbSystemRequestSuspend);
   REGISTER_SYMBOL(SbSystemRequestUnpause);
-#endif  // SB_API_VERSION >= 13
-
+#endif  // SB_API_VERSION < 13
+  REGISTER_SYMBOL(SbSystemSignWithCertificationSecretKey);
 #if SB_API_VERSION < 13
   REGISTER_SYMBOL(SbSystemSort);
 #endif  // SB_API_VERSION < 13
-
+  REGISTER_SYMBOL(SbSystemSupportsResume);
   REGISTER_SYMBOL(SbSystemSymbolize);
+  REGISTER_SYMBOL(SbThreadContextGetPointer);
   REGISTER_SYMBOL(SbThreadCreate);
   REGISTER_SYMBOL(SbThreadCreateLocalKey);
   REGISTER_SYMBOL(SbThreadDestroyLocalKey);
@@ -299,134 +392,42 @@
   REGISTER_SYMBOL(SbThreadGetName);
   REGISTER_SYMBOL(SbThreadIsEqual);
   REGISTER_SYMBOL(SbThreadJoin);
-  REGISTER_SYMBOL(SbThreadSetLocalValue);
-  REGISTER_SYMBOL(SbThreadSetName);
-  REGISTER_SYMBOL(SbThreadSleep);
-  REGISTER_SYMBOL(SbThreadYield);
-  REGISTER_SYMBOL(SbTimeGetMonotonicNow);
-  REGISTER_SYMBOL(SbTimeGetNow);
-  REGISTER_SYMBOL(SbTimeZoneGetCurrent);
-  REGISTER_SYMBOL(SbTimeZoneGetName);
-  REGISTER_SYMBOL(SbUserGetCurrent);
-  REGISTER_SYMBOL(SbUserGetProperty);
-  REGISTER_SYMBOL(SbUserGetPropertySize);
-  REGISTER_SYMBOL(SbUserGetSignedIn);
-  REGISTER_SYMBOL(SbWindowCreate);
-  REGISTER_SYMBOL(SbWindowDestroy);
-  REGISTER_SYMBOL(SbWindowGetPlatformHandle);
-  REGISTER_SYMBOL(SbWindowGetSize);
-  REGISTER_SYMBOL(SbWindowSetDefaultOptions);
-  REGISTER_SYMBOL(SbSystemGetPath);
-  REGISTER_SYMBOL(SbGetEglInterface);
-  REGISTER_SYMBOL(SbGetGlesInterface);
-  REGISTER_SYMBOL(SbFileAtomicReplace);
-
-#if SB_CAN(MAP_EXECUTABLE_MEMORY)
-  REGISTER_SYMBOL(SbMemoryFlush);
-#endif  // SB_CAN(MAP_EXECUTABLE_MEMORY)
-
-  REGISTER_SYMBOL(SbPlayerGetPreferredOutputMode);
-  REGISTER_SYMBOL(SbMemoryMap);
-  REGISTER_SYMBOL(SbMemoryUnmap);
-  REGISTER_SYMBOL(SbMemoryProtect);
-  REGISTER_SYMBOL(SbUiNavGetInterface);
-  REGISTER_SYMBOL(SbWindowBlurOnScreenKeyboard);
-  REGISTER_SYMBOL(SbWindowFocusOnScreenKeyboard);
-  REGISTER_SYMBOL(SbWindowGetOnScreenKeyboardBoundingRect);
-  REGISTER_SYMBOL(SbWindowHideOnScreenKeyboard);
-  REGISTER_SYMBOL(SbWindowIsOnScreenKeyboardShown);
-  REGISTER_SYMBOL(SbWindowSetOnScreenKeyboardKeepFocus);
-  REGISTER_SYMBOL(SbWindowShowOnScreenKeyboard);
-  REGISTER_SYMBOL(SbWindowOnScreenKeyboardSuggestionsSupported);
-  REGISTER_SYMBOL(SbWindowUpdateOnScreenKeyboardSuggestions);
-  REGISTER_SYMBOL(SbWindowOnScreenKeyboardIsSupported);
-  REGISTER_SYMBOL(SbAccessibilityGetCaptionSettings);
-  REGISTER_SYMBOL(SbAccessibilitySetCaptionsEnabled);
-  REGISTER_SYMBOL(SbMicrophoneClose);
-  REGISTER_SYMBOL(SbMicrophoneCreate);
-  REGISTER_SYMBOL(SbMicrophoneDestroy);
-  REGISTER_SYMBOL(SbMicrophoneGetAvailable);
-  REGISTER_SYMBOL(SbMicrophoneIsSampleRateSupported);
-  REGISTER_SYMBOL(SbMicrophoneOpen);
-  REGISTER_SYMBOL(SbMicrophoneRead);
-  REGISTER_SYMBOL(SbSocketIsIpv6Supported);
-  REGISTER_SYMBOL(SbSpeechSynthesisCancel);
-  REGISTER_SYMBOL(SbSpeechSynthesisSpeak);
-  REGISTER_SYMBOL(SbTimeGetMonotonicThreadNow);
-#if SB_API_VERSION == 12
-  REGISTER_SYMBOL(SbSpeechRecognizerIsSupported);
-#endif
-#if SB_API_VERSION == 12
-  REGISTER_SYMBOL(SbSpeechRecognizerCreate);
-  REGISTER_SYMBOL(SbSpeechRecognizerDestroy);
-  REGISTER_SYMBOL(SbSpeechRecognizerStart);
-  REGISTER_SYMBOL(SbSpeechRecognizerStop);
-  REGISTER_SYMBOL(SbSpeechRecognizerCancel);
-#endif  // SB_API_VERSION == 12
-  REGISTER_SYMBOL(SbSpeechSynthesisIsSupported);
-  REGISTER_SYMBOL(SbTimeIsTimeThreadNowSupported);
-  REGISTER_SYMBOL(SbDrmIsServerCertificateUpdatable);
-  REGISTER_SYMBOL(SbDrmUpdateServerCertificate);
-  REGISTER_SYMBOL(SbMediaGetAudioBufferBudget);
-  REGISTER_SYMBOL(SbMediaGetBufferAlignment);
-  REGISTER_SYMBOL(SbMediaGetBufferAllocationUnit);
-  REGISTER_SYMBOL(SbMediaGetBufferGarbageCollectionDurationThreshold);
-  REGISTER_SYMBOL(SbMediaGetBufferPadding);
-  REGISTER_SYMBOL(SbMediaGetBufferStorageType);
-  REGISTER_SYMBOL(SbMediaGetInitialBufferCapacity);
-  REGISTER_SYMBOL(SbMediaGetMaxBufferCapacity);
-  REGISTER_SYMBOL(SbMediaGetProgressiveBufferBudget);
-  REGISTER_SYMBOL(SbMediaGetVideoBufferBudget);
-  REGISTER_SYMBOL(SbMediaIsBufferPoolAllocateOnDemand);
-  REGISTER_SYMBOL(SbMediaIsBufferUsingMemoryPool);
-  REGISTER_SYMBOL(SbPlayerGetInfo2);
-  REGISTER_SYMBOL(SbPlayerGetMaximumNumberOfSamplesPerWrite);
-  REGISTER_SYMBOL(SbPlayerSeek2);
-  REGISTER_SYMBOL(SbPlayerWriteSample2);
-  REGISTER_SYMBOL(SbSystemSupportsResume);
-  REGISTER_SYMBOL(SbAudioSinkGetMinBufferSizeInFrames);
-  REGISTER_SYMBOL(SbCPUFeaturesGet);
-  REGISTER_SYMBOL(SbMediaSetAudioWriteDuration);
-  REGISTER_SYMBOL(SbSystemGetExtension);
-  REGISTER_SYMBOL(SbSystemSignWithCertificationSecretKey);
-  REGISTER_SYMBOL(SbThreadContextGetPointer);
   REGISTER_SYMBOL(SbThreadSamplerCreate);
   REGISTER_SYMBOL(SbThreadSamplerDestroy);
   REGISTER_SYMBOL(SbThreadSamplerFreeze);
   REGISTER_SYMBOL(SbThreadSamplerIsSupported);
   REGISTER_SYMBOL(SbThreadSamplerThaw);
+  REGISTER_SYMBOL(SbThreadSetLocalValue);
+  REGISTER_SYMBOL(SbThreadSetName);
+  REGISTER_SYMBOL(SbThreadSleep);
+  REGISTER_SYMBOL(SbThreadYield);
+  REGISTER_SYMBOL(SbTimeGetMonotonicNow);
+  REGISTER_SYMBOL(SbTimeGetMonotonicThreadNow);
+  REGISTER_SYMBOL(SbTimeGetNow);
+  REGISTER_SYMBOL(SbTimeIsTimeThreadNowSupported);
+  REGISTER_SYMBOL(SbTimeZoneGetCurrent);
+  REGISTER_SYMBOL(SbTimeZoneGetName);
+  REGISTER_SYMBOL(SbUiNavGetInterface);
+  REGISTER_SYMBOL(SbUserGetCurrent);
+  REGISTER_SYMBOL(SbUserGetProperty);
+  REGISTER_SYMBOL(SbUserGetPropertySize);
+  REGISTER_SYMBOL(SbUserGetSignedIn);
+  REGISTER_SYMBOL(SbWindowBlurOnScreenKeyboard);
+  REGISTER_SYMBOL(SbWindowCreate);
+  REGISTER_SYMBOL(SbWindowDestroy);
+  REGISTER_SYMBOL(SbWindowFocusOnScreenKeyboard);
   REGISTER_SYMBOL(SbWindowGetDiagonalSizeInInches);
-  REGISTER_SYMBOL(kSbDefaultMmapThreshold);
-  REGISTER_SYMBOL(kSbFileMaxName);
-  REGISTER_SYMBOL(kSbFileMaxOpen);
-  REGISTER_SYMBOL(kSbFileAltSepChar);
-  REGISTER_SYMBOL(kSbFileAltSepString);
-  REGISTER_SYMBOL(kSbFileMaxPath);
-  REGISTER_SYMBOL(kSbFileSepChar);
-  REGISTER_SYMBOL(kSbFileSepString);
-  REGISTER_SYMBOL(kSbHasAc3Audio);
-  REGISTER_SYMBOL(kSbHasMediaWebmVp9Support);
-  REGISTER_SYMBOL(kSbHasThreadPrioritySupport);
-  REGISTER_SYMBOL(kSbMallocAlignment);
-#if SB_API_VERSION >= 14
-  REGISTER_SYMBOL(kSbMaxSystemPathCacheDirectorySize);
-#endif  // SB_API_VERSION >= 14
-  REGISTER_SYMBOL(kSbMaxThreadLocalKeys);
-  REGISTER_SYMBOL(kSbMaxThreadNameLength);
-  REGISTER_SYMBOL(kSbMediaMaxAudioBitrateInBitsPerSecond);
-  REGISTER_SYMBOL(kSbMediaMaxVideoBitrateInBitsPerSecond);
-  REGISTER_SYMBOL(kSbMediaVideoFrameAlignment);
-  REGISTER_SYMBOL(kSbMemoryLogPath);
-  REGISTER_SYMBOL(kSbMemoryPageSize);
-  REGISTER_SYMBOL(kSbNetworkReceiveBufferSize);
-  REGISTER_SYMBOL(kSbMaxThreads);
-  REGISTER_SYMBOL(kSbPathSepChar);
-  REGISTER_SYMBOL(kSbPathSepString);
-  REGISTER_SYMBOL(kSbPreferredRgbaByteOrder);
-  REGISTER_SYMBOL(kSbUserMaxSignedIn);
-#if SB_API_VERSION >= 13
-  REGISTER_SYMBOL(SbSystemNetworkIsDisconnected);
-#endif  // SB_API_VERSION >= 13
+  REGISTER_SYMBOL(SbWindowGetOnScreenKeyboardBoundingRect);
+  REGISTER_SYMBOL(SbWindowGetPlatformHandle);
+  REGISTER_SYMBOL(SbWindowGetSize);
+  REGISTER_SYMBOL(SbWindowHideOnScreenKeyboard);
+  REGISTER_SYMBOL(SbWindowIsOnScreenKeyboardShown);
+  REGISTER_SYMBOL(SbWindowOnScreenKeyboardIsSupported);
+  REGISTER_SYMBOL(SbWindowOnScreenKeyboardSuggestionsSupported);
+  REGISTER_SYMBOL(SbWindowSetDefaultOptions);
+  REGISTER_SYMBOL(SbWindowSetOnScreenKeyboardKeepFocus);
+  REGISTER_SYMBOL(SbWindowShowOnScreenKeyboard);
+  REGISTER_SYMBOL(SbWindowUpdateOnScreenKeyboardSuggestions);
 }  // NOLINT
 
 const void* ExportedSymbols::Lookup(const char* name) {
diff --git a/starboard/linux/x64x11/main.cc b/starboard/linux/x64x11/main.cc
index d2bb68a..56e1caa 100644
--- a/starboard/linux/x64x11/main.cc
+++ b/starboard/linux/x64x11/main.cc
@@ -35,10 +35,12 @@
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
   if (starboard::shared::starboard::CommandLine(argc, argv)
-          .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
-  } else {
+          .HasSwitch(starboard::shared::starboard::kStartHandlerAtLaunch) &&
+      !starboard::shared::starboard::CommandLine(argc, argv)
+           .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
     third_party::crashpad::wrapper::InstallCrashpadHandler(false);
+  } else {
+    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
   }
 #endif
 
diff --git a/starboard/raspi/shared/main.cc b/starboard/raspi/shared/main.cc
index d935df8..ee0d66c 100644
--- a/starboard/raspi/shared/main.cc
+++ b/starboard/raspi/shared/main.cc
@@ -38,10 +38,12 @@
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
   if (starboard::shared::starboard::CommandLine(argc, argv)
-          .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
-  } else {
+          .HasSwitch(starboard::shared::starboard::kStartHandlerAtLaunch) &&
+      !starboard::shared::starboard::CommandLine(argc, argv)
+           .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
     third_party::crashpad::wrapper::InstallCrashpadHandler(false);
+  } else {
+    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
   }
 #endif
   starboard::raspi::shared::ApplicationDispmanx application;
diff --git a/starboard/shared/starboard/player/filter/BUILD.gn b/starboard/shared/starboard/player/filter/BUILD.gn
index c37a424..f5f9d4f 100644
--- a/starboard/shared/starboard/player/filter/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/BUILD.gn
@@ -23,8 +23,8 @@
     "//starboard/shared/starboard/player/filter/audio_frame_tracker.cc",
     "//starboard/shared/starboard/player/filter/audio_frame_tracker.h",
     "//starboard/shared/starboard/player/filter/audio_renderer_internal.h",
-    "//starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc",
-    "//starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h",
+    "//starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc",
+    "//starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h",
     "//starboard/shared/starboard/player/filter/audio_renderer_sink.h",
     "//starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc",
     "//starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h",
diff --git a/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc
similarity index 87%
rename from starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc
rename to starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc
index 5e0536b..709cd8b 100644
--- a/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.cc
+++ b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h"
 
 #include <algorithm>
 #include <string>
@@ -29,7 +29,7 @@
 namespace {
 
 // This class works only when the input format and output format are the same.
-// It allows for a simplified AudioRendererImpl implementation by always using a
+// It allows for a simplified AudioRendererPcm implementation by always using a
 // resampler.
 class IdentityAudioResampler : public AudioResampler {
  public:
@@ -50,7 +50,7 @@
   bool eos_reached_;
 };
 
-// AudioRendererImpl uses AudioTimeStretcher internally to adjust to playback
+// AudioRendererPcm uses AudioTimeStretcher internally to adjust to playback
 // rate. So we try to use kSbMediaAudioSampleTypeFloat32 and only use
 // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.  To use
 // kSbMediaAudioSampleTypeFloat32 will cause an extra conversion from float32 to
@@ -65,7 +65,7 @@
 
 }  // namespace
 
-AudioRendererImpl::AudioRendererImpl(
+AudioRendererPcm::AudioRendererPcm(
     scoped_ptr<AudioDecoder> decoder,
     scoped_ptr<AudioRendererSink> audio_renderer_sink,
     const SbMediaAudioSampleInfo& audio_sample_info,
@@ -80,9 +80,9 @@
       frames_consumed_set_at_(SbTimeGetMonotonicNow()),
       decoder_(decoder.Pass()),
       process_audio_data_job_(
-          std::bind(&AudioRendererImpl::ProcessAudioData, this)),
+          std::bind(&AudioRendererPcm::ProcessAudioData, this)),
       audio_renderer_sink_(audio_renderer_sink.Pass()) {
-  SB_DLOG(INFO) << "Creating AudioRendererImpl with " << channels_
+  SB_DLOG(INFO) << "Creating AudioRendererPcm with " << channels_
                 << " channels, " << bytes_per_frame_ << " bytes per frame, "
                 << max_cached_frames_ << " max cached frames, and "
                 << min_frames_per_append_ << " min frames per append.";
@@ -93,22 +93,22 @@
   frame_buffers_[0] = &frame_buffer_[0];
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-  Schedule(std::bind(&AudioRendererImpl::CheckAudioSinkStatus, this),
+  Schedule(std::bind(&AudioRendererPcm::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
 }
 
-AudioRendererImpl::~AudioRendererImpl() {
-  SB_DLOG(INFO) << "Destroying AudioRendererImpl with " << channels_
+AudioRendererPcm::~AudioRendererPcm() {
+  SB_DLOG(INFO) << "Destroying AudioRendererPcm with " << channels_
                 << " channels, " << bytes_per_frame_ << " bytes per frame, "
                 << max_cached_frames_ << " max cached frames, and "
                 << min_frames_per_append_ << " min frames per append.";
   SB_DCHECK(BelongsToCurrentThread());
 }
 
-void AudioRendererImpl::Initialize(const ErrorCB& error_cb,
-                                   const PrerolledCB& prerolled_cb,
-                                   const EndedCB& ended_cb) {
+void AudioRendererPcm::Initialize(const ErrorCB& error_cb,
+                                  const PrerolledCB& prerolled_cb,
+                                  const EndedCB& ended_cb) {
   SB_DCHECK(error_cb);
   SB_DCHECK(prerolled_cb);
   SB_DCHECK(ended_cb);
@@ -120,11 +120,11 @@
   prerolled_cb_ = prerolled_cb;
   ended_cb_ = ended_cb;
 
-  decoder_->Initialize(std::bind(&AudioRendererImpl::OnDecoderOutput, this),
+  decoder_->Initialize(std::bind(&AudioRendererPcm::OnDecoderOutput, this),
                        error_cb);
 }
 
-void AudioRendererImpl::WriteSample(
+void AudioRendererPcm::WriteSample(
     const scoped_refptr<InputBuffer>& input_buffer) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
@@ -139,11 +139,11 @@
   can_accept_more_data_ = false;
 
   decoder_->Decode(input_buffer,
-                   std::bind(&AudioRendererImpl::OnDecoderConsumed, this));
+                   std::bind(&AudioRendererPcm::OnDecoderConsumed, this));
   first_input_written_ = true;
 }
 
-void AudioRendererImpl::WriteEndOfStream() {
+void AudioRendererPcm::WriteEndOfStream() {
   SB_DCHECK(BelongsToCurrentThread());
   // TODO: Check |can_accept_more_data_| and make WriteEndOfStream() depend on
   // CanAcceptMoreData() or callback.
@@ -162,28 +162,28 @@
   first_input_written_ = true;
 }
 
-void AudioRendererImpl::SetVolume(double volume) {
+void AudioRendererPcm::SetVolume(double volume) {
   SB_DCHECK(BelongsToCurrentThread());
   audio_renderer_sink_->SetVolume(volume);
 }
 
-bool AudioRendererImpl::IsEndOfStreamWritten() const {
+bool AudioRendererPcm::IsEndOfStreamWritten() const {
   SB_DCHECK(BelongsToCurrentThread());
   return eos_state_ >= kEOSWrittenToDecoder;
 }
 
-bool AudioRendererImpl::IsEndOfStreamPlayed() const {
+bool AudioRendererPcm::IsEndOfStreamPlayed() const {
   ScopedLock lock(mutex_);
   return IsEndOfStreamPlayed_Locked();
 }
 
-bool AudioRendererImpl::CanAcceptMoreData() const {
+bool AudioRendererPcm::CanAcceptMoreData() const {
   SB_DCHECK(BelongsToCurrentThread());
   return eos_state_ == kEOSNotReceived && can_accept_more_data_ &&
          (!decoder_sample_rate_ || !time_stretcher_.IsQueueFull());
 }
 
-void AudioRendererImpl::Play() {
+void AudioRendererPcm::Play() {
   SB_DCHECK(BelongsToCurrentThread());
 
   ScopedLock lock(mutex_);
@@ -191,14 +191,14 @@
   consume_frames_called_ = false;
 }
 
-void AudioRendererImpl::Pause() {
+void AudioRendererPcm::Pause() {
   SB_DCHECK(BelongsToCurrentThread());
 
   ScopedLock lock(mutex_);
   paused_ = true;
 }
 
-void AudioRendererImpl::SetPlaybackRate(double playback_rate) {
+void AudioRendererPcm::SetPlaybackRate(double playback_rate) {
   SB_DCHECK(BelongsToCurrentThread());
 
   ScopedLock lock(mutex_);
@@ -223,7 +223,7 @@
   }
 }
 
-void AudioRendererImpl::Seek(SbTime seek_to_time) {
+void AudioRendererPcm::Seek(SbTime seek_to_time) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(seek_to_time >= 0);
 
@@ -272,15 +272,15 @@
   CancelPendingJobs();
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-  Schedule(std::bind(&AudioRendererImpl::CheckAudioSinkStatus, this),
+  Schedule(std::bind(&AudioRendererPcm::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
 }
 
-SbTime AudioRendererImpl::GetCurrentMediaTime(bool* is_playing,
-                                              bool* is_eos_played,
-                                              bool* is_underflow,
-                                              double* playback_rate) {
+SbTime AudioRendererPcm::GetCurrentMediaTime(bool* is_playing,
+                                             bool* is_eos_played,
+                                             bool* is_underflow,
+                                             double* playback_rate) {
   SB_DCHECK(is_playing);
   SB_DCHECK(is_eos_played);
   SB_DCHECK(is_underflow);
@@ -364,10 +364,10 @@
   return media_time;
 }
 
-void AudioRendererImpl::GetSourceStatus(int* frames_in_buffer,
-                                        int* offset_in_frames,
-                                        bool* is_playing,
-                                        bool* is_eos_reached) {
+void AudioRendererPcm::GetSourceStatus(int* frames_in_buffer,
+                                       int* offset_in_frames,
+                                       bool* is_playing,
+                                       bool* is_eos_reached) {
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   sink_callbacks_since_last_check_.increment();
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
@@ -406,14 +406,13 @@
 
       if (silence_frames_to_write <= max_cached_frames_ - start_offset) {
         memset(frame_buffer_.data() + start_offset * bytes_per_frame_, 0,
-                    silence_frames_to_write * bytes_per_frame_);
+               silence_frames_to_write * bytes_per_frame_);
       } else {
         memset(frame_buffer_.data() + start_offset * bytes_per_frame_, 0,
-                    (max_cached_frames_ - start_offset) * bytes_per_frame_);
-        memset(
-            frame_buffer_.data(), 0,
-            (silence_frames_to_write - max_cached_frames_ + start_offset) *
-                bytes_per_frame_);
+               (max_cached_frames_ - start_offset) * bytes_per_frame_);
+        memset(frame_buffer_.data(), 0,
+               (silence_frames_to_write - max_cached_frames_ + start_offset) *
+                   bytes_per_frame_);
       }
       silence_frames_written_after_eos_on_sink_thread_ +=
           silence_frames_to_write;
@@ -422,8 +421,8 @@
   }
 }
 
-void AudioRendererImpl::ConsumeFrames(int frames_consumed,
-                                      SbTime frames_consumed_at) {
+void AudioRendererPcm::ConsumeFrames(int frames_consumed,
+                                     SbTime frames_consumed_at) {
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
   sink_callbacks_since_last_check_.increment();
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
@@ -450,8 +449,8 @@
   }
 }
 
-void AudioRendererImpl::OnError(bool capability_changed,
-                                const std::string& error_message) {
+void AudioRendererPcm::OnError(bool capability_changed,
+                               const std::string& error_message) {
   SB_DCHECK(error_cb_);
   if (capability_changed) {
     error_cb_(kSbPlayerErrorCapabilityChanged, error_message);
@@ -463,7 +462,7 @@
   }
 }
 
-void AudioRendererImpl::UpdateVariablesOnSinkThread_Locked(
+void AudioRendererPcm::UpdateVariablesOnSinkThread_Locked(
     SbTime system_time_on_consume_frames) {
   mutex_.DCheckAcquired();
 
@@ -504,7 +503,7 @@
   }
 }
 
-void AudioRendererImpl::OnFirstOutput(
+void AudioRendererPcm::OnFirstOutput(
     const SbMediaAudioSampleType decoded_sample_type,
     const SbMediaAudioFrameStorageType decoded_storage_type,
     const int decoded_sample_rate) {
@@ -544,13 +543,13 @@
   }
 }
 
-bool AudioRendererImpl::IsEndOfStreamPlayed_Locked() const {
+bool AudioRendererPcm::IsEndOfStreamPlayed_Locked() const {
   mutex_.DCheckAcquired();
   return eos_state_ >= kEOSSentToSink &&
          total_frames_sent_to_sink_ == total_frames_consumed_by_sink_;
 }
 
-void AudioRendererImpl::OnDecoderConsumed() {
+void AudioRendererPcm::OnDecoderConsumed() {
   SB_DCHECK(BelongsToCurrentThread());
 
   // TODO: Unify EOS and non EOS request once WriteEndOfStream() depends on
@@ -562,7 +561,7 @@
   }
 }
 
-void AudioRendererImpl::OnDecoderOutput() {
+void AudioRendererPcm::OnDecoderOutput() {
   SB_DCHECK(BelongsToCurrentThread());
 
   ++pending_decoder_outputs_;
@@ -575,7 +574,7 @@
   ProcessAudioData();
 }
 
-void AudioRendererImpl::ProcessAudioData() {
+void AudioRendererPcm::ProcessAudioData() {
   SB_DCHECK(BelongsToCurrentThread());
 
   process_audio_data_job_token_.ResetToInvalid();
@@ -673,7 +672,7 @@
   }
 }
 
-bool AudioRendererImpl::AppendAudioToFrameBuffer(bool* is_frame_buffer_full) {
+bool AudioRendererPcm::AppendAudioToFrameBuffer(bool* is_frame_buffer_full) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(is_frame_buffer_full);
 
@@ -722,17 +721,16 @@
   int frames_appended = 0;
 
   if (frames_to_append > max_cached_frames_ - offset_to_append) {
-    memcpy(&frame_buffer_[offset_to_append * bytes_per_frame_],
-                 source_buffer,
-                 (max_cached_frames_ - offset_to_append) * bytes_per_frame_);
+    memcpy(&frame_buffer_[offset_to_append * bytes_per_frame_], source_buffer,
+           (max_cached_frames_ - offset_to_append) * bytes_per_frame_);
     source_buffer += (max_cached_frames_ - offset_to_append) * bytes_per_frame_;
     frames_to_append -= max_cached_frames_ - offset_to_append;
     frames_appended += max_cached_frames_ - offset_to_append;
     offset_to_append = 0;
   }
 
-  memcpy(&frame_buffer_[offset_to_append * bytes_per_frame_],
-               source_buffer, frames_to_append * bytes_per_frame_);
+  memcpy(&frame_buffer_[offset_to_append * bytes_per_frame_], source_buffer,
+         frames_to_append * bytes_per_frame_);
   frames_appended += frames_to_append;
 
   total_frames_sent_to_sink_ += frames_appended;
@@ -741,7 +739,7 @@
 }
 
 #if SB_PLAYER_FILTER_ENABLE_STATE_CHECK
-void AudioRendererImpl::CheckAudioSinkStatus() {
+void AudioRendererPcm::CheckAudioSinkStatus() {
   SB_DCHECK(BelongsToCurrentThread());
 
   // Check if sink callbacks are called too frequently.
@@ -770,7 +768,7 @@
                      << sink_callbacks_since_last_check
                      << " callbacks since last check.";
   }
-  Schedule(std::bind(&AudioRendererImpl::CheckAudioSinkStatus, this),
+  Schedule(std::bind(&AudioRendererPcm::CheckAudioSinkStatus, this),
            kCheckAudioSinkStatusInterval);
 }
 #endif  // SB_PLAYER_FILTER_ENABLE_STATE_CHECK
diff --git a/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h
similarity index 92%
rename from starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h
rename to starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h
index 1129b9d..7c37d7b 100644
--- a/starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h
+++ b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_IMPL_H_
-#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_IMPL_H_
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_PCM_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_PCM_H_
 
 #include <functional>
 #include <string>
@@ -54,10 +54,10 @@
 // A class that sits in between the audio decoder, the audio sink and the
 // pipeline to coordinate data transfer between these parties.  It also serves
 // as the authority of playback time.
-class AudioRendererImpl : public AudioRenderer,
-                          public MediaTimeProvider,
-                          private AudioRendererSink::RenderCallback,
-                          private JobQueue::JobOwner {
+class AudioRendererPcm : public AudioRenderer,
+                         public MediaTimeProvider,
+                         private AudioRendererSink::RenderCallback,
+                         private JobQueue::JobOwner {
  public:
   // |max_cached_frames| is a soft limit for the max audio frames this class can
   // cache so it can:
@@ -66,12 +66,12 @@
   //    longer accept more data.
   // |min_frames_per_append| is the min number of frames that the audio renderer
   // tries to append to the sink buffer at once.
-  AudioRendererImpl(scoped_ptr<AudioDecoder> decoder,
-                    scoped_ptr<AudioRendererSink> audio_renderer_sink,
-                    const SbMediaAudioSampleInfo& audio_sample_info,
-                    int max_cached_frames,
-                    int min_frames_per_append);
-  ~AudioRendererImpl() override;
+  AudioRendererPcm(scoped_ptr<AudioDecoder> decoder,
+                   scoped_ptr<AudioRendererSink> audio_renderer_sink,
+                   const SbMediaAudioSampleInfo& audio_sample_info,
+                   int max_cached_frames,
+                   int min_frames_per_append);
+  ~AudioRendererPcm() override;
 
   void Initialize(const ErrorCB& error_cb,
                   const PrerolledCB& prerolled_cb,
@@ -215,4 +215,4 @@
 }  // namespace shared
 }  // namespace starboard
 
-#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_IMPL_H_
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RENDERER_INTERNAL_PCM_H_
diff --git a/starboard/shared/starboard/player/filter/audio_renderer_sink.h b/starboard/shared/starboard/player/filter/audio_renderer_sink.h
index 5aa8a1f..de4e394 100644
--- a/starboard/shared/starboard/player/filter/audio_renderer_sink.h
+++ b/starboard/shared/starboard/player/filter/audio_renderer_sink.h
@@ -27,7 +27,7 @@
 namespace player {
 namespace filter {
 
-// The interface used by AudioRendererImpl to output audio samples.
+// The interface used by AudioRendererPcm to output audio samples.
 class AudioRendererSink {
  public:
   class RenderCallback {
diff --git a/starboard/shared/starboard/player/filter/player_components.cc b/starboard/shared/starboard/player/filter/player_components.cc
index f8eb7b0..94d71e0 100644
--- a/starboard/shared/starboard/player/filter/player_components.cc
+++ b/starboard/shared/starboard/player/filter/player_components.cc
@@ -18,7 +18,7 @@
 #include "starboard/shared/starboard/application.h"
 #include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
 #include "starboard/shared/starboard/player/filter/media_time_provider_impl.h"
 #include "starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h"
@@ -52,7 +52,7 @@
 class PlayerComponentsImpl : public PlayerComponents {
  public:
   PlayerComponentsImpl(scoped_ptr<MediaTimeProviderImpl> media_time_provider,
-                       scoped_ptr<AudioRendererImpl> audio_renderer,
+                       scoped_ptr<AudioRendererPcm> audio_renderer,
                        scoped_ptr<VideoRendererImpl> video_renderer)
       : media_time_provider_(media_time_provider.Pass()),
         audio_renderer_(audio_renderer.Pass()),
@@ -72,7 +72,7 @@
  private:
   // |media_time_provider_| will only be used when |audio_renderer_| is nullptr.
   scoped_ptr<MediaTimeProviderImpl> media_time_provider_;
-  scoped_ptr<AudioRendererImpl> audio_renderer_;
+  scoped_ptr<AudioRendererPcm> audio_renderer_;
   scoped_ptr<VideoRendererImpl> video_renderer_;
 };
 
@@ -216,7 +216,7 @@
   }
 
   scoped_ptr<MediaTimeProviderImpl> media_time_provider_impl;
-  scoped_ptr<AudioRendererImpl> audio_renderer;
+  scoped_ptr<AudioRendererPcm> audio_renderer;
   scoped_ptr<VideoRendererImpl> video_renderer;
 
   if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone) {
@@ -228,9 +228,9 @@
                            &min_frames_per_append);
 
     audio_renderer.reset(
-        new AudioRendererImpl(audio_decoder.Pass(), audio_renderer_sink.Pass(),
-                              creation_parameters.audio_sample_info(),
-                              max_cached_frames, min_frames_per_append));
+        new AudioRendererPcm(audio_decoder.Pass(), audio_renderer_sink.Pass(),
+                             creation_parameters.audio_sample_info(),
+                             max_cached_frames, min_frames_per_append));
   }
 
   if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
diff --git a/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index c7b15c9..ae7e57c 100644
--- a/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/shared/starboard/player/filter/audio_renderer_internal_impl.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h"
 
 #include <functional>
 #include <set>
@@ -114,7 +114,7 @@
 
     const int kMaxCachedFrames = 256 * 1024;
     const int kMaxFramesPerAppend = 16384;
-    audio_renderer_.reset(new AudioRendererImpl(
+    audio_renderer_.reset(new AudioRendererPcm(
         make_scoped_ptr<AudioDecoder>(audio_decoder_),
         make_scoped_ptr<AudioRendererSink>(audio_renderer_sink_),
         GetDefaultAudioSampleInfo(), kMaxCachedFrames, kMaxFramesPerAppend));
@@ -231,7 +231,7 @@
   AudioDecoder::ConsumedCB consumed_cb_;
   bool prerolled_ = true;
 
-  scoped_ptr<AudioRendererImpl> audio_renderer_;
+  scoped_ptr<AudioRendererPcm> audio_renderer_;
   MockAudioDecoder* audio_decoder_;
   MockAudioRendererSink* audio_renderer_sink_;
   AudioRendererSink::RenderCallback* renderer_callback_;
diff --git a/starboard/shared/starboard/starboard_switches.cc b/starboard/shared/starboard/starboard_switches.cc
index 7da02f4..ad4917e 100644
--- a/starboard/shared/starboard/starboard_switches.cc
+++ b/starboard/shared/starboard/starboard_switches.cc
@@ -19,6 +19,7 @@
 namespace starboard {
 
 const char kStartHandlerAtCrash[] = "start_handler_at_crash";
+const char kStartHandlerAtLaunch[] = "start_handler_at_launch";
 
 }  // namespace starboard
 }  // namespace shared
diff --git a/starboard/shared/starboard/starboard_switches.h b/starboard/shared/starboard/starboard_switches.h
index 025e03b..55fea4e 100644
--- a/starboard/shared/starboard/starboard_switches.h
+++ b/starboard/shared/starboard/starboard_switches.h
@@ -25,6 +25,10 @@
 // before the app runs and keeping it running all the time. This option reduces
 // memory consumption by the crash handler.
 extern const char kStartHandlerAtCrash[];
+// Use this flag to start the handler
+// before the app launches. Without this flag, the crash handler starts only
+// when a crash happens. This option increases the memory consumption.
+extern const char kStartHandlerAtLaunch[];
 
 }  // namespace starboard
 }  // namespace shared
diff --git a/starboard/shared/test_webapi_extension/BUILD.gn b/starboard/shared/test_webapi_extension/BUILD.gn
deleted file mode 100644
index 331ced9..0000000
--- a/starboard/shared/test_webapi_extension/BUILD.gn
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2021 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-static_library("cobalt_test_webapi_extension") {
-  # List of all source files and header files needed to support the IDL
-  # definitions.
-  sources = [
-    "my_new_interface.cc",
-    "my_new_interface.h",
-    "webapi_extension.cc",
-  ]
-
-  deps = [
-    "//base",
-    "//cobalt/dom",
-    "//cobalt/script",
-  ]
-}
diff --git a/starboard/shared/test_webapi_extension/my_new_enum.idl b/starboard/shared/test_webapi_extension/my_new_enum.idl
deleted file mode 100644
index 8d2f185..0000000
--- a/starboard/shared/test_webapi_extension/my_new_enum.idl
+++ /dev/null
@@ -1,19 +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.
-
-enum MyNewEnum {
-  "apples",
-  "oranges",
-  "peaches"
-};
diff --git a/starboard/shared/test_webapi_extension/my_new_interface.cc b/starboard/shared/test_webapi_extension/my_new_interface.cc
deleted file mode 100644
index e704629..0000000
--- a/starboard/shared/test_webapi_extension/my_new_interface.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/shared/test_webapi_extension/my_new_interface.h"
-
-namespace cobalt {
-namespace webapi_extension {
-
-MyNewInterface::MyNewInterface(const scoped_refptr<dom::Window>& window) {
-  // Provide an initial value for the enum.
-  enum_value_ = kMyNewEnumApples;
-}
-
-MyNewInterface::~MyNewInterface() {}
-
-}  // namespace webapi_extension
-}  // namespace cobalt
diff --git a/starboard/shared/test_webapi_extension/my_new_interface.h b/starboard/shared/test_webapi_extension/my_new_interface.h
deleted file mode 100644
index 29a1fdb..0000000
--- a/starboard/shared/test_webapi_extension/my_new_interface.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
-#define STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
-
-#include <string>
-
-#include "cobalt/dom/window.h"
-#include "cobalt/script/wrappable.h"
-#include "starboard/shared/test_webapi_extension/my_new_enum.h"
-
-namespace cobalt {
-namespace webapi_extension {
-
-class MyNewInterface : public script::Wrappable {
- public:
-  explicit MyNewInterface(const scoped_refptr<dom::Window>& window);
-
-  const std::string& foo() const { return foo_; }
-  void set_foo(const std::string& value) { foo_ = value; }
-
-  void SetMyNewEnum(MyNewEnum value) { enum_value_ = value; }
-  MyNewEnum GetMyNewEnum() const { return enum_value_; }
-
-  // All types derived from script::Wrappable must have this annotation.
-  DEFINE_WRAPPABLE_TYPE(MyNewInterface);
-
- private:
-  // Since script::Wrappable inherits from base::RefCounted<>, we make the
-  // destructor private.
-  ~MyNewInterface() override;
-
-  std::string foo_;
-
-  MyNewEnum enum_value_;
-
-  DISALLOW_COPY_AND_ASSIGN(MyNewInterface);
-};
-
-}  // namespace webapi_extension
-}  // namespace cobalt
-
-#endif  // STARBOARD_SHARED_TEST_WEBAPI_EXTENSION_MY_NEW_INTERFACE_H_
diff --git a/starboard/shared/test_webapi_extension/webapi_extension.cc b/starboard/shared/test_webapi_extension/webapi_extension.cc
deleted file mode 100644
index 23791dd..0000000
--- a/starboard/shared/test_webapi_extension/webapi_extension.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "base/compiler_specific.h"
-#include "cobalt/script/global_environment.h"
-#include "starboard/shared/test_webapi_extension/my_new_interface.h"
-
-namespace cobalt {
-namespace browser {
-
-base::Optional<std::string> GetWebAPIExtensionObjectPropertyName() {
-  return std::string("myInterface");
-}
-
-scoped_refptr<script::Wrappable> CreateWebAPIExtensionObject(
-    const scoped_refptr<dom::Window>& window,
-    script::GlobalEnvironment* global_environment) {
-  return scoped_refptr<script::Wrappable>(
-      new webapi_extension::MyNewInterface(window));
-}
-
-}  // namespace browser
-}  // namespace cobalt
diff --git a/starboard/win/shared/platform_configuration/BUILD.gn b/starboard/win/shared/platform_configuration/BUILD.gn
index 37e60e7..7d01219 100644
--- a/starboard/win/shared/platform_configuration/BUILD.gn
+++ b/starboard/win/shared/platform_configuration/BUILD.gn
@@ -263,6 +263,9 @@
     # objects.
     # https://connect.microsoft.com/VisualStudio/feedback/details/783808/static-analyzer-warning-c28285-for-std-min-and-std-max
     "/wd28285",
+
+    # Deprecated function warning.
+    "/wd4996",
   ]
 }
 
diff --git a/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp b/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
index 57dcb4f..acc7a5a 100644
--- a/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
+++ b/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
@@ -162,7 +162,7 @@
     swapChainDesc.SampleDesc.Quality    = 0;
     swapChainDesc.BufferUsage =
         DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
-    swapChainDesc.BufferCount = 4;
+    swapChainDesc.BufferCount = 3;
     swapChainDesc.SwapEffect  = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
     swapChainDesc.Scaling     = DXGI_SCALING_STRETCH;
     swapChainDesc.AlphaMode   = DXGI_ALPHA_MODE_UNSPECIFIED;
@@ -200,7 +200,7 @@
     result = device->QueryInterface(__uuidof(IDXGIDevice1), (void **)pDXGIDevice.GetAddressOf());
     if (SUCCEEDED(result))
     {
-      pDXGIDevice->SetMaximumFrameLatency(swapChainDesc.BufferCount-1);
+      pDXGIDevice->SetMaximumFrameLatency(swapChainDesc.BufferCount);
     }
 
     return result;
diff --git a/third_party/web_platform_tests/cors/resources/304.py b/third_party/web_platform_tests/cors/resources/304.py
index 2fc83b9..265569d 100755
--- a/third_party/web_platform_tests/cors/resources/304.py
+++ b/third_party/web_platform_tests/cors/resources/304.py
@@ -38,7 +38,9 @@
         else:
             status = 200, "OK"
             headers.append(("Access-Control-Allow-Origin", "*"))
-            headers.append(("Content-Type", "text/plain"))
+            # Cobalt does not support text/plain caching.
+            # headers.append(("Content-Type", "text/plain"))
+            headers.append(("Content-Type", "text/html"))
             headers.append(("Cache-Control", "private, max-age=3, must-revalidate"))
             headers.append(("ETag", etag))
             return status, headers, "Success"
diff --git a/third_party/web_platform_tests/workers/interfaces/WorkerUtils/WindowTimers/005.html b/third_party/web_platform_tests/workers/interfaces/WorkerUtils/WindowTimers/005.html
index a1e5044..06885e4 100644
--- a/third_party/web_platform_tests/workers/interfaces/WorkerUtils/WindowTimers/005.html
+++ b/third_party/web_platform_tests/workers/interfaces/WorkerUtils/WindowTimers/005.html
@@ -8,7 +8,7 @@
   async_test(function () {
     var worker = new Worker('005.js');
     worker.onmessage = this.step_func(function (e) {
-      assert_equals(e.data, '1');
+      assert_equals(e.data, 1);
       this.done();
     });
   });