Import Cobalt 23.lts.1.309312
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8a5bc66..e6ccec8 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3195,7 +3195,7 @@
     ":base_unittests_bundle_data",
   ]
   if (is_starboard){
-    content_deps = [
+    data_deps += [
       ":base_unittests_bundle_data",
       "//third_party/icu:icudata",
     ]
diff --git a/cobalt/base/BUILD.gn b/cobalt/base/BUILD.gn
index bd5c1d3..898475d 100644
--- a/cobalt/base/BUILD.gn
+++ b/cobalt/base/BUILD.gn
@@ -124,5 +124,5 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-  content_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/black_box_tests/black_box_tests.py b/cobalt/black_box_tests/black_box_tests.py
index 7ac37d0..737c875 100644
--- a/cobalt/black_box_tests/black_box_tests.py
+++ b/cobalt/black_box_tests/black_box_tests.py
@@ -63,8 +63,6 @@
     'allow_eval',
     'compression_test',
     'disable_eval_with_csp',
-    # http_cache is disabled due to flakiness. Planned update to make use of
-    # transferSize rather than load timings to make it more consistent.
     'http_cache',
     'persistent_cookie',
     'soft_mic_platform_service_test',
diff --git a/cobalt/black_box_tests/testdata/web_worker_test.html b/cobalt/black_box_tests/testdata/web_worker_test.html
index 2976cdf..9be8f5b 100644
--- a/cobalt/black_box_tests/testdata/web_worker_test.html
+++ b/cobalt/black_box_tests/testdata/web_worker_test.html
@@ -37,9 +37,10 @@
     console.log('running');
 
     // This is expected trigger a an error event on window.
-    var worker_with_error = new Worker('web_worker_test_with_syntax_error.js');
+    var worker_with_error = new Worker('web_worker_test_with_syntax_error.js',
+        { name : 'worker_with_error'});
 
-    var worker = new Worker('web_worker_test.js');
+    var worker = new Worker('web_worker_test.js', { name : 'test_worker'});
     console.log(worker);
     worker.onmessage = function (event) {
         message_event_count += 1;
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn
index e3ca3a6..4c30f02 100644
--- a/cobalt/browser/BUILD.gn
+++ b/cobalt/browser/BUILD.gn
@@ -57,21 +57,21 @@
     "//cobalt/base",
     "//net",
   ]
-  content_deps = [
+  data_deps = [
     "//cobalt/dom:licenses",
     "//cobalt/network:copy_ssl_certificates",
     "//third_party/icu:icudata",
   ]
   if (cobalt_font_package == "empty") {
-    content_deps += [ "//cobalt/content/fonts:copy_font_data" ]
+    data_deps += [ "//cobalt/content/fonts:copy_font_data" ]
   } else {
-    content_deps += [
+    data_deps += [
       "//cobalt/content/fonts:copy_fonts",
       "//cobalt/content/fonts:fonts_xml",
     ]
   }
   if (!is_gold) {
-    content_deps += [
+    data_deps += [
       "//cobalt/debug/backend/content:copy_backend_web_files",
       "//cobalt/debug/console/content:copy_console_web_files",
       "//cobalt/debug/remote/content:copy_remote_web_files",
@@ -84,9 +84,6 @@
       "//third_party/devtools:supported_css_properties",
     ]
   }
-  if (defined(platform_i18n_config_path)) {
-    content_deps += [ platform_i18n_config_path ]
-  }
 }
 
 ##############################
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc
index b6a691f..21005ba 100644
--- a/cobalt/browser/application.cc
+++ b/cobalt/browser/application.cc
@@ -512,12 +512,13 @@
     "available trackers.";
 #endif  // defined(ENABLE_DEBUGGER) && defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
 
-bool AddCrashHandlerAnnotations() {
+void AddCrashHandlerAnnotations() {
   auto crash_handler_extension =
-      SbSystemGetExtension(kCobaltExtensionCrashHandlerName);
+      static_cast<const CobaltExtensionCrashHandlerApi*>(
+          SbSystemGetExtension(kCobaltExtensionCrashHandlerName));
   if (!crash_handler_extension) {
     LOG(INFO) << "No crash handler extension, not sending annotations.";
-    return false;
+    return;
   }
 
   auto platform_info = cobalt::browser::GetUserAgentPlatformInfoFromSystem();
@@ -541,23 +542,41 @@
   product.push_back('\0');
   version.push_back('\0');
 
-  CrashpadAnnotations crashpad_annotations;
-  memset(&crashpad_annotations, 0, sizeof(CrashpadAnnotations));
-  strncpy(crashpad_annotations.user_agent_string, user_agent.c_str(),
-          USER_AGENT_STRING_MAX_SIZE);
-  strncpy(crashpad_annotations.product, product.c_str(),
-          CRASHPAD_ANNOTATION_DEFAULT_LENGTH);
-  strncpy(crashpad_annotations.version, version.c_str(),
-          CRASHPAD_ANNOTATION_DEFAULT_LENGTH);
-  bool result = static_cast<const CobaltExtensionCrashHandlerApi*>(
-                    crash_handler_extension)
-                    ->OverrideCrashpadAnnotations(&crashpad_annotations);
+  bool result = true;
+  if (crash_handler_extension->version == 1) {
+    CrashpadAnnotations crashpad_annotations;
+    memset(&crashpad_annotations, 0, sizeof(CrashpadAnnotations));
+    strncpy(crashpad_annotations.user_agent_string, user_agent.c_str(),
+            USER_AGENT_STRING_MAX_SIZE);
+    strncpy(crashpad_annotations.product, product.c_str(),
+            CRASHPAD_ANNOTATION_DEFAULT_LENGTH);
+    strncpy(crashpad_annotations.version, version.c_str(),
+            CRASHPAD_ANNOTATION_DEFAULT_LENGTH);
+    result = crash_handler_extension->OverrideCrashpadAnnotations(
+        &crashpad_annotations);
+  } else if (crash_handler_extension->version > 1) {
+    // These particular annotation key names (e.g., ver) are expected by
+    // Crashpad.
+    // TODO(b/201538792): figure out a clean way to define these constants once
+    // and use them everywhere.
+    if (!crash_handler_extension->SetString("user_agent_string",
+                                            user_agent.c_str())) {
+      result = false;
+    }
+    if (!crash_handler_extension->SetString("prod", product.c_str())) {
+      result = false;
+    }
+    if (!crash_handler_extension->SetString("ver", version.c_str())) {
+      result = false;
+    }
+  } else {
+    result = false;
+  }  // Invalid extension version
   if (result) {
     LOG(INFO) << "Sent annotations to crash handler";
   } else {
-    LOG(ERROR) << "Could not send annotations to crash handler.";
+    LOG(ERROR) << "Could not send some annotation(s) to crash handler.";
   }
-  return result;
 }
 
 }  // namespace
@@ -660,11 +679,10 @@
   unconsumed_deep_link_ = GetInitialDeepLink();
   DLOG(INFO) << "Initial deep link: " << unconsumed_deep_link_;
 
-  WebModule::Options web_options("MainWebModule");
   storage::StorageManager::Options storage_manager_options;
   network::NetworkModule::Options network_module_options;
   // Create the main components of our browser.
-  BrowserModule::Options options(web_options);
+  BrowserModule::Options options;
   network_module_options.preferred_language = language;
   network_module_options.persistent_settings = persistent_settings_.get();
   options.persistent_settings = persistent_settings_.get();
@@ -749,7 +767,7 @@
   // Set callback to be notified when a navigation occurs that destroys the
   // underlying WebModule.
   options.web_module_created_callback =
-      base::Bind(&Application::WebModuleCreated, base::Unretained(this));
+      base::Bind(&Application::MainWebModuleCreated, base::Unretained(this));
 
   // The main web module's stat tracker tracks event stats.
   options.web_module_options.track_event_stats = true;
@@ -1370,8 +1388,9 @@
 }
 #endif
 
-void Application::WebModuleCreated(WebModule* web_module) {
-  TRACE_EVENT0("cobalt::browser", "Application::WebModuleCreated()");
+void Application::MainWebModuleCreated(WebModule* web_module) {
+  TRACE_EVENT0("cobalt::browser", "Application::MainWebModuleCreated()");
+  // Note: This is a callback function that runs in the MainWebModule thread.
   DCHECK(web_module);
   if (preload_timestamp_.has_value()) {
     web_module->SetApplicationStartOrPreloadTimestamp(
@@ -1499,10 +1518,9 @@
     // again after the next WebModule is created.
     unconsumed_deep_link_ = link;
     deep_link = unconsumed_deep_link_;
+    deep_link_timestamp_ = timestamp;
   }
 
-  deep_link_timestamp_ = timestamp;
-
   LOG(INFO) << "Dispatching deep link: " << deep_link;
   DispatchEventInternal(new base::DeepLinkEvent(
       deep_link, base::Bind(&Application::OnDeepLinkConsumedCallback,
@@ -1516,11 +1534,17 @@
 
 void Application::DispatchDeepLinkIfNotConsumed() {
   std::string deep_link;
+#if SB_API_VERSION >= 13
+  SbTimeMonotonic timestamp;
+#endif  // SB_API_VERSION >= 13
   // This block exists to ensure that the lock is held while accessing
   // unconsumed_deep_link_.
   {
     base::AutoLock auto_lock(unconsumed_deep_link_lock_);
     deep_link = unconsumed_deep_link_;
+#if SB_API_VERSION >= 13
+    timestamp = deep_link_timestamp_;
+#endif  // SB_API_VERSION >= 13
   }
 
   if (!deep_link.empty()) {
@@ -1531,7 +1555,7 @@
   }
 #if SB_API_VERSION >= 13
   if (browser_module_) {
-    browser_module_->SetDeepLinkTimestamp(deep_link_timestamp_);
+    browser_module_->SetDeepLinkTimestamp(timestamp);
   }
 #endif  // SB_API_VERSION >= 13
 }
diff --git a/cobalt/browser/application.h b/cobalt/browser/application.h
index d2ad7ca..96480b7 100644
--- a/cobalt/browser/application.h
+++ b/cobalt/browser/application.h
@@ -90,7 +90,7 @@
 #endif
 
   // Called when a navigation occurs in the BrowserModule.
-  void WebModuleCreated(WebModule* web_module);
+  void MainWebModuleCreated(WebModule* web_module);
 
   void CollectUnloadEventTimingInfo(base::TimeTicks start_time,
                                     base::TimeTicks end_time);
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index b80da96..780f86d 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -341,11 +341,12 @@
 
   // Setup our main web module to have the H5VCC API injected into it.
   DCHECK(!ContainsKey(
+      options_.web_module_options.injected_global_object_attributes, "h5vcc"));
+  DCHECK(!ContainsKey(
       options_.web_module_options.web_options.injected_global_object_attributes,
       "h5vcc"));
-  options_.web_module_options.web_options
-      .injected_global_object_attributes["h5vcc"] =
-      base::Bind(&BrowserModule::CreateH5vcc, base::Unretained(this));
+  options_.web_module_options.injected_global_object_attributes["h5vcc"] =
+      base::Bind(&BrowserModule::CreateH5vccCallback, base::Unretained(this));
 
   if (command_line->HasSwitch(switches::kDisableTimerResolutionLimit)) {
     options_.web_module_options.limit_performance_timer_resolution = false;
@@ -515,7 +516,7 @@
   if (web_module_) {
     lifecycle_observers_.RemoveObserver(web_module_.get());
   }
-  web_module_.reset(NULL);
+  web_module_.reset();
 
   // Increment the navigation generation so that we can attach it to event
   // callbacks as a way of identifying the new web module from the old ones.
@@ -608,7 +609,8 @@
   options.web_options.service_worker_jobs =
       service_worker_registry_.service_worker_jobs();
   options.web_options.platform_info = platform_info_.get();
-  web_module_.reset(new WebModule(
+  web_module_.reset(new WebModule("MainWebModule"));
+  web_module_->Run(
       url, application_state_,
       base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
                  base::Unretained(this), main_web_module_generation_),
@@ -616,7 +618,7 @@
       base::Bind(&BrowserModule::OnWindowClose, base::Unretained(this)),
       base::Bind(&BrowserModule::OnWindowMinimize, base::Unretained(this)),
       can_play_type_handler_.get(), media_module_.get(), viewport_size,
-      GetResourceProvider(), kLayoutMaxRefreshFrequencyInHz, options));
+      GetResourceProvider(), kLayoutMaxRefreshFrequencyInHz, options);
   lifecycle_observers_.AddObserver(web_module_.get());
 
   if (system_window_) {
@@ -686,6 +688,7 @@
     const base::Optional<math::Rect>& clip_rect,
     const base::Closure& done_callback) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::RequestScreenshotToFile()");
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   DCHECK(screen_shot_writer_);
 
   scoped_refptr<render_tree::Node> render_tree;
@@ -704,6 +707,7 @@
     const base::Optional<math::Rect>& clip_rect,
     const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::RequestScreenshotToMemory()");
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   DCHECK(screen_shot_writer_);
 
   scoped_refptr<render_tree::Node> render_tree;
@@ -898,6 +902,7 @@
 }
 
 void BrowserModule::OnWindowSizeChanged(const ViewportSize& viewport_size) {
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   if (web_module_) {
     web_module_->SetSize(viewport_size);
   }
@@ -964,6 +969,7 @@
 
 void BrowserModule::OnCaptionSettingsChanged(
     const base::AccessibilityCaptionSettingsChangedEvent* event) {
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   if (web_module_) {
     web_module_->InjectCaptionSettingsChangedEvent();
   }
@@ -972,6 +978,7 @@
 #if SB_API_VERSION >= 13
 void BrowserModule::OnDateTimeConfigurationChanged(
     const base::DateTimeConfigurationChangedEvent* event) {
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   icu::TimeZone::adoptDefault(icu::TimeZone::detectHostTimeZone());
   if (web_module_) {
     web_module_->UpdateDateTimeConfiguration();
@@ -1122,12 +1129,14 @@
 }
 
 void BrowserModule::OnWindowOnOnlineEvent(const base::Event* event) {
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   if (web_module_) {
     web_module_->InjectWindowOnOnlineEvent(event);
   }
 }
 
 void BrowserModule::OnWindowOnOfflineEvent(const base::Event* event) {
+  DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   if (web_module_) {
     web_module_->InjectWindowOnOfflineEvent(event);
   }
@@ -2021,13 +2030,15 @@
   }
 }
 
-scoped_refptr<script::Wrappable> BrowserModule::CreateH5vcc(
-    script::EnvironmentSettings* settings) {
-  DCHECK(web_module_);
+scoped_refptr<script::Wrappable> BrowserModule::CreateH5vccCallback(
+    WebModule* web_module, web::EnvironmentSettings* settings) {
+  DCHECK_NE(base::MessageLoop::current(), self_message_loop_);
+  // Note: This is a callback function that runs in the MainWebModule thread.
+  // The web_module_ member can not be safely used in this function.
 
   h5vcc::H5vcc::Settings h5vcc_settings;
   h5vcc_settings.set_media_source_setting_func = base::Bind(
-      &WebModule::SetMediaSourceSetting, base::Unretained(web_module_.get()));
+      &WebModule::SetMediaSourceSetting, base::Unretained(web_module));
   h5vcc_settings.network_module = network_module_;
 #if SB_IS(EVERGREEN)
   h5vcc_settings.updater_module = updater_module_;
@@ -2035,22 +2046,27 @@
   h5vcc_settings.account_manager = account_manager_;
   h5vcc_settings.event_dispatcher = event_dispatcher_;
 
-  auto* dom_settings = base::polymorphic_downcast<dom::DOMSettings*>(settings);
-
-  h5vcc_settings.user_agent_data =
-      dom_settings->window()->navigator()->user_agent_data();
-  h5vcc_settings.global_environment =
-      dom_settings->context()->global_environment();
+  h5vcc_settings.user_agent_data = settings->context()
+                                       ->GetWindowOrWorkerGlobalScope()
+                                       ->navigator_base()
+                                       ->user_agent_data();
+  h5vcc_settings.global_environment = settings->context()->global_environment();
   h5vcc_settings.persistent_settings = options_.persistent_settings;
 
   auto* h5vcc_object = new h5vcc::H5vcc(h5vcc_settings);
   if (!web_module_created_callback_.is_null()) {
-    web_module_created_callback_.Run(web_module_.get());
+    web_module_created_callback_.Run(web_module);
   }
   return scoped_refptr<script::Wrappable>(h5vcc_object);
 }
 
 void BrowserModule::SetDeepLinkTimestamp(SbTimeMonotonic timestamp) {
+  if (base::MessageLoop::current() != self_message_loop_) {
+    self_message_loop_->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&BrowserModule::SetDeepLinkTimestamp,
+                              base::Unretained(this), timestamp));
+    return;
+  }
   DCHECK(web_module_);
   web_module_->SetDeepLinkTimestamp(timestamp);
 }
diff --git a/cobalt/browser/browser_module.h b/cobalt/browser/browser_module.h
index 6730fa2..646b750 100644
--- a/cobalt/browser/browser_module.h
+++ b/cobalt/browser/browser_module.h
@@ -99,9 +99,8 @@
   // All browser subcomponent options should have default constructors that
   // setup reasonable default options.
   struct Options {
-    explicit Options(const WebModule::Options& web_options)
-        : web_module_options(web_options),
-          command_line_auto_mem_settings(
+    Options()
+        : command_line_auto_mem_settings(
               memory_settings::AutoMemSettings::kTypeCommandLine),
           build_auto_mem_settings(memory_settings::AutoMemSettings::kTypeBuild),
           enable_splash_screen_on_reloads(true) {}
@@ -465,9 +464,10 @@
   // Returns the topic used, or an empty Optional if a topic isn't found.
   base::Optional<std::string> SetSplashScreenTopicFallback(const GURL& url);
 
-  // Function that creates the H5vcc object that will be injected into WebModule
-  scoped_refptr<script::Wrappable> CreateH5vcc(
-      script::EnvironmentSettings* settings);
+  // Callback function that creates the H5vcc object that will be injected into
+  // the MainWebModule.
+  scoped_refptr<script::Wrappable> CreateH5vccCallback(
+      WebModule* web_module, web::EnvironmentSettings* settings);
 
   // Validates the PersistentSettings for cache backend, if in use.
   void ValidateCacheBackendSettings();
diff --git a/cobalt/browser/debug_console.cc b/cobalt/browser/debug_console.cc
index 72069e3..1f0bba5 100644
--- a/cobalt/browser/debug_console.cc
+++ b/cobalt/browser/debug_console.cc
@@ -97,7 +97,7 @@
 scoped_refptr<script::Wrappable> CreateDebugHub(
     const debug::console::DebugHub::GetHudModeCallback& get_hud_mode_function,
     const debug::CreateDebugClientCallback& create_debug_client_callback,
-    script::EnvironmentSettings* settings) {
+    web::EnvironmentSettings* settings) {
   return new debug::console::DebugHub(get_hud_mode_function,
                                       create_debug_client_callback);
 }
@@ -116,7 +116,7 @@
     const base::Closure& maybe_freeze_callback) {
   mode_ = GetInitialMode();
 
-  WebModule::Options web_module_options("DebugConsoleWebModule");
+  WebModule::Options web_module_options;
   // The debug console does not load any image assets.
   web_module_options.image_cache_capacity = 0;
   // Disable CSP for the Debugger's WebModule. This will also allow eval() in
@@ -143,15 +143,15 @@
   web_module_options.web_options.network_module = network_module;
   web_module_options.web_options.platform_info = platform_info;
 
-  web_module_.reset(
-      new WebModule(GURL(kInitialDebugConsoleUrl), initial_application_state,
-                    render_tree_produced_callback,
-                    base::Bind(&DebugConsole::OnError, base::Unretained(this)),
-                    WebModule::CloseCallback(), /* window_close_callback */
-                    base::Closure(),            /* window_minimize_callback */
-                    NULL /* can_play_type_handler */, NULL /* media_module */,
-                    window_dimensions, resource_provider, layout_refresh_rate,
-                    web_module_options));
+  web_module_.reset(new WebModule("DebugConsoleWebModule"));
+  web_module_->Run(GURL(kInitialDebugConsoleUrl), initial_application_state,
+                   render_tree_produced_callback,
+                   base::Bind(&DebugConsole::OnError, base::Unretained(this)),
+                   WebModule::CloseCallback(), /* window_close_callback */
+                   base::Closure(),            /* window_minimize_callback */
+                   NULL /* can_play_type_handler */, NULL /* media_module */,
+                   window_dimensions, resource_provider, layout_refresh_rate,
+                   web_module_options);
 }
 
 DebugConsole::~DebugConsole() {}
diff --git a/cobalt/browser/on_screen_keyboard_starboard_bridge.cc b/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
index 39b998c..ccd818e 100644
--- a/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
+++ b/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
@@ -96,5 +96,34 @@
   return SbWindowSetOnScreenKeyboardKeepFocus(sb_window_provider_.Run(),
                                               keep_focus);
 }
+
+void OnScreenKeyboardStarboardBridge::SetBackgroundColor(uint8 r, uint8 g,
+                                                         uint8 b) {
+  const CobaltExtensionOnScreenKeyboardApi* on_screen_keyboard_extension =
+      static_cast<const CobaltExtensionOnScreenKeyboardApi*>(
+          SbSystemGetExtension(kCobaltExtensionOnScreenKeyboardName));
+
+  if (on_screen_keyboard_extension &&
+      strcmp(on_screen_keyboard_extension->name,
+             kCobaltExtensionOnScreenKeyboardName) == 0 &&
+      on_screen_keyboard_extension->version >= 1) {
+    on_screen_keyboard_extension->SetBackgroundColor(sb_window_provider_.Run(),
+                                                     r, g, b);
+  }
+}
+
+void OnScreenKeyboardStarboardBridge::SetLightTheme(bool light_theme) {
+  const CobaltExtensionOnScreenKeyboardApi* on_screen_keyboard_extension =
+      static_cast<const CobaltExtensionOnScreenKeyboardApi*>(
+          SbSystemGetExtension(kCobaltExtensionOnScreenKeyboardName));
+
+  if (on_screen_keyboard_extension &&
+      strcmp(on_screen_keyboard_extension->name,
+             kCobaltExtensionOnScreenKeyboardName) == 0 &&
+      on_screen_keyboard_extension->version >= 1) {
+    on_screen_keyboard_extension->SetLightTheme(sb_window_provider_.Run(),
+                                                light_theme);
+  }
+}
 }  // namespace browser
 }  // namespace cobalt
diff --git a/cobalt/browser/on_screen_keyboard_starboard_bridge.h b/cobalt/browser/on_screen_keyboard_starboard_bridge.h
index f73cc11..682f2f4 100644
--- a/cobalt/browser/on_screen_keyboard_starboard_bridge.h
+++ b/cobalt/browser/on_screen_keyboard_starboard_bridge.h
@@ -19,6 +19,7 @@
 
 #include "base/callback.h"
 #include "cobalt/dom/on_screen_keyboard_bridge.h"
+#include "cobalt/extension/on_screen_keyboard.h"
 #include "starboard/window.h"
 
 namespace cobalt {
@@ -58,6 +59,10 @@
 
   void SetKeepFocus(bool keep_focus) override;
 
+  void SetBackgroundColor(uint8 r, uint8 g, uint8 b) override;
+
+  void SetLightTheme(bool light_theme) override;
+
  private:
   base::Callback<SbWindow()> sb_window_provider_;
 };
diff --git a/cobalt/browser/splash_screen.cc b/cobalt/browser/splash_screen.cc
index 2739ec0..952f790 100644
--- a/cobalt/browser/splash_screen.cc
+++ b/cobalt/browser/splash_screen.cc
@@ -67,7 +67,7 @@
       self_message_loop_(base::MessageLoop::current()),
       on_splash_screen_shutdown_complete_(on_splash_screen_shutdown_complete),
       shutdown_signaled_(false) {
-  WebModule::Options web_module_options("SplashScreenWebModule");
+  WebModule::Options web_module_options;
 
   // We want the splash screen to load and appear as quickly as possible, so
   // we set it and its image decoding thread to be high priority.
@@ -106,13 +106,14 @@
   web_module_options.web_options.platform_info = platform_info;
 
   DCHECK(url_to_pass);
-  web_module_.reset(new WebModule(
-      *url_to_pass, initial_application_state, render_tree_produced_callback_,
-      base::Bind(&OnError), on_window_close,
-      base::Closure(),  // window_minimize_callback
-      NULL /* can_play_type_handler */, NULL /* media_module */,
-      window_dimensions, resource_provider, layout_refresh_rate,
-      web_module_options));
+  web_module_.reset(new WebModule("SplashScreenWebModule"));
+  web_module_->Run(*url_to_pass, initial_application_state,
+                   render_tree_produced_callback_, base::Bind(&OnError),
+                   on_window_close,
+                   base::Closure(),  // window_minimize_callback
+                   NULL /* can_play_type_handler */, NULL /* media_module */,
+                   window_dimensions, resource_provider, layout_refresh_rate,
+                   web_module_options);
 }
 
 SplashScreen::~SplashScreen() {
diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc
index ecc0f56..a7cfd00 100644
--- a/cobalt/browser/web_module.cc
+++ b/cobalt/browser/web_module.cc
@@ -1320,15 +1320,32 @@
   } while (event && !layout_manager_->IsRenderTreePending());
 }
 
-WebModule::Options::Options(const std::string& name)
-    : web_options(name),
-      layout_trigger(layout::LayoutManager::kOnDocumentMutation),
+WebModule::Options::Options()
+    : layout_trigger(layout::LayoutManager::kOnDocumentMutation),
       mesh_cache_capacity(configuration::Configuration::GetInstance()
                               ->CobaltMeshCacheSizeInBytes()) {
   web_options.stack_size = cobalt::browser::kWebModuleStackSize;
 }
 
-WebModule::WebModule(
+WebModule::WebModule(const std::string& name)
+    : ui_nav_root_(new ui_navigation::NavItem(
+          ui_navigation::kNativeItemTypeContainer,
+          // Currently, events do not need to be processed for the root item.
+          base::Closure(), base::Closure(), base::Closure())) {
+  web_agent_.reset(new web::Agent(name));
+}
+
+WebModule::~WebModule() {
+  DCHECK(message_loop());
+  DCHECK(web_agent_);
+  // This will cancel the timers for tasks, which help the thread exit
+  ClearAllIntervalsAndTimeouts();
+  web_agent_->WaitUntilDone();
+  web_agent_->Stop();
+  web_agent_.reset();
+}
+
+void WebModule::Run(
     const GURL& initial_url, base::ApplicationState initial_application_state,
     const OnRenderTreeProducedCallback& render_tree_produced_callback,
     OnErrorCallback error_callback, const CloseCallback& window_close_callback,
@@ -1336,11 +1353,7 @@
     media::CanPlayTypeHandler* can_play_type_handler,
     media::MediaModule* media_module, const ViewportSize& window_dimensions,
     render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
-    const Options& options)
-    : ui_nav_root_(new ui_navigation::NavItem(
-          ui_navigation::kNativeItemTypeContainer,
-          // Currently, events do not need to be processed for the root item.
-          base::Closure(), base::Closure(), base::Closure())) {
+    const Options& options) {
   ConstructionData construction_data(
       initial_url, initial_application_state, render_tree_produced_callback,
       error_callback, window_close_callback, window_minimize_callback,
@@ -1351,24 +1364,27 @@
 #endif  // defined(ENABLE_DEBUGGER)
       &synchronous_loader_interrupt_, options);
 
-  web_agent_.reset(
-      new web::Agent(options.web_options,
-                     base::Bind(&WebModule::Initialize, base::Unretained(this),
-                                construction_data),
-                     this));
+  web::Agent::Options web_options(options.web_options);
+  if (!options.injected_global_object_attributes.empty()) {
+    for (Options::InjectedGlobalObjectAttributes::const_iterator iter =
+             options.injected_global_object_attributes.begin();
+         iter != options.injected_global_object_attributes.end(); ++iter) {
+      DCHECK(!ContainsKey(web_options.injected_global_object_attributes,
+                          iter->first));
+      // Trampoline to the given callback, adding a pointer to this WebModule.
+      web_options.injected_global_object_attributes[iter->first] =
+          base::Bind(iter->second, base::Unretained(this));
+    }
+  }
+
+  web_agent_->Run(web_options,
+                  base::Bind(&WebModule::InitializeTaskInThread,
+                             base::Unretained(this), construction_data),
+                  this);
 }
 
-WebModule::~WebModule() {
-  DCHECK(message_loop());
-  DCHECK(web_agent_);
-  // This will cancel the timers for tasks, which help the thread exit
-  ClearAllIntervalsAndTimeouts();
-  web_agent_->WaitUntilDone();
-  web_agent_.reset();
-}
-
-void WebModule::Initialize(const ConstructionData& data,
-                           web::Context* context) {
+void WebModule::InitializeTaskInThread(const ConstructionData& data,
+                                       web::Context* context) {
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
   impl_.reset(new Impl(context, data));
 }
diff --git a/cobalt/browser/web_module.h b/cobalt/browser/web_module.h
index 3e442c8..a29c1bd 100644
--- a/cobalt/browser/web_module.h
+++ b/cobalt/browser/web_module.h
@@ -55,6 +55,7 @@
 #include "cobalt/web/blob.h"
 #include "cobalt/web/context.h"
 #include "cobalt/web/csp_delegate.h"
+#include "cobalt/web/environment_settings.h"
 #include "cobalt/web/user_agent_platform_info.h"
 #include "cobalt/webdriver/session_driver.h"
 #include "starboard/atomic.h"
@@ -90,9 +91,15 @@
                   public LifecycleObserver {
  public:
   struct Options {
+    typedef base::Callback<scoped_refptr<script::Wrappable>(
+        WebModule*, web::EnvironmentSettings*)>
+        CreateObjectFunction;
+    typedef base::hash_map<std::string, CreateObjectFunction>
+        InjectedGlobalObjectAttributes;
+
     // All optional parameters defined in this structure should have their
     // values initialized in the default constructor to useful defaults.
-    explicit Options(const std::string& name);
+    Options();
 
     web::Agent::Options web_options;
 
@@ -240,6 +247,11 @@
     // time.
     base::Callback<void(base::TimeTicks, base::TimeTicks)>
         collect_unload_event_time_callback;
+
+    // injected_global_attributes contains a map of attributes to be injected
+    // into the Web Agent's window object upon construction.  This provides
+    // a mechanism to inject custom APIs into the Web Agent object.
+    InjectedGlobalObjectAttributes injected_global_object_attributes;
   };
 
   typedef layout::LayoutManager::LayoutResults LayoutResults;
@@ -248,18 +260,19 @@
   typedef base::Callback<void(const GURL&, const std::string&)> OnErrorCallback;
   typedef dom::Window::CloseCallback CloseCallback;
 
-  WebModule(const GURL& initial_url,
-            base::ApplicationState initial_application_state,
-            const OnRenderTreeProducedCallback& render_tree_produced_callback,
-            OnErrorCallback error_callback,
-            const CloseCallback& window_close_callback,
-            const base::Closure& window_minimize_callback,
-            media::CanPlayTypeHandler* can_play_type_handler,
-            media::MediaModule* media_module,
-            const cssom::ViewportSize& window_dimensions,
-            render_tree::ResourceProvider* resource_provider,
-            float layout_refresh_rate, const Options& options);
+  explicit WebModule(const std::string& name);
   ~WebModule();
+  void Run(const GURL& initial_url,
+           base::ApplicationState initial_application_state,
+           const OnRenderTreeProducedCallback& render_tree_produced_callback,
+           OnErrorCallback error_callback,
+           const CloseCallback& window_close_callback,
+           const base::Closure& window_minimize_callback,
+           media::CanPlayTypeHandler* can_play_type_handler,
+           media::MediaModule* media_module,
+           const cssom::ViewportSize& window_dimensions,
+           render_tree::ResourceProvider* resource_provider,
+           float layout_refresh_rate, const Options& options);
 
   // Injects an on screen keyboard input event into the web module. The value
   // for type represents beforeinput or input.
@@ -391,7 +404,7 @@
 
  private:
   // Data required to construct a WebModule, initialized in the constructor and
-  // passed to |Initialize|.
+  // passed to |InitializeTaskInThread|.
   struct ConstructionData {
     ConstructionData(const GURL& initial_url,
                      base::ApplicationState initial_application_state,
@@ -453,9 +466,10 @@
   // Forward declaration of the private implementation class.
   class Impl;
 
-  // Called by the constructor to create the private implementation object and
+  // Called by |Run| to create the private implementation object and
   // perform any other initialization required on the dedicated thread.
-  void Initialize(const ConstructionData& data, web::Context* context);
+  void InitializeTaskInThread(const ConstructionData& data,
+                              web::Context* context);
 
   void ClearAllIntervalsAndTimeouts();
 
@@ -467,7 +481,7 @@
   // The message loop this object is running on.
   base::MessageLoop* message_loop() const {
     DCHECK(web_agent_);
-    return web_agent_->message_loop();
+    return web_agent_ ? web_agent_->message_loop() : nullptr;
   }
 
   // Private implementation object.
diff --git a/cobalt/build/build.id b/cobalt/build/build.id
new file mode 100644
index 0000000..ce753b0
--- /dev/null
+++ b/cobalt/build/build.id
@@ -0,0 +1 @@
+309312
\ No newline at end of file
diff --git a/cobalt/demos/content/watchdog-demo/index.html b/cobalt/demos/content/watchdog-demo/index.html
index c5f17f8..d23e7d4 100644
--- a/cobalt/demos/content/watchdog-demo/index.html
+++ b/cobalt/demos/content/watchdog-demo/index.html
@@ -63,7 +63,7 @@
           } else if (watchdogFunction == 'unregister') {
             ret = h5vcc.crashLog.unregister('test-name');
           } else if (watchdogFunction == 'ping') {
-            ret = h5vcc.crashLog.ping('test-name', `test-ping`);
+            ret = h5vcc.crashLog.ping('test-name', 'test-ping');
           } else if (watchdogFunction == 'getWatchdogViolations') {
             ret = h5vcc.crashLog.getWatchdogViolations();
           } else if (watchdogFunction == 'getPersistentSettingWatchdogEnable') {
diff --git a/cobalt/dom/BUILD.gn b/cobalt/dom/BUILD.gn
index f014522..ff47acc 100644
--- a/cobalt/dom/BUILD.gn
+++ b/cobalt/dom/BUILD.gn
@@ -439,5 +439,5 @@
     "//url",
   ]
 
-  content_deps = [ ":licenses" ]
+  data_deps = [ ":licenses" ]
 }
diff --git a/cobalt/dom/on_screen_keyboard.cc b/cobalt/dom/on_screen_keyboard.cc
index 54c2242..6f8da1b 100644
--- a/cobalt/dom/on_screen_keyboard.cc
+++ b/cobalt/dom/on_screen_keyboard.cc
@@ -23,6 +23,63 @@
 
 namespace cobalt {
 namespace dom {
+namespace {
+bool IsValidRGB(int r, int g, int b) {
+  if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
+    return false;
+  } else {
+    return true;
+  }
+}
+
+bool ParseColor(const char* color_str, int& r, int& g, int& b) {
+  size_t len = strlen(color_str);
+  if (len == 0) {
+    return false;
+  }
+
+  // Handle hexadecimal color notation #RRGGBB
+  int r_tmp, g_tmp, b_tmp;
+  bool is_hex =
+      SbStringScanF(color_str, "#%02x%02x%02x", &r_tmp, &g_tmp, &b_tmp) == 3;
+  if (is_hex && IsValidRGB(r_tmp, g_tmp, b_tmp)) {
+    r = r_tmp;
+    g = g_tmp;
+    b = b_tmp;
+    return true;
+  }
+
+  // Handle rgb color notation rgb(R, G, B)
+  if (!is_hex && len >= 10 &&
+      SbStringCompareNoCaseN("rgb(", color_str, 4) == 0) {
+    int rgb_tmp[3] = {-1, -1, -1};
+    const char* ptr = color_str + 4;
+    int i = 0;
+    while (*ptr) {
+      if (isdigit(*ptr)) {
+        char* end;
+        rgb_tmp[i++] = static_cast<int>(strtol(ptr, &end, 10));
+        if (i == 3) {
+          break;
+        }
+        ptr = (const char*)end;
+      } else if (isspace(*ptr) || *ptr == ',') {
+        ptr++;
+      } else {
+        return false;
+      }
+    }
+
+    if (IsValidRGB(rgb_tmp[0], rgb_tmp[1], rgb_tmp[2])) {
+      r = rgb_tmp[0];
+      g = rgb_tmp[1];
+      b = rgb_tmp[2];
+      return true;
+    }
+  }
+  return false;
+}
+}  // namespace
 
 OnScreenKeyboard::OnScreenKeyboard(
     script::EnvironmentSettings* settings, OnScreenKeyboardBridge* bridge,
@@ -46,6 +103,21 @@
           .second;
   DCHECK(is_emplaced);
   bridge_->Show(data_.c_str(), ticket);
+
+  if (background_color_.has_value()) {
+    int r, g, b;
+    if (ParseColor(background_color_.value().c_str(), r, g, b)) {
+      bridge_->SetBackgroundColor(static_cast<uint8>(r), static_cast<uint8>(g),
+                                  static_cast<uint8>(b));
+    } else {
+      LOG(WARNING) << "Invalid on-screen keyboard background color: "
+                   << background_color_.value();
+    }
+  }
+  if (light_theme_.has_value()) {
+    bridge_->SetLightTheme(light_theme_.value());
+  }
+
   return promise;
 }
 
diff --git a/cobalt/dom/on_screen_keyboard.h b/cobalt/dom/on_screen_keyboard.h
index 8690c95..ecad6bf 100644
--- a/cobalt/dom/on_screen_keyboard.h
+++ b/cobalt/dom/on_screen_keyboard.h
@@ -99,6 +99,18 @@
   void set_keep_focus(bool keep_focus);
   bool keep_focus() const { return keep_focus_; }
 
+  void set_background_color(base::Optional<std::string>& background_color) {
+    background_color_ = background_color;
+  }
+  base::Optional<std::string> background_color() const {
+    return background_color_;
+  }
+
+  void set_light_theme(base::Optional<bool> light_theme) {
+    light_theme_ = light_theme;
+  }
+  base::Optional<bool> light_theme() const { return light_theme_; }
+
   // Called by the WebModule to dispatch DOM show, hide, focus, blur and
   // suggestions updated events.
   void DispatchHideEvent(int ticket);
@@ -135,6 +147,10 @@
 
   bool suggestions_supported_;
 
+  base::Optional<std::string> background_color_;
+
+  base::Optional<bool> light_theme_;
+
   DISALLOW_COPY_AND_ASSIGN(OnScreenKeyboard);
 };
 }  // namespace dom
diff --git a/cobalt/dom/on_screen_keyboard.idl b/cobalt/dom/on_screen_keyboard.idl
index ebe632c..9487eec 100644
--- a/cobalt/dom/on_screen_keyboard.idl
+++ b/cobalt/dom/on_screen_keyboard.idl
@@ -43,4 +43,15 @@
   attribute EventHandler onfocus;
   attribute EventHandler onblur;
   attribute EventHandler oninput;
+
+  // This attribute overrides the background color of on-screen keyboard.
+  // The attribute is only fully supported on Apple TV at the moment.
+  // backgroundColor should be a string of below formats:
+  // 1. hex color notation #RRGGBB, e.g. "#0000FF".
+  // 2. rgb color notation rgb(R, G, B), e.g. "rgb(0, 0, 255)".
+  attribute DOMString? backgroundColor;
+
+  // This attribute overrides the light theme of on-screen keyboard.
+  // The attribute is only fully supported on Apple TV at the moment.
+  attribute boolean? lightTheme;
 };
diff --git a/cobalt/dom/on_screen_keyboard_bridge.h b/cobalt/dom/on_screen_keyboard_bridge.h
index d58c0ad..ac6ad1c 100644
--- a/cobalt/dom/on_screen_keyboard_bridge.h
+++ b/cobalt/dom/on_screen_keyboard_bridge.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "base/basictypes.h"
 #include "cobalt/dom/dom_rect.h"
 
 namespace cobalt {
@@ -39,6 +40,8 @@
   virtual scoped_refptr<DOMRect> BoundingRect() const = 0;
   virtual void SetKeepFocus(bool keep_focus) = 0;
   virtual bool IsValidTicket(int ticket) const = 0;
+  virtual void SetBackgroundColor(uint8 r, uint8 g, uint8 b) = 0;
+  virtual void SetLightTheme(bool light_theme) = 0;
 };
 
 }  // namespace dom
diff --git a/cobalt/dom/on_screen_keyboard_test.cc b/cobalt/dom/on_screen_keyboard_test.cc
index 4ab1e6f..8a8573c 100644
--- a/cobalt/dom/on_screen_keyboard_test.cc
+++ b/cobalt/dom/on_screen_keyboard_test.cc
@@ -112,6 +112,10 @@
 
   void SetKeepFocus(bool keep_focus) override { SetKeepFocusMock(keep_focus); }
 
+  void SetBackgroundColor(uint8 r, uint8 g, uint8 b) override {}
+
+  void SetLightTheme(bool light_theme) override {}
+
   MOCK_METHOD1(ShowMock, void(std::string));
   MOCK_METHOD0(HideMock, void());
   MOCK_METHOD0(BlurMock, void());
@@ -604,5 +608,49 @@
   EXPECT_TRUE(EvaluateScript(script, NULL));
 }
 
+TEST_F(OnScreenKeyboardTest, SetBackgroundColor) {
+  if (SkipLocale()) return;
+
+  std::string result;
+  EXPECT_TRUE(
+      EvaluateScript("window.onScreenKeyboard.backgroundColor;", &result));
+  EXPECT_EQ("null", result);
+
+  std::string color_str = "#0000FF";
+  EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.backgroundColor = '" +
+                                 color_str +
+                                 "';"
+                                 "window.onScreenKeyboard.backgroundColor",
+                             &result));
+
+  EXPECT_EQ(color_str, result);
+
+  color_str = "rgb(0, 0, 100)";
+  EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.backgroundColor = '" +
+                                 color_str +
+                                 "';"
+                                 "window.onScreenKeyboard.backgroundColor",
+                             &result));
+
+  EXPECT_EQ(color_str, result);
+}
+
+TEST_F(OnScreenKeyboardTest, SetLightTheme) {
+  if (SkipLocale()) return;
+
+  std::string result;
+  EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.lightTheme;", &result));
+  EXPECT_EQ("null", result);
+
+  std::string light_theme_str = "true";
+  EXPECT_TRUE(
+      EvaluateScript("window.onScreenKeyboard.lightTheme = " + light_theme_str +
+                         ";"
+                         "window.onScreenKeyboard.lightTheme",
+                     &result));
+
+  EXPECT_EQ(light_theme_str, result);
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/cobalt/encoding/BUILD.gn b/cobalt/encoding/BUILD.gn
index e1f0665..904043f 100644
--- a/cobalt/encoding/BUILD.gn
+++ b/cobalt/encoding/BUILD.gn
@@ -52,5 +52,5 @@
     "//testing/gtest",
   ]
 
