Import Cobalt 23.lts.1.309136
diff --git a/cobalt/CHANGELOG.md b/cobalt/CHANGELOG.md
index 9e4ea3e..0a77358 100644
--- a/cobalt/CHANGELOG.md
+++ b/cobalt/CHANGELOG.md
@@ -3,10 +3,49 @@
 This document records all notable changes made to Cobalt since the last release.
 
 ## Version 23
- - **Deleted deprecated --webdriver_listen_ip switch.**
+ - **Cobalt now uses GN (Generate Ninja) meta-build system**
 
-   The `--webdriver_listen_ip` switch was deprecated in Cobalt 22 in favor of
-   `--dev_servers_listen_ip`.
+   Cobalt now uses GN instead of GYP which is now fully deprecated. This
+   significantly improves performance of meta-build generation.
+
+   A [migration guide](https://cobalt.dev/gen/starboard/build/doc/migrating_gyp_to_gn.html)
+   has been published to help with migrations.
+
+ - **Added support for HTTP caching**
+
+   Cobalt now supports caching HTTP requests and compiled V8 JS for improved
+   startup times.
+
+ - **Added Identifier For Advertising (IFA) support**
+
+   Cobalt now implements the IFA
+   [guidelines](https://iabtechlab.com/OTT-IFA).
+
+ - **Added experimental support for Web Workers**
+
+   Partial support for Dedicated Workers and Service Workers has been
+   implemented.
+
+ - **Added Watchdog for detecting application hangs**
+
+   A Watchdog has been added to Cobalt to detect if Cobalt has hanged and needs
+   to be restarted.
+
+ - **Evergreen supports LZ4 compressed binaries**
+
+   Evergreen binaries can now be stored compressed, using LZ4, for up to a 50%
+   reduction in Cobalt binary storage.
+
+ - **Reproducible builds are now supported for production configurations**
+
+   Improvements in the buildsystem now allows for fully reproducible builds for
+   production configurations. This allows for improved traceability for open
+   source releases.
+
+ - **Crash handler improvement**
+
+   The crash handler now launches on crash instead of start, saving
+   10MB of application memory.
 
 ## Version 22
  - **C++14 is required to compile Cobalt 22.**
diff --git a/cobalt/base/init_cobalt.cc b/cobalt/base/init_cobalt.cc
index 9632c5b..a22be1e 100644
--- a/cobalt/base/init_cobalt.cc
+++ b/cobalt/base/init_cobalt.cc
@@ -22,8 +22,10 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/path_service.h"
+#include "base/threading/platform_thread.h"
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/path_provider.h"
+#include "starboard/thread.h"
 
 namespace cobalt {
 namespace {
@@ -44,6 +46,11 @@
   // Register a path provider for Cobalt-specific paths.
   base::PathService::RegisterProvider(&PathProvider, paths::PATH_COBALT_START,
                                       paths::PATH_COBALT_END);
+
+  // Copy the Starboard thread name to the PlatformThread name.
+  char thread_name[128] = {'\0'};
+  SbThreadGetName(thread_name, 127);
+  base::PlatformThread::SetName(thread_name);
 }
 
 const char* GetInitialDeepLink() { return s_initial_deep_link.Get().c_str(); }
diff --git a/cobalt/black_box_tests/black_box_tests.py b/cobalt/black_box_tests/black_box_tests.py
index 14b8bc6..7ac37d0 100644
--- a/cobalt/black_box_tests/black_box_tests.py
+++ b/cobalt/black_box_tests/black_box_tests.py
@@ -69,7 +69,6 @@
     'persistent_cookie',
     'soft_mic_platform_service_test',
     'text_encoding_test',
-    'web_crypto_api_test',
     'web_debugger',
     'web_platform_tests',
     'web_worker_test',
diff --git a/cobalt/black_box_tests/testdata/web_crypto_api_test.html b/cobalt/black_box_tests/testdata/web_crypto_api_test.html
deleted file mode 100644
index 18feadf..0000000
--- a/cobalt/black_box_tests/testdata/web_crypto_api_test.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<!--
-  Copyright 2022 The Cobalt Authors. All Rights Reserved.
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<html>
-
-<head>
-  <title>Cobalt WebCryptoAPI Worker Test</title>
-  <script src='black_box_js_test_utils.js'></script>
-</head>
-
-<body>
-  <script>
-    const tests = [
-      'crypto_defined',
-      'subtle_encrypt_decrypt',
-      'subtle_importKey_verify_sign',
-      'getRandomValues',
-    ];
-    let pending = tests.length;
-    const worker = new Worker('web_crypto_api_test.js');
-    worker.onmessage = event => {
-      assertEqual('pass', event.data);
-      pending--;
-      if (pending === 0) {
-        onEndTest();
-      }
-    };
-    tests.map(test => worker.postMessage(test));
-    setupFinished();
-    window.setTimeout(notReached, 3000);
-  </script>
-</body>
-</html>
diff --git a/cobalt/black_box_tests/testdata/web_crypto_api_test.js b/cobalt/black_box_tests/testdata/web_crypto_api_test.js
deleted file mode 100644
index 9d653b1..0000000
--- a/cobalt/black_box_tests/testdata/web_crypto_api_test.js
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright 2022 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Tests borrowed from Web Platform Tests.
-// TODO(b/237798928): remove these tests and enable web platform tests. This
-//                    requires supporting sending/receiving objects using
-//                    |postMessage()|.
-
-const getHmacImportedTestVectors = (() => {
-  const plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]);
-  const raw = {
-    'SHA-1': new Uint8Array([71, 162, 7, 70, 209, 113, 121, 219, 101, 224, 167, 157, 237, 255, 199, 253, 241, 129, 8, 27]),
-    'SHA-256': new Uint8Array([229, 136, 236, 8, 17, 70, 61, 118, 114, 65, 223, 16, 116, 180, 122, 228, 7, 27, 81, 242, 206, 54, 83, 123, 166, 156, 205, 195, 253, 194, 183, 168]),
-    'SHA-384': new Uint8Array([107, 29, 162, 142, 171, 31, 88, 42, 217, 113, 142, 255, 224, 94, 35, 213, 253, 44, 152, 119, 162, 217, 68, 63, 144, 190, 192, 147, 190, 206, 46, 167, 210, 53, 76, 208, 189, 197, 225, 71, 210, 233, 0, 147, 115, 73, 68, 136]),
-    'SHA-512': new Uint8Array([93, 204, 53, 148, 67, 170, 246, 82, 250, 19, 117, 214, 179, 230, 31, 220, 242, 155, 180, 162, 139, 213, 211, 220, 250, 64, 248, 47, 144, 107, 178, 128, 4, 85, 219, 3, 181, 211, 31, 185, 114, 161, 90, 109, 1, 3, 162, 78, 86, 209, 86, 161, 25, 192, 229, 161, 233, 42, 68, 195, 197, 101, 124, 249])
-  };
-  const signatures = {
-    'SHA-1': new Uint8Array([5, 51, 144, 42, 153, 248, 82, 78, 229, 10, 240, 29, 56, 222, 220, 225, 51, 217, 140, 160]),
-    'SHA-256': new Uint8Array([133, 164, 12, 234, 46, 7, 140, 40, 39, 163, 149, 63, 251, 102, 194, 123, 41, 26, 71, 43, 13, 112, 160, 0, 11, 69, 216, 35, 128, 62, 235, 84]),
-    'SHA-384': new Uint8Array([33, 124, 61, 80, 240, 186, 154, 109, 110, 174, 30, 253, 215, 165, 24, 254, 46, 56, 128, 181, 130, 164, 13, 6, 30, 144, 153, 193, 224, 38, 239, 88, 130, 84, 139, 93, 92, 236, 221, 85, 152, 217, 155, 107, 111, 48, 87, 255]),
-    'SHA-512': new Uint8Array([97, 251, 39, 140, 63, 251, 12, 206, 43, 241, 207, 114, 61, 223, 216, 239, 31, 147, 28, 12, 97, 140, 37, 144, 115, 36, 96, 89, 57, 227, 249, 162, 198, 244, 175, 105, 11, 218, 52, 7, 220, 47, 87, 112, 246, 160, 164, 75, 149, 77, 100, 163, 50, 227, 238, 8, 33, 171, 248, 43, 127, 62, 153, 193])
-  };
-  return key_usage => Object.keys(raw).map(hash =>
-    crypto.subtle.importKey(
-      'raw',
-      raw[hash],
-      {name: 'HMAC', hash},
-      false,
-      key_usage,
-    ).then(key => ({
-      name: `HMAC with ${hash}`,
-      algorithm: {name: 'HMAC', hash},
-      key,
-      plaintext,
-      signature: signatures[hash],
-    })));
-})();
-
-const copyBuffer = sourceBuffer => {
-  const source = new Uint8Array(sourceBuffer);
-  const copy = new Uint8Array(sourceBuffer.byteLength)
-  for (let i = 0; i < source.byteLength; i++) {
-    copy[i] = source[i];
-  }
-  return copy;
-};
-
-const copyAndAlterBuffer = buffer => {
-  const copy = copyBuffer(buffer);
-  copy[0] = 255 - copy[0];
-  return copy;
-};
-
-const runSubtleImportKeyVerifySignTests = () => {
-  const test_cases = [
-    {test_name: 'verification'},
-    {
-      test_name: 'verification with altered signature after call',
-      modify_vector: v => {
-        v.signature = copyBuffer(v.signature);
-        return v;
-      },
-    },
-    {
-      test_name: 'with altered plaintext after call',
-      modify_vector: v => {
-        v.plaintext = copyBuffer(v.plaintext);
-        return v;
-      },
-    },
-    {
-      test_name: 'no verify usage',
-      key_usage: ['sign'],
-      expected_is_verified: null,
-      expected_err: err => err.name === 'InvalidAccessError',
-    },
-    {
-      test_name: 'round trip',
-      sign: true,
-    },
-    {
-      test_name: 'verification failure due to wrong plaintext',
-      modify_vector: v => {
-        v.plaintext = copyAndAlterBuffer(v.plaintext);
-        return v;
-      },
-      expected_is_verified: false,
-    },
-    {
-      test_name: 'verification failure due to wrong signature',
-      modify_vector: v => {
-        v.signature = copyAndAlterBuffer(v.signature);
-        return v;
-      },
-      expected_is_verified: false,
-    },
-    {
-      test_name: 'verification failure due to wrong signature length',
-      modify_vector: v => {
-        v.signature = v.signature.slice(1);
-        return v;
-      },
-      expected_is_verified: false,
-    },
-  ];
-  return Promise.all(test_cases.flatMap(({
-    key_usage=['verify', 'sign'],
-    modify_vector=(v => v),
-    sign=false,
-    expected_is_verified=true,
-    expected_err=(err => false),
-    test_name,
-  }) =>
-    getHmacImportedTestVectors(key_usage).map(promise_vector =>
-      promise_vector
-        .then(modify_vector)
-        .then(vector =>
-          (sign ? crypto.subtle.sign(vector.algorithm, vector.key, vector.plaintext) : Promise.resolve(vector.signature))
-            .then(signature => crypto.subtle.verify(vector.algorithm, vector.key, signature, vector.plaintext))
-            .then(is_verified => [is_verified === expected_is_verified, 'is_verified'])
-            .catch(err => [expected_err(err), 'error for test ' + vector.name + ': ' + err.message + '\''])
-            .then(result => [result[0], vector.name + ' ' + test_name + ': ' + result[1]]))
-        .catch(err => [false, test_name + ': importVectorKeys failed'])
-    )));
-};
-
-const runGetRandomValuesTests = () =>
-  Promise.resolve([
-    Int8Array,
-    Int16Array,
-    Int32Array,
-    Uint8Array,
-    Uint8ClampedArray,
-    Uint16Array,
-    Uint32Array,
-  ].map(ctor => [crypto.getRandomValues(new ctor(8)).constructor === ctor, `${ctor.name} mismatch`]));
-
-const getAesCtrImportedTestVectors = (() => {
-  const plaintext = new Uint8Array([84, 104, 105, 115, 32, 115,
-      112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110,
-      32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32,
-      74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80,
-      73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114,
-      109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114,
-      121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32,
-      111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105,
-      110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97,
-      116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97,
-      115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115,
-      105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110,
-      101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32,
-      118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110,
-      44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112,
-      116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99,
-      114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100,
-      105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105,
-      116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97,
-      110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112,
-      108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111,
-      32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110,
-      100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116,
-      104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97,
-      116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115,
-      115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102,
-      111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112,
-      101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
-      101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32,
-      65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111,
-      109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101,
-      114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110,
-      116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111,
-      99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111,
-      100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32,
-      97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102,
-      105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32,
-      97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116,
-      121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105,
-      99, 97, 116, 105, 111, 110, 115, 46]);
-
-  // We want some random key bytes of various sizes.
-  // These were randomly generated from a script.
-  const keyBytes = {
-      128: new Uint8Array([222, 192, 212, 252, 191, 60, 71,
-          65, 200, 146, 218, 189, 28, 212, 192, 78]),
-      192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208,
-          61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]),
-      256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236,
-          233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116,
-          216, 0, 11, 191, 48])
-  };
-
-  // AES-CTR needs a 16 byte (128 bit) counter.
-  const counter = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39,
-      167, 108, 39, 162, 132]);
-
-  // Results. These were created using the Python cryptography module.
-
-  // AES-CTR produces ciphertext
-  const ciphertext = {
-    128: new Uint8Array([233, 17, 117, 253, 164, 245, 234, 87, 197, 43, 13, 0, 11, 190, 152, 175, 104, 192, 165, 144, 88, 174, 237, 138, 181, 183, 6, 53, 3, 161, 206, 71, 13, 121, 218, 209, 116, 249, 10, 170, 250, 165, 68, 157, 132, 141, 200, 178, 197, 87, 209, 231, 250, 75, 154, 65, 162, 251, 30, 159, 234, 20, 20, 181, 147, 218, 180, 12, 4, 241, 75, 79, 129, 64, 15, 228, 60, 147, 153, 1, 129, 176, 150, 161, 85, 97, 22, 154, 234, 23, 127, 16, 4, 22, 226, 11, 104, 16, 176, 14, 225, 176, 79, 239, 103, 243, 190, 222, 40, 186, 244, 212, 29, 57, 125, 175, 21, 17, 233, 2, 13, 119, 102, 233, 230, 4, 16, 222, 56, 225, 67, 45, 191, 250, 15, 153, 45, 193, 240, 212, 117, 101, 68, 232, 199, 101, 175, 125, 247, 6, 249, 14, 0, 157, 185, 56, 76, 51, 228, 77, 234, 84, 60, 42, 119, 187, 213, 32, 34, 222, 65, 231, 215, 26, 73, 141, 231, 254, 185, 118, 14, 180, 126, 80, 51, 102, 200, 141, 204, 45, 26, 56, 119, 136, 222, 45, 143, 120, 231, 44, 43, 221, 136, 21, 188, 138, 84, 232, 208, 238, 226, 117, 104, 60, 165, 4, 18, 144, 240, 49, 173, 90, 68, 84, 239, 161, 124, 196, 144, 119, 24, 243, 239, 75, 117, 254, 219, 209, 53, 131, 37, 79, 68, 26, 21, 168, 163, 50, 59, 18, 244, 11, 143, 190, 188, 129, 108, 249, 180, 104, 216, 215, 165, 160, 251, 84, 132, 152, 195, 154, 110, 216, 70, 21, 248, 148, 146, 152, 56, 174, 248, 227, 1, 102, 15, 118, 182, 50, 73, 63, 35, 112, 159, 237, 253, 94, 16, 127, 120, 38, 127, 51, 27, 96, 163, 140, 20, 111, 151, 16, 72, 74, 74, 205, 239, 241, 16, 179, 183, 116, 95, 248, 58, 168, 203, 93, 233, 225, 91, 17, 226, 10, 120, 85, 114, 4, 31, 40, 82, 161, 152, 17, 86, 237, 207, 7, 228, 110, 182, 65, 68, 68, 156, 206, 116, 185, 204, 148, 22, 58, 111, 218, 138, 225, 146, 25, 114, 29, 96, 183, 87, 181, 181, 236, 113, 141, 171, 213, 9, 84, 182, 230, 163, 147, 246, 86, 246, 52, 111, 64, 34, 157, 12, 80, 224, 28, 21, 112, 31, 42, 79, 229, 210, 90, 23, 78, 223, 155, 144, 238, 12, 14, 191, 158, 6, 181, 254, 0, 85, 134, 56, 161, 234, 55, 129, 64, 59, 12, 146, 6, 217, 232, 20, 214, 167, 159, 183, 165, 96, 96, 225, 199, 23, 106, 243, 108, 106, 26, 214, 53, 152, 26, 155, 253, 128, 7, 216, 207, 109, 159, 147, 240, 232, 226, 43, 147, 169, 162, 204, 215, 9, 10, 177, 223, 99, 206, 163, 240, 64]),
-    192: new Uint8Array([98, 123, 235, 65, 14, 86, 80, 133, 88, 104, 244, 125, 165, 185, 163, 4, 3, 230, 62, 58, 113, 222, 46, 210, 17, 155, 95, 19, 125, 125, 70, 234, 105, 54, 23, 246, 114, 9, 237, 191, 9, 194, 34, 254, 156, 11, 50, 216, 80, 178, 185, 221, 132, 154, 27, 85, 82, 49, 241, 123, 23, 106, 119, 134, 203, 0, 151, 66, 149, 218, 124, 247, 227, 233, 236, 184, 88, 234, 174, 250, 83, 168, 33, 15, 122, 26, 96, 213, 210, 4, 52, 92, 20, 12, 64, 12, 209, 197, 69, 100, 15, 56, 60, 63, 241, 52, 18, 189, 93, 146, 47, 60, 33, 200, 218, 243, 43, 169, 17, 108, 19, 199, 174, 33, 107, 186, 57, 95, 167, 138, 180, 187, 53, 113, 208, 148, 190, 48, 167, 53, 209, 52, 153, 184, 231, 63, 168, 54, 179, 238, 93, 130, 125, 3, 149, 119, 60, 25, 142, 150, 183, 193, 29, 18, 3, 219, 235, 219, 26, 116, 217, 196, 108, 6, 96, 103, 212, 48, 227, 91, 124, 77, 181, 169, 18, 111, 123, 83, 26, 169, 230, 88, 103, 185, 153, 93, 143, 152, 142, 231, 41, 226, 226, 156, 179, 206, 212, 67, 18, 193, 187, 53, 252, 214, 15, 228, 246, 131, 170, 101, 134, 212, 100, 170, 146, 47, 57, 125, 50, 230, 51, 246, 74, 175, 129, 196, 178, 206, 176, 52, 153, 39, 77, 24, 186, 99, 137, 83, 105, 111, 168, 35, 176, 24, 29, 170, 223, 74, 160, 138, 247, 12, 102, 233, 136, 59, 172, 228, 242, 84, 13, 34, 155, 80, 80, 87, 180, 143, 129, 61, 213, 54, 41, 8, 183, 102, 126, 179, 127, 77, 55, 176, 152, 41, 131, 85, 86, 225, 87, 216, 139, 226, 196, 195, 210, 34, 33, 161, 249, 153, 205, 197, 128, 41, 28, 121, 6, 159, 25, 211, 168, 137, 26, 217, 249, 113, 81, 141, 18, 1, 250, 228, 68, 238, 74, 54, 99, 167, 236, 176, 199, 148, 161, 143, 156, 51, 189, 204, 59, 240, 151, 170, 85, 63, 23, 38, 152, 199, 12, 81, 217, 244, 178, 231, 249, 159, 224, 107, 214, 58, 127, 116, 143, 219, 155, 80, 55, 213, 171, 80, 127, 235, 20, 247, 12, 104, 228, 147, 202, 124, 143, 110, 223, 76, 221, 154, 175, 143, 185, 237, 222, 189, 104, 218, 72, 244, 55, 253, 138, 183, 92, 231, 68, 176, 239, 171, 100, 10, 63, 61, 194, 228, 15, 133, 216, 45, 60, 135, 203, 142, 127, 153, 172, 223, 213, 230, 220, 189, 223, 234, 156, 134, 238, 220, 251, 104, 209, 117, 175, 47, 46, 148, 6, 61, 216, 215, 39, 30, 116, 212, 45, 112, 202, 227, 198, 98, 253, 97, 177, 120, 74, 238, 68, 99, 240, 96, 43, 88, 166]),
-    256: new Uint8Array([55, 82, 154, 67, 47, 80, 186, 78, 83, 56, 95, 130, 102, 236, 61, 236, 204, 236, 234, 222, 122, 226, 147, 149, 233, 41, 16, 118, 201, 91, 185, 162, 79, 71, 146, 252, 221, 110, 165, 137, 75, 129, 94, 219, 93, 94, 64, 34, 250, 190, 5, 90, 6, 177, 167, 224, 25, 121, 85, 91, 87, 152, 56, 100, 191, 35, 1, 156, 177, 179, 127, 253, 173, 176, 87, 247, 40, 207, 178, 175, 10, 51, 209, 70, 52, 76, 251, 160, 172, 203, 77, 191, 97, 58, 123, 238, 82, 60, 166, 214, 134, 14, 71, 74, 156, 15, 77, 6, 141, 76, 10, 205, 148, 204, 85, 203, 242, 30, 66, 133, 202, 21, 17, 108, 151, 2, 15, 44, 51, 180, 88, 80, 8, 248, 254, 151, 201, 226, 156, 6, 39, 197, 212, 124, 72, 217, 75, 232, 139, 155, 22, 199, 242, 223, 116, 10, 141, 42, 7, 85, 99, 5, 184, 43, 145, 159, 122, 135, 202, 46, 209, 157, 178, 114, 98, 194, 119, 194, 19, 242, 167, 236, 162, 94, 90, 106, 219, 234, 67, 11, 162, 225, 6, 17, 152, 23, 16, 84, 40, 90, 255, 158, 8, 105, 198, 56, 220, 213, 36, 203, 241, 242, 85, 218, 103, 90, 202, 214, 215, 134, 121, 169, 149, 139, 122, 143, 155, 178, 29, 217, 197, 128, 173, 25, 111, 154, 14, 76, 106, 101, 0, 215, 187, 33, 223, 116, 205, 89, 52, 206, 60, 77, 141, 31, 57, 211, 74, 42, 219, 88, 210, 36, 196, 128, 151, 136, 124, 222, 157, 59, 225, 70, 163, 234, 59, 173, 228, 198, 134, 76, 249, 228, 69, 181, 196, 194, 179, 239, 78, 43, 143, 94, 234, 10, 177, 192, 185, 171, 231, 164, 254, 91, 44, 11, 29, 148, 223, 107, 18, 149, 61, 50, 115, 38, 14, 128, 189, 9, 77, 236, 151, 163, 23, 122, 156, 236, 11, 80, 66, 190, 24, 4, 4, 12, 148, 57, 64, 59, 143, 114, 247, 66, 111, 167, 86, 173, 98, 102, 207, 44, 134, 89, 231, 64, 50, 157, 208, 210, 79, 159, 133, 73, 118, 98, 202, 215, 57, 247, 29, 97, 116, 1, 28, 119, 248, 243, 31, 180, 66, 38, 40, 141, 251, 134, 129, 126, 241, 113, 22, 50, 28, 113, 187, 158, 217, 125, 182, 233, 144, 246, 32, 88, 88, 15, 0, 102, 131, 67, 31, 34, 150, 98, 241, 213, 227, 205, 175, 254, 3, 53, 70, 124, 167, 38, 53, 104, 140, 147, 158, 200, 179, 45, 100, 101, 246, 81, 166, 53, 247, 60, 10, 78, 127, 10, 173, 176, 232, 31, 91, 203, 250, 236, 38, 113, 172, 151, 253, 194, 253, 50, 242, 76, 148, 23, 117, 195, 122, 104, 16, 212, 177, 113, 188, 138, 186, 144, 168, 102, 3])
-  };
-
-  const keyLengths = [128, 192, 256];
-  return key_usage => {
-    const keyLength = keyLengths[0];
-    return {
-    passing: keyLengths.map(keyLength =>
-      crypto.subtle.importKey(
-          'raw',
-          keyBytes[keyLength],
-          {name: 'AES-CTR'},
-          false,
-          key_usage,
-        )
-        .then(key => ({
-          name: "AES-CTR " + keyLength + "-bit key",
-          algorithm: {name: "AES-CTR", counter, length: 64},
-          key,
-          plaintext,
-          result: ciphertext[keyLength]
-        }))),
-    failing: keyLengths.flatMap(keyLength =>
-      [
-        {
-          name: "AES-CTR " + keyLength + "-bit key, 0-bit counter",
-          algorithm: {name: "AES-CTR", counter, length: 0},
-          plaintext,
-          result: ciphertext[keyLength],
-        },
-        {
-          name: "AES-CTR " + keyLength + "-bit key, 129-bit counter",
-          algorithm: {name: "AES-CTR", counter, length: 129},
-          plaintext,
-          result: ciphertext[keyLength],
-        },
-      ].map(vector =>
-        crypto.subtle.importKey(
-          'raw',
-          keyBytes[keyLength],
-          {name: 'AES-CTR'},
-          false,
-          key_usage,
-        ).then(key => Object.assign(vector, {key}))
-      )
-    )
-  }};
-})();
-
-const equalBuffers = (a, b) => {
-  if (a.byteLength !== b.byteLength) {
-    return false;
-  }
-  const aBytes = new Uint8Array(a);
-  const bBytes = new Uint8Array(b);
-
-  for (let i = 0; i < a.byteLength; i++) {
-    if (aBytes[i] !== bBytes[i]) {
-      return false;
-    }
-  }
-
-  return true;
-};
-
-const runSubtleEncryptDecrptTests = () => {
-  const test_cases = [
-    {test_name: 'encrypt'},
-    {
-      test_name: 'altered plaintext',
-      modify_vector: vector => {
-        vector.plaintext = copyBuffer(vector.plaintext);
-        return vector;
-      },
-    },
-    {
-      test_name: 'decrypt',
-      encrypt_or_decrypt: 'decrypt',
-    },
-    {
-      test_name: 'decrypt with altered ciphertext',
-      modify_vector: vector => {
-        vector.result = copyBuffer(vector.result);
-        return vector;
-      },
-      encrypt_or_decrypt: 'decrypt',
-    },
-    {
-      test_name: 'without encrypt usage',
-      key_usage: ['decrypt'],
-      expected_result: false,
-      expected_err: err => err.name === 'InvalidAccessError',
-    },
-    {
-      test_name: 'mismatched key and algorithm',
-      modify_vector: vector => {
-        vector.algorithm.name = 'AES-CBC';
-        vector.algorithm.iv = new Uint8Array(16); // Need syntactically valid parameter to get to error being checked.
-        return vector;
-      },
-      expected_result: false,
-      expected_err: err => err.name === 'InvalidAccessError',
-    },
-    {
-      test_name: 'without decrypt usage',
-      key_usage: ['encrypt'],
-      encrypt_or_decrypt: 'decrypt',
-      expected_result: false,
-      expected_err: err => err.name === 'InvalidAccessError',
-    },
-    {
-      test_name: 'OperationError due to data lengths',
-      vector_type: 'failing',
-      expected_result: false,
-      expected_err: err => err.name === 'OperationError',
-    },
-    {
-      test_name: 'OperationError due to data lengths for decryption',
-      vector_type: 'failing',
-      encrypt_or_decrypt: 'decrypt',
-      expected_result: false,
-      expected_err: err => err.name === 'OperationError',
-    },
-  ];
-  return Promise.all([test_cases].flatMap(({
-    key_usage=['encrypt', 'decrypt'],
-    modify_vector=(v => v),
-    vector_type='passing',
-    encrypt_or_decrypt='encrypt',
-    expected_result=true,
-    expected_err=(err => false),
-    test_name,
-  }) =>
-    getAesCtrImportedTestVectors(key_usage)[vector_type].map(promiseVector =>
-      promiseVector
-        .then(modify_vector)
-        .then(vector =>
-          (encrypt_or_decrypt === 'encrypt' ?
-            crypto.subtle.encrypt(vector.algorithm, vector.key, vector.plaintext)
-              .then(result => [equalBuffers(result, vector.result) === expected_result, 'Should return expected result'])
-              .catch(err => [expected_err(err), 'encrypt error for test ' + vector.name + ': ' + err.message]) :
-            crypto.subtle.decrypt(vector.algorithm, vector.key, vector.result)
-              .then(plaintext => [equalBuffers(plaintext, vector.plaintext) === expected_result, 'Should return expected result'])
-              .catch(err => [expected_err(err), 'decrypt error for test ' + vector.name + ': ' + err.message]))
-            .then(result => [result[0], vector.name + ' ' + test_name + ': ' + result[1]])
-          .catch(err => [false, 'importKey failed for ' + vector.name])))));
-};
-
-const postResults = (promise_results, suite_name) =>
-  promise_results
-    .then(results => {
-      const failures = results.filter(([status, message]) => !status).map(([status, message]) => message);
-      if (failures.length === 0) {
-        postMessage('pass');
-      } else {
-        postMessage('\n' + `Failures for ${suite_name}:` + '\n' + failures.join('\n'));
-      }
-    })
-    .catch(err => postMessage(err + ': ' + err.message));
-
-onmessage = event => {
-  if (event.data === 'crypto_defined') {
-    postResults(Promise.resolve([
-      [!!crypto, 'crypto is not defined.'],
-      [!!crypto.subtle, 'crypto.subtle is not defined.'],
-      [!!crypto.subtle.importKey, 'crypto.subtle.importKey is not defined.'],
-      [!!crypto.subtle.sign, 'crypto.subtle.sign is not defined.'],
-      [!!crypto.subtle.verify, 'crypto.subtle.verify is not defined.'],
-      [!!crypto.getRandomValues, 'crypto.getRandomValues is not defined.'],
-    ]), event.data);
-  } else if (event.data === 'subtle_encrypt_decrypt') {
-    postResults(runSubtleEncryptDecrptTests(), event.data);
-  } else if (event.data === 'subtle_importKey_verify_sign') {
-    postResults(runSubtleImportKeyVerifySignTests(), event.data);
-  } else if (event.data === 'getRandomValues') {
-    postResults(runGetRandomValuesTests(), event.data);
-  } else {
-    postMessage(`Unexpected message received: ${event.data}.`);
-  }
-};
diff --git a/cobalt/black_box_tests/testdata/web_worker_test.html b/cobalt/black_box_tests/testdata/web_worker_test.html
index ce7c99f..2976cdf 100644
--- a/cobalt/black_box_tests/testdata/web_worker_test.html
+++ b/cobalt/black_box_tests/testdata/web_worker_test.html
@@ -26,8 +26,19 @@
 
 <body>
 <script>
