Import Cobalt 22.master.0.304711
diff --git a/src/.pre-commit-config.yaml b/src/.pre-commit-config.yaml
index 6240f63..7bc0f93 100644
--- a/src/.pre-commit-config.yaml
+++ b/src/.pre-commit-config.yaml
@@ -117,6 +117,7 @@
         language: python
         stages: [push]
         always_run: true
+        pass_filenames: false
     -   id: run-py2-tests
         name: Run Python 2 Tests
         description: Run Python 2 unittests
diff --git a/src/CONTRIBUTING.md b/src/CONTRIBUTING.md
index 71f6b83..332d6ab 100644
--- a/src/CONTRIBUTING.md
+++ b/src/CONTRIBUTING.md
@@ -53,7 +53,7 @@
   1. Run `git clang-format HEAD~` to apply default C++ formatting rules,
      followed by `git commit -a --amend` to squash any formatting changes
      into your commit.
-  1. Run `git cl upload` to upload the review to
+  1. Run `git push origin HEAD:refs/for/master` to upload the review to
      [Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/).
   1. Someone from the maintainers team will review the code, putting up comments
      on any things that need to change for submission.
@@ -62,4 +62,3 @@
   1. If you do not need to make any more changes, a maintainer will integrate
      the change into our private repository, and it will get pushed out to the
      public repository after some time.
-
diff --git a/src/base/files/file_path_unittest.cc b/src/base/files/file_path_unittest.cc
index f7c698a..afc30e7 100644
--- a/src/base/files/file_path_unittest.cc
+++ b/src/base/files/file_path_unittest.cc
@@ -433,8 +433,8 @@
     { FPL("rom:/a"),    true },
     { FPL("cache:/.."), true },
     { FPL("rom:/.."),   true },
-#else  // !defined(FILE_PATH_USES_DRIVE_LETTERS) && \
-          !defined(FILE_PATH_USES_MOUNT_POINT_NAME)
+#else  // !defined(FILE_PATH_USES_DRIVE_LETTERS) &&
+       // !defined(FILE_PATH_USES_MOUNT_POINT_NAME)
     { FPL("/"),   true },
     { FPL("/a"),  true },
     { FPL("/."),  true },
diff --git a/src/cobalt/black_box_tests/testdata/freeze_timers.html b/src/cobalt/black_box_tests/testdata/freeze_timers.html
new file mode 100644
index 0000000..0ad6f4c
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/freeze_timers.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+
+<head>
+  <title>Cobalt WindowTimers test with freeze state</title>
+  <script src='black_box_js_test_utils.js'></script>
+</head>
+
+<body>
+  <h1>
+    <span>ID element</span>
+  </h1>
+  <script>
+
+    // Delay for setInterval (ms)
+    var kSetIntervalDelay = 600;
+    // Delay for setTimeout (ms)
+    var kSetTimeoutDelay = 800;
+    // Delay for the long setTimeout (ms)
+    var kLongSetTimeoutDelay = 13000;
+
+    // Allowed time difference for callback expected execution time (ms)
+    // This value is quite high because in devel builds on slower devices there
+    // may occesionally be larger delays.
+    var kAllowedTimeDifference = 100;
+
+    // Last time when the app was resumed from frozen state.
+    var resumeTime = 0;
+    // Expected execution time for interval callback
+    var expectedIntervalTime = 0;
+    // Expected execution time for timeout callback
+    var expectedTimeoutTime = 0;
+    // Expected execution time for long timeout callback
+    var expectedLongTimeoutTime = 0;
+
+    let wasEverFrozen = false;
+    let frozen = false;
+    let eventWhileFrozen = false;
+
+    // Callback for registering entering freeze state.
+    function freezeCallback() {
+      console.log("document.onfreeze")
+      wasEverFrozen = true;
+      frozen = true;
+    }
+
+    // Callback for registering leaving freeze state.
+    function resumeCallback() {
+      resumeTime = performance.now();
+      console.log("document.onresume")
+      if (!frozen) {
+        console.log('ERROR: Resuming while not frozen');
+        notReached();
+      }
+      if (eventWhileFrozen) {
+        console.log('ERROR: Timer callback received while frozen');
+        notReached();
+      }
+      frozen = false;
+    }
+
+    function logState(prefix) {
+      console.log(prefix + ": State:" +
+        " document.visibilityState == " + document.visibilityState +
+        " document.hidden == " + document.hidden +
+        " document.hasFocus() == " + document.hasFocus());
+    }
+
+    function verifyNotFrozen() {
+      eventWhileFrozen |= frozen;
+    }
+
+    function CheckTiming(expectedTime) {
+      if (expectedTime != 0) {
+        // Callbacks are allowed to execute no more than kAllowedTimeDifference
+        // before or after the expected time, except when the app was frozen,
+        // in which case it should be executed immediately when resuming, while
+        // the document is still hidden.
+        if (wasEverFrozen && resumeTime > expectedTime && document.hidden) {
+          // The callback is too late, but that is because the document was
+          // frozen.
+          return;
+        }
+
+        if (Math.abs(performance.now() - expectedTime) > kAllowedTimeDifference) {
+          console.log('ERROR: Timer callback timing out of allowed range (' +
+              Math.round(Math.abs(performance.now() - expectedTime)) +
+              'ms difference)');
+          notReached();
+        }
+      }
+    }
+
+    function DoTimeout() {
+      verifyNotFrozen();
+      CheckTiming(expectedTimeoutTime);
+      logState('Timeout');
+      expectedTimeoutTime = performance.now() + kSetTimeoutDelay;
+      setTimeout(DoTimeout, kSetTimeoutDelay);
+    }
+
+    function DoLongTimeout() {
+      verifyNotFrozen();
+      CheckTiming(expectedLongTimeoutTime);
+      logState('Long Timeout');
+      if (wasEverFrozen && !eventWhileFrozen) {
+        onEndTest();
+      }
+
+      expectedLongTimeoutTime = performance.now() + kLongSetTimeoutDelay;
+      setTimeout(DoLongTimeout, kLongSetTimeoutDelay);
+    }
+
+    function DoInterval() {performance.now()
+      var now = performance.now();
+      verifyNotFrozen();
+      CheckTiming(expectedIntervalTime);
+      logState('Interval');
+      expectedIntervalTime = now + kSetIntervalDelay;
+    }
+
+    document.addEventListener("freeze", freezeCallback);
+    document.addEventListener("resume", resumeCallback);
+
+    DoLongTimeout();
+    DoTimeout();
+    setInterval(DoInterval, kSetIntervalDelay);
+
+    setupFinished();
+  </script>
+</body>
diff --git a/src/cobalt/black_box_tests/testdata/override_ua_parameters.html b/src/cobalt/black_box_tests/testdata/override_ua_parameters.html
new file mode 100644
index 0000000..d005b11
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/override_ua_parameters.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cobalt override ua parameters test</title>
+  <script src='black_box_js_test_utils.js'></script>
+</head>
+
+<body>
+  <div>Full User-Agent Data</div>
+
+  <script>
+    // Enable User-Agent Client Hints API
+    h5vcc.settings.set("NavigatorUAData", 1);
+
+    navigator.userAgentData.getHighEntropyValues(
+      ["model", "uaFullVersion", "cobaltBuildNumber", "cobaltBuildConfiguration",
+        "jsEngineVersion", "rasterizer", "evergreenVersion",
+        "starboardVersion", "originalDesignManufacturer",
+        "deviceType", "chipset", "modelYear", "deviceBrand",
+        "connectionType", "aux"])
+      .then(
+        ua => {
+          assertEqual(ua.aux, "foo.bar.baz.qux/21.2.1.41.0");
+          assertEqual(ua.deviceBrand, "Cobalt");
+          assertEqual(ua.cobaltBuildConfiguration, "debug");
+          assertEqual(ua.chipset, "foobar0000");
+          assertEqual(ua.cobaltBuildNumber, "289852");
+          assertEqual(ua.connectionType, "Wireless");
+          assertEqual(ua.deviceType, "ATV");
+          assertEqual(ua.evergreenVersion, "");
+          assertEqual(ua.jsEngineVersion, "v8/7.7.299.8-jit");
+          assertEqual(ua.model, "QUUX");
+          assertEqual(ua.modelYear, "2018");
+          assertEqual(ua.originalDesignManufacturer, "Quuz");
+          assertEqual(ua.starboardVersion, "Starboard/12");
+          assertEqual(ua.rasterizer, "gles");
+          assertEqual(ua.uaFullVersion, "21.lts.2.289852-debug");
+
+          onEndTest();
+          setupFinished();
+        });
+  </script>
+</body>
+
+</html>
diff --git a/src/cobalt/black_box_tests/tests/freeze_timers.py b/src/cobalt/black_box_tests/tests/freeze_timers.py
new file mode 100644
index 0000000..538743c
--- /dev/null
+++ b/src/cobalt/black_box_tests/tests/freeze_timers.py
@@ -0,0 +1,44 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests WindowTimers and application freezing and resuming."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import _env  # pylint: disable=unused-import
+
+import time
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+
+class FreezeVisibilityTest(black_box_tests.BlackBoxTestCase):
+  """Verify correct timer suspend and resume during and after freeze event."""
+
+  def test_simple(self):
+
+    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
+      url = server.GetURL(file_name='testdata/freeze_timers.html')
+
+      with self.CreateCobaltRunner(url=url) as runner:
+        runner.WaitForJSTestsSetup()
+        time.sleep(2.5)
+        runner.SendConceal()
+        time.sleep(2.5)
+        runner.SendFreeze()
+        time.sleep(3.333)
+        runner.SendFocus()
+        self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/override_ua_parameters.py b/src/cobalt/black_box_tests/tests/override_ua_parameters.py
new file mode 100644
index 0000000..4f5fd45
--- /dev/null
+++ b/src/cobalt/black_box_tests/tests/override_ua_parameters.py
@@ -0,0 +1,87 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Test overriding UA parameters when launching Cobalt."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import SimpleHTTPServer
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import MakeRequestHandlerClass
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+# The base path of the requested assets is the parent directory.
+_SERVER_ROOT_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
+
+
+class JavascriptRequestDetector(MakeRequestHandlerClass(_SERVER_ROOT_PATH)):
+  """Proxies everything to SimpleHTTPRequestHandler, except some paths."""
+
+  def do_GET(self):  # pylint: disable=invalid-name
+    """Handles HTTP GET requests for resources."""
+    # Check if UA string in the request header reflects correct UA params
+    # overrides
+    ua_request_header = self.headers.get('user-agent', '')
+    expected_ua_request_header = 'Mozilla/5.0 (Corge grault-v7a; '\
+        'Garply 7.1.2; Waldo OS 6.0) Cobalt/21.lts.2.289852-debug '\
+        '(unlike Gecko) v8/7.7.299.8-jit gles Starboard/12, '\
+        'Quuz_ATV_foobar0000_2018/Unknown (Cobalt, QUUX, Wireless) '\
+        'foo.bar.baz.qux/21.2.1.41.0'
+
+    if not ua_request_header == expected_ua_request_header:
+      raise ValueError('UA string in HTTP request header does not match with '\
+          'UA params overrides specified in command line\n'\
+          'UA string in HTTP request header:%s' % (ua_request_header))
+
+    return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+
+class OverrideUAParametersTest(black_box_tests.BlackBoxTestCase):
+  """Test overriding UA parameters."""
+
+  def test_simple(self):
+    """Set UA parameters when launching Cobalt."""
+
+    with ThreadedWebServer(
+        JavascriptRequestDetector,
+        binding_address=self.GetBindingAddress()) as server:
+      with self.CreateCobaltRunner(
+          url=server.GetURL(file_name='testdata/override_ua_parameters.html'),
+          target_params=[
+              '--user_agent_client_hints='\
+              'aux_field=foo.bar.baz.qux/21.2.1.41.0;'\
+              'brand=Cobalt;'\
+              'build_configuration=debug;'\
+              'chipset_model_number=foobar0000;'\
+              'cobalt_build_version_number=289852;'\
+              'cobalt_version=21.lts.2;'\
+              'connection_type=Wireless;'\
+              'device_type=ATV;'\
+              'evergreen_type=;'\
+              'evergreen_version=;'\
+              'javascript_engine_version=v8/7.7.299.8-jit;'\
+              'firmware_version=;'\
+              'model=QUUX;'\
+              'model_year=2018;'\
+              'original_design_manufacturer=Quuz;'\
+              'os_name_and_version=Corge grault-v7a\\; Garply 7.1.2\\; '\
+                  'Waldo OS 6.0;'\
+              'starboard_version=Starboard/12;'\
+              'rasterizer_type=gles'
+          ]) as runner:
+        runner.WaitForJSTestsSetup()
+        self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/web_debugger.py b/src/cobalt/black_box_tests/tests/web_debugger.py
index f7b9b11..6130376 100644
--- a/src/cobalt/black_box_tests/tests/web_debugger.py
+++ b/src/cobalt/black_box_tests/tests/web_debugger.py
@@ -224,8 +224,6 @@
   def setUp(self):
     platform_vars = self.platform_config.GetVariables(
         self.launcher_params.config)
-    if platform_vars['javascript_engine'] != 'v8':
-      self.skipTest('DevTools requires V8')
 
     cobalt_vars = self.cobalt_config.GetVariables(self.launcher_params.config)
     if not cobalt_vars['enable_debugger']:
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index bb922b0..1f6e058 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -1471,7 +1471,6 @@
   DCHECK(application_state_ == base::kApplicationStateBlurred);
   application_state_ = base::kApplicationStateConcealed;
   ConcealInternal(timestamp);
-  OnMaybeFreeze();
 }
 
 void BrowserModule::Focus(SbTimeMonotonic timestamp) {
@@ -1824,7 +1823,7 @@
 }
 
 void BrowserModule::OnMaybeFreeze() {
-  TRACE_EVENT0("cobalt::browser", "BrowserModule::MaybeFreeze()");
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::OnMaybeFreeze()");
   if (base::MessageLoop::current() != self_message_loop_) {
     self_message_loop_->task_runner()->PostTask(
         FROM_HERE,
@@ -1846,6 +1845,7 @@
       web_module_ready_to_freeze &&
       application_state_ == base::kApplicationStateConcealed) {
 #if SB_API_VERSION >= 13
+    DLOG(INFO) << "System request to freeze the app.";
     SbSystemRequestFreeze();
 #endif  // SB_API_VERSION >= 13
   }
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 785e879..9ff797a 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -175,6 +175,24 @@
     "'network_operator'_'device_type'_'chipset_model_number'_'model_year'/"
     "'firmware_version' ('brand', 'model', 'connection_type') 'aux_field'\".";
 
+const char kUserAgentClientHints[] = "user_agent_client_hints";
+const char kUserAgentClientHintsHelp[] =
+    "Specify custom user agent client hints for device simulations. "
+    "Configure user agent fields in a string delimited by semicolon ';'. "
+    "If semicolon is expected to be in value of any user agent field, "
+    "escape the semicolon by prefixing it with backslash '\\'. "
+    "Empty field value is allowed. "
+    "Example: "
+    "--user_agent_client_hints=\"device_type=GAME;"
+    "os_name_and_version=Linux armeabi-v7a\\; Android 7.1.2;evergreen_type=\" "
+    "The 18 supported UA fields for overriding are: aux_field, brand, "
+    "build_configuration, chipset_model_number, cobalt_build_version_number, "
+    "cobalt_build_version_number, connection_type, device_type, "
+    "evergreen_type,evergreen_version, firmware_version, "
+    "javascript_engine_version, model, model_year, "
+    "original_design_manufacturer, os_name_and_version, starboard_version, "
+    "rasterizer_type";
+
 const char kUserAgentOsNameVersion[] = "user_agent_os_name_version";
 const char kUserAgentOsNameVersionHelp[] =
     "Specifies a custom 'os_name_and_version' user agent field with otherwise "
@@ -446,6 +464,7 @@
         {kStubImageDecoder, kStubImageDecoderHelp},
         {kSuspendFuzzer, kSuspendFuzzerHelp}, {kTimedTrace, kTimedTraceHelp},
         {kUserAgent, kUserAgentHelp},
+        {kUserAgentClientHints, kUserAgentClientHintsHelp},
         {kUserAgentOsNameVersion, kUserAgentOsNameVersionHelp},
         {kUseTTS, kUseTTSHelp}, {kWebDriverListenIp, kWebDriverListenIpHelp},
         {kWebDriverPort, kWebDriverPortHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 9b3a0c2..bd69530 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -78,6 +78,8 @@
 extern const char kSuspendFuzzerHelp[];
 extern const char kTimedTrace[];
 extern const char kTimedTraceHelp[];
+extern const char kUserAgentClientHints[];
+extern const char kUserAgentClienthintsHelp[];
 extern const char kUserAgentOsNameVersion[];
 extern const char kUserAgentOsNameVersionHelp[];
 extern const char kUseTTS[];
diff --git a/src/cobalt/browser/user_agent_platform_info.cc b/src/cobalt/browser/user_agent_platform_info.cc
index 8c47fee..aab3d91 100644
--- a/src/cobalt/browser/user_agent_platform_info.cc
+++ b/src/cobalt/browser/user_agent_platform_info.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/browser/user_agent_platform_info.h"
 
+#include <map>
 #include <memory>
 
 #include "base/command_line.h"
@@ -37,49 +38,145 @@
 namespace cobalt {
 namespace browser {
 
-namespace {
+void GetUserAgentInputMap(
+    const std::string& user_agent_input,
+    std::map<std::string, std::string>& user_agent_input_map) {
+  struct state {
+    std::string field{""};
+    std::string value{""};
+    bool field_value_delimiter_found{false};
 
-std::string CreateDeviceTypeString(SbSystemDeviceType device_type) {
-  switch (device_type) {
-    case kSbSystemDeviceTypeBlueRayDiskPlayer:
-      return "BDP";
-    case kSbSystemDeviceTypeGameConsole:
-      return "GAME";
-    case kSbSystemDeviceTypeOverTheTopBox:
-      return "OTT";
-    case kSbSystemDeviceTypeSetTopBox:
-      return "STB";
-    case kSbSystemDeviceTypeTV:
-      return "TV";
-    case kSbSystemDeviceTypeAndroidTV:
-      return "ATV";
-    case kSbSystemDeviceTypeDesktopPC:
-      return "DESKTOP";
-    case kSbSystemDeviceTypeUnknown:
-      return "UNKNOWN";
-    default:
-      NOTREACHED();
-      return "UNKNOWN";
+    void reset() {
+      field.clear();
+      value.clear();
+      field_value_delimiter_found = false;
+    }
+  } current_state;
+
+  char escape_char = '\\';
+  char override_delimit_char = ';';
+  char field_value_delimit_char = '=';
+  bool prev_is_escape_char = false;
+
+  for (auto cur_char : user_agent_input) {
+    if (cur_char == override_delimit_char) {
+      if (prev_is_escape_char) {
+        if (!current_state.value.empty() &&
+            current_state.value.back() == escape_char) {  // escape delimiter
+                                                          // found in value.
+
+          current_state.value.back() = override_delimit_char;
+        } else {  // not a valid case for field, reset
+          current_state.reset();
+        }
+      } else {
+        if (current_state.field_value_delimiter_found) {  // valid field value
+                                                          // pair found.
+          user_agent_input_map[current_state.field] = current_state.value;
+        }  // else, in current captured override, field_value_delimit_char
+           // is not found, invalid.
+        current_state.reset();
+      }
+    } else if (cur_char == field_value_delimit_char) {
+      if (current_state.field.empty()) {  // field is not found when encounter
+                                          // field_value_delimit_char,
+                                          // invalid.
+        current_state.reset();
+      } else {
+        current_state.field_value_delimiter_found =
+            true;  // a field is found, next char is expected to be value
+      }
+    } else {
+      if (current_state.field_value_delimiter_found) {
+        current_state.value.push_back(cur_char);
+      } else {
+        current_state.field.push_back(cur_char);
+      }
+    }
+    if (cur_char == escape_char) {
+      prev_is_escape_char = true;
+    } else {
+      prev_is_escape_char = false;
+    }
+  }
+  if (current_state.field_value_delimiter_found) {
+    user_agent_input_map[current_state.field] = current_state.value;
   }
 }
 
-const char kUnspecifiedConnectionTypeName[] = "UnspecifiedConnectionType";
+namespace {
+
+struct DeviceTypeName {
+  SbSystemDeviceType device_type;
+  char device_type_string[8];
+};
+
+const DeviceTypeName kDeviceTypeStrings[] = {
+    {kSbSystemDeviceTypeBlueRayDiskPlayer, "BDP"},
+    {kSbSystemDeviceTypeGameConsole, "GAME"},
+    {kSbSystemDeviceTypeOverTheTopBox, "OTT"},
+    {kSbSystemDeviceTypeSetTopBox, "STB"},
+    {kSbSystemDeviceTypeTV, "TV"},
+    {kSbSystemDeviceTypeAndroidTV, "ATV"},
+    {kSbSystemDeviceTypeDesktopPC, "DESKTOP"},
+    {kSbSystemDeviceTypeUnknown, "UNKNOWN"}};
+
+std::string CreateDeviceTypeString(SbSystemDeviceType device_type) {
+  for (auto& map : kDeviceTypeStrings) {
+    if (map.device_type == device_type) {
+      return std::string(map.device_type_string);
+    }
+  }
+  NOTREACHED();
+  return "UNKNOWN";
+}
+
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+SbSystemDeviceType GetDeviceType(std::string device_type_string) {
+  for (auto& map : kDeviceTypeStrings) {
+    if (!SbStringCompareNoCase(map.device_type_string,
+                               device_type_string.c_str())) {
+      return map.device_type;
+    }
+  }
+  return kSbSystemDeviceTypeUnknown;
+}
+#endif
+
+struct ConnectionTypeName {
+  SbSystemConnectionType connection_type;
+  char connection_type_string[26];
+};
+
+const ConnectionTypeName kConnectionTypeStrings[] = {
+    {kSbSystemConnectionTypeWired, "Wired"},
+    {kSbSystemConnectionTypeWireless, "Wireless"},
+    {kSbSystemConnectionTypeUnknown, "UnspecifiedConnectionType"}};
 
 std::string CreateConnectionTypeString(
     const base::Optional<SbSystemConnectionType>& connection_type) {
   if (connection_type) {
-    switch (*connection_type) {
-      case kSbSystemConnectionTypeWired:
-        return "Wired";
-      case kSbSystemConnectionTypeWireless:
-        return "Wireless";
-      case kSbSystemConnectionTypeUnknown:
-        return kUnspecifiedConnectionTypeName;
+    for (auto& map : kConnectionTypeStrings) {
+      if (map.connection_type == connection_type) {
+        return std::string(map.connection_type_string);
+      }
     }
   }
-  return kUnspecifiedConnectionTypeName;
+  return "UnspecifiedConnectionType";
 }
 
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+SbSystemConnectionType GetConnectionType(std::string connection_type_string) {
+  for (auto& map : kConnectionTypeStrings) {
+    if (!SbStringCompareNoCase(map.connection_type_string,
+                               connection_type_string.c_str())) {
+      return map.connection_type;
+    }
+  }
+  return kSbSystemConnectionTypeUnknown;
+}
+#endif
+
 static bool isAsciiAlphaDigit(int c) {
   return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c);
 }
@@ -142,7 +239,6 @@
   return base::Optional<std::string>(clean);
 }
 
-
 // Function that will query Starboard and populate a UserAgentPlatformInfo
 // object based on those results.  This is de-coupled from
 // CreateUserAgentString() so that the common logic in CreateUserAgentString()
@@ -269,8 +365,85 @@
 
   // Connection type
   info.set_connection_type(SbSystemGetConnectionType());
-}
 
+// Apply overrides from command line
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+  if (base::CommandLine::InitializedForCurrentProcess()) {
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    if (command_line->HasSwitch(switches::kUserAgentClientHints)) {
+      LOG(INFO) << "Entering UA overrides";
+      std::string user_agent_input =
+          command_line->GetSwitchValueASCII(switches::kUserAgentClientHints);
+
+      std::map<std::string, std::string> user_agent_input_map;
+      GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+      for (const auto& input : user_agent_input_map) {
+        LOG(INFO) << "Overriding " << input.first << " to " << input.second;
+
+        if (!input.first.compare("starboard_version")) {
+          info.set_starboard_version(input.second);
+          LOG(INFO) << "Set starboard version to " << input.second;
+        } else if (!input.first.compare("os_name_and_version")) {
+          info.set_os_name_and_version(input.second);
+          LOG(INFO) << "Set os name and version to " << input.second;
+        } else if (!input.first.compare("original_design_manufacturer")) {
+          info.set_original_design_manufacturer(input.second);
+          LOG(INFO) << "Set original design manufacturer to " << input.second;
+        } else if (!input.first.compare("device_type")) {
+          info.set_device_type(GetDeviceType(input.second));
+          LOG(INFO) << "Set device type to " << input.second;
+        } else if (!input.first.compare("chipset_model_number")) {
+          info.set_chipset_model_number(input.second);
+          LOG(INFO) << "Set chipset model to " << input.second;
+        } else if (!input.first.compare("model_year")) {
+          info.set_model_year(input.second);
+          LOG(INFO) << "Set model year to " << input.second;
+        } else if (!input.first.compare("firmware_version")) {
+          info.set_firmware_version(input.second);
+          LOG(INFO) << "Set firmware version to " << input.second;
+        } else if (!input.first.compare("brand")) {
+          info.set_brand(input.second);
+          LOG(INFO) << "Set brand to " << input.second;
+        } else if (!input.first.compare("model")) {
+          info.set_model(input.second);
+          LOG(INFO) << "Set model to " << input.second;
+        } else if (!input.first.compare("aux_field")) {
+          info.set_aux_field(input.second);
+          LOG(INFO) << "Set aux field to " << input.second;
+        } else if (!input.first.compare("connection_type")) {
+          info.set_connection_type(GetConnectionType(input.second));
+          LOG(INFO) << "Set connection type to " << input.second;
+        } else if (!input.first.compare("javascript_engine_version")) {
+          info.set_javascript_engine_version(input.second);
+          LOG(INFO) << "Set javascript engine version to " << input.second;
+        } else if (!input.first.compare("rasterizer_type")) {
+          info.set_rasterizer_type(input.second);
+          LOG(INFO) << "Set rasterizer type to " << input.second;
+        } else if (!input.first.compare("evergreen_type")) {
+          info.set_evergreen_type(input.second);
+          LOG(INFO) << "Set evergreen type to " << input.second;
+        } else if (!input.first.compare("evergreen_version")) {
+          info.set_evergreen_version(input.second);
+          LOG(INFO) << "Set evergreen version to " << input.second;
+        } else if (!input.first.compare("cobalt_version")) {
+          info.set_cobalt_version(input.second);
+          LOG(INFO) << "Set cobalt type to " << input.second;
+        } else if (!input.first.compare("cobalt_build_version_number")) {
+          info.set_cobalt_build_version_number(input.second);
+          LOG(INFO) << "Set cobalt build version to " << input.second;
+        } else if (!input.first.compare("build_configuration")) {
+          info.set_build_configuration(input.second);
+          LOG(INFO) << "Set build configuration to " << input.second;
+        } else {
+          LOG(WARNING) << "Unsupported user agent field: " << input.first;
+        }
+      }
+    }
+  }
+
+#endif
+}
 }  // namespace
 
 UserAgentPlatformInfo::UserAgentPlatformInfo() {
diff --git a/src/cobalt/browser/user_agent_platform_info.h b/src/cobalt/browser/user_agent_platform_info.h
index db6c94d..7922671 100644
--- a/src/cobalt/browser/user_agent_platform_info.h
+++ b/src/cobalt/browser/user_agent_platform_info.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_BROWSER_USER_AGENT_PLATFORM_INFO_H_
 #define COBALT_BROWSER_USER_AGENT_PLATFORM_INFO_H_
 
+#include <map>
 #include <string>
 
 #include "cobalt/dom/user_agent_platform_info.h"
@@ -22,6 +23,10 @@
 namespace cobalt {
 namespace browser {
 
+void GetUserAgentInputMap(
+    const std::string& user_agent_input,
+    std::map<std::string, std::string>& user_agent_input_map);
+
 class UserAgentPlatformInfo : public dom::UserAgentPlatformInfo {
  public:
   UserAgentPlatformInfo();
diff --git a/src/cobalt/browser/user_agent_string_test.cc b/src/cobalt/browser/user_agent_string_test.cc
index d2537e7..bbd66a7 100644
--- a/src/cobalt/browser/user_agent_string_test.cc
+++ b/src/cobalt/browser/user_agent_string_test.cc
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/browser/user_agent_string.h"
+#include <map>
 
+#include "cobalt/browser/user_agent_platform_info.h"
+#include "cobalt/browser/user_agent_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
@@ -349,6 +351,87 @@
   EXPECT_NE(std::string::npos, user_agent_string.find(" V8/6.5.254.28"));
 }
 
+TEST(GetUserAgentInputMapTest, DelimitParamsBySemicolon) {
+  std::map<std::string, std::string> user_agent_input_map;
+  const std::string user_agent_input =
+      "model_year=2049;starboard_version=Starboard/"
+      "13;original_design_manufacturer=foo;device_type=GAME";
+  GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+  std::map<std::string, std::string> expected_user_agent_input_map{
+      {"device_type", "GAME"},
+      {"model_year", "2049"},
+      {"original_design_manufacturer", "foo"},
+      {"starboard_version", "Starboard/13"},
+  };
+  EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, HandleSpecialChar) {
+  std::map<std::string, std::string> user_agent_input_map;
+  const std::string user_agent_input =
+      "aux_field=foo.bar.baz.qux/"
+      "21.2.1.41.0;invalid-field~name=quux(#quuz)";
+  GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+  std::map<std::string, std::string> expected_user_agent_input_map{
+      {"aux_field", "foo.bar.baz.qux/21.2.1.41.0"},
+      {"invalid-field~name", "quux(#quuz)"},
+  };
+  EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, EscapeSemicolonInValue) {
+  std::map<std::string, std::string> user_agent_input_map;
+  const std::string user_agent_input =
+      "os_name_and_version=Foo bar-v7a\\; Baz 7.1.2\\; Qux OS "
+      "6.0;model=QUUX";
+  GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+  std::map<std::string, std::string> expected_user_agent_input_map{
+      {"model", "QUUX"},
+      {"os_name_and_version", "Foo bar-v7a; Baz 7.1.2; Qux OS 6.0"},
+  };
+  EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, OmitEscapeSemicolonInField) {
+  std::map<std::string, std::string> user_agent_input_map;
+  const std::string user_agent_input = "foo//;bar=baz";
+
+  GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+  std::map<std::string, std::string> expected_user_agent_input_map{
+      {"bar", "baz"},
+  };
+  EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, HandleEmptyFieldValue) {
+  std::map<std::string, std::string> user_agent_input_map;
+  const std::string user_agent_input =
+      "evergreen_type=;device_type=GAME;evergreen_version=";
+  GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+  std::map<std::string, std::string> expected_user_agent_input_map{
+      {"device_type", "GAME"},
+      {"evergreen_type", ""},
+      {"evergreen_version", ""},
+  };
+  EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, FailSafeWithInvalidInput) {
+  std::map<std::string, std::string> user_agent_input_map;
+  const std::string user_agent_input =
+      ";model;aux_field=;=dummy;device_type=GAME;invalid_field";
+  GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+  std::map<std::string, std::string> expected_user_agent_input_map{
+      {"aux_field", ""}, {"device_type", "GAME"},
+  };
+  EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
 }  // namespace
 
 }  // namespace browser
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 606938d..ef84f3b 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -261,8 +261,8 @@
   void DoSynchronousLayoutAndGetRenderTree(
       scoped_refptr<render_tree::Node>* render_tree);
 
-  void SetApplicationStartOrPreloadTimestamp(
-      bool is_preload, SbTimeMonotonic timestamp);
+  void SetApplicationStartOrPreloadTimestamp(bool is_preload,
+                                             SbTimeMonotonic timestamp);
   void SetDeepLinkTimestamp(SbTimeMonotonic timestamp);
 
  private:
@@ -1031,8 +1031,8 @@
 void WebModule::Impl::SetApplicationStartOrPreloadTimestamp(
     bool is_preload, SbTimeMonotonic timestamp) {
   DCHECK(window_);
-  window_->performance()->SetApplicationStartOrPreloadTimestamp(
-      is_preload, timestamp);
+  window_->performance()->SetApplicationStartOrPreloadTimestamp(is_preload,
+                                                                timestamp);
 }
 
 void WebModule::Impl::SetDeepLinkTimestamp(SbTimeMonotonic timestamp) {
@@ -1171,12 +1171,12 @@
   SetApplicationState(base::kApplicationStateBlurred, timestamp);
 }
 
-void WebModule::Impl::Conceal(
-    render_tree::ResourceProvider* resource_provider,
-    SbTimeMonotonic timestamp) {
+void WebModule::Impl::Conceal(render_tree::ResourceProvider* resource_provider,
+                              SbTimeMonotonic timestamp) {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Conceal()");
   SetResourceProvider(resource_provider);
 
+  SetApplicationState(base::kApplicationStateConcealed, timestamp);
   layout_manager_->Suspend();
   // Purge the cached resources prior to the freeze. That may cancel pending
   // loads, allowing the freeze to occur faster and preventing unnecessary
@@ -1201,21 +1201,25 @@
   }
 
   loader_factory_->UpdateResourceProvider(resource_provider_);
-  SetApplicationState(base::kApplicationStateConcealed, timestamp);
+
+  if (window_->media_session()->media_session_client() != NULL) {
+    window_->media_session()
+        ->media_session_client()
+        ->PostDelayedTaskForMaybeFreezeCallback();
+  }
 }
 
 void WebModule::Impl::Freeze(SbTimeMonotonic timestamp) {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Freeze()");
+  SetApplicationState(base::kApplicationStateFrozen, timestamp);
 
   // Clear out the loader factory's resource provider, possibly aborting any
   // in-progress loads.
   loader_factory_->Suspend();
-  SetApplicationState(base::kApplicationStateFrozen, timestamp);
 }
 
-void WebModule::Impl::Unfreeze(
-    render_tree::ResourceProvider* resource_provider,
-    SbTimeMonotonic timestamp) {
+void WebModule::Impl::Unfreeze(render_tree::ResourceProvider* resource_provider,
+                               SbTimeMonotonic timestamp) {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Unfreeze()");
   synchronous_loader_interrupt_.Reset();
   DCHECK(resource_provider);
@@ -1224,9 +1228,8 @@
   SetApplicationState(base::kApplicationStateConcealed, timestamp);
 }
 
-void WebModule::Impl::Reveal(
-  render_tree::ResourceProvider* resource_provider,
-  SbTimeMonotonic timestamp) {
+void WebModule::Impl::Reveal(render_tree::ResourceProvider* resource_provider,
+                             SbTimeMonotonic timestamp) {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Reveal()");
   synchronous_loader_interrupt_.Reset();
   DCHECK(resource_provider);
@@ -1693,9 +1696,8 @@
 
   impl_->CancelSynchronousLoads();
 
-  auto impl_blur =
-      base::Bind(&WebModule::Impl::Blur,
-                 base::Unretained(impl_.get()), timestamp);
+  auto impl_blur = base::Bind(&WebModule::Impl::Blur,
+                              base::Unretained(impl_.get()), timestamp);
 
 #if defined(ENABLE_DEBUGGER)
   // We normally need to block here so that the call doesn't return until the
@@ -1725,9 +1727,9 @@
   // We must block here so that the call doesn't return until the web
   // application has had a chance to process the whole event.
   message_loop()->task_runner()->PostBlockingTask(
-      FROM_HERE, base::Bind(&WebModule::Impl::Conceal,
-                            base::Unretained(impl_.get()),
-                            resource_provider, timestamp));
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::Conceal, base::Unretained(impl_.get()),
+                 resource_provider, timestamp));
 }
 
 void WebModule::Freeze(SbTimeMonotonic timestamp) {
@@ -1737,9 +1739,8 @@
   // We must block here so that the call doesn't return until the web
   // application has had a chance to process the whole event.
   message_loop()->task_runner()->PostBlockingTask(
-      FROM_HERE,
-      base::Bind(&WebModule::Impl::Freeze,
-                 base::Unretained(impl_.get()), timestamp));
+      FROM_HERE, base::Bind(&WebModule::Impl::Freeze,
+                            base::Unretained(impl_.get()), timestamp));
 }
 
 void WebModule::Unfreeze(render_tree::ResourceProvider* resource_provider,
@@ -1748,9 +1749,9 @@
   DCHECK_NE(base::MessageLoop::current(), message_loop());
 
   message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&WebModule::Impl::Unfreeze,
-                            base::Unretained(impl_.get()),
-                            resource_provider, timestamp));
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::Unfreeze, base::Unretained(impl_.get()),
+                 resource_provider, timestamp));
 }
 
 void WebModule::Reveal(render_tree::ResourceProvider* resource_provider,
@@ -1759,9 +1760,9 @@
   DCHECK_NE(base::MessageLoop::current(), message_loop());
 
   message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&WebModule::Impl::Reveal,
-                            base::Unretained(impl_.get()),
-                            resource_provider, timestamp));
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::Reveal, base::Unretained(impl_.get()),
+                 resource_provider, timestamp));
 }
 
 void WebModule::Focus(SbTimeMonotonic timestamp) {
@@ -1769,9 +1770,8 @@
   DCHECK_NE(base::MessageLoop::current(), message_loop());
 
   message_loop()->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&WebModule::Impl::Focus,
-                 base::Unretained(impl_.get()), timestamp));
+      FROM_HERE, base::Bind(&WebModule::Impl::Focus,
+                            base::Unretained(impl_.get()), timestamp));
 }
 
 void WebModule::ReduceMemory() {
@@ -1834,24 +1834,22 @@
   DCHECK(impl_);
   if (base::MessageLoop::current() != message_loop()) {
     message_loop()->task_runner()->PostBlockingTask(
-      FROM_HERE,
-      base::Bind(&WebModule::Impl::SetApplicationStartOrPreloadTimestamp,
-                 base::Unretained(impl_.get()), is_preload, timestamp));
+        FROM_HERE,
+        base::Bind(&WebModule::Impl::SetApplicationStartOrPreloadTimestamp,
+                   base::Unretained(impl_.get()), is_preload, timestamp));
   } else {
     impl_->SetApplicationStartOrPreloadTimestamp(is_preload, timestamp);
   }
 }
 
 void WebModule::SetDeepLinkTimestamp(SbTimeMonotonic timestamp) {
-  TRACE_EVENT0("cobalt::browser",
-               "WebModule::SetDeepLinkTimestamp()");
+  TRACE_EVENT0("cobalt::browser", "WebModule::SetDeepLinkTimestamp()");
   DCHECK(message_loop());
   DCHECK(impl_);
   if (base::MessageLoop::current() != message_loop()) {
     message_loop()->task_runner()->PostBlockingTask(
-      FROM_HERE,
-      base::Bind(&WebModule::Impl::SetDeepLinkTimestamp,
-                 base::Unretained(impl_.get()), timestamp));
+        FROM_HERE, base::Bind(&WebModule::Impl::SetDeepLinkTimestamp,
+                              base::Unretained(impl_.get()), timestamp));
   } else {
     impl_->SetDeepLinkTimestamp(timestamp);
   }
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 97ccdad..6409624 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-304062
\ No newline at end of file
+304711
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 3556181..0c543e7 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -410,12 +410,6 @@
     # a warning if the engine consumes more memory than this value specifies.
     'reduce_gpu_memory_by%': -1,
 
-    # Note: Ideally, |javascript_engine| would live here, however due to weird
-    # gyp variable usage in bindings gyp files (that if we removed, would
-    # result in large code duplication), we have to define it in the python gyp
-    # platform files instead.  See "starboard/build/platform_configuration.py"
-    # for its documentation.
-
     # Deprecated. Implement the CobaltExtensionConfigurationApi function
     # CobaltEnableJit instead.
     'cobalt_enable_jit%': -1,
diff --git a/src/cobalt/demos/content/performance-api-demo/performance-lifecycle-timing-demo.html b/src/cobalt/demos/content/performance-api-demo/performance-lifecycle-timing-demo.html
new file mode 100644
index 0000000..72e472f
--- /dev/null
+++ b/src/cobalt/demos/content/performance-api-demo/performance-lifecycle-timing-demo.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Performance Lifecycle Timing Demo</title>
+  <style>
+    body {
+      background-color: rgb(255, 255, 255);
+      color: #0047ab;
+      font-size: 50px;
+    }
+  </style>
+</head>
+
+<body>
+  <script>
+    var lifecycleTiming = performance.getEntriesByType("lifecycle")[0];
+
+    function LogInfo(state, time) {
+      var message = state + ": " + time;
+      console.log(message);
+      document.getElementById('info').innerHTML += message + ".\n";
+    }
+
+    function handleVisibilityChange() {
+      if (document.visibilityState == "visible") {
+        ReportPerformanceLifecycleTimingInfo();
+      }
+    }
+
+    function ReportPerformanceLifecycleTimingInfo() {
+      if (lifecycleTiming.appBlur != 0) {
+        LogInfo("Blur time", lifecycleTiming.appBlur);
+      }
+      if (lifecycleTiming.appConceal != 0) {
+        LogInfo("Conceal time", lifecycleTiming.appConceal);
+      }
+      if (lifecycleTiming.appReveal != 0) {
+        LogInfo("Reveal time", lifecycleTiming.appReveal);
+      }
+      if (lifecycleTiming.appFocus != 0) {
+        LogInfo("Focus time", lifecycleTiming.appFocus);
+      }
+      if (lifecycleTiming.appFreeze != 0) {
+        LogInfo("Freeze time", lifecycleTiming.appFreeze);
+      }
+      if (lifecycleTiming.appUnfreeze != 0) {
+        LogInfo("Unfreeze time", lifecycleTiming.appUnfreeze);
+      }
+    }
+
+    document.addEventListener("visibilitychange", handleVisibilityChange);
+
+    window.onload = function () {
+      LogInfo("Performance Now", window.performance.now());
+      LogInfo("Start time", lifecycleTiming.appStart);
+    };
+
+  </script>
+  <div>
+    <span id='info' style='white-space: pre; background-color:#00FF00'></span>
+  </div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/cobalt/demos/content/performance-api-demo/performance-api-demo.html b/src/cobalt/demos/content/performance-api-demo/performance-resource-timing-demo.html
similarity index 100%
rename from src/cobalt/demos/content/performance-api-demo/performance-api-demo.html
rename to src/cobalt/demos/content/performance-api-demo/performance-resource-timing-demo.html
diff --git a/src/cobalt/doc/docker_build.md b/src/cobalt/doc/docker_build.md
index 7a04812..4ba59a9 100644
--- a/src/cobalt/doc/docker_build.md
+++ b/src/cobalt/doc/docker_build.md
@@ -21,7 +21,7 @@
 where config is one of the four optimization levels, debug, devel, qa and gold,
 and target is the build target passed to `ninja`
 
-See [Cobalt README](https://cobalt.googlesource.com/cobalt/+/master/src/README.md#build-types)
+See [Cobalt README](../../README.md#build-types)
 for full details.
 
 Builds will be available in your `${COBALT_SRC}/out` directory.
@@ -63,4 +63,4 @@
 
   `docker-compose run linux-x64x11 /bin/bash`
 
-and try to build cobalt [with the usual `gyp / ninja` flow](https://cobalt.googlesource.com/cobalt/+/master/src/README.md#building-and-running-the-code).
+and try to build cobalt [with the usual `gyp / ninja` flow](../../README.md#building-and-running-the-code).
diff --git a/src/cobalt/doc/platform_services.md b/src/cobalt/doc/platform_services.md
index d2991b3..dadf6ae 100644
--- a/src/cobalt/doc/platform_services.md
+++ b/src/cobalt/doc/platform_services.md
@@ -53,7 +53,7 @@
 
 The Platform Services extension is exposed to the web app via the following IDL:
 
-*   [src/cobalt/h5vcc/h5vcc\_platform\_service.idl](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/cobalt/h5vcc/h5vcc_platform_service.idl)
+*   [src/cobalt/h5vcc/h5vcc\_platform\_service.idl](../h5vcc/h5vcc_platform_service.idl)
 
 The entrypoint for defined Platform Services extensions will be accessible in
 the `H5vccPlatformService` object. Note that ArrayBuffers are chosen to
@@ -62,10 +62,10 @@
 
 ### Starboard Interface
 
-Implementing the Starboard layer of Platform Service extension support uses the 
+Implementing the Starboard layer of Platform Service extension support uses the
 following interface in parallel with the IDL interface:
 
-*   [src/cobalt/extension/platform\_service.h](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/cobalt/extension/platform_service.h)
+*   [src/cobalt/extension/platform\_service.h](../extension/platform_service.h)
 
 `CobaltExtensionPlatformServiceApi` is the main interface for the Starboard
 layer.
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index 93e64d6..ce58fe0 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -159,21 +159,34 @@
   return cached_font_info.font;
 }
 
+std::vector<FontFace*> FontCache::GetFacesForFamilyAndStyle(
+    const std::string& family, render_tree::FontStyle style) {
+  std::vector<FontFace*> faces;
+  FontFaceMap::iterator font_face_map_iterator = font_face_map_->find(family);
+  if (font_face_map_iterator != font_face_map_->end()) {
+    // Add all font-face entries that match the family.
+    std::vector<const FontFaceStyleSet::Entry*> entries =
+        font_face_map_iterator->second.GetEntriesThatMatchStyle(style);
+    for (auto entry : entries) {
+      FontFace* face = new FontFace();
+      face->entry = entry;
+      faces.push_back(face);
+    }
+  } else {
+    // This is a local font. One face can represent it.
+    FontFace* face = new FontFace();
+    faces.push_back(face);
+  }
+  return faces;
+}
+
 scoped_refptr<render_tree::Font> FontCache::TryGetFont(
     const std::string& family, render_tree::FontStyle style, float size,
-    FontListFont::State* state) {
+    FontFace::State* state,
+    const FontFaceStyleSet::Entry* maybe_style_set_entry) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  FontFaceMap::iterator font_face_map_iterator = font_face_map_->find(family);
   int64 request_time_start = base::TimeTicks::Now().ToInternalValue();
-  if (font_face_map_iterator != font_face_map_->end()) {
-    // Retrieve the font face style set entry that most closely matches the
-    // desired style. Given that a font face was found for this family, it
-    // should never be NULL.
-    // https://www.w3.org/TR/css3-fonts/#font-prop-desc
-    const FontFaceStyleSet::Entry* style_set_entry =
-        font_face_map_iterator->second.MatchStyle(style);
-    DCHECK(style_set_entry != NULL);
-
+  if (maybe_style_set_entry) {
     // Walk the entry's sources:
     // - If a remote source is encountered, always return the results of its
     //   attempted retrieval, regardless of its success.
@@ -183,8 +196,9 @@
     //   instead.
     // https://www.w3.org/TR/css3-fonts/#src-desc
     for (FontFaceSources::const_iterator source_iterator =
-             style_set_entry->sources.begin();
-         source_iterator != style_set_entry->sources.end(); ++source_iterator) {
+             maybe_style_set_entry->sources.begin();
+         source_iterator != maybe_style_set_entry->sources.end();
+         ++source_iterator) {
       if (source_iterator->IsUrlSource()) {
         auto font = TryGetRemoteFont(source_iterator->GetUrl(), size, state);
         GlobalStats::GetInstance()->OnFontRequestComplete(request_time_start);
@@ -199,7 +213,7 @@
       }
     }
 
-    *state = FontListFont::kUnavailableState;
+    *state = FontFace::kUnavailableState;
     return NULL;
   } else {
     auto font = TryGetLocalFont(family, style, size, state);
@@ -335,7 +349,7 @@
 }
 
 scoped_refptr<render_tree::Font> FontCache::TryGetRemoteFont(
-    const GURL& url, float size, FontListFont::State* state) {
+    const GURL& url, float size, FontFace::State* state) {
   // Retrieve the font from the remote typeface cache, potentially triggering a
   // load.
   scoped_refptr<loader::font::CachedRemoteTypeface> cached_remote_typeface =
@@ -370,16 +384,16 @@
   scoped_refptr<render_tree::Typeface> typeface =
       cached_remote_typeface->TryGetResource();
   if (typeface.get() != NULL) {
-    *state = FontListFont::kLoadedState;
+    *state = FontFace::kLoadedState;
     return GetFontFromTypefaceAndSize(typeface, size);
   } else {
     if (cached_remote_typeface->IsLoadingComplete()) {
-      *state = FontListFont::kUnavailableState;
+      *state = FontFace::kUnavailableState;
     } else if (requested_remote_typeface_iterator->second
                    ->HasActiveRequestTimer()) {
-      *state = FontListFont::kLoadingWithTimerActiveState;
+      *state = FontFace::kLoadingWithTimerActiveState;
     } else {
-      *state = FontListFont::kLoadingWithTimerExpiredState;
+      *state = FontFace::kLoadingWithTimerExpiredState;
     }
     return NULL;
   }
@@ -387,7 +401,7 @@
 
 scoped_refptr<render_tree::Font> FontCache::TryGetLocalFont(
     const std::string& family, render_tree::FontStyle style, float size,
-    FontListFont::State* state) {
+    FontFace::State* state) {
   DCHECK(resource_provider());
   DCHECK(resource_provider() != NULL);
   // Only request the local font from the resource provider if the family is
@@ -398,10 +412,10 @@
   // signifies using the default font.
   if (!family.empty() &&
       !resource_provider()->HasLocalFontFamily(family.c_str())) {
-    *state = FontListFont::kUnavailableState;
+    *state = FontFace::kUnavailableState;
     return NULL;
   } else {
-    *state = FontListFont::kLoadedState;
+    *state = FontFace::kLoadedState;
     return GetFontFromTypefaceAndSize(
         GetCachedLocalTypeface(
             resource_provider()->GetLocalTypeface(family.c_str(), style)),
@@ -410,7 +424,7 @@
 }
 
 scoped_refptr<render_tree::Font> FontCache::TryGetLocalFontByFaceName(
-    const std::string& font_face, float size, FontListFont::State* state) {
+    const std::string& font_face, float size, FontFace::State* state) {
   do {
     if (font_face.empty()) {
       break;
@@ -424,11 +438,11 @@
     const scoped_refptr<render_tree::Typeface>& typeface_cached(
         GetCachedLocalTypeface(typeface));
 
-    *state = FontListFont::kLoadedState;
+    *state = FontFace::kLoadedState;
     return GetFontFromTypefaceAndSize(typeface_cached, size);
   } while (false);
 
-  *state = FontListFont::kUnavailableState;
+  *state = FontFace::kUnavailableState;
   return NULL;
 }
 
diff --git a/src/cobalt/dom/font_cache.h b/src/cobalt/dom/font_cache.h
index 44d10a9..932743c 100644
--- a/src/cobalt/dom/font_cache.h
+++ b/src/cobalt/dom/font_cache.h
@@ -201,13 +201,19 @@
   const scoped_refptr<render_tree::Font>& GetFontFromTypefaceAndSize(
       const scoped_refptr<render_tree::Typeface>& typeface, float size);
 
+
+  // Retrieves a list of the font faces that match the given family name and
+  // style. If no explicit font faces exist, creates a faux face for
+  // representing a local font.
+  std::vector<FontFace*> GetFacesForFamilyAndStyle(
+      const std::string& family, render_tree::FontStyle style);
+
   // Attempts to retrieve a font. If the family maps to a font face, then this
   // makes a request to |TryGetRemoteFont()|; otherwise, it makes a request
   // to |TryGetLocalFont()|. This function may return NULL.
-  scoped_refptr<render_tree::Font> TryGetFont(const std::string& family,
-                                              render_tree::FontStyle style,
-                                              float size,
-                                              FontListFont::State* state);
+  scoped_refptr<render_tree::Font> TryGetFont(
+      const std::string& family, render_tree::FontStyle style, float size,
+      FontFace::State* state, const FontFaceStyleSet::Entry* maybe_entry);
 
   // Returns the character fallback typeface map associated with the specified
   // style. Each unique style has its own exclusive map. If it doesn't already
@@ -259,20 +265,20 @@
   // font are registered with the remote typeface cache to be called when the
   // load finishes.
   scoped_refptr<render_tree::Font> TryGetRemoteFont(const GURL& url, float size,
-                                                    FontListFont::State* state);
+                                                    FontFace::State* state);
 
   // Returns NULL if the requested family is not empty and is not available in
   // the resource provider. Otherwise, returns the best matching local font.
   scoped_refptr<render_tree::Font> TryGetLocalFont(const std::string& family,
                                                    render_tree::FontStyle style,
                                                    float size,
-                                                   FontListFont::State* state);
+                                                   FontFace::State* state);
 
   // Lookup by a typeface (aka font_face), typeface is defined as font family +
   // style (weight, width, and style).
   // Returns NULL if the requested font face is not found.
   scoped_refptr<render_tree::Font> TryGetLocalFontByFaceName(
-      const std::string& font_face, float size, FontListFont::State* state);
+      const std::string& font_face, float size, FontFace::State* state);
 
   // Called when a remote typeface either successfully loads or fails to load.
   // In either case, the event can impact the fonts contained within the font
diff --git a/src/cobalt/dom/font_cache_test.cc b/src/cobalt/dom/font_cache_test.cc
index 49656eb..1c22311 100644
--- a/src/cobalt/dom/font_cache_test.cc
+++ b/src/cobalt/dom/font_cache_test.cc
@@ -100,6 +100,13 @@
   const std::string postscript_font_name("DancingScript");
   std::unique_ptr<FontCache::FontFaceMap> ffm =
       CreateFontFaceMapHelper(family_name, postscript_font_name);
+
+  const FontFaceStyleSet::Entry* entry = nullptr;
+  FontCache::FontFaceMap::iterator ffm_iterator = ffm->find(family_name);
+  if (ffm_iterator != ffm->end()) {
+    entry = ffm_iterator->second.GetEntriesThatMatchStyle(kNormalUpright)[0];
+  }
+  EXPECT_TRUE(entry);
   font_cache_->SetFontFaceMap(std::move(ffm));
 
   EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _))
@@ -109,9 +116,9 @@
               GetLocalTypefaceIfAvailableMock(postscript_font_name))
       .WillOnce(Return(sample_typeface_));
 
-  FontListFont::State state;
+  FontFace::State state;
   scoped_refptr<render_tree::Font> f =
-      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
+      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state, entry);
 
   EXPECT_TRUE(f);
 }
@@ -121,6 +128,13 @@
   const std::string family_name("Dancing Script");
   std::unique_ptr<FontCache::FontFaceMap> ffm =
       CreateFontFaceMapHelper(family_name, invalid_postscript_font_name);
+
+  const FontFaceStyleSet::Entry* entry = nullptr;
+  FontCache::FontFaceMap::iterator ffm_iterator = ffm->find(family_name);
+  if (ffm_iterator != ffm->end()) {
+    entry = ffm_iterator->second.GetEntriesThatMatchStyle(kNormalUpright)[0];
+  }
+  EXPECT_TRUE(entry);
   font_cache_->SetFontFaceMap(std::move(ffm));
 
   EXPECT_CALL(mock_resource_provider_,
@@ -128,9 +142,9 @@
       .Times(1);
   EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _));
 
-  FontListFont::State state;
+  FontFace::State state;
   scoped_refptr<render_tree::Font> f =
-      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
+      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state, entry);
   EXPECT_FALSE(f);
 }
 
diff --git a/src/cobalt/dom/font_face.cc b/src/cobalt/dom/font_face.cc
index 5f6fdb5..6c44d75 100644
--- a/src/cobalt/dom/font_face.cc
+++ b/src/cobalt/dom/font_face.cc
@@ -39,25 +39,22 @@
   }
 }
 
-const FontFaceStyleSet::Entry* FontFaceStyleSet::MatchStyle(
+std::vector<const FontFaceStyleSet::Entry*>
+FontFaceStyleSet::GetEntriesThatMatchStyle(
     const render_tree::FontStyle& pattern) const {
-  return entries_.empty() ? NULL
-                          : &entries_[GetClosestStyleEntryIndex(pattern)];
-}
-
-size_t FontFaceStyleSet::GetClosestStyleEntryIndex(
-    const render_tree::FontStyle& pattern) const {
-  size_t closest_index = 0;
+  std::vector<const FontFaceStyleSet::Entry*> entries;
   int max_score = std::numeric_limits<int>::min();
-  for (size_t i = 0; i < entries_.size(); ++i) {
-    int score = MatchScore(pattern, entries_[i].style);
-    if (score > max_score) {
-      closest_index = i;
-      max_score = score;
+  for (const auto& entry : entries_) {
+    int score = MatchScore(pattern, entry.style);
+    if (score >= max_score) {
+      if (score > max_score) {
+        max_score = score;
+        entries.clear();
+      }
+      entries.push_back(&entry);
     }
   }
-
-  return closest_index;
+  return entries;
 }
 
 int FontFaceStyleSet::MatchScore(
diff --git a/src/cobalt/dom/font_face.h b/src/cobalt/dom/font_face.h
index 1bfb40a..a5f80cc 100644
--- a/src/cobalt/dom/font_face.h
+++ b/src/cobalt/dom/font_face.h
@@ -66,7 +66,19 @@
       return style.weight == other.style.weight &&
              style.slant == other.style.slant && sources == other.sources;
     }
+    struct UnicodeRange {
+      // Sorts ranges primarily based on start and secondarily based on end.
+      bool operator<(const UnicodeRange& range) const {
+        if (start == range.start) {
+          return end < range.end;
+        }
+        return start < range.start;
+      }
+      uint32 start;
+      uint32 end;
+    };
 
+    std::set<UnicodeRange> unicode_range;
     render_tree::FontStyle style;
     FontFaceSources sources;
   };
@@ -77,10 +89,11 @@
   // into the set. All pre-existing url entries within the set are retained.
   void CollectUrlSources(std::set<GURL>* urls) const;
 
-  // Returns the style set entry with the style most closely matching the
-  // requested pattern. If the style set contains any entries, it is guaranteed
-  // to not return NULL.
-  const Entry* MatchStyle(const render_tree::FontStyle& pattern) const;
+
+  // Returns a list of entries with the style that most closesly matches the
+  // pattern.
+  std::vector<const Entry*> GetEntriesThatMatchStyle(
+      const render_tree::FontStyle& pattern) const;
 
   bool operator==(const FontFaceStyleSet& other) const {
     return entries_ == other.entries_;
@@ -89,11 +102,6 @@
  private:
   typedef std::vector<Entry> Entries;
 
-  // Returns the index of the entry with a style most closely matching the
-  // pattern (the lower the score, the more closely it matches). In the case of
-  // a tie, the earliest encountered entry is given priority.
-  size_t GetClosestStyleEntryIndex(const render_tree::FontStyle& pattern) const;
-
   // Returns the match score between two patterns. The score logic matches that
   // within SkFontStyleSet_Cobalt::match_score().
   int MatchScore(const render_tree::FontStyle& pattern,
diff --git a/src/cobalt/dom/font_face_updater.cc b/src/cobalt/dom/font_face_updater.cc
index 2a486c6..940eaaa 100644
--- a/src/cobalt/dom/font_face_updater.cc
+++ b/src/cobalt/dom/font_face_updater.cc
@@ -29,6 +29,7 @@
 #include "cobalt/cssom/property_value_visitor.h"
 #include "cobalt/cssom/string_value.h"
 #include "cobalt/cssom/style_sheet_list.h"
+#include "cobalt/cssom/unicode_range_value.h"
 #include "cobalt/cssom/url_src_value.h"
 #include "cobalt/cssom/url_value.h"
 
@@ -214,8 +215,10 @@
 
 void FontFaceProvider::VisitUnicodeRange(
     cssom::UnicodeRangeValue* unicode_range) {
-  NOTIMPLEMENTED()
-      << "FontFaceProvider::UnicodeRange support not implemented yet.";
+  FontFaceStyleSet::Entry::UnicodeRange range = {
+      static_cast<uint32>(unicode_range->start()),
+      static_cast<uint32>(unicode_range->end())};
+  style_set_entry_.unicode_range.insert(range);
 }
 
 // Check for a supported format. If no format hints are supplied, then the user
@@ -307,6 +310,9 @@
   if (css_font_face_rule->data()->weight()) {
     css_font_face_rule->data()->weight()->Accept(&font_face_provider);
   }
+  if (css_font_face_rule->data()->unicode_range()) {
+    css_font_face_rule->data()->unicode_range()->Accept(&font_face_provider);
+  }
 
   if (font_face_provider.IsFontFaceValid()) {
     (*font_face_map_)[font_face_provider.font_family()].AddEntry(
diff --git a/src/cobalt/dom/font_list.cc b/src/cobalt/dom/font_list.cc
index ae4cbb4..1328c87 100644
--- a/src/cobalt/dom/font_list.cc
+++ b/src/cobalt/dom/font_list.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/font_list.h"
 
 #include "base/i18n/char_iterator.h"
+#include "cobalt/base/unicode/character_values.h"
 #include "cobalt/dom/font_cache.h"
 
 namespace cobalt {
@@ -24,6 +25,19 @@
 
 const base::char16 kHorizontalEllipsisValue = 0x2026;
 
+bool CharInRange(
+    const std::set<FontFaceStyleSet::Entry::UnicodeRange>& unicode_range,
+    uint32 utf32_character) {
+  if (unicode_range.empty()) return true;
+  for (const auto& range : unicode_range) {
+    if (range.start > utf32_character) break;
+    if ((range.start <= utf32_character) && (utf32_character <= range.end)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 FontList::FontList(FontCache* font_cache, const FontListKey& font_list_key)
@@ -40,18 +54,34 @@
           font_cache_->GetCharacterFallbackTypefaceMap(style_)) {
   // Add all of the family names to the font list fonts.
   for (size_t i = 0; i < font_list_key.family_names.size(); ++i) {
-    fonts_.push_back(FontListFont(font_list_key.family_names[i]));
+    FontListFont font = FontListFont(font_list_key.family_names[i]);
+    font.faces =
+        font_cache_->GetFacesForFamilyAndStyle(font.family_name, style_);
+    fonts_.push_back(font);
   }
 
   // Add an empty font at the end in order to fall back to the default typeface.
-  fonts_.push_back(FontListFont(""));
+  FontListFont default_font = FontListFont("");
+  FontFace* default_face = new FontFace();
+  default_font.faces = std::vector<FontFace*>{default_face};
+  fonts_.push_back(default_font);
+}
+
+FontList::~FontList() {
+  for (FontListFont font : fonts_) {
+    for (FontFace* face : font.faces) {
+      delete face;
+    }
+  }
 }
 
 void FontList::Reset() {
   for (size_t i = 0; i < fonts_.size(); ++i) {
     FontListFont& font_list_font = fonts_[i];
-    font_list_font.set_state(FontListFont::kUnrequestedState);
-    font_list_font.set_font(NULL);
+    for (auto face : font_list_font.faces) {
+      face->state = FontFace::kUnrequestedState;
+      face->font = NULL;
+    }
   }
 
   primary_font_ = NULL;
@@ -69,20 +99,22 @@
   for (size_t i = 0; i < fonts_.size(); ++i) {
     FontListFont& font_list_font = fonts_[i];
 
-    if (font_list_font.state() == FontListFont::kLoadingWithTimerActiveState ||
-        font_list_font.state() == FontListFont::kLoadingWithTimerExpiredState) {
-      font_list_font.set_state(FontListFont::kUnrequestedState);
-      // If a loaded font hasn't been found yet, then the cached values need to
-      // be reset. It'll potentially change the primary font.
-      if (!found_loaded_font) {
-        primary_font_ = NULL;
-        is_font_metrics_set_ = false;
-        is_space_width_set_ = false;
-        is_ellipsis_info_set_ = false;
-        ellipsis_font_ = NULL;
+    for (auto face : font_list_font.faces) {
+      if (face->state == FontFace::kLoadingWithTimerActiveState ||
+          face->state == FontFace::kLoadingWithTimerExpiredState) {
+        face->state = FontFace::kUnrequestedState;
+        // If a loaded font hasn't been found yet, then the cached values need
+        // to be reset. It'll potentially change the primary font.
+        if (!found_loaded_font) {
+          primary_font_ = NULL;
+          is_font_metrics_set_ = false;
+          is_space_width_set_ = false;
+          is_ellipsis_info_set_ = false;
+          ellipsis_font_ = NULL;
+        }
+      } else if (face->state == FontFace::kLoadedState) {
+        found_loaded_font = true;
       }
-    } else if (font_list_font.state() == FontListFont::kLoadedState) {
-      found_loaded_font = true;
     }
   }
 }
@@ -97,8 +129,10 @@
     // display text, simply leaving transparent text is considered
     // non-conformant behavior."
     //   https://www.w3.org/TR/css3-fonts/#font-face-loading
-    if (fonts_[i].state() == FontListFont::kLoadingWithTimerActiveState) {
-      return false;
+    for (auto face : fonts_[i].faces) {
+      if (face->state == FontFace::kLoadingWithTimerActiveState) {
+        return false;
+      }
     }
   }
 
@@ -194,18 +228,21 @@
   // Walk the list of fonts, requesting any encountered that are in an
   // unrequested state. The first font encountered that has the character is the
   // character font.
-  for (size_t i = 0; i < fonts_.size(); ++i) {
-    FontListFont& font_list_font = fonts_[i];
+  for (auto font_list_font : fonts_) {
+    for (FontFace* face : font_list_font.faces) {
+      const FontFaceStyleSet::Entry* entry = face->entry;
+      if (entry && !CharInRange(entry->unicode_range, utf32_character)) {
+        continue;
+      }
+      if (face->state == FontFace::kUnrequestedState) {
+        RequestFont(font_list_font.family_name, face);
+      }
 
-    if (font_list_font.state() == FontListFont::kUnrequestedState) {
-      RequestFont(i);
-    }
-
-    if (font_list_font.state() == FontListFont::kLoadedState) {
-      *glyph_index =
-          font_list_font.font()->GetGlyphForCharacter(utf32_character);
-      if (*glyph_index != render_tree::kInvalidGlyphIndex) {
-        return font_list_font.font();
+      if (face->state == FontFace::kLoadedState) {
+        *glyph_index = face->font->GetGlyphForCharacter(utf32_character);
+        if (*glyph_index != render_tree::kInvalidGlyphIndex) {
+          return face->font;
+        }
       }
     }
   }
@@ -241,18 +278,25 @@
   // time to do it now.
   if (!primary_font_) {
     // Walk the list of fonts, requesting any encountered that are in an
-    // unrequested state. The first font encountered that is loaded is
-    // the primary font.
-    for (size_t i = 0; i < fonts_.size(); ++i) {
-      FontListFont& font_list_font = fonts_[i];
+    // unrequested state. The first font encountered that is loaded and whose
+    // unicode range includes the space character is the primary font.
+    // https://www.w3.org/TR/css-fonts-4/#first-available-font
+    for (auto font_list_font : fonts_) {
+      for (FontFace* face : font_list_font.faces) {
+        const FontFaceStyleSet::Entry* entry = face->entry;
+        if (entry && !CharInRange(entry->unicode_range,
+                                  base::unicode::kSpaceCharacter)) {
+          continue;
+        }
+        if (face->state == FontFace::kUnrequestedState) {
+          RequestFont(font_list_font.family_name, face);
+        }
 
-      if (font_list_font.state() == FontListFont::kUnrequestedState) {
-        RequestFont(i);
-      }
-
-      if (font_list_font.state() == FontListFont::kLoadedState) {
-        primary_font_ = font_list_font.font();
-        break;
+        if (face->state == FontFace::kLoadedState) {
+          primary_font_ = face->font;
+          DCHECK(primary_font_);
+          return primary_font_;
+        }
       }
     }
   }
@@ -261,41 +305,20 @@
   return primary_font_;
 }
 
-void FontList::RequestFont(size_t index) {
-  FontListFont& font_list_font = fonts_[index];
-  FontListFont::State state;
+void FontList::RequestFont(const std::string& family, FontFace* used_face) {
+  FontFace::State state;
 
   // Request the font from the font cache; the state of the font will be set
   // during the call.
-  scoped_refptr<render_tree::Font> render_tree_font = font_cache_->TryGetFont(
-      font_list_font.family_name(), style_, size_, &state);
+  scoped_refptr<render_tree::Font> render_tree_font =
+      font_cache_->TryGetFont(family, style_, size_, &state, used_face->entry);
 
-  if (state == FontListFont::kLoadedState) {
+  if (state == FontFace::kLoadedState) {
     DCHECK(render_tree_font.get() != NULL);
 
-    // Walk all of the fonts in the list preceding the loaded font. If they have
-    // the same typeface as the loaded font, then set the font list font as a
-    // duplicate. There's no reason to have multiple fonts in the list with the
-    // same typeface.
-    render_tree::TypefaceId typeface_id = render_tree_font->GetTypefaceId();
-    for (size_t i = 0; i < index; ++i) {
-      FontListFont& check_font = fonts_[i];
-      if (check_font.state() == FontListFont::kLoadedState &&
-          check_font.font()->GetTypefaceId() == typeface_id) {
-        font_list_font.set_state(FontListFont::kDuplicateState);
-        break;
-      }
-    }
-
-    // If this font wasn't a duplicate, then its time to initialize its font
-    // data. This font is now available to use.
-    if (font_list_font.state() != FontListFont::kDuplicateState) {
-      font_list_font.set_state(FontListFont::kLoadedState);
-      font_list_font.set_font(render_tree_font);
-    }
-  } else {
-    font_list_font.set_state(state);
+    used_face->font = render_tree_font;
   }
+  used_face->state = state;
 }
 
 void FontList::GenerateEllipsisInfo() {
@@ -310,10 +333,10 @@
 
 void FontList::GenerateSpaceWidth() {
   if (!is_space_width_set_) {
-    const scoped_refptr<render_tree::Font>& primary_font = GetPrimaryFont();
-    render_tree::GlyphIndex space_glyph =
-        primary_font->GetGlyphForCharacter(' ');
-    space_width_ = primary_font->GetGlyphWidth(space_glyph);
+    render_tree::GlyphIndex space_glyph = render_tree::kInvalidGlyphIndex;
+    space_width_ =
+        GetCharacterFont(base::unicode::kSpaceCharacter, &space_glyph)
+            ->GetGlyphWidth(space_glyph);
     if (space_width_ == 0) {
       DLOG(WARNING) << "Font being used with space width of 0!";
     }
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h
index 8c95c94..1d2ddbd 100644
--- a/src/cobalt/dom/font_list.h
+++ b/src/cobalt/dom/font_list.h
@@ -22,6 +22,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/containers/small_map.h"
 #include "base/memory/ref_counted.h"
+#include "cobalt/dom/font_face.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/font.h"
 #include "cobalt/render_tree/font_provider.h"
@@ -33,41 +34,41 @@
 
 class FontCache;
 
-// A specific font family within a font list. It has an internal state, which
-// lets the font list know whether or not the font has already been requested,
-// and if so, whether or not it was available. |font_| and |character_map_|
-// will only be non-NULL in the case where |state_| is set to |kLoadedState|.
-class FontListFont {
- public:
+// A font-face for a font-family. It has an internal state, which lets the font
+// list know whether or not the font has already been requested, and if so,
+// whether or not it was available. |font_| will only be non-NULL in the case
+// where |state_| is set to |kLoadedState|.
+struct FontFace {
   enum State {
     kUnrequestedState,
     kLoadingWithTimerActiveState,
     kLoadingWithTimerExpiredState,
     kLoadedState,
     kUnavailableState,
-    kDuplicateState,
   };
+  State state = kUnrequestedState;
+  const FontFaceStyleSet::Entry* entry = nullptr;
 
+  // The render_tree::Font obtained via the font cache using |family_name_| in
+  // font list font, along with |style_| and |size_| from the containing font
+  // list, and the unicode range needed for the requested character. It is only
+  // non-NULL in the case where |state_| is set to |kLoadedState|.
+  scoped_refptr<render_tree::Font> font;
+};
+
+// A specific font family within a font list. A family may have more than one
+// FontFace if the FontFaces have different unicode ranges specified.
+struct FontListFont {
   explicit FontListFont(const std::string& family_name)
-      : family_name_(family_name), state_(kUnrequestedState) {}
+      : family_name(family_name) {}
 
-  const std::string& family_name() const { return family_name_; }
-
-  State state() const { return state_; }
-  void set_state(State state) { state_ = state; }
-
-  const scoped_refptr<render_tree::Font>& font() const { return font_; }
-  void set_font(const scoped_refptr<render_tree::Font>& font) { font_ = font; }
-
- private:
-  std::string family_name_;
-  State state_;
+  std::string family_name;
 
   // The render_tree::Font obtained via the font cache using |family_name_| in
   // font list font, along with |style_| and |size_| from the containing font
   // list. It is only non-NULL in the case where |state_| is set to
   // |kLoadedState|.
-  scoped_refptr<render_tree::Font> font_;
+  std::vector<FontFace*> faces;
 };
 
 // The key used for maps with a |FontList| value. It is also used for
@@ -178,6 +179,8 @@
       int32 utf32_character, render_tree::GlyphIndex* glyph_index) override;
 
  private:
+  ~FontList() override;
+
   const scoped_refptr<render_tree::Font>& GetFallbackCharacterFont(
       int32 utf32_character, render_tree::GlyphIndex* glyph_index);
 
@@ -191,9 +194,10 @@
   const scoped_refptr<render_tree::Font>& GetPrimaryFont();
 
   // Request a font from the font cache and update its state depending on the
-  // results of the request. If the font is successfully set, then both its
-  // |font_| and |character_map_| are non-NULL after this call.
-  void RequestFont(size_t index);
+  // results of the request. If the font is successfully set, then its |font_|
+  // is non-NULL after this call.
+  void RequestFont(const std::string& family, FontFace* face);
+
 
   // Lazily generates the ellipsis font and ellipsis width. If it is already
   // generated then it immediately returns.
diff --git a/src/cobalt/dom/html_style_element.cc b/src/cobalt/dom/html_style_element.cc
index a83be61..aa80cde 100644
--- a/src/cobalt/dom/html_style_element.cc
+++ b/src/cobalt/dom/html_style_element.cc
@@ -79,9 +79,8 @@
 
   base::Optional<std::string> content = text_content();
   const std::string& text = content.value_or(base::EmptyString());
-  if (bypass_csp || text.empty() ||
-      csp_delegate->AllowInline(CspDelegate::kStyle, inline_style_location_,
-                                text)) {
+  if (bypass_csp || csp_delegate->AllowInline(CspDelegate::kStyle,
+                                              inline_style_location_, text)) {
     scoped_refptr<cssom::CSSStyleSheet> css_style_sheet =
         document->html_element_context()->css_parser()->ParseStyleSheet(
             text, inline_style_location_);
diff --git a/src/cobalt/dom/performance.cc b/src/cobalt/dom/performance.cc
index 8154cc1..390fc55 100644
--- a/src/cobalt/dom/performance.cc
+++ b/src/cobalt/dom/performance.cc
@@ -87,7 +87,10 @@
       base::DefaultClock::GetInstance(), tick_clock_);
   lifecycle_timing_ = base::MakeRefCounted<PerformanceLifecycleTiming>(
       "lifecycle timing", time_origin());
+  // Queue lifecycle timing.
   QueuePerformanceEntry(lifecycle_timing_);
+  // Add lifecycle timing to the performance entry buffer.
+  performance_entry_buffer_.push_back(lifecycle_timing_);
 }
 
 // static
diff --git a/src/cobalt/dom/performance_high_resolution_time.h b/src/cobalt/dom/performance_high_resolution_time.h
index 0684e41..4388b2e 100644
--- a/src/cobalt/dom/performance_high_resolution_time.h
+++ b/src/cobalt/dom/performance_high_resolution_time.h
@@ -43,19 +43,26 @@
 //   https://w3c.github.io/hr-time/#clock-resolution
 inline DOMHighResTimeStamp ClampTimeStampMinimumResolution(
     base::TimeTicks ticks,
-    int64_t min_resolution_in_microseconds) {
-    int64_t microseconds = ticks.ToInternalValue();
-    return base::TimeDelta::FromMicroseconds(microseconds -
-        (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
+  int64_t min_resolution_in_microseconds) {
+  int64_t microseconds = ticks.since_origin().InMicroseconds();
+  return base::TimeDelta::FromMicroseconds(microseconds -
+      (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
 }
 
 // Clamp customized minimum clock resolution in milliseconds.
 //   https://w3c.github.io/hr-time/#clock-resolution
 inline DOMHighResTimeStamp ClampTimeStampMinimumResolution(base::TimeDelta delta,
     int64_t min_resolution_in_microseconds) {
-    int64_t microseconds = delta.InMicroseconds();
-    return base::TimeDelta::FromMicroseconds(microseconds -
-        (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
+  int64_t microseconds = delta.InMicroseconds();
+  return base::TimeDelta::FromMicroseconds(microseconds -
+      (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
+}
+
+inline DOMHighResTimeStamp
+    ClampTimeStampMinimumResolution(DOMHighResTimeStamp time_delta,
+                                    int64_t min_resolution_in_microseconds) {
+  return time_delta -
+      (static_cast<int64_t>(time_delta) % min_resolution_in_microseconds);
 }
 
 }  // namespace dom
diff --git a/src/cobalt/dom/performance_lifecycle_timing.cc b/src/cobalt/dom/performance_lifecycle_timing.cc
index f09fee0..d81ebb0 100644
--- a/src/cobalt/dom/performance_lifecycle_timing.cc
+++ b/src/cobalt/dom/performance_lifecycle_timing.cc
@@ -44,11 +44,8 @@
       const DOMHighResTimeStamp time_origin, SbTimeMonotonic monotonic_time) {
     SbTimeMonotonic time_delta = SbTimeGetNow() - SbTimeGetMonotonicNow();
     base::Time base_time = base::Time::FromSbTime(time_delta + monotonic_time);
-    base::TimeTicks time_ticks =
-        base::TimeTicks::FromInternalValue(static_cast<int64_t>(
-            base_time.ToJsTime()));
-    return ClampTimeStampMinimumResolution(time_ticks,
-        Performance::kPerformanceTimerMinResolutionInMicroseconds) - time_origin;
+    return ClampTimeStampMinimumResolution(base_time.ToJsTime() - time_origin,
+        Performance::kPerformanceTimerMinResolutionInMicroseconds);
   }
 
 }  // namespace
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 52fe0bb..34e0c86 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -164,8 +164,8 @@
                                captions, script_value_factory)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           relay_on_load_event_(new RelayLoadEvent(this))),
-      ALLOW_THIS_IN_INITIALIZER_LIST(
-          window_timers_(new WindowTimers(this, debugger_hooks()))),
+      ALLOW_THIS_IN_INITIALIZER_LIST(window_timers_(
+          new WindowTimers(this, debugger_hooks(), initial_application_state))),
       ALLOW_THIS_IN_INITIALIZER_LIST(animation_frame_request_callback_list_(
           new AnimationFrameRequestCallbackList(this, debugger_hooks()))),
       crypto_(new Crypto()),
@@ -540,6 +540,7 @@
       state);
   if (timestamp == 0) return;
   performance_->SetApplicationState(state, timestamp);
+  window_timers_->SetApplicationState(state);
 }
 
 bool Window::ReportScriptError(const script::ErrorReport& error_report) {
diff --git a/src/cobalt/dom/window_timers.cc b/src/cobalt/dom/window_timers.cc
index 283208c..3b5c079 100644
--- a/src/cobalt/dom/window_timers.cc
+++ b/src/cobalt/dom/window_timers.cc
@@ -19,15 +19,18 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/application_state.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/global_stats.h"
 #include "nb/memory_scope.h"
 
 namespace cobalt {
 namespace dom {
 
-int WindowTimers::SetTimeout(const TimerCallbackArg& handler, int timeout) {
-  TRACK_MEMORY_SCOPE("DOM");
+int WindowTimers::TryAddNewTimer(Timer::TimerType type,
+                                 const TimerCallbackArg& handler, int timeout) {
   int handle = GetFreeTimerHandle();
   DCHECK(handle);
 
@@ -37,15 +40,16 @@
   }
 
   if (callbacks_active_) {
-    auto* timer = new base::OneShotTimer();
-    timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout),
-                 base::Bind(&WindowTimers::RunTimerCallback,
-                            base::Unretained(this), handle));
-    timers_[handle] = new TimerInfo(
-        owner_, std::unique_ptr<base::internal::TimerBase>(timer), handler);
+    auto* timer = new Timer(type, owner_, handler, timeout, handle, this);
+    if (application_state_ != base::kApplicationStateFrozen) {
+      timer->StartOrResume();
+    }
+    timers_[handle] = timer;
     debugger_hooks_.AsyncTaskScheduled(
-        timers_[handle], "SetTimeout",
-        base::DebuggerHooks::AsyncTaskFrequency::kOneshot);
+        timers_[handle], type == Timer::kOneShot ? "SetTimeout" : "SetInterval",
+        type == Timer::kOneShot
+            ? base::DebuggerHooks::AsyncTaskFrequency::kOneshot
+            : base::DebuggerHooks::AsyncTaskFrequency::kRecurring);
   } else {
     timers_[handle] = nullptr;
   }
@@ -53,32 +57,16 @@
   return handle;
 }
 
+int WindowTimers::SetTimeout(const TimerCallbackArg& handler, int timeout) {
+  TRACK_MEMORY_SCOPE("DOM");
+  return TryAddNewTimer(Timer::kOneShot, handler, timeout);
+}
+
 void WindowTimers::ClearTimeout(int handle) { timers_.erase(handle); }
 
 int WindowTimers::SetInterval(const TimerCallbackArg& handler, int timeout) {
-  int handle = GetFreeTimerHandle();
-  DCHECK(handle);
-
-  if (handle == 0) {  // unable to get a free timer handle
-    // avoid accidentally overwriting existing timers
-    return 0;
-  }
-
-  if (callbacks_active_) {
-    auto* timer(new base::RepeatingTimer());
-    timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout),
-                 base::Bind(&WindowTimers::RunTimerCallback,
-                            base::Unretained(this), handle));
-    timers_[handle] = new TimerInfo(
-        owner_, std::unique_ptr<base::internal::TimerBase>(timer), handler);
-    debugger_hooks_.AsyncTaskScheduled(
-        timers_[handle], "SetInterval",
-        base::DebuggerHooks::AsyncTaskFrequency::kRecurring);
-  } else {
-    timers_[handle] = nullptr;
-  }
-
-  return handle;
+  TRACK_MEMORY_SCOPE("DOM");
+  return TryAddNewTimer(Timer::kRepeating, handler, timeout);
 }
 
 void WindowTimers::ClearInterval(int handle) {
@@ -138,7 +126,7 @@
   {
     // Keep a |TimerInfo| reference, so it won't be released when running the
     // callback.
-    scoped_refptr<TimerInfo> timer_info = timer->second;
+    scoped_refptr<Timer> timer_info = timer->second;
     base::ScopedAsyncTask async_task(debugger_hooks_, timer_info);
     timer_info->callback_reference().value().Run();
   }
@@ -157,5 +145,92 @@
   GlobalStats::GetInstance()->StopJavaScriptEvent();
 }
 
+void WindowTimers::SetApplicationState(base::ApplicationState state) {
+  switch (state) {
+    case base::kApplicationStateFrozen:
+      DCHECK_EQ(application_state_, base::kApplicationStateConcealed);
+      for (auto timer : timers_) {
+        timer.second->Pause();
+      }
+      break;
+    case base::kApplicationStateConcealed:
+      if (application_state_ == base::kApplicationStateFrozen) {
+        for (auto timer : timers_) {
+          timer.second->StartOrResume();
+        }
+      }
+      break;
+    case base::kApplicationStateStopped:
+    case base::kApplicationStateBlurred:
+    case base::kApplicationStateStarted:
+      break;
+  }
+  application_state_ = state;
+}
+
+WindowTimers::Timer::Timer(TimerType type, script::Wrappable* const owner,
+                           const TimerCallbackArg& callback, int timeout,
+                           int handle, WindowTimers* window_timers)
+    : type_(type),
+      callback_(owner, callback),
+      timeout_(timeout),
+      handle_(handle),
+      window_timers_(window_timers) {}
+
+void WindowTimers::Timer::Pause() {
+  if (timer_) {
+    // The desired runtime is preserved here to determine whether the timer
+    // should fire immediately when resuming.
+    desired_run_time_ = timer_->desired_run_time();
+    timer_.reset();
+  }
+}
+
+void WindowTimers::Timer::StartOrResume() {
+  if (timer_ != nullptr) return;
+  switch (type_) {
+    case kOneShot:
+      if (desired_run_time_) {
+        // Adjust the new timeout for the time spent while paused.
+        auto now = base::TimeTicks::Now();
+        if (desired_run_time_ <= now) {
+          // The previous desired run time was in the past or is right now, so
+          // it should fire immediately.
+          timeout_ = 0;
+        } else {
+          // Set the timeout to keep the same desired run time. Note that since
+          // the timer uses the timeout that we request to set a new desired
+          // run time from the clock, the resumed timer will likely fire
+          // slightly later.
+          base::TimeDelta time_delta(desired_run_time_.value() - now);
+          timeout_ = static_cast<int>(time_delta.InMilliseconds());
+        }
+      }
+      timer_ = CreateAndStart<base::OneShotTimer>();
+      break;
+    case kRepeating:
+      timer_ = CreateAndStart<base::RepeatingTimer>();
+      if (timeout_ && desired_run_time_ &&
+          desired_run_time_ < base::TimeTicks::Now()) {
+        // The timer was paused and the desired run time is in the past.
+        // Call the callback once before continuing the repeating timer.
+        base::SequencedTaskRunnerHandle::Get()->PostTask(
+            FROM_HERE, base::Bind(&WindowTimers::RunTimerCallback,
+                                  base::Unretained(window_timers_), handle_));
+      }
+      break;
+  }
+}
+
+template <class TimerClass>
+std::unique_ptr<base::internal::TimerBase>
+WindowTimers::Timer::CreateAndStart() {
+  auto* timer = new TimerClass();
+  timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_),
+               base::Bind(&WindowTimers::RunTimerCallback,
+                          base::Unretained(window_timers_), handle_));
+  return std::unique_ptr<base::internal::TimerBase>(timer);
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/window_timers.h b/src/cobalt/dom/window_timers.h
index 5d4442b..42dcf56 100644
--- a/src/cobalt/dom/window_timers.h
+++ b/src/cobalt/dom/window_timers.h
@@ -16,10 +16,15 @@
 #define COBALT_DOM_WINDOW_TIMERS_H_
 
 #include <memory>
+#include <utility>
+#include <vector>
 
 #include "base/containers/hash_tables.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "cobalt/base/application_state.h"
 #include "cobalt/base/debugger_hooks.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/script_value.h"
@@ -33,10 +38,11 @@
   typedef script::CallbackFunction<void()> TimerCallback;
   typedef script::ScriptValue<TimerCallback> TimerCallbackArg;
   explicit WindowTimers(script::Wrappable* const owner,
-                        const base::DebuggerHooks& debugger_hooks)
-      : current_timer_index_(0),
-        owner_(owner),
-        debugger_hooks_(debugger_hooks) {}
+                        const base::DebuggerHooks& debugger_hooks,
+                        base::ApplicationState application_state)
+      : owner_(owner),
+        debugger_hooks_(debugger_hooks),
+        application_state_(application_state) {}
   ~WindowTimers() {}
 
   int SetTimeout(const TimerCallbackArg& handler, int timeout);
@@ -55,25 +61,51 @@
   // event queue without adding more on to the end of it.
   void DisableCallbacks();
 
+  void SetApplicationState(base::ApplicationState state);
+
  private:
-  class TimerInfo : public base::RefCounted<TimerInfo> {
+  class Timer : public base::RefCounted<Timer> {
    public:
-    TimerInfo(script::Wrappable* const owner,
-              std::unique_ptr<base::internal::TimerBase> timer,
-              const TimerCallbackArg& callback)
-        : timer_(std::move(timer)), callback_(owner, callback) {}
+    enum TimerType { kOneShot, kRepeating };
+
+    Timer(TimerType type, script::Wrappable* const owner,
+          const TimerCallbackArg& callback, int timeout, int handle,
+          WindowTimers* window_timers);
 
     base::internal::TimerBase* timer() { return timer_.get(); }
     TimerCallbackArg::Reference& callback_reference() { return callback_; }
 
+    // Pause this timer. The timer will not fire when paused.
+    void Pause();
+    // Start or Resume this timer. If the timer was paused and the desired run
+    // time is in the past, it will fire immediately.
+    void StartOrResume();
+
    private:
-    ~TimerInfo() {}
+    ~Timer() {}
+
+    // Create and start a timer of the specified TimerClass type.
+    template <class TimerClass>
+    std::unique_ptr<base::internal::TimerBase> CreateAndStart();
+
+    TimerType type_;
     std::unique_ptr<base::internal::TimerBase> timer_;
     TimerCallbackArg::Reference callback_;
+    int timeout_;
+    int handle_;
+    WindowTimers* window_timers_;
 
-    friend class base::RefCounted<TimerInfo>;
+    // Store the desired run tim of a paused timer.
+    base::Optional<base::TimeTicks> desired_run_time_;
+
+    friend class base::RefCounted<Timer>;
   };
-  typedef base::hash_map<int, scoped_refptr<TimerInfo> > Timers;
+  typedef base::hash_map<int, scoped_refptr<Timer> > Timers;
+
+  // Try to add a new timer of the given type, return the handle or 0 when
+  // failed.
+  int TryAddNewTimer(Timer::TimerType type, const TimerCallbackArg& handler,
+                     int timeout);
 
   // Returns a positive integer timer handle that hasn't been assigned, or 0
   // if none can be found.
@@ -84,7 +116,7 @@
   void RunTimerCallback(int handle);
 
   Timers timers_;
-  int current_timer_index_;
+  int current_timer_index_ = 0;
   script::Wrappable* const owner_;
   const base::DebuggerHooks& debugger_hooks_;
 
@@ -92,6 +124,8 @@
   // is fired as we are waiting for it to drain.
   bool callbacks_active_ = true;
 
+  base::ApplicationState application_state_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowTimers);
 };
 
diff --git a/src/cobalt/extension/configuration.h b/src/cobalt/extension/configuration.h
index 4dc64d3..f9ff6fa 100644
--- a/src/cobalt/extension/configuration.h
+++ b/src/cobalt/extension/configuration.h
@@ -190,10 +190,9 @@
   // the specified amount. -1 disables the value.
   int (*CobaltReduceGpuMemoryBy)();
 
-  // Can be set to enable zealous garbage collection, if |javascript_engine|
-  // supports it.  Zealous garbage collection will cause garbage collection
-  // to occur much more frequently than normal, for the purpose of finding or
-  // reproducing bugs.
+  // Can be set to enable zealous garbage collection. Zealous garbage
+  // collection will cause garbage collection to occur much more frequently
+  // than normal, for the purpose of finding or reproducing bugs.
   bool (*CobaltGcZeal)();
 
   // Defines what kind of rasterizer will be used.  This can be adjusted to
diff --git a/src/cobalt/h5vcc/h5vcc_platform_service.cc b/src/cobalt/h5vcc/h5vcc_platform_service.cc
index 8462103..f20b5ca 100644
--- a/src/cobalt/h5vcc/h5vcc_platform_service.cc
+++ b/src/cobalt/h5vcc/h5vcc_platform_service.cc
@@ -102,9 +102,21 @@
   }
   uint64_t output_length = 0;
   bool invalid_state = 0;
+
+  // Make sure the data pointer is not null as the platform service may not
+  // handle that properly.
+  void* data_ptr = data->Data();
+  size_t data_length = data->ByteLength();
+  if (data_ptr == nullptr) {
+    // If the data length is 0, then it's okay to point to a static array.
+    DCHECK(data_length == 0);
+    static int null_data = 0;
+    data_ptr = &null_data;
+    data_length = 0;
+  }
+
   void* output_data = platform_service_api_->Send(
-      ext_service_, data->Data(), data->ByteLength(), &output_length,
-      &invalid_state);
+      ext_service_, data_ptr, data_length, &output_length, &invalid_state);
   if (invalid_state) {
     dom::DOMException::Raise(dom::DOMException::kInvalidStateErr,
                              "Service unable to accept data currently.",
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 3254864..a42730d 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -567,6 +567,11 @@
   return margin_top() + border_top_width();
 }
 
+void Box::SetPaddingInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+                           LayoutUnit bottom) {
+  padding_insets_.SetInsets(left, top, right, bottom);
+}
+
 RectLayoutUnit Box::GetContentBoxFromMarginBox() const {
   return RectLayoutUnit(GetContentBoxLeftEdgeOffsetFromMarginBox(),
                         GetContentBoxTopEdgeOffsetFromMarginBox(), width(),
@@ -982,20 +987,11 @@
   }
 }
 
-bool IsBorderStyleNoneOrHidden(
-    const scoped_refptr<cssom::PropertyValue>& border_style) {
-  if (border_style == cssom::KeywordValue::GetNone() ||
-      border_style == cssom::KeywordValue::GetHidden()) {
-    return true;
-  }
-  return false;
-}
-
 render_tree::BorderStyle GetRenderTreeBorderStyle(
     const scoped_refptr<cssom::PropertyValue>& border_style) {
   render_tree::BorderStyle render_tree_border_style =
       render_tree::kBorderStyleNone;
-  if (!IsBorderStyleNoneOrHidden(border_style)) {
+  if (!Box::IsBorderStyleNoneOrHidden(border_style)) {
     DCHECK_EQ(border_style, cssom::KeywordValue::GetSolid());
     render_tree_border_style = render_tree::kBorderStyleSolid;
   }
@@ -1003,25 +999,26 @@
   return render_tree_border_style;
 }
 
-Border CreateBorderFromStyle(
-    const scoped_refptr<const cssom::CSSComputedStyleData>& style) {
+Border CreateBorderFromUsedStyle(
+    const scoped_refptr<const cssom::CSSComputedStyleData>& style,
+    InsetsLayoutUnit border_insets) {
   render_tree::BorderSide left(
-      GetUsedNonNegativeLength(style->border_left_width()).toFloat(),
+      border_insets.left().toFloat(),
       GetRenderTreeBorderStyle(style->border_left_style()),
       GetUsedColor(style->border_left_color()));
 
   render_tree::BorderSide right(
-      GetUsedNonNegativeLength(style->border_right_width()).toFloat(),
+      border_insets.right().toFloat(),
       GetRenderTreeBorderStyle(style->border_right_style()),
       GetUsedColor(style->border_right_color()));
 
   render_tree::BorderSide top(
-      GetUsedNonNegativeLength(style->border_top_width()).toFloat(),
+      border_insets.top().toFloat(),
       GetRenderTreeBorderStyle(style->border_top_style()),
       GetUsedColor(style->border_top_color()));
 
   render_tree::BorderSide bottom(
-      GetUsedNonNegativeLength(style->border_bottom_width()).toFloat(),
+      border_insets.bottom().toFloat(),
       GetRenderTreeBorderStyle(style->border_bottom_style()),
       GetUsedColor(style->border_bottom_color()));
 
@@ -1033,38 +1030,35 @@
     const scoped_refptr<cssom::MutableCSSComputedStyleData>&
         destination_style) {
   // NOTE: Properties set by PopulateBaseStyleForBorderNode() should match the
-  // properties used by SetupBorderNodeFromStyle().
+  // properties used by SetupBorderNodeFromUsedStyle(), except for the border
+  // width which is not used to determine the render tree border.
 
   // Left
-  destination_style->set_border_left_width(source_style->border_left_width());
   destination_style->set_border_left_style(source_style->border_left_style());
   destination_style->set_border_left_color(source_style->border_left_color());
 
   // Right
-  destination_style->set_border_right_width(source_style->border_right_width());
   destination_style->set_border_right_style(source_style->border_right_style());
   destination_style->set_border_right_color(source_style->border_right_color());
 
   // Top
-  destination_style->set_border_top_width(source_style->border_top_width());
   destination_style->set_border_top_style(source_style->border_top_style());
   destination_style->set_border_top_color(source_style->border_top_color());
 
   // Bottom
-  destination_style->set_border_bottom_width(
-      source_style->border_bottom_width());
   destination_style->set_border_bottom_style(
       source_style->border_bottom_style());
   destination_style->set_border_bottom_color(
       source_style->border_bottom_color());
 }
 
-void SetupBorderNodeFromStyle(
+void SetupBorderNodeFromUsedStyle(
     const base::Optional<RoundedCorners>& rounded_corners,
+    const InsetsLayoutUnit border_insets,
     const scoped_refptr<const cssom::CSSComputedStyleData>& style,
     RectNode::Builder* rect_node_builder) {
-  rect_node_builder->border =
-      std::unique_ptr<Border>(new Border(CreateBorderFromStyle(style)));
+  rect_node_builder->border = std::unique_ptr<Border>(
+      new Border(CreateBorderFromUsedStyle(style, border_insets)));
 
   if (rounded_corners) {
     rect_node_builder->rounded_corners =
@@ -1358,7 +1352,7 @@
       IsBorderStyleNoneOrHidden(computed_style()->border_top_style()) &&
       IsBorderStyleNoneOrHidden(computed_style()->border_right_style()) &&
       IsBorderStyleNoneOrHidden(computed_style()->border_bottom_style())) {
-    border_insets_ = InsetsLayoutUnit();
+    ResetBorderInsets();
     return;
   }
 
@@ -1631,18 +1625,25 @@
 
   math::RectF rect(GetClampedBorderBoxSize());
   RectNode::Builder rect_node_builder(rect);
-  SetupBorderNodeFromStyle(rounded_corners, computed_style(),
-                           &rect_node_builder);
+
+  // When an inline box is split, margins, borders, and padding
+  // have no visual effect where the split occurs. (or at any split, when there
+  // are several).
+  //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+
+  SetupBorderNodeFromUsedStyle(rounded_corners, border_insets_,
+                               computed_style(), &rect_node_builder);
 
   scoped_refptr<RectNode> border_node(
       new RectNode(std::move(rect_node_builder)));
   border_node_builder->AddChild(border_node);
 
   if (has_animated_border) {
-    AddAnimations<RectNode>(
-        base::Bind(&PopulateBaseStyleForBorderNode),
-        base::Bind(&SetupBorderNodeFromStyle, rounded_corners),
-        *css_computed_style_declaration(), border_node, animate_node_builder);
+    AddAnimations<RectNode>(base::Bind(&PopulateBaseStyleForBorderNode),
+                            base::Bind(&SetupBorderNodeFromUsedStyle,
+                                       rounded_corners, border_insets_),
+                            *css_computed_style_declaration(), border_node,
+                            animate_node_builder);
   }
 }
 
@@ -2116,6 +2117,20 @@
   }
 }
 
+void Box::SetBorderInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+                          LayoutUnit bottom) {
+  border_insets_.SetInsets(left, top, right, bottom);
+}
+
+bool Box::IsBorderStyleNoneOrHidden(
+    const scoped_refptr<cssom::PropertyValue>& border_style) {
+  if (border_style == cssom::KeywordValue::GetNone() ||
+      border_style == cssom::KeywordValue::GetHidden()) {
+    return true;
+  }
+  return false;
+}
+
 bool Box::ApplyTransformActionToCoordinate(TransformAction action,
                                            math::Vector2dF* coordinate) const {
   std::vector<math::Vector2dF> coordinate_vector;
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 06a2790..a36935a 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -403,6 +403,8 @@
       bool transform_forms_root) const;
   Vector2dLayoutUnit GetBorderBoxOffsetFromMarginBox() const;
 
+  void ResetBorderInsets() { border_insets_ = InsetsLayoutUnit(); }
+
   // Padding box.
   LayoutUnit padding_left() const { return padding_insets_.left(); }
   LayoutUnit padding_top() const { return padding_insets_.top(); }
@@ -419,6 +421,11 @@
   LayoutUnit GetPaddingBoxLeftEdgeOffsetFromMarginBox() const;
   LayoutUnit GetPaddingBoxTopEdgeOffsetFromMarginBox() const;
 
+  // Set padding insets in InlineContainerBox UpdatePaddings to an empty
+  // LayoutUnit or the computed_style value using is_split_on_*_.
+  void SetPaddingInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+                        LayoutUnit bottom);
+
   // Content box.
   LayoutUnit width() const { return content_size_.width(); }
   LayoutUnit height() const { return content_size_.height(); }
@@ -729,6 +736,9 @@
   base::Optional<LayoutUnit> collapsed_margin_bottom_;
   base::Optional<LayoutUnit> collapsed_empty_margin_;
 
+  static bool IsBorderStyleNoneOrHidden(
+      const scoped_refptr<cssom::PropertyValue>& border_style);
+
  protected:
   UsedStyleProvider* used_style_provider() const {
     return used_style_provider_;
@@ -807,6 +817,11 @@
       const base::Optional<LayoutUnit>& possibly_overconstrained_margin_left,
       const base::Optional<LayoutUnit>& possibly_overconstrained_margin_right);
 
+  // Set border insets in InlineContainerBox UpdateBorders to an empty
+  // LayoutUnit or the computed_style value using is_split_on_*_.
+  void SetBorderInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+                       LayoutUnit bottom);
+
  private:
   struct CachedRenderTreeNodeInfo {
     explicit CachedRenderTreeNodeInfo(const math::Vector2dF& offset)
@@ -817,9 +832,9 @@
   };
 
   // Updates used values of "border" properties.
-  void UpdateBorders();
+  virtual void UpdateBorders();
   // Updates used values of "padding" properties.
-  void UpdatePaddings(const LayoutParams& layout_params);
+  virtual void UpdatePaddings(const LayoutParams& layout_params);
 
   // Computes the normalized "outer" rounded corners (if there are any) from the
   // border radii.
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index ad09c7d..6a09ed1 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -626,7 +626,7 @@
 
       container_box_ = base::WrapRefCounted(new InlineContainerBox(
           css_computed_style_declaration_, context_->used_style_provider,
-          context_->layout_stat_tracker));
+          context_->layout_stat_tracker, (*paragraph_)->base_direction()));
       break;
     // Generate an inline-level block container box. The inside of
     // an inline-block is formatted as a block box, and the element itself
diff --git a/src/cobalt/layout/inline_container_box.cc b/src/cobalt/layout/inline_container_box.cc
index e59dd7f..2a9bc8d 100644
--- a/src/cobalt/layout/inline_container_box.cc
+++ b/src/cobalt/layout/inline_container_box.cc
@@ -27,7 +27,7 @@
     const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
         css_computed_style_declaration,
     UsedStyleProvider* used_style_provider,
-    LayoutStatTracker* layout_stat_tracker)
+    LayoutStatTracker* layout_stat_tracker, BaseDirection base_direction)
     : ContainerBox(css_computed_style_declaration, used_style_provider,
                    layout_stat_tracker),
       should_collapse_leading_white_space_(false),
@@ -41,7 +41,10 @@
           css_computed_style_declaration->data()->font_family(),
           css_computed_style_declaration->data()->font_size(),
           css_computed_style_declaration->data()->font_style(),
-          css_computed_style_declaration->data()->font_weight())) {}
+          css_computed_style_declaration->data()->font_weight())),
+      is_split_on_left_(false),
+      is_split_on_right_(false),
+      base_direction_(base_direction) {}
 
 InlineContainerBox::~InlineContainerBox() {}
 
@@ -55,7 +58,7 @@
         // container box.
         return false;
       }
-      // Fall through if out-of-flow.
+    // Fall through if out-of-flow.
 
     case kInlineLevel:
       // If the inline container box already contains a line break, then no
@@ -74,13 +77,19 @@
 }
 
 scoped_refptr<ContainerBox> InlineContainerBox::TrySplitAtEnd() {
-  scoped_refptr<InlineContainerBox> box_after_split(
-      new InlineContainerBox(css_computed_style_declaration(),
-                             used_style_provider(), layout_stat_tracker()));
-  // When an inline box is split, margins, borders, and padding have no visual
-  // effect where the split occurs.
-  //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
-  // TODO: Implement the above comment.
+  scoped_refptr<InlineContainerBox> box_after_split(new InlineContainerBox(
+      css_computed_style_declaration(), used_style_provider(),
+      layout_stat_tracker(), base_direction_));
+  // Set the state of where the sibling boxes are split using
+  // base_direction_ to determine the correct way to split the boxes for
+  // dir : rtl or ltr.
+  if (base_direction_ == kLeftToRightBaseDirection) {
+    is_split_on_right_ = true;
+    box_after_split->SetIsSplitOnLeft(true);
+  } else {
+    is_split_on_left_ = true;
+    box_after_split->SetIsSplitOnRight(true);
+  }
 
   return box_after_split;
 }
@@ -93,6 +102,14 @@
   return inline_top_margin_;
 }
 
+void InlineContainerBox::SetIsSplitOnLeft(bool is_split_on_left) {
+  is_split_on_left_ = is_split_on_left;
+}
+
+void InlineContainerBox::SetIsSplitOnRight(bool is_split_on_right) {
+  is_split_on_right_ = is_split_on_right;
+}
+
 void InlineContainerBox::UpdateContentSizeAndMargins(
     const LayoutParams& layout_params) {
   // Lay out child boxes as one line without width constraints and white space
@@ -127,16 +144,36 @@
     //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
     set_width(line_box.shrink_to_fit_width());
 
-    base::Optional<LayoutUnit> maybe_margin_left = GetUsedMarginLeftIfNotAuto(
-        computed_style(), layout_params.containing_block_size);
-    base::Optional<LayoutUnit> maybe_margin_right = GetUsedMarginRightIfNotAuto(
-        computed_style(), layout_params.containing_block_size);
+    if (is_split_on_left_) {
+      // When an inline box is split, margins, borders, and padding
+      // have no visual effect where the split occurs. (or at any split, when
+      // there are several).
+      //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+      set_margin_left(LayoutUnit());
+    } else {
+      // A computed value of "auto" for "margin-left" or "margin-right" becomes
+      // a used value of "0".
+      //   https://www.w3.org/TR/CSS21/visudet.html#inline-width
+      base::Optional<LayoutUnit> maybe_margin_left = GetUsedMarginLeftIfNotAuto(
+          computed_style(), layout_params.containing_block_size);
+      set_margin_left(maybe_margin_left.value_or(LayoutUnit()));
+    }
 
-    // A computed value of "auto" for "margin-left" or "margin-right" becomes
-    // a used value of "0".
-    //   https://www.w3.org/TR/CSS21/visudet.html#inline-width
-    set_margin_left(maybe_margin_left.value_or(LayoutUnit()));
-    set_margin_right(maybe_margin_right.value_or(LayoutUnit()));
+    if (is_split_on_right_) {
+      // When an inline box is split, margins, borders, and padding
+      // have no visual effect where the split occurs. (or at any split, when
+      // there are several).
+      //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+      set_margin_right(LayoutUnit());
+    } else {
+      // A computed value of "auto" for "margin-left" or "margin-right" becomes
+      // a used value of "0".
+      //   https://www.w3.org/TR/CSS21/visudet.html#inline-width
+      base::Optional<LayoutUnit> maybe_margin_right =
+          GetUsedMarginRightIfNotAuto(computed_style(),
+                                      layout_params.containing_block_size);
+      set_margin_right(maybe_margin_right.value_or(LayoutUnit()));
+    }
   }
 
   // The "height" property does not apply. The height of the content area should
@@ -172,6 +209,44 @@
   baseline_offset_from_margin_box_top_ = line_box.baseline_offset_from_top();
 }
 
+void InlineContainerBox::UpdateBorders() {
+  if (IsBorderStyleNoneOrHidden(computed_style()->border_left_style()) &&
+      IsBorderStyleNoneOrHidden(computed_style()->border_top_style()) &&
+      IsBorderStyleNoneOrHidden(computed_style()->border_right_style()) &&
+      IsBorderStyleNoneOrHidden(computed_style()->border_bottom_style())) {
+    ResetBorderInsets();
+    return;
+  }
+  // When an inline box is split, margins, borders, and padding
+  // have no visual effect where the split occurs. (or at any split, when there
+  // are several).
+  //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+  SetBorderInsets(
+      is_split_on_left_ ? LayoutUnit() : GetUsedBorderLeft(computed_style()),
+      GetUsedBorderTop(computed_style()),
+      is_split_on_right_ ? LayoutUnit() : GetUsedBorderRight(computed_style()),
+      GetUsedBorderBottom(computed_style()));
+}
+
+void InlineContainerBox::UpdatePaddings(const LayoutParams& layout_params) {
+  // When an inline box is split, margins, borders, and padding
+  // have no visual effect where the split occurs. (or at any split, when there
+  // are several).
+  //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+  SetPaddingInsets(
+      is_split_on_left_
+          ? LayoutUnit()
+          : GetUsedPaddingLeft(computed_style(),
+                               layout_params.containing_block_size),
+      GetUsedPaddingTop(computed_style(), layout_params.containing_block_size),
+      is_split_on_right_
+          ? LayoutUnit()
+          : GetUsedPaddingRight(computed_style(),
+                                layout_params.containing_block_size),
+      GetUsedPaddingBottom(computed_style(),
+                           layout_params.containing_block_size));
+}
+
 WrapResult InlineContainerBox::TryWrapAt(
     WrapAtPolicy wrap_at_policy, WrapOpportunityPolicy wrap_opportunity_policy,
     bool is_line_existence_justified, LayoutUnit available_width,
@@ -632,14 +707,21 @@
 
 void InlineContainerBox::SplitAtIterator(
     Boxes::const_iterator child_split_iterator) {
-  // TODO: When an inline box is split, margins, borders, and padding
-  //       have no visual effect where the split occurs.
-  //   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
-
   // Move the children after the split into a new box.
-  scoped_refptr<InlineContainerBox> box_after_split(
-      new InlineContainerBox(css_computed_style_declaration(),
-                             used_style_provider(), layout_stat_tracker()));
+  scoped_refptr<InlineContainerBox> box_after_split(new InlineContainerBox(
+      css_computed_style_declaration(), used_style_provider(),
+      layout_stat_tracker(), base_direction_));
+
+  // Set the state of where the sibling boxes are split using
+  // base_direction_ to determine the correct way to split the boxes for
+  // dir : rtl or ltr.
+  if (base_direction_ == kLeftToRightBaseDirection) {
+    is_split_on_right_ = true;
+    box_after_split->SetIsSplitOnLeft(true);
+  } else {
+    is_split_on_left_ = true;
+    box_after_split->SetIsSplitOnRight(true);
+  }
 
   // Update the split sibling links.
   box_after_split->split_sibling_ = split_sibling_;
diff --git a/src/cobalt/layout/inline_container_box.h b/src/cobalt/layout/inline_container_box.h
index 1c15c83..dbd3df7 100644
--- a/src/cobalt/layout/inline_container_box.h
+++ b/src/cobalt/layout/inline_container_box.h
@@ -39,7 +39,8 @@
   InlineContainerBox(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
                          css_computed_style_declaration,
                      UsedStyleProvider* used_style_provider,
-                     LayoutStatTracker* layout_stat_tracker);
+                     LayoutStatTracker* layout_stat_tracker,
+                     BaseDirection base_direction);
   ~InlineContainerBox() override;
 
   // From |Box|.
@@ -47,6 +48,10 @@
 
   void UpdateContentSizeAndMargins(const LayoutParams& layout_params) override;
 
+  void UpdateBorders() override;
+
+  void UpdatePaddings(const LayoutParams& layout_params) override;
+
   WrapResult TryWrapAt(WrapAtPolicy wrap_at_policy,
                        WrapOpportunityPolicy wrap_opportunity_policy,
                        bool is_line_existence_justified,
@@ -76,6 +81,8 @@
   LayoutUnit GetBaselineOffsetFromTopMarginEdge() const override;
   LayoutUnit GetInlineLevelBoxHeight() const override;
   LayoutUnit GetInlineLevelTopMargin() const override;
+  void SetIsSplitOnLeft(bool is_split_on_left);
+  void SetIsSplitOnRight(bool is_split_on_right);
 
   // From |ContainerBox|.
   bool TryAddChild(const scoped_refptr<Box>& child_box) override;
@@ -136,6 +143,11 @@
   // A font used for text width and line height calculations.
   const scoped_refptr<dom::FontList> used_font_;
 
+  bool is_split_on_left_;
+  bool is_split_on_right_;
+
+  BaseDirection base_direction_;
+
   // A reference to the next inline container box in a linked list of inline
   // container boxes produced from splits of the initial text box. This enables
   // HTMLElement to retain access to all of its layout boxes after they are
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index cd89a85..8ab844f 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -722,7 +722,7 @@
 
 // The specifications indicate that if positions are not specified for color
 // stops, then they should be filled in automatically by evenly spacing them
-// between the two neighbooring color stops that DO have positions specified.
+// between the two neighboring color stops that DO have positions specified.
 // This function implements this.  It assumes that unspecified position values
 // are indicated by a value of -1.0f.
 void InterpolateUnspecifiedColorStopPositions(
@@ -1706,8 +1706,7 @@
 //   https://www.w3.org/TR/css-flexbox-1/#flex-basis-property
 base::Optional<LayoutUnit> GetUsedFlexBasisIfNotContent(
     const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style,
-    bool main_direction_is_horizontal,
-    LayoutUnit main_space,
+    bool main_direction_is_horizontal, LayoutUnit main_space,
     bool* flex_basis_depends_on_available_space) {
   // Percentage values of flex-basis are resolved against the flex item's
   // containing block (i.e. its flex container).
@@ -1861,30 +1860,26 @@
 
 LayoutUnit GetUsedBorderLeft(
     const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
-  return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
-                        computed_style->border_left_width().get())
-                        ->value());
+  return LayoutUnit(
+      GetUsedNonNegativeLength(computed_style->border_left_width()));
 }
 
 LayoutUnit GetUsedBorderTop(
     const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
-  return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
-                        computed_style->border_top_width().get())
-                        ->value());
+  return LayoutUnit(
+      GetUsedNonNegativeLength(computed_style->border_top_width()));
 }
 
 LayoutUnit GetUsedBorderRight(
     const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
-  return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
-                        computed_style->border_right_width().get())
-                        ->value());
+  return LayoutUnit(
+      GetUsedNonNegativeLength(computed_style->border_right_width()));
 }
 
 LayoutUnit GetUsedBorderBottom(
     const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
-  return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
-                        computed_style->border_bottom_width().get())
-                        ->value());
+  return LayoutUnit(
+      GetUsedNonNegativeLength(computed_style->border_bottom_width()));
 }
 
 LayoutUnit GetUsedPaddingLeft(
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png
new file mode 100644
index 0000000..7e58ccb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
similarity index 96%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
index cd91b37..de17dbd 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
@@ -12,6 +12,7 @@
  body {
    margin: 0px;
    font-family: Roboto;
+   font-size: 16px;
  }
  span {
    padding-right: 12px;
@@ -24,4 +25,3 @@
  </div>
 </body>
 </html>
-
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png
new file mode 100644
index 0000000..3bcf7d0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html
new file mode 100644
index 0000000..317def4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<!--
+ | When an inline box is split, margins, borders, and padding have no visual
+ | effect where the split occurs (or at any split, when there are several).
+ |   https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+ -->
+<html>
+  <head>
+    <style>
+      .containing-block {
+        width: 15rem;
+        display: inline-block;
+      }
+      .text-block {
+        color: white;
+        background-color: blue;
+        margin-left: 0.5rem;
+        font-family: Roboto;
+        font-size: 16px;
+      }
+      </style>
+  </head>
+  <body>
+    <div class="containing-block">
+      <span class="text-block"> BADGE </span> <span class="text-block"> FOO BAR BAZ, 11:00 AM - 7:00 PM </span>
+    </div>
+  </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png
new file mode 100644
index 0000000..f62eddb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
similarity index 94%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
index 78fdaaf..0f30f53 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
@@ -14,6 +14,8 @@
       border: 5px solid green;
       margin: 20px;
       padding: 5px;
+      font-family: Roboto;
+      font-size: 16px;
     }
   </style>
 </head>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
index 7f7ba6b..3659681 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
@@ -182,6 +182,9 @@
 9-4-2-inline-boxes-should-split-at-br-elements
 9-4-2-long-inline-boxes-should-be-split
 9-4-2-margin-should-not-overflow-line-box
+9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs
+9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs
+9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2
 9-4-2-multiple-inline-boxes-exceeding-width-of-line-box-should-be-split
 9-4-2-nested-inline-boxes-should-split-at-br-elements
 9-4-2-splitting-of-boxes-can-affect-containing-box-of-children
diff --git a/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png b/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png
index 42ad0ea..87075bf 100644
--- a/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png
+++ b/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png
new file mode 100644
index 0000000..2cd7bd0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html
new file mode 100644
index 0000000..c257b7e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!--
+ | Unicode range test. Tests that font style takes precedence over a matching unicode-range.
+ | https://www.w3.org/TR/css3-fonts/#font-prop-desc
+ -->
+<html>
+<head>
+  <style>
+    @font-face {
+        font-family: 'TestFont';
+        src: local('Noto Serif');
+        unicode-range: U+4d-5a; /* =, M-Z */
+        font-weight: bold;
+    }
+    @font-face {
+        font-family: 'TestFont';
+        src: local('Roboto');
+        font-weight: normal;
+    }
+    @font-face {
+        font-family: 'TestFont2';
+        src: local('Dancing Script');
+        font-weight: bold;
+    }
+    body {
+      background-color: black;
+      font-size: 75px;
+      color: #fff;
+      font-weight: bold;
+      font-family: 'TestFont', 'TestFont2'; /*TestFont2 should be used. */
+    }
+  </style>
+</head>
+<body>
+    <div>Hello</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png
new file mode 100644
index 0000000..c32526d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html
new file mode 100644
index 0000000..f4f2cbd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!--
+ | Unicode range test. Tests multiple unicode-range values and multiple
+ | font-families with the same style but different unicode ranges and sources.
+ | Also tests that duplicate sources with differing ranges load correctly.
+ | https://www.w3.org/TR/css3-fonts/#font-prop-desc
+ -->
+<html>
+<head>
+  <style>
+    @font-face {
+        font-family: 'TestFontA';
+        src: local('Dancing Script');
+        unicode-range: U+4D; /* M */
+    }
+    @font-face {
+        font-family: 'TestFontB';
+        src: local('Dancing Script');
+        unicode-range: U+3d, U+4e-5a; /* =, N-Z */
+    }
+    @font-face {
+        font-family: 'TestFontB';
+        src: local('Noto Serif');
+        unicode-range: U+26; /* & */
+    }
+    body {
+      background-color: black;
+      font-size: 75px;
+      color: #fff;
+      font-family: 'TestFontA', 'TestFontB', 'Roboto';
+    }
+  </style>
+</head>
+<body>
+    <div>Me & You = Us</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
index aac78cf..8c922d4 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
@@ -4,6 +4,8 @@
 4-3-use-first-available-local-font-face
 4-3-use-next-font-family-if-font-face-sources-unavailable
 4-4-use-correct-style-in-font-face-set
+4-5-use-correct-font-with-unicode-range
+4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range
 5-2-use-correct-style-in-font-family
 5-2-use-first-available-listed-font-family
 5-2-use-numerical-font-weights-in-family-face-matching
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index 7b8dffd..ed0b959 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -238,13 +238,11 @@
 void DecodeError(const base::Optional<std::string>& error) {
   if (error) LOG(ERROR) << *error;
 }
-
 }  // namespace
 
 bool AnimatedWebPImage::DecodeOneFrame(int frame_index) {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeOneFrame()");
   TRACK_MEMORY_SCOPE("Rendering");
-  DCHECK(task_runner_->BelongsToCurrentThread());
   lock_.AssertAcquired();
 
   WebPIterator webp_iterator;
@@ -407,6 +405,32 @@
   return loop_count_ == 1 && current_frame_index_ == frame_count_;
 }
 
+scoped_refptr<render_tree::Image> AnimatedWebPImage::GetFrameForDebugging(
+    int target_frame) {
+  TRACE_EVENT0("cobalt::loader::image",
+               "AnimatedWebPImage::GetFrameForDebugging()");
+
+  base::AutoLock lock(lock_);
+  DCHECK(!should_dispose_previous_frame_to_background_ && !current_canvas_);
+  DCHECK(current_frame_index_ == 0 && !is_playing_);
+
+  if (target_frame <= 0 || target_frame > frame_count_) {
+    LOG(WARNING) << "Invalid frame index: " << target_frame;
+    return nullptr;
+  }
+
+  for (int frame = 1; frame <= target_frame; ++frame) {
+    DecodeOneFrame(frame);
+  }
+
+  // Reset states when GetFrameForDebugging finishes
+  should_dispose_previous_frame_to_background_ = false;
+  scoped_refptr<render_tree::Image> target_canvas = current_canvas_;
+  current_canvas_ = nullptr;
+
+  return target_canvas;
+}
+
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index c0fd222..135bf6a 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -63,6 +63,9 @@
 
   void AppendChunk(const uint8* data, size_t input_byte);
 
+  // Returns the render image of the frame for debugging
+  scoped_refptr<render_tree::Image> GetFrameForDebugging(int target_frame);
+
  private:
   ~AnimatedWebPImage() override;
 
diff --git a/src/cobalt/loader/image/image_decoder_mock.h b/src/cobalt/loader/image/image_decoder_mock.h
new file mode 100644
index 0000000..ad8ff3a
--- /dev/null
+++ b/src/cobalt/loader/image/image_decoder_mock.h
@@ -0,0 +1,119 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_LOADER_IMAGE_IMAGE_DECODER_MOCK_H_
+#define COBALT_LOADER_IMAGE_IMAGE_DECODER_MOCK_H_
+
+#include <memory>
+#include <string>
+
+#include "cobalt/loader/image/image_decoder.h"
+#include "cobalt/render_tree/resource_provider_stub.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+struct MockImageDecoderCallback {
+  void SuccessCallback(const scoped_refptr<Image>& value) { image = value; }
+
+  MOCK_METHOD1(LoadCompleteCallback,
+               void(const base::Optional<std::string>& message));
+
+  scoped_refptr<Image> image;
+};
+
+class MockImageDecoder : public Decoder {
+ public:
+  MockImageDecoder();
+  explicit MockImageDecoder(render_tree::ResourceProvider* resource_provider);
+  ~MockImageDecoder() override {}
+
+  LoadResponseType OnResponseStarted(
+      Fetcher* fetcher,
+      const scoped_refptr<net::HttpResponseHeaders>& headers) override;
+
+  void DecodeChunk(const char* data, size_t size) override;
+
+  void Finish() override;
+  bool Suspend() override;
+  void Resume(render_tree::ResourceProvider* resource_provider) override;
+
+  scoped_refptr<Image> image();
+
+  void ExpectCallWithError(const base::Optional<std::string>& error);
+
+ protected:
+  std::unique_ptr<render_tree::ResourceProviderStub> resource_provider_stub_;
+  render_tree::ResourceProvider* resource_provider_;
+  base::NullDebuggerHooks debugger_hooks_;
+  ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_;
+  std::unique_ptr<Decoder> image_decoder_;
+};
+
+MockImageDecoder::MockImageDecoder()
+    : resource_provider_stub_(new render_tree::ResourceProviderStub()),
+      resource_provider_(resource_provider_stub_.get()) {
+  image_decoder_.reset(new ImageDecoder(
+      resource_provider_, debugger_hooks_,
+      base::Bind(&MockImageDecoderCallback::SuccessCallback,
+                 base::Unretained(&image_decoder_callback_)),
+      base::Bind(&MockImageDecoderCallback::LoadCompleteCallback,
+                 base::Unretained(&image_decoder_callback_))));
+}
+
+MockImageDecoder::MockImageDecoder(
+    render_tree::ResourceProvider* resource_provider)
+    : resource_provider_(resource_provider) {
+  image_decoder_.reset(new ImageDecoder(
+      resource_provider_, debugger_hooks_,
+      base::Bind(&MockImageDecoderCallback::SuccessCallback,
+                 base::Unretained(&image_decoder_callback_)),
+      base::Bind(&MockImageDecoderCallback::LoadCompleteCallback,
+                 base::Unretained(&image_decoder_callback_))));
+}
+
+LoadResponseType MockImageDecoder::OnResponseStarted(
+    Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) {
+  return image_decoder_->OnResponseStarted(fetcher, headers);
+}
+
+void MockImageDecoder::DecodeChunk(const char* data, size_t size) {
+  image_decoder_->DecodeChunk(data, size);
+}
+
+void MockImageDecoder::Finish() { image_decoder_->Finish(); }
+
+bool MockImageDecoder::Suspend() { return image_decoder_->Suspend(); }
+
+void MockImageDecoder::Resume(
+    render_tree::ResourceProvider* resource_provider) {
+  image_decoder_->Resume(resource_provider);
+}
+
+scoped_refptr<Image> MockImageDecoder::image() {
+  return image_decoder_callback_.image;
+}
+
+void MockImageDecoder::ExpectCallWithError(
+    const base::Optional<std::string>& error) {
+  EXPECT_CALL(image_decoder_callback_, LoadCompleteCallback(error));
+}
+}  // namespace image
+}  // namespace loader
+};  // namespace cobalt
+
+#endif  // COBALT_LOADER_IMAGE_IMAGE_DECODER_MOCK_H_
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc
index 5aaf617..6ccf613 100644
--- a/src/cobalt/loader/image/image_decoder_test.cc
+++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/loader/image/image_decoder.h"
-
 #include <memory>
 #include <string>
 #include <vector>
@@ -27,89 +25,14 @@
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/loader/image/animated_webp_image.h"
+#include "cobalt/loader/image/image_decoder_mock.h"
 #include "cobalt/loader/image/jpeg_image_decoder.h"
-#include "cobalt/render_tree/resource_provider_stub.h"
 #include "starboard/configuration.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace loader {
 namespace image {
-
 namespace {
-
-struct MockImageDecoderCallback {
-  void SuccessCallback(const scoped_refptr<Image>& value) { image = value; }
-
-  MOCK_METHOD1(LoadCompleteCallback,
-               void(const base::Optional<std::string>& message));
-
-  scoped_refptr<Image> image;
-};
-
-class MockImageDecoder : public Decoder {
- public:
-  MockImageDecoder();
-  ~MockImageDecoder() override {}
-
-  LoadResponseType OnResponseStarted(
-      Fetcher* fetcher,
-      const scoped_refptr<net::HttpResponseHeaders>& headers) override;
-
-  void DecodeChunk(const char* data, size_t size) override;
-
-  void Finish() override;
-  bool Suspend() override;
-  void Resume(render_tree::ResourceProvider* resource_provider) override;
-
-  scoped_refptr<Image> image();
-
-  void ExpectCallWithError(const base::Optional<std::string>& error);
-
- protected:
-  render_tree::ResourceProviderStub resource_provider_;
-  base::NullDebuggerHooks debugger_hooks_;
-  ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_;
-  std::unique_ptr<Decoder> image_decoder_;
-};
-
-MockImageDecoder::MockImageDecoder() {
-  image_decoder_.reset(new ImageDecoder(
-      &resource_provider_, debugger_hooks_,
-      base::Bind(&MockImageDecoderCallback::SuccessCallback,
-                 base::Unretained(&image_decoder_callback_)),
-      base::Bind(&MockImageDecoderCallback::LoadCompleteCallback,
-                 base::Unretained(&image_decoder_callback_))));
-}
-
-LoadResponseType MockImageDecoder::OnResponseStarted(
-    Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) {
-  return image_decoder_->OnResponseStarted(fetcher, headers);
-}
-
-void MockImageDecoder::DecodeChunk(const char* data, size_t size) {
-  image_decoder_->DecodeChunk(data, size);
-}
-
-void MockImageDecoder::Finish() { image_decoder_->Finish(); }
-
-bool MockImageDecoder::Suspend() { return image_decoder_->Suspend(); }
-
-void MockImageDecoder::Resume(
-    render_tree::ResourceProvider* resource_provider) {
-  image_decoder_->Resume(resource_provider);
-}
-
-scoped_refptr<Image> MockImageDecoder::image() {
-  return image_decoder_callback_.image;
-}
-
-void MockImageDecoder::ExpectCallWithError(
-    const base::Optional<std::string>& error) {
-  EXPECT_CALL(image_decoder_callback_, LoadCompleteCallback(error));
-}
-
 base::FilePath GetTestImagePath(const char* file_name) {
   base::FilePath data_directory;
   CHECK(base::PathService::Get(base::DIR_TEST_DATA, &data_directory));
@@ -226,7 +149,6 @@
 
   return CheckSameColor(pixels, size.width(), size.height(), test_color);
 }
-
 }  // namespace
 
 // TODO: Test special images like the image has gAMA chunk information,
@@ -846,7 +768,6 @@
   EXPECT_EQ(math::Size(480, 270), animated_webp_image->GetSize());
   EXPECT_TRUE(animated_webp_image->IsOpaque());
 }
-
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.cc b/src/cobalt/loader/image/jpeg_image_decoder.cc
index feff4f7..5516e4a 100644
--- a/src/cobalt/loader/image/jpeg_image_decoder.cc
+++ b/src/cobalt/loader/image/jpeg_image_decoder.cc
@@ -15,12 +15,13 @@
 #include "cobalt/loader/image/jpeg_image_decoder.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/console_log.h"
 #include "nb/memory_scope.h"
-#include "third_party/libjpeg/jpegint.h"
+#include "third_party/libjpeg-turbo/jpegint.h"
 
 namespace cobalt {
 namespace loader {
@@ -167,7 +168,7 @@
   jmp_buf jump_buffer;
   info_.client_data = &jump_buffer;
 
-  // int setjmp(jmp_buf env) saves the current environment (ths program state),
+  // int setjmp(jmp_buf env) saves the current environment (this program state),
   // at some point of program execution, into a platform-specific data
   // structure (jmp_buf) that can be used at some later point of program
   // execution by longjmp to restore the program state to that saved by setjmp
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.h b/src/cobalt/loader/image/jpeg_image_decoder.h
index afe61f5..aa08822 100644
--- a/src/cobalt/loader/image/jpeg_image_decoder.h
+++ b/src/cobalt/loader/image/jpeg_image_decoder.h
@@ -23,10 +23,11 @@
 #include "base/callback.h"
 #include "cobalt/loader/image/image.h"
 #include "cobalt/loader/image/image_data_decoder.h"
+#include "starboard/file.h"
 
 // Inhibit C++ name-mangling for libjpeg functions.
 extern "C" {
-#include "third_party/libjpeg/jpeglib.h"
+#include "third_party/libjpeg-turbo/jpeglib.h"
 }
 
 namespace cobalt {
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index b4f2551..295c773 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -111,7 +111,7 @@
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
         '<(DEPTH)/cobalt/renderer/test/png_utils/png_utils.gyp:png_utils',
         '<(DEPTH)/url/url.gyp:url',
-        '<(DEPTH)/third_party/libjpeg/libjpeg.gyp:libjpeg',
+        '<(DEPTH)/third_party/libjpeg-turbo/libjpeg.gyp:libjpeg',
         '<(DEPTH)/third_party/libpng/libpng.gyp:libpng',
         '<(DEPTH)/third_party/libwebp/libwebp.gyp:libwebp',
         'embed_resources_as_header_files_loader',
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index baebdee..5a9b541 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -327,8 +327,8 @@
         get_decode_target_graphics_context_provider_func,
     bool allow_resume_after_suspend, MediaLog* media_log,
     VideoFrameProvider* video_frame_provider)
-    : pipeline_identifier_(base::StringPrintf("%X",
-                           g_pipeline_identifier_counter++)),
+    : pipeline_identifier_(
+          base::StringPrintf("%X", g_pipeline_identifier_counter++)),
       task_runner_(task_runner),
       allow_resume_after_suspend_(allow_resume_after_suspend),
       window_(window),
@@ -535,9 +535,9 @@
       player = std::move(player_);
     }
 
-    DLOG(INFO) << "Destroying SbPlayer.";
+    LOG(INFO) << "Destroying SbPlayer.";
     player.reset();
-    DLOG(INFO) << "SbPlayer destroyed.";
+    LOG(INFO) << "SbPlayer destroyed.";
   }
 
   // When Stop() is in progress, we no longer need to call |error_cb_|.
@@ -907,7 +907,7 @@
 
   {
     base::AutoLock auto_lock(lock_);
-    DLOG(INFO) << "StarboardPlayer created with url: " << source_url;
+    LOG(INFO) << "Creating StarboardPlayer with url: " << source_url;
     player_.reset(new StarboardPlayer(
         task_runner_, source_url, window_, this, set_bounds_helper_.get(),
         allow_resume_after_suspend_, *decode_to_texture_output_mode_,
@@ -917,6 +917,7 @@
       SetVolumeTask(volume_);
     } else {
       player_.reset();
+      LOG(INFO) << "Failed to create a valid StarboardPlayer.";
     }
   }
 
@@ -941,8 +942,7 @@
 
   base::AutoLock auto_lock(lock_);
   if (!player_) {
-    DLOG(INFO)
-        << "Player not set before calling SbPlayerPipeline::SetDrmSystem";
+    LOG(INFO) << "Player not set before calling SbPlayerPipeline::SetDrmSystem";
     return;
   }
 
@@ -997,6 +997,7 @@
     // available, reset the existing player first to reduce the number of active
     // players.
     player_.reset();
+    LOG(INFO) << "Creating StarboardPlayer.";
     player_.reset(new StarboardPlayer(
         task_runner_, get_decode_target_graphics_context_provider_func_,
         audio_config, video_config, window_, drm_system, this,
@@ -1008,6 +1009,7 @@
       SetVolumeTask(volume_);
     } else {
       player_.reset();
+      LOG(INFO) << "Failed to create a valid StarboardPlayer.";
     }
   }
 
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 3f34f40..7d19612 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -517,7 +517,7 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
 
   DCHECK(!on_encrypted_media_init_data_encountered_cb_.is_null());
-  DLOG(INFO) << "CreateUrlPlayer passed url " << url;
+  LOG(INFO) << "CreateUrlPlayer passed url " << url;
   player_ =
       SbUrlPlayerCreate(url.c_str(), window_, &StarboardPlayer::PlayerStatusCB,
                         &StarboardPlayer::EncryptedMediaInitDataEncounteredCB,
@@ -778,10 +778,10 @@
       break;
 #if SB_API_VERSION < 12
     case kSbPlayerDecoderStateBufferFull:
-      DLOG(WARNING) << "kSbPlayerDecoderStateBufferFull has been deprecated.";
+      LOG(WARNING) << "kSbPlayerDecoderStateBufferFull has been deprecated.";
       return;
     case kSbPlayerDecoderStateDestroyed:
-      DLOG(WARNING) << "kSbPlayerDecoderStateDestroyed has been deprecated.";
+      LOG(WARNING) << "kSbPlayerDecoderStateDestroyed has been deprecated.";
       return;
 #endif  // SB_API_VERSION < 12
   }
diff --git a/src/cobalt/media/formats/mp4/box_definitions.cc b/src/cobalt/media/formats/mp4/box_definitions.cc
index 0fc6975..631b713 100644
--- a/src/cobalt/media/formats/mp4/box_definitions.cc
+++ b/src/cobalt/media/formats/mp4/box_definitions.cc
@@ -1304,7 +1304,8 @@
          reader->ReadChild(&decode_time) && reader->MaybeReadChildren(&runs) &&
          reader->MaybeReadChild(&auxiliary_offset) &&
          reader->MaybeReadChild(&auxiliary_size) &&
-         reader->MaybeReadChild(&sdtp));
+         reader->MaybeReadChild(&sdtp) &&
+         reader->MaybeReadChild(&sample_encryption));
 
   // There could be multiple SampleGroupDescription and SampleToGroup boxes with
   // different grouping types. For common encryption, the relevant grouping type
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 050af74..f53cf91 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -33,9 +33,6 @@
 
 namespace {
 
-// Used to ensure that there is no more than one instance of WebMediaPlayerImpl.
-WebMediaPlayerImpl* s_instance;
-
 // Limits the range of playback rate.
 //
 // TODO(kylep): Revisit these.
@@ -137,10 +134,6 @@
 
   video_frame_provider_ = new VideoFrameProvider();
 
-  DLOG_IF(ERROR, s_instance)
-      << "More than one WebMediaPlayerImpl has been created.";
-  s_instance = this;
-
   DCHECK(buffer_allocator_);
   media_log_->AddEvent(
       media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
@@ -166,10 +159,6 @@
 
   ON_INSTANCE_RELEASED(WebMediaPlayerImpl);
 
-  DLOG_IF(ERROR, s_instance != this)
-      << "More than one WebMediaPlayerImpl has been created.";
-  s_instance = NULL;
-
   if (delegate_) {
     delegate_->UnregisterPlayer(this);
   }
@@ -229,7 +218,7 @@
   DCHECK_EQ(main_loop_, base::MessageLoop::current());
 
   UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
-  DLOG(INFO) << "Start URL playback";
+  LOG(INFO) << "Start URL playback";
 
   // Handle any volume changes that occurred before load().
   SetVolume(GetClient()->Volume());
@@ -249,7 +238,7 @@
   TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::LoadMediaSource");
   DCHECK_EQ(main_loop_, base::MessageLoop::current());
 
-  DLOG(INFO) << "Start MEDIASOURCE playback";
+  LOG(INFO) << "Start MEDIASOURCE playback";
 
   // Handle any volume changes that occurred before load().
   SetVolume(GetClient()->Volume());
@@ -276,7 +265,7 @@
   DCHECK_EQ(main_loop_, base::MessageLoop::current());
 
   UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
-  DLOG(INFO) << "Start PROGRESSIVE playback";
+  LOG(INFO) << "Start PROGRESSIVE playback";
 
   // Handle any volume changes that occurred before load().
   SetVolume(GetClient()->Volume());
@@ -898,11 +887,11 @@
   // Note: stopping the pipeline might block for a long time.
   base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                              base::WaitableEvent::InitialState::NOT_SIGNALED);
-  DLOG(INFO) << "Trying to stop media pipeline.";
+  LOG(INFO) << "Trying to stop media pipeline.";
   pipeline_->Stop(
       base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
   waiter.Wait();
-  DLOG(INFO) << "Media pipeline stopped.";
+  LOG(INFO) << "Media pipeline stopped.";
 
   // And then detach the proxy, it may live on the render thread for a little
   // longer until all the tasks are finished.
diff --git a/src/cobalt/media_integration_tests/functionality/general_playback.py b/src/cobalt/media_integration_tests/functionality/general_playback.py
index d90eca4..6edc0ca 100644
--- a/src/cobalt/media_integration_tests/functionality/general_playback.py
+++ b/src/cobalt/media_integration_tests/functionality/general_playback.py
@@ -38,8 +38,10 @@
       # Let the playback play for 10 seconds.
       app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 10)
 
+
 TEST_PARAMETERS = [
     ('H264', PlaybackUrls.H264_ONLY, None),
+    ('PROGRESSIVE', PlaybackUrls.PROGRESSIVE, None),
     ('ENCRYPTED', PlaybackUrls.ENCRYPTED, None),
     ('VR', PlaybackUrls.VR, None),
     ('VP9', PlaybackUrls.VP9, MimeStrings.VP9),
diff --git a/src/cobalt/media_integration_tests/functionality/suspend_resume.py b/src/cobalt/media_integration_tests/functionality/suspend_resume.py
index cc26c33..5b7de5e 100644
--- a/src/cobalt/media_integration_tests/functionality/suspend_resume.py
+++ b/src/cobalt/media_integration_tests/functionality/suspend_resume.py
@@ -54,8 +54,10 @@
       # Let the playback play for 2 seconds.
       app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
 
+
 TEST_PARAMETERS = [
     ('H264', PlaybackUrls.H264_ONLY, None),
+    ('PROGRESSIVE', PlaybackUrls.PROGRESSIVE, None),
     ('ENCRYPTED', PlaybackUrls.ENCRYPTED, None),
     ('LIVE', PlaybackUrls.LIVE, None),
     ('VP9', PlaybackUrls.VP9, MimeStrings.VP9),
diff --git a/src/cobalt/media_integration_tests/test_app.py b/src/cobalt/media_integration_tests/test_app.py
index cea1406..8503dd5 100644
--- a/src/cobalt/media_integration_tests/test_app.py
+++ b/src/cobalt/media_integration_tests/test_app.py
@@ -13,7 +13,6 @@
 # limitations under the License.
 """A module to support fundamental communications with Cobalt application."""
 
-import enum
 import json
 import logging
 import threading
@@ -90,7 +89,7 @@
   MEDIA_FAST_FORWARD = u'\uf005'
 
 
-class Features(enum.Enum):
+class Features():
   """Set of platform features."""
   SUSPEND_AND_RESUME = 1
   SEND_KEYS = 2
@@ -153,7 +152,7 @@
       self._NotifyHandlersOnStateChanged()
 
 
-class PipelinePlayerState(enum.IntEnum):
+class PipelinePlayerState(int):
   """Set of pipeline states, equals to SbPlayerState."""
   INITIALIZED = 0  # kSbPlayerStateInitialized
   PREROLLING = 1  # kSbPlayerStatePrerolling
@@ -298,16 +297,22 @@
             not self.is_ended and not self.is_suspended and not self.is_stopped)
 
 
-class MediaSessionPlaybackState(enum.Enum):
+class MediaSessionPlaybackState(int):
   """Set of media session playback states."""
   NONE = 0
   PAUSED = 1
   PLAYING = 2
 
-  # Aliases to convert string to the enum.
-  none = 0
-  paused = 1
-  playing = 2
+  @staticmethod
+  def FromString(str):
+    if str == 'none':
+      return 0
+    if str == 'paused':
+      return 1
+    if str == 'playing':
+      return 2
+    raise NotImplementedError(
+        '"%s" is not a valid media session playback state.' % str)
 
 
 class MediaSessionState():
@@ -327,8 +332,8 @@
     self.title = GetValueFromQueryResult(metadata, 'title', '')
     self.artist = GetValueFromQueryResult(metadata, 'artist', '')
     self.album = GetValueFromQueryResult(metadata, 'album', '')
-    self.playback_state = MediaSessionPlaybackState[GetValueFromQueryResult(
-        query_result, 'playbackState', 'none')]
+    self.playback_state = MediaSessionPlaybackState.FromString(
+        GetValueFromQueryResult(query_result, 'playbackState', 'none'))
 
   def __eq__(self, other):
     if not isinstance(other, self.__class__):
@@ -478,7 +483,7 @@
       self._NotifyHandlersOnStateChanged()
 
 
-class AdsState(enum.IntEnum):
+class AdsState(int):
   """Set of ads states. The numeric values are used in ads state query."""
   NONE = 0
   PLAYING = 1
diff --git a/src/cobalt/media_integration_tests/test_util.py b/src/cobalt/media_integration_tests/test_util.py
index c2153327..6bd08d9 100644
--- a/src/cobalt/media_integration_tests/test_util.py
+++ b/src/cobalt/media_integration_tests/test_util.py
@@ -62,6 +62,7 @@
 
   DEFAULT = 'https://www.youtube.com/tv'
   H264_ONLY = 'https://www.youtube.com/tv#/watch?v=RACW52qnJMI'
+  PROGRESSIVE = 'https://www.youtube.com/tv#/watch?v=w5tbgPJxyGA'
   ENCRYPTED = 'https://www.youtube.com/tv#/watch?v=iNvUS1dnwfw'
 
   VP9 = 'https://www.youtube.com/tv#/watch?v=x7GkebUe6XQ'
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
index 99097ef..7dd8853 100644
--- a/src/cobalt/media_session/media_session_client.cc
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -175,6 +175,15 @@
   return result;
 }
 
+void MediaSessionClient::PostDelayedTaskForMaybeFreezeCallback() {
+  if (is_active()) return;
+
+  media_session_->task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&MediaSessionClient::RunMaybeFreezeCallback,
+                            base::Unretained(this), ++sequence_number_),
+                            kMaybeFreezeDelay);
+}
+
 void MediaSessionClient::UpdatePlatformPlaybackState(
     CobaltExtensionMediaSessionPlaybackState state) {
   DCHECK(media_session_->task_runner_);
@@ -190,12 +199,7 @@
     UpdateMediaSessionState();
   }
 
-  if (!is_active()) {
-    media_session_->task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(&MediaSessionClient::RunMaybeFreezeCallback,
-                              base::Unretained(this), ++sequence_number_),
-                              kMaybeFreezeDelay);
-  }
+  PostDelayedTaskForMaybeFreezeCallback();
 }
 
 void MediaSessionClient::RunMaybeFreezeCallback(int sequence_number) {
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
index 115b852..35a9a31 100644
--- a/src/cobalt/media_session/media_session_client.h
+++ b/src/cobalt/media_session/media_session_client.h
@@ -95,9 +95,8 @@
     media_session_ = media_session;
   }
 
-  // If the media session is not active, then run MaybeFreezeCallback to
-  // suspend the App.
-  void RunMaybeFreezeCallback(int sequence_number);
+  // Post a delayed task for running MaybeFreeze callback.
+  void PostDelayedTaskForMaybeFreezeCallback();
 
  private:
   THREAD_CHECKER(thread_checker_);
@@ -135,6 +134,10 @@
   MediaSessionAction ConvertMediaSessionAction(
       CobaltExtensionMediaSessionAction action);
 
+  // If the media session is not active, then run MaybeFreezeCallback to
+  // suspend the App.
+  void RunMaybeFreezeCallback(int sequence_number);
+
   base::Closure maybe_freeze_callback_;
 
   // This is for checking the sequence number of PostDelayedTask. It should be
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
index 1380682..1be401c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
@@ -14,7 +14,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl
index 8ca842b..02f89e1 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl
@@ -14,7 +14,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
index 63ff1ed..11db997 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
@@ -19,7 +19,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
index 9c5c1f3..e7447a2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
@@ -14,7 +14,7 @@
 varying mediump float vinCoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl
index 3d40e8a..1ee4967 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl
@@ -14,7 +14,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
index cbcba0c..7bf9ef3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
@@ -19,7 +19,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
index 083f5e4..e6a6613 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
@@ -14,7 +14,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
index e737a75..a5049c7 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
@@ -21,7 +21,7 @@
 varying mediump float vinCoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
index d3a03a4..e338bcf 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
@@ -18,7 +18,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
index 5f2b1d3..667a564 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
@@ -15,7 +15,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
index 084c066..35a3010 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
@@ -15,7 +15,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
index cb27f80..1f47fae 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
@@ -15,7 +15,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
index 1ba24f6..b7223a3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
@@ -18,7 +18,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
index da56a36..ba5cadd 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
@@ -20,7 +20,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
index e2ec055..8bcc27a 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
@@ -16,7 +16,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
index fe3a8ae..2ac7fcc 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
@@ -16,7 +16,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
index f1bbe9d..1cb8137 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
@@ -16,7 +16,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
index b073544..ba8e732 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
@@ -19,7 +19,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl
index 987a9fd..8ea773f 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl
@@ -15,7 +15,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
index 3657419..2cec90c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
@@ -13,7 +13,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl
index a80e7d6..8335e68 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl
@@ -13,7 +13,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
index 6e1c100..7685597 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
@@ -18,7 +18,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
index e055c58..a1f1060 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
@@ -18,7 +18,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl
index be9d988..ecc4cb4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl
@@ -18,7 +18,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl
index cb3bcb1..71ce693 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl
@@ -22,7 +22,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl
index 68b87cd..4fb093a 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl
@@ -18,7 +18,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl
index 36f18a2..bffd97d 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl
@@ -22,7 +22,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl
index a76b32a..9324401 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl
@@ -18,7 +18,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl
index f5c5d14..39b4e76 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl
@@ -16,7 +16,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl
index 58cdb65..0e27646 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl
@@ -22,7 +22,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl
index 62db91a..482238d 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl
@@ -25,7 +25,7 @@
 }
 mediump vec4 stage_Stage1_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c1_c0_c0_c0;
-    mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
     _sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c1_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl
index 77855e7..563d103 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl
@@ -25,7 +25,7 @@
 }
 mediump vec4 stage_Stage1_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c1_c0_c0_c0;
-    mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
     _sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c1_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl
index 52553ba..37c926c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl
@@ -18,7 +18,7 @@
 }
 mediump vec4 stage_Stage2_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c1_c0_c0_c0;
-    mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
     _sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c1_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl
index 6522fd0..aa9cd37 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl
@@ -11,7 +11,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c0_c0_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample453_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c0_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl
index a35702c..5eedd12 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl
@@ -21,7 +21,7 @@
 }
 mediump vec4 stage_Stage2_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c1_c0_c0_c0;
-    mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
     _sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c1_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl
index 28eab3a..ddacd34 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl
@@ -23,7 +23,7 @@
 }
 mediump vec4 stage_Stage2_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c1_c0_c0_c0;
-    mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
     _sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c1_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl
index e435cc9..8dd97f4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl
@@ -14,7 +14,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c0_c0_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample453_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c0_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl
index 3dcf693..d0e6ce0 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl
@@ -10,7 +10,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl
index aa5970f..6adacd9 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl
@@ -11,7 +11,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
index 224674d..07a5ac3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
@@ -10,7 +10,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl
index 572a984..70771b9 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl
@@ -51,7 +51,7 @@
         highp float test = dot(offset, offset) - 1.0;
         highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
         highp float grad_dot = dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
         outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
index 1ea6bd8..3c26f70 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
@@ -11,7 +11,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
index d8c1e31..298ccc0 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
@@ -11,7 +11,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
index 4e004af..92cba89 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
@@ -12,7 +12,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl
index 4b56cd0..6f7f457 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl
@@ -9,7 +9,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl
index fa2dffc..edebf92 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl
@@ -9,7 +9,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl
index 84896ca..78b3466 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl
@@ -12,7 +12,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl
index 98af413..b1749e3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl
@@ -21,14 +21,14 @@
         highp float test = dot(offset, offset) - 1.0;
         highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
         highp float grad_dot = dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
         offset = vEllipseOffsets_Stage0.xy * vEllipseRadii_Stage0.zw;
         test = dot(offset, offset) - 1.0;
         grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.zw);
         grad_dot = dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
         edgeAlpha *= clamp(0.5 + test * invlen, 0.0, 1.0);
         outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
index e7cd090..fda3d24 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
@@ -21,9 +21,9 @@
         {
             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
         }
-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
         mediump float afwidth;
-        afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));
+        afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));
         mediump float val = smoothstep(-afwidth, afwidth, distance);
         outputCoverage_Stage0 = vec4(val);
     }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
index ee26f8b..8b55db4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
@@ -14,7 +14,7 @@
         highp float test = dot(offset, offset) - 1.0;
         highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
         highp float grad_dot = dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
         outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
index 0cb3d54..dd2801e 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
@@ -18,7 +18,7 @@
         highp vec2 grad = vec2(vEllipseOffsets0_Stage0.x * duvdx.x + vEllipseOffsets0_Stage0.y * duvdx.y, vEllipseOffsets0_Stage0.x * duvdy.x + vEllipseOffsets0_Stage0.y * duvdy.y);
         grad *= vEllipseOffsets0_Stage0.z;
         highp float grad_dot = 4.0 * dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = inversesqrt(grad_dot);
         invlen *= vEllipseOffsets0_Stage0.z;
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
@@ -29,7 +29,7 @@
         grad = vec2(vEllipseOffsets1_Stage0.x * duvdx.x + vEllipseOffsets1_Stage0.y * duvdx.y, vEllipseOffsets1_Stage0.x * duvdy.x + vEllipseOffsets1_Stage0.y * duvdy.y);
         grad *= vEllipseOffsets0_Stage0.z;
         grad_dot = 4.0 * dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         invlen = inversesqrt(grad_dot);
         invlen *= vEllipseOffsets0_Stage0.z;
         edgeAlpha *= clamp(0.5 + test * invlen, 0.0, 1.0);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
index 6148054..ff7654e 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
@@ -18,7 +18,7 @@
         highp vec2 grad = vec2(vEllipseOffsets0_Stage0.x * duvdx.x + vEllipseOffsets0_Stage0.y * duvdx.y, vEllipseOffsets0_Stage0.x * duvdy.x + vEllipseOffsets0_Stage0.y * duvdy.y);
         grad *= vEllipseOffsets0_Stage0.z;
         highp float grad_dot = 4.0 * dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = inversesqrt(grad_dot);
         invlen *= vEllipseOffsets0_Stage0.z;
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
index 0b4a811..4676e51 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
@@ -27,7 +27,7 @@
         highp float implicit = dot(Z, d) - 1.0;

         highp float grad_dot = 4.0 * dot(Z, Z);

         {

-            grad_dot = max(grad_dot, 6.1036000261083245e-05);

+            grad_dot = max(grad_dot, 6.103600026108325e-05);

         }

         highp float approx_dist = implicit * inversesqrt(grad_dot);

         {

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
index 53a0a74..32a6af2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
@@ -18,9 +18,9 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

-        afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));

+        afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
index 1b61907..7c4db08 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
@@ -18,9 +18,9 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

-        afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));

+        afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
index f4db076..421494c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
@@ -18,19 +18,19 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

         mediump vec2 dist_grad = vec2(dFdx(distance), -dFdy(distance));

         mediump float dg_len2 = dot(dist_grad, dist_grad);

-        if (dg_len2 < 9.9999997473787516e-05) {

-            dist_grad = vec2(0.70709997415542603, 0.70709997415542603);

+        if (dg_len2 < 9.999999747378752e-05) {

+            dist_grad = vec2(0.707099974155426, 0.707099974155426);

         } else {

             dist_grad = dist_grad * inversesqrt(dg_len2);

         }

         mediump vec2 Jdx = dFdx(vIntTextureCoords_Stage0);

         mediump vec2 Jdy = -dFdy(vIntTextureCoords_Stage0);

         mediump vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x, dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);

-        afwidth = 0.64999997615814209 * length(grad);

+        afwidth = 0.6499999761581421 * length(grad);

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
index 20eef11..663553b 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
@@ -18,10 +18,10 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

         mediump float st_grad_len = length(-dFdy(vIntTextureCoords_Stage0));

-        afwidth = abs(0.64999997615814209 * st_grad_len);

+        afwidth = abs(0.6499999761581421 * st_grad_len);

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
index ac92c03..80cd4a3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
@@ -18,10 +18,10 @@
         {
             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
         }
-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
         mediump float afwidth;
         mediump float st_grad_len = length(-dFdy(vIntTextureCoords_Stage0));
-        afwidth = abs(0.64999997615814209 * st_grad_len);
+        afwidth = abs(0.6499999761581421 * st_grad_len);
         mediump float val = smoothstep(-afwidth, afwidth, distance);
         outputCoverage_Stage0 = vec4(val);
     }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl
index 85b6c4e..165aeed 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl
@@ -18,19 +18,19 @@
         {
             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
         }
-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
         mediump float afwidth;
         mediump vec2 dist_grad = vec2(dFdx(distance), -dFdy(distance));
         mediump float dg_len2 = dot(dist_grad, dist_grad);
-        if (dg_len2 < 9.9999997473787516e-05) {
-            dist_grad = vec2(0.70709997415542603, 0.70709997415542603);
+        if (dg_len2 < 9.999999747378752e-05) {
+            dist_grad = vec2(0.707099974155426, 0.707099974155426);
         } else {
             dist_grad = dist_grad * inversesqrt(dg_len2);
         }
         mediump vec2 Jdx = dFdx(vIntTextureCoords_Stage0);
         mediump vec2 Jdy = -dFdy(vIntTextureCoords_Stage0);
         mediump vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x, dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);
-        afwidth = 0.64999997615814209 * length(grad);
+        afwidth = 0.6499999761581421 * length(grad);
         mediump float val = smoothstep(-afwidth, afwidth, distance);
         outputCoverage_Stage0 = vec4(val);
     }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl
index 07ec93a..525fe12 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl
@@ -19,7 +19,7 @@
     }
     mediump vec4 output_Stage2;
     {
-        mediump float luma = clamp(dot(vec3(0.2125999927520752, 0.71520000696182251, 0.072200000286102295), output_Stage1.xyz), 0.0, 1.0);
+        mediump float luma = clamp(dot(vec3(0.2125999927520752, 0.7152000069618225, 0.0722000002861023), output_Stage1.xyz), 0.0, 1.0);
         output_Stage2 = vec4(0.0, 0.0, 0.0, luma);
     }
     {
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl
index bc07de6..e8d7417 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl
@@ -18,7 +18,7 @@
     mediump vec4 out0_c1_c0;
     mediump vec4 inputColor = _input;
     {
-        mediump float nonZeroAlpha = max(inputColor.w, 9.9999997473787516e-05);
+        mediump float nonZeroAlpha = max(inputColor.w, 9.999999747378752e-05);
         inputColor = vec4(inputColor.xyz / nonZeroAlpha, nonZeroAlpha);
     }
     out0_c1_c0 = um_Stage2_c1_c0_c0_c0 * inputColor + uv_Stage2_c1_c0_c0_c0;
@@ -32,7 +32,7 @@
 }
 mediump vec4 stage_Stage2_c1_c0_c1_c0(mediump vec4 _input) {
     mediump vec4 _sample1278;
-    mediump float nonZeroAlpha = max(_input.w, 9.9999997473787516e-05);
+    mediump float nonZeroAlpha = max(_input.w, 9.999999747378752e-05);
     mediump vec4 coord = vec4(_input.xyz / nonZeroAlpha, nonZeroAlpha);
     coord = coord * 0.9960939884185791 + vec4(0.0019529999699443579, 0.0019529999699443579, 0.0019529999699443579, 0.0019529999699443579);
     _sample1278.w = texture2D(uTextureSampler_0_Stage2, vec2(coord.w, 0.125)).w;
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl
index a29ab60..1db4bf2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl
@@ -7,7 +7,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample453_c0_c0_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample453_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample453_c0_c0_c0_c0;
 }
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
index 7a4a543..eaf9838 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
@@ -28,12 +28,17 @@
 
 ShaderProgramManager::ShaderProgramManager() {
   // These are shaders that get instantiated during video playback when the
-  // users starts interacting with the transport controls. They are preloaded
+  // user starts interacting with the transport controls. They are preloaded
   // to prevent UI-hiccups.
   // These shaders are generated from egl/generated_shader_impl.h
   Preload<ShaderVertexOffsetRcorner, ShaderFragmentColorBlurRrects>();
   Preload<ShaderVertexColorOffset, ShaderFragmentColorInclude>();
   Preload<ShaderVertexRcornerTexcoord, ShaderFragmentRcornerTexcoordColor>();
+  Preload<ShaderVertexRcornerTexcoord,
+          ShaderFragmentRcornerTexcoordColorYuv3>();
+  Preload<ShaderVertexRcorner, ShaderFragmentRcornerColor>();
+  Preload<ShaderVertexColorTexcoord, ShaderFragmentColorTexcoord>();
+  Preload<ShaderVertexColorTexcoord, ShaderFragmentColorTexcoordYuv3>();
 }
 
 ShaderProgramManager::~ShaderProgramManager() {
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 6ee0ccb..8bc8886 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -19,6 +19,8 @@
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
+#include "cobalt/loader/image/animated_webp_image.h"
+#include "cobalt/loader/image/image_decoder_mock.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/math/size_f.h"
@@ -68,6 +70,9 @@
 #endif
 #endif
 
+using cobalt::loader::image::AnimatedWebPImage;
+using cobalt::loader::image::MockImageDecoder;
+using cobalt::loader::image::MockImageDecoderCallback;
 using cobalt::math::Matrix3F;
 using cobalt::math::PointF;
 using cobalt::math::RectF;
@@ -4566,6 +4571,38 @@
           TranslateMatrix(output_surface_size().width() * -0.5f, 0.0f)));
 }
 
+TEST_F(PixelTest, DebugAnimatedWebPFrame) {
+  MockImageDecoder image_decoder(GetResourceProvider());
+  image_decoder.ExpectCallWithError(base::nullopt);
+
+  std::vector<uint8> image_data =
+      GetFileData(GetTestFilePath("loading-spinner-opaque.webp"));
+  image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]),
+                            image_data.size());
+  image_decoder.Finish();
+
+  scoped_refptr<AnimatedWebPImage> animated_webp_image =
+      base::polymorphic_downcast<AnimatedWebPImage*>(
+          image_decoder.image().get());
+
+  scoped_refptr<Image> frame_image =
+      animated_webp_image->GetFrameForDebugging(20);
+
+  scoped_refptr<ImageNode> frame_image_node = new ImageNode(frame_image);
+
+  CompositionNode::Builder builder;
+
+  // Test opaque background animated webp image on top of an opaque canvas
+  // webp image's background should be ignored and only shows underlying
+  // canvas's color.
+  builder.AddChild(new ClearRectNode(math::RectF(output_surface_size()),
+                                     ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
+  builder.AddChild(frame_image_node);
+
+  scoped_refptr<Node> root = new CompositionNode(builder);
+  TestTree(root);
+}
+
 #endif  // !SB_HAS(BLITTER)
 
 }  // namespace rasterizer
diff --git a/src/cobalt/renderer/rasterizer/testdata/DebugAnimatedWebPFrame-expected.png b/src/cobalt/renderer/rasterizer/testdata/DebugAnimatedWebPFrame-expected.png
new file mode 100644
index 0000000..c7ba0fe
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/DebugAnimatedWebPFrame-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/loading-spinner-opaque.webp b/src/cobalt/renderer/rasterizer/testdata/loading-spinner-opaque.webp
new file mode 100644
index 0000000..6cee3dd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/loading-spinner-opaque.webp
Binary files differ
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index 5904624..f63ce0b 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -102,6 +102,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/loader/loader.gyp:loader',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
         'render_tree_pixel_tester',
diff --git a/src/cobalt/script/engine.gyp b/src/cobalt/script/engine.gyp
index 6f84340..9360caa 100644
--- a/src/cobalt/script/engine.gyp
+++ b/src/cobalt/script/engine.gyp
@@ -22,16 +22,12 @@
       'sources': [
         'javascript_engine.h',
       ],
-      'conditions': [
-        [ 'javascript_engine == "v8"', { 'dependencies': ['v8c/v8c.gyp:engine', ], }, ],
-      ],
+      'dependencies': ['v8c/v8c.gyp:engine', ],
     },
     {
       'target_name': 'engine_shell',
       'type': 'none',
-      'conditions': [
-        [ 'javascript_engine == "v8"', { 'dependencies': ['v8c/v8c.gyp:v8c', ], }, ],
-      ],
+      'dependencies': ['v8c/v8c.gyp:v8c', ],
     },
   ],
 }
diff --git a/src/cobalt/script/v8c/v8c_variables.gypi b/src/cobalt/script/v8c/v8c_variables.gypi
index 05d0b84..e6fab6f 100644
--- a/src/cobalt/script/v8c/v8c_variables.gypi
+++ b/src/cobalt/script/v8c/v8c_variables.gypi
@@ -14,39 +14,35 @@
 
 {
   'variables': {
-    'conditions': [
-      ['javascript_engine == "v8"', {
-        'generated_bindings_prefix': 'v8c',
-        'engine_include_dirs': [],
-        'engine_dependencies': [
-          '<(DEPTH)/third_party/v8/v8.gyp:v8',
-          '<(DEPTH)/third_party/v8/v8.gyp:v8_libplatform',
-          '<(DEPTH)/net/net.gyp:net',
-        ],
-        'engine_defines': [],
-        'engine_templates_dir': [
-          '<(DEPTH)/cobalt/bindings/v8c/templates',
-        ],
-        'engine_template_files': [
-          '<(DEPTH)/cobalt/bindings/v8c/templates/callback-interface.cc.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/callback-interface.h.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/generated-types.h.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/interface.cc.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/interface.h.template',
-          '<(DEPTH)/cobalt/bindings/v8c/templates/macros.cc.template',
-        ],
-        'engine_bindings_scripts': [
-          '<(DEPTH)/cobalt/bindings/v8c/code_generator_v8c.py',
-          '<(DEPTH)/cobalt/bindings/v8c/idl_compiler_v8c.py',
-          '<(DEPTH)/cobalt/bindings/v8c/generate_conversion_header_v8c.py',
-        ],
-        'engine_idl_compiler':
-            '<(DEPTH)/cobalt/bindings/v8c/idl_compiler_v8c.py',
-        'engine_conversion_header_generator_script':
-            '<(DEPTH)/cobalt/bindings/v8c/generate_conversion_header_v8c.py',
-      }],
+    'generated_bindings_prefix': 'v8c',
+    'engine_include_dirs': [],
+    'engine_dependencies': [
+      '<(DEPTH)/third_party/v8/v8.gyp:v8',
+      '<(DEPTH)/third_party/v8/v8.gyp:v8_libplatform',
+      '<(DEPTH)/net/net.gyp:net',
     ],
+    'engine_defines': [],
+    'engine_templates_dir': [
+      '<(DEPTH)/cobalt/bindings/v8c/templates',
+    ],
+    'engine_template_files': [
+      '<(DEPTH)/cobalt/bindings/v8c/templates/callback-interface.cc.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/callback-interface.h.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/generated-types.h.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/interface.cc.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/interface.h.template',
+      '<(DEPTH)/cobalt/bindings/v8c/templates/macros.cc.template',
+    ],
+    'engine_bindings_scripts': [
+      '<(DEPTH)/cobalt/bindings/v8c/code_generator_v8c.py',
+      '<(DEPTH)/cobalt/bindings/v8c/idl_compiler_v8c.py',
+      '<(DEPTH)/cobalt/bindings/v8c/generate_conversion_header_v8c.py',
+    ],
+    'engine_idl_compiler':
+        '<(DEPTH)/cobalt/bindings/v8c/idl_compiler_v8c.py',
+    'engine_conversion_header_generator_script':
+        '<(DEPTH)/cobalt/bindings/v8c/generate_conversion_header_v8c.py',
   },
 }
diff --git a/src/cobalt/site/docs/contributors/index.md b/src/cobalt/site/docs/contributors/index.md
index ea93cc6..77811fa 100644
--- a/src/cobalt/site/docs/contributors/index.md
+++ b/src/cobalt/site/docs/contributors/index.md
@@ -52,7 +52,7 @@
 *  Ensure you or your company have signed the appropriate CLA as discussed
    in the [Before You Contribute](#before-you-contribute) section above.
 *  Rebase your changes down into a single git commit.
-*  Run `git cl upload` to upload the review to
+*  Run `git push origin HEAD:refs/for/master` to upload the review to
    [Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/).
 *  Someone from the maintainers team reviews the code, adding comments on
    any things that need to change before the code can be submitted.
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index 8b1437b..cf7b156 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -50,14 +50,14 @@
     image: cobalt-base
     scale: 0
 
-  base-xenial:
+  base-bionic:
     build:
       args:
-        - BASE_OS=ubuntu
-        - BASE_OS_TAG=xenial
+        - BASE_OS=gcr.io/cloud-marketplace-containers/google/ubuntu1804
+        - BASE_OS_TAG=latest
       context: ./docker/linux
       dockerfile: base/Dockerfile
-    image: base-xenial
+    image: base-bionic
     scale: 0
 
   # Define common build container for Linux
@@ -70,15 +70,15 @@
       - base
     scale: 0
 
-  build-base-xenial:
+  build-base-bionic:
     build:
       context: ./docker/linux
       dockerfile: base/build/Dockerfile
       args:
-        - FROM_IMAGE=base-xenial
-    image: build-base-xenial
+        - FROM_IMAGE=base-bionic
+    image: build-base-bionic
     depends_on:
-      - base-xenial
+      - base-bionic
     scale: 0
 
   stub:
@@ -129,17 +129,17 @@
       PLATFORM: linux-x64x11
       CONFIG: ${CONFIG:-debug}
 
-  linux-x64x11-xenial:
+  linux-x64x11-bionic:
     <<: *common-definitions
     <<: *build-volumes
     build:
       context: ./docker/linux
       dockerfile: linux-x64x11/Dockerfile
       args:
-        - FROM_IMAGE=build-base-xenial
-    image: linux-x64x11-xenial
+        - FROM_IMAGE=build-base-bionic
+    image: linux-x64x11-bionic
     depends_on:
-      - build-base-xenial
+      - build-base-bionic
     scale: 0
 
   linux-x64x11-clang-3-9:
@@ -154,7 +154,7 @@
       PLATFORM: linux-x64x11-clang-3-9
       CONFIG: ${CONFIG:-debug}
     depends_on:
-      - linux-x64x11-xenial
+      - linux-x64x11-bionic
 
   linux-x64x11-clang-3-9-prebuilt:
     <<: *common-definitions
@@ -259,6 +259,17 @@
       PLATFORM: ${PLATFORM:-raspi-2}
       CONFIG: ${CONFIG:-debug}
 
+  raspi-gn:
+    <<: *build-common-definitions
+    build:
+      context: ./docker/linux/raspi
+      dockerfile: ./gn/Dockerfile
+    image: cobalt-build-raspi-gn
+    environment:
+      <<: *shared-build-env
+      PLATFORM: ${PLATFORM:-raspi-2}
+      CONFIG: ${CONFIG:-debug}
+
   # Define common build container for Evergreen
   build-evergreen:
     <<: *build-common-definitions
diff --git a/src/docker/linux/clang-3-9/Dockerfile b/src/docker/linux/clang-3-9/Dockerfile
index 97bab91..45b1245 100644
--- a/src/docker/linux/clang-3-9/Dockerfile
+++ b/src/docker/linux/clang-3-9/Dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM linux-x64x11-xenial
+FROM linux-x64x11-bionic
 
 ARG DEBIAN_FRONTEND=noninteractive
 
diff --git a/src/docker/linux/raspi/Dockerfile b/src/docker/linux/raspi/Dockerfile
index c5d800d..0c015f7 100644
--- a/src/docker/linux/raspi/Dockerfile
+++ b/src/docker/linux/raspi/Dockerfile
@@ -28,6 +28,7 @@
         libxml2 \
         binutils-aarch64-linux-gnu \
         binutils-arm-linux-gnueabi \
+        libglib2.0-dev \
     && /opt/clean-after-apt.sh
 
 # Get the combined toolchains package.
diff --git a/src/docker/linux/raspi/gn/Dockerfile b/src/docker/linux/raspi/gn/Dockerfile
new file mode 100644
index 0000000..2952fef
--- /dev/null
+++ b/src/docker/linux/raspi/gn/Dockerfile
@@ -0,0 +1,18 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM cobalt-build-raspi
+
+CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" target_cpu=\"arm\" treat_warnings_as_errors=false" && \
+    ninja -j ${NINJA_PARALLEL} -C ${OUTDIR}/${PLATFORM}_${CONFIG}
diff --git a/src/docker/precommit_hooks/Dockerfile b/src/docker/precommit_hooks/Dockerfile
new file mode 100644
index 0000000..eff79a4
--- /dev/null
+++ b/src/docker/precommit_hooks/Dockerfile
@@ -0,0 +1,36 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM gcr.io/cloud-marketplace-containers/google/debian10:latest
+
+RUN apt update -qqy \
+    && apt install -qqy --no-install-recommends \
+        git python2 python3 python3-pip \
+    && apt-get clean autoclean \
+    && apt-get autoremove -y --purge \
+    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
+    && rm -rf /var/lib/{apt,dpkg,cache,log}
+
+RUN pip3 install pre-commit
+
+WORKDIR /code
+
+ENV PRE_COMMIT_COLOR=always
+ENV SKIP=test-download-from-gcs-helper,check-bug-in-commit-message,run-py2-tests
+
+CMD pre-commit install -t pre-push -t pre-commit && \
+    (pre-commit run --from-ref ${FROM_REF} --to-ref ${TO_REF} --hook-stage commit; \
+     ret=$?; \
+     pre-commit run --from-ref ${FROM_REF} --to-ref ${TO_REF} --hook-stage push; \
+     exit $(( $ret || $? )))
diff --git a/src/docker/precommit_hooks/docker-compose.yml b/src/docker/precommit_hooks/docker-compose.yml
new file mode 100644
index 0000000..6106d19
--- /dev/null
+++ b/src/docker/precommit_hooks/docker-compose.yml
@@ -0,0 +1,27 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: '2.4'
+
+services:
+  pre-commit:
+    build:
+      context: .
+      dockerfile: Dockerfile
+    image: pre-commit
+    volumes:
+      - ${COBALT_SRC:-../../}:/code/
+    environment:
+      FROM_REF: ${FROM_REF:-HEAD^1}
+      TO_REF: ${TO_REF:-HEAD}
diff --git a/src/starboard/_env.py b/src/starboard/_env.py
index 021908e..f091f9e 100644
--- a/src/starboard/_env.py
+++ b/src/starboard/_env.py
@@ -21,6 +21,6 @@
 
 _ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
 if not path.exists(_ENV):
-  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  print('%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV))
   sys.exit(1)
 load_source('', _ENV)
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
index e5a4254..1f96ccd 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
@@ -14,10 +14,16 @@
 
 package dev.cobalt.coat;
 
+import static dev.cobalt.util.Log.TAG;
+
+import dev.cobalt.util.Log;
 import dev.cobalt.util.UsedByNative;
 
 /** Abstract class that provides an interface for Cobalt to interact with a platform service. */
 public abstract class CobaltService {
+  // Indicate is the service opened, and be able to send data to client
+  protected boolean opened = true;
+
   /** Interface that returns an object that extends CobaltService. */
   public interface Factory {
     /** Create the service. */
@@ -59,14 +65,41 @@
   @UsedByNative
   public abstract ResponseToClient receiveFromClient(byte[] data);
 
-  /** Close the service. */
+  /**
+   * Close the service.
+   *
+   * <p>Once this function returns, it is invalid to call sendToClient for the nativeService, so
+   * synchronization must be used to protect against this.
+   */
   @SuppressWarnings("unused")
   @UsedByNative
+  public void onClose() {
+    synchronized (this) {
+      opened = false;
+      close();
+    }
+  }
+
   public abstract void close();
 
-  /** Send data from the service to the client. */
+  /**
+   * Send data from the service to the client.
+   *
+   * <p>This may be called from a separate thread, do not call nativeSendToClient() once onClose()
+   * is processed.
+   */
   protected void sendToClient(long nativeService, byte[] data) {
-    nativeSendToClient(nativeService, data);
+    synchronized (this) {
+      if (!opened) {
+        Log.w(
+            TAG,
+            "Platform service did not send data to client, because client already closed the"
+                + " platform service.");
+        return;
+      }
+
+      nativeSendToClient(nativeService, data);
+    }
   }
 
   private native void nativeSendToClient(long nativeService, byte[] data);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index 455f47b..68d95ef 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.hardware.input.InputManager;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
@@ -33,6 +34,7 @@
 import android.util.Size;
 import android.util.SizeF;
 import android.view.Display;
+import android.view.InputDevice;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
 import androidx.annotation.Nullable;
@@ -172,7 +174,7 @@
     Service service = serviceHolder.get();
     if (service == null) {
       if (appContext == null) {
-        Log.w(TAG, "Activiy already destoryed.");
+        Log.w(TAG, "Activiy already destroyed.");
         return;
       }
       Log.i(TAG, "Cold start - Instantiating a MediaPlaybackService.");
@@ -250,6 +252,7 @@
   @UsedByNative
   public void requestStop(int errorLevel) {
     if (!starboardStopped) {
+      Log.i(TAG, "Request to stop");
       nativeStopApp(errorLevel);
     }
   }
@@ -261,6 +264,7 @@
   public void requestSuspend() {
     Activity activity = activityHolder.get();
     if (activity != null) {
+      Log.i(TAG, "Request to suspend");
       activity.finish();
     }
   }
@@ -435,7 +439,21 @@
     // connected input audio device is a microphone.
     AudioManager audioManager = (AudioManager) appContext.getSystemService(AUDIO_SERVICE);
     AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_INPUTS);
-    return devices.length > 0;
+    if (devices.length > 0) {
+      return true;
+    }
+
+    // fallback to check for BT voice capable RCU
+    InputManager inputManager = (InputManager) appContext.getSystemService(Context.INPUT_SERVICE);
+    final int[] inputDeviceIds = inputManager.getInputDeviceIds();
+    for (int inputDeviceId : inputDeviceIds) {
+      final InputDevice inputDevice = inputManager.getInputDevice(inputDeviceId);
+      final boolean hasMicrophone = inputDevice.hasMicrophone();
+      if (hasMicrophone) {
+        return true;
+      }
+    }
+    return false;
   }
 
   /**
@@ -682,9 +700,7 @@
     cobaltServices.remove(serviceName);
   }
 
-  /**
-   * Returns the application start timestamp.
-   */
+  /** Returns the application start timestamp. */
   @SuppressWarnings("unused")
   @UsedByNative
   protected long getAppStartTimestamp() {
@@ -693,8 +709,8 @@
       long javaStartTimestamp = ((CobaltActivity) activity).getAppStartTimestamp();
       long cppTimestamp = nativeSbTimeGetMonotonicNow();
       long javaStopTimestamp = System.nanoTime();
-      return cppTimestamp -
-          (javaStartTimestamp - javaStopTimestamp) / timeNanosecondsPerMicrosecond;
+      return cppTimestamp
+          - (javaStartTimestamp - javaStopTimestamp) / timeNanosecondsPerMicrosecond;
     }
     return 0;
   }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
index 18418e8..f265e98 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
@@ -97,6 +97,11 @@
                       || info.getType() == AudioDeviceInfo.TYPE_HDMI)) {
                 // TODO: Avoid destroying the AudioTrack if the new devices can support the current
                 // AudioFormat.
+                Log.v(
+                    TAG,
+                    String.format(
+                        "Setting |hasAudioDeviceChanged| to true for audio device %s, %s.",
+                        info.getProductName(), getDeviceTypeNameV23(info.getType())));
                 hasAudioDeviceChanged.set(true);
                 break;
               }
@@ -105,6 +110,11 @@
 
           @Override
           public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            Log.v(
+                TAG,
+                String.format(
+                    "onAudioDevicesAdded() called, |initialDevicesAdded| is: %b.",
+                    initialDevicesAdded));
             if (initialDevicesAdded) {
               handleConnectedDeviceChange(addedDevices);
               return;
@@ -114,6 +124,7 @@
 
           @Override
           public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+            Log.v(TAG, "onAudioDevicesRemoved() called.");
             handleConnectedDeviceChange(removedDevices);
           }
         },
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
index eaeb513..c31e017 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
@@ -464,7 +464,8 @@
         // focus if |explicitUserActionRequired| is true. Currently we're not able to recognize
         // it. But if we don't have window focus, we know the user is not interacting with our app
         // and we should not request media focus.
-        if (!explicitUserActionRequired || activityHolder.get().hasWindowFocus()) {
+        if (!explicitUserActionRequired
+            || (activityHolder.get() != null && activityHolder.get().hasWindowFocus())) {
           explicitUserActionRequired = false;
           configureMediaFocus(playbackState);
         } else {
@@ -478,7 +479,7 @@
     }
 
     // Ignore updates to the MediaSession metadata if playback is stopped.
-    if (playbackState == PLAYBACK_STATE_NONE) {
+    if (playbackState == PLAYBACK_STATE_NONE || mediaSession == null) {
       return;
     }
 
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 0ca198c..2473e28 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -351,7 +351,8 @@
         float maxMasteringLuminance,
         float minMasteringLuminance,
         int maxCll,
-        int maxFall) {
+        int maxFall,
+        boolean forceBigEndianHdrMetadata) {
       this.colorRange = colorRange;
       this.colorStandard = colorStandard;
       this.colorTransfer = colorTransfer;
@@ -365,7 +366,14 @@
 
       // This logic is inspired by
       // https://github.com/google/ExoPlayer/blob/deb9b301b2c7ef66fdd7d8a3e58298a79ba9c619/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java#L1803.
-      ByteBuffer hdrStaticInfo = ByteBuffer.allocateDirect(25).order(ByteOrder.LITTLE_ENDIAN);
+      ByteBuffer hdrStaticInfo = ByteBuffer.allocateDirect(25);
+      // Force big endian in case the HDR metadata causes problems in production.
+      if (forceBigEndianHdrMetadata) {
+        hdrStaticInfo.order(ByteOrder.BIG_ENDIAN);
+      } else {
+        hdrStaticInfo.order(ByteOrder.LITTLE_ENDIAN);
+      }
+
       hdrStaticInfo.put((byte) 0);
       hdrStaticInfo.putShort((short) ((primaryRChromaticityX * MAX_CHROMATICITY) + 0.5f));
       hdrStaticInfo.putShort((short) ((primaryRChromaticityY * MAX_CHROMATICITY) + 0.5f));
diff --git a/src/starboard/android/shared/BUILD.gn b/src/starboard/android/shared/BUILD.gn
index 66628a2..a84353e 100644
--- a/src/starboard/android/shared/BUILD.gn
+++ b/src/starboard/android/shared/BUILD.gn
@@ -180,7 +180,6 @@
     "//starboard/shared/signal/suspend_signals.cc",
     "//starboard/shared/signal/suspend_signals.h",
     "//starboard/shared/signal/system_request_conceal.cc",
-    "//starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc",
     "//starboard/shared/starboard/application.cc",
     "//starboard/shared/starboard/application.h",
     "//starboard/shared/starboard/audio_sink/audio_sink_create.cc",
@@ -381,6 +380,7 @@
     "system_has_capability.cc",
     "system_network_is_disconnected.cc",
     "system_platform_error.cc",
+    "system_request_freeze_no_freezedone_callback.cc",
     "system_request_stop.cc",
     "system_request_suspend.cc",
     "thread_create.cc",
diff --git a/src/starboard/android/shared/gyp_configuration.py b/src/starboard/android/shared/gyp_configuration.py
index 018dbb6..69a6787 100644
--- a/src/starboard/android/shared/gyp_configuration.py
+++ b/src/starboard/android/shared/gyp_configuration.py
@@ -106,8 +106,6 @@
             self.android_abi,
         'include_path_platform_deploy_gypi':
             'starboard/android/shared/platform_deploy.gypi',
-        'javascript_engine':
-            'v8',
     })
     return variables
 
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index 0415adc..3b5427d 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -198,6 +198,7 @@
     const SbMediaColorMetadata* color_metadata,
     bool require_software_codec,
     int tunnel_mode_audio_session_id,
+    bool force_big_endian_hdr_metadata,
     std::string* error_message) {
   SB_DCHECK(error_message);
 
@@ -222,7 +223,7 @@
         color_range != COLOR_VALUE_UNKNOWN) {
       const auto& mastering_metadata = color_metadata->mastering_metadata;
       j_color_info.Reset(env->NewObjectOrAbort(
-          "dev/cobalt/media/MediaCodecBridge$ColorInfo", "(IIIFFFFFFFFFFII)V",
+          "dev/cobalt/media/MediaCodecBridge$ColorInfo", "(IIIFFFFFFFFFFIIZ)V",
           color_range, color_standard, color_transfer,
           mastering_metadata.primary_r_chromaticity_x,
           mastering_metadata.primary_r_chromaticity_y,
@@ -233,7 +234,8 @@
           mastering_metadata.white_point_chromaticity_x,
           mastering_metadata.white_point_chromaticity_y,
           mastering_metadata.luminance_max, mastering_metadata.luminance_min,
-          color_metadata->max_cll, color_metadata->max_fall));
+          color_metadata->max_cll, color_metadata->max_fall,
+          force_big_endian_hdr_metadata));
     }
   }
 
diff --git a/src/starboard/android/shared/media_codec_bridge.h b/src/starboard/android/shared/media_codec_bridge.h
index 8ec1f94..60a6487 100644
--- a/src/starboard/android/shared/media_codec_bridge.h
+++ b/src/starboard/android/shared/media_codec_bridge.h
@@ -112,6 +112,7 @@
       const SbMediaColorMetadata* color_metadata,
       bool require_software_codec,
       int tunnel_mode_audio_session_id,
+      bool force_big_endian_hdr_metadata,
       std::string* error_message);
 
   ~MediaCodecBridge();
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index 29567ed..69293d1 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -111,6 +111,7 @@
                            bool require_software_codec,
                            const FrameRenderedCB& frame_rendered_cb,
                            int tunnel_mode_audio_session_id,
+                           bool force_big_endian_hdr_metadata,
                            std::string* error_message)
     : media_type_(kSbMediaTypeVideo),
       host_(host),
@@ -125,7 +126,7 @@
   media_codec_bridge_ = MediaCodecBridge::CreateVideoMediaCodecBridge(
       video_codec, width, height, fps, this, j_output_surface, j_media_crypto,
       color_metadata, require_software_codec, tunnel_mode_audio_session_id,
-      error_message);
+      force_big_endian_hdr_metadata, error_message);
   if (!media_codec_bridge_) {
     SB_LOG(ERROR) << "Failed to create video media codec bridge with error: "
                   << *error_message;
diff --git a/src/starboard/android/shared/media_decoder.h b/src/starboard/android/shared/media_decoder.h
index 6550cdd..569036a 100644
--- a/src/starboard/android/shared/media_decoder.h
+++ b/src/starboard/android/shared/media_decoder.h
@@ -85,6 +85,7 @@
                bool require_software_codec,
                const FrameRenderedCB& frame_rendered_cb,
                int tunnel_mode_audio_session_id,
+               bool force_big_endian_hdr_metadata,
                std::string* error_message);
   ~MediaDecoder();
 
diff --git a/src/starboard/android/shared/media_is_audio_supported.cc b/src/starboard/android/shared/media_is_audio_supported.cc
index e7b04de..a9c63f6 100644
--- a/src/starboard/android/shared/media_is_audio_supported.cc
+++ b/src/starboard/android/shared/media_is_audio_supported.cc
@@ -45,71 +45,54 @@
   if (!mime) {
     return false;
   }
+
   MimeType mime_type(content_type);
-  // Allows for disabling the use of the AudioDeviceCallback API to detect when
-  // audio peripherals are connected. Enabled by default.
-  // (https://developer.android.com/reference/android/media/AudioDeviceCallback)
-  auto enable_audio_device_callback_parameter_value =
-      mime_type.GetParamStringValue("enableaudiodevicecallback", "");
-  if (!enable_audio_device_callback_parameter_value.empty() &&
-      enable_audio_device_callback_parameter_value != "true" &&
-      enable_audio_device_callback_parameter_value != "false") {
-    SB_LOG(INFO) << "Invalid value for audio mime parameter "
-                    "\"enableaudiodevicecallback\": "
-                 << enable_audio_device_callback_parameter_value << ".";
-    return false;
+  if (strlen(content_type) > 0) {
+    // Allows for disabling the use of the AudioDeviceCallback API to detect
+    // when audio peripherals are connected. Enabled by default.
+    // (https://developer.android.com/reference/android/media/AudioDeviceCallback)
+    mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+    // Allows for enabling tunneled playback. Disabled by default.
+    // (https://source.android.com/devices/tv/multimedia-tunneling)
+    mime_type.RegisterBoolParameter("tunnelmode");
+    // Enables audio passthrough if the codec supports it.
+    mime_type.RegisterBoolParameter("audiopassthrough");
+
+    if (!mime_type.is_valid()) {
+      return false;
+    }
   }
-  // Allows for enabling tunneled playback. Disabled by default.
-  // (https://source.android.com/devices/tv/multimedia-tunneling)
-  auto enable_tunnel_mode_parameter_value =
-      mime_type.GetParamStringValue("tunnelmode", "");
-  if (!enable_tunnel_mode_parameter_value.empty() &&
-      enable_tunnel_mode_parameter_value != "true" &&
-      enable_tunnel_mode_parameter_value != "false") {
-    SB_LOG(INFO) << "Invalid value for audio mime parameter \"tunnelmode\": "
-                 << enable_tunnel_mode_parameter_value << ".";
-    return false;
-  } else if (enable_tunnel_mode_parameter_value == "true" &&
-             !SbAudioSinkIsAudioSampleTypeSupported(
-                 kSbMediaAudioSampleTypeInt16Deprecated)) {
+
+  bool enable_tunnel_mode = mime_type.GetParamBoolValue("tunnelmode", false);
+  if (enable_tunnel_mode && !SbAudioSinkIsAudioSampleTypeSupported(
+                                kSbMediaAudioSampleTypeInt16Deprecated)) {
     SB_LOG(WARNING)
         << "Tunnel mode is rejected because int16 sample is required "
            "but not supported.";
     return false;
   }
 
-  auto audio_passthrough_parameter_value =
-      mime_type.GetParamStringValue("audiopassthrough", "");
-  if (!audio_passthrough_parameter_value.empty() &&
-      audio_passthrough_parameter_value != "true" &&
-      audio_passthrough_parameter_value != "false") {
-    SB_LOG(INFO) << "Invalid value for audio mime parameter "
-                    "\"audiopassthrough\": "
-                 << audio_passthrough_parameter_value
-                 << ". Passthrough is disabled.";
-    return false;
-  }
-  if (audio_passthrough_parameter_value == "false" && is_passthrough) {
-    SB_LOG(INFO) << "Passthrough is rejected because audio mime parameter "
-                    "\"audiopassthrough\" == false.";
-    return false;
-  }
-
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
-  const bool must_support_tunnel_mode =
-      enable_tunnel_mode_parameter_value == "true";
   auto media_codec_supported =
       env->CallStaticBooleanMethodOrAbort(
           "dev/cobalt/media/MediaCodecUtil", "hasAudioDecoderFor",
           "(Ljava/lang/String;IZ)Z", j_mime.Get(), static_cast<jint>(bitrate),
-          must_support_tunnel_mode) == JNI_TRUE;
+          enable_tunnel_mode) == JNI_TRUE;
   if (!media_codec_supported) {
     return false;
   }
+
   if (!is_passthrough) {
     return true;
   }
+
+  if (!mime_type.GetParamBoolValue("audiopassthrough", true)) {
+    SB_LOG(INFO) << "Passthrough codec is rejected because passthrough is "
+                    "disabled through mime param.";
+    return false;
+  }
+
   SbMediaAudioCodingType coding_type;
   switch (audio_codec) {
     case kSbMediaAudioCodecAc3:
diff --git a/src/starboard/android/shared/media_is_video_supported.cc b/src/starboard/android/shared/media_is_video_supported.cc
index 332e4b2..095a698 100644
--- a/src/starboard/android/shared/media_is_video_supported.cc
+++ b/src/starboard/android/shared/media_is_video_supported.cc
@@ -95,29 +95,32 @@
   if (!mime) {
     return false;
   }
+  // Check extended parameters for correctness and return false if any invalid
+  // invalid params are found.
   MimeType mime_type(content_type);
   // Allows for enabling tunneled playback. Disabled by default.
-  // (https://source.android.com/devices/tv/multimedia-tunneling)
-  auto enable_tunnel_mode_parameter_value =
-      mime_type.GetParamStringValue("tunnelmode", "");
-  if (!enable_tunnel_mode_parameter_value.empty() &&
-      enable_tunnel_mode_parameter_value != "true" &&
-      enable_tunnel_mode_parameter_value != "false") {
-    SB_LOG(INFO) << "Invalid value for video mime parameter \"tunnelmode\": "
-                 << enable_tunnel_mode_parameter_value << ".";
+  // https://source.android.com/devices/tv/multimedia-tunneling
+  mime_type.RegisterBoolParameter("tunnelmode");
+  // Override endianness on HDR Info header. Defaults to little.
+  mime_type.RegisterStringParameter("hdrinfoendianness", "big|little");
+
+  if (!mime_type.is_valid()) {
     return false;
-  } else if (enable_tunnel_mode_parameter_value == "true" &&
-             decode_to_texture_required) {
+  }
+
+  bool must_support_tunnel_mode =
+      mime_type.GetParamBoolValue("tunnelmode", false);
+  if (must_support_tunnel_mode && decode_to_texture_required) {
     SB_LOG(WARNING) << "Tunnel mode is rejected because output mode decode to "
                        "texture is required but not supported.";
     return false;
   }
+
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
   const bool must_support_hdr = (transfer_id != kSbMediaTransferIdBt709 &&
                                  transfer_id != kSbMediaTransferIdUnspecified);
-  const bool must_support_tunnel_mode =
-      enable_tunnel_mode_parameter_value == "true";
+
   // We assume that if a device supports a format for clear playback, it will
   // also support it for encrypted playback. However, some devices require
   // tunneled playback to be encrypted, so we must align the tunnel mode
diff --git a/src/starboard/android/shared/platform_configuration/BUILD.gn b/src/starboard/android/shared/platform_configuration/BUILD.gn
index 27b0a08..75b13be 100644
--- a/src/starboard/android/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/android/shared/platform_configuration/BUILD.gn
@@ -60,27 +60,6 @@
     cflags += [ "-g" ]
   }
 
-  if (sb_pedantic_warnings) {
-    cflags += [
-      "-Wall",
-      "-Wextra",
-      "-Wunreachable-code",
-
-      # Don"t get pedantic about warnings from base macros. These must be
-      # disabled after the -Wall above, so this has to be done here rather
-      # than in the platform"s target toolchain.
-      # TODO: Rebase base and use static_assert instead of COMPILE_ASSERT
-
-      "-Wno-unused-local-typedef",  # COMPILE_ASSERT
-      "-Wno-missing-field-initializers",  # LAZY_INSTANCE_INITIALIZER
-
-      # It"s OK not to use some input parameters. Note that the order
-      # matters: Wall implies Wunused-parameter and Wno-unused-parameter
-      # has no effect if specified before Wall.
-      "-Wno-unused-parameter",
-    ]
-  }
-
   if (use_asan) {
     cflags += [
       "-fsanitize=address",
@@ -206,3 +185,23 @@
 config("library_config") {
   cflags = [ "-fPIC" ]
 }
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+
+    # Don't get pedantic about warnings from base macros. These must be
+    # disabled after the -Wall above, so this has to be done here rather
+    # than in the platform's target toolchain.
+    # TODO: Rebase base and use static_assert instead of COMPILE_ASSERT
+    "-Wno-unused-local-typedef",  # COMPILE_ASSERT
+    "-Wno-missing-field-initializers",  # LAZY_INSTANCE_INITIALIZER
+
+    # It's OK not to use some input parameters. Note that the order
+    # matters: Wall implies Wunused-parameter and Wno-unused-parameter
+    # has no effect if specified before Wall.
+    "-Wno-unused-parameter",
+  ]
+}
diff --git a/src/starboard/android/shared/platform_configuration/configuration.gni b/src/starboard/android/shared/platform_configuration/configuration.gni
index 19850b6..f1c28dd 100644
--- a/src/starboard/android/shared/platform_configuration/configuration.gni
+++ b/src/starboard/android/shared/platform_configuration/configuration.gni
@@ -39,6 +39,9 @@
 size_config_path = "//starboard/android/shared/platform_configuration:size"
 speed_config_path = "//starboard/android/shared/platform_configuration:speed"
 
+pedantic_warnings_config_path =
+    "//starboard/android/shared/platform_configuration:pedantic_warnings"
+
 executable_configs +=
     [ "//starboard/android/shared/platform_configuration:executable_config" ]
 shared_library_configs +=
diff --git a/src/starboard/android/shared/platform_service.cc b/src/starboard/android/shared/platform_service.cc
index 7e3a479..0670bc8 100644
--- a/src/starboard/android/shared/platform_service.cc
+++ b/src/starboard/android/shared/platform_service.cc
@@ -78,7 +78,7 @@
 
 void Close(CobaltExtensionPlatformService service) {
   JniEnvExt* env = JniEnvExt::Get();
-  env->CallVoidMethodOrAbort(service->cobalt_service, "close", "()V");
+  env->CallVoidMethodOrAbort(service->cobalt_service, "onClose", "()V");
   ScopedLocalJavaRef<jstring> j_name(
       env->NewStringStandardUTFOrAbort(service->name));
   env->CallStarboardVoidMethodOrAbort("closeCobaltService",
diff --git a/src/starboard/android/shared/player_components_factory.h b/src/starboard/android/shared/player_components_factory.h
index b2cbfed..a1af6ee 100644
--- a/src/starboard/android/shared/player_components_factory.h
+++ b/src/starboard/android/shared/player_components_factory.h
@@ -183,24 +183,6 @@
     return (value + alignment - 1) / alignment * alignment;
   }
 
-  static bool IsAudioDeviceCallbackEnabled(
-      const CreationParameters& creation_parameters) {
-    using starboard::shared::starboard::media::MimeType;
-
-    MimeType mime_type(creation_parameters.audio_mime());
-    auto enable_audio_device_callback_parameter_value =
-        mime_type.GetParamStringValue("enableaudiodevicecallback", "");
-    if (enable_audio_device_callback_parameter_value.empty() ||
-        enable_audio_device_callback_parameter_value == "true") {
-      SB_LOG(INFO) << "AudioDeviceCallback is enabled.";
-      return true;
-    }
-    SB_LOG(INFO) << "Mime attribute \"enableaudiodevicecallback\" is set to: "
-                 << enable_audio_device_callback_parameter_value
-                 << ". AudioDeviceCallback is disabled.";
-    return false;
-  }
-
   scoped_ptr<PlayerComponents> CreateComponents(
       const CreationParameters& creation_parameters,
       std::string* error_message) override {
@@ -215,8 +197,21 @@
     }
 
     MimeType audio_mime_type(creation_parameters.audio_mime());
-    if (audio_mime_type.GetParamStringValue("audiopassthrough", "") ==
-        "false") {
+
+    if (strlen(creation_parameters.audio_mime()) > 0) {
+      audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+      audio_mime_type.RegisterBoolParameter("audiopassthrough");
+      if (!audio_mime_type.is_valid()) {
+        return scoped_ptr<PlayerComponents>();
+      }
+    }
+
+    bool enable_audio_device_callback =
+        audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
+    SB_LOG(INFO) << "AudioDeviceCallback is "
+                 << (enable_audio_device_callback ? "enabled." : "disabled.");
+
+    if (!audio_mime_type.GetParamBoolValue("audiopassthrough", true)) {
       SB_LOG(INFO) << "Mime attribute \"audiopassthrough\" is set to: "
                       "false. Passthrough is disabled.";
       return scoped_ptr<PlayerComponents>();
@@ -228,7 +223,7 @@
     audio_renderer.reset(new AudioRendererPassthrough(
         creation_parameters.audio_sample_info(),
         GetExtendedDrmSystem(creation_parameters.drm_system()),
-        IsAudioDeviceCallbackEnabled(creation_parameters)));
+        enable_audio_device_callback));
     if (!audio_renderer->is_valid()) {
       return scoped_ptr<PlayerComponents>();
     }
@@ -272,31 +267,56 @@
     using starboard::shared::starboard::media::MimeType;
     SB_DCHECK(error_message);
 
+    const char* audio_mime =
+        creation_parameters.audio_codec() != kSbMediaAudioCodecNone
+            ? creation_parameters.audio_mime()
+            : "";
+    MimeType audio_mime_type(audio_mime);
+    if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone &&
+        strlen(creation_parameters.audio_mime()) > 0) {
+      audio_mime_type.RegisterBoolParameter("tunnelmode");
+      audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+
+      if (!audio_mime_type.is_valid()) {
+        *error_message =
+            "Invalid audio MIME: '" + std::string(audio_mime) + "'";
+        return false;
+      }
+    }
+
+    const char* video_mime =
+        creation_parameters.video_codec() != kSbMediaVideoCodecNone
+            ? creation_parameters.video_mime()
+            : "";
+    MimeType video_mime_type(video_mime);
+    if (creation_parameters.video_codec() != kSbMediaVideoCodecNone &&
+        strlen(creation_parameters.video_mime()) > 0) {
+      video_mime_type.RegisterBoolParameter("tunnelmode");
+
+      if (!video_mime_type.is_valid()) {
+        *error_message =
+            "Invalid video MIME: '" + std::string(video_mime) + "'";
+        return false;
+      }
+    }
+
     int tunnel_mode_audio_session_id = -1;
     bool enable_tunnel_mode = false;
     if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone &&
         creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
-      MimeType audio_mime_type(creation_parameters.audio_mime());
-      MimeType video_mime_type(creation_parameters.video_mime());
-      auto enable_tunnel_mode_audio_parameter_value =
-          audio_mime_type.GetParamStringValue("tunnelmode", "");
-      auto enable_tunnel_mode_video_parameter_value =
-          video_mime_type.GetParamStringValue("tunnelmode", "");
-      if (enable_tunnel_mode_audio_parameter_value == "true" &&
-          enable_tunnel_mode_video_parameter_value == "true") {
-        enable_tunnel_mode = true;
-      } else {
-        if (enable_tunnel_mode_audio_parameter_value.empty()) {
-          enable_tunnel_mode_audio_parameter_value = "not provided";
-        }
-        if (enable_tunnel_mode_video_parameter_value.empty()) {
-          enable_tunnel_mode_video_parameter_value = "not provided";
-        }
-        SB_LOG(INFO) << "Tunnel mode is disabled. Audio mime parameter "
-                        "\"tunnelmode\" value: "
-                     << enable_tunnel_mode_audio_parameter_value
+      bool enable_tunnel_mode =
+          audio_mime_type.GetParamBoolValue("tunnelmode", false) &&
+          video_mime_type.GetParamBoolValue("tunnelmode", false);
+
+      if (!enable_tunnel_mode) {
+        SB_LOG(INFO) << "Tunnel mode is disabled. "
+                     << "Audio mime parameter \"tunnelmode\" value: "
+                     << audio_mime_type.GetParamStringValue("tunnelmode",
+                                                            "<not provided>")
                      << ", video mime parameter \"tunnelmode\" value: "
-                     << enable_tunnel_mode_video_parameter_value << ".";
+                     << video_mime_type.GetParamStringValue("tunnelmode",
+                                                            "<not provided>")
+                     << ".";
       }
     } else {
       SB_LOG(INFO) << "Tunnel mode requires both an audio and video stream. "
@@ -359,7 +379,10 @@
           decoder_creator));
 
       bool enable_audio_device_callback =
-          IsAudioDeviceCallbackEnabled(creation_parameters);
+          audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
+      SB_LOG(INFO) << "AudioDeviceCallback is "
+                   << (enable_audio_device_callback ? "enabled." : "disabled.");
+
       if (tunnel_mode_audio_session_id != -1) {
         *audio_renderer_sink = TryToCreateTunnelModeAudioRendererSink(
             tunnel_mode_audio_session_id, creation_parameters,
@@ -431,6 +454,20 @@
       int tunnel_mode_audio_session_id,
       bool force_secure_pipeline_under_tunnel_mode,
       std::string* error_message) {
+    using starboard::shared::starboard::media::MimeType;
+    // Use mime param to determine endianness of HDR metadata. If param is
+    // missing or invalid it defaults to Little Endian.
+    MimeType video_mime_type(creation_parameters.video_mime());
+
+    if (strlen(creation_parameters.video_mime()) > 0) {
+      video_mime_type.RegisterStringParameter("hdrinfoendianness",
+                                              "big|little");
+    }
+    const std::string& hdr_info_endianness =
+        video_mime_type.GetParamStringValue("hdrinfoendianness",
+                                            /*default=*/"little");
+    bool force_big_endian_hdr_metadata = hdr_info_endianness == "big";
+
     scoped_ptr<VideoDecoder> video_decoder(new VideoDecoder(
         creation_parameters.video_codec(),
         GetExtendedDrmSystem(creation_parameters.drm_system()),
@@ -438,7 +475,7 @@
         creation_parameters.decode_target_graphics_context_provider(),
         creation_parameters.max_video_capabilities(),
         tunnel_mode_audio_session_id, force_secure_pipeline_under_tunnel_mode,
-        error_message));
+        force_big_endian_hdr_metadata, error_message));
     if (creation_parameters.video_codec() == kSbMediaVideoCodecAv1 ||
         video_decoder->is_decoder_created()) {
       return video_decoder.Pass();
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 8338cbc..6e61339 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -164,6 +164,7 @@
         'system_has_capability.cc',
         'system_network_is_disconnected.cc',
         'system_platform_error.cc',
+        'system_request_freeze_no_freezedone_callback.cc',
         'system_request_stop.cc',
         'system_request_suspend.cc',
         'thread_create.cc',
@@ -357,7 +358,6 @@
         '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
         '<(DEPTH)/starboard/shared/signal/system_request_conceal.cc',
-        '<(DEPTH)/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc',
         '<(DEPTH)/starboard/shared/starboard/application.cc',
         '<(DEPTH)/starboard/shared/starboard/application.h',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
diff --git a/src/starboard/android/shared/system_get_property.cc b/src/starboard/android/shared/system_get_property.cc
index fa9b8ce..9d17a69 100644
--- a/src/starboard/android/shared/system_get_property.cc
+++ b/src/starboard/android/shared/system_get_property.cc
@@ -106,10 +106,9 @@
                                       value_length, kUnknownValue);
     case kSbSystemPropertyModelYear: {
       char key1[PROP_VALUE_MAX] = "";
-      SB_DCHECK(GetAndroidSystemProperty("ro.oem.key1", key1, PROP_VALUE_MAX,
-                                         kUnknownValue));
-      if (strcmp(key1, kUnknownValue) == 0 ||
-          strlen(key1) < 10) {
+      GetAndroidSystemProperty("ro.oem.key1", key1, PROP_VALUE_MAX,
+                               kUnknownValue);
+      if (strcmp(key1, kUnknownValue) == 0 || strlen(key1) < 10) {
         return CopyStringAndTestIfSuccess(out_value, value_length,
                                           kUnknownValue);
       }
diff --git a/src/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc b/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
similarity index 74%
rename from src/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc
rename to src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
index ad245d6..d245e9e 100644
--- a/src/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc
+++ b/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
@@ -14,9 +14,12 @@
 
 #include "starboard/system.h"
 
+#include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/shared/signal/signal_internal.h"
 #include "starboard/shared/starboard/application.h"
 
+using starboard::android::shared::JniEnvExt;
+
 #if SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 #include "starboard/loader_app/pending_restart.h"
 #endif  // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
@@ -29,16 +32,22 @@
     SbLogFlush();
     starboard::shared::starboard::Application::Get()->Stop(0);
   } else {
-    // Let the platform decide if directly transit into Frozen. There
-    // is no FreezeDone callback for stopping all thread execution
+    // There is no FreezeDone callback for stopping all thread execution
     // after fully transitioning into Frozen.
     starboard::shared::starboard::Application::Get()->Freeze(NULL, NULL);
+
+    // Let Android platform directly transit into Frozen.
+    JniEnvExt* env = JniEnvExt::Get();
+    env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
   }
 #else
-  // Let the platform decide if directly transit into Frozen. There
-  // is no FreezeDone callback for stopping all thread execution
+  // There is no FreezeDone callback for stopping all thread execution
   // after fully transitioning into Frozen.
   starboard::shared::starboard::Application::Get()->Freeze(NULL, NULL);
+
+  // Let Android platform directly transit into Frozen.
+  JniEnvExt* env = JniEnvExt::Get();
+  env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
 #endif  // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 }
 #endif  // SB_API_VERSION >= 13
diff --git a/src/starboard/android/shared/video_decoder.cc b/src/starboard/android/shared/video_decoder.cc
index bdd2226..775496c 100644
--- a/src/starboard/android/shared/video_decoder.cc
+++ b/src/starboard/android/shared/video_decoder.cc
@@ -218,6 +218,7 @@
                            const char* max_video_capabilities,
                            int tunnel_mode_audio_session_id,
                            bool force_secure_pipeline_under_tunnel_mode,
+                           bool force_big_endian_hdr_metadata,
                            std::string* error_message)
     : video_codec_(video_codec),
       drm_system_(static_cast<DrmSystem*>(drm_system)),
@@ -228,7 +229,8 @@
       has_new_texture_available_(false),
       surface_condition_variable_(surface_destroy_mutex_),
       require_software_codec_(max_video_capabilities &&
-                              strlen(max_video_capabilities) > 0) {
+                              strlen(max_video_capabilities) > 0),
+      force_big_endian_hdr_metadata_(force_big_endian_hdr_metadata) {
   SB_DCHECK(error_message);
 
   if (tunnel_mode_audio_session_id != -1) {
@@ -556,7 +558,8 @@
       drm_system_, color_metadata_ ? &*color_metadata_ : nullptr,
       require_software_codec_,
       std::bind(&VideoDecoder::OnTunnelModeFrameRendered, this, _1),
-      tunnel_mode_audio_session_id_, error_message));
+      tunnel_mode_audio_session_id_, force_big_endian_hdr_metadata_,
+      error_message));
   if (media_decoder_->is_valid()) {
     if (error_cb_) {
       media_decoder_->Initialize(
diff --git a/src/starboard/android/shared/video_decoder.h b/src/starboard/android/shared/video_decoder.h
index 69c2cfd..cc5dc1e 100644
--- a/src/starboard/android/shared/video_decoder.h
+++ b/src/starboard/android/shared/video_decoder.h
@@ -67,6 +67,7 @@
                const char* max_video_capabilities,
                int tunnel_mode_audio_session_id,
                bool force_secure_pipeline_under_tunnel_mode,
+               bool force_big_endian_hdr_metadata,
                std::string* error_message);
   ~VideoDecoder() override;
 
@@ -135,6 +136,9 @@
   // the main player and SW decoder for sub players.
   const bool require_software_codec_;
 
+  // Force endianness of HDR Metadata.
+  const bool force_big_endian_hdr_metadata_;
+
   const int tunnel_mode_audio_session_id_ = -1;
   // On some platforms tunnel mode is only supported in the secure pipeline.  So
   // we create a dummy drm system to force the video playing in secure pipeline
diff --git a/src/starboard/android/x86/gyp_configuration.py b/src/starboard/android/x86/gyp_configuration.py
index 9af8e0c..66dad6c 100644
--- a/src/starboard/android/x86/gyp_configuration.py
+++ b/src/starboard/android/x86/gyp_configuration.py
@@ -45,11 +45,16 @@
           'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.*',
           'SbMediaSetAudioWriteDurationTests/SbMediaSetAudioWriteDurationTest'
           '.WriteContinuedLimitedInput/*',
+          'SbMediaSetAudioWriteDurationTests/SbMediaSetAudioWriteDurationTest'
+          '.WriteLimitedInput/*',
       ],
       'player_filter_tests': [
           'AudioDecoderTests/*',
           'VideoDecoderTests/*',
 
+          'PlayerComponentsTests/PlayerComponentsTest.Preroll/*',
+          'PlayerComponentsTests/PlayerComponentsTest.Pause/*',
+
           'PlayerComponentsTests/PlayerComponentsTest.*/2',
           'PlayerComponentsTests/PlayerComponentsTest.*/4',
           'PlayerComponentsTests/PlayerComponentsTest.*/9',
diff --git a/src/starboard/build/config/BUILD.gn b/src/starboard/build/config/BUILD.gn
index 05310b8..e903cb3 100644
--- a/src/starboard/build/config/BUILD.gn
+++ b/src/starboard/build/config/BUILD.gn
@@ -149,3 +149,15 @@
     configs = [ size_config_path ]
   }
 }
+
+config("pedantic_warnings") {
+  if (defined(pedantic_warnings_config_path)) {
+    configs = [ pedantic_warnings_config_path ]
+  }
+}
+
+config("no_pedantic_warnings") {
+  if (defined(no_pedantic_warnings_config_path)) {
+    configs = [ no_pedantic_warnings_config_path ]
+  }
+}
diff --git a/src/starboard/build/config/BUILDCONFIG.gn b/src/starboard/build/config/BUILDCONFIG.gn
index 31a8fd5..38169b8 100644
--- a/src/starboard/build/config/BUILDCONFIG.gn
+++ b/src/starboard/build/config/BUILDCONFIG.gn
@@ -101,11 +101,12 @@
 default_compiler_configs = [
   "//build/config/compiler:default_include_dirs",
   "//build/config/compiler:no_exceptions",
+  "//$starboard_path/platform_configuration",
   "//starboard/build/config:base",
   "//starboard/build/config:host",
   "//starboard/build/config:size",
   "//starboard/build/config:target",
-  "//$starboard_path/platform_configuration",
+  "//starboard/build/config:no_pedantic_warnings",
 ]
 
 if (is_starboard) {
diff --git a/src/starboard/build/config/base_configuration.gni b/src/starboard/build/config/base_configuration.gni
index 8a80d04..6523033 100644
--- a/src/starboard/build/config/base_configuration.gni
+++ b/src/starboard/build/config/base_configuration.gni
@@ -23,10 +23,6 @@
   # value is meant to be overridden by a Starboard ABI file.
   sb_api_version = 14
 
-  # Enabling this variable enables pedantic levels of warnings for the current
-  # toolchain.
-  sb_pedantic_warnings = false
-
   # Enables embedding Cobalt as a shared library within another app. This
   # requires a 'lib' starboard implementation for the corresponding platform.
   sb_enable_lib = false
diff --git a/src/starboard/build/doc/gn_migrate_stub_to_platform.md b/src/starboard/build/doc/gn_migrate_stub_to_platform.md
new file mode 100644
index 0000000..f07e338
--- /dev/null
+++ b/src/starboard/build/doc/gn_migrate_stub_to_platform.md
@@ -0,0 +1,128 @@
+# Stub to Platform GN Migration
+
+This document outlines a step by step process for converting the stub platform's
+GN files to GN files that will be able to be built for your platform. It assumes
+you have an already working port of Starboard using GYP.
+
+## Steps to Migrate Stub Files to your platform's GN Files
+
+This is **one** way for migrating your platform from GYP to GN. The benefit of
+following this is that you can have regular checkpoints to see if your migration
+is going correctly, rather than trying to do the entire migration at once where
+it's uncertain how much progress is being made. \
+Here are the steps to do your migration:
+
+1.  [Copy stub files over to your platform and build them](#copy-stub-files-over-to-your-platform-and-build-them).
+2.  [Replace stub toolchain with your platform's toolchain](#replace-stub-toolchain-with-your-platforms-toolchain).
+3.  [Replace stub configuration with your platform's configuration](#replace-stub-configuration-with-your-platforms-configuration).
+4.  [Replace stubbed starboard_platform target sources with your platform's
+    sources](#replace-stubbed-starboardplatform-sources-with-your-platforms-sources).
+
+After each step, you should be able to build the starboard_platform target.
+For example, you would build raspi2 starboard_platform target with the following
+commands:
+```
+$gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel"'
+$ninja -C out/raspi-2gn_devel/ starboard
+```
+
+### Copy Stub Files Over to Your Platform and Build Them
+
+Here is a list of steps outlining which files to copy over and how to build
+those files:
+
+1.  Copy over files from the stub implementation to the platform folder. This
+    list gives you an example of which files to copy over for your platform.
+    This is an example for files to be copied over for your platform's port at
+    starboard/YOUR_PLATFORM
+    *   starboard/stub/BUILD.gn > starboard/YOUR_PLATFORM/BUILD.gn
+    *   starboard/stub/platform_configuration/BUILD.gn >
+        starboard/YOUR_PLATFORM/platform_configuration/BUILD.gn
+    *   starboard/stub/platform_configuration/configuration.gni >
+        starboard/YOUR_PLATFORM/platform_configuration/configuration.gni
+    *   starboard/stub/toolchain/BUILD.gn >
+        starboard/YOUR_PLATFORM/toolchain/BUILD.gn
+2.  Add your platform path to starboard/build/platforms.gni as referenced
+    [here](../migrating_gyp_to_gn.md#adding-your-platform-to-starboard)
+3.  Resolve any errors which come up for missing/incorrect file paths. Then, you
+    should be able to build your platform target with the stubbed out files
+    suggested in the above section.
+
+### Replace Stub Toolchain with Your Platform's Toolchain
+
+Follow instructions [here](../migrating_gyp_to_gn.md#migrating-a-toolchain) for
+migrating the toolchain. Resolve errors and build the starboard_platform target
+with the stubbed files.
+
+### Replace Stub Configuration with Your Platform's Configuration
+
+This involves migrating the compiler flags and build variables as referenced
+[here](../migrating_gyp_to_gn.md#migrating-a-platform).
+
+> **Highly recommended** \
+> It’s good to turn off the `treat_warnings_as_errors flag` until you can compile
+> the starboard_platform target with the platform files.
+> If this flag is not disabled you might run into a lot of
+> warnings turned errors and it might take time to solve all those errors.
+> Meanwhile you won't be in a buildable state which might make it uncertain as to
+> how much progress you are actually making.
+> For disabling the flag you can pass that as an argument to gn.
+> Here's an example for disabling the flag for raspi2:
+> ```
+> $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" treat_warnings_as_errors=false'
+> ```
+
+Resolve errors and build the starboard_platform target with the stubbed files.
+
+### Replace Stubbed starboard_platform Sources with Your Platform's Sources
+
+This involves adding files for the starboard_platform target as suggested
+[here](../migrating_gyp_to_gn.md#migrating-a-platform).
+
+While building any target, follow the recommendation above of building the
+target with `treat_warnings_as_errors=false`.
+
+Once you can build your platform files, you can remove the
+`treat_warnings_as_errors=false` flag and resolve the warning errors.
+
+## FAQ
+
+1.  **I’m getting a build error! What should I do?** \
+    Some common questions to ask yourself:
+
+    *   Is the same target building with GYP + ninja (as GN + Ninja)?
+
+        > For example if the `nplb` target is not being built by GN, check first
+        > if it can be built with GYP. If GYP cannot build it, this indicates
+        > that some flags are missing in GYP itself so it might be prudent to
+        > solve that first before migrating to GN.
+
+    *   Am I missing a config/dependency to include the missing file?
+
+        > [gn check](https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/docs/reference.md#cmd_check)
+        > can help point out missing dependencies.
+
+    *   Is the same file being included in the build by GYP?
+
+        > Add a preprocessor directive like #error "This file is included" in
+        > that file and see if GYP + Ninja prints out that error message.
+
+    *   Is the same code path being followed by GYP + ninja ?
+
+        > Use the same method as above.
+
+    *   Are the compiler flags for this file the same as in GYP ?
+
+        > To compare flags for GYP vs GN refer
+        > [section](../migrating_gyp_to_gn.md#validating-a-target). To check if
+        > the variables/flags you are compiling have changed since GYP, refer
+        > [page](../migration_changes.md).
+
+    *   Have you passed in the default arguments for your platform correctly?
+
+        > Default variables such as `target_cpu`, `target_os` and others can be
+        > overridden by passing arguments to gn while building. Here's an
+        > example of passing the default argument `target_cpu` for raspi2:
+        > ```
+        > $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" target_cpu="arm"'
+        > ```
diff --git a/src/starboard/build/doc/migrating_gyp_to_gn.md b/src/starboard/build/doc/migrating_gyp_to_gn.md
index 2333987..1a3d4bb 100644
--- a/src/starboard/build/doc/migrating_gyp_to_gn.md
+++ b/src/starboard/build/doc/migrating_gyp_to_gn.md
@@ -203,6 +203,21 @@
 comparison tool, i.e. [meld](https://meldmerge.org/). This will allow you to see
 any changes in commands, i.e. with flags or otherwise.
 
+The name of the intermediate .o, .d files is different in both cases: this
+doesn't cause any issues. Keep this in mind while comparing the ninja flags for
+GYP vs GN. Here is an example for raspi2 while compiling the same source file
+```
+starboard/common/new.cc
+```
+GYP generates:
+```
+obj/starboard/common/common.new.cc.o
+```
+GN generates:
+```
+obj/starboard/common/common/new.o
+```
+
 ### Validating a Platform
 
 Checking that an entire platform has been migrated successfully is slightly more
@@ -214,6 +229,12 @@
 You can use the same comparison method of using `format_ninja.py` as discussed
 [in the section above](#validating-a-target).
 
+### Step by Step Stub to Your Platform Migration Guide
+
+This [document](../gn_migrate_stub_to_platform.md) outlines a step by step
+process for converting the stub platform's GN files to GN files that will be
+able to be built for your platform.
+
 [cobalt_porting_guide]: https://cobalt.dev/starboard/porting.html
 [gn_check_tool]: https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/docs/reference.md#cmd_check
 [gn_doc_home]: https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/docs
diff --git a/src/starboard/build/doc/migration_changes.md b/src/starboard/build/doc/migration_changes.md
index 145ed7b..433d1a3 100644
--- a/src/starboard/build/doc/migration_changes.md
+++ b/src/starboard/build/doc/migration_changes.md
@@ -38,6 +38,8 @@
 `optimize_target_for_speed` (1) | `"//starboard/build/config:speed"`                    | Optimizations
 `compiler_flags_*_speed`        | `speed_config_path`                                   | Optimizations
 `compiler_flags_*_size`         | `size_config_path`                                    | Optimizations
+`sb_pedantic_warnings`          | `pedantic_warnings_config_path`                       | Compiler Options
+`sb_pedantic_warnings`          | `no_pedantic_warnings_config_path`                    | Compiler Options
 
 Notes:
 
@@ -53,3 +55,14 @@
     configurations for your platform by creating `config`s and pointing to the
     correct ones for `speed_config_path` and `size_config_path` in your
     platform's `platform_configuration/configuration.gni` file.
+
+*   *Compiler Options:* Cobalt compiles some targets with stricter settings
+    than others, depending on the platform. Before these targets would opt into
+    the stricter settings by settings `sb_pedantic_warnings: 1` in their
+    `variables` section. Now they will add the appropriate config like so:
+    `configs += [ "//starboard/build/config:pedantic_warnings" ]` and remove
+    the default: `configs -= [ "//starboard/build/config:no_pedantic_warnings"
+    ]`. The additional config that is used to compile these targets is
+    specified with the `pedantic_warnings_config_path` and
+    `no_pedantic_warnings_config_path` variables in your platform's
+    `platform_configuration/configuration.gni` file.
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index ad015de..f04b9e7 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -271,9 +271,6 @@
         'use_tsan':
             use_tsan,
 
-        'javascript_engine':
-            'v8',
-
         # If the path to the Starboard ABI file is able to be formatted, it will
         # be using the experimental Starboard API version of the file, i.e.
         # |sabi.SB_API_VERSION|. Otherwise, the value provided is just assumed
@@ -287,8 +284,6 @@
             config_name,
         'cobalt_fastbuild':
             0,
-        'custom_media_session_client':
-            0,
         'enable_vr':
             0,
     }
diff --git a/src/starboard/build/platforms.gni b/src/starboard/build/platforms.gni
index b3793c2..ed9af54 100644
--- a/src/starboard/build/platforms.gni
+++ b/src/starboard/build/platforms.gni
@@ -57,4 +57,8 @@
     name = "raspi-2"
     path = "starboard/raspi/2"
   },
+  {
+    name = "raspi-2-skia"
+    path = "starboard/raspi/2/skia"
+  },
 ]
diff --git a/src/starboard/contrib/linux/README.md b/src/starboard/contrib/linux/README.md
new file mode 100644
index 0000000..8c3fd94
--- /dev/null
+++ b/src/starboard/contrib/linux/README.md
@@ -0,0 +1,18 @@
+### Example Linux variant for Stadia
+
+## ./linux/stadia
+  This directory must be copied into starboard/linux/ in order to be built. Once this directory is copied into starboard/linux, you will be able to run the build commands for a Stadia configuration.
+  Sample commands:
+
+```bash
+$ cp starboard/contrib/linux/stadia starboard/linux/
+$ ./cobalt/build/gyp_cobalt linux-stadia
+$ ninja -C out/linux-stadia_devel cobalt
+```
+
+**NOTE: This target will not be able to successfully link without a shared library that implements the shared interface defined in ./clients**
+
+This build configuration was based completely on the linux-x64x11 configuration,
+and the only differences are that this configuration extends ApplicationX11 in
+order to initialize Stadia and that this configuration registers the Stadia
+extension API in SbSystemGetExtension.
diff --git a/src/starboard/contrib/linux/stadia/__init__.py b/src/starboard/contrib/linux/stadia/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/__init__.py
diff --git a/src/starboard/contrib/linux/stadia/atomic_public.h b/src/starboard/contrib/linux/stadia/atomic_public.h
new file mode 100644
index 0000000..6c07e70
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/atomic_public.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_LINUX_STADIA_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_STADIA_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif  // STARBOARD_LINUX_STADIA_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/contrib/linux/stadia/configuration_public.h b/src/starboard/contrib/linux/stadia/configuration_public.h
new file mode 100644
index 0000000..8fb5278
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/configuration_public.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Desktop x64 Linux. Other devices will have
+// specific Starboard implementations, even if they ultimately are running some
+// version of Linux.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_LINUX_STADIA_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_STADIA_CONFIGURATION_PUBLIC_H_
+
+// Include the x64x11 Linux configuration on which this config is based.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif  // STARBOARD_LINUX_STADIA_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/contrib/linux/stadia/gyp_configuration.gypi b/src/starboard/contrib/linux/stadia/gyp_configuration.gypi
new file mode 100644
index 0000000..14694c7
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/gyp_configuration.gypi
@@ -0,0 +1,43 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    'variables': {
+      'sb_evergreen_compatible': 1,
+      'sb_evergreen_compatible_libunwind': 1,
+    }
+  },
+  'target_defaults': {
+    'default_configuration': 'linux-stadia_debug',
+    'configurations': {
+      'linux-stadia_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'linux-stadia_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'linux-stadia_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'linux-stadia_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '<(DEPTH)/starboard/linux/x64x11/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/contrib/linux/stadia/gyp_configuration.py b/src/starboard/contrib/linux/stadia/gyp_configuration.py
new file mode 100644
index 0000000..c75a68c
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/gyp_configuration.py
@@ -0,0 +1,59 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux Stadia platform configuration."""
+
+from starboard.linux.shared import gyp_configuration as shared_configuration
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import touch
+
+
+class LinuxStadiaConfiguration(shared_configuration.LinuxConfiguration):
+  """Starboard Linux Stadia platform configuration."""
+
+  def __init__(self,
+               platform='linux-stadia',
+               asan_enabled_by_default=True,
+               sabi_json_path='starboard/sabi/default/sabi.json'):
+    super(LinuxStadiaConfiguration,
+          self).__init__(platform, asan_enabled_by_default, sabi_json_path)
+
+  def GetTargetToolchain(self, **kwargs):
+    return self.GetHostToolchain(**kwargs)
+
+  def GetHostToolchain(self, **kwargs):
+    environment_variables = self.GetEnvironmentVariables()
+    cc_path = environment_variables['CC']
+    cxx_path = environment_variables['CXX']
+
+    return [
+        clang.CCompiler(path=cc_path),
+        clang.CxxCompiler(path=cxx_path),
+        clang.AssemblerWithCPreprocessor(path=cc_path),
+        ar.StaticThinLinker(),
+        ar.StaticLinker(),
+        clangxx.ExecutableLinker(path=cxx_path),
+        clangxx.SharedLibraryLinker(path=cxx_path),
+        cp.Copy(),
+        touch.Stamp(),
+        bash.Shell(),
+    ]
+
+
+def CreatePlatformConfig():
+  return LinuxStadiaConfiguration(
+      sabi_json_path='starboard/sabi/x64/sysv/sabi-v{sb_api_version}.json')
diff --git a/src/starboard/contrib/linux/stadia/libraries.gypi b/src/starboard/contrib/linux/stadia/libraries.gypi
new file mode 100644
index 0000000..fbf7bd1
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/libraries.gypi
@@ -0,0 +1,19 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'includes': [
+    '<(DEPTH)/starboard/linux/x64x11/shared/libraries.gypi',
+  ],
+}
diff --git a/src/starboard/contrib/linux/stadia/main.cc b/src/starboard/contrib/linux/stadia/main.cc
new file mode 100644
index 0000000..61c90a1
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/main.cc
@@ -0,0 +1,49 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <time.h>
+
+#include "starboard/configuration.h"
+#include "starboard/contrib/stadia/x11/application_stadia_x11.h"
+#include "starboard/shared/signal/crash_signals.h"
+#include "starboard/shared/signal/suspend_signals.h"
+#include "starboard/shared/starboard/link_receiver.h"
+
+#include "third_party/crashpad/wrapper/wrapper.h"
+
+extern "C" SB_EXPORT_PLATFORM int main(int argc, char** argv) {
+  tzset();
+  starboard::shared::signal::InstallCrashSignalHandlers();
+  starboard::shared::signal::InstallSuspendSignalHandlers();
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  third_party::crashpad::wrapper::InstallCrashpadHandler();
+#endif
+
+#if SB_HAS_QUIRK(BACKTRACE_DLOPEN_BUG)
+  // Call backtrace() once to work around potential
+  // crash bugs in glibc, in dlopen()
+  SbLogRawDumpStack(3);
+#endif
+
+  starboard::contrib::stadia::x11::ApplicationStadiaX11 application;
+  int result = 0;
+  {
+    starboard::shared::starboard::LinkReceiver receiver(&application);
+    result = application.Run(argc, argv);
+  }
+  starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallCrashSignalHandlers();
+  return result;
+}
diff --git a/src/starboard/contrib/linux/stadia/starboard_platform.gyp b/src/starboard/contrib/linux/stadia/starboard_platform.gyp
new file mode 100644
index 0000000..ed04709
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/starboard_platform.gyp
@@ -0,0 +1,44 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Note, that despite the file extension ".gyp", this file is included by several
+# platform variants of linux-x64x11, like a ".gypi" file, since those platforms
+# have no need to modify this code.
+{
+  'includes': ['<(DEPTH)/starboard/linux/x64x11/shared/starboard_platform_target.gypi'],
+
+  'variables': {
+    'starboard_platform_sources': [
+      '<(DEPTH)/starboard/linux/stadia/main.cc',
+      '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
+      '<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
+      '<(DEPTH)/starboard/linux/x64x11/system_get_property_impl.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/image_decode.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/image_is_decode_supported.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/jpeg_image_decoder.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/jpeg_image_decoder.h',
+      '<(DEPTH)/starboard/shared/starboard/link_receiver.cc',
+      '<(DEPTH)/starboard/contrib/stadia/x11/application_stadia_x11.cc',
+      '<(DEPTH)/starboard/shared/x11/egl_swap_buffers.cc',
+      '<(DEPTH)/starboard/contrib/stadia/get_platform_service_api.cc',
+      '<(DEPTH)/starboard/contrib/stadia/get_platform_service_api.h',
+      '<(DEPTH)/starboard/shared/x11/player_set_bounds.cc',
+      '<(DEPTH)/starboard/shared/x11/window_create.cc',
+      '<(DEPTH)/starboard/shared/x11/window_destroy.cc',
+      '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
+      '<(DEPTH)/starboard/shared/x11/window_get_size.cc',
+      '<(DEPTH)/starboard/shared/x11/window_internal.cc',
+    ],
+  },
+}
diff --git a/src/starboard/contrib/linux/stadia/system_get_extensions.cc b/src/starboard/contrib/linux/stadia/system_get_extensions.cc
new file mode 100644
index 0000000..f5db101
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/system_get_extensions.cc
@@ -0,0 +1,47 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "cobalt/extension/configuration.h"
+#include "cobalt/extension/crash_handler.h"
+#include "starboard/common/string.h"
+#include "starboard/contrib/stadia/get_platform_service_api.h"
+#include "starboard/shared/starboard/crash_handler.h"
+#include "starboard/system.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h"
+#endif
+#include "starboard/linux/shared/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  const starboard::elf_loader::EvergreenConfig* evergreen_config =
+      starboard::elf_loader::EvergreenConfig::GetInstance();
+  if (evergreen_config != NULL &&
+      evergreen_config->custom_get_extension_ != NULL) {
+    const void* ext = evergreen_config->custom_get_extension_(name);
+    if (ext != NULL) {
+      return ext;
+    }
+  }
+#endif
+  if (strcmp(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::shared::GetConfigurationApi();
+  }
+  if (strcmp(name, kCobaltExtensionCrashHandlerName) == 0) {
+    return starboard::common::GetCrashHandlerApi();
+  }
+  if (strcmp(name, kCobaltExtensionPlatformServiceName) == 0) {
+    return starboard::contrib::stadia::GetPlatformServiceApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/contrib/linux/stadia/thread_types_public.h b/src/starboard/contrib/linux/stadia/thread_types_public.h
new file mode 100644
index 0000000..96dc485
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/thread_types_public.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_LINUX_STADIA_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_STADIA_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif  // STARBOARD_LINUX_STADIA_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/contrib/stadia/README.md b/src/starboard/contrib/stadia/README.md
new file mode 100644
index 0000000..1063f30
--- /dev/null
+++ b/src/starboard/contrib/stadia/README.md
@@ -0,0 +1,13 @@
+### Reference implementation for Stadia
+
+ This directory contains patches for Starboard required in order to use the Stadia web app from Cobalt.
+
+## ./
+
+  Files in the top-level directory offer the ability to pass binary messages back and forth between the Stadia web app and native Stadia services.
+
+## ./clients
+  Files in the clients/ subdirectory mimics the public interface of an underlying services library.
+
+## ./x11
+  This directory contains a fork of the starboard/shared/x11 code that integrates the Stadia-required changes in order to enable communication with an underlying shared library.
diff --git a/src/starboard/contrib/stadia/clients/vendor/public/stadia_export.h b/src/starboard/contrib/stadia/clients/vendor/public/stadia_export.h
new file mode 100644
index 0000000..bac4582
--- /dev/null
+++ b/src/starboard/contrib/stadia/clients/vendor/public/stadia_export.h
@@ -0,0 +1,30 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_EXPORT_H_
+#define STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_EXPORT_H_
+
+// Generates a forward declaration, a string with the function name, and a
+// typedef for the given return_type, function_name, and parameters. This macro
+// ensures consistency between the symbol that can be loaded dynamincally for
+// this function and the underlying implementation. e.g.
+// STADIA_EXPORT_FUNCTION(bool, GreaterThan, (int a, int b)) will produce the
+// following statements:
+//     * bool GreaterThan(int a, int b);
+//     * const char* kGreaterThan = "_GreaterThan";
+//     * typedef bool (*GreaterThanFunction)(int a, int b);
+#define STADIA_EXPORT_FUNCTION(return_type, name, parameters) \
+  return_type name parameters;                                \
+  const char* k##name = "_" #name;                            \
+  typedef return_type(*name##Function) parameters
+#endif  // STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_EXPORT_H_
diff --git a/src/starboard/contrib/stadia/clients/vendor/public/stadia_lifecycle.h b/src/starboard/contrib/stadia/clients/vendor/public/stadia_lifecycle.h
new file mode 100644
index 0000000..efdd0b7
--- /dev/null
+++ b/src/starboard/contrib/stadia/clients/vendor/public/stadia_lifecycle.h
@@ -0,0 +1,26 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_LIFECYCLE_H_
+#define STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_LIFECYCLE_H_
+
+#include "starboard/contrib/stadia/clients/vendor/public/stadia_export.h"
+
+extern "C" {
+
+// An initialization call for Stadia Plugins that should be called when the
+// application window is created.
+STADIA_EXPORT_FUNCTION(void, StadiaInitialize, ());
+}
+#endif  // STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_LIFECYCLE_H_
diff --git a/src/starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h b/src/starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h
new file mode 100644
index 0000000..4b5e699
--- /dev/null
+++ b/src/starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h
@@ -0,0 +1,50 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_PLUGIN_H_
+#define STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_PLUGIN_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "starboard/contrib/stadia/clients/vendor/public/stadia_export.h"
+
+// An interface header that allows to interact with Stadia plugins.
+
+extern "C" {
+typedef struct StadiaPlugin StadiaPlugin;
+
+// Callback that the StadiaPlugin will invoke with data that it needs to pass
+// back to the client.
+typedef void (*StadiaPluginReceiveFromCallback)(const uint8_t* message,
+                                                size_t length,
+                                                void* user_data);
+
+STADIA_EXPORT_FUNCTION(bool, StadiaPluginHas, (const char* channel));
+
+STADIA_EXPORT_FUNCTION(StadiaPlugin*,
+                       StadiaPluginOpen,
+                       (const char* channel,
+                        StadiaPluginReceiveFromCallback callback,
+                        void* user_data));
+
+STADIA_EXPORT_FUNCTION(void,
+                       StadiaPluginSendTo,
+                       (StadiaPlugin* plugin,
+                        const char* message,
+                        size_t length));
+
+STADIA_EXPORT_FUNCTION(void, StadiaPluginClose, (StadiaPlugin* plugin));
+}
+#endif  // STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_PLUGIN_H_
diff --git a/src/starboard/contrib/stadia/get_platform_service_api.cc b/src/starboard/contrib/stadia/get_platform_service_api.cc
new file mode 100644
index 0000000..d4dc0ac
--- /dev/null
+++ b/src/starboard/contrib/stadia/get_platform_service_api.cc
@@ -0,0 +1,144 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/contrib/stadia/get_platform_service_api.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "cobalt/extension/platform_service.h"
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/common/string.h"
+#include "starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h"
+#include "starboard/event.h"
+#include "starboard/memory.h"
+#include "starboard/window.h"
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+
+namespace {
+
+namespace {
+// Encapsulates a channel name and data to be sent on that channel.
+struct StadiaPluginSendToData {
+  StadiaPlugin* plugin;
+  std::vector<uint8_t> data;
+  StadiaPluginSendToData(StadiaPlugin* plugin, const std::vector<uint8_t>& data)
+      : plugin(plugin), data(data) {}
+};
+}  // namespace
+
+bool HasPlatformService(const char* name) {
+  return StadiaPluginHas(name);
+}
+
+CobaltExtensionPlatformService OpenPlatformService(
+    void* context,
+    const char* name_c_str,
+    ReceiveMessageCallback receive_callback) {
+  // name_c_str is allocated by Cobalt, but must be freed here.
+  std::unique_ptr<const char[]> service_name(name_c_str);
+
+  SB_DCHECK(context);
+  SB_LOG(INFO) << "Open " << service_name.get();
+
+  if (!StadiaPluginHas(&service_name[0])) {
+    SB_LOG(ERROR) << "Cannot open service. Service not found. "
+                  << service_name.get();
+    return kCobaltExtensionPlatformServiceInvalid;
+  }
+
+  auto std_callback = std::make_unique<
+      std::function<void(const std::vector<uint8_t>& message)>>(
+      [receive_callback, context](const std::vector<uint8_t>& message) -> void {
+
+        receive_callback(context, message.data(), message.size());
+
+      });
+
+  StadiaPlugin* plugin = StadiaPluginOpen(
+      name_c_str,
+
+      [](const uint8_t* const message, size_t length, void* user_data) -> void {
+
+        auto callback = static_cast<
+            const std::function<void(const std::vector<uint8_t>& message)>*>(
+            user_data);
+        std::vector<uint8_t> data(message, message + length);
+        (*callback)(data);
+      },
+      std_callback.release());
+
+  return reinterpret_cast<CobaltExtensionPlatformService>(plugin);
+}
+
+void ClosePlatformService(CobaltExtensionPlatformService service) {
+  SB_DCHECK(service);
+  auto plugin = reinterpret_cast<StadiaPlugin*>(service);
+  StadiaPluginClose(plugin);
+}
+
+void* SendToPlatformService(CobaltExtensionPlatformService service,
+                            void* data,
+                            uint64_t length,
+                            uint64_t* output_length,
+                            bool* invalid_state) {
+  SB_DCHECK(service);
+  SB_DCHECK(data);
+  SB_DCHECK(output_length);
+  SB_DCHECK(invalid_state);
+
+  auto plugin = reinterpret_cast<StadiaPlugin*>(service);
+  std::vector<uint8_t> buffer(static_cast<uint8_t*>(data),
+                              static_cast<uint8_t*>(data) + length);
+
+  auto send_to_plugin_data =
+      std::make_unique<StadiaPluginSendToData>(plugin, buffer);
+
+  // Use the main thread.
+  SbEventSchedule(
+      [](void* context) -> void {
+        auto internal_data = std::unique_ptr<StadiaPluginSendToData>(
+            static_cast<StadiaPluginSendToData*>(context));
+
+        std::vector<uint8_t> plugin_data(internal_data->data);
+        StadiaPluginSendTo(internal_data->plugin,
+                           reinterpret_cast<const char*>(plugin_data.data()),
+                           plugin_data.size());
+      },
+      send_to_plugin_data.release(), 0);
+
+  std::vector<uint8_t> response = std::vector<uint8_t>();
+  return response.data();
+}
+
+const CobaltExtensionPlatformServiceApi kPlatformServiceApi = {
+    kCobaltExtensionPlatformServiceName,
+    // API version that's implemented.
+    1, &HasPlatformService, &OpenPlatformService, &ClosePlatformService,
+    &SendToPlatformService,
+};
+}  // namespace
+
+const void* GetPlatformServiceApi() {
+  return &kPlatformServiceApi;
+}
+
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
diff --git a/src/starboard/contrib/stadia/get_platform_service_api.h b/src/starboard/contrib/stadia/get_platform_service_api.h
new file mode 100644
index 0000000..5382ad1
--- /dev/null
+++ b/src/starboard/contrib/stadia/get_platform_service_api.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_CONTRIB_STADIA_GET_PLATFORM_SERVICE_API_H_
+#define STARBOARD_CONTRIB_STADIA_GET_PLATFORM_SERVICE_API_H_
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+
+const void* GetPlatformServiceApi();
+
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
+#endif  // STARBOARD_CONTRIB_STADIA_GET_PLATFORM_SERVICE_API_H_
diff --git a/src/starboard/contrib/stadia/x11/application_stadia_x11.cc b/src/starboard/contrib/stadia/x11/application_stadia_x11.cc
new file mode 100644
index 0000000..fb2fc76
--- /dev/null
+++ b/src/starboard/contrib/stadia/x11/application_stadia_x11.cc
@@ -0,0 +1,40 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/contrib/stadia/x11/application_stadia_x11.h"
+
+#include "starboard/contrib/stadia/services/vendor/linux/public/stadia_lifecycle.h"
+#include "starboard/shared/x11/application_x11.cc"
+#include "starboard/shared/x11/window_internal.h"
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+namespace x11 {
+
+using ::starboard::shared::dev_input::DevInput;
+
+constexpr char kAppId[] = "com.google.stadia.linux";
+
+SbWindow ApplicationStadiaX11::CreateWindow(const SbWindowOptions* options) {
+  SbWindow window =
+      starboard::shared::x11::ApplicationX11::CreateWindow(options);
+  StadiaInitialize();
+  return window;
+}
+
+}  // namespace x11
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
diff --git a/src/starboard/contrib/stadia/x11/application_stadia_x11.h b/src/starboard/contrib/stadia/x11/application_stadia_x11.h
new file mode 100644
index 0000000..3d40274
--- /dev/null
+++ b/src/starboard/contrib/stadia/x11/application_stadia_x11.h
@@ -0,0 +1,40 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_CONTRIB_STADIA_X11_APPLICATION_STADIA_X11_H_
+#define STARBOARD_CONTRIB_STADIA_X11_APPLICATION_STADIA_X11_H_
+
+#include "starboard/shared/x11/application_x11.h"
+#include "starboard/window.h"
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+namespace x11 {
+
+// This application engine combines the generic queue with the X11 event queue.
+class ApplicationStadiaX11 : public starboard::shared::x11::ApplicationX11 {
+ public:
+  ApplicationStadiaX11();
+  ~ApplicationStadiaX11();
+
+  SbWindow CreateWindow(const SbWindowOptions* options) override;
+};
+
+}  // namespace x11
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
+
+#endif  // STARBOARD_CONRTIB_STADIA_X11_APPLICATION_STADIA_X11_H_
diff --git a/src/starboard/doc/evergreen/cobalt_evergreen_lite.md b/src/starboard/doc/evergreen/cobalt_evergreen_lite.md
index 1dcbbc1..06dacf9 100644
--- a/src/starboard/doc/evergreen/cobalt_evergreen_lite.md
+++ b/src/starboard/doc/evergreen/cobalt_evergreen_lite.md
@@ -75,7 +75,7 @@
 
 ## How is Evergreen different from porting Cobalt previously?
 
-Same as the [Evergreen full doc](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/doc/evergreen/cobalt_evergreen_overview.md).
+Same as the [Evergreen full doc](cobalt_evergreen_overview.md).
 
 ## Building Cobalt Evergreen Components
 
diff --git a/src/starboard/doc/evergreen/cobalt_evergreen_overview.md b/src/starboard/doc/evergreen/cobalt_evergreen_overview.md
index 75b6940..51130f1 100644
--- a/src/starboard/doc/evergreen/cobalt_evergreen_overview.md
+++ b/src/starboard/doc/evergreen/cobalt_evergreen_overview.md
@@ -408,7 +408,7 @@
 
 The number of installation slots is directly controlled using
 `kMaxNumInstallations`, defined in
-[loader\_app.cc](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/loader_app/loader_app.cc).
+[loader\_app.cc](../../loader_app/loader_app.cc).
 
 It is worth noting that all slot configurations specify that the first
 installation slot (`SLOT_0`) will always be the read-only factory system image.
@@ -549,7 +549,7 @@
  suspending if there is a pending restart.
 
 Please see
-[`suspend_signals.cc`](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/shared/signal/suspend_signals.cc)
+[`suspend_signals.cc`](../../shared/signal/suspend_signals.cc)
 for an example.
 
 ### Multi-App Support
@@ -644,7 +644,7 @@
 ```
 
 Please see
-[`loader_app_switches.cc`](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/loader_app/loader_app.cc)
+[`loader_app_switches.cc`](../../loader_app/loader_app.cc)
 for full list of available command-line flags.
 
 ### Platform Security
diff --git a/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md b/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md
index b72d0f7..a9fec25 100644
--- a/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md
+++ b/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md
@@ -53,8 +53,15 @@
 $ cp -r out/evergreen-arm-hardfp-sbversion-12_qa/lib   $COEG_PATH/content/app/cobalt/
 $ cp -r out/evergreen-arm-hardfp-sbversion-12_qa/content   $COEG_PATH/content/app/cobalt/
 
-## Download the manifest file
-$ curl https://storage.googleapis.com/evergreen_public/latest/manifest.json -o $COEG_PATH/content/app/cobalt/manifest.json
+## Create a file named manifest.json with the following content, and put it under $COEG_PATH/content/app/cobalt/
+$ cat > $COEG_PATH/content/app/cobalt/manifest.json <<EOF
+{
+        "manifest_version": 2,
+        "name": "Cobalt",
+        "description": "Cobalt",
+        "version": "1.0.0"
+}
+EOF
 ```
 
 ## Deployment instructions
diff --git a/src/starboard/doc/starboard_abi.md b/src/starboard/doc/starboard_abi.md
index f97a788..bd0d309 100644
--- a/src/starboard/doc/starboard_abi.md
+++ b/src/starboard/doc/starboard_abi.md
@@ -36,18 +36,18 @@
 
 With the Starboard ABI being the source of truth for all things architecture
 related, each platform must now include a Starboard ABI file in its build (see
-[//starboard/sabi](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/sabi)
+[//starboard/sabi](../sabi)
 for examples). Starboard ABI files are JSON, and should all contain identical
 keys with the values being appropriate for the architecture. Each platform must
 override the new
-[GetPathToSabiJsonFile](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/build/platform_configuration.py##339)
+[GetPathToSabiJsonFile](../build/platform_configuration.py##339)
 method in its platform configuration to return the path to the desired Starboard
 ABI file (e.g.
-[//starboard/linux/shared/gyp\_configuration.py](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/linux/shared/gyp_configuration.py)).
+[//starboard/linux/shared/gyp\_configuration.py](../linux/shared/gyp_configuration.py)).
 By default, an empty and invalid Starboard ABI file is provided.
 
 Additionally, all platforms must include the
-[sabi.gypi](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/sabi/sabi.gypi)
+[sabi.gypi](../sabi/sabi.gypi)
 in their build configuration. This file will consume the specified Starboard ABI
 file, resulting in the creation of a set of GYP variables and preprocessor
 macros. The preprocessor macros are passed directly to the compiler and replace
@@ -56,7 +56,7 @@
 The newly defined GYP variables need to be transformed into toolchain specific
 flags; these flags are what actually makes the build result in a binary for the
 desired architecture. These flags will, in most cases, be identical to the flags
-already being used for building. 
+already being used for building.
 
 The process outlined above is shown in the diagram below.
 
@@ -106,14 +106,14 @@
 
 1.  When configuring your build, the Starboard ABI file that was specified will
     have its values sanity checked against a provided
-    [schema](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/sabi/sabi.schema.json).
+    [schema](../sabi/sabi.schema.json).
 1.  When building, a number of static assertions will assert correctness of a
     number of features generated from the Starboard ABI file against the
     features of the binary.
 1.  The NPLB test suite has been expanded to include [additional
-    tests](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/nplb/sabi/)
+    tests](../nplb/sabi/)
     capable of verifying the remaining features of the binary.
 
 Finally, binaries produced by the Cobalt team for your architecture, including
 NPLB, will be made available to ensure end-to-end correctness of the produced
-binaries.
\ No newline at end of file
+binaries.
diff --git a/src/starboard/elf_loader/elf_loader.gyp b/src/starboard/elf_loader/elf_loader.gyp
index 27cdce7..b93ed1d 100644
--- a/src/starboard/elf_loader/elf_loader.gyp
+++ b/src/starboard/elf_loader/elf_loader.gyp
@@ -142,6 +142,7 @@
       ],
       'dependencies': [
         'elf_loader_sys',
+        '<(DEPTH)/starboard/elf_loader/sabi_string.gyp:sabi_string',
         '<(DEPTH)/starboard/starboard.gyp:starboard',
       ],
       'sources': [
diff --git a/src/starboard/evergreen/shared/gyp_configuration.gypi b/src/starboard/evergreen/shared/gyp_configuration.gypi
index 67ebcf1..dc8fbed 100644
--- a/src/starboard/evergreen/shared/gyp_configuration.gypi
+++ b/src/starboard/evergreen/shared/gyp_configuration.gypi
@@ -22,7 +22,6 @@
 
     'enable_vr': 0,
     'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/default_options_starboard.gyp:default_options',
-    'javascript_engine': 'v8',
 
     'cobalt_font_package': 'empty',
 
diff --git a/src/starboard/evergreen/shared/gyp_configuration.py b/src/starboard/evergreen/shared/gyp_configuration.py
index 526b209..4d92efe 100644
--- a/src/starboard/evergreen/shared/gyp_configuration.py
+++ b/src/starboard/evergreen/shared/gyp_configuration.py
@@ -45,8 +45,6 @@
     variables = super(EvergreenConfiguration, self).GetVariables(
         config_name, use_clang=1)
     variables.update({
-        'javascript_engine':
-            'v8',
         'cobalt_repo_root':
             paths.REPOSITORY_ROOT,
         'include_path_platform_deploy_gypi':
diff --git a/src/starboard/evergreen/testing/raspi/start_cobalt.sh b/src/starboard/evergreen/testing/raspi/start_cobalt.sh
index df4efb4..a1ea738 100755
--- a/src/starboard/evergreen/testing/raspi/start_cobalt.sh
+++ b/src/starboard/evergreen/testing/raspi/start_cobalt.sh
@@ -30,7 +30,7 @@
 
   URL="${1}"
   LOG="${2}"
-  __LOADER="${3}"
+  declare -n loader_pid_ref=$3
   ARGS="${4}"
 
   stop_cobalt
@@ -46,7 +46,7 @@
 
   eval "${SSH}\"/home/pi/coeg/loader_app --url=\"\"${URL}\"\" ${ARGS} \" 2>&1 | tee \"${LOG_PATH}/${LOG}\"" &
 
-  eval $__LOADER=$!
+  loader_pid_ref=$!
 
-  log "info" " Cobalt process ID is ${__LOADER}"
+  log "info" " Cobalt process ID is ${loader_pid_ref}"
 }
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index 4e4a96c..b408b63 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -45,8 +45,6 @@
     variables = super(LinuxConfiguration, self).GetVariables(
         config_name, use_clang=1)
     variables.update({
-        'javascript_engine':
-            'v8',
         'include_path_platform_deploy_gypi':
             'starboard/linux/shared/platform_deploy.gypi',
     })
diff --git a/src/starboard/linux/shared/platform_configuration/BUILD.gn b/src/starboard/linux/shared/platform_configuration/BUILD.gn
index e6bfc37..9920863 100644
--- a/src/starboard/linux/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/linux/shared/platform_configuration/BUILD.gn
@@ -99,33 +99,6 @@
     "-Wl,-gc-sections",
   ]
 
-  if (sb_pedantic_warnings) {
-    cflags += [
-      "-Wall",
-      "-Wextra",
-      "-Wunreachable-code",
-    ]
-  } else {
-    cflags += [
-      # 'this' pointer cannot be NULL...pointer may be assumed
-      # to always convert to true.
-      "-Wno-undefined-bool-conversion",
-
-      # Skia doesn't use overrides.
-      "-Wno-inconsistent-missing-override",
-
-      # Do not warn for implicit type conversions that may change a value.
-      "-Wno-conversion",
-
-      # shifting a negative signed value is undefined
-      "-Wno-shift-negative-value",
-
-      # Width of bit-field exceeds width of its type- value will be truncated
-      "-Wno-bitfield-width",
-      "-Wno-undefined-var-template",
-    ]
-  }
-
   if (use_asan) {
     cflags += [
       "-fsanitize=address",
@@ -212,3 +185,32 @@
     "-fdata-sections",
   ]
 }
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+  ]
+}
+
+config("no_pedantic_warnings") {
+  cflags = [
+    # 'this' pointer cannot be NULL...pointer may be assumed
+    # to always convert to true.
+    "-Wno-undefined-bool-conversion",
+
+    # Skia doesn't use overrides.
+    "-Wno-inconsistent-missing-override",
+
+    # Do not warn for implicit type conversions that may change a value.
+    "-Wno-conversion",
+
+    # shifting a negative signed value is undefined
+    "-Wno-shift-negative-value",
+
+    # Width of bit-field exceeds width of its type- value will be truncated
+    "-Wno-bitfield-width",
+    "-Wno-undefined-var-template",
+  ]
+}
diff --git a/src/starboard/linux/shared/platform_configuration/configuration.gni b/src/starboard/linux/shared/platform_configuration/configuration.gni
index 3618b00..eda9b87 100644
--- a/src/starboard/linux/shared/platform_configuration/configuration.gni
+++ b/src/starboard/linux/shared/platform_configuration/configuration.gni
@@ -27,4 +27,9 @@
 speed_config_path = "//starboard/linux/shared/platform_configuration:speed"
 size_config_path = "//starboard/linux/shared/platform_configuration:size"
 
+pedantic_warnings_config_path =
+    "//starboard/linux/shared/platform_configuration:pedantic_warnings"
+no_pedantic_warnings_config_path =
+    "//starboard/linux/shared/platform_configuration:no_pedantic_warnings"
+
 sb_widevine_platform = "linux"
diff --git a/src/starboard/linux/x64x11/shared/starboard_platform.gypi b/src/starboard/linux/x64x11/shared/starboard_platform.gypi
index 6e05ca9..a635975 100644
--- a/src/starboard/linux/x64x11/shared/starboard_platform.gypi
+++ b/src/starboard/linux/x64x11/shared/starboard_platform.gypi
@@ -37,7 +37,7 @@
 
     'starboard_platform_dependencies': [
       '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
-      '<(DEPTH)/third_party/libjpeg/libjpeg.gyp:libjpeg',
+      '<(DEPTH)/third_party/libjpeg-turbo/libjpeg.gyp:libjpeg',
     ],
 
     # Exclude shared implementations specified by the included .gypi if this
diff --git a/src/starboard/nplb/drm_helpers.h b/src/starboard/nplb/drm_helpers.h
index be29b72..2b43c12 100644
--- a/src/starboard/nplb/drm_helpers.h
+++ b/src/starboard/nplb/drm_helpers.h
@@ -132,75 +132,15 @@
     0xbc, 0x6a, 0x6b, 0xed, 0x13, 0xfb, 0x0d, 0x49, 0xd3, 0x8a, 0x45, 0xeb,
     0x87, 0xa5, 0xf4};
 
+// Widevine-specific CENC Initialization data.
+// https://www.w3.org/TR/eme-stream-mp4/
+// https://www.w3.org/TR/eme-initdata-cenc/#common-system
 static constexpr uint8_t kCencInitData[] = {
-    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00,
-    0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
-    0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x14, 0x08, 0x01, 0x12, 0x10,
-    0x31, 0xfd, 0x5b, 0x66, 0x19, 0xfc, 0x5e, 0xad, 0x86, 0x7c, 0xff, 0xb5,
-    0x84, 0xed, 0x4c, 0x19, 0x00, 0x00, 0x02, 0xf4, 0x70, 0x73, 0x73, 0x68,
-    0x00, 0x00, 0x00, 0x00, 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86,
-    0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95, 0x00, 0x00, 0x02, 0xd4,
-    0xd4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xca, 0x02, 0x3c, 0x00,
-    0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00,
-    0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x20, 0x00, 0x78, 0x00, 0x6d, 0x00,
-    0x6c, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x68, 0x00,
-    0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00,
-    0x73, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00,
-    0x73, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00,
-    0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00,
-    0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x52, 0x00,
-    0x4d, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00,
-    0x2f, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x6c, 0x00,
-    0x61, 0x00, 0x79, 0x00, 0x52, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00,
-    0x79, 0x00, 0x48, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00,
-    0x72, 0x00, 0x22, 0x00, 0x20, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00,
-    0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3d, 0x00, 0x22, 0x00,
-    0x34, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00,
-    0x30, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x44, 0x00, 0x41, 0x00,
-    0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x52, 0x00,
-    0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00,
-    0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00,
-    0x45, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x3e, 0x00,
-    0x31, 0x00, 0x36, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x45, 0x00,
-    0x59, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x3c, 0x00,
-    0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00,
-    0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x54, 0x00, 0x52, 0x00,
-    0x3c, 0x00, 0x2f, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00,
-    0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x52, 0x00,
-    0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00,
-    0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00,
-    0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x5a, 0x00, 0x6c, 0x00, 0x76, 0x00,
-    0x39, 0x00, 0x4d, 0x00, 0x66, 0x00, 0x77, 0x00, 0x5a, 0x00, 0x72, 0x00,
-    0x56, 0x00, 0x36, 0x00, 0x47, 0x00, 0x66, 0x00, 0x50, 0x00, 0x2b, 0x00,
-    0x31, 0x00, 0x68, 0x00, 0x4f, 0x00, 0x31, 0x00, 0x4d, 0x00, 0x47, 0x00,
-    0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00,
-    0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x43, 0x00, 0x48, 0x00,
-    0x45, 0x00, 0x43, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x55, 0x00, 0x4d, 0x00,
-    0x3e, 0x00, 0x4a, 0x00, 0x58, 0x00, 0x46, 0x00, 0x36, 0x00, 0x57, 0x00,
-    0x38, 0x00, 0x41, 0x00, 0x64, 0x00, 0x51, 0x00, 0x2b, 0x00, 0x49, 0x00,
-    0x3d, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x43, 0x00, 0x48, 0x00, 0x45, 0x00,
-    0x43, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x55, 0x00, 0x4d, 0x00, 0x3e, 0x00,
-    0x3c, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00,
-    0x4c, 0x00, 0x3e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00,
-    0x73, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x77, 0x00, 0x77, 0x00,
-    0x77, 0x00, 0x2e, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00,
-    0x75, 0x00, 0x62, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00,
-    0x6d, 0x00, 0x2f, 0x00, 0x61, 0x00, 0x70, 0x00, 0x69, 0x00, 0x2f, 0x00,
-    0x64, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x70, 0x00, 0x6c, 0x00,
-    0x61, 0x00, 0x79, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00,
-    0x79, 0x00, 0x3f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00,
-    0x63, 0x00, 0x65, 0x00, 0x3d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x55, 0x00,
-    0x54, 0x00, 0x55, 0x00, 0x42, 0x00, 0x45, 0x00, 0x26, 0x00, 0x61, 0x00,
-    0x6d, 0x00, 0x70, 0x00, 0x3b, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00,
-    0x65, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x3d, 0x00,
-    0x39, 0x00, 0x33, 0x00, 0x39, 0x00, 0x35, 0x00, 0x62, 0x00, 0x34, 0x00,
-    0x36, 0x00, 0x64, 0x00, 0x37, 0x00, 0x64, 0x00, 0x63, 0x00, 0x64, 0x00,
-    0x37, 0x00, 0x38, 0x00, 0x38, 0x00, 0x66, 0x00, 0x3c, 0x00, 0x2f, 0x00,
-    0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00,
-    0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00,
-    0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x57, 0x00, 0x52, 0x00,
-    0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00,
-    0x52, 0x00, 0x3e, 0x00, 0x00};
+    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00,
+    0x00, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
+    0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x14, 0x08,
+    0x01, 0x12, 0x10, 0x31, 0xfd, 0x5b, 0x66, 0x19, 0xfc, 0x5e, 0xad,
+    0x86, 0x7c, 0xff, 0xb5, 0x84, 0xed, 0x4c, 0x19};
 
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn b/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
index b6efbad..05753b8 100644
--- a/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
@@ -18,6 +18,7 @@
   sources = [
     "//starboard/common/test_main.cc",
     "checks.h",
+    "crashpad_config_test.cc",
     "executable_memory_test.cc",
     "fonts_test.cc",
     "sabi_test.cc",
diff --git a/src/starboard/raspi/2/BUILD.gn b/src/starboard/raspi/2/BUILD.gn
index 3cf94e9..92dbb16 100644
--- a/src/starboard/raspi/2/BUILD.gn
+++ b/src/starboard/raspi/2/BUILD.gn
@@ -14,7 +14,11 @@
 
 static_library("starboard_platform") {
   check_includes = false
+  sources = [
+    "//starboard/raspi/shared/configuration.cc",
+    "//starboard/raspi/shared/configuration.h",
+    "//starboard/raspi/shared/system_get_extensions.cc",
+  ]
   configs += [ "//starboard/build/config:starboard_implementation" ]
-
   public_deps = [ "//starboard/raspi/shared:starboard_platform" ]
 }
diff --git a/src/starboard/raspi/2/gyp_configuration.py b/src/starboard/raspi/2/gyp_configuration.py
index 705bc44..7cf32e4 100644
--- a/src/starboard/raspi/2/gyp_configuration.py
+++ b/src/starboard/raspi/2/gyp_configuration.py
@@ -26,9 +26,6 @@
 
   def GetVariables(self, config_name):
     variables = super(Raspi2PlatformConfig, self).GetVariables(config_name)
-    variables.update({
-        'javascript_engine': 'v8',
-    })
     return variables
 
 
diff --git a/src/starboard/raspi/2/skia/BUILD.gn b/src/starboard/raspi/2/skia/BUILD.gn
new file mode 100644
index 0000000..bcb5b92
--- /dev/null
+++ b/src/starboard/raspi/2/skia/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+static_library("starboard_platform") {
+  check_includes = false
+  sources = [
+    "//starboard/raspi/2/skia/configuration.cc",
+    "//starboard/raspi/2/skia/configuration.h",
+    "//starboard/raspi/2/skia/system_get_extensions.cc",
+  ]
+  configs += [ "//starboard/build/config:starboard_implementation" ]
+  public_deps = [ "//starboard/raspi/shared:starboard_platform" ]
+}
diff --git a/src/starboard/raspi/2/skia/platform_configuration/BUILD.gn b/src/starboard/raspi/2/skia/platform_configuration/BUILD.gn
new file mode 100644
index 0000000..17a93b7
--- /dev/null
+++ b/src/starboard/raspi/2/skia/platform_configuration/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("platform_configuration") {
+  configs = [
+    "//starboard/raspi/2/platform_configuration",
+  ]
+}
diff --git a/src/starboard/raspi/2/skia/platform_configuration/configuration.gni b/src/starboard/raspi/2/skia/platform_configuration/configuration.gni
new file mode 100644
index 0000000..4cd4761
--- /dev/null
+++ b/src/starboard/raspi/2/skia/platform_configuration/configuration.gni
@@ -0,0 +1,15 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/raspi/shared/platform_configuration/configuration.gni")
diff --git a/src/starboard/raspi/2/skia/toolchain/BUILD.gn b/src/starboard/raspi/2/skia/toolchain/BUILD.gn
new file mode 100644
index 0000000..653ad00
--- /dev/null
+++ b/src/starboard/raspi/2/skia/toolchain/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
+
+clang_toolchain("host") {
+  clang_base_path = raspi_clang_base_path
+}
+
+gcc_toolchain("target") {
+  cc = "$gcc_toolchain_cc"
+  cxx = "$gcc_toolchain_cxx"
+  ld = cxx
+
+  ar = "$gcc_toolchain_ar"
+  strip = "$gcc_toolchain_strip"
+
+  toolchain_args = {
+    is_clang = false
+  }
+}
diff --git a/src/starboard/raspi/2/toolchain/BUILD.gn b/src/starboard/raspi/2/toolchain/BUILD.gn
index 57a35ca..7e872a6 100644
--- a/src/starboard/raspi/2/toolchain/BUILD.gn
+++ b/src/starboard/raspi/2/toolchain/BUILD.gn
@@ -13,23 +13,20 @@
 # limitations under the License.
 
 import("//build/toolchain/gcc_toolchain.gni")
-
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-raspi_toolchain_path = "$_home_dir/raspi_tools/tools/arm-bcm2708/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin"
+import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = raspi_clang_base_path
 }
 
 gcc_toolchain("target") {
-  cc = "$raspi_toolchain_path/arm-linux-gnueabihf-gcc"
-  cxx = "$raspi_toolchain_path/arm-linux-gnueabihf-g++"
+  cc = "$gcc_toolchain_cc"
+  cxx = "$gcc_toolchain_cxx"
   ld = cxx
 
   # We use whatever 'ar' resolves to in gyp.
-  ar = "ar"
-  strip = "$raspi_toolchain_path/arm-linux-gnueabihf-strip"
+  ar = "$gcc_toolchain_ar"
+  strip = "$gcc_toolchain_strip"
 
   toolchain_args = {
     is_clang = false
diff --git a/src/starboard/raspi/shared/BUILD.gn b/src/starboard/raspi/shared/BUILD.gn
index 37c97e7..3828973 100644
--- a/src/starboard/raspi/shared/BUILD.gn
+++ b/src/starboard/raspi/shared/BUILD.gn
@@ -18,6 +18,8 @@
 }
 
 static_library("starboard_platform_sources") {
+  check_includes = false
+
   sources = [
     "//starboard/linux/shared/atomic_public.h",
     "//starboard/linux/shared/configuration_constants.cc",
@@ -31,8 +33,6 @@
     "//starboard/linux/shared/system_has_capability.cc",
     "//starboard/raspi/shared/application_dispmanx.cc",
     "//starboard/raspi/shared/audio_sink_type_dispatcher.cc",
-    "//starboard/raspi/shared/configuration.cc",
-    "//starboard/raspi/shared/configuration.h",
     "//starboard/raspi/shared/dispmanx_util.cc",
     "//starboard/raspi/shared/dispmanx_util.h",
     "//starboard/raspi/shared/graphics.cc",
@@ -62,7 +62,6 @@
     "//starboard/raspi/shared/open_max/video_decoder.h",
     "//starboard/raspi/shared/player_components_factory.cc",
     "//starboard/raspi/shared/system_get_device_type.cc",
-    "//starboard/raspi/shared/system_get_extensions.cc",
     "//starboard/raspi/shared/system_get_property.cc",
     "//starboard/raspi/shared/system_gles2.cc",
     "//starboard/raspi/shared/thread_create_priority.cc",
@@ -376,7 +375,11 @@
 
   sources += common_player_sources
 
-  configs += [ "//starboard/build/config:starboard_implementation" ]
+  configs += [
+    "//starboard/build/config:pedantic_warnings",
+    "//starboard/build/config:starboard_implementation",
+  ]
+  configs -= [ "//starboard/build/config:no_pedantic_warnings" ]
 
   public_deps = [
     ":starboard_base_symbolize",
@@ -386,9 +389,7 @@
     "//starboard/shared/starboard/media:media_util",
     "//starboard/shared/starboard/player/filter:filter_based_player_sources",
   ]
-  if (sb_is_evergreen_compatible) {
-    public_deps += [ "//starboard/elf_loader:evergreen_config" ]
-  }
+
   if (sb_is_evergreen_compatible && !sb_evergreen_compatible_enable_lite) {
     public_deps += [ "//starboard/loader_app:pending_restart" ]
   }
@@ -397,9 +398,17 @@
     "//third_party/libevent",
     "//third_party/opus",
   ]
+
   if (sb_evergreen_compatible_use_libunwind) {
     deps += [ "//third_party/llvm-project/libunwind:unwind_starboard" ]
   }
+
+  if (sb_is_evergreen_compatible) {
+    public_deps += [ "//starboard/elf_loader:evergreen_config" ]
+    deps += [ "//third_party/crashpad/wrapper" ]
+  } else {
+    deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+  }
 }
 
 static_library("starboard_base_symbolize") {
diff --git a/src/starboard/raspi/shared/platform_configuration/BUILD.gn b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
index fbbab79..98d7b28 100644
--- a/src/starboard/raspi/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
@@ -125,29 +125,6 @@
     "-std=gnu++14",
     "-Wno-literal-suffix",
   ]
-
-  if (sb_pedantic_warnings) {
-    cflags += [
-      "-Wall",
-      "-Wextra",
-      "-Wunreachable-code",
-
-      # Raspi toolchain is based off an old version of gcc, which
-      # falsely flags some code.  That same code does not warn with gcc 6.3.
-      # This decision should be revisited after raspi toolchain is upgraded.
-      "-Wno-maybe-uninitialized",
-
-      #TODO: Renable -Werror after fixing all warnings.
-      #"-Werror",
-      "-Wno-expansion-to-defined",
-      "-Wno-implicit-fallthrough",
-    ]
-  } else {
-    cflags += [
-      # Do not warn for implicit type conversions that may change a value.
-      "-Wno-conversion",
-    ]
-  }
 }
 
 config("platform_configuration") {
@@ -193,3 +170,28 @@
     "-fdata-sections",
   ]
 }
+
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+
+    # Raspi toolchain is based off an old version of gcc, which
+    # falsely flags some code.  That same code does not warn with gcc 6.3.
+    # This decision should be revisited after raspi toolchain is upgraded.
+    "-Wno-maybe-uninitialized",
+
+    #TODO: Renable -Werror after fixing all warnings.
+    #"-Werror",
+    "-Wno-expansion-to-defined",
+    "-Wno-implicit-fallthrough",
+  ]
+}
+
+config("no_pedantic_warnings") {
+  cflags = [
+    # Do not warn for implicit type conversions that may change a value.
+    "-Wno-conversion",
+  ]
+}
diff --git a/src/starboard/raspi/shared/platform_configuration/configuration.gni b/src/starboard/raspi/shared/platform_configuration/configuration.gni
index c96fc14..fd4a3ce 100644
--- a/src/starboard/raspi/shared/platform_configuration/configuration.gni
+++ b/src/starboard/raspi/shared/platform_configuration/configuration.gni
@@ -14,7 +14,13 @@
 
 import("//starboard/build/config/base_configuration.gni")
 
+arm_float_abi = "hard"
 sb_pedantic_warnings = true
 sb_static_contents_output_data_dir = "$root_out_dir/content"
 
+pedantic_warnings_config_path =
+    "//starboard/raspi/shared/platform_configuration:pedantic_warnings"
+no_pedantic_warnings_config_path =
+    "//starboard/raspi/shared/platform_configuration:no_pedantic_warnings"
+
 sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
diff --git a/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni b/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
new file mode 100644
index 0000000..d773598
--- /dev/null
+++ b/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
@@ -0,0 +1,26 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+_home_dir = getenv("HOME")
+_raspi_home_dir = getenv("RASPI_HOME")
+assert(_raspi_home_dir != "",
+       "RasPi builds require the 'RASPI_HOME' environment variable to be set.")
+
+raspi_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
+_raspi_toolchain_path = "$_raspi_home_dir/tools/arm-bcm2708/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin"
+
+gcc_toolchain_ar = "ar"
+gcc_toolchain_cc = "$_raspi_toolchain_path/arm-linux-gnueabihf-gcc"
+gcc_toolchain_cxx = "$_raspi_toolchain_path/arm-linux-gnueabihf-g++"
+gcc_toolchain_strip = "$_raspi_toolchain_path/arm-linux-gnueabihf-strip"
diff --git a/src/starboard/sabi/_env.py b/src/starboard/sabi/_env.py
index 021908e..f091f9e 100644
--- a/src/starboard/sabi/_env.py
+++ b/src/starboard/sabi/_env.py
@@ -21,6 +21,6 @@
 
 _ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
 if not path.exists(_ENV):
-  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  print('%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV))
   sys.exit(1)
 load_source('', _ENV)
diff --git a/src/starboard/sabi/omaha_request.py b/src/starboard/sabi/omaha_request.py
index 3d496d1..2af9202 100644
--- a/src/starboard/sabi/omaha_request.py
+++ b/src/starboard/sabi/omaha_request.py
@@ -20,7 +20,7 @@
 import argparse
 import sys
 import requests
-import simplejson
+import json as simplejson
 
 from starboard.sabi import generate_sabi_id
 
@@ -117,6 +117,9 @@
   _REQUEST['request']['sbversion'] = args.sbversion
   _REQUEST['request']['app'][0]['version'] = args.version
 
+  print('Querying: [[ {} ]]'.format(_ENDPOINT_QA if args.qa else _ENDPOINT))
+  print('Request:  [[ {} ]]'.format(simplejson.dumps(_REQUEST)))
+
   print(
       requests.post(
           _ENDPOINT_QA if args.qa else _ENDPOINT,
diff --git a/src/starboard/sabi/sabi.py b/src/starboard/sabi/sabi.py
index a49ae2e..5dca3b9 100644
--- a/src/starboard/sabi/sabi.py
+++ b/src/starboard/sabi/sabi.py
@@ -11,6 +11,6 @@
 # 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.
-"""Source of truth of the default/experimental Starboard API version."""
+"""Source of truth of the default Starboard API version."""
 
 SB_API_VERSION = 14
diff --git a/src/starboard/shared/libjpeg/jpeg_image_decoder.cc b/src/starboard/shared/libjpeg/jpeg_image_decoder.cc
index cbd6adb..c990179 100644
--- a/src/starboard/shared/libjpeg/jpeg_image_decoder.cc
+++ b/src/starboard/shared/libjpeg/jpeg_image_decoder.cc
@@ -21,12 +21,15 @@
 
 #include "starboard/common/log.h"
 #include "starboard/configuration.h"
+#include "starboard/file.h"
 #include "starboard/linux/shared/decode_target_internal.h"
 
 // Inhibit C++ name-mangling for libjpeg functions.
 extern "C" {
-#include "third_party/libjpeg/jpeglib.h"
-#include "third_party/libjpeg/jpegint.h"
+// clang-format off
+#include "third_party/libjpeg-turbo/jpeglib.h"
+#include "third_party/libjpeg-turbo/jpegint.h"
+// clang-format on
 }
 
 namespace starboard {
@@ -274,7 +277,7 @@
   jmp_buf jump_buffer;
   info.client_data = &jump_buffer;
 
-  // int setjmp(jmp_buf env) saves the current environment (ths program state),
+  // int setjmp(jmp_buf env) saves the current environment (this program state),
   // at some point of program execution, into a platform-specific data
   // structure (jmp_buf) that can be used at some later point of program
   // execution by longjmp to restore the program state to that saved by setjmp
diff --git a/src/starboard/shared/starboard/media/mime_type.cc b/src/starboard/shared/starboard/media/mime_type.cc
index 0008844..035de69 100644
--- a/src/starboard/shared/starboard/media/mime_type.cc
+++ b/src/starboard/shared/starboard/media/mime_type.cc
@@ -14,8 +14,12 @@
 
 #include "starboard/shared/starboard/media/mime_type.h"
 
+#include <algorithm>
+#include <iosfwd>
 #include <locale>
-#include <sstream>
+#include <numeric>
+#include <string>
+#include <vector>
 
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
@@ -43,6 +47,11 @@
   if (!buffer.fail() && buffer.rdbuf()->in_avail() == 0) {
     return MimeType::kParamTypeFloat;
   }
+
+  if (value == "true" || value == "false") {
+    return MimeType::kParamTypeBoolean;
+  }
+
   return MimeType::kParamTypeString;
 }
 
@@ -85,6 +94,22 @@
   return result;
 }
 
+const char* ParamTypeToString(MimeType::ParamType param_type) {
+  switch (param_type) {
+    case MimeType::kParamTypeInteger:
+      return "Integer";
+    case MimeType::kParamTypeFloat:
+      return "Float";
+    case MimeType::kParamTypeString:
+      return "String";
+    case MimeType::kParamTypeBoolean:
+      return "Boolean";
+    default:
+      SB_NOTREACHED();
+      return "Unknown";
+  }
+}
+
 }  // namespace
 
 const int MimeType::kInvalidParamIndex = -1;
@@ -117,8 +142,11 @@
   for (Strings::iterator iter = components.begin(); iter != components.end();
        ++iter) {
     std::vector<std::string> name_and_value = SplitAndTrim(*iter, '=');
+    // The parameter must be on the format 'name=value' and neither |name| nor
+    // |value| can be empty. |value| must also not contain '|'.
     if (name_and_value.size() != 2 || name_and_value[0].empty() ||
-        name_and_value[1].empty()) {
+        name_and_value[1].empty() ||
+        name_and_value[1].find('|') != std::string::npos) {
       return;
     }
     Param param;
@@ -215,6 +243,17 @@
   return params_[index].value;
 }
 
+bool MimeType::GetParamBoolValue(int index) const {
+  SB_DCHECK(is_valid());
+  SB_DCHECK(index < GetParamCount());
+
+  if (GetParamType(index) != kParamTypeBoolean) {
+    return false;
+  }
+
+  return params_[index].value == "true";
+}
+
 int MimeType::GetParamIntValue(const char* name, int default_value) const {
   int index = GetParamIndexByName(name);
   if (index != kInvalidParamIndex) {
@@ -242,6 +281,60 @@
   return default_value;
 }
 
+bool MimeType::GetParamBoolValue(const char* name, bool default_value) const {
+  int index = GetParamIndexByName(name);
+  if (index != kInvalidParamIndex) {
+    return GetParamBoolValue(index);
+  }
+  return default_value;
+}
+
+bool MimeType::RegisterBoolParameter(const char* name) {
+  return RegisterParameter(name, kParamTypeBoolean);
+}
+
+bool MimeType::RegisterStringParameter(const char* name,
+                                       const std::string& pattern /* = "" */) {
+  if (!RegisterParameter(name, kParamTypeString)) {
+    return false;
+  }
+
+  int param_index = GetParamIndexByName(name);
+  if (param_index == kInvalidParamIndex || pattern.empty()) {
+    return true;
+  }
+
+  // Compare the parameter value with the provided pattern.
+  const std::string& param_value = GetParamStringValue(param_index);
+  bool matches = false;
+  size_t match_start = 0;
+  while (!matches) {
+    match_start = pattern.find(param_value, match_start);
+    if (match_start == std::string::npos) {
+      break;
+    }
+
+    size_t match_end = match_start + param_value.length();
+    matches =
+        // Matches if the match is at the start of the string or
+        // if the preceding character is the divider _and_
+        (match_start <= 0 || pattern[match_start - 1] == '|') &&
+        // if the end of the match is the end of the pattern or
+        // if the succeeding character is the divider.
+        (match_end >= pattern.length() || pattern[match_end] == '|');
+    match_start = match_end + 1;
+  }
+
+  if (matches) {
+    return true;
+  }
+
+  SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
+               << "' does not match the supplied pattern: '" << pattern << "'";
+  is_valid_ = false;
+  return false;
+}
+
 int MimeType::GetParamIndexByName(const char* name) const {
   for (size_t i = 0; i < params_.size(); ++i) {
     if (SbStringCompareNoCase(params_[i].name.c_str(), name) == 0) {
@@ -251,6 +344,36 @@
   return kInvalidParamIndex;
 }
 
+bool MimeType::RegisterParameter(const char* name, ParamType param_type) {
+  if (!is_valid()) {
+    return false;
+  }
+
+  int index = GetParamIndexByName(name);
+  if (index == kInvalidParamIndex) {
+    return true;
+  }
+
+  const std::string& param_value = GetParamStringValue(index);
+  ParamType parsed_type = GetParamType(index);
+
+  // Check that the parameter can be returned as the requested type.
+  // Allowed conversions:
+  // Any Type -> String, Int -> Float
+  bool convertible =
+      param_type == parsed_type || param_type == kParamTypeString ||
+      (param_type == kParamTypeFloat && parsed_type == kParamTypeInteger);
+  if (!convertible) {
+    SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
+                 << "' can't be converted to " << ParamTypeToString(param_type);
+    is_valid_ = false;
+    return false;
+  }
+
+  // All validations succeeded.
+  return true;
+}
+
 }  // namespace media
 }  // namespace starboard
 }  // namespace shared
diff --git a/src/starboard/shared/starboard/media/mime_type.h b/src/starboard/shared/starboard/media/mime_type.h
index 7c3d619..a05a301 100644
--- a/src/starboard/shared/starboard/media/mime_type.h
+++ b/src/starboard/shared/starboard/media/mime_type.h
@@ -50,6 +50,7 @@
     kParamTypeInteger,
     kParamTypeFloat,
     kParamTypeString,
+    kParamTypeBoolean,
   };
 
   static const int kInvalidParamIndex;
@@ -71,12 +72,30 @@
   int GetParamIntValue(int index) const;
   float GetParamFloatValue(int index) const;
   const std::string& GetParamStringValue(int index) const;
+  bool GetParamBoolValue(int index) const;
 
   int GetParamIntValue(const char* name, int default_value) const;
   float GetParamFloatValue(const char* name, float default_value) const;
   const std::string& GetParamStringValue(
       const char* name,
       const std::string& default_value) const;
+  bool GetParamBoolValue(const char* name, bool default_value) const;
+
+  // Pre-register a mime parameter of type boolean.
+  // Returns true if the mime type is valid and the value passes validation.
+  // If the parameter validation fails this MimeType will be marked invalid.
+  // NOTE: The function returns true for missing parameters.
+  bool RegisterBoolParameter(const char* name);
+
+  // Pre-register a mime parameter of type string.
+  // Returns true if the mime type is valid and the value passes validation.
+  // Allows passing a pattern on the format "value_1|...|value_n"
+  // where the parameter value must match one of the values in the pattern in
+  // order to be considered valid.
+  // If the parameter validation fails this MimeType will be marked invalid.
+  // NOTE: The function returns true for missing parameters.
+  bool RegisterStringParameter(const char* name,
+                               const std::string& pattern = "");
 
  private:
   struct Param {
@@ -90,6 +109,7 @@
   typedef std::vector<Param> Params;
 
   int GetParamIndexByName(const char* name) const;
+  bool RegisterParameter(const char* name, ParamType type);
 
   const std::string raw_content_type_;
   bool is_valid_;
diff --git a/src/starboard/shared/starboard/media/mime_type_test.cc b/src/starboard/shared/starboard/media/mime_type_test.cc
index 54dfdfc..854444d 100644
--- a/src/starboard/shared/starboard/media/mime_type_test.cc
+++ b/src/starboard/shared/starboard/media/mime_type_test.cc
@@ -41,7 +41,7 @@
 }
 
 // Valid mime type must have a type/subtype without space in between.
-TEST(MimeTypeTest, InvalidType) {
+TEST(MimeTypeTest, InvalidMimeType) {
   {
     MimeType mime_type("video");
     EXPECT_FALSE(mime_type.is_valid());
@@ -83,6 +83,21 @@
         "param1=\" value1 \";codecs=\"abc, def\"");
     EXPECT_FALSE(mime_type.is_valid());
   }
+  {
+    // Parameter name must not be empty.
+    MimeType mime_type("video/mp4; =value");
+    EXPECT_FALSE(mime_type.is_valid());
+  }
+  {
+    // Parameter value must not be empty.
+    MimeType mime_type("video/mp4; name=");
+    EXPECT_FALSE(mime_type.is_valid());
+  }
+  {
+    // No '|' allowed.
+    MimeType mime_type("video/mp4; name=ab|c");
+    EXPECT_FALSE(mime_type.is_valid());
+  }
 }
 
 TEST(MimeTypeTest, ValidContentTypeWithTypeAndSubtypeOnly) {
@@ -165,10 +180,13 @@
 
 TEST(MimeTypeTest, GetParamType) {
   {
-    MimeType mime_type("video/mp4; name0=123; name1=1.2; name2=xyz");
+    MimeType mime_type(
+        "video/mp4; name0=123; name1=1.2; name2=xyz; name3=true; name4=false");
     EXPECT_EQ(MimeType::kParamTypeInteger, mime_type.GetParamType(0));
     EXPECT_EQ(MimeType::kParamTypeFloat, mime_type.GetParamType(1));
     EXPECT_EQ(MimeType::kParamTypeString, mime_type.GetParamType(2));
+    EXPECT_EQ(MimeType::kParamTypeBoolean, mime_type.GetParamType(3));
+    EXPECT_EQ(MimeType::kParamTypeBoolean, mime_type.GetParamType(4));
   }
 
   {
@@ -230,6 +248,12 @@
   }
 }
 
+TEST(MimeTypeTest, GetParamBoolValueWithIndex) {
+  MimeType mime_type("video/mp4; name0=true; name1=false");
+  EXPECT_TRUE(mime_type.GetParamBoolValue(0));
+  EXPECT_FALSE(mime_type.GetParamBoolValue(1));
+}
+
 TEST(MimeTypeTest, GetParamValueInInvalidType) {
   MimeType mime_type("video/mp4; name0=abc; name1=123.4");
   EXPECT_FLOAT_EQ(0, mime_type.GetParamIntValue(0));
@@ -280,6 +304,110 @@
   }
 }
 
+TEST(MimeTypeTest, GetParamBooleanValueWithName) {
+  MimeType mime_type("video/mp4; name0=true; name1=false; name2=trues");
+  EXPECT_TRUE(mime_type.GetParamBoolValue("name0", false));
+  EXPECT_FALSE(mime_type.GetParamBoolValue("name1", true));
+  // Default value
+  EXPECT_FALSE(mime_type.GetParamBoolValue("name2", false));
+}
+
+TEST(MimeTypeTest, ReadParamOfDifferentTypes) {
+  // Ensure that the parameter type is enforced correctly.
+  MimeType mime_type("video/mp4; string=value; int=1; float=1.0; bool=true");
+  ASSERT_TRUE(mime_type.is_valid());
+  // Read params as their original types.
+  EXPECT_EQ(mime_type.GetParamStringValue("string", ""), "value");
+  EXPECT_EQ(mime_type.GetParamIntValue("int", 0), 1);
+  EXPECT_EQ(mime_type.GetParamFloatValue("float", 0.0), 1.0);
+  EXPECT_EQ(mime_type.GetParamBoolValue("bool", false), true);
+  // All param types can be read as strings.
+  EXPECT_EQ(mime_type.GetParamStringValue("int", ""), "1");
+  EXPECT_EQ(mime_type.GetParamStringValue("float", ""), "1.0");
+  EXPECT_EQ(mime_type.GetParamStringValue("bool", ""), "true");
+  // Int can also be read as float.
+  EXPECT_EQ(mime_type.GetParamFloatValue("int", 0.0), 1.0);
+
+  // Test failing validation for ints,
+  EXPECT_EQ(mime_type.GetParamIntValue("string", 0), 0);
+  EXPECT_EQ(mime_type.GetParamIntValue("float", 0), 0);
+  EXPECT_EQ(mime_type.GetParamIntValue("bool", 0), 0);
+
+  // floats,
+  EXPECT_EQ(mime_type.GetParamFloatValue("string", 0.0), 0.0);
+  EXPECT_EQ(mime_type.GetParamFloatValue("bool", 0.0), 0.0);
+
+  // and bools.
+  EXPECT_FALSE(mime_type.GetParamBoolValue("string", false));
+  EXPECT_FALSE(mime_type.GetParamBoolValue("int", false));
+  EXPECT_FALSE(mime_type.GetParamBoolValue("float", false));
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithPatterns) {
+  MimeType mime_type("video/mp4; string=yes");
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "yes"));
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "yes|no"));
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "no|yes|no"));
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "no|no|yes"));
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "noyes|yes"));
+  EXPECT_TRUE(mime_type.is_valid());
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithShortPatterns) {
+  MimeType mime_type("video/mp4; string=y");
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "y"));
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "y|n"));
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string", "n|y"));
+  EXPECT_TRUE(mime_type.is_valid());
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithPartialMatches) {
+  {
+    MimeType mime_type("video/mp4; string=yes");
+    EXPECT_FALSE(mime_type.RegisterStringParameter("string", "yesno|no"));
+    EXPECT_FALSE(mime_type.is_valid());
+  }
+  {
+    MimeType mime_type("video/mp4; string=yes");
+    EXPECT_FALSE(mime_type.RegisterStringParameter("string", "noyes|no"));
+    EXPECT_FALSE(mime_type.is_valid());
+  }
+  {
+    MimeType mime_type("video/mp4; string=yes");
+    EXPECT_FALSE(mime_type.RegisterStringParameter("string", "no|yesno"));
+    EXPECT_FALSE(mime_type.is_valid());
+  }
+  {
+    MimeType mime_type("video/mp4; string=yes");
+    EXPECT_FALSE(mime_type.RegisterStringParameter("string", "no|noyes"));
+    EXPECT_FALSE(mime_type.is_valid());
+  }
+}
+
+TEST(MimeTypeTest, MissingParamReturnsTrueOnRegistration) {
+  MimeType mime_type("video/mp4");
+  EXPECT_TRUE(mime_type.RegisterStringParameter("string"));
+  EXPECT_TRUE(mime_type.is_valid());
+  EXPECT_EQ(mime_type.GetParamStringValue("string", "default"), "default");
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithEmptyishPattern) {
+  {
+    MimeType mime_type("video/mp4; string=yes");
+    EXPECT_FALSE(mime_type.RegisterStringParameter("string", "|"));
+  }
+  {
+    MimeType mime_type("video/mp4; string=yes");
+    EXPECT_FALSE(mime_type.RegisterStringParameter("string", "||"));
+  }
+}
+
+TEST(MimeTypeTest, CannotRegisterParamWithInvalidMimeType) {
+  MimeType mime_type("video/mp4; string=");
+  ASSERT_FALSE(mime_type.is_valid());
+  EXPECT_FALSE(mime_type.RegisterStringParameter("string"));
+}
+
 }  // namespace
 }  // namespace media
 }  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
index 5127ae4..4c77870 100644
--- a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
@@ -15,12 +15,14 @@
 #include <cmath>
 #include <deque>
 #include <functional>
+#include <memory>
 #include <numeric>
 #include <queue>
 
 #include "starboard/common/mutex.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration_constants.h"
+#include "starboard/directory.h"
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
@@ -362,8 +364,8 @@
     return test_params;
   }
 
-  vector<const char*> supported_files = GetSupportedAudioTestFiles(
-      kExcludeHeaac, 6, "audiopassthrough=\"false\"");
+  vector<const char*> supported_files =
+      GetSupportedAudioTestFiles(kExcludeHeaac, 6, "audiopassthrough=false");
 
   // Generate test cases. For example, we have |supported_files| [A, B, C].
   // Add tests A->A, A->B, A->C, B->A, B->B, B->C, C->A, C->B and C->C.
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index 00933f3..eee3730 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -637,7 +637,7 @@
     AudioDecoderTest,
     Combine(ValuesIn(GetSupportedAudioTestFiles(kIncludeHeaac,
                                                 6,
-                                                "audiopassthrough=\"false\"")),
+                                                "audiopassthrough=false")),
             Bool()));
 
 }  // namespace
diff --git a/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc b/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc
index fe7932f..afa4fad 100644
--- a/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc
@@ -38,6 +38,8 @@
 namespace {
 
 using ::starboard::testing::FakeGraphicsContextProvider;
+using std::placeholders::_1;
+using std::placeholders::_2;
 using std::string;
 using std::unique_ptr;
 using std::vector;
@@ -121,7 +123,7 @@
 
     if (GetAudioRenderer()) {
       GetAudioRenderer()->Initialize(
-          std::bind(&PlayerComponentsTest::OnError, this),
+          std::bind(&PlayerComponentsTest::OnError, this, _1, _2),
           std::bind(&PlayerComponentsTest::OnPrerolled, this,
                     kSbMediaTypeAudio),
           std::bind(&PlayerComponentsTest::OnEnded, this, kSbMediaTypeAudio));
@@ -129,7 +131,7 @@
     SetPlaybackRate(playback_rate_);
     if (GetVideoRenderer()) {
       GetVideoRenderer()->Initialize(
-          std::bind(&PlayerComponentsTest::OnError, this),
+          std::bind(&PlayerComponentsTest::OnError, this, _1, _2),
           std::bind(&PlayerComponentsTest::OnPrerolled, this,
                     kSbMediaTypeVideo),
           std::bind(&PlayerComponentsTest::OnEnded, this, kSbMediaTypeVideo));
@@ -401,7 +403,11 @@
   // 1s in the tests.
   const SbTime kMaxWriteAheadDuration = kSbTimeSecond;
 
-  void OnError() { has_error_ = true; }
+  void OnError(SbPlayerError error, const std::string& error_message) {
+    has_error_ = true;
+    SB_LOG(ERROR) << "Caught renderer error (" << error
+                  << "): " << error_message;
+  }
   void OnPrerolled(SbMediaType media_type) {
     if (media_type == kSbMediaTypeAudio) {
       audio_prerolled_ = true;
diff --git a/src/starboard/shared/starboard/player/filter/testing/test_util.cc b/src/starboard/shared/starboard/player/filter/testing/test_util.cc
index 759fc83..21447a4 100644
--- a/src/starboard/shared/starboard/player/filter/testing/test_util.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/test_util.cc
@@ -209,8 +209,8 @@
       if (SbMediaIsVideoSupported(
               dmp_reader.video_codec(),
 #if SB_API_VERSION >= 12
-              "",  // content_type
-#endif             // SB_API_VERSION >= 12
+              dmp_reader.video_mime_type().c_str(),
+#endif  // SB_API_VERSION >= 12
 #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT)
               -1, -1, 8, kSbMediaPrimaryIdUnspecified,
               kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
diff --git a/src/starboard/stub/gyp_configuration.py b/src/starboard/stub/gyp_configuration.py
index 4f0285f..6329189 100644
--- a/src/starboard/stub/gyp_configuration.py
+++ b/src/starboard/stub/gyp_configuration.py
@@ -45,9 +45,6 @@
   def GetVariables(self, configuration):
     variables = super(StubConfiguration, self).GetVariables(
         configuration, use_clang=1)
-    variables.update({
-        'javascript_engine': 'v8',
-    })
     return variables
 
   def GetEnvironmentVariables(self):
diff --git a/src/starboard/stub/platform_configuration/BUILD.gn b/src/starboard/stub/platform_configuration/BUILD.gn
index ace2a65..a5bdb8f 100644
--- a/src/starboard/stub/platform_configuration/BUILD.gn
+++ b/src/starboard/stub/platform_configuration/BUILD.gn
@@ -85,30 +85,32 @@
 
   # Defined to get format macro constants from <inttypes.h>.
   defines = [ "__STDC_FORMAT_MACROS" ]
+}
 
-  if (sb_pedantic_warnings) {
-    cflags += [
-      "-Wall",
-      "-Wextra",
-      "-Wunreachable-code",
-    ]
-  } else {
-    cflags += [
-      # 'this' pointer cannot be NULL...pointer may be assumed
-      # to always convert to true.
-      "-Wno-undefined-bool-conversion",
+config("pedantic_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+    "-Wunreachable-code",
+  ]
+}
 
-      # Skia doesn't use overrides.
-      "-Wno-inconsistent-missing-override",
+config("no_pedantic_warnings") {
+  cflags = [
+    # 'this' pointer cannot be NULL...pointer may be assumed
+    # to always convert to true.
+    "-Wno-undefined-bool-conversion",
 
-      # Do not warn for implicit type conversions that may change a value.
-      "-Wno-conversion",
+    # Skia doesn't use overrides.
+    "-Wno-inconsistent-missing-override",
 
-      # shifting a negative signed value is undefined
-      "-Wno-shift-negative-value",
+    # Do not warn for implicit type conversions that may change a value.
+    "-Wno-conversion",
 
-      # Width of bit-field exceeds width of its type- value will be truncated
-      "-Wno-bitfield-width",
-    ]
-  }
+    # shifting a negative signed value is undefined
+    "-Wno-shift-negative-value",
+
+    # Width of bit-field exceeds width of its type- value will be truncated
+    "-Wno-bitfield-width",
+  ]
 }
diff --git a/src/starboard/stub/platform_configuration/configuration.gni b/src/starboard/stub/platform_configuration/configuration.gni
index 61a778f..7386286 100644
--- a/src/starboard/stub/platform_configuration/configuration.gni
+++ b/src/starboard/stub/platform_configuration/configuration.gni
@@ -23,3 +23,8 @@
 sabi_path = "//starboard/sabi/x64/sysv/sabi-v$sb_api_version.json"
 
 install_target_path = "//starboard/build/install/install_target.gni"
+
+pedantic_warnings_config_path =
+    "//starboard/stub/platform_configuration:pedantic_warnings"
+no_pedantic_warnings_config_path =
+    "//starboard/stub/platform_configuration:no_pedantic_warnings"
diff --git a/src/third_party/crashpad/handler/handler.gyp b/src/third_party/crashpad/handler/handler.gyp
index 1275a4d..d862d70 100644
--- a/src/third_party/crashpad/handler/handler.gyp
+++ b/src/third_party/crashpad/handler/handler.gyp
@@ -102,6 +102,7 @@
           'ldflags': [
             '-Wl,--as-needed',
             '-Wl,-gc-sections',
+            '-Wl,-z,noexecstack',
           ],
         }],
         ['OS=="win"',  {
diff --git a/src/third_party/devtools/copy_devtools_modules.rsp b/src/third_party/devtools/copy_devtools_modules.rsp
deleted file mode 100644
index 39ad8b2..0000000
--- a/src/third_party/devtools/copy_devtools_modules.rsp
+++ /dev/null
@@ -1,476 +0,0 @@
-front_end/network/network.js
-front_end/network/SignedExchangeInfoView.js
-front_end/network/ResourceWebSocketFrameView.js
-front_end/network/RequestTimingView.js
-front_end/network/RequestResponseView.js
-front_end/network/RequestPreviewView.js
-front_end/network/RequestInitiatorView.js
-front_end/network/RequestHeadersView.js
-front_end/network/RequestHTMLView.js
-front_end/network/RequestCookiesView.js
-front_end/network/NetworkWaterfallColumn.js
-front_end/network/NetworkTimeCalculator.js
-front_end/network/NetworkSearchScope.js
-front_end/network/NetworkPanel.js
-front_end/network/NetworkOverview.js
-front_end/network/NetworkManageCustomHeadersView.js
-front_end/network/NetworkLogViewColumns.js
-front_end/network/NetworkLogView.js
-front_end/network/NetworkItemView.js
-front_end/network/NetworkFrameGrouper.js
-front_end/network/NetworkDataGridNode.js
-front_end/network/NetworkConfigView.js
-front_end/network/HARWriter.js
-front_end/network/EventSourceMessagesView.js
-front_end/network/BlockedURLsPane.js
-front_end/network/BinaryResourceView.js
-front_end/test_runner/test_runner.js
-front_end/test_runner/TestRunner.js
-front_end/emulation/emulation.js
-front_end/emulation/SensorsView.js
-front_end/emulation/MediaQueryInspector.js
-front_end/emulation/InspectedPagePlaceholder.js
-front_end/emulation/GeolocationsSettingsTab.js
-front_end/emulation/EmulatedDevices.js
-front_end/emulation/DevicesSettingsTab.js
-front_end/emulation/DeviceModeWrapper.js
-front_end/emulation/DeviceModeView.js
-front_end/emulation/DeviceModeToolbar.js
-front_end/emulation/DeviceModeModel.js
-front_end/emulation/AdvancedApp.js
-front_end/inspector_main/inspector_main.js
-front_end/inspector_main/RenderingOptions.js
-front_end/inspector_main/InspectorMain.js
-front_end/js_main/js_main.js
-front_end/js_main/JsMain.js
-front_end/search/search.js
-front_end/search/SearchView.js
-front_end/search/SearchResultsPane.js
-front_end/search/SearchConfig.js
-front_end/screencast/screencast.js
-front_end/screencast/ScreencastView.js
-front_end/screencast/ScreencastApp.js
-front_end/screencast/InputModel.js
-front_end/performance_monitor/performance_monitor.js
-front_end/performance_monitor/PerformanceMonitor.js
-front_end/main/main.js
-front_end/main/SimpleApp.js
-front_end/main/MainImpl.js
-front_end/main/ExecutionContextSelector.js
-front_end/snippets/snippets.js
-front_end/snippets/SnippetsQuickOpen.js
-front_end/snippets/ScriptSnippetFileSystem.js
-front_end/settings/settings.js
-front_end/settings/SettingsScreen.js
-front_end/settings/FrameworkBlackboxSettingsTab.js
-front_end/security/security.js
-front_end/security/SecurityPanel.js
-front_end/security/SecurityModel.js
-front_end/javascript_metadata/javascript_metadata.js
-front_end/javascript_metadata/NativeFunctions.js
-front_end/javascript_metadata/JavaScriptMetadata.js
-front_end/har_importer/har_importer.js
-front_end/har_importer/HARImporter.js
-front_end/har_importer/HARFormat.js
-front_end/browser_debugger/browser_debugger.js
-front_end/browser_debugger/XHRBreakpointsSidebarPane.js
-front_end/browser_debugger/ObjectEventListenersSidebarPane.js
-front_end/browser_debugger/EventListenerBreakpointsSidebarPane.js
-front_end/browser_debugger/DOMBreakpointsSidebarPane.js
-front_end/layer_viewer/layer_viewer.js
-front_end/layer_viewer/TransformController.js
-front_end/layer_viewer/PaintProfilerView.js
-front_end/layer_viewer/Layers3DView.js
-front_end/layer_viewer/LayerViewHost.js
-front_end/layer_viewer/LayerTreeOutline.js
-front_end/layer_viewer/LayerDetailsView.js
-front_end/cm_web_modes/cm_web_modes.js
-front_end/cm_web_modes/cm_web_modes_cm.js
-front_end/cm_web_modes/cm_web_modes_headless.js
-front_end/cm_web_modes/css.js
-front_end/cm_web_modes/javascript.js
-front_end/cm_web_modes/xml.js
-front_end/cm_web_modes/htmlmixed.js
-front_end/cm_web_modes/htmlembedded.js
-front_end/text_editor/text_editor.js
-front_end/text_editor/TextEditorAutocompleteController.js
-front_end/text_editor/CodeMirrorUtils.js
-front_end/text_editor/CodeMirrorTextEditor.js
-front_end/quick_open/quick_open.js
-front_end/quick_open/QuickOpen.js
-front_end/quick_open/HelpQuickOpen.js
-front_end/quick_open/FilteredListWidget.js
-front_end/quick_open/CommandMenu.js
-front_end/elements/elements.js
-front_end/elements/elements-legacy.js
-front_end/elements/StylesSidebarPane.js
-front_end/elements/StylePropertyTreeElement.js
-front_end/elements/StylePropertyHighlighter.js
-front_end/elements/PropertiesWidget.js
-front_end/elements/PlatformFontsWidget.js
-front_end/elements/NodeStackTraceWidget.js
-front_end/elements/MetricsSidebarPane.js
-front_end/elements/MarkerDecorator.js
-front_end/elements/InspectElementModeController.js
-front_end/elements/EventListenersWidget.js
-front_end/elements/ElementsTreeOutline.js
-front_end/elements/ElementsTreeElement.js
-front_end/elements/ElementsTreeElementHighlighter.js
-front_end/elements/ElementStatePaneWidget.js
-front_end/elements/ElementsSidebarPane.js
-front_end/elements/ElementsPanel.js
-front_end/elements/ElementsBreadcrumbs.js
-front_end/elements/DOMPath.js
-front_end/elements/DOMLinkifier.js
-front_end/elements/ComputedStyleWidget.js
-front_end/elements/ComputedStyleModel.js
-front_end/elements/ColorSwatchPopoverIcon.js
-front_end/elements/ClassesPaneWidget.js
-front_end/timeline_model/timeline_model.js
-front_end/timeline_model/TracingLayerTree.js
-front_end/timeline_model/TimelineProfileTree.js
-front_end/timeline_model/TimelineModel.js
-front_end/timeline_model/TimelineModelFilter.js
-front_end/timeline_model/TimelineJSProfile.js
-front_end/timeline_model/TimelineIRModel.js
-front_end/timeline_model/TimelineFrameModel.js
-front_end/help/help.js
-front_end/help/ReleaseNoteView.js
-front_end/help/ReleaseNoteText.js
-front_end/help/HelpImpl.js
-front_end/workspace_diff/workspace_diff.js
-front_end/workspace_diff/WorkspaceDiff.js
-front_end/mobile_throttling/mobile_throttling.js
-front_end/mobile_throttling/ThrottlingSettingsTab.js
-front_end/mobile_throttling/ThrottlingPresets.js
-front_end/mobile_throttling/ThrottlingManager.js
-front_end/mobile_throttling/NetworkThrottlingSelector.js
-front_end/mobile_throttling/NetworkPanelIndicator.js
-front_end/mobile_throttling/MobileThrottlingSelector.js
-front_end/event_listeners/event_listeners.js
-front_end/event_listeners/EventListenersView.js
-front_end/event_listeners/EventListenersUtils.js
-front_end/object_ui/object_ui.js
-front_end/object_ui/RemoteObjectPreviewFormatter.js
-front_end/object_ui/ObjectPropertiesSection.js
-front_end/object_ui/ObjectPopoverHelper.js
-front_end/object_ui/JavaScriptREPL.js
-front_end/object_ui/JavaScriptAutocomplete.js
-front_end/object_ui/CustomPreviewComponent.js
-front_end/cookie_table/cookie_table.js
-front_end/cookie_table/CookiesTable.js
-front_end/cm_modes/cm_modes.js
-front_end/cm_modes/DefaultCodeMirrorMimeMode.js
-front_end/cm_modes/clike.js
-front_end/cm_modes/coffeescript.js
-front_end/cm_modes/php.js
-front_end/cm_modes/python.js
-front_end/cm_modes/shell.js
-front_end/cm_modes/livescript.js
-front_end/cm_modes/markdown.js
-front_end/cm_modes/clojure.js
-front_end/cm_modes/jsx.js
-front_end/css_overview/css_overview.js
-front_end/css_overview/CSSOverviewUnusedDeclarations.js
-front_end/css_overview/CSSOverviewStartView.js
-front_end/css_overview/CSSOverviewSidebarPanel.js
-front_end/css_overview/CSSOverviewProcessingView.js
-front_end/css_overview/CSSOverviewPanel.js
-front_end/css_overview/CSSOverviewModel.js
-front_end/css_overview/CSSOverviewController.js
-front_end/css_overview/CSSOverviewCompletedView.js
-front_end/console/console.js
-front_end/console/ConsoleContextSelector.js
-front_end/console/ConsoleFilter.js
-front_end/console/ConsoleSidebar.js
-front_end/console/ConsolePanel.js
-front_end/console/ConsolePinPane.js
-front_end/console/ConsolePrompt.js
-front_end/console/ConsoleView.js
-front_end/console/ConsoleViewMessage.js
-front_end/console/ConsoleViewport.js
-front_end/source_frame/source_frame.js
-front_end/source_frame/XMLView.js
-front_end/source_frame/SourcesTextEditor.js
-front_end/source_frame/SourceFrame.js
-front_end/source_frame/source_frame.js
-front_end/source_frame/SourceCodeDiff.js
-front_end/source_frame/ResourceSourceFrame.js
-front_end/source_frame/PreviewFactory.js
-front_end/source_frame/JSONView.js
-front_end/source_frame/ImageView.js
-front_end/source_frame/FontView.js
-front_end/source_frame/BinaryResourceViewFactory.js
-front_end/inline_editor/inline_editor.js
-front_end/inline_editor/SwatchPopoverHelper.js
-front_end/inline_editor/CSSShadowModel.js
-front_end/inline_editor/CSSShadowEditor.js
-front_end/inline_editor/ColorSwatch.js
-front_end/inline_editor/BezierUI.js
-front_end/inline_editor/BezierEditor.js
-front_end/diff/diff.js
-front_end/diff/diff_match_patch.js
-front_end/diff/DiffWrapper.js
-front_end/formatter/formatter.js
-front_end/formatter/ScriptFormatter.js
-front_end/formatter/FormatterWorkerPool.js
-front_end/color_picker/color_picker.js
-front_end/color_picker/Spectrum.js
-front_end/color_picker/ContrastOverlay.js
-front_end/color_picker/ContrastInfo.js
-front_end/color_picker/ContrastDetails.js
-front_end/cm/cm.js
-front_end/cm/active-line.js
-front_end/cm/brace-fold.js
-front_end/cm/closebrackets.js
-front_end/cm/codemirror.js
-front_end/cm/comment.js
-front_end/cm/foldcode.js
-front_end/cm/foldgutter.js
-front_end/cm/mark-selection.js
-front_end/cm/matchbrackets.js
-front_end/cm/multiplex.js
-front_end/cm/overlay.js
-front_end/formatter_worker.unbundled.js
-front_end/heap_snapshot_worker.unbundled.js
-front_end/heap_snapshot_model/heap_snapshot_model.js
-front_end/heap_snapshot_model/HeapSnapshotModel.js
-front_end/heap_snapshot_worker/heap_snapshot_worker.js
-front_end/heap_snapshot_worker/AllocationProfile.js
-front_end/heap_snapshot_worker/HeapSnapshot.js
-front_end/heap_snapshot_worker/HeapSnapshotLoader.js
-front_end/heap_snapshot_worker/HeapSnapshotWorker.js
-front_end/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.js
-front_end/text_utils/text_utils.js
-front_end/text_utils/TextUtils.js
-front_end/text_utils/TextRange.js
-front_end/text_utils/Text.js
-front_end/formatter_worker/formatter_worker.js
-front_end/formatter_worker/RelaxedJSONParser.js
-front_end/formatter_worker/JavaScriptOutline.js
-front_end/formatter_worker/JavaScriptFormatter.js
-front_end/formatter_worker/IdentityFormatter.js
-front_end/formatter_worker/HTMLFormatter.js
-front_end/formatter_worker/FormatterWorker.js
-front_end/formatter_worker/FormattedContentBuilder.js
-front_end/formatter_worker/ESTreeWalker.js
-front_end/formatter_worker/CSSRuleParser.js
-front_end/formatter_worker/CSSFormatter.js
-front_end/formatter_worker/AcornTokenizer.js
-front_end/cm_headless/cm_headless.js
-front_end/cm_headless/headlesscodemirror.js
-front_end/data_grid/data_grid.js
-front_end/data_grid/ViewportDataGrid.js
-front_end/data_grid/SortableDataGrid.js
-front_end/data_grid/ShowMoreDataGridNode.js
-front_end/data_grid/DataGrid.js
-front_end/protocol_monitor/protocol_monitor.js
-front_end/protocol_monitor/ProtocolMonitor.js
-front_end/console_counters/console_counters.js
-front_end/console_counters/WarningErrorCounter.js
-front_end/extensions/extensions.js
-front_end/extensions/ExtensionAPI.js
-front_end/extensions/ExtensionPanel.js
-front_end/extensions/ExtensionServer.js
-front_end/extensions/ExtensionTraceProvider.js
-front_end/extensions/ExtensionView.js
-front_end/browser_sdk/browser_sdk.js
-front_end/browser_sdk/LogManager.js
-front_end/persistence/persistence.js
-front_end/persistence/WorkspaceSettingsTab.js
-front_end/persistence/PlatformFileSystem.js
-front_end/persistence/PersistenceUtils.js
-front_end/persistence/PersistenceImpl.js
-front_end/persistence/PersistenceActions.js
-front_end/persistence/NetworkPersistenceManager.js
-front_end/persistence/IsolatedFileSystemManager.js
-front_end/persistence/IsolatedFileSystem.js
-front_end/persistence/FileSystemWorkspaceBinding.js
-front_end/persistence/EditFileSystemView.js
-front_end/persistence/Automapping.js
-front_end/components/components.js
-front_end/components/TargetDetachedDialog.js
-front_end/components/Reload.js
-front_end/components/Linkifier.js
-front_end/components/JSPresentationUtils.js
-front_end/components/ImagePreview.js
-front_end/components/DockController.js
-front_end/bindings/bindings.js
-front_end/bindings/TempFile.js
-front_end/bindings/StylesSourceMapping.js
-front_end/bindings/SASSSourceMapping.js
-front_end/bindings/ResourceUtils.js
-front_end/bindings/ResourceScriptMapping.js
-front_end/bindings/ResourceMapping.js
-front_end/bindings/PresentationConsoleMessageHelper.js
-front_end/bindings/NetworkProject.js
-front_end/bindings/LiveLocation.js
-front_end/bindings/FileUtils.js
-front_end/bindings/DefaultScriptMapping.js
-front_end/bindings/DebuggerWorkspaceBinding.js
-front_end/bindings/CSSWorkspaceBinding.js
-front_end/bindings/ContentProviderBasedProject.js
-front_end/bindings/CompilerScriptMapping.js
-front_end/bindings/BreakpointManager.js
-front_end/bindings/BlackboxManager.js
-front_end/workspace/workspace.js
-front_end/workspace/WorkspaceImpl.js
-front_end/workspace/UISourceCode.js
-front_end/workspace/FileManager.js
-front_end/services/services.js
-front_end/services/ServiceManager.js
-front_end/sdk/sdk.js
-front_end/sdk/TracingModel.js
-front_end/sdk/TracingManager.js
-front_end/sdk/TargetManager.js
-front_end/sdk/Target.js
-front_end/sdk/SourceMapManager.js
-front_end/sdk/SourceMap.js
-front_end/sdk/ServiceWorkerManager.js
-front_end/sdk/ServiceWorkerCacheModel.js
-front_end/sdk/ServerTiming.js
-front_end/sdk/SecurityOriginManager.js
-front_end/sdk/SDKModel.js
-front_end/sdk/Script.js
-front_end/sdk/ScreenCaptureModel.js
-front_end/sdk/RuntimeModel.js
-front_end/sdk/ResourceTreeModel.js
-front_end/sdk/Resource.js
-front_end/sdk/RemoteObject.js
-front_end/sdk/ProfileTreeModel.js
-front_end/sdk/IssuesModel.js
-front_end/sdk/PerformanceMetricsModel.js
-front_end/sdk/PaintProfiler.js
-front_end/sdk/OverlayModel.js
-front_end/sdk/NetworkRequest.js
-front_end/sdk/NetworkManager.js
-front_end/sdk/NetworkLog.js
-front_end/sdk/LogModel.js
-front_end/sdk/LayerTreeBase.js
-front_end/sdk/IsolateManager.js
-front_end/sdk/HeapProfilerModel.js
-front_end/sdk/HARLog.js
-front_end/sdk/FilmStripModel.js
-front_end/sdk/EmulationModel.js
-front_end/sdk/DOMModel.js
-front_end/sdk/DOMDebuggerModel.js
-front_end/sdk/DebuggerModel.js
-front_end/sdk/CSSStyleSheetHeader.js
-front_end/sdk/CSSStyleDeclaration.js
-front_end/sdk/CSSRule.js
-front_end/sdk/CSSProperty.js
-front_end/sdk/CSSModel.js
-front_end/sdk/CSSMetadata.js
-front_end/sdk/CSSMedia.js
-front_end/sdk/CSSMatchedStyles.js
-front_end/sdk/CPUProfilerModel.js
-front_end/sdk/CPUProfileDataModel.js
-front_end/sdk/CookieParser.js
-front_end/sdk/CookieModel.js
-front_end/sdk/CompilerSourceMappingContentProvider.js
-front_end/sdk/ConsoleModel.js
-front_end/sdk/Connections.js
-front_end/sdk/ChildTargetManager.js
-front_end/protocol/protocol.js
-front_end/protocol/NodeURL.js
-front_end/protocol/InspectorBackend.js
-front_end/host/host.js
-front_end/host/UserMetrics.js
-front_end/host/ResourceLoader.js
-front_end/host/Platform.js
-front_end/host/InspectorFrontendHost.js
-front_end/host/InspectorFrontendHostAPI.js
-front_end/dom_extension/DOMExtension.js
-front_end/dom_extension/dom_extension.js
-front_end/root.js
-front_end/Runtime.js
-front_end/platform/utilities.js
-front_end/platform/platform.js
-front_end/ui/ARIAUtils.js
-front_end/ui/ZoomManager.js
-front_end/ui/XWidget.js
-front_end/ui/XLink.js
-front_end/ui/XElement.js
-front_end/ui/Widget.js
-front_end/ui/View.js
-front_end/ui/ViewManager.js
-front_end/ui/UIUtils.js
-front_end/ui/ui.js
-front_end/ui/Treeoutline.js
-front_end/ui/Tooltip.js
-front_end/ui/Toolbar.js
-front_end/ui/ThrottledWidget.js
-front_end/ui/TextPrompt.js
-front_end/ui/TextEditor.js
-front_end/ui/TargetCrashedScreen.js
-front_end/ui/TabbedPane.js
-front_end/ui/SyntaxHighlighter.js
-front_end/ui/SuggestBox.js
-front_end/ui/SplitWidget.js
-front_end/ui/SoftDropDown.js
-front_end/ui/SoftContextMenu.js
-front_end/ui/ShortcutsScreen.js
-front_end/ui/ShortcutRegistry.js
-front_end/ui/SettingsUI.js
-front_end/ui/SegmentedButton.js
-front_end/ui/SearchableView.js
-front_end/ui/RootView.js
-front_end/ui/ResizerWidget.js
-front_end/ui/ReportView.js
-front_end/ui/RemoteDebuggingTerminatedScreen.js
-front_end/ui/ProgressIndicator.js
-front_end/ui/PopoverHelper.js
-front_end/ui/Panel.js
-front_end/ui/ListWidget.js
-front_end/ui/ListModel.js
-front_end/ui/ListControl.js
-front_end/ui/KeyboardShortcut.js
-front_end/ui/InspectorView.js
-front_end/ui/InplaceEditor.js
-front_end/ui/Infobar.js
-front_end/ui/Icon.js
-front_end/ui/HistoryInput.js
-front_end/ui/GlassPane.js
-front_end/ui/Geometry.js
-front_end/ui/Fragment.js
-front_end/ui/ForwardedInputEventHandler.js
-front_end/ui/FilterSuggestionBuilder.js
-front_end/ui/FilterBar.js
-front_end/ui/EmptyWidget.js
-front_end/ui/DropTarget.js
-front_end/ui/Dialog.js
-front_end/ui/ContextMenu.js
-front_end/ui/Context.js
-front_end/ui/ARIAUtils.js
-front_end/ui/ActionRegistry.js
-front_end/ui/Action.js
-front_end/ui/ActionDelegate.js
-front_end/ui/ContextFlavorListener.js
-front_end/root.js
-front_end/common/common.js
-front_end/common/common-legacy.js
-front_end/common/App.js
-front_end/common/AppProvider.js
-front_end/common/CharacterIdMap.js
-front_end/common/Color.js
-front_end/common/ContentProvider.js
-front_end/common/EventTarget.js
-front_end/common/JavaScriptMetaData.js
-front_end/common/Linkifier.js
-front_end/common/Object.js
-front_end/common/Console.js
-front_end/common/ParsedURL.js
-front_end/common/Progress.js
-front_end/common/QueryParamHandler.js
-front_end/common/ResourceType.js
-front_end/common/Revealer.js
-front_end/common/Runnable.js
-front_end/common/SegmentedRange.js
-front_end/common/Settings.js
-front_end/common/StaticContentProvider.js
-front_end/common/StringOutputStream.js
-front_end/common/TextDictionary.js
-front_end/common/Throttler.js
-front_end/common/Trie.js
-front_end/common/UIString.js
-front_end/common/Worker.js
diff --git a/src/third_party/libjpeg-turbo/BUILD.gn b/src/third_party/libjpeg-turbo/BUILD.gn
index d566340..39a4f7a 100644
--- a/src/third_party/libjpeg-turbo/BUILD.gn
+++ b/src/third_party/libjpeg-turbo/BUILD.gn
@@ -21,7 +21,9 @@
     "jpeglib.h",
     "jpeglibmangler.h",
   ]
-  defines = [ "MANGLE_JPEG_NAMES" ]
+  if (!is_starboard) {
+    defines = [ "MANGLE_JPEG_NAMES" ]
+  }
 }
 
 if (current_cpu == "x86" || current_cpu == "x64") {
diff --git a/src/third_party/libjpeg-turbo/jconfig.h b/src/third_party/libjpeg-turbo/jconfig.h
index 7d605e1..b197503 100644
--- a/src/third_party/libjpeg-turbo/jconfig.h
+++ b/src/third_party/libjpeg-turbo/jconfig.h
@@ -18,10 +18,8 @@
 /* Support in-memory source/destination managers */
 #define MEM_SRCDST_SUPPORTED 1
 
-#if !defined(STARBOARD)
 /* Use accelerated SIMD routines. */
 #define WITH_SIMD 1
-#endif
 
 #if defined(STARBOARD)
 #define NEED_STARBOARD_MEMORY
diff --git a/src/third_party/libjpeg-turbo/libjpeg.gyp b/src/third_party/libjpeg-turbo/libjpeg.gyp
index 2c9d00c..01af1bd 100644
--- a/src/third_party/libjpeg-turbo/libjpeg.gyp
+++ b/src/third_party/libjpeg-turbo/libjpeg.gyp
@@ -15,7 +15,7 @@
       ],
       'defines': [
         'BMP_SUPPORTED',
-        'PPM_SUPPORTED'
+        'PPM_SUPPORTED',
       ],
       'variables': {
         'no_simd_files': [
@@ -103,6 +103,57 @@
         #'jdatadst.c',
       ],
       'conditions': [
+        # arm processor specific optimizations.
+        ['target_arch == "arm" and arm_neon == 1 or target_arch == "arm64" and arm_neon == 1', {
+          'include_dirs': [
+            'simd/arm',
+          ],
+          'sources!': [
+            '<@(no_simd_files)'
+          ],
+          'sources': [
+            'simd/arm/jccolor-neon.c',
+            'simd/arm/jcgray-neon.c',
+            'simd/arm/jcphuff-neon.c',
+            'simd/arm/jcsample-neon.c',
+            'simd/arm/jdcolor-neon.c',
+            'simd/arm/jdmerge-neon.c',
+            'simd/arm/jdsample-neon.c',
+            'simd/arm/jfdctfst-neon.c',
+            'simd/arm/jfdctint-neon.c',
+            'simd/arm/jidctfst-neon.c',
+            'simd/arm/jidctint-neon.c',
+            'simd/arm/jidctred-neon.c',
+            'simd/arm/jquanti-neon.c',
+          ],
+          'defines': [
+            'WITH_SIMD',
+            'NEON_INTRINSICS',
+          ],
+          'conditions': [
+            ['target_arch == "arm"', {
+              'sources': [
+                'simd/arm/aarch32/jsimd.c',
+                'simd/arm/aarch32/jchuff-neon.c',
+              ],
+            }],
+            ['target_arch == "arm64"', {
+              'sources': [
+                'simd/arm/aarch64/jsimd.c',
+                'simd/arm/aarch64/jchuff-neon.c',
+              ],
+            }],
+            # For all Evergreen hardfp and softfp platforms.
+            ['floating_point_fpu=="vfpv3"', {
+              'cflags!': [
+                '-mfpu=<(floating_point_fpu)',
+              ],
+              'cflags': [
+                '-mfpu=neon',
+              ],
+            }],
+          ],
+        }],
         # x86_64 specific optimizations.
         ['<(yasm_exists) == 1 and target_arch == "x64"', {
           'sources!': [
diff --git a/src/third_party/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c b/src/third_party/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
index 066fcdb..19d94f7 100644
--- a/src/third_party/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
+++ b/src/third_party/libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
@@ -31,7 +31,7 @@
 #include "../../../jsimddct.h"
 #include "../../jsimd.h"
 #include "../jchuff.h"
-#include "../neon-compat.h"
+#include "neon-compat.h"
 
 #include <limits.h>
 
diff --git a/src/third_party/libjpeg-turbo/turbojpeg.c b/src/third_party/libjpeg-turbo/turbojpeg.c
index 3dd0e6f..72a2b90 100644
--- a/src/third_party/libjpeg-turbo/turbojpeg.c
+++ b/src/third_party/libjpeg-turbo/turbojpeg.c
@@ -50,6 +50,9 @@
 #include "starboard/client_porting/poem/string_poem.h"
 #include "starboard/configuration.h"
 #include "starboard/client_porting/poem/stdio_poem.h"
+
+#include "starboard/once.h"
+#include "starboard/thread.h"
 #else
 #include <stdio.h>
 #endif
@@ -70,9 +73,38 @@
 /* Error handling (based on example in example.txt) */
 
 #if defined(STARBOARD)
-static char errStr[JMSG_LENGTH_MAX] = "No error";
+static SbThreadLocalKey g_err_key = kSbThreadLocalKeyInvalid;
+static SbOnceControl g_err_once = SB_ONCE_INITIALIZER;
+
+void initialize_err_key(void) {
+  g_err_key = SbThreadCreateLocalKey(SbMemoryDeallocate);
+  SB_DCHECK(SbThreadIsValidLocalKey(g_err_key));
+}
+
+char* errStr() {
+  SbOnce(&g_err_once, initialize_err_key);
+
+  char* value = (char*)(SbThreadGetLocalValue(g_err_key));
+
+  if (value) {
+    return value;
+  }
+
+  value = SbMemoryAllocate(sizeof(char) * JMSG_LENGTH_MAX);
+
+  SB_DCHECK(value);
+  SB_DCHECK(SbThreadSetLocalValue(g_err_key, value));
+
+  memset(value, 0, JMSG_LENGTH_MAX);
+  strcpy(value, "No error");
+
+  return value;
+}
+
+#define GET_ERRSTR errStr()
 #else
 static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
+#define GET_ERRSTR errStr
 #endif
 
 struct my_error_mgr {
@@ -101,7 +133,7 @@
 
 static void my_output_message(j_common_ptr cinfo)
 {
-  (*cinfo->err->format_message) (cinfo, errStr);
+  (*cinfo->err->format_message) (cinfo, GET_ERRSTR);
 }
 
 static void my_emit_message(j_common_ptr cinfo, int msg_level)
@@ -146,7 +178,7 @@
     if (scan_no > 500) {
       snprintf(myprog->this->errStr, JMSG_LENGTH_MAX,
                "Progressive JPEG image has more than 500 scans");
-      snprintf(errStr, JMSG_LENGTH_MAX,
+      snprintf(GET_ERRSTR, JMSG_LENGTH_MAX,
                "Progressive JPEG image has more than 500 scans");
       myprog->this->isInstanceError = TRUE;
       myerr->warning = FALSE;
@@ -209,13 +241,13 @@
 };
 
 #define THROWG(m) { \
-  snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
+  snprintf(GET_ERRSTR, JMSG_LENGTH_MAX, "%s", m); \
   retval = -1;  goto bailout; \
 }
 #define THROW_UNIX(m) { \
   char message[512]; \
   SbSystemGetErrorString(errno, message, SB_ARRAY_SIZE_INT(message)); \
-  snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, message); \
+  snprintf(GET_ERRSTR, JMSG_LENGTH_MAX, "%s\n%s", m, message); \
   retval = -1;  goto bailout; \
 }
 #define THROW(m) { \
@@ -234,7 +266,7 @@
   j_decompress_ptr dinfo = NULL; \
   \
   if (!this) { \
-    snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX, "Invalid handle"); \
     return -1; \
   } \
   cinfo = &this->cinfo;  dinfo = &this->dinfo; \
@@ -246,7 +278,7 @@
   j_compress_ptr cinfo = NULL; \
   \
   if (!this) { \
-    snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX, "Invalid handle"); \
     return -1; \
   } \
   cinfo = &this->cinfo; \
@@ -258,7 +290,7 @@
   j_decompress_ptr dinfo = NULL; \
   \
   if (!this) { \
-    snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX, "Invalid handle"); \
     return -1; \
   } \
   dinfo = &this->dinfo; \
@@ -443,13 +475,13 @@
     this->isInstanceError = FALSE;
     return this->errStr;
   } else
-    return errStr;
+    return GET_ERRSTR;
 }
 
 
 DLLEXPORT char *tjGetErrorStr(void)
 {
-  return errStr;
+  return GET_ERRSTR;
 }
 
 
@@ -528,7 +560,7 @@
   tjinstance *this = NULL;
 
   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
-    snprintf(errStr, JMSG_LENGTH_MAX,
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX,
              "tjInitCompress(): Memory allocation failure");
     return NULL;
   }
@@ -1200,7 +1232,7 @@
   tjinstance *this;
 
   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
-    snprintf(errStr, JMSG_LENGTH_MAX,
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX,
              "tjInitDecompress(): Memory allocation failure");
     return NULL;
   }
@@ -1284,7 +1316,7 @@
 DLLEXPORT tjscalingfactor *tjGetScalingFactors(int *numscalingfactors)
 {
   if (numscalingfactors == NULL) {
-    snprintf(errStr, JMSG_LENGTH_MAX,
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX,
              "tjGetScalingFactors(): Invalid argument");
     return NULL;
   }
@@ -1891,7 +1923,7 @@
   tjhandle handle = NULL;
 
   if ((this = (tjinstance *)malloc(sizeof(tjinstance))) == NULL) {
-    snprintf(errStr, JMSG_LENGTH_MAX,
+    snprintf(GET_ERRSTR, JMSG_LENGTH_MAX,
              "tjInitTransform(): Memory allocation failure");
     return NULL;
   }
diff --git a/src/third_party/web_platform_tests/tools/wpt/requirements.txt b/src/third_party/web_platform_tests/tools/wpt/requirements.txt
index 7369cb8..a8ed785 100644
--- a/src/third_party/web_platform_tests/tools/wpt/requirements.txt
+++ b/src/third_party/web_platform_tests/tools/wpt/requirements.txt
@@ -1 +1 @@
-requests==2.14.2
+requests==2.26.0