-  content_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/extension/crash_handler.h b/cobalt/extension/crash_handler.h
index 8cf4732..912caba 100644
--- a/cobalt/extension/crash_handler.h
+++ b/cobalt/extension/crash_handler.h
@@ -36,8 +36,15 @@
 
   // The fields below this point were added in version 1 or later.
 
+  // Deprecated in version 2 and later.
   bool (*OverrideCrashpadAnnotations)(
       CrashpadAnnotations* crashpad_annotations);
+
+  // The fields below this point were added in version 2 or later.
+
+  // Sets a (key, value) pair for the handler to include when annotating a
+  // crash. Returns true on success and false on failure.
+  bool (*SetString)(const char* key, const char* value);
 } CobaltExtensionCrashHandlerApi;
 
 #ifdef __cplusplus
diff --git a/cobalt/extension/extension_test.cc b/cobalt/extension/extension_test.cc
index cd7158d..58f051e 100644
--- a/cobalt/extension/extension_test.cc
+++ b/cobalt/extension/extension_test.cc
@@ -221,9 +221,14 @@
   }
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
-  EXPECT_EQ(extension_api->version, 1u);
+  EXPECT_GE(extension_api->version, 1u);
+  EXPECT_LE(extension_api->version, 2u);
   EXPECT_NE(extension_api->OverrideCrashpadAnnotations, nullptr);
 