+    var window_error_event_count = 0;
+    window.onerror = function (message) {
+        window_error_event_count += 1;
+        assertIncludes('TypeError: self.Foo is not a function', message);
+        console.log('window got onerror', message);
+    };
+
     var message_event_count = 0;
     console.log('running');
+
+    // This is expected trigger a an error event on window.
+    var worker_with_error = new Worker('web_worker_test_with_syntax_error.js');
+
     var worker = new Worker('web_worker_test.js');
     console.log(worker);
     worker.onmessage = function (event) {
@@ -86,6 +97,7 @@
                 window.setTimeout(
                     () => {
                         assertEqual(14, message_event_count);
+                        assertEqual(1, window_error_event_count);
                         onEndTest();
                     }, 250);
                 break;
diff --git a/cobalt/black_box_tests/testdata/web_worker_test_with_syntax_error.js b/cobalt/black_box_tests/testdata/web_worker_test_with_syntax_error.js
new file mode 100644
index 0000000..041a9b9
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/web_worker_test_with_syntax_error.js
@@ -0,0 +1,16 @@
+// Copyright 2022 The Cobalt Authors.All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+console.log('Running script with syntax error.');
+self.Foo('Bar');
diff --git a/cobalt/black_box_tests/tests/web_crypto_api_test.py b/cobalt/black_box_tests/tests/web_crypto_api_test.py
deleted file mode 100644
index 96f453d..0000000
--- a/cobalt/black_box_tests/tests/web_crypto_api_test.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright 2022 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Tests basic WebCrypto API functionality."""
-
-from cobalt.black_box_tests import black_box_tests
-from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
-
-
-class WebCryptoApiTest(black_box_tests.BlackBoxTestCase):
-  """Test basic WebCrypto API functionality."""
-
-  def test_simple(self):
-
-    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
-      url = server.GetURL(file_name='testdata/web_crypto_api_test.html')
-
-      with self.CreateCobaltRunner(url=url) as runner:
-        runner.WaitForJSTestsSetup()
-        self.assertTrue(runner.JSTestsSucceeded())
diff --git a/cobalt/demos/content/BUILD.gn b/cobalt/demos/content/BUILD.gn
index c23edf1..9e6b742 100644
--- a/cobalt/demos/content/BUILD.gn
+++ b/cobalt/demos/content/BUILD.gn
@@ -111,8 +111,8 @@
 
   if (is_internal_build) {
     sources += [
+      "internal/runtime-dump.html",
       "javascript-fuzzer/index.html",
-      "kabuki/runtime-dump.html",
       "media-element-demo/public/assets/ac3.mp4",
       "media-element-demo/public/assets/dash-audio.mp4",
       "media-element-demo/public/assets/dash-video-1080p.mp4",
diff --git a/cobalt/dom/source_buffer.cc b/cobalt/dom/source_buffer.cc
index f49d151..ffaee80 100644
--- a/cobalt/dom/source_buffer.cc
+++ b/cobalt/dom/source_buffer.cc
@@ -105,11 +105,38 @@
 
 }  // namespace
 
+SourceBuffer::OnInitSegmentReceivedHelper::OnInitSegmentReceivedHelper(
+    SourceBuffer* source_buffer)
+    : source_buffer_(source_buffer) {
+  DCHECK(source_buffer_);
+}
+
+void SourceBuffer::OnInitSegmentReceivedHelper::Detach() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  source_buffer_ = nullptr;
+}
+
+void SourceBuffer::OnInitSegmentReceivedHelper::TryToRunOnInitSegmentReceived(
+    std::unique_ptr<MediaTracks> tracks) {
+  if (!task_runner_->BelongsToCurrentThread()) {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&OnInitSegmentReceivedHelper::TryToRunOnInitSegmentReceived,
+                   this, base::Passed(&tracks)));
+    return;
+  }
+
+  if (source_buffer_) {
+    source_buffer_->OnInitSegmentReceived(std::move(tracks));
+  }
+}
+
 SourceBuffer::SourceBuffer(script::EnvironmentSettings* settings,
                            const std::string& id, MediaSource* media_source,
                            bool asynchronous_reduction_enabled,
                            ChunkDemuxer* chunk_demuxer, EventQueue* event_queue)
     : web::EventTarget(settings),
+      on_init_segment_received_helper_(new OnInitSegmentReceivedHelper(this)),
       id_(id),
       asynchronous_reduction_enabled_(asynchronous_reduction_enabled),
       evict_extra_in_bytes_(GetEvictExtraInBytes(settings)),
@@ -130,7 +157,8 @@
 
   chunk_demuxer_->SetTracksWatcher(
       id_,
-      base::Bind(&SourceBuffer::OnInitSegmentReceived, base::Unretained(this)));
+      base::Bind(&OnInitSegmentReceivedHelper::TryToRunOnInitSegmentReceived,
+                 on_init_segment_received_helper_));
   chunk_demuxer_->SetParseWarningCallback(
       id, base::BindRepeating([](::media::SourceBufferParseWarning warning) {
         LOG(WARNING) << "Encountered SourceBufferParseWarning "
@@ -388,6 +416,9 @@
     return;
   }
 
+  DCHECK(on_init_segment_received_helper_);
+  on_init_segment_received_helper_->Detach();
+
   if (active_algorithm_handle_) {
     active_algorithm_handle_->Abort();
     active_algorithm_handle_ = nullptr;
@@ -437,13 +468,6 @@
 
 void SourceBuffer::OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks) {
   if (!first_initialization_segment_received_) {
-    // This can be called from non-web thread when the append is async.
-    if (!web_task_runner_->BelongsToCurrentThread()) {
-      web_task_runner_->PostTask(
-          FROM_HERE, base::Bind(&SourceBuffer::OnInitSegmentReceived, this,
-                                base::Passed(&tracks)));
-      return;
-    }
     media_source_->SetSourceBufferActive(this, true);
     first_initialization_segment_received_ = true;
   }
diff --git a/cobalt/dom/source_buffer.h b/cobalt/dom/source_buffer.h
index 65bbe48..0cf7f28 100644
--- a/cobalt/dom/source_buffer.h
+++ b/cobalt/dom/source_buffer.h
@@ -52,6 +52,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/timer/timer.h"
@@ -142,6 +143,28 @@
  private:
   typedef ::media::MediaTracks MediaTracks;
 
+  // SourceBuffer is inherited from base::RefCounted<> and its ref count cannot
+  // be used on non-web threads.  On the other hand the call to
+  // OnInitSegmentReceived() may happen on a worker thread for algorithm
+  // offloading.  This object allows to manage the life time of the SourceBuffer
+  // object across threads while still maintain it as a sub-class of
+  // base::RefCounted<>.
+  class OnInitSegmentReceivedHelper
+      : public base::RefCountedThreadSafe<OnInitSegmentReceivedHelper> {
+   public:
+    explicit OnInitSegmentReceivedHelper(SourceBuffer* source_buffer);
+    void Detach();
+    void TryToRunOnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks);
+
+   private:
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner_ =
+        base::MessageLoop::current()->task_runner();
+    // The access to |source_buffer_| always happens on |task_runner_|, and
+    // needn't be explicitly synchronized by a mutex.
+    SourceBuffer* source_buffer_;
+  };
+
+
   void OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks);
   void ScheduleEvent(base::Token event_name);
   void ScheduleAndMaybeDispatchImmediately(base::Token event_name);
@@ -162,14 +185,13 @@
       const std::string& track_type,
       const std::string& byte_stream_track_id) const;
 
+  scoped_refptr<OnInitSegmentReceivedHelper> on_init_segment_received_helper_;
   const std::string id_;
   const bool asynchronous_reduction_enabled_;
   const size_t evict_extra_in_bytes_;
 
   MediaSource* media_source_;
   ChunkDemuxer* chunk_demuxer_;
-  scoped_refptr<base::SingleThreadTaskRunner> web_task_runner_ =
-      base::MessageLoop::current()->task_runner();
   scoped_refptr<TrackDefaultList> track_defaults_ = new TrackDefaultList(NULL);
   EventQueue* event_queue_;
 
diff --git a/cobalt/dom/window.cc b/cobalt/dom/window.cc
index aca75dc..0284726 100644
--- a/cobalt/dom/window.cc
+++ b/cobalt/dom/window.cc
@@ -504,35 +504,33 @@
   // 7. Let event be a new trusted ErrorEvent object that does not bubble but is
   //    cancelable, and which has the event name error.
   // NOTE: Cobalt does not currently support trusted events.
-  web::ErrorEventInit error_event_init;
-  error_event_init.set_bubbles(false);
-  error_event_init.set_cancelable(true);
+  web::ErrorEventInit error;
+  error.set_bubbles(false);
+  error.set_cancelable(true);
 
   if (error_report.is_muted) {
     // 6. If script has muted errors, then set message to "Script error.", set
     //    location to the empty string, set line and col to 0, and set error
     //    object to null.
-    error_event_init.set_message("Script error.");
-    error_event_init.set_filename("");
-    error_event_init.set_lineno(0);
-    error_event_init.set_colno(0);
-    error_event_init.set_error(NULL);
+    error.set_message("Script error.");
+    error.set_filename("");
+    error.set_lineno(0);
+    error.set_colno(0);
+    error.set_error(NULL);
   } else {
     // 8. Initialize event's message attribute to message.
-    error_event_init.set_message(error_report.message);
+    error.set_message(error_report.message);
     // 9. Initialize event's filename attribute to location.
-    error_event_init.set_filename(error_report.filename);
+    error.set_filename(error_report.filename);
     // 10. Initialize event's lineno attribute to line.
-    error_event_init.set_lineno(error_report.line_number);
+    error.set_lineno(error_report.line_number);
     // 11. Initialize event's colno attribute to col.
-    error_event_init.set_colno(error_report.column_number);
+    error.set_colno(error_report.column_number);
     // 12. Initialize event's error attribute to error object.
-    error_event_init.set_error(error_report.error ? error_report.error.get()
-                                                  : NULL);
+    error.set_error(error_report.error ? error_report.error.get() : NULL);
   }
 
-  scoped_refptr<web::ErrorEvent> error_event(
-      new web::ErrorEvent(base::Tokens::error(), error_event_init));
+  scoped_refptr<web::ErrorEvent> error_event(new web::ErrorEvent(error));
 
   // 13. Dispatch event at target.
   DispatchEvent(error_event);
diff --git a/cobalt/h5vcc/h5vcc_crash_log.cc b/cobalt/h5vcc/h5vcc_crash_log.cc
index 564feda..737d64c 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.cc
+++ b/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -126,7 +126,8 @@
 bool H5vccCrashLog::Register(const std::string& name,
                              const std::string& description,
                              WatchdogState watchdog_state,
-                             int64_t time_interval, int64_t time_wait,
+                             int64_t time_interval_milliseconds,
+                             int64_t time_wait_milliseconds,
                              WatchdogReplace watchdog_replace) {
   watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
   if (watchdog) {
@@ -162,7 +163,8 @@
         replace = watchdog::NONE;
     }
     return watchdog->Register(name, description, monitor_state,
-                              time_interval * 1000, time_wait * 1000, replace);
+                              time_interval_milliseconds * 1000,
+                              time_wait_milliseconds * 1000, replace);
   }
   return false;
 }
diff --git a/cobalt/h5vcc/h5vcc_crash_log.h b/cobalt/h5vcc/h5vcc_crash_log.h
index 60a51b1..b85c0c5 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.h
+++ b/cobalt/h5vcc/h5vcc_crash_log.h
@@ -35,8 +35,10 @@
   void TriggerCrash(H5vccCrashType intent);
 
   bool Register(const std::string& name, const std::string& description,
-                WatchdogState watchdog_state, int64_t time_interval,
-                int64_t time_wait, WatchdogReplace watchdog_replace);
+                WatchdogState watchdog_state,
+                int64_t time_interval_milliseconds,
+                int64_t time_wait_milliseconds,
+                WatchdogReplace watchdog_replace);
 
   bool Unregister(const std::string& name);
 
diff --git a/cobalt/h5vcc/h5vcc_crash_log.idl b/cobalt/h5vcc/h5vcc_crash_log.idl
index 2bbf4df..7892219 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.idl
+++ b/cobalt/h5vcc/h5vcc_crash_log.idl
@@ -34,16 +34,17 @@
   //   description, information on the Watchdog client.
   //   watchdog_state, application state to continue monitoring client up to.
   //     Inclusive.
-  //   time_interval, maximum number of milliseconds allowed between pings
-  //     before triggering a Watchdog violation. Min value of 1000.
-  //   time_wait, number of milliseconds to initially wait before Watchdog
-  //     violations can be triggered. Reapplies after client resumes from idle
-  //     state due to application state changes.
+  //   time_interval_milliseconds, maximum number of milliseconds allowed
+  //     between pings before triggering a Watchdog violation. Min value of
+  //     1000.
+  //   time_wait_milliseconds, number of milliseconds to initially wait before
+  //     Watchdog violations can be triggered. Reapplies after client resumes
+  //     from idle state due to application state changes.
   //   watchdog_replace, behavior with previously registered Watchdog clients
   //     of the same name.
   boolean register(DOMString name, DOMString description,
-                   WatchdogState watchdog_state, long long time_interval,
-                   long long time_wait, WatchdogReplace watchdog_replace);
+                   WatchdogState watchdog_state, long long time_interval_milliseconds,
+                   long long time_wait_milliseconds, WatchdogReplace watchdog_replace);
 
   // Returns true if Watchdog client was unregistered. Name determines the
   // Watchdog client to unregister.
@@ -61,7 +62,7 @@
   // Example json:
   // {
   //   "test-name":{
-  //     "description":"test-description",
+  //     "description":"test-desc",
   //     "violations":[
   //       {
   //         "monitorState":"kApplicationStateStarted",
diff --git a/cobalt/h5vcc/h5vcc_updater.cc b/cobalt/h5vcc/h5vcc_updater.cc
index 97162bb..abf3f41 100644
--- a/cobalt/h5vcc/h5vcc_updater.cc
+++ b/cobalt/h5vcc/h5vcc_updater.cc
@@ -75,6 +75,22 @@
   return cobalt::updater::GetLibrarySha256(index);
 }
 
+bool H5vccUpdater::GetUseCompressedUpdates() const {
+  if (!updater_module_) {
+    return false;
+  }
+
+  return updater_module_->GetUseCompressedUpdates();
+}
+
+void H5vccUpdater::SetUseCompressedUpdates(bool use_compressed_updates) {
+  if (!updater_module_) {
+    return;
+  }
+
+  return updater_module_->SetUseCompressedUpdates(use_compressed_updates);
+}
+
 #endif  // SB_IS(EVERGREEN)
 }  // namespace h5vcc
 }  // namespace cobalt
diff --git a/cobalt/h5vcc/h5vcc_updater.h b/cobalt/h5vcc/h5vcc_updater.h
index 22c3185..66f3506 100644
--- a/cobalt/h5vcc/h5vcc_updater.h
+++ b/cobalt/h5vcc/h5vcc_updater.h
@@ -48,6 +48,9 @@
 
   std::string GetLibrarySha256(uint16 index) const;
 
+  bool GetUseCompressedUpdates() const;
+  void SetUseCompressedUpdates(bool use_compressed_updates);
+
 #else
   H5vccUpdater() {}
 #endif
diff --git a/cobalt/h5vcc/h5vcc_updater.idl b/cobalt/h5vcc/h5vcc_updater.idl
index d71d603..96ff583 100644
--- a/cobalt/h5vcc/h5vcc_updater.idl
+++ b/cobalt/h5vcc/h5vcc_updater.idl
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Custom API for getting and setting update channel for evergreen.
+// Custom API for getting and setting Evergreen update settings.
 
 [Conditional=SB_IS_EVERGREEN]
 interface H5vccUpdater {
@@ -27,4 +27,12 @@
 
   DOMString getLibrarySha256(unsigned short index);
 
+  boolean getUseCompressedUpdates();
+
+  // Sets whether compressed (rather than uncompressed) Evergreen binaries
+  // should be requested, overwriting any value set by the
+  // --use_compressed_updates command-line switch. This API should only be used
+  // for testing.
+  void setUseCompressedUpdates(boolean use_compressed_updates);
+
 };
diff --git a/cobalt/script/script_runner.cc b/cobalt/script/script_runner.cc
index a379a10..8463a48 100644
--- a/cobalt/script/script_runner.cc
+++ b/cobalt/script/script_runner.cc
@@ -99,7 +99,7 @@
 #if defined(HANDLE_CORE_DUMP)
     script_runner_log.Get().IncrementFailCount();
 #endif
-    return "";
+    return result;
   }
 #if defined(HANDLE_CORE_DUMP)
   script_runner_log.Get().IncrementSuccessCount();
diff --git a/cobalt/updater/configurator.cc b/cobalt/updater/configurator.cc
index c953dc0..ea03435 100644
--- a/cobalt/updater/configurator.cc
+++ b/cobalt/updater/configurator.cc
@@ -70,6 +70,12 @@
   if (network_module != nullptr) {
     user_agent_string_ = network_module->GetUserAgent();
   }
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          browser::switches::kUseCompressedUpdates)) {
+    use_compressed_updates_.store(true);
+  } else {
+    use_compressed_updates_.store(false);
+  }
 }
 Configurator::~Configurator() { LOG(INFO) << "Configurator::~Configurator"; }
 
@@ -171,11 +177,8 @@
       "certscope", GetDeviceProperty(kSbSystemPropertyCertificationScope)));
 
   // Compression status
-  params.insert(std::make_pair(
-      "usecompressedupdates", base::CommandLine::ForCurrentProcess()->HasSwitch(
-                                  browser::switches::kUseCompressedUpdates)
-                                  ? "True"
-                                  : "False"));
+  params.insert(std::make_pair("usecompressedupdates",
+                               GetUseCompressedUpdates() ? "True" : "False"));
 
   return params;
 }
@@ -282,5 +285,13 @@
   previous_updater_status_ = status;
 }
 
+bool Configurator::GetUseCompressedUpdates() const {
+  return use_compressed_updates_.load();
+}
+
+void Configurator::SetUseCompressedUpdates(bool use_compressed_updates) {
+  use_compressed_updates_.store(use_compressed_updates);
+}
+
 }  // namespace updater
 }  // namespace cobalt
diff --git a/cobalt/updater/configurator.h b/cobalt/updater/configurator.h
index 54bb6c0..630861a 100644
--- a/cobalt/updater/configurator.h
+++ b/cobalt/updater/configurator.h
@@ -28,6 +28,7 @@
 #include "cobalt/network/network_module.h"
 #include "components/update_client/configurator.h"
 #include "components/update_client/persisted_data.h"
+#include "starboard/atomic.h"
 
 class GURL;
 class PrefService;
@@ -98,6 +99,9 @@
   void SetMinFreeSpaceBytes(uint64_t bytes) override;
   uint64_t GetMinFreeSpaceBytes() override;
 
+  bool GetUseCompressedUpdates() const override;
+  void SetUseCompressedUpdates(bool use_compressed_updates) override;
+
  private:
   friend class base::RefCountedThreadSafe<Configurator>;
   ~Configurator() override;
@@ -117,6 +121,7 @@
   std::string user_agent_string_;
   uint64_t min_free_space_bytes_ = 48 * 1024 * 1024;
   base::Lock min_free_space_bytes_lock_;
+  starboard::atomic_bool use_compressed_updates_;
 
   DISALLOW_COPY_AND_ASSIGN(Configurator);
 };
diff --git a/cobalt/updater/updater_module.cc b/cobalt/updater/updater_module.cc
index dfbb14b..00460fd 100644
--- a/cobalt/updater/updater_module.cc
+++ b/cobalt/updater/updater_module.cc
@@ -327,7 +327,7 @@
   LOG(INFO) << "UpdaterModule::GetUpdaterChannel";
   auto config = updater_configurator_;
   if (!config) {
-    LOG(ERROR) << "UpdaterModule::GetUpdaterChannel: missing config";
+    LOG(ERROR) << "UpdaterModule::GetUpdaterChannel: missing configurator";
     return "";
   }
 
@@ -347,7 +347,7 @@
   LOG(INFO) << "UpdaterModule::GetUpdaterStatus";
   auto config = updater_configurator_;
   if (!config) {
-    LOG(ERROR) << "UpdaterModule::GetUpdaterStatus: missing configuration";
+    LOG(ERROR) << "UpdaterModule::GetUpdaterStatus: missing configurator";
     return "";
   }
 
@@ -409,5 +409,36 @@
   }
 }
 
+// TODO(b/244367569): refactor similar getter and setter methods in this class
+// to share common code.
+bool UpdaterModule::GetUseCompressedUpdates() const {
+  LOG(INFO) << "UpdaterModule::GetUseCompressedUpdates";
+  auto config = updater_configurator_;
+  if (!config) {
+    LOG(ERROR) << "UpdaterModule::GetUseCompressedUpdates: missing "
+               << "configurator";
+    return false;
+  }
+
+  bool use_compressed_updates = config->GetUseCompressedUpdates();
+  LOG(INFO) << "UpdaterModule::GetUseCompressedUpdates use_compressed_updates="
+            << use_compressed_updates;
+  return use_compressed_updates;
+}
+
+void UpdaterModule::SetUseCompressedUpdates(bool use_compressed_updates) {
+  auto config = updater_configurator_;
+  if (!config) {
+    LOG(ERROR) << "UpdaterModule::SetUseCompressedUpdates: missing "
+               << "configurator";
+    return;
+  }
+
+  LOG(INFO) << "UpdaterModule::SetUseCompressedUpdates use_compressed_updates="
+            << use_compressed_updates;
+  config->SetUseCompressedUpdates(use_compressed_updates);
+}
+
+
 }  // namespace updater
 }  // namespace cobalt
diff --git a/cobalt/updater/updater_module.h b/cobalt/updater/updater_module.h
index 0d6f793..157b121 100644
--- a/cobalt/updater/updater_module.h
+++ b/cobalt/updater/updater_module.h
@@ -153,6 +153,9 @@
 
   void SetMinFreeSpaceBytes(uint64_t bytes);
 
+  bool GetUseCompressedUpdates() const;
+  void SetUseCompressedUpdates(bool use_compressed_updates);
+
  private:
   std::unique_ptr<base::Thread> updater_thread_;
   scoped_refptr<update_client::UpdateClient> update_client_;
diff --git a/cobalt/watchdog/BUILD.gn b/cobalt/watchdog/BUILD.gn
index 8e231cf..131d334 100644
--- a/cobalt/watchdog/BUILD.gn
+++ b/cobalt/watchdog/BUILD.gn
@@ -25,3 +25,16 @@
     "//starboard/common",
   ]
 }
+
+target(gtest_target_type, "watchdog_test") {
+  testonly = true
+
+  sources = [ "watchdog_test.cc" ]
+
+  deps = [
+    ":watchdog",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/cobalt/watchdog/watchdog.cc b/cobalt/watchdog/watchdog.cc
index 0a46c90..8ad8474 100644
--- a/cobalt/watchdog/watchdog.cc
+++ b/cobalt/watchdog/watchdog.cc
@@ -64,11 +64,21 @@
 
 bool Watchdog::Initialize(
     persistent_storage::PersistentSettings* persistent_settings) {
+  return InitializeCustom(persistent_settings,
+                          std::string(kWatchdogViolationsJson),
+                          kWatchdogMonitorFrequency);
+}
+
+bool Watchdog::InitializeCustom(
+    persistent_storage::PersistentSettings* persistent_settings,
+    std::string watchdog_file_name, int64_t watchdog_monitor_frequency) {
   persistent_settings_ = persistent_settings;
   is_disabled_ = !GetPersistentSettingWatchdogEnable();
 
   if (is_disabled_) return true;
 
+  watchdog_file_name_ = watchdog_file_name;
+  watchdog_monitor_frequency_ = watchdog_monitor_frequency;
   SB_CHECK(SbMutexCreate(&mutex_));
   pending_write_ = false;
   write_wait_time_microseconds_ = kWatchdogWriteWaitTime;
@@ -124,16 +134,16 @@
 
 std::string Watchdog::GetWatchdogFilePath() {
   // Gets the Watchdog violations file path with lazy initialization.
-  if (watchdog_file_ == "") {
+  if (watchdog_file_path_ == "") {
     // Sets Watchdog violations file path.
     std::vector<char> cache_dir(kSbFileMaxPath + 1, 0);
     SbSystemGetPath(kSbSystemPathCacheDirectory, cache_dir.data(),
                     kSbFileMaxPath);
-    watchdog_file_ = std::string(cache_dir.data()) + kSbFileSepString +
-                     std::string(kWatchdogViolationsJson);
-    SB_LOG(INFO) << "[Watchdog] Violations filepath: " << watchdog_file_;
+    watchdog_file_path_ =
+        std::string(cache_dir.data()) + kSbFileSepString + watchdog_file_name_;
+    SB_LOG(INFO) << "[Watchdog] Violations filepath: " << watchdog_file_path_;
   }
-  return watchdog_file_;
+  return watchdog_file_path_;
 }
 
 void Watchdog::WriteWatchdogViolations() {
@@ -207,7 +217,7 @@
     if (watchdog_violation) MaybeTriggerCrash(context);
 
     SB_CHECK(SbMutexRelease(&(static_cast<Watchdog*>(context))->mutex_));
-    SbThreadSleep(kWatchdogMonitorFrequency);
+    SbThreadSleep(static_cast<Watchdog*>(context)->watchdog_monitor_frequency_);
   }
   return nullptr;
 }
@@ -389,16 +399,17 @@
 
 bool Watchdog::Register(std::string name, std::string description,
                         base::ApplicationState monitor_state,
-                        int64_t time_interval, int64_t time_wait,
-                        Replace replace) {
+                        int64_t time_interval_microseconds,
+                        int64_t time_wait_microseconds, Replace replace) {
   if (is_disabled_) return true;
 
   // Validates parameters.
-  if (time_interval < kWatchdogMonitorFrequency || time_wait < 0) {
+  if (time_interval_microseconds < watchdog_monitor_frequency_ ||
+      time_wait_microseconds < 0) {
     SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
-    if (time_interval < kWatchdogMonitorFrequency) {
+    if (time_interval_microseconds < watchdog_monitor_frequency_) {
       SB_DLOG(ERROR) << "[Watchdog] Time interval less than min: "
-                     << kWatchdogMonitorFrequency;
+                     << watchdog_monitor_frequency_;
     } else {
       SB_DLOG(ERROR) << "[Watchdog] Time wait is negative.";
     }
@@ -433,8 +444,8 @@
   client->description = description;
   client->ping_infos = base::Value(base::Value::Type::LIST);
   client->monitor_state = monitor_state;
-  client->time_interval_microseconds = time_interval;
-  client->time_wait_microseconds = time_wait;
+  client->time_interval_microseconds = time_interval_microseconds;
+  client->time_wait_microseconds = time_wait_microseconds;
   client->time_registered_microseconds = current_time;
   client->time_registered_monotonic_microseconds = current_monotonic_time;
   client->time_last_pinged_microseconds = current_time;
@@ -515,7 +526,7 @@
   return client_exists;
 }
 
-std::string Watchdog::GetWatchdogViolations() {
+std::string Watchdog::GetWatchdogViolations(bool clear) {
   // Gets a json string containing the Watchdog violations since the last
   // call (up to the kWatchdogMaxViolations limit).
 
@@ -536,13 +547,13 @@
     watchdog_json = std::string(buffer.data());
 
     // Removes all Watchdog violations.
-    if (violations_map_) {
-      base::DictionaryValue** dict = nullptr;
-      violations_map_->GetAsDictionary(dict);
-      if (dict) (*dict)->Clear();
-      violations_count_ = 0;
+    if (clear) {
+      if (violations_map_) {
+        static_cast<base::DictionaryValue*>(violations_map_.get())->Clear();
+        violations_count_ = 0;
+      }
+      starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
     }
-    starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
     SB_LOG(INFO) << "[Watchdog] Reading violations:\n" << watchdog_json;
   } else {
     SB_LOG(INFO) << "[Watchdog] No violations.";
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index 1532f79..04ba81d 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -73,16 +73,22 @@
 class Watchdog : public Singleton<Watchdog> {
  public:
   bool Initialize(persistent_storage::PersistentSettings* persistent_settings);
+  // Directly used for testing only.
+  bool InitializeCustom(
+      persistent_storage::PersistentSettings* persistent_settings,
+      std::string watchdog_file_name, int64_t watchdog_monitor_frequency);
   bool InitializeStub();
   void Uninitialize();
+  std::string GetWatchdogFilePath();
   void UpdateState(base::ApplicationState state);
   bool Register(std::string name, std::string description,
-                base::ApplicationState monitor_state, int64_t time_interval,
-                int64_t time_wait = 0, Replace replace = NONE);
+                base::ApplicationState monitor_state,
+                int64_t time_interval_microseconds,
+                int64_t time_wait_microseconds = 0, Replace replace = NONE);
   bool Unregister(const std::string& name, bool lock = true);
   bool Ping(const std::string& name);
   bool Ping(const std::string& name, const std::string& info);
-  std::string GetWatchdogViolations();
+  std::string GetWatchdogViolations(bool clear = true);
   bool GetPersistentSettingWatchdogEnable();
   void SetPersistentSettingWatchdogEnable(bool enable_watchdog);
   bool GetPersistentSettingWatchdogCrash();
@@ -94,7 +100,6 @@
 #endif  // defined(_DEBUG)
 
  private:
-  std::string GetWatchdogFilePath();
   void WriteWatchdogViolations();
   static void* Monitor(void* context);
   static void UpdateViolationsMap(void* context, Client* client,
@@ -104,8 +109,10 @@
   static void MaybeWriteWatchdogViolations(void* context);
   static void MaybeTriggerCrash(void* context);
 
-  // Watchdog violations file path.
-  std::string watchdog_file_;
+  // The Watchdog violations json filename.
+  std::string watchdog_file_name_;
+  // The Watchdog violations json filepath.
+  std::string watchdog_file_path_;
   // Access to persistent settings.
   persistent_storage::PersistentSettings* persistent_settings_;
   // Flag to disable Watchdog. When disabled, Watchdog behaves like a stub
@@ -137,6 +144,8 @@
   SbThread watchdog_thread_;
   // Flag to stop monitor thread.
   bool is_monitoring_;
+  // The frequency in microseconds of monitor loops.
+  int64_t watchdog_monitor_frequency_;
 
 #if defined(_DEBUG)
   starboard::Mutex delay_lock_;
diff --git a/cobalt/watchdog/watchdog_test.cc b/cobalt/watchdog/watchdog_test.cc
new file mode 100644
index 0000000..fb0cf19
--- /dev/null
+++ b/cobalt/watchdog/watchdog_test.cc
@@ -0,0 +1,489 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "cobalt/watchdog/watchdog.h"
+#include "starboard/common/file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace watchdog {
+
+namespace {
+
+const char kWatchdogViolationsJson[] = "watchdog_test.json";
+const int64_t kWatchdogMonitorFrequency = 100000;
+
+}  // namespace
+
+class WatchdogTest : public testing::Test {
+ protected:
+  WatchdogTest() {}
+
+  void SetUp() final {
+    watchdog_ = new watchdog::Watchdog();
+    watchdog_->InitializeCustom(nullptr, std::string(kWatchdogViolationsJson),
+                                kWatchdogMonitorFrequency);
+    watchdog_->GetWatchdogViolations();
+  }
+
+  void TearDown() final {
+    watchdog_->Uninitialize();
+    delete watchdog_;
+    watchdog_ = nullptr;
+  }
+
+  base::Value CreateDummyViolationDict(std::string desc, int begin, int end) {
+    base::Value violation_dict(base::Value::Type::DICTIONARY);
+    violation_dict.SetKey("description", base::Value(desc));
+    base::Value list(base::Value::Type::LIST);
+    for (int i = begin; i < end; i++)
+      list.GetList().emplace_back(CreateDummyViolation(i));
+    violation_dict.SetKey("violations", list.Clone());
+    return violation_dict.Clone();
+  }
+
+  base::Value CreateDummyViolation(int timestamp_violation) {
+    base::Value violation(base::Value::Type::DICTIONARY);
+    base::Value ping_infos(base::Value::Type::LIST);
+    violation.SetKey("pingInfos", ping_infos.Clone());
+    violation.SetKey("monitorState",
+                     base::Value(std::string(GetApplicationStateString(
+                         base::kApplicationStateStarted))));
+    violation.SetKey("timeIntervalMilliseconds", base::Value("0"));
+    violation.SetKey("timeWaitMilliseconds", base::Value("0"));
+    violation.SetKey("timestampRegisteredMilliseconds", base::Value("0"));
+    violation.SetKey("timestampLastPingedMilliseconds", base::Value("0"));
+    violation.SetKey("timestampViolationMilliseconds",
+                     base::Value(std::to_string(timestamp_violation)));
+    violation.SetKey("violationDurationMilliseconds", base::Value("0"));
+    base::Value registered_clients(base::Value::Type::LIST);
+    violation.SetKey("registeredClients", registered_clients.Clone());
+    return violation.Clone();
+  }
+
+  watchdog::Watchdog* watchdog_;
+};
+
+TEST_F(WatchdogTest, RedundantRegistersShouldFail) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+
+  ASSERT_FALSE(watchdog_->Register("test-name", "test-desc",
+                                   base::kApplicationStateStarted,
+                                   kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0, PING));
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0, ALL));
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, RegisterOnlyAcceptsValidParameters) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0));
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+  ASSERT_FALSE(watchdog_->Register("test-name-1", "test-desc-1",
+                                   base::kApplicationStateStarted, 1, 0));
+  ASSERT_FALSE(watchdog_->Unregister("test-name-1"));
+  ASSERT_FALSE(watchdog_->Register("test-name-2", "test-desc-2",
+                                   base::kApplicationStateStarted, 0, 0));
+  ASSERT_FALSE(watchdog_->Unregister("test-name-2"));
+  ASSERT_FALSE(watchdog_->Register("test-name-3", "test-desc-3",
+                                   base::kApplicationStateStarted, -1, 0));
+  ASSERT_FALSE(watchdog_->Unregister("test-name-3"));
+  ASSERT_TRUE(watchdog_->Register("test-name-4", "test-desc-4",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 1));
+  ASSERT_TRUE(watchdog_->Unregister("test-name-4"));
+  ASSERT_TRUE(watchdog_->Register("test-name-5", "test-desc-5",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0));
+  ASSERT_TRUE(watchdog_->Unregister("test-name-5"));
+  ASSERT_FALSE(watchdog_->Register("test-name-6", "test-desc-6",
+                                   base::kApplicationStateStarted,
+                                   kWatchdogMonitorFrequency, -1));
+  ASSERT_FALSE(watchdog_->Unregister("test-name-6"));
+}
+
+TEST_F(WatchdogTest, UnmatchedUnregistersShouldFail) {
+  ASSERT_FALSE(watchdog_->Unregister("test-name"));
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+  ASSERT_FALSE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, UnmatchedPingsShouldFail) {
+  ASSERT_FALSE(watchdog_->Ping("test-name"));
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Ping("test-name"));
+  ASSERT_TRUE(watchdog_->Ping("test-name"));
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+  ASSERT_FALSE(watchdog_->Ping("test-name"));
+}
+
+TEST_F(WatchdogTest, PingOnlyAcceptsValidParameters) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Ping("test-name", "42"));
+  ASSERT_FALSE(
+      watchdog_->Ping("test-name",
+                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+                      "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+                      "xxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, ViolationsJsonShouldPersistAndBeValid) {
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Ping("test-name", "test-ping"));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+  TearDown();
+  watchdog_ = new watchdog::Watchdog();
+  watchdog_->InitializeCustom(nullptr, std::string(kWatchdogViolationsJson),
+                              kWatchdogMonitorFrequency);
+
+  // Validates Violation json file.
+  std::string json = watchdog_->GetWatchdogViolations();
+  ASSERT_NE(json, "");
+  std::unique_ptr<base::Value> violations_map = base::JSONReader::Read(json);
+  ASSERT_NE(violations_map, nullptr);
+  base::Value* violation_dict = violations_map->FindKey("test-name");
+  ASSERT_NE(violation_dict, nullptr);
+  base::Value* description = violation_dict->FindKey("description");
+  ASSERT_NE(description, nullptr);
+  ASSERT_EQ(description->GetString(), "test-desc");
+  base::Value* violations = violation_dict->FindKey("violations");
+  ASSERT_NE(violations, nullptr);
+  ASSERT_EQ(violations->GetList().size(), 1);
+  base::Value* monitor_state = violations->GetList()[0].FindKey("monitorState");
+  ASSERT_NE(monitor_state, nullptr);
+  ASSERT_EQ(
+      monitor_state->GetString(),
+      std::string(GetApplicationStateString(base::kApplicationStateStarted)));
+  base::Value* ping_infos = violations->GetList()[0].FindKey("pingInfos");
+  ASSERT_NE(ping_infos, nullptr);
+  ASSERT_EQ(ping_infos->GetList().size(), 1);
+  base::Value* info = ping_infos->GetList()[0].FindKey("info");
+  ASSERT_NE(info, nullptr);
+  ASSERT_EQ(info->GetString(), "test-ping");
+  base::Value* timestamp_milliseconds =
+      ping_infos->GetList()[0].FindKey("timestampMilliseconds");
+  ASSERT_NE(timestamp_milliseconds, nullptr);
+  std::stoll(timestamp_milliseconds->GetString());
+  base::Value* registered_clients =
+      violations->GetList()[0].FindKey("registeredClients");
+  ASSERT_NE(registered_clients, nullptr);
+  ASSERT_EQ(registered_clients->GetList().size(), 1);
+  ASSERT_EQ(registered_clients->GetList()[0].GetString(), "test-name");
+  base::Value* time_interval_milliseconds =
+      violations->GetList()[0].FindKey("timeIntervalMilliseconds");
+  ASSERT_NE(time_interval_milliseconds, nullptr);
+  std::stoll(time_interval_milliseconds->GetString());
+  base::Value* time_wait_milliseconds =
+      violations->GetList()[0].FindKey("timeWaitMilliseconds");
+  ASSERT_NE(time_wait_milliseconds, nullptr);
+  std::stoll(time_wait_milliseconds->GetString());
+  base::Value* timestamp_last_pinged_milliseconds =
+      violations->GetList()[0].FindKey("timestampLastPingedMilliseconds");
+  ASSERT_NE(timestamp_last_pinged_milliseconds, nullptr);
+  std::stoll(timestamp_last_pinged_milliseconds->GetString());
+  base::Value* timestamp_registered_milliseconds =
+      violations->GetList()[0].FindKey("timestampRegisteredMilliseconds");
+  ASSERT_NE(timestamp_registered_milliseconds, nullptr);
+  std::stoll(timestamp_registered_milliseconds->GetString());
+  base::Value* timestamp_violation_milliseconds =
+      violations->GetList()[0].FindKey("timestampViolationMilliseconds");
+  ASSERT_NE(timestamp_violation_milliseconds, nullptr);
+  std::stoll(timestamp_violation_milliseconds->GetString());
+  base::Value* violation_duration_milliseconds =
+      violations->GetList()[0].FindKey("violationDurationMilliseconds");
+  ASSERT_NE(violation_duration_milliseconds, nullptr);
+  std::stoll(violation_duration_milliseconds->GetString());
+}
+
+TEST_F(WatchdogTest, RedundantViolationsShouldStack) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  std::string json = watchdog_->GetWatchdogViolations(false);
+  ASSERT_NE(json, "");
+  std::unique_ptr<base::Value> uncleared_violations_map =
+      base::JSONReader::Read(json);
+  ASSERT_NE(uncleared_violations_map, nullptr);
+  base::Value* violation_dict = uncleared_violations_map->FindKey("test-name");
+  base::Value* violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 1);
+  std::string uncleared_timestamp =
+      violations->GetList()[0]
+          .FindKey("timestampLastPingedMilliseconds")
+          ->GetString();
+  int64_t uncleared_duration =
+      std::stoll(violations->GetList()[0]
+                     .FindKey("violationDurationMilliseconds")
+                     ->GetString());
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  json = watchdog_->GetWatchdogViolations(false);
+  ASSERT_NE(json, "");
+  std::unique_ptr<base::Value> violations_map = base::JSONReader::Read(json);
+  ASSERT_NE(violations_map, nullptr);
+  violation_dict = violations_map->FindKey("test-name");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 1);
+  std::string timestamp = violations->GetList()[0]
+                              .FindKey("timestampLastPingedMilliseconds")
+                              ->GetString();
+  int64_t duration = std::stoll(violations->GetList()[0]
+                                    .FindKey("violationDurationMilliseconds")
+                                    ->GetString());
+  ASSERT_EQ(uncleared_timestamp, timestamp);
+  ASSERT_LT(uncleared_duration, duration);
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, ViolationsShouldResetAfterFetch) {
+  ASSERT_TRUE(watchdog_->Register("test-name-1", "test-desc-1",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_TRUE(watchdog_->Unregister("test-name-1"));
+  std::string json = watchdog_->GetWatchdogViolations();
+  ASSERT_NE(json.find("test-name-1"), std::string::npos);
+  ASSERT_EQ(json.find("test-name-2"), std::string::npos);
+  ASSERT_TRUE(watchdog_->Register("test-name-2", "test-desc-2",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_TRUE(watchdog_->Unregister("test-name-2"));
+  json = watchdog_->GetWatchdogViolations();
+  ASSERT_EQ(json.find("test-name-1"), std::string::npos);
+  ASSERT_NE(json.find("test-name-2"), std::string::npos);
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+}
+
+TEST_F(WatchdogTest, PingInfosAreEvictedAfterMax) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test_desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  for (int i = 0; i < 21; i++) {
+    ASSERT_TRUE(watchdog_->Ping("test-name", std::to_string(i)));
+  }
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  std::string json = watchdog_->GetWatchdogViolations();
+  ASSERT_NE(json, "");
+  std::unique_ptr<base::Value> violations_map = base::JSONReader::Read(json);
+  ASSERT_NE(violations_map, nullptr);
+  base::Value* violation_dict = violations_map->FindKey("test-name");
+  base::Value* violations = violation_dict->FindKey("violations");
+  base::Value* pingInfos = violations->GetList()[0].FindKey("pingInfos");
+  ASSERT_EQ(pingInfos->GetList().size(), 20);
+  ASSERT_EQ(pingInfos->GetList()[0].FindKey("info")->GetString(), "1");
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, ViolationsAreEvictedAfterMax) {
+  // Creates maxed Violation json file.
+  std::unique_ptr<base::Value> dummy_map =
+      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+  dummy_map->SetKey("test-name-1",
+                    CreateDummyViolationDict("test-desc-1", 0, 99));
+  dummy_map->SetKey("test-name-2",
+                    CreateDummyViolationDict("test-desc-2", 1, 102));
+  std::string json;
+  base::JSONWriter::Write(*dummy_map, &json);
+  starboard::ScopedFile file(watchdog_->GetWatchdogFilePath().c_str(),
+                             kSbFileCreateAlways | kSbFileWrite);
+  file.WriteAll(json.c_str(), static_cast<int>(json.size()));
+
+  ASSERT_TRUE(watchdog_->Register("test-name-3", "test-desc-3",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Register("test-name-4", "test-desc-4",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+
+  json = watchdog_->GetWatchdogViolations(false);
+  ASSERT_NE(json, "");
+  std::unique_ptr<base::Value> uncleared_violations_map =
+      base::JSONReader::Read(json);
+  ASSERT_NE(uncleared_violations_map, nullptr);
+  base::Value* violation_dict =
+      uncleared_violations_map->FindKey("test-name-1");
+  base::Value* violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 99);
+  violation_dict = uncleared_violations_map->FindKey("test-name-2");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 99);
+  ASSERT_EQ(violations->GetList()[0]
+                .FindKey("timestampViolationMilliseconds")
+                ->GetString(),
+            "3");
+  violation_dict = uncleared_violations_map->FindKey("test-name-3");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 1);
+  violation_dict = uncleared_violations_map->FindKey("test-name-4");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 1);
+
+  ASSERT_TRUE(watchdog_->Ping("test-name-3"));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+
+  json = watchdog_->GetWatchdogViolations();
+  ASSERT_NE(json, "");
+  std::unique_ptr<base::Value> violations_map = base::JSONReader::Read(json);
+  ASSERT_NE(violations_map, nullptr);
+  violation_dict = violations_map->FindKey("test-name-1");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 98);
+  ASSERT_EQ(violations->GetList()[0]
+                .FindKey("timestampViolationMilliseconds")
+                ->GetString(),
+            "1");
+  violation_dict = violations_map->FindKey("test-name-2");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 99);
+  violation_dict = violations_map->FindKey("test-name-3");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 2);
+  violation_dict = violations_map->FindKey("test-name-4");
+  violations = violation_dict->FindKey("violations");
+  ASSERT_EQ(violations->GetList().size(), 1);
+
+  ASSERT_TRUE(watchdog_->Unregister("test-name-3"));
+  ASSERT_TRUE(watchdog_->Unregister("test-name-4"));
+}
+
+TEST_F(WatchdogTest, UpdateStateShouldPreventViolations) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  watchdog_->UpdateState(base::kApplicationStateBlurred);
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, TimeWaitShouldPreventViolations) {
+  ASSERT_TRUE(watchdog_->Register(
+      "test-name", "test-desc", base::kApplicationStateStarted,
+      kWatchdogMonitorFrequency, kWatchdogMonitorFrequency * 3));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, PingsShouldPreventViolations) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency / 2);
+  ASSERT_TRUE(watchdog_->Ping("test-name"));
+  SbThreadSleep(kWatchdogMonitorFrequency / 2);
+  ASSERT_TRUE(watchdog_->Ping("test-name"));
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  SbThreadSleep(kWatchdogMonitorFrequency / 2);
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0, PING));
+  SbThreadSleep(kWatchdogMonitorFrequency / 2);
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0, PING));
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  SbThreadSleep(kWatchdogMonitorFrequency / 2);
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0, ALL));
+  SbThreadSleep(kWatchdogMonitorFrequency / 2);
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency, 0, ALL));
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, UnregisterShouldPreventViolations) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+}
+
+TEST_F(WatchdogTest, KillSwitchShouldPreventViolations) {
+  TearDown();
+  watchdog_ = new watchdog::Watchdog();
+  watchdog_->InitializeStub();
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+}
+
+TEST_F(WatchdogTest, FrequentConsecutiveViolationsShouldNotWrite) {
+  ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
+                                  base::kApplicationStateStarted,
+                                  kWatchdogMonitorFrequency));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  std::string write_json = "";
+  starboard::ScopedFile read_file(watchdog_->GetWatchdogFilePath().c_str(),
+                                  kSbFileOpenOnly | kSbFileRead);
+  if (read_file.IsValid()) {
+    int64_t kFileSize = read_file.GetSize();
+    std::vector<char> buffer(kFileSize + 1, 0);
+    read_file.ReadAll(buffer.data(), kFileSize);
+    write_json = std::string(buffer.data());
+  }
+  ASSERT_NE(write_json, "");
+  ASSERT_TRUE(watchdog_->Ping("test-name"));
+  SbThreadSleep(kWatchdogMonitorFrequency * 2);
+  ASSERT_TRUE(watchdog_->Unregister("test-name"));
+  std::string no_write_json = "";
+  starboard::ScopedFile read_file_again(
+      watchdog_->GetWatchdogFilePath().c_str(), kSbFileOpenOnly | kSbFileRead);
+  if (read_file_again.IsValid()) {
+    int64_t kFileSize = read_file_again.GetSize();
+    std::vector<char> buffer(kFileSize + 1, 0);
+    read_file_again.ReadAll(buffer.data(), kFileSize);
+    no_write_json = std::string(buffer.data());
+  }
+  ASSERT_NE(no_write_json, "");
+  ASSERT_EQ(write_json, no_write_json);
+  std::string json = watchdog_->GetWatchdogViolations();
+  ASSERT_NE(write_json, json);
+}
+
+}  // namespace watchdog
+}  // namespace cobalt
diff --git a/cobalt/web/error_event.h b/cobalt/web/error_event.h
index 2322f98..8fad2b6 100644
--- a/cobalt/web/error_event.h
+++ b/cobalt/web/error_event.h
@@ -33,18 +33,17 @@
 //   https://www.w3.org/TR/html50/webappapis.html#errorevent
 class ErrorEvent : public Event {
  public:
-  ErrorEvent() : Event(base::Tokens::error()), lineno_(0), colno_(0) {}
-  explicit ErrorEvent(const std::string& type)
-      : Event(type), lineno_(0), colno_(0) {}
-  ErrorEvent(const std::string& type, const web::ErrorEventInit& init_dict)
-      : Event(type, init_dict),
+  ErrorEvent() : Event(base::Tokens::error()) {}
+  explicit ErrorEvent(const std::string& type) : Event(type) {}
+  explicit ErrorEvent(const web::ErrorEventInit& init_dict)
+      : Event(base::Tokens::error(), init_dict),
         message_(init_dict.message()),
         filename_(init_dict.filename()),
         lineno_(init_dict.lineno()),
         colno_(init_dict.colno()) {
     InitError(init_dict);
   }
-  ErrorEvent(base::Token type, const web::ErrorEventInit& init_dict)
+  ErrorEvent(const std::string& type, const web::ErrorEventInit& init_dict)
       : Event(type, init_dict),
         message_(init_dict.message()),
         filename_(init_dict.filename()),
@@ -55,8 +54,8 @@
 
   // Web API: ErrorEvent
   //
-  std::string message() const { return message_; }
-  std::string filename() const { return filename_; }
+  const std::string& message() const { return message_; }
+  const std::string& filename() const { return filename_; }
   uint32 lineno() const { return lineno_; }
   uint32 colno() const { return colno_; }
 
@@ -82,8 +81,8 @@
 
   std::string message_;
   std::string filename_;
-  uint32 lineno_;
-  uint32 colno_;
+  uint32 lineno_ = 0;
+  uint32 colno_ = 0;
   std::unique_ptr<script::ValueHandleHolder::Reference> error_;
 };
 
diff --git a/cobalt/worker/worker.cc b/cobalt/worker/worker.cc
index b8e283b..aeed129 100644
--- a/cobalt/worker/worker.cc
+++ b/cobalt/worker/worker.cc
@@ -24,16 +24,16 @@
 #include "base/threading/thread.h"
 #include "cobalt/browser/user_agent_platform_info.h"
 #include "cobalt/script/environment_settings.h"
+#include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/web/error_event.h"
+#include "cobalt/web/error_event_init.h"
 #include "cobalt/web/message_port.h"
 #include "cobalt/worker/dedicated_worker_global_scope.h"
 #include "cobalt/worker/worker_global_scope.h"
 #include "cobalt/worker/worker_options.h"
 #include "cobalt/worker/worker_settings.h"
-
-#include "cobalt/script/v8c/conversion_helpers.h"
-#include "cobalt/script/v8c/v8c_exception_state.h"
-#include "cobalt/script/v8c/v8c_value_handle.h"
 #include "v8/include/v8.h"
 
 namespace cobalt {
@@ -253,10 +253,18 @@
   std::string retval = web_context_->script_runner()->Execute(
       content, script_location, mute_errors, &succeeded);
   if (!succeeded) {
-    LOG(WARNING) << "Script execution failed : " << retval;
     options_.outside_settings->context()
-        ->GetWindowOrWorkerGlobalScope()
-        ->DispatchEvent(new web::Event(base::Tokens::error()));
+        ->message_loop()
+        ->task_runner()
+        ->PostTask(FROM_HERE,
+                   base::BindOnce(
+                       [](web::Context* context, const std::string& message) {
+                         web::ErrorEventInit error;
+                         error.set_message(message);
+                         context->GetWindowOrWorkerGlobalScope()->DispatchEvent(
+                             new web::ErrorEvent(error));
+                       },
+                       options_.outside_settings->context(), retval));
   }
 
   // 24. Enable outside port's port message queue.
diff --git a/cobalt/worker/worker_global_scope.cc b/cobalt/worker/worker_global_scope.cc
index 3316581..662aaa5 100644
--- a/cobalt/worker/worker_global_scope.cc
+++ b/cobalt/worker/worker_global_scope.cc
@@ -359,7 +359,6 @@
       }
       if (!succeeded) {
         // TODO(): Handle script execution errors.
-        LOG(WARNING) << "Script Execution Failed : " << retval;
         web::DOMException::Raise(web::DOMException::kSyntaxErr,
                                  exception_state);
         return;
diff --git a/components/update_client/configurator.h b/components/update_client/configurator.h
index e4619e9..d1b5e40 100644
--- a/components/update_client/configurator.h
+++ b/components/update_client/configurator.h
@@ -182,6 +182,9 @@
 
   virtual void SetMinFreeSpaceBytes(uint64_t bytes) = 0;
   virtual uint64_t GetMinFreeSpaceBytes() = 0;
+
+  virtual bool GetUseCompressedUpdates() const = 0;
+  virtual void SetUseCompressedUpdates(bool use_compressed_updates) = 0;
 #endif
 
  protected:
diff --git a/components/update_client/test_configurator.h b/components/update_client/test_configurator.h
index 9b37687..fa07aa7 100644
--- a/components/update_client/test_configurator.h
+++ b/components/update_client/test_configurator.h
@@ -127,7 +127,7 @@
   void SetAppGuid(const std::string& app_guid);
 
 #if defined(STARBOARD)
-  // TODO: add unit tests for updater channels and status
+  // TODO: add unit tests for updater channels, status, and compressed updates.
   void SetChannel(const std::string& channel) override {}
   void CompareAndSwapChannelChanged(int old_value, int new_value) override {}
   std::string GetUpdaterStatus() const override { return ""; }
@@ -139,6 +139,9 @@
   void SetMinFreeSpaceBytes(uint64_t bytes) override {}
 
   uint64_t GetMinFreeSpaceBytes() override { return 0; }
+
+  bool GetUseCompressedUpdates() const override { return false; }
+  void SetUseCompressedUpdates(bool use_compressed_updates) override {}
 #else
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return &test_url_loader_factory_;
diff --git a/starboard/CHANGELOG.md b/starboard/CHANGELOG.md
index 99eece6..69ca601 100644
--- a/starboard/CHANGELOG.md
+++ b/starboard/CHANGELOG.md
@@ -63,6 +63,15 @@
 set `kSbMaxSystemPathCacheDirectorySize` to a larger value in
 "starboard/<PLATFORM_PATH>/configuration_constants.cc".
 
+### Add nplb tests to enforce performance of SbMediaCanPlayMimeAndKeySystem().
+Add SbMediaCanPlayMimeAndKeySystem.ValidatePerformance to enforce the
+performance of SbMediaCanPlayMimeAndKeySystem(). On platforms that fail such
+tests, MimeSupportabilityCache and KeySystemSupportabilityCache can be enabled
+to improve the performance. The caches store the results of previous queries and
+reuse them for repeated queries. Note that if caches are enabled, the platform
+need to clear the caches if there's any codec or audio/video output capability
+change.
+
 ## Version 13
 ### Changed lifecycle events to add support for a concealed state.
 
diff --git a/starboard/android/shared/audio_renderer_passthrough.cc b/starboard/android/shared/audio_renderer_passthrough.cc
index 7056e8d..b65ca06 100644
--- a/starboard/android/shared/audio_renderer_passthrough.cc
+++ b/starboard/android/shared/audio_renderer_passthrough.cc
@@ -435,7 +435,9 @@
   // silence can be observed after seeking on some audio receivers.
   // TODO: Consider reusing audio sink for non-passthrough playbacks, to see if
   //       it reduces latency after seeking.
-  audio_track_bridge_->PauseAndFlush();
+  if (audio_track_bridge_ && audio_track_bridge_->is_valid()) {
+    audio_track_bridge_->PauseAndFlush();
+  }
   seek_to_time_ = seek_to_time;
   paused_ = true;
   if (update_status_and_write_data_token_.is_valid()) {
diff --git a/starboard/audio_sink.h b/starboard/audio_sink.h
index 7d6c794..a3b9b14 100644
--- a/starboard/audio_sink.h
+++ b/starboard/audio_sink.h
@@ -117,7 +117,6 @@
 // |context|: A value that is passed back to all callbacks and is generally
 // used to point at a class or struct that contains state associated with the
 // audio sink.
-
 SB_EXPORT SbAudioSink
 SbAudioSinkCreate(int channels,
                   int sampling_frequency_hz,
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/.gitignore b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/.gitignore
deleted file mode 100644
index 1294fe2..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-out
-node_modules
-package-lock.json
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/.vscodeignore b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/.vscodeignore
deleted file mode 100644
index 5ff3c19..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/.vscodeignore
+++ /dev/null
@@ -1,9 +0,0 @@
-.vscode/**
-.vscode-test/**
-out/test/**
-test/**
-src/**
-**/*.map
-.gitignore
-tsconfig.json
-vsc-extension-quickstart.md
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/LICENSE b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/LICENSE
deleted file mode 100644
index c558158..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2017 The LLVM Developers
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/README.md b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/README.md
deleted file mode 100644
index f950cb6..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/README.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# vscode-clangd
-
-Provides C/C++ language IDE features for VS Code using [clangd](https://clang.llvm.org/extra/clangd.html).
-
-## Usage
-
-`vscode-clangd` provides the features designated by the [Language Server
-Protocol](https://github.com/Microsoft/language-server-protocol), such as
-code completion, code formatting and goto definition.
-
-**Note**: `clangd` is under heavy development, not all LSP features are
-implemented. See [Current Status](https://clang.llvm.org/extra/clangd.html#current-status)
-for details.
-
-To use `vscode-clangd` extension in VS Code, you need to install `vscode-clangd`
-from VS Code extension marketplace.
-
-`vscode-clangd` will attempt to find the `clangd` binary on your `PATH`.
-Alternatively, the `clangd` executable can be specified in your VS Code
-`settings.json` file:
-
-```json
-{
-    "clangd.path": "/absolute/path/to/clangd"
-}
-```
-
-To obtain `clangd` binary, please see the [installing Clangd](https://clang.llvm.org/extra/clangd.html#installing-clangd).
-
-## Development
-
-A guide of developing `vscode-clangd` extension.
-
-### Requirements
-
-* VS Code
-* node.js and npm
-
-### Steps
-
-1. Make sure you disable the installed `vscode-clangd` extension in VS Code.
-2. Make sure you have clangd in /usr/bin/clangd or edit src/extension.ts to
-point to the binary.
-3. In order to start a development instance of VS code extended with this, run:
-
-```bash
-   $ cd /path/to/clang-tools-extra/clangd/clients/clangd-vscode/
-   $ npm install
-   $ code .
-   # When VS Code starts, press <F5>.
-```
-
-## Publish to VS Code Marketplace
-
-New changes to `clangd-vscode` are not released until a new version is published
-to the marketplace.
-
-### Requirements
-
-* Make sure install the `vsce` command (`npm install -g vsce`)
-* `llvm-vs-code-extensions` account
-* Bump the version in `package.json`, and commit the change to upstream
-
-The extension is published under `llvm-vs-code-extensions` account, which is
-currently maintained by clangd developers. If you want to make a new release,
-please contact cfe-dev@lists.llvm.org.
-
-### Steps
-
-```bash
-  $ cd /path/to/clang-tools-extra/clangd/clients/clangd-vscode/
-  # For the first time, you need to login in the account. vsce will ask you for
-    the Personal Access Token, and remember it for future commands.
-  $ vsce login llvm-vs-code-extensions
-  $ vsce publish
-```
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/package.json b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/package.json
deleted file mode 100644
index eeb5112..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/package.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
-    "name": "vscode-clangd",
-    "displayName": "vscode-clangd",
-    "description": "Clang Language Server",
-    "version": "0.0.6",
-    "publisher": "llvm-vs-code-extensions",
-    "homepage": "https://clang.llvm.org/extra/clangd.html",
-    "engines": {
-        "vscode": "^1.18.0"
-    },
-    "categories": [
-        "Programming Languages",
-        "Linters",
-        "Snippets"
-    ],
-    "keywords": [
-        "C",
-        "C++",
-        "LSP",
-        "Clangd",
-        "LLVM"
-    ],
-    "activationEvents": [
-        "onLanguage:cpp",
-        "onLanguage:c"
-    ],
-    "main": "./out/src/extension",
-    "scripts": {
-        "vscode:prepublish": "tsc -p ./",
-        "compile": "tsc -watch -p ./",
-        "postinstall": "node ./node_modules/vscode/bin/install",
-        "test": "node ./node_modules/vscode/bin/test"
-    },
-    "dependencies": {
-        "vscode-languageclient": "^4.0.0",
-        "vscode-languageserver": "^4.0.0"
-    },
-    "devDependencies": {
-        "typescript": "^2.0.3",
-        "vscode": "^1.1.0",
-        "mocha": "^2.3.3",
-        "@types/node": "^6.0.40",
-        "@types/mocha": "^2.2.32"
-    },
-    "repository": {
-        "type": "svn",
-        "url": "http://llvm.org/svn/llvm-project/clang-tools-extra/trunk/clangd/clients/clangd-vscode/"
-    },
-    "contributes": {
-        "configuration": {
-            "type": "object",
-            "title": "clangd configuration",
-            "properties": {
-                "clangd.path": {
-                    "type": "string",
-                    "default": "clangd",
-                    "description": "The path to clangd executable, e.g.: /usr/bin/clangd"
-                },
-                "clangd.arguments": {
-                    "type": "array",
-                    "default": [],
-                    "items": {
-                        "type": "string"
-                    },
-                    "description": "Arguments for clangd server"
-                },
-                "clangd.syncFileEvents": {
-                    "type": "boolean",
-                    "default": true,
-                    "description": "Whether or not to send file events to clangd (File created, changed or deleted). This can be disabled for performance consideration."
-                },
-                "clangd.trace": {
-                    "type": "string",
-                    "description": "Names a file that clangd should log a performance trace to, in chrome trace-viewer JSON format."
-                }
-            }
-        }
-    }
-}
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
deleted file mode 100644
index a126a19..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import * as vscode from 'vscode';
-import * as vscodelc from 'vscode-languageclient';
-import { realpathSync } from 'fs';
-
-/**
- * Method to get workspace configuration option
- * @param option name of the option (e.g. for clangd.path should be path)
- * @param defaultValue default value to return if option is not set
- */
-function getConfig<T>(option: string, defaultValue?: any): T {
-    const config = vscode.workspace.getConfiguration('clangd');
-    return config.get<T>(option, defaultValue);
-}
-
-/**
- *  this method is called when your extension is activate
- *  your extension is activated the very first time the command is executed
- */
-export function activate(context: vscode.ExtensionContext) {
-    const syncFileEvents = getConfig<boolean>('syncFileEvents', true);
-
-    const clangd: vscodelc.Executable = {
-        command: getConfig<string>('path'),
-        args: getConfig<string[]>('arguments')
-    };
-    const traceFile = getConfig<string>('trace');
-    if (!!traceFile) {
-        const trace = { CLANGD_TRACE: traceFile };
-        clangd.options = { env: { ...process.env, ...trace } };
-    }
-    const serverOptions: vscodelc.ServerOptions = clangd;
-
-    const filePattern: string = '**/*.{' +
-        ['cpp', 'c', 'cc', 'cxx', 'c++', 'm', 'mm', 'h', 'hh', 'hpp', 'hxx', 'inc'].join() + '}';
-    const clientOptions: vscodelc.LanguageClientOptions = {
-        // Register the server for C/C++ files
-        documentSelector: [{ scheme: 'file', pattern: filePattern }],
-        synchronize: !syncFileEvents ? undefined : {
-            fileEvents: vscode.workspace.createFileSystemWatcher(filePattern)
-        },
-        // Resolve symlinks for all files provided by clangd.
-        // This is a workaround for a bazel + clangd issue - bazel produces a symlink tree to build in,
-        // and when navigating to the included file, clangd passes its path inside the symlink tree
-        // rather than its filesystem path.
-        // FIXME: remove this once clangd knows enough about bazel to resolve the
-        // symlinks where needed (or if this causes problems for other workflows).
-        uriConverters: {
-            code2Protocol: (value: vscode.Uri) => value.toString(),
-            protocol2Code: (value: string) =>
-                vscode.Uri.file(realpathSync(vscode.Uri.parse(value).fsPath))
-        }
-    };
-
-    const clangdClient = new vscodelc.LanguageClient('Clang Language Server', serverOptions, clientOptions);
-    console.log('Clang Language Server is now active!');
-
-    const disposable = clangdClient.start();
-}
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/test/extension.test.ts b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/test/extension.test.ts
deleted file mode 100644
index de4e068..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/test/extension.test.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/** The module 'assert' provides assertion methods from node */
-import * as assert from 'assert';
-
-import * as vscode from 'vscode';
-import * as myExtension from '../src/extension';
-
-// TODO: add tests
-suite("Extension Tests", () => {
-
-    // Defines a Mocha unit test
-    test("Something 1", () => {
-        assert.equal(-1, [1, 2, 3].indexOf(5));
-        assert.equal(-1, [1, 2, 3].indexOf(0));
-    });
-});
\ No newline at end of file
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/test/index.ts b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/test/index.ts
deleted file mode 100644
index 50bae45..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/test/index.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
-//
-// This file is providing the test runner to use when running extension tests.
-// By default the test runner in use is Mocha based.
-//
-// You can provide your own test runner if you want to override it by exporting
-// a function run(testRoot: string, clb: (error:Error) => void) that the extension
-// host can call to run the tests. The test runner is expected to use console.log
-// to report the results back to the caller. When the tests are finished, return
-// a possible error to the callback or null if none.
-
-var testRunner = require('vscode/lib/testrunner');
-
-// You can directly control Mocha options by uncommenting the following lines
-// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
-testRunner.configure({
-    ui: 'tdd', 		// the TDD UI is being used in extension.test.ts (suite, test, etc.)
-    useColors: true // colored output from test results
-});
-
-module.exports = testRunner;
\ No newline at end of file
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/tsconfig.json b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/tsconfig.json
deleted file mode 100644
index 0b05f30..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/tsconfig.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-    "compilerOptions": {
-        "module": "commonjs",
-        "target": "es6",
-        "outDir": "out",
-        "lib": [
-            "es6",
-            "es2015.core",
-            "es2015.collection",
-            "es2015.generator",
-            "es2015.iterable",
-            "es2015.promise",
-            "es2015.symbol",
-            "es2016.array.include"
-        ],
-        "sourceMap": true,
-        "rootDir": ".",
-        "alwaysStrict": true,
-        "noEmitOnError": true,
-        "noFallthroughCasesInSwitch": true,
-        "noImplicitAny": true,
-        "noImplicitReturns": true,
-        "noImplicitThis": true
-    },
-    "exclude": [
-        "node_modules",
-        ".vscode-test"
-    ]
-}
\ No newline at end of file
diff --git a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/vsc-extension-quickstart.md b/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/vsc-extension-quickstart.md
deleted file mode 100644
index 24675ec..0000000
--- a/third_party/llvm-project/clang-tools-extra/clangd/clients/clangd-vscode/vsc-extension-quickstart.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Toy VS Code Extension for clangd
-
-## What's in the folder
-* This folder contains all of the files necessary for your extension
-* `package.json` - this is the manifest file in which you declare your extension and command.
-The sample plugin registers a command and defines its title and command name. With this information
-VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
-* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
-The file exports one function, `activate`, which is called the very first time your extension is
-activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
-We pass the function containing the implementation of the command as the second parameter to
-`registerCommand`.
-
-## Get up and running straight away
-* press `F5` to open a new window with your extension loaded
-* run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
-* set breakpoints in your code inside `src/extension.ts` to debug your extension
-* find output from your extension in the debug console
-
-## Make changes
-* you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`
-* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
-
-## Explore the API
-* you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
-
-## Run tests
-* open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
-* press `F5` to run the tests in a new window with your extension loaded
-* see the output of the test result in the debug console
-* make changes to `test/extension.test.ts` or create new test files inside the `test` folder
-    * by convention, the test runner will only consider files matching the name pattern `**.test.ts`
-    * you can create folders inside the `test` folder to structure your tests any way you want
\ No newline at end of file