+  if (extension_api->version >= 2) {
+    EXPECT_NE(extension_api->SetString, nullptr);
+  }
+
   const ExtensionApi* second_extension_api =
       static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
   EXPECT_EQ(second_extension_api, extension_api)
diff --git a/cobalt/extension/on_screen_keyboard.h b/cobalt/extension/on_screen_keyboard.h
new file mode 100644
index 0000000..315e2b7
--- /dev/null
+++ b/cobalt/extension/on_screen_keyboard.h
@@ -0,0 +1,51 @@
+// 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_EXTENSION_ON_SCREEN_KEYBOARD_H_
+#define COBALT_EXTENSION_ON_SCREEN_KEYBOARD_H_
+
+#include "starboard/system.h"
+#include "starboard/window.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define kCobaltExtensionOnScreenKeyboardName \
+  "dev.cobalt.extension.OnScreenKeyboard"
+
+typedef struct CobaltExtensionOnScreenKeyboardApi {
+  // Name should be the string
+  // |kCobaltExtensionOnScreenKeyboardName|. This helps to validate that
+  // the extension API is correct.
+  const char* name;
+
+  // This specifies the version of the API that is implemented.
+  uint32_t version;
+
+  // The fields below this point were added in version 1 or later.
+
+  // This function overrides the background color of on-screen keyboard in RGB
+  // color space, where r, g, b are between 0 and 255.
+  void (*SetBackgroundColor)(SbWindow window, uint8_t r, uint8_t g, uint8_t b);
+
+  // This function overrides the light theme of on-screen keyboard.
+  void (*SetLightTheme)(SbWindow window, bool light_theme);
+} CobaltExtensionOnScreenKeyboardApi;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_EXTENSION_ON_SCREEN_KEYBOARD_H_
diff --git a/cobalt/h5vcc/h5vcc_crash_log.cc b/cobalt/h5vcc/h5vcc_crash_log.cc
index 737d64c..a240d92 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.cc
+++ b/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -20,6 +20,7 @@
 #include "base/atomicops.h"
 #include "base/memory/singleton.h"
 #include "base/synchronization/lock.h"
+#include "cobalt/extension/crash_handler.h"
 
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #include STARBOARD_CORE_DUMP_HANDLER_INCLUDE
@@ -97,6 +98,15 @@
 
 bool H5vccCrashLog::SetString(const std::string& key,
                               const std::string& value) {
+  auto crash_handler_extension =
+      static_cast<const CobaltExtensionCrashHandlerApi*>(
+          SbSystemGetExtension(kCobaltExtensionCrashHandlerName));
+  if (crash_handler_extension && crash_handler_extension->version >= 2) {
+    return crash_handler_extension->SetString(key.c_str(), value.c_str());
+  }
+  // The platform has not implemented a version of the CrashHandler Cobalt
+  // Extension appropriate for this use case.
+
   // Forward the call to a global singleton so that we keep a consistent crash
   // log globally.
   CrashLogDictionary::GetInstance()->SetString(key, value);
diff --git a/cobalt/h5vcc/h5vcc_crash_log.idl b/cobalt/h5vcc/h5vcc_crash_log.idl
index 7892219..e93caac 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.idl
+++ b/cobalt/h5vcc/h5vcc_crash_log.idl
@@ -56,9 +56,8 @@
   boolean ping(DOMString name, DOMString ping_info);
 
   // Returns a json string containing the Watchdog violations since the last
-  // 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.
+  // call, up to 200. Clears internal cache of Watchdog violations to prevent
+  // duplicates. Timestamps are stored as strings due to int size constraints.
   // Example json:
   // {
   //   "test-name":{
diff --git a/cobalt/layout_tests/BUILD.gn b/cobalt/layout_tests/BUILD.gn
index 092b8df..328176d 100644
--- a/cobalt/layout_tests/BUILD.gn
+++ b/cobalt/layout_tests/BUILD.gn
@@ -64,15 +64,15 @@
     "//url",
   ]
 
-  content_deps = [
+  data_deps = [
     "//cobalt/layout_tests/testdata:layout_copy_test_data",
     "//cobalt/network:copy_ssl_certificates",
     "//third_party/icu:icudata",
   ]
   if (cobalt_font_package == "empty") {
-    content_deps += [ "//cobalt/content/fonts:copy_font_data" ]
+    data_deps += [ "//cobalt/content/fonts:copy_font_data" ]
   } else {
-    content_deps += [
+    data_deps += [
       "//cobalt/content/fonts:copy_fonts",
       "//cobalt/content/fonts:fonts_xml",
     ]
@@ -99,5 +99,5 @@
     "//url",
   ]
 
-  content_deps = [ "//cobalt/layout_tests/testdata:layout_copy_test_data" ]
+  data_deps = [ "//cobalt/layout_tests/testdata:layout_copy_test_data" ]
 }
diff --git a/cobalt/layout_tests/layout_snapshot.cc b/cobalt/layout_tests/layout_snapshot.cc
index 57e3769..88d07f3 100644
--- a/cobalt/layout_tests/layout_snapshot.cc
+++ b/cobalt/layout_tests/layout_snapshot.cc
@@ -83,7 +83,7 @@
 
   // Use test runner mode to allow the content itself to dictate when it is
   // ready for layout should be performed.  See cobalt/dom/test_runner.h.
-  browser::WebModule::Options web_module_options("SnapshotURL");
+  browser::WebModule::Options web_module_options;
   web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
   web_module_options.image_cache_capacity = kImageCacheCapacity;
   web_module_options.provide_screenshot_function = screenshot_provider;
@@ -97,7 +97,8 @@
   base::Optional<browser::WebModule::LayoutResults> results;
 
   // Create the WebModule and wait for a layout to occur.
-  browser::WebModule web_module(
+  browser::WebModule web_module("SnapshotURL");
+  web_module.Run(
       url, base::kApplicationStateStarted,
       base::Bind(&WebModuleOnRenderTreeProducedCallback, &results, &run_loop,
                  base::MessageLoop::current()),
diff --git a/cobalt/layout_tests/web_platform_tests.cc b/cobalt/layout_tests/web_platform_tests.cc
index 520ed72..c5406bf 100644
--- a/cobalt/layout_tests/web_platform_tests.cc
+++ b/cobalt/layout_tests/web_platform_tests.cc
@@ -209,7 +209,7 @@
       web::kCspEnforcementEnable, CspDelegatePermissive::Create);
   // Use test runner mode to allow the content itself to dictate when it is
   // ready for layout should be performed.  See cobalt/dom/test_runner.h.
-  browser::WebModule::Options web_module_options("RunWebPlatformTest");
+  browser::WebModule::Options web_module_options;
   web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
   // We assume that we won't suspend/resume while running the tests, and so
   // we take advantage of the convenience of inline script tags.
@@ -222,7 +222,8 @@
   base::RunLoop run_loop;
 
   // Create the WebModule and wait for a layout to occur.
-  browser::WebModule web_module(
+  browser::WebModule web_module("RunWebPlatformTest");
+  web_module.Run(
       url, base::kApplicationStateStarted,
       base::Bind(&WebModuleOnRenderTreeProducedCallback, &results),
       base::Bind(&WebModuleErrorCallback, &run_loop,
diff --git a/cobalt/loader/BUILD.gn b/cobalt/loader/BUILD.gn
index 9378b97..3b8f6e0 100644
--- a/cobalt/loader/BUILD.gn
+++ b/cobalt/loader/BUILD.gn
@@ -203,8 +203,6 @@
   ]
 
   data_deps = [ ":copy_loader_test_data" ]
-
-  content_deps = [ ":copy_loader_test_data" ]
 }
 
 copy("copy_loader_test_data") {
diff --git a/cobalt/media/base/pipeline.h b/cobalt/media/base/pipeline.h
index 9228cd3..4490669 100644
--- a/cobalt/media/base/pipeline.h
+++ b/cobalt/media/base/pipeline.h
@@ -57,6 +57,7 @@
   typedef ::media::PipelineStatistics PipelineStatistics;
   typedef ::media::PipelineStatus PipelineStatus;
   typedef ::media::PipelineStatusCallback PipelineStatusCallback;
+  typedef ::media::PipelineStatusCB PipelineStatusCB;
 
   typedef base::Callback<void(PipelineStatus status, bool is_initial_preroll,
                               const std::string& error_message)>
@@ -126,7 +127,7 @@
   // It is an error to call this method after the pipeline has already started.
   virtual void Start(Demuxer* demuxer,
                      const SetDrmSystemReadyCB& set_drm_system_ready_cb,
-                     PipelineStatusCallback ended_cb, const ErrorCB& error_cb,
+                     const PipelineStatusCB& ended_cb, const ErrorCB& error_cb,
                      const SeekCB& seek_cb,
                      const BufferingStateCB& buffering_state_cb,
                      const base::Closure& duration_change_cb,
@@ -140,7 +141,7 @@
                      const OnEncryptedMediaInitDataEncounteredCB&
                          encrypted_media_init_data_encountered_cb,
                      const std::string& source_url,
-                     PipelineStatusCallback ended_cb, const ErrorCB& error_cb,
+                     const PipelineStatusCB& ended_cb, const ErrorCB& error_cb,
                      const SeekCB& seek_cb,
                      const BufferingStateCB& buffering_state_cb,
                      const base::Closure& duration_change_cb,
diff --git a/cobalt/media/base/sbplayer_pipeline.cc b/cobalt/media/base/sbplayer_pipeline.cc
index dc3a212..9a8fb5b 100644
--- a/cobalt/media/base/sbplayer_pipeline.cc
+++ b/cobalt/media/base/sbplayer_pipeline.cc
@@ -76,7 +76,7 @@
 struct StartTaskParameters {
   Demuxer* demuxer;
   SetDrmSystemReadyCB set_drm_system_ready_cb;
-  PipelineStatusCallback ended_cb;
+  ::media::PipelineStatusCB ended_cb;
   ErrorCB error_cb;
   Pipeline::SeekCB seek_cb;
   Pipeline::BufferingStateCB buffering_state_cb;
@@ -113,7 +113,7 @@
 
   void Start(Demuxer* demuxer,
              const SetDrmSystemReadyCB& set_drm_system_ready_cb,
-             PipelineStatusCallback ended_cb, const ErrorCB& error_cb,
+             const PipelineStatusCB& ended_cb, const ErrorCB& error_cb,
              const SeekCB& seek_cb, const BufferingStateCB& buffering_state_cb,
              const base::Closure& duration_change_cb,
              const base::Closure& output_mode_change_cb,
@@ -123,7 +123,7 @@
   void Start(const SetDrmSystemReadyCB& set_drm_system_ready_cb,
              const OnEncryptedMediaInitDataEncounteredCB&
                  encrypted_media_init_data_encountered_cb,
-             const std::string& source_url, PipelineStatusCallback ended_cb,
+             const std::string& source_url, const PipelineStatusCB& ended_cb,
              const ErrorCB& error_cb, const SeekCB& seek_cb,
              const BufferingStateCB& buffering_state_cb,
              const base::Closure& duration_change_cb,
@@ -265,7 +265,7 @@
 
   // Permanent callbacks passed in via Start().
   SetDrmSystemReadyCB set_drm_system_ready_cb_;
-  PipelineStatusCallback ended_cb_;
+  PipelineStatusCB ended_cb_;
   ErrorCB error_cb_;
   BufferingStateCB buffering_state_cb_;
   base::Closure duration_change_cb_;
@@ -441,7 +441,7 @@
 
 void SbPlayerPipeline::Start(Demuxer* demuxer,
                              const SetDrmSystemReadyCB& set_drm_system_ready_cb,
-                             PipelineStatusCallback ended_cb,
+                             const PipelineStatusCB& ended_cb,
                              const ErrorCB& error_cb, const SeekCB& seek_cb,
                              const BufferingStateCB& buffering_state_cb,
                              const base::Closure& duration_change_cb,
@@ -462,7 +462,7 @@
   StartTaskParameters parameters;
   parameters.demuxer = demuxer;
   parameters.set_drm_system_ready_cb = set_drm_system_ready_cb;
-  parameters.ended_cb = std::move(ended_cb);
+  parameters.ended_cb = ended_cb;
   parameters.error_cb = error_cb;
   parameters.seek_cb = seek_cb;
   parameters.buffering_state_cb = buffering_state_cb;
@@ -484,7 +484,7 @@
                              const OnEncryptedMediaInitDataEncounteredCB&
                                  on_encrypted_media_init_data_encountered_cb,
                              const std::string& source_url,
-                             PipelineStatusCallback ended_cb,
+                             const PipelineStatusCB& ended_cb,
                              const ErrorCB& error_cb, const SeekCB& seek_cb,
                              const BufferingStateCB& buffering_state_cb,
                              const base::Closure& duration_change_cb,
@@ -504,7 +504,7 @@
   StartTaskParameters parameters;
   parameters.demuxer = NULL;
   parameters.set_drm_system_ready_cb = set_drm_system_ready_cb;
-  parameters.ended_cb = std::move(ended_cb);
+  parameters.ended_cb = ended_cb;
   parameters.error_cb = error_cb;
   parameters.seek_cb = seek_cb;
   parameters.buffering_state_cb = buffering_state_cb;
@@ -810,7 +810,7 @@
 
   demuxer_ = parameters.demuxer;
   set_drm_system_ready_cb_ = parameters.set_drm_system_ready_cb;
-  ended_cb_ = std::move(parameters.ended_cb);
+  ended_cb_ = parameters.ended_cb;
   error_cb_ = parameters.error_cb;
   {
     base::AutoLock auto_lock(lock_);
@@ -1327,7 +1327,7 @@
       break;
     }
     case kSbPlayerStateEndOfStream:
-      std::move(ended_cb_).Run(::media::PIPELINE_OK);
+      ended_cb_.Run(::media::PIPELINE_OK);
       ended_ = true;
       break;
     case kSbPlayerStateDestroyed:
diff --git a/cobalt/media/base/starboard_player.cc b/cobalt/media/base/starboard_player.cc
index ae7defd..8c97ffb 100644
--- a/cobalt/media/base/starboard_player.cc
+++ b/cobalt/media/base/starboard_player.cc
@@ -565,12 +565,6 @@
 
   bool is_visible = SbWindowIsValid(window_);
   SbMediaAudioCodec audio_codec = audio_sample_info_.codec;
-  SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
-  // TODO: This is temporary for supporting background media playback.
-  //       Need to be removed with media refactor.
-  if (is_visible) {
-    video_codec = video_sample_info_.codec;
-  }
 
   bool has_audio = audio_codec != kSbMediaAudioCodecNone;
 
diff --git a/cobalt/media/progressive/avc_parser.cc b/cobalt/media/progressive/avc_parser.cc
index f9dcc0b..4c447db 100644
--- a/cobalt/media/progressive/avc_parser.cc
+++ b/cobalt/media/progressive/avc_parser.cc
@@ -477,6 +477,7 @@
       aac.GetChannelLayout(kSbrInMimetype),
       aac.GetOutputSamplesPerSecond(kSbrInMimetype), aac.codec_specific_data(),
       ::media::EncryptionScheme::kUnencrypted, base::TimeDelta(), 0);
+  audio_config_.set_aac_extra_data(aac.codec_specific_data());
 }
 
 size_t AVCParser::CalculatePrependSize(DemuxerStream::Type type,
diff --git a/cobalt/renderer/BUILD.gn b/cobalt/renderer/BUILD.gn
index 2fbc58b..21a89fc 100644
--- a/cobalt/renderer/BUILD.gn
+++ b/cobalt/renderer/BUILD.gn
@@ -146,11 +146,10 @@
     ":renderer_download_lottie_test_data",
   ]
 
-  content_deps = data_deps
   if (cobalt_font_package == "empty") {
-    content_deps += [ "//cobalt/content/fonts:copy_font_data" ]
+    data_deps += [ "//cobalt/content/fonts:copy_font_data" ]
   } else {
-    content_deps += [
+    data_deps += [
       "//cobalt/content/fonts:copy_fonts",
       "//cobalt/content/fonts:fonts_xml",
     ]
diff --git a/cobalt/renderer/sandbox/BUILD.gn b/cobalt/renderer/sandbox/BUILD.gn
index faebec3..30d7def 100644
--- a/cobalt/renderer/sandbox/BUILD.gn
+++ b/cobalt/renderer/sandbox/BUILD.gn
@@ -31,7 +31,7 @@
     "//cobalt/trace_event",
   ]
 
-  content_deps = [ "//cobalt/renderer/test/scenes:scenes_copy_test_data" ]
+  data_deps = [ "//cobalt/renderer/test/scenes:scenes_copy_test_data" ]
 }
 
 # This target will build a sandbox application that allows for easy
@@ -50,5 +50,5 @@
     "//cobalt/trace_event",
   ]
 
-  content_deps = [ "//cobalt/renderer/test/scenes:scenes_copy_test_data" ]
+  data_deps = [ "//cobalt/renderer/test/scenes:scenes_copy_test_data" ]
 }
diff --git a/cobalt/speech/sandbox/BUILD.gn b/cobalt/speech/sandbox/BUILD.gn
index 7853ac1..4b78a30 100644
--- a/cobalt/speech/sandbox/BUILD.gn
+++ b/cobalt/speech/sandbox/BUILD.gn
@@ -41,5 +41,5 @@
 
   deps += cobalt_platform_dependencies
 
-  content_deps = [ "//cobalt/speech:speech_testdata" ]
+  data_deps = [ "//cobalt/speech:speech_testdata" ]
 }
diff --git a/cobalt/web/agent.cc b/cobalt/web/agent.cc
index f52e113..f44a345 100644
--- a/cobalt/web/agent.cc
+++ b/cobalt/web/agent.cc
@@ -19,6 +19,7 @@
 #include <utility>
 
 #include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/script_loader_factory.h"
@@ -49,7 +50,7 @@
 namespace {
 class Impl : public Context {
  public:
-  explicit Impl(const Agent::Options& options);
+  Impl(const std::string& name, const Agent::Options& options);
   virtual ~Impl();
 
   void AddEnvironmentSettingsChangeObserver(
@@ -222,7 +223,8 @@
       environment_settings_change_observers_;
 };
 
-Impl::Impl(const Agent::Options& options) : name_(options.name) {
+Impl::Impl(const std::string& name, const Agent::Options& options)
+    : name_(name) {
   TRACE_EVENT0("cobalt::web", "Agent::Impl::Impl()");
   service_worker_jobs_ = options.service_worker_jobs;
   platform_info_ = options.platform_info;
@@ -235,7 +237,7 @@
   DCHECK(fetcher_factory_);
 
   script_loader_factory_.reset(new loader::ScriptLoaderFactory(
-      options.name.c_str(), fetcher_factory_.get(), options.thread_priority));
+      name.c_str(), fetcher_factory_.get(), options.thread_priority));
   DCHECK(script_loader_factory_);
 
   javascript_engine_ =
@@ -469,9 +471,31 @@
 
 void Agent::WillDestroyCurrentMessageLoop() { context_.reset(); }
 
-Agent::Agent(const Options& options, InitializeCallback initialize_callback,
-             DestructionObserver* destruction_observer)
-    : thread_(options.name) {
+Agent::Agent(const std::string& name) : thread_(name) {}
+
+void Agent::Stop() {
+  DCHECK(message_loop());
+  DCHECK(thread_.IsRunning());
+
+  if (context() && context()->service_worker_jobs()) {
+    context()->service_worker_jobs()->UnregisterWebContext(context());
+  }
+
+  // Ensure that the destruction observer got added before stopping the thread.
+  destruction_observer_added_.Wait();
+  // Stop the thread. This will cause the destruction observer to be notified.
+  thread_.Stop();
+}
+
+Agent::~Agent() {
+  DCHECK(!thread_.IsRunning());
+  if (thread_.IsRunning()) {
+    Stop();
+  }
+}
+
+void Agent::Run(const Options& options, InitializeCallback initialize_callback,
+                DestructionObserver* destruction_observer) {
   // Start the dedicated thread and create the internal implementation
   // object on that thread.
   base::Thread::Options thread_options(base::MessageLoop::TYPE_DEFAULT,
@@ -481,8 +505,9 @@
   DCHECK(message_loop());
 
   message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&Agent::Initialize, base::Unretained(this), options,
-                            initialize_callback));
+      FROM_HERE,
+      base::Bind(&Agent::InitializeTaskInThread, base::Unretained(this),
+                 options, initialize_callback));
 
   if (destruction_observer) {
     message_loop()->task_runner()->PostTask(
@@ -507,30 +532,17 @@
                             base::Unretained(&destruction_observer_added_)));
 }
 
-Agent::~Agent() {
-  DCHECK(message_loop());
-  DCHECK(thread_.IsRunning());
-
-  if (context() && context()->service_worker_jobs()) {
-    context()->service_worker_jobs()->UnregisterWebContext(context());
-  }
-
-  // Ensure that the destruction observer got added before stopping the thread.
-  destruction_observer_added_.Wait();
-  // Stop the thread. This will cause the destruction observer to be notified.
-  thread_.Stop();
-}
-
-void Agent::Initialize(const Options& options,
-                       InitializeCallback initialize_callback) {
+void Agent::InitializeTaskInThread(const Options& options,
+                                   InitializeCallback initialize_callback) {
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
-  context_.reset(CreateContext(options, thread_.message_loop()));
+  context_.reset(
+      CreateContext(thread_.thread_name(), options, thread_.message_loop()));
   initialize_callback.Run(context_.get());
 }
 
-Context* Agent::CreateContext(const Options& options,
+Context* Agent::CreateContext(const std::string& name, const Options& options,
                               base::MessageLoop* message_loop) {
-  auto* context = new Impl(options);
+  auto* context = new Impl(name, options);
   context->set_message_loop(message_loop);
   if (options.service_worker_jobs) {
     options.service_worker_jobs->RegisterWebContext(context);
diff --git a/cobalt/web/agent.h b/cobalt/web/agent.h
index 956a6f6..aa48bb1 100644
--- a/cobalt/web/agent.h
+++ b/cobalt/web/agent.h
@@ -40,19 +40,12 @@
 class Agent : public base::MessageLoop::DestructionObserver {
  public:
   struct Options {
-    explicit Options(const std::string name) : name(name) {}
     typedef base::Callback<scoped_refptr<script::Wrappable>(
-        script::EnvironmentSettings*)>
+        web::EnvironmentSettings*)>
         CreateObjectFunction;
     typedef base::hash_map<std::string, CreateObjectFunction>
         InjectedGlobalObjectAttributes;
 
-    // The name of the Web Agent.  This is useful for debugging purposes as
-    // in the case where multiple Web Agent objects exist, it can be used to
-    // differentiate which objects belong to which Web Agent.  It is used
-    // to name some CVals.
-    std::string name;
-
     // Specifies the priority of the web agent's thread.  This is the thread
     // that is responsible for executing JavaScript.
     base::ThreadPriority thread_priority = base::ThreadPriority::NORMAL;
@@ -83,14 +76,17 @@
       JavaScriptHeapStatisticsCallback;
 
   typedef base::Callback<void(Context*)> InitializeCallback;
-  Agent(const Options& options, InitializeCallback initialize_callback,
-        DestructionObserver* destruction_observer = nullptr);
+  explicit Agent(const std::string& name);
   ~Agent();
 
-  static Context* CreateContext(const Options& options,
+  void Run(const Options& options, InitializeCallback initialize_callback,
+           DestructionObserver* destruction_observer = nullptr);
+  void Stop();
+
+  static Context* CreateContext(const std::string& name, const Options& options,
                                 base::MessageLoop* message_loop = nullptr);
   static Context* CreateContext(const std::string& name) {
-    return CreateContext(Options(name));
+    return CreateContext(name, Options());
   }
 
   Context* context() {
@@ -118,8 +114,8 @@
  private:
   // Called by the constructor to create the private implementation object and
   // perform any other initialization required on the dedicated thread.
-  void Initialize(const Options& options,
-                  InitializeCallback initialize_callback);
+  void InitializeTaskInThread(const Options& options,
+                              InitializeCallback initialize_callback);
 
   // The thread created and owned by this Web Agent.
   // All sub-objects of this object are created on this thread, and all public
diff --git a/cobalt/web/url_utils.cc b/cobalt/web/url_utils.cc
index 462c6f4..c69bc2d 100644
--- a/cobalt/web/url_utils.cc
+++ b/cobalt/web/url_utils.cc
@@ -126,7 +126,8 @@
 //   https://www.w3.org/TR/2014/WD-url-1-20141209/#pre-update-steps
 void URLUtils::RunPreUpdateSteps(const GURL& new_url,
                                  const std::string& value) {
-  DLOG(INFO) << "Update URL to " << new_url.possibly_invalid_spec();
+  DLOG(INFO) << "Update URL to "
+             << (value.empty() ? new_url.possibly_invalid_spec() : value);
 
   // 1. If value is not given, let value be the result of serializing the
   // associated url.
diff --git a/cobalt/webdriver/BUILD.gn b/cobalt/webdriver/BUILD.gn
index abb1b08..e275c91 100644
--- a/cobalt/webdriver/BUILD.gn
+++ b/cobalt/webdriver/BUILD.gn
@@ -155,6 +155,4 @@
     ":copy_webdriver_data",
     ":webdriver_copy_test_data",
   ]
-
-  content_deps = data_deps
 }
diff --git a/cobalt/webdriver/session_driver.cc b/cobalt/webdriver/session_driver.cc
index 1c45c01..b04aa70 100644
--- a/cobalt/webdriver/session_driver.cc
+++ b/cobalt/webdriver/session_driver.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/webdriver/session_driver.h"
 
+#include <utility>
+
 #include "base/logging.h"
 #include "cobalt/base/log_message_handler.h"
 
@@ -56,13 +58,13 @@
       logging_callback_id_(0) {
   logging_callback_id_ = base::LogMessageHandler::GetInstance()->AddCallback(
       base::Bind(&SessionDriver::LogMessageHandler, base::Unretained(this)));
-  window_driver_ = create_window_driver_callback_.Run(GetUniqueWindowId());
+  RefreshWindowDriver();
 }
 
 void SessionDriver::RefreshWindowDriver() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  window_driver_ =
-      create_window_driver_callback_.Run(window_driver_->window_id());
+  window_driver_ = create_window_driver_callback_.Run(
+      window_driver_ ? window_driver_->window_id() : GetUniqueWindowId());
 }
 
 SessionDriver::~SessionDriver() {
diff --git a/cobalt/worker/dedicated_worker.cc b/cobalt/worker/dedicated_worker.cc
index 0dff482..1daf030 100644
--- a/cobalt/worker/dedicated_worker.cc
+++ b/cobalt/worker/dedicated_worker.cc
@@ -58,7 +58,7 @@
   //    allow the page to start dedicated workers).
   // 2. Let outside settings be the current settings object.
   // 3. Parse the scriptURL argument relative to outside settings.
-  Worker::Options options(kDedicatedWorkerName);
+  Worker::Options options;
   const GURL& base_url = environment_settings()->base_url();
   options.url = base_url.Resolve(script_url_);
 
@@ -83,7 +83,7 @@
   options.web_options.service_worker_jobs =
       options.outside_settings->context()->service_worker_jobs();
 
-  worker_.reset(new Worker(options));
+  worker_.reset(new Worker(kDedicatedWorkerName, options));
   // 10. Return worker.
 }
 
diff --git a/cobalt/worker/service_worker_object.cc b/cobalt/worker/service_worker_object.cc
index 436f256..e63c421 100644
--- a/cobalt/worker/service_worker_object.cc
+++ b/cobalt/worker/service_worker_object.cc
@@ -58,6 +58,7 @@
     DCHECK(message_loop());
     DCHECK(web_context_);
     web_agent_->WaitUntilDone();
+    web_agent_->Stop();
     web_agent_.reset();
     web_context_ = nullptr;
   }
@@ -123,10 +124,11 @@
 void ServiceWorkerObject::ObtainWebAgentAndWaitUntilDone() {
   TRACE_EVENT0("cobalt::worker",
                "ServiceWorkerObject::ObtainWebAgentAndWaitUntilDone()");
-  web_agent_.reset(new web::Agent(
+  web_agent_.reset(new web::Agent(options_.name));
+  web_agent_->Run(
       options_.web_options,
       base::Bind(&ServiceWorkerObject::Initialize, base::Unretained(this)),
-      this));
+      this);
   web_agent_->WaitUntilDone();
 }
 
diff --git a/cobalt/worker/service_worker_object.h b/cobalt/worker/service_worker_object.h
index 8b4daa6..a3cf5cb 100644
--- a/cobalt/worker/service_worker_object.h
+++ b/cobalt/worker/service_worker_object.h
@@ -56,15 +56,16 @@
  public:
   // Worker Options needed at thread run time.
   struct Options {
-    explicit Options(
+    Options(
         const std::string& name, network::NetworkModule* network_module,
         ServiceWorkerRegistrationObject* containing_service_worker_registration)
-        : web_options(name),
+        : name(name),
           containing_service_worker_registration(
               containing_service_worker_registration) {
       web_options.network_module = network_module;
     }
 
+    std::string name;
     web::Agent::Options web_options;
     ServiceWorkerRegistrationObject* containing_service_worker_registration;
   };
diff --git a/cobalt/worker/worker.cc b/cobalt/worker/worker.cc
index a820c8f..a716910 100644
--- a/cobalt/worker/worker.cc
+++ b/cobalt/worker/worker.cc
@@ -43,7 +43,7 @@
 bool PermitAnyURL(const GURL&, bool) { return true; }
 }  // namespace
 
-Worker::Worker(const Options& options) : options_(options) {
+Worker::Worker(const char* name, const Options& options) : options_(options) {
   // Algorithm for 'run a worker'
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#run-a-worker
   // 1. Let is shared be true if worker is a SharedWorker object, and false
@@ -57,9 +57,15 @@
   // 6. Let agent be the result of obtaining a dedicated/shared worker agent
   //    given outside settings and is shared. Run the rest of these steps in
   //    that agent.
-  web_agent_.reset(new web::Agent(
-      options.web_options,
-      base::Bind(&Worker::Initialize, base::Unretained(this)), this));
+  std::string agent_name(name);
+  if (!options.options.name().empty()) {
+    agent_name.push_back(':');
+    agent_name.append(options.options.name());
+  }
+  web_agent_.reset(new web::Agent(agent_name));
+  web_agent_->Run(options.web_options,
+                  base::Bind(&Worker::Initialize, base::Unretained(this)),
+                  this);
 }
 
 void Worker::WillDestroyCurrentMessageLoop() {
@@ -126,7 +132,7 @@
 #endif  // ENABLE_DEBUGGER
 
   // 10. Set worker global scope's name to the value of options's name member.
-  dedicated_worker_global_scope->set_name(options_.web_options.name);
+  dedicated_worker_global_scope->set_name(options_.options.name());
   // 11. Append owner to worker global scope's owner set.
   // 12. If is shared is true, then:
   //     1. Set worker global scope's constructor origin to outside settings's
@@ -309,6 +315,7 @@
   if (web_agent_) {
     DCHECK(message_loop());
     web_agent_->WaitUntilDone();
+    web_agent_->Stop();
     web_agent_.reset();
     web_context_ = nullptr;
   }
diff --git a/cobalt/worker/worker.h b/cobalt/worker/worker.h
index 2c488ec..feea239 100644
--- a/cobalt/worker/worker.h
+++ b/cobalt/worker/worker.h
@@ -55,8 +55,6 @@
  public:
   // Worker Options needed at thread run time.
   struct Options {
-    explicit Options(const std::string& name) : web_options(name) {}
-
     web::Agent::Options web_options;
 
     // True if worker is a SharedWorker object, and false otherwise.
@@ -70,7 +68,7 @@
     WorkerOptions options;
   };
 
-  explicit Worker(const Options& options);
+  Worker(const char* name, const Options& options);
   ~Worker();
   Worker(const Worker&) = delete;
   Worker& operator=(const Worker&) = delete;
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 866c00e..1044e1d 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4229,7 +4229,7 @@
   ]
 
   if (is_starboard) {
-    content_deps = [
+    data_deps = [
       ":net_unittest_files",
       ":third_party_unittest_files",
       "//third_party/icu:icudata",
diff --git a/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers b/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers
index fce2271..d41294c 100644
--- a/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers
+++ b/net/data/url_request_unittest/redirect302-to-echo-cacheable.mock-http-headers
@@ -2,3 +2,4 @@
 Location: /echo
 Content-Length: 1
 Cache-control: max-age=60000
+Content-Type: example/unit_test
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
index c8ca20a..2a42726 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -336,6 +336,7 @@
             GetFilenameFromEntryFileKeyAndFileIndex(entry_file_key_, i));
 #if defined(STARBOARD)
         ok = false;
+        // Note: Files can not be renamed on Starboard.
 #else
         ok = base::ReplaceFile(old_name, new_name, &out_error) && ok;
 #endif
@@ -350,6 +351,7 @@
           path_.AppendASCII(GetSparseFilenameFromEntryFileKey(entry_file_key_));
 #if defined(STARBOARD)
       ok = false;
+      // Note: Files can not be renamed on Starboard.
 #else
       ok = base::ReplaceFile(old_name, new_name, &out_error) && ok;
 #endif
diff --git a/net/disk_cache/simple/simple_util.cc b/net/disk_cache/simple/simple_util.cc
index 5c1f5a7..513de96 100644
--- a/net/disk_cache/simple/simple_util.cc
+++ b/net/disk_cache/simple/simple_util.cc
@@ -63,20 +63,30 @@
 std::string GetFilenameFromEntryFileKeyAndFileIndex(
     const SimpleFileTracker::EntryFileKey& key,
     int file_index) {
+#if defined(STARBOARD)
+  return base::StringPrintf("%016" PRIx64 "_%1d", key.entry_hash, file_index);
+#else
+  // Files are not renamed on Starboard.
   if (key.doom_generation == 0)
     return base::StringPrintf("%016" PRIx64 "_%1d", key.entry_hash, file_index);
   else
     return base::StringPrintf("todelete_%016" PRIx64 "_%1d_%" PRIu64,
                               key.entry_hash, file_index, key.doom_generation);
+#endif
 }
 
 std::string GetSparseFilenameFromEntryFileKey(
     const SimpleFileTracker::EntryFileKey& key) {
+#if defined(STARBOARD)
+  return base::StringPrintf("%016" PRIx64 "_s", key.entry_hash);
+#else
+  // Files are not renamed on Starboard.
   if (key.doom_generation == 0)
     return base::StringPrintf("%016" PRIx64 "_s", key.entry_hash);
   else
     return base::StringPrintf("todelete_%016" PRIx64 "_s_%" PRIu64,
                               key.entry_hash, key.doom_generation);
+#endif
 }
 
 std::string GetFilenameFromKeyAndFileIndex(const std::string& key,
diff --git a/net/http/http_cache_lookup_manager_unittest.cc b/net/http/http_cache_lookup_manager_unittest.cc
index ac7dec9..d3776d7 100644
--- a/net/http/http_cache_lookup_manager_unittest.cc
+++ b/net/http/http_cache_lookup_manager_unittest.cc
@@ -37,7 +37,8 @@
 std::unique_ptr<MockTransaction> CreateMockTransaction(const GURL& url) {
   MockTransaction mock_trans = {
       url.spec().c_str(), "GET", base::Time(), "", LOAD_NORMAL,
-      "HTTP/1.1 200 OK",
+      "HTTP/1.1 200 OK\n"
+      "Content-Type: example/unit_test",
       "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
       "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
       base::Time(), "<html><body>Google Blah Blah</body></html>",
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 9f13b95..5758e00 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -68,7 +68,8 @@
 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"};
+    "font/ttf",  "font/woff",     "font/woff2", "text/javascript",
+    "example/unit_test", "application/javascript"};
 #endif
 
 constexpr TimeDelta kStaleRevalidateTimeout = TimeDelta::FromSeconds(60);
@@ -3085,14 +3086,11 @@
   // 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;
-      }
+  bool is_allowed_mime_type = false;
+  for (auto allowed_type : kMimeTypesCacheAllowlist) {
+    if (mime_type.compare(allowed_type) == 0) {
+      is_allowed_mime_type = true;
+      break;
     }
   }
 #else
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 0740dc8..0d887ad 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -369,7 +369,8 @@
     base::Time(),
     "",
     LOAD_VALIDATE_CACHE,
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test\n",
     "Cache-Control: max-age=10000\n",
     base::Time(),
     "<html><body>Google Blah Blah</body></html>",
@@ -437,6 +438,10 @@
 // AddHeadersFromString() (_not_ AddHeaderFromString()).
 #define EXTRA_HEADER EXTRA_HEADER_LINE "\r\n"
 
+// Adds the unit_test MIME type to the EXTRA_HEADER for compatibility
+// with http_cache_transaction that now will require a MIME type.
+#define MIME_TYPE_EXTRA_HEADER "Content-Type: example/unit_test\r\n" EXTRA_HEADER
+
 static const char kExtraHeaderKey[] = "Extra";
 
 // Static.
@@ -477,7 +482,10 @@
       ranges.size() != 1) {
     // This is not a byte range request. We return 200.
     response_status->assign("HTTP/1.1 200 OK");
-    response_headers->assign("Date: Wed, 28 Nov 2007 09:40:09 GMT");
+    // Adds the unit_test MIME type to the response_headers for compatibility
+    // with http_cache_transaction that now will require a MIME type.
+    response_headers->assign("Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
+                             "Content-Type: example/unit_test");
     response_data->assign("Not a range");
     return;
   }
@@ -537,7 +545,8 @@
 const MockTransaction kRangeGET_TransactionOK = {
     "http://www.google.com/range", "GET", base::Time(),
     "Range: bytes = 40-49\r\n" EXTRA_HEADER, LOAD_NORMAL,
-    "HTTP/1.1 206 Partial Content",
+    "HTTP/1.1 206 Partial Content\n"
+    "Content-Type: example/unit_test",
     "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
     "ETag: \"foo\"\n"
     "Accept-Ranges: bytes\n"
@@ -4759,7 +4768,8 @@
   MockHttpCache cache;
 
   ScopedMockTransaction transaction(kETagGET_Transaction);
-  transaction.status = "HTTP/1.0 200 OK";
+  transaction.status = "HTTP/1.0 200 OK\n"
+                       "Content-Type: example/unit_test\n";
 
   // Write to the cache.
   RunTransactionTest(cache.http_cache(), transaction);
@@ -4782,7 +4792,8 @@
   MockHttpCache cache;
 
   ScopedMockTransaction transaction(kETagGET_Transaction);
-  transaction.status = "HTTP/1.0 200 OK";
+  transaction.status = "HTTP/1.0 200 OK\n"
+                       "Content-Type: example/unit_test\n";
 
   // Write to the cache.
   RunTransactionTest(cache.http_cache(), transaction);
@@ -4961,7 +4972,8 @@
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache1) {
   // First network response for |kUrl|.
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
     "body1"
@@ -4969,7 +4981,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Last-Modified: Fri, 03 Jul 2009 02:14:27 GMT\n",
     "body2"
@@ -4987,7 +5000,8 @@
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache2) {
   // First network response for |kUrl|.
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Etag: \"ETAG1\"\n"
     "Expires: Wed, 7 Sep 2033 21:46:42 GMT\n",  // Should never expire.
@@ -4996,7 +5010,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Etag: \"ETAG2\"\n"
     "Expires: Wed, 7 Sep 2033 21:46:42 GMT\n",  // Should never expire.
@@ -5015,7 +5030,8 @@
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache3) {
   // First network response for |kUrl|.
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Server: server1\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5024,7 +5040,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 304 Not Modified",
+    "HTTP/1.1 304 Not Modified\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Server: server2\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5035,7 +5052,8 @@
     "HTTP/1.1 200 OK",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Server: server2\n"
-    "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
+    "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n"
+    "Content-Type: example/unit_test\n",
     "body1"
   };
 
@@ -5055,7 +5073,8 @@
   const char kUrl[] = "http://foobar.com/main.css";
 
   static const Response kNetResponse = {
-    "HTTP/1.1 304 Not Modified",
+    "HTTP/1.1 304 Not Modified\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
     ""
@@ -5099,7 +5118,8 @@
   const char kUrl[] = "http://foobar.com/main.css";
 
   static const Response kNetResponse = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
     "foobar!!!"
@@ -5140,7 +5160,8 @@
 // (the if-modified-since date is 2 days AFTER the cache's modification date).
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache6) {
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Server: server1\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5149,7 +5170,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 304 Not Modified",
+    "HTTP/1.1 304 Not Modified\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Server: server2\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5170,7 +5192,8 @@
 // response (304) to update the cache.
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache7) {
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Etag: \"Foo1\"\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5179,7 +5202,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 304 Not Modified",
+    "HTTP/1.1 304 Not Modified\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Etag: \"Foo2\"\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5197,7 +5221,8 @@
 // and if-modified-since updates the cache.
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache8) {
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Etag: \"Foo1\"\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5206,7 +5231,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Etag: \"Foo2\"\n"
     "Last-Modified: Fri, 03 Jul 2009 02:14:27 GMT\n",
@@ -5225,7 +5251,8 @@
 // and if-modified-since does not update the cache with only one match.
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache9) {
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Etag: \"Foo1\"\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5234,7 +5261,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Etag: \"Foo2\"\n"
     "Last-Modified: Fri, 03 Jul 2009 02:14:27 GMT\n",
@@ -5254,7 +5282,8 @@
 // and if-modified-since does not update the cache with only one match.
 TEST_F(HttpCacheTest, ConditionalizedRequestUpdatesCache10) {
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Etag: \"Foo1\"\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
@@ -5263,7 +5292,8 @@
 
   // Second network response for |kUrl|.
   static const Response kNetResponse2 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
     "Etag: \"Foo2\"\n"
     "Last-Modified: Fri, 03 Jul 2009 02:14:27 GMT\n",
@@ -5654,7 +5684,9 @@
 
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_EQ("HTTP/1.1 200 OK\nContent-Length: 42\n", headers);
+  EXPECT_EQ("HTTP/1.1 200 OK\n"
+            "Content-Type: example/unit_test\n"
+            "Content-Length: 42\n", headers);
   RemoveMockTransaction(&transaction);
 }
 
@@ -7050,7 +7082,8 @@
 TEST_F(HttpCacheTest, RangeGET_301) {
   MockHttpCache cache;
   ScopedMockTransaction transaction(kRangeGET_TransactionOK);
-  transaction.status = "HTTP/1.1 301 Moved Permanently";
+  transaction.status = "HTTP/1.1 301 Moved Permanently\n"
+                       "Content-Type: example/unit_test";
   transaction.response_headers = "Location: http://www.bar.com/\n";
   transaction.data = "";
   transaction.handler = NULL;
@@ -8394,6 +8427,7 @@
   ScopedMockTransaction transaction(kRangeGET_TransactionOK);
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
+                          "Content-Type: example/unit_test\n"
                           "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
                           "ETag: \"foo\"\n"
                           "Accept-Ranges: bytes\n"
@@ -8411,6 +8445,7 @@
       "HTTP/1.1 200 OK\n"
       "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
       "Accept-Ranges: bytes\n"
+      "Content-Type: example/unit_test\n"
       "ETag: \"foo\"\n"
       "Content-Length: 80\n");
 
@@ -8552,7 +8587,8 @@
   // The server will return 200 instead of a byte range.
   std::string expected_headers(
       "HTTP/1.1 200 OK\n"
-      "Date: Wed, 28 Nov 2007 09:40:09 GMT\n");
+      "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
+      "Content-Type: example/unit_test\n");
 
   EXPECT_EQ(expected_headers, headers);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
@@ -8607,6 +8643,7 @@
   AddMockTransaction(&kRangeGET_TransactionOK);
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
+                          "Content-Type: example/unit_test\n"
                           "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
                           "ETag: \"foo\"\n"
                           "Accept-Ranges: bytes\n"
@@ -8699,7 +8736,7 @@
 
   // Now make a regular request.
   std::string headers;
-  transaction.request_headers = EXTRA_HEADER;
+  transaction.request_headers = MIME_TYPE_EXTRA_HEADER;
   transaction.data = "Not a range";
   RangeTransactionServer handler;
   handler.set_bad_200(true);
@@ -8720,6 +8757,7 @@
   ScopedMockTransaction transaction(kRangeGET_TransactionOK);
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
+                          "Content-Type: example/unit_test\n"
                           "Last-Modified: Sat, 18 Apr 2009 01:10:43 GMT\n"
                           "ETag: \"foo\"\n"
                           "Accept-Ranges: bytes\n"
@@ -8842,7 +8880,8 @@
   MockHttpCache cache;
 
   ScopedMockTransaction kTestTransaction(kSimpleGET_Transaction);
-  kTestTransaction.status = "HTTP/1.1 301 Moved Permanently";
+  kTestTransaction.status = "HTTP/1.1 301 Moved Permanently\n"
+                            "Content-Type: example/unit_test";
   kTestTransaction.response_headers = "Location: http://www.bar.com/\n";
 
   MockHttpRequest request(kTestTransaction);
@@ -9138,7 +9177,8 @@
   request.data = kData;
 
   static const Response kNetResponse1 = {
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Fri, 12 Jun 2009 21:46:42 GMT\n"
     "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
     kData
@@ -9154,7 +9194,8 @@
   request.load_flags = LOAD_VALIDATE_CACHE;
 
   static const Response kNetResponse2 = {
-    "HTTP/1.1 304 Not Modified",
+    "HTTP/1.1 304 Not Modified\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 22 Jul 2009 03:15:26 GMT\n",
     ""
   };
@@ -9178,6 +9219,7 @@
 
   EXPECT_EQ("HTTP/1.1 200 OK\n"
             "Date: Wed, 22 Jul 2009 03:15:26 GMT\n"
+            "Content-Type: example/unit_test\n"
             "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n",
             ToSimpleString(response.headers));
 
@@ -9673,6 +9715,7 @@
   AddMockTransaction(&kRangeGET_TransactionOK);
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
+                          "Content-Type: example/unit_test\n"
                           "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
                           "ETag: \"foo\"\n"
                           "Accept-Ranges: bytes\n"
@@ -10091,6 +10134,7 @@
   AddMockTransaction(&kRangeGET_TransactionOK);
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
+                          "Content-Type: example/unit_test\n"
                           "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
                           "ETag: \"foo\"\n"
                           "Accept-Ranges: bytes\n"
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index 9b9c92c..8914895 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -48,7 +48,8 @@
     base::Time(),
     "",
     LOAD_NORMAL,
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Cache-Control: max-age=10000\n",
     base::Time(),
     "<html><body>Google Blah Blah</body></html>",
@@ -67,7 +68,8 @@
     base::Time(),
     "",
     LOAD_NORMAL,
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "",
     base::Time(),
     "<html><body>Google Blah Blah</body></html>",
@@ -86,7 +88,8 @@
     base::Time(),
     "",
     LOAD_NORMAL,
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
     "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
     base::Time(),
@@ -106,7 +109,8 @@
     base::Time(),
     "",
     LOAD_NORMAL,
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Cache-Control: max-age=10000\n"
     "Etag: \"foopy\"\n",
     base::Time(),
@@ -126,7 +130,8 @@
     base::Time(),
     "Range: 0-100\r\n",
     LOAD_NORMAL,
-    "HTTP/1.1 200 OK",
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: example/unit_test",
     "Cache-Control: max-age=10000\n",
     base::Time(),
     "<html><body>Google Blah Blah</body></html>",
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index b4a9ab4..e55c504 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1156,12 +1156,6 @@
     raw_header_size_ = GetTotalReceivedBytes();
 
     ConvertRealLoadTimesToBlockingTimes(&load_timing_info_);
-#if defined (STARBOARD)
-    load_timing_info_.encoded_body_size = static_cast<uint64_t>(GetTotalReceivedBytes());
-    if (!load_timing_info_callback_.is_null()) {
-      load_timing_info_callback_.Run(load_timing_info_);
-    }
-#endif  // defined(STARBOARD)
   }
 }
 
@@ -1171,6 +1165,13 @@
   if (has_notified_completion_)
     return;
 
+  #if defined (STARBOARD)
+    load_timing_info_.encoded_body_size = static_cast<uint64_t>(GetTotalReceivedBytes());
+    if (load_timing_info_callback_) {
+      load_timing_info_callback_.Run(load_timing_info_);
+    }
+  #endif  // defined(STARBOARD)
+
   is_pending_ = false;
   is_redirecting_ = false;
   has_notified_completion_ = true;
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index fc7b92b..e0f4358 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -494,6 +494,7 @@
   {
     MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
     MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
+                                 "Content-Type: example/unit_test\r\n"
                                  "Content-Length: 12\r\n\r\n"),
                         MockRead("Test Content")};
 
diff --git a/starboard/android/apk/app/CMakeLists.txt b/starboard/android/apk/app/CMakeLists.txt
index d5d598f..5b1cb17 100644
--- a/starboard/android/apk/app/CMakeLists.txt
+++ b/starboard/android/apk/app/CMakeLists.txt
@@ -38,6 +38,13 @@
   )
 endif()
 
+# Abort gracefully if the config has not been generated. This should only happen
+# when building from Android Studio which builds all supported ABIs by default.
+if(NOT EXISTS "${COBALT_PRODUCT_DIR}")
+  message(WARNING "android-${COBALT_ARCH}_${COBALT_CONFIG} not configured. Please run GN. Skipping config.")
+  return()
+endif()
+
 # If COBALT_CONTENT_DIR isn't set for a particular deploy target use the
 # 'content/data' directory.
 if(NOT COBALT_CONTENT_DIR)
@@ -47,9 +54,9 @@
 endif()
 
 # If COBALT_LIBRARY_DIR isn't set for a particular deploy target use the
-# toplevel lib/ subdirectory.
+# product root.
 if(NOT COBALT_LIBRARY_DIR)
-  set(COBALT_LIBRARY_DIR ${COBALT_PRODUCT_DIR}/lib)
+  set(COBALT_LIBRARY_DIR ${COBALT_PRODUCT_DIR})
 endif()
 
 # For platform deploy builds, use the -n parameter to skip Cobalt ninja and
@@ -76,9 +83,11 @@
 # ("cobalt_content" never gets created, so this runs every time.)
 add_custom_command(OUTPUT cobalt_content
     DEPENDS coat_lib
+    COMMAND ${CMAKE_COMMAND} -E make_directory
+            ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../../../../${COBALT_CONFIG}
     COMMAND ${CMAKE_COMMAND} -E create_symlink
             ${COBALT_CONTENT_DIR}
-            ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../../../../cobalt_content
+            ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../../../../${COBALT_CONFIG}/cobalt_content
 )
 
 # We need a target (not a file) for the phony native dependency below.
diff --git a/starboard/android/apk/app/build.gradle b/starboard/android/apk/app/build.gradle
index 424a48d..5622927 100644
--- a/starboard/android/apk/app/build.gradle
+++ b/starboard/android/apk/app/build.gradle
@@ -135,16 +135,16 @@
         }
         // Add the directories symlinked by the CMake "cobalt_content" custom command.
         debug {
-            assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
+            assets.srcDir "${buildDir}/intermediates/cxx/debug/cobalt_content"
         }
         devel {
-            assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
+            assets.srcDir "${buildDir}/intermediates/cxx/devel/cobalt_content"
         }
         qa {
-            assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
+            assets.srcDir "${buildDir}/intermediates/cxx/qa/cobalt_content"
         }
         release {
-            assets.srcDir "${buildDir}/intermediates/cxx/cobalt_content"
+            assets.srcDir "${buildDir}/intermediates/cxx/gold/cobalt_content"
         }
     }
     externalNativeBuild {
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index eeed7a7..1478b37 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -781,6 +781,10 @@
       Log.e(TAG, "Failed to set operating rate with invalid fps " + mFps);
       return;
     }
+    if (mMediaCodec == null) {
+      Log.e(TAG, "Failed to set operating rate when media codec is null.");
+      return;
+    }
     double operatingRate = mPlaybackRate * mFps;
     Bundle b = new Bundle();
     b.putFloat(MediaFormat.KEY_OPERATING_RATE, (float) operatingRate);
diff --git a/starboard/android/apk/build.gradle b/starboard/android/apk/build.gradle
index 3db6ad6..1247002 100644
--- a/starboard/android/apk/build.gradle
+++ b/starboard/android/apk/build.gradle
@@ -42,17 +42,16 @@
     }
 }
 
-task clean(type: Delete) {
-    delete rootProject.buildDir
-}
-
 // Move the 'buildDir' for all projects into sub-directories of a shared top-level build directory,
 // which is either the root's original 'buildDir' or a custom location when building for platform
 // deploy.  Note that the platform deploy action sets a custom 'cobaltGradleDir' property rather
 // than setting 'buildDir' directly on the command line since Gradle tries to get smart about
 // 'buildDir' which can end up putting it at the wrong depth in the file system.
-def rootBuildDir = hasProperty('cobaltGradleDir') ? new File(cobaltGradleDir, 'build') : buildDir
-allprojects { buildDir = new File(rootBuildDir, project.name).canonicalFile }
+allprojects { buildDir = new File(gradle.ext.rootBuildDir, project.name).canonicalFile }
+
+task clean(type: Delete) {
+    delete gradle.ext.rootBuildDir
+}
 
 // Android Studio Gradle plugin 3.5+ builds all supported ABIs for a connected device. Override the
 // property it sets to build just the first one, which is the preferred ABI for the device.
diff --git a/starboard/android/apk/settings.gradle b/starboard/android/apk/settings.gradle
index 4fe2070..3b60efd 100644
--- a/starboard/android/apk/settings.gradle
+++ b/starboard/android/apk/settings.gradle
@@ -15,3 +15,7 @@
 include ':app'
 
 println "GRADLE VERSION: $gradle.gradleVersion"
+
+gradle.ext.rootBuildDir = hasProperty('cobaltGradleDir')
+    ? new File(cobaltGradleDir, 'build')
+    : new File(rootDir, "/../../../out/android_studio/gradle/build").canonicalFile
diff --git a/starboard/build/config/BUILDCONFIG.gn b/starboard/build/config/BUILDCONFIG.gn
index 41086c5..774be60 100644
--- a/starboard/build/config/BUILDCONFIG.gn
+++ b/starboard/build/config/BUILDCONFIG.gn
@@ -197,9 +197,7 @@
 # content targets.
 import("//starboard/build/config/install.gni")
 
-# Set up the method of generating the install targets as defined by the
-# platform.
-import("$install_target_path")
+# Template for copying content files to the install directory.
 template("install_content") {
   target(invoker.target_type, target_name) {
     forward_variables_from(invoker, "*", [ "install_content" ])
@@ -209,6 +207,8 @@
   }
 
   if (defined(invoker.install_content) && invoker.install_content) {
+    # We're using a custom script to copy the files here because rebase_path
+    # can't be used with {{}} expansions in the outputs of a copy target.
     action("${target_name}_install_content") {
       forward_variables_from(invoker, [ "testonly" ])
 
@@ -216,6 +216,14 @@
 
       sources = get_target_outputs(":${invoker.target_name}")
 
+      # List of files in metadata so they can be forwarded for bundling.
+      metadata = {
+        install_content = sources
+
+        # Don't inherit any metadata from upstream deps.
+        ignore_install_content = deps
+      }
+
       install_content_dir = "$sb_install_output_dir/$sb_install_content_subdir"
       outputs = []
       foreach(source, sources) {
@@ -242,6 +250,9 @@
   }
 }
 
+# Set up the method of generating the install targets as defined by the
+# platform.
+import("$install_target_path")
 template("action") {
   install_content(target_name) {
     forward_variables_from(invoker, "*")
@@ -266,68 +277,28 @@
 template("executable") {
   target_with_platform_configs(target_name) {
     target_type = "executable"
-    forward_variables_from(invoker,
-                           "*",
-                           [
-                             "install_target",
-                             "content_deps",
-                           ])
+    forward_variables_from(invoker, "*", [ "install_target" ])
   }
 
   if (current_toolchain == default_toolchain &&
       (!defined(invoker.install_target) || invoker.install_target)) {
     executable_target_name = target_name
 
-    content_deps = []
-    if (defined(invoker.content_deps)) {
-      content_deps += invoker.content_deps
+    data_deps = []
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
     }
     if (defined(platform_i18n_config_path)) {
-      content_deps += [ platform_i18n_config_path ]
+      data_deps += [ platform_i18n_config_path ]
     }
 
     if (separate_install_targets_for_bundling) {
-      bundle_deps = []
-      foreach(content_dep, content_deps) {
-        bundle_deps += [ "${content_dep}_install_content" ]
-      }
-
-      # TODO(b/220024845): Make this much better.
-      action("${executable_target_name}_bundle_content") {
+      import("//starboard/build/config/bundle_content.gni")
+      bundle_content("${executable_target_name}_bundle_content") {
         forward_variables_from(invoker, [ "testonly" ])
 
-        deps = [ ":$executable_target_name" ]
-        deps += bundle_deps
-
-        sources = []
-        foreach(bundle_dep, bundle_deps) {
-          # Split the target path and name. E.g. //path/to:target -> [ "//path/to", "target" ].
-          target_path_and_name = string_split(bundle_dep, ":")
-
-          # The content_dep target will place its file under the root gen dir.
-          content_dep_list_file = string_join("/",
-                                              [
-                                                root_gen_dir,
-                                                target_path_and_name[1],
-                                              ])
-          target_path_and_name = []
-
-          sources += [ "${content_dep_list_file}_files.tmp" ]
-        }
-
-        bundle_content_dir = "$sb_install_output_dir/$executable_target_name/$sb_install_content_subdir"
-
-        # TODO(b/220024845): We don't have the list of output files.
-        outputs = [ bundle_content_dir ]
-
-        script = "//starboard/build/copy_bundle_content.py"
-        args = [
-          "--output_dir",
-          rebase_path(bundle_content_dir, root_build_dir),
-          "--base_dir",
-          rebase_path(sb_static_contents_output_data_dir, root_build_dir),
-        ]
-        args += rebase_path(sources, root_build_dir)
+        bundle_name = executable_target_name
+        bundle_deps = data_deps
       }
     }
 
@@ -339,12 +310,12 @@
       if (defined(invoker.deps)) {
         deps += invoker.deps
       }
+      foreach(dep, data_deps) {
+        deps += [ "${dep}_install_content" ]
+      }
       if (separate_install_targets_for_bundling) {
         deps += [ ":${executable_target_name}_bundle_content" ]
       }
-      foreach(content_dep, content_deps) {
-        deps += [ "${content_dep}_install_content" ]
-      }
     }
   }
 }
@@ -352,68 +323,29 @@
 template("shared_library") {
   target_with_platform_configs(target_name) {
     target_type = "shared_library"
-    forward_variables_from(invoker,
-                           "*",
-                           [
-                             "install_target",
-                             "content_deps",
-                           ])
+    forward_variables_from(invoker, "*", [ "install_target" ])
   }
 
   if (current_toolchain == default_toolchain &&
       (!defined(invoker.install_target) || invoker.install_target)) {
     shared_library_target_name = target_name
 
-    content_deps = []
-    if (defined(invoker.content_deps)) {
-      content_deps += invoker.content_deps
+    data_deps = []
+    if (defined(invoker.data_deps)) {
+      data_deps += invoker.data_deps
     }
     if (defined(platform_i18n_config_path)) {
-      content_deps += [ platform_i18n_config_path ]
+      data_deps += [ platform_i18n_config_path ]
     }
 
     if (separate_install_targets_for_bundling) {
-      bundle_deps = []
-      foreach(content_dep, content_deps) {
-        bundle_deps += [ "${content_dep}_install_content" ]
-      }
-
-      # TODO(b/220024845): Make this much better.
-      action("${shared_library_target_name}_bundle_content") {
+      import("//starboard/build/config/bundle_content.gni")
+      bundle_content("${shared_library_target_name}_bundle_content") {
         forward_variables_from(invoker, [ "testonly" ])
 
-        deps = [ ":$shared_library_target_name" ]
-        deps += bundle_deps
-
-        sources = []
-        foreach(bundle_dep, bundle_deps) {
-          # Split the target path and name. E.g. //path/to:target -> [ "//path/to", "target" ].
-          target_path_and_name = string_split(bundle_dep, ":")
-
-          # The content_dep target will place its file under the root gen dir.
-          content_dep_list_file = string_join("/",
-                                              [
-                                                root_gen_dir,
-                                                target_path_and_name[1],
-                                              ])
-          target_path_and_name = []
-
-          sources += [ "${content_dep_list_file}_files.tmp" ]
-        }
-
-        bundle_content_dir = "$sb_install_output_dir/$shared_library_target_name/$sb_install_content_subdir"
-
-        # TODO(b/220024845): We don't have the list of output files.
-        outputs = [ bundle_content_dir ]
-
-        script = "//starboard/build/copy_bundle_content.py"
-        args = [
-          "--output_dir",
-          rebase_path(bundle_content_dir, root_build_dir),
-          "--base_dir",
-          rebase_path(sb_static_contents_output_data_dir, root_build_dir),
-        ]
-        args += rebase_path(sources, root_build_dir)
+        bundle_name = shared_library_target_name
+        bundle_deps = []
+        bundle_deps = data_deps
       }
     }
 
@@ -425,12 +357,12 @@
       if (defined(invoker.deps)) {
         deps += invoker.deps
       }
+      foreach(dep, data_deps) {
+        deps += [ "${dep}_install_content" ]
+      }
       if (separate_install_targets_for_bundling) {
         deps += [ ":${shared_library_target_name}_bundle_content" ]
       }
-      foreach(content_dep, content_deps) {
-        deps += [ "${content_dep}_install_content" ]
-      }
     }
   }
 }
diff --git a/starboard/build/config/bundle_content.gni b/starboard/build/config/bundle_content.gni
new file mode 100644
index 0000000..2008c95
--- /dev/null
+++ b/starboard/build/config/bundle_content.gni
@@ -0,0 +1,69 @@
+# 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.
+
+template("bundle_content") {
+  bundle_name = invoker.bundle_name
+  bundle_deps = invoker.bundle_deps
+
+  bundle_content_list_file = "$root_gen_dir/${target_name}_files.txt"
+  file_format = "list lines"
+
+  # If the platform bundles content the list of content files can be collected
+  # from the metadata field of the generated install_content targets.
+  generated_file("list_$target_name") {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    # Rebase the paths the script working directory.
+    rebase = root_build_dir
+
+    data_keys = [ "install_content" ]
+
+    walk_keys = [ "ignore_install_content" ]
+
+    deps = []
+    foreach(dep, bundle_deps) {
+      deps += [ "${dep}_install_content" ]
+    }
+
+    output_conversion = file_format
+    outputs = [ bundle_content_list_file ]
+  }
+
+  action(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    deps = [ ":list_$target_name" ]
+
+    inputs = [ bundle_content_list_file ]
+
+    bundle_content_dir =
+        "$sb_install_output_dir/$bundle_name/$sb_install_content_subdir"
+
+    # TODO(b/220024845): We don't have the list of output files. The files
+    # are listed in `bundle_content_list_file` but can't be accessed in GN
+    # as `read_file` can't reliably access files generated by
+    # `generated_file` targets.
+    outputs = [ bundle_content_dir ]
+
+    script = "//starboard/build/copy_install_content.py"
+    args = [
+      "--output_dir",
+      rebase_path(bundle_content_dir, root_build_dir),
+      "--base_dir",
+      rebase_path(sb_static_contents_output_data_dir, root_build_dir),
+      "--files_list",
+      rebase_path(bundle_content_list_file, root_build_dir),
+    ]
+  }
+}
diff --git a/starboard/build/copy_bundle_content.py b/starboard/build/copy_bundle_content.py
deleted file mode 100644
index 859dd08..0000000
--- a/starboard/build/copy_bundle_content.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# 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.
-
-# TODO(b/216341587): Refactor install_content to get rid of this file.
-
-# This file is loosely based on starboard/build/copy_data.py
-"""Copies all input files to the output directory.
-
-The folder structure of the input files is maintained in the output relative
-to the 'base_dir' parameter.
-"""
-
-import argparse
-import os
-import shutil
-
-
-class InvalidArgumentException(Exception):
-  pass
-
-
-def copy_files(files_to_copy, base_dir, output_dir):
-  if not os.path.exists(output_dir):
-    os.makedirs(output_dir)
-
-  for path in files_to_copy:
-    # In certain cases, files would fail to open on windows if relative paths
-    # were provided.  Using absolute paths fixes this.
-    filename = os.path.abspath(path)
-
-    # Get the path of the file relative to the source base_dir.
-    rel_path = os.path.relpath(path, base_dir)
-    output_dir = os.path.abspath(output_dir)
-    # Use rel_path to preserve the input folder structure in the output.
-    output_filename = os.path.abspath(os.path.join(output_dir, rel_path))
-
-    # In cases where a directory has turned into a file or vice versa, delete it
-    # before copying it below.
-    if os.path.exists(output_dir) and not os.path.isdir(output_dir):
-      os.remove(output_dir)
-    if os.path.exists(output_filename) and os.path.isdir(output_filename):
-      shutil.rmtree(output_filename)
-
-    if not os.path.exists(os.path.dirname(output_filename)):
-      os.makedirs(os.path.dirname(output_filename))
-
-    if os.path.isfile(filename):
-      shutil.copy(filename, output_filename)
-    else:
-      # TODO(b/211909342): Remove this branch once lottie files are listed.
-      shutil.copytree(filename, output_filename)
-
-
-if __name__ == '__main__':
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '--output_dir', dest='output_dir', required=True, help='output directory')
-  parser.add_argument(
-      '--base_dir',
-      dest='base_dir',
-      required=True,
-      help='source base directory')
-  parser.add_argument(
-      'input_paths',
-      metavar='path',
-      nargs='*',
-      help='path to a file containing the list of files to copy')
-  options = parser.parse_args()
-
-  # Load file names from the files containing the list of file names.
-  file_names = []
-  for input_path in options.input_paths:
-    with open(input_path.strip()) as files_list:
-      file_names.extend([line.strip() for line in files_list])
-
-  copy_files(file_names, options.base_dir, options.output_dir)
diff --git a/starboard/build/copy_install_content.py b/starboard/build/copy_install_content.py
index 767079b..1e29cbf 100644
--- a/starboard/build/copy_install_content.py
+++ b/starboard/build/copy_install_content.py
@@ -31,6 +31,9 @@
 
 
 def copy_files(files_to_copy, base_dir, output_dir):
+  if not os.path.exists(output_dir):
+    os.makedirs(output_dir)
+
   for path in files_to_copy:
     # All input paths must point at files.
     # TODO(b/211909342): Re-enable once lottie test files are listed.
@@ -53,8 +56,8 @@
     if os.path.exists(output_filename) and os.path.isdir(output_filename):
       shutil.rmtree(output_filename)
 
-    if not os.path.exists(output_dir):
-      os.makedirs(output_dir)
+    if not os.path.exists(os.path.dirname(output_filename)):
+      os.makedirs(os.path.dirname(output_filename))
 
     if os.path.isfile(filename):
       shutil.copy(filename, output_filename)
diff --git a/starboard/client_porting/eztime/BUILD.gn b/starboard/client_porting/eztime/BUILD.gn
index 5611ee1..df579de 100644
--- a/starboard/client_porting/eztime/BUILD.gn
+++ b/starboard/client_porting/eztime/BUILD.gn
@@ -42,5 +42,5 @@
     "//testing/gtest",
   ]
 
-  content_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/elf_loader/BUILD.gn b/starboard/elf_loader/BUILD.gn
index 68af8bd..07974b0 100644
--- a/starboard/elf_loader/BUILD.gn
+++ b/starboard/elf_loader/BUILD.gn
@@ -87,11 +87,11 @@
 }
 
 target(final_executable_type, "elf_loader_sandbox") {
-  content_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [ "//third_party/icu:icudata" ]
   if (cobalt_font_package == "empty") {
-    content_deps += [ "//cobalt/content/fonts:copy_font_data" ]
+    data_deps += [ "//cobalt/content/fonts:copy_font_data" ]
   } else {
-    content_deps += [
+    data_deps += [
       "//cobalt/content/fonts:copy_fonts",
       "//cobalt/content/fonts:fonts_xml",
     ]
@@ -170,7 +170,7 @@
       ":elf_loader",
     ]
 
-    content_deps = [ ":copy_elf_loader_testdata" ]
+    data_deps = [ ":copy_elf_loader_testdata" ]
   }
 }
 
diff --git a/starboard/elf_loader/sandbox.cc b/starboard/elf_loader/sandbox.cc
index 019309f..2f2eb05 100644
--- a/starboard/elf_loader/sandbox.cc
+++ b/starboard/elf_loader/sandbox.cc
@@ -79,13 +79,16 @@
   if (!get_user_agent_func) {
     SB_LOG(ERROR) << "Failed to get user agent string";
   } else {
-    CrashpadAnnotations cobalt_version_info;
-    memset(&cobalt_version_info, 0, sizeof(CrashpadAnnotations));
-    starboard::strlcpy(cobalt_version_info.user_agent_string,
-                       get_user_agent_func(), USER_AGENT_STRING_MAX_SIZE);
-    third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
-        cobalt_version_info);
-    SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+    std::vector<char> buffer(USER_AGENT_STRING_MAX_SIZE);
+    starboard::strlcpy(buffer.data(), get_user_agent_func(),
+                       USER_AGENT_STRING_MAX_SIZE);
+    if (third_party::crashpad::wrapper::InsertCrashpadAnnotation(
+            third_party::crashpad::wrapper::kCrashpadUserAgentStringKey,
+            buffer.data())) {
+      SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+    } else {
+      SB_DLOG(INFO) << "Failed to add user agent string to Crashpad.";
+    }
   }
 
   if (!g_sb_event_func) {
diff --git a/starboard/examples/window/BUILD.gn b/starboard/examples/window/BUILD.gn
index beb0040..93c3aae 100644
--- a/starboard/examples/window/BUILD.gn
+++ b/starboard/examples/window/BUILD.gn
@@ -17,5 +17,5 @@
 
   sources = [ "main.cc" ]
   public_deps = [ "//starboard" ]
-  content_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/loader_app/loader_app.cc b/starboard/loader_app/loader_app.cc
index 8bae605..fcb2975 100644
--- a/starboard/loader_app/loader_app.cc
+++ b/starboard/loader_app/loader_app.cc
@@ -160,13 +160,16 @@
   if (!get_user_agent_func) {
     SB_LOG(ERROR) << "Failed to get user agent string";
   } else {
-    CrashpadAnnotations cobalt_version_info;
-    memset(&cobalt_version_info, 0, sizeof(CrashpadAnnotations));
-    starboard::strlcpy(cobalt_version_info.user_agent_string,
-                       get_user_agent_func(), USER_AGENT_STRING_MAX_SIZE);
-    third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
-        cobalt_version_info);
-    SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+    std::vector<char> buffer(USER_AGENT_STRING_MAX_SIZE);
+    starboard::strlcpy(buffer.data(), get_user_agent_func(),
+                       USER_AGENT_STRING_MAX_SIZE);
+    if (third_party::crashpad::wrapper::InsertCrashpadAnnotation(
+            third_party::crashpad::wrapper::kCrashpadUserAgentStringKey,
+            buffer.data())) {
+      SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+    } else {
+      SB_DLOG(INFO) << "Failed to add user agent string to Crashpad.";
+    }
   }
 
   g_sb_event_func = reinterpret_cast<void (*)(const SbEvent*)>(
diff --git a/starboard/loader_app/slot_management.cc b/starboard/loader_app/slot_management.cc
index 9f76b11..fc5549a 100644
--- a/starboard/loader_app/slot_management.cc
+++ b/starboard/loader_app/slot_management.cc
@@ -28,6 +28,7 @@
 #include "starboard/loader_app/installation_manager.h"
 #include "starboard/memory.h"
 #include "starboard/string.h"
+#include "third_party/crashpad/wrapper/annotations.h"
 #include "third_party/crashpad/wrapper/wrapper.h"
 
 namespace starboard {
@@ -315,13 +316,16 @@
     if (!get_user_agent_func) {
       SB_LOG(ERROR) << "Failed to get user agent string";
     } else {
-      CrashpadAnnotations cobalt_version_info;
-      memset(&cobalt_version_info, 0, sizeof(CrashpadAnnotations));
-      starboard::strlcpy(cobalt_version_info.user_agent_string,
-                         get_user_agent_func(), USER_AGENT_STRING_MAX_SIZE);
-      third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
-          cobalt_version_info);
-      SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+      std::vector<char> buffer(USER_AGENT_STRING_MAX_SIZE);
+      starboard::strlcpy(buffer.data(), get_user_agent_func(),
+                         USER_AGENT_STRING_MAX_SIZE);
+      if (third_party::crashpad::wrapper::InsertCrashpadAnnotation(
+              third_party::crashpad::wrapper::kCrashpadUserAgentStringKey,
+              buffer.data())) {
+        SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+      } else {
+        SB_DLOG(INFO) << "Failed to add user agent string to Crashpad.";
+      }
     }
 
     SB_DLOG(INFO) << "Successfully loaded Cobalt!\n";
diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn
index c96b0d5..7b44620 100644
--- a/starboard/nplb/BUILD.gn
+++ b/starboard/nplb/BUILD.gn
@@ -291,11 +291,6 @@
   data_deps = [
     "//starboard/nplb/testdata/file_tests:nplb_file_tests_data",
     "//starboard/shared/starboard/player:player_download_test_data",
-  ]
-
-  content_deps = [
-    "//starboard/nplb/testdata/file_tests:nplb_file_tests_data",
-    "//starboard/shared/starboard/player:player_download_test_data",
     "//third_party/icu:icudata",
   ]
 }
diff --git a/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn b/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
index 705025b..f22104c 100644
--- a/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
+++ b/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
@@ -37,5 +37,5 @@
     "//testing/gmock",
   ]
 
-  content_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/shared/starboard/crash_handler.cc b/starboard/shared/starboard/crash_handler.cc
index 572a022..0d6ec2d 100644
--- a/starboard/shared/starboard/crash_handler.cc
+++ b/starboard/shared/starboard/crash_handler.cc
@@ -25,14 +25,16 @@
 namespace {
 
 bool OverrideCrashpadAnnotations(CrashpadAnnotations* crashpad_annotations) {
-  CrashpadAnnotations annotations;
-  memset(&annotations, 0, sizeof(CrashpadAnnotations));
-  memcpy(&annotations, crashpad_annotations, sizeof(CrashpadAnnotations));
-  return third_party::crashpad::wrapper::AddAnnotationsToCrashpad(annotations);
+  return false;  // Deprecated
+}
+
+bool SetString(const char* key, const char* value) {
+  return third_party::crashpad::wrapper::InsertCrashpadAnnotation(key, value);
 }
 
 const CobaltExtensionCrashHandlerApi kCrashHandlerApi = {
-    kCobaltExtensionCrashHandlerName, 1, &OverrideCrashpadAnnotations,
+    kCobaltExtensionCrashHandlerName, 2, &OverrideCrashpadAnnotations,
+    &SetString,
 };
 
 }  // namespace
diff --git a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h
index 3f6e7c0..8a997a8 100644
--- a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h
+++ b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h
@@ -62,9 +62,6 @@
   void Reset() override;
 
  private:
-  void ProcessOneInputBuffer(const scoped_refptr<InputBuffer>& input_buffer,
-                             const ConsumedCB& consumed_cb);
-
   void InitializeAudioDecoder(const SbMediaAudioSampleInfo& audio_sample_info);
   void TeardownAudioDecoder();
   void OnDecoderOutput();
diff --git a/starboard/shared/starboard/player/filter/testing/BUILD.gn b/starboard/shared/starboard/player/filter/testing/BUILD.gn
index 15ca8b9..a126ef0 100644
--- a/starboard/shared/starboard/player/filter/testing/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/testing/BUILD.gn
@@ -41,7 +41,7 @@
 
   deps = cobalt_platform_dependencies
 
-  content_deps =
+  data_deps =
       [ "//starboard/shared/starboard/player:player_download_test_data" ]
 }
 
diff --git a/starboard/shared/starboard/player/filter/tools/BUILD.gn b/starboard/shared/starboard/player/filter/tools/BUILD.gn
index 698d480..4e59d2f 100644
--- a/starboard/shared/starboard/player/filter/tools/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/tools/BUILD.gn
@@ -27,7 +27,7 @@
     "//starboard",
     "//starboard/shared/starboard/player:player_download_test_data",
   ]
-  content_deps = [
+  data_deps = [
     "//starboard/shared/starboard/player:player_download_test_data",
     "//third_party/icu:icudata",
   ]
diff --git a/third_party/crashpad/client/BUILD.gn b/third_party/crashpad/client/BUILD.gn
index 19aa8db..6ecd5c5 100644
--- a/third_party/crashpad/client/BUILD.gn
+++ b/third_party/crashpad/client/BUILD.gn
@@ -90,12 +90,13 @@
     "../util",
   ]
 
+  deps = []
+
   if (crashpad_is_in_starboard) {
     public_deps += [ "//starboard/elf_loader:evergreen_info" ]
+    deps += [ "../wrapper/proto:crashpad_annotations_proto" ]
   }
 
-  deps = []
-
   if (crashpad_is_win) {
     libs = [ "rpcrt4.lib" ]
     cflags = [ "/wd4201" ]  # nonstandard extension used : nameless struct/union
diff --git a/third_party/crashpad/client/crashpad_client.h b/third_party/crashpad/client/crashpad_client.h
index 9dd12dd..4cf5a0e 100644
--- a/third_party/crashpad/client/crashpad_client.h
+++ b/third_party/crashpad/client/crashpad_client.h
@@ -41,7 +41,6 @@
 #if defined(STARBOARD)
 #include "starboard/elf_loader/evergreen_info.h"
 #include "third_party/crashpad/snapshot/sanitized/sanitization_information.h"
-#include "third_party/crashpad/wrapper/annotations.h"
 #endif
 
 namespace crashpad {
@@ -391,14 +390,17 @@
   //! \return `true` on success, `false` on failure with a message logged.
   static bool SendEvergreenInfoToHandler(EvergreenInfo evergreen_info);
 
-  //! \brief Sends mapping info to the handler
+  //! \brief Inserts annotation mapping info for the handler
   //!
-  //! A handler must have already been installed before calling this method.
-  //! \param[in] annotations A CrashpadAnnotations struct, whose information
-  //!     was created on Evergreen startup.
+  //! A signal handler must have already been installed before calling this
+  //! method. Whether or not the annotation is sent to the Crashpad handler,
+  //! or just prepared to be sent, depends on whether the Crashpad handler is
+  //! started at launch or at crash.
+  //! \param[in] key The annotation's key.
+  //! \param[in] value The annotation's value.
   //!
   //! \return `true` on success, `false` on failure with a message logged.
-  static bool SendAnnotationsToHandler(CrashpadAnnotations annotations);
+  static bool InsertAnnotationForHandler(const char* key, const char* value);
 
   //! \brief Sends mapping info to the handler
   //!
diff --git a/third_party/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/client/crashpad_client_linux.cc
index 5738668..1b579bf 100644
--- a/third_party/crashpad/client/crashpad_client_linux.cc
+++ b/third_party/crashpad/client/crashpad_client_linux.cc
@@ -26,6 +26,7 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "client/client_argv_handling.h"
+#include "starboard/common/mutex.h"
 #include "third_party/lss/lss.h"
 #include "util/file/file_io.h"
 #include "util/file/filesystem.h"
@@ -37,6 +38,15 @@
 #include "util/misc/from_pointer_cast.h"
 #include "util/posix/double_fork_and_exec.h"
 #include "util/posix/signals.h"
+// TODO(b/201538792): resolve conflict between mini_chromium and base functions.
+#ifdef LogMessage
+#define LOG_MESSAGE_DEFINED
+#undef LogMessage
+#endif
+#include "third_party/crashpad/wrapper/proto/crashpad_annotations.pb.h"
+#ifdef LOG_MESSAGE_DEFINED
+#define LogMessage MLogMessage
+#endif
 
 namespace crashpad {
 
@@ -44,9 +54,7 @@
 
 #if defined(STARBOARD)
 constexpr char kEvergreenInfoKey[] = "evergreen-information";
-constexpr char kProductKey[] = "annotation=prod";
-constexpr char kVersionKey[] = "annotation=ver";
-constexpr char kUAKey[] = "annotation=user_agent_string";
+constexpr char kAnnotationKey[] = "annotation=%s";
 #endif
 
 std::string FormatArgumentInt(const std::string& name, int value) {
@@ -183,10 +191,10 @@
     return SendEvergreenInfoImpl();
   }
 
-  bool SendAnnotations(CrashpadAnnotations annotations) {
-    annotations_ = annotations;
-    return SendAnnotationsImpl();
+  bool InsertAnnotation(const char* key, const char* value) {
+    return InsertAnnotationImpl(key, value);
   }
+
 #endif
 
   // The base implementation for all signal handlers, suitable for calling
@@ -227,7 +235,6 @@
 
 #if defined(STARBOARD)
   const EvergreenInfo& GetEvergreenInfo() { return evergreen_info_; }
-  const CrashpadAnnotations& GetAnnotations() { return annotations_; }
   const SanitizationInformation& GetSanitizationInformation() {
     return sanitization_information_;
   }
@@ -239,7 +246,7 @@
 
 #if defined(STARBOARD)
   virtual bool SendEvergreenInfoImpl() = 0;
-  virtual bool SendAnnotationsImpl() = 0;
+  virtual bool InsertAnnotationImpl(const char* key, const char* value) = 0;
 #endif
 
   virtual void HandleCrashImpl() = 0;
@@ -262,7 +269,6 @@
 
 #if defined(STARBOARD)
   EvergreenInfo evergreen_info_;
-  CrashpadAnnotations annotations_;
   SanitizationInformation sanitization_information_;
 #endif
 
@@ -297,12 +303,18 @@
     argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
                                                   &GetExceptionInfo()));
 
+#if defined(STARBOARD)
+    argv_strings_.push_back("--handler-started-at-crash");
+#endif
+
     StringVectorToCStringVector(argv_strings_, &argv_);
     return Install(unhandled_signals);
   }
 
 #if defined(STARBOARD)
   bool SendEvergreenInfoImpl() override {
+    starboard::ScopedLock scoped_lock(argv_lock_);
+
     bool updated = false;
     for (auto& s : argv_strings_) {
       if (s.compare(2, strlen(kEvergreenInfoKey), kEvergreenInfoKey) == 0) {
@@ -324,27 +336,19 @@
     return true;
   }
 
-  bool SendAnnotationsImpl() override {
-    bool updated_product = false;
-    bool updated_version = false;
-    bool updated_user_agent_string = false;
+  bool InsertAnnotationImpl(const char* key, const char* value) override {
+    starboard::ScopedLock scoped_lock(argv_lock_);
+
+    std::string formatted_key = base::StringPrintf(kAnnotationKey, key);
+    bool updated_annotation = false;
     for (auto& s : argv_strings_) {
-      if (UpdateAnnotation(s, kProductKey, GetAnnotations().product)) {
-        updated_product = true;
-      } else if (UpdateAnnotation(s, kVersionKey, GetAnnotations().version)) {
-        updated_version = true;
-      } else if (UpdateAnnotation(s, kUAKey, GetAnnotations().user_agent_string)) {
-        updated_user_agent_string = true;
+      if (UpdateAnnotation(s, formatted_key, value)) {
+        updated_annotation = true;
       }
     }
-    if (!updated_product) {
-      AddAnnotation(argv_strings_, kProductKey, GetAnnotations().product);
-    }
-    if (!updated_version) {
-      AddAnnotation(argv_strings_, kVersionKey, GetAnnotations().version);
-    }
-    if (!updated_user_agent_string) {
-      AddAnnotation(argv_strings_, kUAKey, GetAnnotations().user_agent_string);
+
+    if (!updated_annotation) {
+      AddAnnotation(argv_strings_, formatted_key, value);
     }
 
     StringVectorToCStringVector(argv_strings_, &argv_);
@@ -382,6 +386,10 @@
 
   std::vector<std::string> argv_strings_;
   std::vector<const char*> argv_;
+#if defined(STARBOARD)
+  // Protects access to both argv_strings_ and argv_.
+  starboard::Mutex argv_lock_;
+#endif
   std::vector<std::string> envp_strings_;
   std::vector<const char*> envp_;
   bool set_envp_ = false;
@@ -446,6 +454,10 @@
   }
 
 #if defined(STARBOARD)
+  char* GetSerializedAnnotations() {
+    return serialized_annotations_.data();
+  }
+
   bool SendEvergreenInfoImpl() override {
     ExceptionHandlerClient client(sock_to_handler_.get(), true);
     ExceptionHandlerProtocol::ClientInformation info = {};
@@ -455,13 +467,36 @@
     return true;
   }
 
-  bool SendAnnotationsImpl() override {
+  bool InsertAnnotationImpl(const char* key, const char* value) override {
+    starboard::ScopedLock scoped_lock(annotations_lock_);
+
+    std::string key_str(key);
+    std::string value_str(value);
+
+    if (strcmp(key, "ver") == 0) {
+      annotations_.set_ver(value_str);
+    } else if (strcmp(key, "prod") == 0) {
+      annotations_.set_prod(value_str);
+    } else if (strcmp(key, "user_agent_string") == 0) {
+      annotations_.set_user_agent_string(value_str);
+    } else {
+      (*annotations_.mutable_runtime_annotations())[key_str] = value_str;
+    }
+
+    serialized_annotations_.resize(annotations_.ByteSize());
+    annotations_.SerializeToArray(serialized_annotations_.data(),
+        annotations_.ByteSize());
+
     ExceptionHandlerClient client(sock_to_handler_.get(), true);
     ExceptionHandlerProtocol::ClientInformation info = {};
-    info.annotations_address = FromPointerCast<VMAddress>(&GetAnnotations());
+    info.serialized_annotations_address = FromPointerCast<VMAddress>(
+        GetSerializedAnnotations());
+    info.serialized_annotations_size = serialized_annotations_.size();
     client.SendAnnotations(info);
+
     return true;
   }
+
 #endif
 
   void HandleCrashImpl() override {
@@ -471,9 +506,12 @@
 #if defined(STARBOARD)
     info.sanitization_information_address =
         FromPointerCast<VMAddress>(&GetSanitizationInformation());
-    info.annotations_address = FromPointerCast<VMAddress>(&GetAnnotations());
+    info.serialized_annotations_address = FromPointerCast<VMAddress>(
+        GetSerializedAnnotations());
+    info.serialized_annotations_size = serialized_annotations_.size();
     info.evergreen_information_address =
         FromPointerCast<VMAddress>(&GetEvergreenInfo());
+    info.handler_start_type = ExceptionHandlerProtocol::kStartAtLaunch;
 #endif
 
 #if defined(OS_CHROMEOS)
@@ -497,6 +535,13 @@
 
   ScopedFileHandle sock_to_handler_;
   pid_t handler_pid_ = -1;
+#if defined(STARBOARD)
+  crashpad::wrapper::CrashpadAnnotations annotations_;
+  std::vector<char> serialized_annotations_;
+
+  // Protects access to both annotations_ and serialized_annotations_;
+  starboard::Mutex annotations_lock_;
+#endif
 
 #if defined(OS_CHROMEOS)
   // An optional UNIX timestamp passed to us from Chrome.
@@ -694,14 +739,14 @@
   return SignalHandler::Get()->SendEvergreenInfo(evergreen_info);
 }
 
-bool CrashpadClient::SendAnnotationsToHandler(
-    CrashpadAnnotations annotations) {
+bool CrashpadClient::InsertAnnotationForHandler(
+    const char* key, const char* value) {
   if (!SignalHandler::Get()) {
     DLOG(ERROR) << "Crashpad isn't enabled";
     return false;
   }
 
-  return SignalHandler::Get()->SendAnnotations(annotations);
+  return SignalHandler::Get()->InsertAnnotation(key, value);
 }
 
 bool CrashpadClient::SendSanitizationInformationToHandler(
diff --git a/third_party/crashpad/handler/BUILD.gn b/third_party/crashpad/handler/BUILD.gn
index 80b7378..10582a8 100644
--- a/third_party/crashpad/handler/BUILD.gn
+++ b/third_party/crashpad/handler/BUILD.gn
@@ -172,7 +172,7 @@
     if (crashpad_is_in_starboard) {
       install_target = !crashpad_is_android
       check_includes = false
-      content_deps = [ "//third_party/icu:icudata" ]
+      data_deps = [ "//third_party/icu:icudata" ]
     }
 
     sources = [ "main.cc" ]
diff --git a/third_party/crashpad/handler/handler_main.cc b/third_party/crashpad/handler/handler_main.cc
index 51bd64e..cbf9bb5 100644
--- a/third_party/crashpad/handler/handler_main.cc
+++ b/third_party/crashpad/handler/handler_main.cc
@@ -172,6 +172,9 @@
 #if defined(STARBOARD)
 "      --evergreen-information=EVERGREEN_INFORMATION_ADDRESS\n"
 "                              the address of a EvegreenInfo struct.\n"
+"      --handler-started-at-crash\n"
+"                              the handler was started at time of crash, as\n"
+"                              opposed to time of launch\n"
 #endif
 "      --url=URL               send crash reports to this Breakpad server URL,\n"
 "                              only if uploads are enabled for the database\n"
@@ -214,6 +217,7 @@
   bool shared_client_connection;
 #if defined(STARBOARD)
   VMAddress evergreen_information_address;
+  bool handler_started_at_crash = false;
 #endif  // defined(STARBOARD)
 #if defined(OS_ANDROID)
   bool write_minidump_to_log;
@@ -590,6 +594,7 @@
     kOptionTraceParentWithException,
 #if defined(STARBOARD)
     kOptionEvergreenInformaton,
+    kOptionHandlerStartedAtCrash,
 #endif  // defined(STARBOARD)
 #endif
     kOptionURL,
@@ -676,6 +681,10 @@
      required_argument,
      nullptr,
      kOptionEvergreenInformaton},
+    {"handler-started-at-crash",
+     no_argument,
+     nullptr,
+     kOptionHandlerStartedAtCrash},
 #endif
     {"url", required_argument, nullptr, kOptionURL},
 #if defined(OS_CHROMEOS)
@@ -850,6 +859,10 @@
         }
         break;
       }
+      case kOptionHandlerStartedAtCrash: {
+        options.handler_started_at_crash = true;
+        break;
+      }
 #endif   // defined(STARBOARD)
 #endif  // OS_LINUX || OS_ANDROID
       case kOptionURL: {
@@ -1070,6 +1083,9 @@
 #if defined(STARBOARD)
     info.evergreen_information_address =
         options.evergreen_information_address;
+    if (options.handler_started_at_crash) {
+      info.handler_start_type = ExceptionHandlerProtocol::kStartAtCrash;
+    }
 #endif   // defined(STARBOARD)
     return exception_handler->HandleException(getppid(), geteuid(), info)
                ? EXIT_SUCCESS
diff --git a/third_party/crashpad/handler/linux/capture_snapshot.cc b/third_party/crashpad/handler/linux/capture_snapshot.cc
index 8fb0735..59350fd 100644
--- a/third_party/crashpad/handler/linux/capture_snapshot.cc
+++ b/third_party/crashpad/handler/linux/capture_snapshot.cc
@@ -31,19 +31,15 @@
     VMAddress requesting_thread_stack_address,
     pid_t* requesting_thread_id,
     std::unique_ptr<ProcessSnapshotLinux>* snapshot,
-    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot
-#if defined(STARBOARD)
-    ,
-    VMAddress evergreen_information_address,
-    VMAddress annotations_address
-#endif
-    ) {
+    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot) {
   std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
       new ProcessSnapshotLinux());
 #if defined(STARBOARD)
   if (!process_snapshot->Initialize(connection,
                                     info.evergreen_information_address,
-                                    info.annotations_address)) {
+                                    info.serialized_annotations_address,
+                                    info.serialized_annotations_size,
+                                    info.handler_start_type)) {
 #else
   if (!process_snapshot->Initialize(connection)) {
 #endif
diff --git a/third_party/crashpad/handler/linux/capture_snapshot.h b/third_party/crashpad/handler/linux/capture_snapshot.h
index 51eca35..b01e316 100644
--- a/third_party/crashpad/handler/linux/capture_snapshot.h
+++ b/third_party/crashpad/handler/linux/capture_snapshot.h
@@ -64,13 +64,7 @@
     VMAddress requesting_thread_stack_address,
     pid_t* requesting_thread_id,
     std::unique_ptr<ProcessSnapshotLinux>* process_snapshot,
-    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot
-#if defined(STARBOARD)
-    ,
-    VMAddress evergreen_information_address,
-    VMAddress annotations_address
-#endif
-    );
+    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot);
 
 }  // namespace crashpad
 
diff --git a/third_party/crashpad/handler/linux/crash_report_exception_handler.cc b/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
index b0f68ce..9a86073 100644
--- a/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
+++ b/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
@@ -89,7 +89,8 @@
 
 bool CrashReportExceptionHandler::AddAnnotations(
     const ExceptionHandlerProtocol::ClientInformation& info) {
-  annotations_address_ = info.annotations_address;
+  serialized_annotations_address_ = info.serialized_annotations_address;
+  serialized_annotations_size_ = info.serialized_annotations_size;
   return true;
 }
 #endif
@@ -153,13 +154,7 @@
                        requesting_thread_stack_address,
                        requesting_thread_id,
                        &process_snapshot,
-                       &sanitized_snapshot
-#if defined(STARBOARD)
-                       ,
-                       evergreen_info_,
-                       annotations_address_
-#endif
-                       )) {
+                       &sanitized_snapshot)) {
     return false;
   }
 
diff --git a/third_party/crashpad/handler/linux/crash_report_exception_handler.h b/third_party/crashpad/handler/linux/crash_report_exception_handler.h
index 5cca2ad..7a32ac0 100644
--- a/third_party/crashpad/handler/linux/crash_report_exception_handler.h
+++ b/third_party/crashpad/handler/linux/crash_report_exception_handler.h
@@ -122,7 +122,8 @@
   const UserStreamDataSources* user_stream_data_sources_;  // weak
 #if defined(STARBOARD)
   VMAddress evergreen_info_;
-  VMAddress annotations_address_;
+  VMAddress serialized_annotations_address_;
+  int serialized_annotations_size_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
diff --git a/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc b/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
index 5fe900b..48fdb11 100644
--- a/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
+++ b/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
@@ -20,7 +20,16 @@
 #include "util/linux/exception_information.h"
 
 #if defined(STARBOARD)
-#include "third_party/crashpad/wrapper/annotations.h"
+#include "third_party/crashpad/util/linux/exception_handler_protocol.h"
+// TODO(b/201538792): resolve conflict between mini_chromium and base functions.
+#ifdef LogMessage
+#define LOG_MESSAGE_DEFINED
+#undef LogMessage
+#endif
+#include "third_party/crashpad/wrapper/proto/crashpad_annotations.pb.h"
+#ifdef LOG_MESSAGE_DEFINED
+#define LogMessage MLogMessage
+#endif
 #endif
 
 namespace crashpad {
@@ -56,7 +65,9 @@
 #if defined(STARBOARD)
 bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection,
                                       VMAddress evergreen_information_address,
-                                      VMAddress annotations_address) {
+                                      VMAddress serialized_annotations_address,
+                                      int serialized_annotations_size,
+                                      ExceptionHandlerProtocol::HandlerStartType handler_start_type) {
   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
 
   if (gettimeofday(&snapshot_time_, nullptr) != 0) {
@@ -70,15 +81,10 @@
     return false;
   }
 
-  CrashpadAnnotations annotations;
-  if (!memory_range_.Read(
-          annotations_address, sizeof(CrashpadAnnotations), &annotations)) {
-    LOG(ERROR) << "Could not read annotations";
+  if (handler_start_type == ExceptionHandlerProtocol::kStartAtCrash) {
+    LOG(INFO) << "Annotations do not need to be read from client process";
   } else {
-    AddAnnotation("user_agent_string",
-                  std::string(annotations.user_agent_string));
-    AddAnnotation("prod", std::string(annotations.product));
-    AddAnnotation("ver", std::string(annotations.version));
+    AddAnnotations(serialized_annotations_address, serialized_annotations_size);
   }
 
   system_.Initialize(&process_reader_, &snapshot_time_);
@@ -90,6 +96,33 @@
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
 }
+
+void ProcessSnapshotLinux::AddAnnotations(
+    VMAddress serialized_annotations_address, int serialized_annotations_size) {
+  std::vector<char> buffer(serialized_annotations_size);
+  if (!memory_range_.Read(serialized_annotations_address,
+                          serialized_annotations_size,
+                          buffer.data())) {
+    LOG(ERROR) << "Could not read annotations";
+    return;
+  }
+
+  crashpad::wrapper::CrashpadAnnotations annotations;
+  if (!annotations.ParseFromArray(buffer.data(), serialized_annotations_size)) {
+      LOG(ERROR) << "Could not parse annotations";
+      return;
+  }
+
+  AddAnnotation("user_agent_string",
+                std::string(annotations.user_agent_string()));
+  AddAnnotation("prod", std::string(annotations.prod()));
+  AddAnnotation("ver", std::string(annotations.ver()));
+
+  for (auto& runtime_annotation : annotations.runtime_annotations()) {
+    AddAnnotation(runtime_annotation.first, runtime_annotation.second);
+  }
+  LOG(INFO) << "Annotations successfully added from client process";
+}
 #endif
 
 pid_t ProcessSnapshotLinux::FindThreadWithStackAddress(
diff --git a/third_party/crashpad/snapshot/linux/process_snapshot_linux.h b/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
index 8cd9ef5..0d20528 100644
--- a/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
+++ b/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
@@ -45,6 +45,7 @@
 #if defined(STARBOARD)
 #include "snapshot/module_snapshot_evergreen.h"
 #include "starboard/elf_loader/evergreen_info.h"
+#include "third_party/crashpad/util/linux/exception_handler_protocol.h"
 #endif
 
 namespace crashpad {
@@ -77,7 +78,9 @@
   //!     an appropriate message logged.
   bool Initialize(PtraceConnection* connnection,
                   VMAddress evergreen_information_address,
-                  VMAddress annotations_address);
+                  VMAddress serialized_annotations_address,
+                  int serialized_annotations_size,
+                  ExceptionHandlerProtocol::HandlerStartType handler_start_type);
 #endif
 
   //! \brief Finds the thread whose stack contains \a stack_address.
@@ -158,6 +161,8 @@
   void InitializeModules();
 #if defined(STARBOARD)
   void InitializeModules(VMAddress evergreen_information_address);
+  void AddAnnotations(VMAddress serialized_annotations_address,
+                      int serialized_annotations_size);
 #endif
   void InitializeAnnotations();
 
diff --git a/third_party/crashpad/util/linux/exception_handler_protocol.cc b/third_party/crashpad/util/linux/exception_handler_protocol.cc
index b139017..5f58721 100644
--- a/third_party/crashpad/util/linux/exception_handler_protocol.cc
+++ b/third_party/crashpad/util/linux/exception_handler_protocol.cc
@@ -26,7 +26,9 @@
 #if defined(STARBOARD)
       ,
       evergreen_information_address(0),
-      annotations_address(0)
+      serialized_annotations_address(0),
+      serialized_annotations_size(0),
+      handler_start_type(kStartAtLaunch)
 #endif
 {
 }
diff --git a/third_party/crashpad/util/linux/exception_handler_protocol.h b/third_party/crashpad/util/linux/exception_handler_protocol.h
index 4cc1662..4f94bd9 100644
--- a/third_party/crashpad/util/linux/exception_handler_protocol.h
+++ b/third_party/crashpad/util/linux/exception_handler_protocol.h
@@ -38,6 +38,12 @@
   //! \brief A boolean status suitable for communication between processes.
   enum Bool : char { kBoolFalse, kBoolTrue };
 
+#if defined(STARBOARD)
+  //! \brief Describes when, in the client process lifecycle, the Crashpad
+  //!     handler was or will be started.
+  enum HandlerStartType : char { kStartAtCrash, kStartAtLaunch };
+#endif
+
   //! \brief Information about a client registered with an
   //!     ExceptionHandlerServer.
   struct ClientInformation {
@@ -57,9 +63,15 @@
     //!     struct, or 0 if there is no such struct.
     VMAddress evergreen_information_address;
 
-    //! \brief The address in the client's address space of an
-    //!     CrashpadAnnotations struct, or 0 if there is no such struct.
-    VMAddress annotations_address;
+    //! \brief The address in the client's address space of a character array
+    //!     containing a serialized CrashpadAnnotations proto, or 0 if there is
+    //!     no such address.
+    VMAddress serialized_annotations_address;
+
+    //! \brief The byte size of the serialized annotations.
+    int serialized_annotations_size;
+
+    HandlerStartType handler_start_type;
 #endif
 
 #if defined(OS_LINUX)
diff --git a/third_party/crashpad/wrapper/proto/BUILD.gn b/third_party/crashpad/wrapper/proto/BUILD.gn
new file mode 100644
index 0000000..4819e31
--- /dev/null
+++ b/third_party/crashpad/wrapper/proto/BUILD.gn
@@ -0,0 +1,20 @@
+# 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.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("crashpad_annotations_proto") {
+  sources = [ "crashpad_annotations.proto" ]
+  generate_python = false
+}
diff --git a/third_party/crashpad/wrapper/proto/crashpad_annotations.proto b/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
new file mode 100644
index 0000000..8eb6205
--- /dev/null
+++ b/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
@@ -0,0 +1,36 @@
+// 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.
+
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+
+package crashpad.wrapper;
+
+// Annotations that can be shared between Cobalt and Crashpad handler processes.
+// Next id: 5
+message CrashpadAnnotations {
+  // The product name.
+  string prod = 1;
+
+  // The product version.
+  string ver = 2;
+
+  // The User-Agent string that identifies brand, model, etc.
+  string user_agent_string = 3;
+
+  // Annotations with keys that are unknown at compile time.
+  map<string, string> runtime_annotations = 4;
+}
+
diff --git a/third_party/crashpad/wrapper/wrapper.cc b/third_party/crashpad/wrapper/wrapper.cc
index 8f19a70..700f9b7 100644
--- a/third_party/crashpad/wrapper/wrapper.cc
+++ b/third_party/crashpad/wrapper/wrapper.cc
@@ -33,8 +33,11 @@
 namespace crashpad {
 namespace wrapper {
 
-namespace {
+const char kCrashpadVersionKey[]  = "ver";
+const char kCrashpadProductKey[]  = "prod";
+const char kCrashpadUserAgentStringKey[]  = "user_agent_string";
 
+namespace {
 // TODO: Get evergreen information from installation.
 const std::string kCrashpadVersion = "1.0.0.0";
 #if defined(STARBOARD_BUILD_TYPE_GOLD)
@@ -215,9 +218,9 @@
   return client->SendEvergreenInfoToHandler(evergreen_info);
 }
 
-bool AddAnnotationsToCrashpad(CrashpadAnnotations annotations) {
+bool InsertCrashpadAnnotation(const char* key, const char* value) {
   ::crashpad::CrashpadClient* client = GetCrashpadClient();
-  return client->SendAnnotationsToHandler(annotations);
+  return client->InsertAnnotationForHandler(key, value);
 }
 
 }  // namespace wrapper
diff --git a/third_party/crashpad/wrapper/wrapper.h b/third_party/crashpad/wrapper/wrapper.h
index 4def1a8..be60fec 100644
--- a/third_party/crashpad/wrapper/wrapper.h
+++ b/third_party/crashpad/wrapper/wrapper.h
@@ -16,17 +16,28 @@
 #define THIRD_PARTY_CRASHPAD_WRAPPER_WRAPPER_H_
 
 #include "starboard/elf_loader/evergreen_info.h" // nogncheck
-#include "third_party/crashpad/wrapper/annotations.h"
 
 namespace third_party {
 namespace crashpad {
 namespace wrapper {
 
+// The key name used in Crashpad for the version annotation.
+extern const char kCrashpadVersionKey[];
+
+// The key name used in Crashpad for the version annotation.
+extern const char kCrashpadProductKey[];
+
+// The key name used in Crashpad for the user_agent_string annotation.
+extern const char kCrashpadUserAgentStringKey[];
+
 void InstallCrashpadHandler(bool start_at_crash);
 
 bool AddEvergreenInfoToCrashpad(EvergreenInfo evergreen_info);
 
-bool AddAnnotationsToCrashpad(CrashpadAnnotations annotations);
+// Associates the given value with the given key in Crashpad's map of
+// annotations, updating the existing value if the map already contains the
+// key. Returns true on success and false on failure.
+bool InsertCrashpadAnnotation(const char* key, const char* value);
 
 }  // namespace wrapper
 }  // namespace crashpad
diff --git a/third_party/crashpad/wrapper/wrapper_stub.cc b/third_party/crashpad/wrapper/wrapper_stub.cc
index bf3c093..f96dd94 100644
--- a/third_party/crashpad/wrapper/wrapper_stub.cc
+++ b/third_party/crashpad/wrapper/wrapper_stub.cc
@@ -18,13 +18,17 @@
 namespace crashpad {
 namespace wrapper {
 
+const char kCrashpadVersionKey[]  = "";
+const char kCrashpadProductKey[]  = "";
+const char kCrashpadUserAgentStringKey[]  = "";
+
 void InstallCrashpadHandler(bool start_at_crash) {}
 
 bool AddEvergreenInfoToCrashpad(EvergreenInfo evergreen_info) {
   return false;
 }
 
-bool AddAnnotationsToCrashpad(CrashpadAnnotations annotations) {
+bool InsertCrashpadAnnotation(const char* key, const char* value) {
   return false;
 }
 
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index 67b9d7b..132c555 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -542,6 +542,6 @@
     configs -= [ "//starboard/build/config:size" ]
     configs += [ "//starboard/build/config:speed" ]
 
-    content_deps = [ "//third_party/zlib/google/test:zip_unittest_files" ]
+    data_deps = [ "//third_party/zlib/google/test:zip_unittest_files" ]
   }
 }