Import Cobalt 23.master.0.309137
diff --git a/cobalt/BUILD.gn b/cobalt/BUILD.gn
index e9c9fcb..c268919 100644
--- a/cobalt/BUILD.gn
+++ b/cobalt/BUILD.gn
@@ -19,6 +19,7 @@
     "//cobalt/bindings/testing:bindings_test",
     "//cobalt/browser:browser_test",
     "//cobalt/cssom:cssom_test",
+    "//cobalt/demos/simple_sandbox",
     "//cobalt/dom:dom_test",
     "//cobalt/dom_parser:dom_parser_test",
     "//cobalt/encoding:text_encoding_test",
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/account/user_authorizer.h b/cobalt/account/user_authorizer.h
index 6bd8ad5..784984a 100644
--- a/cobalt/account/user_authorizer.h
+++ b/cobalt/account/user_authorizer.h
@@ -77,7 +77,8 @@
   // request. Calling other methods after |Shutdown| may have no effect.
   virtual void Shutdown() {}
 
-  // Instantiates an instance of the platform-specific implementation.
+  // Instantiates an instance of the platform-specific implementation. This may
+  // return nullptr if UserAuthorizer functions are not supported.
   static UserAuthorizer* Create();
 
  private:
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/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/browser/idl_files.gni b/cobalt/browser/idl_files.gni
index 258a1ce..db2d7c5 100644
--- a/cobalt/browser/idl_files.gni
+++ b/cobalt/browser/idl_files.gni
@@ -157,7 +157,7 @@
   "//cobalt/h5vcc/h5vcc.idl",
   "//cobalt/h5vcc/h5vcc_accessibility.idl",
   "//cobalt/h5vcc/h5vcc_account_info.idl",
-  "//cobalt/h5vcc/h5vcc_account_manager.idl",
+  "//cobalt/h5vcc/h5vcc_account_manager_internal.idl",
   "//cobalt/h5vcc/h5vcc_audio_config.idl",
   "//cobalt/h5vcc/h5vcc_audio_config_array.idl",
   "//cobalt/h5vcc/h5vcc_crash_log.idl",
diff --git a/cobalt/demos/simple_sandbox/BUILD.gn b/cobalt/demos/simple_sandbox/BUILD.gn
new file mode 100644
index 0000000..bd9cf74
--- /dev/null
+++ b/cobalt/demos/simple_sandbox/BUILD.gn
@@ -0,0 +1,23 @@
+# 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.
+
+# This is a sample minimal sandbox application.
+
+target(final_executable_type, "simple_sandbox") {
+  sources = [ "simple_sandbox.cc" ]
+
+  deps = [ "//cobalt/base" ]
+
+  deps += cobalt_platform_dependencies
+}
diff --git a/cobalt/demos/simple_sandbox/simple_sandbox.cc b/cobalt/demos/simple_sandbox/simple_sandbox.cc
new file mode 100644
index 0000000..5c2d32c
--- /dev/null
+++ b/cobalt/demos/simple_sandbox/simple_sandbox.cc
@@ -0,0 +1,80 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/bind.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "cobalt/base/wrap_main.h"
+
+namespace cobalt {
+namespace demos {
+
+class SimpleSandbox {
+ public:
+  static SimpleSandbox* GetInstance() {
+    return base::Singleton<SimpleSandbox>::get();
+  }
+
+  static void StartApplication(int argc, char** argv, const char* link,
+                               const base::Closure& quit_closure,
+                               SbTimeMonotonic timestamp);
+
+  static void StopApplication();
+
+  base::Thread* thread() { return &thread_; }
+
+ private:
+  base::Thread thread_{"SimpleSandboxThread"};
+  // friend struct base::DefaultSingletonTraits<SimpleSandbox>;
+};
+
+// static
+void SimpleSandbox::StartApplication(int argc, char** argv, const char* link,
+                                     const base::Closure& quit_closure,
+                                     SbTimeMonotonic timestamp) {
+  LOG(INFO) << "SimpleSandbox::StartApplication";
+
+  SimpleSandbox::GetInstance()->thread()->Start();
+
+  SimpleSandbox::GetInstance()
+      ->thread()
+      ->message_loop()
+      ->task_runner()
+      ->PostDelayedTask(FROM_HERE,
+                        base::BindOnce(
+                            [](base::MessageLoop* main_loop,
+                               const base::Closure& quit_closure) {
+                              LOG(INFO) << "Lambda Function in the thread.";
+
+                              // Stop after seconds.
+                              main_loop->task_runner()->PostDelayedTask(
+                                  FROM_HERE, quit_closure,
+                                  base::TimeDelta::FromSeconds(2));
+                            },
+                            base::MessageLoop::current(), quit_closure),
+                        base::TimeDelta(base::TimeDelta::FromSeconds(1)));
+}
+
+// static
+void SimpleSandbox::StopApplication() {
+  LOG(INFO) << "SimpleSandbox::StopApplication";
+}
+
+}  // namespace demos
+}  // namespace cobalt
+
+COBALT_WRAP_BASE_MAIN(cobalt::demos::SimpleSandbox::StartApplication,
+                      cobalt::demos::SimpleSandbox::StopApplication);
diff --git a/cobalt/dom/document.cc b/cobalt/dom/document.cc
index 4e665a5..41bfa41a 100644
--- a/cobalt/dom/document.cc
+++ b/cobalt/dom/document.cc
@@ -448,7 +448,7 @@
     return;
   }
   if (cookie_jar_) {
-    cookie_jar_->SetCookie(url_as_gurl(), cookie);
+    cookie_jar_->SetCookie(location()->url(), cookie);
   }
 }
 
@@ -467,7 +467,7 @@
   }
   if (cookie_jar_) {
     return net::CanonicalCookie::BuildCookieLine(
-        cookie_jar_->GetCookies(url_as_gurl()));
+        cookie_jar_->GetCookies(location()->url()));
   } else {
     DLOG(WARNING) << "Document has no cookie jar";
     return "";
@@ -485,7 +485,7 @@
     return;
   }
   if (cookie_jar_) {
-    cookie_jar_->SetCookie(url_as_gurl(), cookie);
+    cookie_jar_->SetCookie(location()->url(), cookie);
   }
 }
 
@@ -501,7 +501,7 @@
   }
   if (cookie_jar_) {
     return net::CanonicalCookie::BuildCookieLine(
-        cookie_jar_->GetCookies(url_as_gurl()));
+        cookie_jar_->GetCookies(location()->url()));
   } else {
     DLOG(WARNING) << "Document has no cookie jar";
     return "";
@@ -517,7 +517,8 @@
   // limited quirks mode, or no-quirks mode), and its type (XML document or HTML
   // document).
   //   https://www.w3.org/TR/dom/#concept-node-clone
-  return new Document(html_element_context_, Document::Options(url_as_gurl()));
+  return new Document(html_element_context_,
+                      Document::Options(location()->url()));
 }
 
 scoped_refptr<HTMLHtmlElement> Document::html() const {
diff --git a/cobalt/dom/document.h b/cobalt/dom/document.h
index 2fc0246..89c65c0 100644
--- a/cobalt/dom/document.h
+++ b/cobalt/dom/document.h
@@ -247,8 +247,6 @@
 
   FontCache* font_cache() const { return font_cache_.get(); }
 
-  const GURL& url_as_gurl() const { return location_->url(); }
-
   scoped_refptr<HTMLHtmlElement> html() const;
 
   // List of scripts that will execute in order as soon as possible.
diff --git a/cobalt/dom/document_test.cc b/cobalt/dom/document_test.cc
index 8909802..b693173 100644
--- a/cobalt/dom/document_test.cc
+++ b/cobalt/dom/document_test.cc
@@ -103,7 +103,7 @@
       new testing::FakeDocument(&html_element_context_, Document::Options(url));
   EXPECT_EQ(url.spec(), document->url());
   EXPECT_EQ(url.spec(), document->document_uri());
-  EXPECT_EQ(url, document->url_as_gurl());
+  EXPECT_EQ(url, document->location()->url());
 }
 
 TEST_F(DocumentTest, IsNotXMLDocument) {
diff --git a/cobalt/dom/dom_settings.cc b/cobalt/dom/dom_settings.cc
index d07474b..8390913 100644
--- a/cobalt/dom/dom_settings.cc
+++ b/cobalt/dom/dom_settings.cc
@@ -44,6 +44,11 @@
 
 DOMSettings::~DOMSettings() {}
 
+Window* DOMSettings::window() const {
+  DCHECK(context()->GetWindowOrWorkerGlobalScope()->IsWindow());
+  return context()->GetWindowOrWorkerGlobalScope()->AsWindow();
+}
+
 const GURL& DOMSettings::base_url() const {
   // From algorithm for to setup up a window environment settings object:
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-window-environment-settings-object
@@ -51,15 +56,10 @@
   //    algorithms are defined as follows:
   //    The API base URL
   //    Return the current base URL of window's associated Document.
-  return window()->document()->url_as_gurl();
+  return window()->document()->location()->url();
 }
 
-scoped_refptr<Window> DOMSettings::window() const {
-  DCHECK(context()->GetWindowOrWorkerGlobalScope()->IsWindow());
-  return context()->GetWindowOrWorkerGlobalScope()->AsWindow();
-}
-
-loader::Origin DOMSettings::document_origin() const {
+loader::Origin DOMSettings::GetOrigin() const {
   return window()->document()->location()->GetOriginAsObject();
 }
 
diff --git a/cobalt/dom/dom_settings.h b/cobalt/dom/dom_settings.h
index 33a5c0a..58e221f 100644
--- a/cobalt/dom/dom_settings.h
+++ b/cobalt/dom/dom_settings.h
@@ -69,7 +69,7 @@
     return microphone_options_;
   }
 
-  scoped_refptr<Window> window() const;
+  Window* window() const;
 
   MediaSourceRegistry* media_source_registry() const {
     return media_source_registry_;
@@ -91,13 +91,14 @@
     return mutation_observer_task_manager_;
   }
 
-  // Return's document's origin.
-  loader::Origin document_origin() const;
-
   // From: script::EnvironmentSettings
   //
   const GURL& base_url() const override;
 
+  // Return the origin of window's associated Document.
+  //   https://html.spec.whatwg.org/#set-up-a-window-environment-settings-object
+  loader::Origin GetOrigin() const override;
+
  private:
   const int max_dom_element_depth_;
   const speech::Microphone::Options microphone_options_;
diff --git a/cobalt/dom/html_anchor_element.cc b/cobalt/dom/html_anchor_element.cc
index c262595..0c15a83 100644
--- a/cobalt/dom/html_anchor_element.cc
+++ b/cobalt/dom/html_anchor_element.cc
@@ -54,7 +54,7 @@
   }
 
   // Resolve the URL given by the href attribute, relative to the element.
-  const GURL& base_url = document->url_as_gurl();
+  const GURL& base_url = document->location()->url();
   GURL absolute_url = base_url.Resolve(value);
 
   // If the previous step fails, then abort these steps.
diff --git a/cobalt/dom/html_element.cc b/cobalt/dom/html_element.cc
index aa38f18..970c56e 100644
--- a/cobalt/dom/html_element.cc
+++ b/cobalt/dom/html_element.cc
@@ -2025,7 +2025,7 @@
   // came from.
   cssom::GURLMap property_key_to_base_url_map;
   property_key_to_base_url_map[cssom::kBackgroundImageProperty] =
-      document->url_as_gurl();
+      document->location()->url();
 
   // Flags tracking which cached values must be invalidated.
   UpdateComputedStyleInvalidationFlags invalidation_flags;
diff --git a/cobalt/dom/html_image_element.cc b/cobalt/dom/html_image_element.cc
index 2cbc162..02300ed 100644
--- a/cobalt/dom/html_image_element.cc
+++ b/cobalt/dom/html_image_element.cc
@@ -119,7 +119,7 @@
   if (!src.empty()) {
     // 7.1. Resolve selected source, relative to the element. If that is not
     // successful, abort these steps.
-    const GURL& base_url = node_document()->url_as_gurl();
+    const GURL& base_url = node_document()->location()->url();
     const GURL selected_source = base_url.Resolve(src);
     if (!selected_source.is_valid()) {
       LOG(WARNING) << src << " cannot be resolved based on " << base_url << ".";
@@ -235,7 +235,7 @@
   // Resolve selected source, relative to the element.
   const auto src_attr = GetAttribute("src");
   const std::string src = src_attr.value_or("");
-  const GURL& base_url = node_document()->url_as_gurl();
+  const GURL& base_url = node_document()->location()->url();
   const GURL selected_source = base_url.Resolve(src);
 
   html_element_context()->performance()->CreatePerformanceResourceTiming(
diff --git a/cobalt/dom/html_link_element.cc b/cobalt/dom/html_link_element.cc
index b51d697..17a6de1 100644
--- a/cobalt/dom/html_link_element.cc
+++ b/cobalt/dom/html_link_element.cc
@@ -147,7 +147,7 @@
 
 void HTMLLinkElement::ResolveAndSetAbsoluteURL() {
   // Resolve the URL given by the href attribute, relative to the element.
-  const GURL& base_url = node_document()->url_as_gurl();
+  const GURL& base_url = node_document()->location()->url();
   absolute_url_ = base_url.Resolve(href());
 
   LOG_IF(WARNING, !absolute_url_.is_valid())
@@ -316,7 +316,7 @@
   // If not loading from network-fetched resources or fetched resource is same
   // origin as the document, set origin-clean flag to true.
   if (request_mode_ != loader::kNoCORSMode || !loader_ ||
-      document->url_as_gurl().SchemeIsFile() ||
+      document->location()->url().SchemeIsFile() ||
       (fetched_last_url_origin_ == document->location()->GetOriginAsObject())) {
     css_style_sheet->SetOriginClean(true);
   }
diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc
index 099559b..d496b01 100644
--- a/cobalt/dom/html_media_element.cc
+++ b/cobalt/dom/html_media_element.cc
@@ -795,7 +795,7 @@
     GURL media_url(src);
     if (media_url.is_empty()) {
       // Try to resolve it as a relative url.
-      media_url = node_document()->url_as_gurl().Resolve(src);
+      media_url = node_document()->location()->url().Resolve(src);
     }
     if (media_url.is_empty()) {
       MediaLoadingFailed(WebMediaPlayer::kNetworkStateFormatError,
@@ -1690,7 +1690,7 @@
   std::string src = this->src();
   GURL current_url = GURL(src);
   if (current_url.is_empty()) {
-    current_url = node_document()->url_as_gurl().Resolve(src);
+    current_url = node_document()->location()->url().Resolve(src);
   }
   if (!current_url.SchemeIs("http") &&
       OriginIsSafe(request_mode_, current_url,
diff --git a/cobalt/dom/html_script_element.cc b/cobalt/dom/html_script_element.cc
index a036d7c..e003c03 100644
--- a/cobalt/dom/html_script_element.cc
+++ b/cobalt/dom/html_script_element.cc
@@ -229,7 +229,7 @@
   //   3. Resolve src relative to the element.
   //   4. If the previous step failed, queue a task to fire a simple event named
   // error at the element, and abort these steps.
-  const GURL& base_url = document_->url_as_gurl();
+  const GURL& base_url = document_->location()->url();
   url_ = base_url.Resolve(src());
   if (!url_.is_valid()) {
     LOG(ERROR) << src() << " cannot be resolved based on " << base_url << ".";
diff --git a/cobalt/dom/lottie_player.cc b/cobalt/dom/lottie_player.cc
index 93baab8..4adb4a8 100644
--- a/cobalt/dom/lottie_player.cc
+++ b/cobalt/dom/lottie_player.cc
@@ -298,7 +298,7 @@
   const std::string src = GetAttribute("src").value_or("");
 
   if (!src.empty()) {
-    const GURL& base_url = node_document()->url_as_gurl();
+    const GURL& base_url = node_document()->location()->url();
     const GURL selected_source = base_url.Resolve(src);
     if (!selected_source.is_valid()) {
       LOG(WARNING) << src << " cannot be resolved based on " << base_url << ".";
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/storage.cc b/cobalt/dom/storage.cc
index 010890f..5706528 100644
--- a/cobalt/dom/storage.cc
+++ b/cobalt/dom/storage.cc
@@ -57,7 +57,7 @@
       key, old_value, new_value, window_->document()->url(), this));
 }
 
-GURL Storage::origin() const { return window_->document()->url_as_gurl(); }
+GURL Storage::origin() const { return window_->document()->location()->url(); }
 
 }  // namespace dom
 }  // namespace cobalt
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/dom/xml_document.h b/cobalt/dom/xml_document.h
index 422b6e4..8e3f684 100644
--- a/cobalt/dom/xml_document.h
+++ b/cobalt/dom/xml_document.h
@@ -29,7 +29,7 @@
   // Custom, not in any spec: Node.
   scoped_refptr<Node> Duplicate() const override {
     return new XMLDocument(html_element_context(),
-                           Document::Options(url_as_gurl()));
+                           Document::Options(location()->url()));
   }
 
   // Custom, not in any spec: Document.
diff --git a/cobalt/h5vcc/BUILD.gn b/cobalt/h5vcc/BUILD.gn
index fd7216f..188c6ed 100644
--- a/cobalt/h5vcc/BUILD.gn
+++ b/cobalt/h5vcc/BUILD.gn
@@ -96,8 +96,8 @@
 
   if (enable_account_manager) {
     sources += [
-      "h5vcc_account_manager.cc",
-      "h5vcc_account_manager.h",
+      "h5vcc_account_manager_internal.cc",
+      "h5vcc_account_manager_internal.h",
     ]
   }
 
diff --git a/cobalt/h5vcc/h5vcc.cc b/cobalt/h5vcc/h5vcc.cc
index 806f7a1..e88b901 100644
--- a/cobalt/h5vcc/h5vcc.cc
+++ b/cobalt/h5vcc/h5vcc.cc
@@ -14,6 +14,11 @@
 
 #include "cobalt/h5vcc/h5vcc.h"
 
+#if defined(COBALT_ENABLE_ACCOUNT_MANAGER)
+#include "cobalt/h5vcc/h5vcc_account_manager_internal.h"
+#include "cobalt/script/source_code.h"
+#endif
+
 #include "cobalt/persistent_storage/persistent_settings.h"
 #include "cobalt/sso/sso_interface.h"
 
@@ -45,6 +50,23 @@
 #else
   system_ = new H5vccSystem();
 #endif
+
+#if defined(COBALT_ENABLE_ACCOUNT_MANAGER)
+  // Bind "H5vccAccountManager" if it is supported. (This is not to be confused
+  // with settings.account_manager.)
+  if (H5vccAccountManagerInternal::IsSupported()) {
+    // Since we don't want to bind an instance of a wrappable, we cannot use
+    // Bind() nor BindTo(). Instead, just evaluate a script to alias the type.
+    scoped_refptr<script::SourceCode> source =
+        script::SourceCode::CreateSourceCode(
+            "H5vccAccountManager = H5vccAccountManagerInternal;"
+            "window.H5vccAccountManager = window.H5vccAccountManagerInternal;",
+            base::SourceLocation("h5vcc.cc", __LINE__, 1));
+    std::string result;
+    bool success = settings.global_environment->EvaluateScript(source, &result);
+    CHECK(success);
+  }
+#endif
 }
 
 void H5vcc::TraceMembers(script::Tracer* tracer) {
diff --git a/cobalt/h5vcc/h5vcc_account_manager.cc b/cobalt/h5vcc/h5vcc_account_manager_internal.cc
similarity index 77%
rename from cobalt/h5vcc/h5vcc_account_manager.cc
rename to cobalt/h5vcc/h5vcc_account_manager_internal.cc
index 8705df7..1c0a3c2 100644
--- a/cobalt/h5vcc/h5vcc_account_manager.cc
+++ b/cobalt/h5vcc/h5vcc_account_manager_internal.cc
@@ -14,7 +14,7 @@
 
 #include <memory>
 
-#include "cobalt/h5vcc/h5vcc_account_manager.h"
+#include "cobalt/h5vcc/h5vcc_account_manager_internal.h"
 
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
@@ -24,32 +24,42 @@
 namespace cobalt {
 namespace h5vcc {
 
-H5vccAccountManager::H5vccAccountManager()
+H5vccAccountManagerInternal::H5vccAccountManagerInternal()
     : user_authorizer_(account::UserAuthorizer::Create()),
       owning_message_loop_(base::MessageLoop::current()),
       thread_("AccountManager") {
   thread_.Start();
 }
 
-void H5vccAccountManager::GetAuthToken(
+// static
+bool H5vccAccountManagerInternal::IsSupported() {
+  auto account_manager = account::UserAuthorizer::Create();
+  if (account_manager) {
+    delete account_manager;
+    return true;
+  }
+  return false;
+}
+
+void H5vccAccountManagerInternal::GetAuthToken(
     const AccessTokenCallbackHolder& callback) {
   DLOG(INFO) << "Get authorization token.";
   PostOperation(kGetToken, callback);
 }
 
-void H5vccAccountManager::RequestPairing(
+void H5vccAccountManagerInternal::RequestPairing(
     const AccessTokenCallbackHolder& callback) {
   DLOG(INFO) << "Request application linking.";
   PostOperation(kPairing, callback);
 }
 
-void H5vccAccountManager::RequestUnpairing(
+void H5vccAccountManagerInternal::RequestUnpairing(
     const AccessTokenCallbackHolder& callback) {
   DLOG(INFO) << "Request application unlinking.";
   PostOperation(kUnpairing, callback);
 }
 
-void H5vccAccountManager::PostOperation(
+void H5vccAccountManagerInternal::PostOperation(
     OperationType operation_type, const AccessTokenCallbackHolder& callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   AccessTokenCallbackReference* token_callback =
@@ -57,31 +67,38 @@
   pending_callbacks_.push_back(
       std::unique_ptr<AccessTokenCallbackReference>(token_callback));
   thread_.message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&H5vccAccountManager::RequestOperationInternal,
-                            user_authorizer_.get(), operation_type,
-                            base::Bind(&H5vccAccountManager::PostResult,
-                                       owning_message_loop_,
-                                       base::AsWeakPtr(this), token_callback)));
+      FROM_HERE,
+      base::Bind(&H5vccAccountManagerInternal::RequestOperationInternal,
+                 user_authorizer_.get(), operation_type,
+                 base::Bind(&H5vccAccountManagerInternal::PostResult,
+                            owning_message_loop_, base::AsWeakPtr(this),
+                            token_callback)));
 }
 
-H5vccAccountManager::~H5vccAccountManager() {
+H5vccAccountManagerInternal::~H5vccAccountManagerInternal() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Give the UserAuthorizer a chance to abort any long running pending requests
   // before the message loop gets shut down.
-  user_authorizer_->Shutdown();
+  if (user_authorizer_) {
+    user_authorizer_->Shutdown();
+  }
 }
 
 // static
-void H5vccAccountManager::RequestOperationInternal(
+void H5vccAccountManagerInternal::RequestOperationInternal(
     account::UserAuthorizer* user_authorizer, OperationType operation,
     const base::Callback<void(const std::string&, uint64_t)>& post_result) {
+  bool enabled = user_authorizer != nullptr;
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(browser::switches::kDisableSignIn)) {
+    enabled = false;
+  }
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+  if (!enabled) {
     post_result.Run(std::string(), 0);
     return;
   }
-#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 
   SbUser current_user = SbUserGetCurrent();
   DCHECK(SbUserIsValid(current_user));
@@ -128,18 +145,18 @@
 }
 
 // static
-void H5vccAccountManager::PostResult(
+void H5vccAccountManagerInternal::PostResult(
     base::MessageLoop* message_loop,
-    base::WeakPtr<H5vccAccountManager> h5vcc_account_manager,
+    base::WeakPtr<H5vccAccountManagerInternal> h5vcc_account_manager,
     AccessTokenCallbackReference* token_callback, const std::string& token,
     uint64_t expiration_in_seconds) {
   message_loop->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&H5vccAccountManager::SendResult, h5vcc_account_manager,
-                 token_callback, token, expiration_in_seconds));
+      FROM_HERE, base::Bind(&H5vccAccountManagerInternal::SendResult,
+                            h5vcc_account_manager, token_callback, token,
+                            expiration_in_seconds));
 }
 
-void H5vccAccountManager::SendResult(
+void H5vccAccountManagerInternal::SendResult(
     AccessTokenCallbackReference* token_callback, const std::string& token,
     uint64_t expiration_in_seconds) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/cobalt/h5vcc/h5vcc_account_manager.h b/cobalt/h5vcc/h5vcc_account_manager_internal.h
similarity index 68%
rename from cobalt/h5vcc/h5vcc_account_manager.h
rename to cobalt/h5vcc/h5vcc_account_manager_internal.h
index 9057f3c..94585a0 100644
--- a/cobalt/h5vcc/h5vcc_account_manager.h
+++ b/cobalt/h5vcc/h5vcc_account_manager_internal.h
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_H5VCC_H5VCC_ACCOUNT_MANAGER_H_
-#define COBALT_H5VCC_H5VCC_ACCOUNT_MANAGER_H_
+#ifndef COBALT_H5VCC_H5VCC_ACCOUNT_MANAGER_INTERNAL_H_
+#define COBALT_H5VCC_H5VCC_ACCOUNT_MANAGER_INTERNAL_H_
 
 #include <memory>
 #include <queue>
 #include <string>
+#include <vector>
 
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
@@ -31,24 +32,32 @@
 namespace cobalt {
 namespace h5vcc {
 
-// Implementation of the H5vccAccountManager interface. Requests will be handled
-// one-at-time on another thread in FIFO order. When a request is complete, the
-// AccessTokenCallback will be fired on the thread that the H5vccAccountManager
-// was created on.
-class H5vccAccountManager : public script::Wrappable,
-                            public base::SupportsWeakPtr<H5vccAccountManager> {
+// Implementation of the H5vccAccountManagerInternal interface. Requests will
+// be handled one-at-time on another thread in FIFO order. When a request is
+// complete, the AccessTokenCallback will be fired on the thread that the
+// H5vccAccountManagerInternal was created on.
+class H5vccAccountManagerInternal
+    : public script::Wrappable,
+      public base::SupportsWeakPtr<H5vccAccountManagerInternal> {
  public:
   typedef script::CallbackFunction<bool(const std::string&, uint64_t)>
       AccessTokenCallback;
   typedef script::ScriptValue<AccessTokenCallback> AccessTokenCallbackHolder;
 
-  H5vccAccountManager();
-  // H5vccAccountManager interface.
+  // Some platforms may enable the account manager interface at compile-time
+  // but need a runtime check to determine if it should be presented to the
+  // web app. If so, H5vccAccountManagerInternal will be bound to
+  // "H5vccAccountManager".
+  static bool IsSupported();
+
+  H5vccAccountManagerInternal();
+
+  // H5vccAccountManagerInternal interface.
   void GetAuthToken(const AccessTokenCallbackHolder& callback);
   void RequestPairing(const AccessTokenCallbackHolder& callback);
   void RequestUnpairing(const AccessTokenCallbackHolder& callback);
 
-  DEFINE_WRAPPABLE_TYPE(H5vccAccountManager);
+  DEFINE_WRAPPABLE_TYPE(H5vccAccountManagerInternal);
 
  private:
   typedef script::ScriptValue<AccessTokenCallback>::Reference
@@ -59,24 +68,24 @@
     kGetToken,
   };
 
-  ~H5vccAccountManager();
+  ~H5vccAccountManagerInternal();
 
   // Posts an operation to the account manager thread.
   void PostOperation(OperationType operation_type,
                      const AccessTokenCallbackHolder& callback);
 
   // Processes an operation on the account manager thread. Static because
-  // H5vccAccountManager may have been destructed before this runs.
+  // H5vccAccountManagerInternal may have been destructed before this runs.
   static void RequestOperationInternal(
       account::UserAuthorizer* user_authorizer, OperationType operation,
       const base::Callback<void(const std::string&, uint64_t)>& post_result);
 
   // Posts the result of an operation from the account manager thread back to
-  // the owning thread. Static because H5vccAccountManager may have been
+  // the owning thread. Static because H5vccAccountManagerInternal may have been
   // destructed before this runs.
   static void PostResult(
       base::MessageLoop* message_loop,
-      base::WeakPtr<H5vccAccountManager> h5vcc_account_manager,
+      base::WeakPtr<H5vccAccountManagerInternal> h5vcc_account_manager,
       AccessTokenCallbackReference* token_callback, const std::string& token,
       uint64_t expiration_in_seconds);
 
@@ -93,9 +102,9 @@
   // Thread checker for the thread that creates this instance.
   THREAD_CHECKER(thread_checker_);
 
-  // The message loop that the H5vccAccountManager was created on. The public
-  // interface must be called from this message loop, and callbacks will be
-  // fired on this loop as well.
+  // The message loop that the H5vccAccountManagerInternal was created on. The
+  // public interface must be called from this message loop, and callbacks will
+  // be fired on this loop as well.
   base::MessageLoop* owning_message_loop_;
 
   // Each incoming request will have a corresponding task posted to this
@@ -105,11 +114,11 @@
   // message loop gets flushed.
   base::Thread thread_;
 
-  friend class scoped_refptr<H5vccAccountManager>;
-  DISALLOW_COPY_AND_ASSIGN(H5vccAccountManager);
+  friend class scoped_refptr<H5vccAccountManagerInternal>;
+  DISALLOW_COPY_AND_ASSIGN(H5vccAccountManagerInternal);
 };
 
 }  // namespace h5vcc
 }  // namespace cobalt
 
-#endif  // COBALT_H5VCC_H5VCC_ACCOUNT_MANAGER_H_
+#endif  // COBALT_H5VCC_H5VCC_ACCOUNT_MANAGER_INTERNAL_H_
diff --git a/cobalt/h5vcc/h5vcc_account_manager.idl b/cobalt/h5vcc/h5vcc_account_manager_internal.idl
similarity index 95%
rename from cobalt/h5vcc/h5vcc_account_manager.idl
rename to cobalt/h5vcc/h5vcc_account_manager_internal.idl
index a62f2d5..0c648a2 100644
--- a/cobalt/h5vcc/h5vcc_account_manager.idl
+++ b/cobalt/h5vcc/h5vcc_account_manager_internal.idl
@@ -15,7 +15,7 @@
   Conditional=COBALT_ENABLE_ACCOUNT_MANAGER,
   Constructor
 ]
-interface H5vccAccountManager {
+interface H5vccAccountManagerInternal {
   void getAuthToken(AccessTokenCallback callback);
   void requestPairing(AccessTokenCallback callback);
   void requestUnpairing(AccessTokenCallback callback);
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/BUILD.gn b/cobalt/script/BUILD.gn
index fb5b577..1582517 100644
--- a/cobalt/script/BUILD.gn
+++ b/cobalt/script/BUILD.gn
@@ -58,6 +58,7 @@
 
   deps = [
     "//cobalt/base",
+    "//cobalt/loader:origin",
     "//nb",
     "//starboard:starboard_headers_only",
     "//third_party/v8",
diff --git a/cobalt/script/environment_settings.h b/cobalt/script/environment_settings.h
index d1761da..1980d9b 100644
--- a/cobalt/script/environment_settings.h
+++ b/cobalt/script/environment_settings.h
@@ -20,6 +20,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "cobalt/base/debugger_hooks.h"
+#include "cobalt/loader/origin.h"
 #include "url/gurl.h"
 
 namespace cobalt {
@@ -47,7 +48,10 @@
   const GURL& creation_url() const { return creation_url_; }
 
   // https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-origin
-  const GURL GetOrigin() const { return creation_url().GetOrigin(); }
+  // TODO(b/244368134): Replace with url::Origin.
+  virtual loader::Origin GetOrigin() const {
+    return loader::Origin(base_url().GetOrigin());
+  }
 
   const base::DebuggerHooks& debugger_hooks() const { return debugger_hooks_; }
 
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 2b097ef..033152a 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;
 }
@@ -283,5 +286,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/web/message_port.cc b/cobalt/web/message_port.cc
index 519e682..e0eb126 100644
--- a/cobalt/web/message_port.cc
+++ b/cobalt/web/message_port.cc
@@ -66,7 +66,7 @@
 }
 
 void MessagePort::PostMessage(const script::ValueHandleHolder& message) {
-  PostMessageSerialized(std::move(SerializeScriptValue(message)));
+  PostMessageSerialized(std::move(script::SerializeScriptValue(message)));
 }
 
 void MessagePort::PostMessageSerialized(
diff --git a/cobalt/worker/service_worker_jobs.cc b/cobalt/worker/service_worker_jobs.cc
index 0c31df7..e30f364 100644
--- a/cobalt/worker/service_worker_jobs.cc
+++ b/cobalt/worker/service_worker_jobs.cc
@@ -841,7 +841,7 @@
   ResolveJobPromise(job, registration);
   // 8. Let settingsObjects be all environment settings objects whose origin is
   //    registration’s scope url's origin.
-  auto registration_origin = registration->scope_url().GetOrigin();
+  auto registration_origin = loader::Origin(registration->scope_url());
   // 9. For each settingsObject of settingsObjects...
   for (auto& context : web_context_registrations_) {
     if (context->environment_settings()->GetOrigin() == registration_origin) {
@@ -1469,7 +1469,7 @@
   DCHECK_NE(kServiceWorkerStateParsed, state);
   // 2. Set worker's state to state.
   worker->set_state(state);
-  auto worker_origin = worker->script_url().GetOrigin();
+  auto worker_origin = loader::Origin(worker->script_url());
   // 3. Let settingsObjects be all environment settings objects whose origin is
   //    worker's script url's origin.
   // 4. For each settingsObject of settingsObjects...
@@ -1615,7 +1615,7 @@
   // Algorithm for Unregister:
   //   https://w3c.github.io/ServiceWorker/#unregister-algorithm
   // 1. If the origin of job’s scope url is not job’s client's origin, then:
-  if (!url::Origin::Create(job->client->GetOrigin())
+  if (!url::Origin::Create(GURL(job->client->GetOrigin().SerializedOrigin()))
            .IsSameOriginWith(url::Origin::Create(job->scope_url))) {
     // 1.1. Invoke Reject Job Promise with job and "SecurityError" DOMException.
     RejectJobPromise(
diff --git a/cobalt/worker/service_worker_object.cc b/cobalt/worker/service_worker_object.cc
index 193638b..436f256 100644
--- a/cobalt/worker/service_worker_object.cc
+++ b/cobalt/worker/service_worker_object.cc
@@ -153,6 +153,9 @@
   //        Return serviceWorker’s script url.
   //      The origin
   //        Return its registering service worker client's origin.
+  WorkerSettings* worker_settings = new WorkerSettings();
+  worker_settings->set_origin(
+      loader::Origin(containing_service_worker_registration()->scope_url()));
   //      The policy container
   //        Return workerGlobalScope’s policy container.
   //      The time origin
@@ -162,7 +165,8 @@
   //      serviceWorker’s script url, top-level creation URL to null, top-level
   //      origin to an implementation-defined value, target browsing context to
   //      null, and active service worker to null.
-  web_context_->setup_environment_settings(new WorkerSettings());
+
+  web_context_->setup_environment_settings(worker_settings);
   web_context_->environment_settings()->set_creation_url(script_url_);
   scoped_refptr<ServiceWorkerGlobalScope> service_worker_global_scope =
       new ServiceWorkerGlobalScope(web_context_->environment_settings(), this);
diff --git a/cobalt/worker/worker.cc b/cobalt/worker/worker.cc
index b8e283b..a820c8f 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 {
@@ -85,8 +85,14 @@
   //    . For the global object, if is shared is true, create a new
   //      SharedWorkerGlobalScope object. Otherwise, create a new
   //      DedicatedWorkerGlobalScope object.
-  web_context_->setup_environment_settings(
-      new WorkerSettings(options_.outside_port));
+  WorkerSettings* worker_settings = new WorkerSettings(options_.outside_port);
+  // From algorithm to set up a worker environment settings object
+  // Let inherited origin be outside settings's origin.
+  // The origin return a unique opaque origin if worker global scope's url's
+  // scheme is "data", and inherited origin otherwise.
+  //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
+  worker_settings->set_origin(options_.outside_settings->GetOrigin());
+  web_context_->setup_environment_settings(worker_settings);
   // From algorithm for to setup up a worker environment settings object:
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
   // 5. Set settings object's creation URL to worker global scope's url.
@@ -253,10 +259,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/cobalt/worker/worker_settings.cc b/cobalt/worker/worker_settings.cc
index 8e53e10..df42733 100644
--- a/cobalt/worker/worker_settings.cc
+++ b/cobalt/worker/worker_settings.cc
@@ -43,6 +43,23 @@
   return context()->GetWindowOrWorkerGlobalScope()->AsWorker()->Url();
 }
 
+loader::Origin WorkerSettings::GetOrigin() const {
+  // From algorithm for to setup up a worker environment settings object:
+  //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
+  // 3. Let settings object be a new environment settings object whose
+  //    algorithms are defined as follows:
+  //    The origin
+  //    Return a unique opaque origin if worker global scope's url's scheme is
+  //    "data", and inherited origin otherwise.
+  DCHECK(context()->GetWindowOrWorkerGlobalScope()->IsWorker());
+  const GURL& url =
+      context()->GetWindowOrWorkerGlobalScope()->AsWorker()->Url();
+  // TODO(b/244368134): Replace with url::Origin::CreateUniqueOpaque().
+  // Note: This does not have to be specialized for service workers, since
+  // their URL can not be a data URL.
+  if (url.SchemeIs("data")) return loader::Origin();
+  return origin_;
+}
 
 }  // namespace worker
 }  // namespace cobalt
diff --git a/cobalt/worker/worker_settings.h b/cobalt/worker/worker_settings.h
index b2d7097..3550b19 100644
--- a/cobalt/worker/worker_settings.h
+++ b/cobalt/worker/worker_settings.h
@@ -37,9 +37,16 @@
   //
   const GURL& base_url() const override;
 
+  // Return the origin of window's associated Document.
+  //   https://html.spec.whatwg.org/#set-up-a-window-environment-settings-object
+  void set_origin(const loader::Origin& origin) { origin_ = origin; }
+  loader::Origin GetOrigin() const override;
+
  private:
   // Outer message port.
   web::MessagePort* message_port_ = nullptr;
+
+  loader::Origin origin_;
 };
 
 }  // namespace worker
diff --git a/cobalt/xhr/xml_http_request.cc b/cobalt/xhr/xml_http_request.cc
index 56de1ee..d943154 100644
--- a/cobalt/xhr/xml_http_request.cc
+++ b/cobalt/xhr/xml_http_request.cc
@@ -570,7 +570,7 @@
   if (upload_) {
     upload_listener_ = upload_->HasOneOrMoreAttributeEventListener();
   }
-  origin_ = loader::Origin(settings_->GetOrigin());
+  origin_ = settings_->GetOrigin();
   // Step 9
   sent_ = true;
   // Now that a send is happening, prevent this object
diff --git a/cobalt/xhr/xml_http_request.h b/cobalt/xhr/xml_http_request.h
index 046e90f..01979a6 100644
--- a/cobalt/xhr/xml_http_request.h
+++ b/cobalt/xhr/xml_http_request.h
@@ -26,6 +26,7 @@
 #include "cobalt/dom/document.h"
 #include "cobalt/loader/cors_preflight.h"
 #include "cobalt/loader/net_fetcher.h"
+#include "cobalt/loader/origin.h"
 #include "cobalt/script/array_buffer.h"
 #include "cobalt/script/array_buffer_view.h"
 #include "cobalt/script/environment_settings.h"
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/docker/linux/base/Dockerfile b/docker/linux/base/Dockerfile
index 50dd9e0..849e656 100644
--- a/docker/linux/base/Dockerfile
+++ b/docker/linux/base/Dockerfile
@@ -32,6 +32,4 @@
         curl xz-utils \
     && /opt/clean-after-apt.sh
 
-RUN python3 -m pip install --upgrade pip
-
 CMD ["/usr/bin/python","--version"]
diff --git a/docker/linux/linux-x64x11/Dockerfile b/docker/linux/linux-x64x11/Dockerfile
index f77957f..e28bacb 100644
--- a/docker/linux/linux-x64x11/Dockerfile
+++ b/docker/linux/linux-x64x11/Dockerfile
@@ -27,7 +27,8 @@
         libxi-dev \
     && /opt/clean-after-apt.sh
 
-RUN python3 -m pip install "selenium==3.141.0" "Brotli==1.0.9"
+COPY ./linux-x64x11/requirements.txt /opt/requirements.txt
+RUN python3 -m pip install --require-hashes --no-deps -r /opt/requirements.txt
 
 CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" sb_api_version=${SB_API_VERSION:-14}" && \
     ninja -v -j ${NINJA_PARALLEL} -C ${OUTDIR}/${PLATFORM}_${CONFIG} ${TARGET:-cobalt_install}
diff --git a/docker/linux/linux-x64x11/requirements.in b/docker/linux/linux-x64x11/requirements.in
new file mode 100644
index 0000000..5d8c046
--- /dev/null
+++ b/docker/linux/linux-x64x11/requirements.in
@@ -0,0 +1,2 @@
+selenium==3.141.0
+Brotli==1.0.9
diff --git a/docker/linux/linux-x64x11/requirements.txt b/docker/linux/linux-x64x11/requirements.txt
new file mode 100644
index 0000000..ef2d62e
--- /dev/null
+++ b/docker/linux/linux-x64x11/requirements.txt
@@ -0,0 +1,78 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+#    pip-compile --generate-hashes requirements.in
+#
+brotli==1.0.9 \
+    --hash=sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d \
+    --hash=sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8 \
+    --hash=sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b \
+    --hash=sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c \
+    --hash=sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c \
+    --hash=sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70 \
+    --hash=sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f \
+    --hash=sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181 \
+    --hash=sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130 \
+    --hash=sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19 \
+    --hash=sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa \
+    --hash=sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429 \
+    --hash=sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126 \
+    --hash=sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4 \
+    --hash=sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0 \
+    --hash=sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b \
+    --hash=sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6 \
+    --hash=sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438 \
+    --hash=sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f \
+    --hash=sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389 \
+    --hash=sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6 \
+    --hash=sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26 \
+    --hash=sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7 \
+    --hash=sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14 \
+    --hash=sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2 \
+    --hash=sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430 \
+    --hash=sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296 \
+    --hash=sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12 \
+    --hash=sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f \
+    --hash=sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d \
+    --hash=sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a \
+    --hash=sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452 \
+    --hash=sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c \
+    --hash=sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761 \
+    --hash=sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649 \
+    --hash=sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b \
+    --hash=sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea \
+    --hash=sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c \
+    --hash=sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a \
+    --hash=sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031 \
+    --hash=sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267 \
+    --hash=sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5 \
+    --hash=sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7 \
+    --hash=sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d \
+    --hash=sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c \
+    --hash=sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43 \
+    --hash=sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa \
+    --hash=sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17 \
+    --hash=sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb \
+    --hash=sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb \
+    --hash=sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b \
+    --hash=sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4 \
+    --hash=sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3 \
+    --hash=sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7 \
+    --hash=sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1 \
+    --hash=sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb \
+    --hash=sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91 \
+    --hash=sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b \
+    --hash=sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1 \
+    --hash=sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806 \
+    --hash=sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3 \
+    --hash=sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1
+    # via -r requirements.in
+selenium==3.141.0 \
+    --hash=sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c \
+    --hash=sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d
+    # via -r requirements.in
+urllib3==1.26.12 \
+    --hash=sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e \
+    --hash=sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997
+    # via selenium
diff --git a/docker/precommit_hooks/Dockerfile b/docker/precommit_hooks/Dockerfile
index 956c86c..f263390 100644
--- a/docker/precommit_hooks/Dockerfile
+++ b/docker/precommit_hooks/Dockerfile
@@ -24,7 +24,8 @@
     && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
     && rm -rf /var/lib/{apt,dpkg,cache,log}
 
-RUN pip3 install "pre-commit<3" "cpplint<2" "yapf<1" "pylint<3"
+COPY requirements.txt /opt/requirements.txt
+RUN pip3 install --require-hashes --no-deps -r /opt/requirements.txt
 
 # === Get GN via CIPD
 ARG GN_SHA256SUM="af7b2dcb3905bca56655e12131b365f1cba8e159db80d2022330c4f522fab2ef  /tmp/gn.zip"
diff --git a/docker/precommit_hooks/requirements.in b/docker/precommit_hooks/requirements.in
new file mode 100644
index 0000000..daa21ff
--- /dev/null
+++ b/docker/precommit_hooks/requirements.in
@@ -0,0 +1,4 @@
+pre-commit<3
+cpplint<2
+yapf<1
+pylint<3
diff --git a/docker/precommit_hooks/requirements.txt b/docker/precommit_hooks/requirements.txt
new file mode 100644
index 0000000..cc066e8
--- /dev/null
+++ b/docker/precommit_hooks/requirements.txt
@@ -0,0 +1,228 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+#    pip-compile --allow-unsafe --generate-hashes requirements.in
+#
+astroid==2.11.7 \
+    --hash=sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b \
+    --hash=sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946
+    # via pylint
+cfgv==3.3.1 \
+    --hash=sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426 \
+    --hash=sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736
+    # via pre-commit
+cpplint==1.6.1 \
+    --hash=sha256:00ddc86d6e4de2a9dcfa272402dcbe21593363a93b7c475bc391e335062f34b1 \
+    --hash=sha256:d430ce8f67afc1839340e60daa89e90de08b874bc27149833077bba726dfc13a
+    # via -r requirements.in
+dill==0.3.5.1 \
+    --hash=sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302 \
+    --hash=sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86
+    # via pylint
+distlib==0.3.5 \
+    --hash=sha256:a7f75737c70be3b25e2bee06288cec4e4c221de18455b2dd037fe2a795cab2fe \
+    --hash=sha256:b710088c59f06338ca514800ad795a132da19fda270e3ce4affc74abf955a26c
+    # via virtualenv
+filelock==3.8.0 \
+    --hash=sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc \
+    --hash=sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4
+    # via virtualenv
+identify==2.5.3 \
+    --hash=sha256:25851c8c1370effb22aaa3c987b30449e9ff0cece408f810ae6ce408fdd20893 \
+    --hash=sha256:887e7b91a1be152b0d46bbf072130235a8117392b9f1828446079a816a05ef44
+    # via pre-commit
+isort==5.10.1 \
+    --hash=sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7 \
+    --hash=sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951
+    # via pylint
+lazy-object-proxy==1.7.1 \
+    --hash=sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7 \
+    --hash=sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a \
+    --hash=sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c \
+    --hash=sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc \
+    --hash=sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f \
+    --hash=sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09 \
+    --hash=sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442 \
+    --hash=sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e \
+    --hash=sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029 \
+    --hash=sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61 \
+    --hash=sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb \
+    --hash=sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0 \
+    --hash=sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35 \
+    --hash=sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42 \
+    --hash=sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1 \
+    --hash=sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad \
+    --hash=sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443 \
+    --hash=sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd \
+    --hash=sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9 \
+    --hash=sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148 \
+    --hash=sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38 \
+    --hash=sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55 \
+    --hash=sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36 \
+    --hash=sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a \
+    --hash=sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b \
+    --hash=sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44 \
+    --hash=sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6 \
+    --hash=sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69 \
+    --hash=sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4 \
+    --hash=sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84 \
+    --hash=sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de \
+    --hash=sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28 \
+    --hash=sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c \
+    --hash=sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1 \
+    --hash=sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8 \
+    --hash=sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b \
+    --hash=sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb
+    # via astroid
+mccabe==0.7.0 \
+    --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \
+    --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e
+    # via pylint
+nodeenv==1.7.0 \
+    --hash=sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e \
+    --hash=sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b
+    # via pre-commit
+platformdirs==2.5.2 \
+    --hash=sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788 \
+    --hash=sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19
+    # via
+    #   pylint
+    #   virtualenv
+pre-commit==2.20.0 \
+    --hash=sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7 \
+    --hash=sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959
+    # via -r requirements.in
+pylint==2.14.5 \
+    --hash=sha256:487ce2192eee48211269a0e976421f334cf94de1806ca9d0a99449adcdf0285e \
+    --hash=sha256:fabe30000de7d07636d2e82c9a518ad5ad7908590fe135ace169b44839c15f90
+    # via -r requirements.in
+pyyaml==6.0 \
+    --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
+    --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
+    --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
+    --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
+    --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
+    --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
+    --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
+    --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
+    --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
+    --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
+    --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
+    --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
+    --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
+    --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
+    --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
+    --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
+    --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
+    --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
+    --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
+    --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
+    --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
+    --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
+    --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
+    --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
+    --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
+    --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
+    --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
+    --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
+    --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
+    --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
+    --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
+    --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
+    --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
+    # via pre-commit
+toml==0.10.2 \
+    --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
+    --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
+    # via pre-commit
+tomli==2.0.1 \
+    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
+    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
+    # via pylint
+tomlkit==0.11.4 \
+    --hash=sha256:25d4e2e446c453be6360c67ddfb88838cfc42026322770ba13d1fbd403a93a5c \
+    --hash=sha256:3235a9010fae54323e727c3ac06fb720752fe6635b3426e379daec60fbd44a83
+    # via pylint
+virtualenv==20.16.3 \
+    --hash=sha256:4193b7bc8a6cd23e4eb251ac64f29b4398ab2c233531e66e40b19a6b7b0d30c1 \
+    --hash=sha256:d86ea0bb50e06252d79e6c241507cb904fcd66090c3271381372d6221a3970f9
+    # via pre-commit
+wrapt==1.14.1 \
+    --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \
+    --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \
+    --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \
+    --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \
+    --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \
+    --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \
+    --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \
+    --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \
+    --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \
+    --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \
+    --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \
+    --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \
+    --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \
+    --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \
+    --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \
+    --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \
+    --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \
+    --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \
+    --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \
+    --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \
+    --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \
+    --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \
+    --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \
+    --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \
+    --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \
+    --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \
+    --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \
+    --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \
+    --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \
+    --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \
+    --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \
+    --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \
+    --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \
+    --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \
+    --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \
+    --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \
+    --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \
+    --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \
+    --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \
+    --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \
+    --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \
+    --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \
+    --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \
+    --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \
+    --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \
+    --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \
+    --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \
+    --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \
+    --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \
+    --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \
+    --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \
+    --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \
+    --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \
+    --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \
+    --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \
+    --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \
+    --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \
+    --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \
+    --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \
+    --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \
+    --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \
+    --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \
+    --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \
+    --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af
+    # via astroid
+yapf==0.32.0 \
+    --hash=sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32 \
+    --hash=sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b
+    # via -r requirements.in
+
+# The following packages are considered to be unsafe in a requirements file:
+setuptools==65.3.0 \
+    --hash=sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82 \
+    --hash=sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57
+    # via
+    #   astroid
+    #   nodeenv
diff --git a/docker/pytest/Dockerfile b/docker/pytest/Dockerfile
index 962201e..d78f0c1 100644
--- a/docker/pytest/Dockerfile
+++ b/docker/pytest/Dockerfile
@@ -15,7 +15,7 @@
 FROM cobalt-base
 
 COPY requirements.txt /opt/requirements.txt
-RUN pip3 install -r /opt/requirements.txt
+RUN pip3 install --require-hashes --no-deps -r /opt/requirements.txt
 
 WORKDIR /code
 
diff --git a/docker/pytest/requirements.txt b/docker/pytest/requirements.txt
index a93c321..bdb88a3 100644
--- a/docker/pytest/requirements.txt
+++ b/docker/pytest/requirements.txt
@@ -1,40 +1,130 @@
 #
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.10
 # To update, run:
 #
-#    pip-compile --output-file=docker/pytest/requirements.txt docker/pytest/requirements.in
+#    pip-compile --generate-hashes requirements.in
 #
-attrs==21.4.0
+attrs==21.4.0 \
+    --hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4 \
+    --hash=sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd
     # via
     #   jsonschema
     #   pytest
-certifi==2021.10.8
+certifi==2021.10.8 \
+    --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \
+    --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569
     # via requests
-chardet==4.0.0
+chardet==4.0.0 \
+    --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
+    --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
     # via requests
-coverage==6.3.2
-    # via -r docker/pytest/requirements.in
-idna==2.10
+coverage==6.3.2 \
+    --hash=sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9 \
+    --hash=sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d \
+    --hash=sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf \
+    --hash=sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7 \
+    --hash=sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6 \
+    --hash=sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4 \
+    --hash=sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059 \
+    --hash=sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39 \
+    --hash=sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536 \
+    --hash=sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac \
+    --hash=sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c \
+    --hash=sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903 \
+    --hash=sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d \
+    --hash=sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05 \
+    --hash=sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684 \
+    --hash=sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1 \
+    --hash=sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f \
+    --hash=sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7 \
+    --hash=sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca \
+    --hash=sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad \
+    --hash=sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca \
+    --hash=sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d \
+    --hash=sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92 \
+    --hash=sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4 \
+    --hash=sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf \
+    --hash=sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6 \
+    --hash=sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1 \
+    --hash=sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4 \
+    --hash=sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359 \
+    --hash=sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3 \
+    --hash=sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620 \
+    --hash=sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512 \
+    --hash=sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69 \
+    --hash=sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2 \
+    --hash=sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518 \
+    --hash=sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0 \
+    --hash=sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa \
+    --hash=sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4 \
+    --hash=sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e \
+    --hash=sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1 \
+    --hash=sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2
+    # via -r requirements.in
+idna==2.10 \
+    --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
+    --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
     # via requests
-iniconfig==1.1.1
+iniconfig==1.1.1 \
+    --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \
+    --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32
     # via pytest
-jsonschema==4.4.0
-    # via -r docker/pytest/requirements.in
-packaging==21.3
+jsonschema==4.4.0 \
+    --hash=sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83 \
+    --hash=sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823
+    # via -r requirements.in
+packaging==21.3 \
+    --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
+    --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522
     # via pytest
-pluggy==1.0.0
+pluggy==1.0.0 \
+    --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \
+    --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3
     # via pytest
-py==1.11.0
+py==1.11.0 \
+    --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \
+    --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378
     # via pytest
-pyparsing==3.0.7
+pyparsing==3.0.7 \
+    --hash=sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea \
+    --hash=sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484
     # via packaging
-pyrsistent==0.18.1
+pyrsistent==0.18.1 \
+    --hash=sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c \
+    --hash=sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc \
+    --hash=sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e \
+    --hash=sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26 \
+    --hash=sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec \
+    --hash=sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286 \
+    --hash=sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045 \
+    --hash=sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec \
+    --hash=sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8 \
+    --hash=sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c \
+    --hash=sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca \
+    --hash=sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22 \
+    --hash=sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a \
+    --hash=sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96 \
+    --hash=sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc \
+    --hash=sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1 \
+    --hash=sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07 \
+    --hash=sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6 \
+    --hash=sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b \
+    --hash=sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5 \
+    --hash=sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6
     # via jsonschema
-pytest==7.1.1
-    # via -r docker/pytest/requirements.in
-requests==2.25.1
-    # via -r docker/pytest/requirements.in
-tomli==2.0.1
+pytest==7.1.1 \
+    --hash=sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63 \
+    --hash=sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea
+    # via -r requirements.in
+requests==2.25.1 \
+    --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
+    --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
+    # via -r requirements.in
+tomli==2.0.1 \
+    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
+    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
     # via pytest
-urllib3==1.26.9
+urllib3==1.26.9 \
+    --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \
+    --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e
     # via requests
diff --git a/docker/windows/base/build/Dockerfile b/docker/windows/base/build/Dockerfile
index ec4ebaa..19aa9b6 100644
--- a/docker/windows/base/build/Dockerfile
+++ b/docker/windows/base/build/Dockerfile
@@ -45,8 +45,9 @@
     Copy-Item C:\Python3\python.exe C:\Python3\python3.exe
 
 # Install python3 packages via PIP and set various configurations.
+COPY ./requirements.txt /requirements.txt
 RUN mkdir C:\pip-cache;`
-    python3 -m pip install six --cache-dir C:\pip-cache;`
+    python3 -m pip install --require-hashes --no-deps -r /requirements.txt --cache-dir C:\pip-cache;`
     C:\fast-win-rmdir.cmd C:\pip-cache;`
     # Configure git global settings.
     git config --global core.autocrlf false;`
diff --git a/docker/windows/base/build/requirements.in b/docker/windows/base/build/requirements.in
new file mode 100644
index 0000000..ffe2fce
--- /dev/null
+++ b/docker/windows/base/build/requirements.in
@@ -0,0 +1 @@
+six
diff --git a/docker/windows/base/build/requirements.txt b/docker/windows/base/build/requirements.txt
new file mode 100644
index 0000000..1fc47bb
--- /dev/null
+++ b/docker/windows/base/build/requirements.txt
@@ -0,0 +1,10 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+#    pip-compile --generate-hashes requirements.in
+#
+six==1.16.0 \
+    --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
+    --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+    # via -r requirements.in
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/apk/apk_sources.gni b/starboard/android/apk/apk_sources.gni
index e0378ea..07912f6 100644
--- a/starboard/android/apk/apk_sources.gni
+++ b/starboard/android/apk/apk_sources.gni
@@ -21,7 +21,6 @@
   "//starboard/android/apk/app/src/main/java/dev/cobalt/account/AccessToken.java",
   "//starboard/android/apk/app/src/main/java/dev/cobalt/account/NoopUserAuthorizer.java",
   "//starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizer.java",
-  "//starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java",
   "//starboard/android/apk/app/src/main/java/dev/cobalt/coat/AudioPermissionRequester.java",
   "//starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltA11yHelper.java",
   "//starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java",
diff --git a/starboard/android/apk/app/src/app/java/dev/cobalt/app/MainActivity.java b/starboard/android/apk/app/src/app/java/dev/cobalt/app/MainActivity.java
index dc26a8a..ee4cdee 100644
--- a/starboard/android/apk/app/src/app/java/dev/cobalt/app/MainActivity.java
+++ b/starboard/android/apk/app/src/app/java/dev/cobalt/app/MainActivity.java
@@ -16,7 +16,7 @@
 
 import android.app.Activity;
 import android.app.Service;
-import dev.cobalt.account.UserAuthorizerImpl;
+import dev.cobalt.account.NoopUserAuthorizer;
 import dev.cobalt.coat.CobaltActivity;
 import dev.cobalt.coat.CobaltService;
 import dev.cobalt.coat.StarboardBridge;
@@ -42,8 +42,7 @@
             getStarboardBridge().requestStop(0);
           }
         };
-    UserAuthorizerImpl userAuthorizer =
-        new UserAuthorizerImpl(getApplicationContext(), activityHolder, stopRequester);
+    NoopUserAuthorizer userAuthorizer = new NoopUserAuthorizer();
     StarboardBridge bridge =
         new StarboardBridge(
             getApplicationContext(),
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/account/NoopUserAuthorizer.java b/starboard/android/apk/app/src/main/java/dev/cobalt/account/NoopUserAuthorizer.java
index e995dd2..cf5b7f9 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/account/NoopUserAuthorizer.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/account/NoopUserAuthorizer.java
@@ -18,6 +18,7 @@
 import dev.cobalt.util.UsedByNative;
 
 /** UserAuthorizer implementation that doesn't try to sign in. */
+@UsedByNative
 public class NoopUserAuthorizer implements UserAuthorizer {
 
   @Override
@@ -55,5 +56,4 @@
   @Override
   public void onRequestPermissionsResult(
       int requestCode, String[] permissions, int[] grantResults) {}
-
 }
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java b/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java
deleted file mode 100644
index 1d96237..0000000
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/account/UserAuthorizerImpl.java
+++ /dev/null
@@ -1,537 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package dev.cobalt.account;
-
-import static android.Manifest.permission.GET_ACCOUNTS;
-import static dev.cobalt.util.Log.TAG;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.OnAccountsUpdateListener;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.widget.Toast;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-import com.google.android.gms.auth.GoogleAuthException;
-import com.google.android.gms.auth.GoogleAuthUtil;
-import com.google.android.gms.auth.UserRecoverableAuthException;
-import com.google.android.gms.common.AccountPicker;
-import com.google.android.gms.common.AccountPicker.AccountChooserOptions;
-import dev.cobalt.coat.R;
-import dev.cobalt.util.Holder;
-import dev.cobalt.util.Log;
-import dev.cobalt.util.UsedByNative;
-import java.io.IOException;
-import java.util.Arrays;
-
-/**
- * Java side implementation for starboard::android::shared::cobalt::AndroidUserAuthorizer.
- *
- * This implements the following business logic:
- * First run...
- * - if there are no accounts, just be be signed-out
- * - if there is one account, sign-in without any UI
- * - if there are more than one accounts, prompt to choose account
- * Subsequent runs...
- * - sign-in to the same account last used to sign-in
- * - if previously signed-out stay signed-out
- * When user clicks 'sign-in' in the UI...
- * - if there are no accounts, allow user to add an account
- * - if there is one account, sign-in without any UI
- * - if there are more than one accounts, prompt to choose account
- * If the last signed-in account is deleted...
- * - kill the app if stopped in the background to prompt next time it starts
- * - at the next app start, show a toast that the account isn't available
- * - if there are no accounts left, just be be signed-out
- * - if there are one or more accounts left, prompt to choose account
- */
-public class UserAuthorizerImpl implements OnAccountsUpdateListener, UserAuthorizer {
-
-  /** Pseudo account indicating the user chose to be signed-out. */
-  public static final Account SIGNED_OUT_ACCOUNT = new Account("-", "-");
-
-  /** Pseudo account indicating a saved account no longer exists. */
-  private static final Account MISSING_ACCOUNT = new Account("!", "!");
-
-  /** Foreshortened expiry of Google OAuth token, which typically lasts 1 hour. */
-  private static final long DEFAULT_EXPIRY_SECONDS = 5 * 60;
-
-  private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
-  private static final String[] OAUTH_SCOPES = {
-      "https://www.googleapis.com/auth/youtube"
-  };
-
-  private static final String SHARED_PREFS_NAME = "user_auth";
-  private static final String ACCOUNT_NAME_PREF_KEY = "signed_in_account";
-
-  /** The thread on which the current request is running, or null if none. */
-  private volatile Thread requestThread;
-
-  private final Context appContext;
-  private final Holder<Activity> activityHolder;
-  private final Runnable stopRequester;
-  private final Handler mainHandler;
-
-  private Account currentAccount = null;
-  private AccessToken currentToken = null;
-
-  // Result from the account picker UI lands here.
-  private String chosenAccountName;
-
-  private volatile boolean waitingForPermission;
-  private volatile boolean permissionGranted;
-
-  public UserAuthorizerImpl(
-      Context appContext, Holder<Activity> activityHolder, Runnable stopRequester) {
-    this.appContext = appContext;
-    this.activityHolder = activityHolder;
-    this.stopRequester = stopRequester;
-    this.mainHandler = new Handler(Looper.getMainLooper());
-    addOnAccountsUpdatedListener(this);
-  }
-
-  @Override
-  public void shutdown() {
-    removeOnAccountsUpdatedListener(this);
-  }
-
-  @Override
-  @SuppressWarnings("unused")
-  @UsedByNative
-  public void interrupt() {
-    Thread t = requestThread;
-    if (t != null) {
-      t.interrupt();
-    }
-  }
-
-  @Override
-  @SuppressWarnings("unused")
-  @UsedByNative
-  public AccessToken authorizeUser() {
-    ensureBackgroundThread();
-    requestThread = Thread.currentThread();
-    // Let the user choose an account, or add one if there are none to choose.
-    // However, if there's only one account just choose it without any prompt.
-    currentAccount = autoSelectOrAddAccount();
-    writeAccountPref(currentAccount);
-    AccessToken accessToken = refreshCurrentToken();
-    requestThread = null;
-    return accessToken;
-  }
-
-  @Override
-  @SuppressWarnings("unused")
-  @UsedByNative
-  public boolean deauthorizeUser() {
-    ensureBackgroundThread();
-    requestThread = Thread.currentThread();
-    currentAccount = SIGNED_OUT_ACCOUNT;
-    writeAccountPref(currentAccount);
-    clearCurrentToken();
-    requestThread = null;
-    return true;
-  }
-
-  @Override
-  @SuppressWarnings("unused")
-  @UsedByNative
-  public AccessToken refreshAuthorization() {
-    ensureBackgroundThread();
-    requestThread = Thread.currentThread();
-
-    // If we haven't yet determined which account to use, check preferences for a saved account.
-    if (currentAccount == null) {
-      Account savedAccount = readAccountPref();
-      if (savedAccount == null) {
-        // No saved account, so this is the first ever run of the app.
-        currentAccount = autoSelectAccount();
-      } else if (savedAccount.equals(MISSING_ACCOUNT)) {
-        // The saved account got deleted.
-        currentAccount = forceSelectAccount();
-      } else {
-        // Use the saved account.
-        currentAccount = savedAccount;
-      }
-      writeAccountPref(currentAccount);
-    }
-
-    AccessToken accessToken = refreshCurrentToken();
-    requestThread = null;
-    return accessToken;
-  }
-
-  private static void ensureBackgroundThread() {
-    if (Looper.myLooper() == Looper.getMainLooper()) {
-      throw new UnsupportedOperationException("UserAuthorizer can't be called on main thread");
-    }
-  }
-
-  private void showToast(int resId, Object... formatArgs) {
-    final String msg = appContext.getResources().getString(resId, formatArgs);
-    mainHandler.post(new Runnable() {
-      @Override
-      public void run() {
-        Toast.makeText(appContext, msg, Toast.LENGTH_LONG).show();
-      }
-    });
-  }
-
-  private Account readAccountPref() {
-    String savedAccountName = loadSignedInAccountName();
-    if (TextUtils.isEmpty(savedAccountName)) {
-      return null;
-    } else if (savedAccountName.equals(SIGNED_OUT_ACCOUNT.name)) {
-      // Don't request permissions or look for a device account if we were signed-out.
-      return SIGNED_OUT_ACCOUNT;
-    } else if (!checkPermission()) {
-      // We won't be able to get the account without permission, so warn the user and be signed-out.
-      showToast(R.string.starboard_missing_account, savedAccountName);
-      return SIGNED_OUT_ACCOUNT;
-    } else {
-      // Find the saved account name among all accounts on the device.
-      for (Account account : getAccounts()) {
-        if (account.name.equals(savedAccountName)) {
-          return account;
-        }
-      }
-      showToast(R.string.starboard_missing_account, savedAccountName);
-      return MISSING_ACCOUNT;
-    }
-  }
-
-  private void writeAccountPref(Account account) {
-    if (account == null) {
-      return;
-    }
-    // Always write the account name, even if it's the signed-out pseudo account.
-    saveSignedInAccountName(account.name);
-  }
-
-  private void clearCurrentToken() {
-    if (currentToken != null) {
-      clearToken(currentToken.getTokenValue());
-      currentToken = null;
-    }
-  }
-
-  private AccessToken refreshCurrentToken() {
-    clearCurrentToken();
-    if (currentAccount == null || SIGNED_OUT_ACCOUNT.equals(currentAccount)) {
-      return null;
-    }
-    String tokenValue = getToken(currentAccount);
-    if (tokenValue == null) {
-      showToast(R.string.starboard_account_auth_error);
-      tokenValue = "";
-    }
-    // TODO: Get the token details and use the actual expiry.
-    long expiry = System.currentTimeMillis() / 1000 + DEFAULT_EXPIRY_SECONDS;
-    currentToken = new AccessToken(tokenValue, expiry);
-    return currentToken;
-  }
-
-  /**
-   * Prompts the user to select an account, or to add an account if there are none. The prompt is
-   * skipped if there is exactly one account to choose from.
-   */
-  private Account autoSelectOrAddAccount() {
-    if (!checkPermission()) {
-      return SIGNED_OUT_ACCOUNT;
-    }
-    Account[] accounts = getAccounts();
-    if (accounts.length == 1) {
-      return accounts[0];
-    }
-    return selectOrAddAccount();
-  }
-
-  /**
-   * Prompts the user to select an account. The prompt is skipped if there are zero or one accounts
-   * to choose from.
-   */
-  private Account autoSelectAccount() {
-    if (!checkPermission()) {
-      return SIGNED_OUT_ACCOUNT;
-    }
-    Account[] accounts = getAccounts();
-    if (accounts.length == 0) {
-      return SIGNED_OUT_ACCOUNT;
-    } else if (accounts.length == 1) {
-      return accounts[0];
-    }
-    return selectOrAddAccount();
-  }
-
-  /**
-   * Prompts the user to select an account, even if there's only one to choose from. The prompt is
-   * skipped if there are zero accounts to choose from.
-   */
-  private Account forceSelectAccount() {
-    // We don't check permissions before calling selectOrAddAccount() because if the account is
-    // missing, readAccountPref() must have just checked, and we don't want to show permission
-    // prompt or rationale to the user twice.
-    Account[] accounts = getAccounts();
-    if (accounts.length == 0) {
-      return SIGNED_OUT_ACCOUNT;
-    }
-    return selectOrAddAccount();
-  }
-
-  /**
-   * Prompts the user to select an account, even if there's only one to choose from. If there are
-   * zero accounts to choose from, the user is prompted to add one.
-   *
-   * The caller should ensure permissions are granted before calling this method to avoid showing
-   * a picker with accounts that we can't access.
-   */
-  private Account selectOrAddAccount() {
-    String accountName = showAccountPicker();
-    // If user cancelled the picker stay signed-out.
-    if (TextUtils.isEmpty(accountName)) {
-      return SIGNED_OUT_ACCOUNT;
-    }
-    // Get the accounts after the picker in case one was added in the account picker.
-    for (Account account : getAccounts()) {
-      if (account.name.equals(accountName)) {
-        return account;
-      }
-    }
-    // This shouldn't happen, but if it does let the user know we're still signed-out.
-    Log.e(TAG, "Selected account is missing");
-    showToast(R.string.starboard_missing_account, accountName);
-    return SIGNED_OUT_ACCOUNT;
-  }
-
-  private synchronized String showAccountPicker() {
-    Activity activity = activityHolder.get();
-    Intent chooseAccountIntent = newChooseAccountIntent(currentAccount);
-    if (activity == null || chooseAccountIntent == null) {
-      return "";
-    }
-    chosenAccountName = null;
-    activity.startActivityForResult(chooseAccountIntent, R.id.rc_choose_account);
-
-    // Block until the account picker activity returns its result.
-    while (chosenAccountName == null) {
-      try {
-        wait();
-      } catch (InterruptedException e) {
-        Log.e(TAG, "Account picker interrupted");
-        // Return empty string, as if the picker was cancelled.
-        return "";
-      }
-    }
-    return chosenAccountName;
-  }
-
-  @Override
-  public void onActivityResult(int requestCode, int resultCode, Intent data) {
-    if (requestCode == R.id.rc_choose_account) {
-      String accountName = null;
-      if (resultCode == Activity.RESULT_OK) {
-        accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
-      } else if (resultCode != Activity.RESULT_CANCELED) {
-        Log.e(TAG, "Account picker error " + resultCode);
-        showToast(R.string.starboard_account_picker_error);
-      }
-
-      // Notify showAccountPicker() which account was chosen.
-      synchronized (this) {
-        // Return empty string if the picker is cancelled or there's an unexpected result.
-        chosenAccountName = (accountName == null) ? "" : accountName;
-        notifyAll();
-      }
-    }
-  }
-
-  @Override
-  public void onAccountsUpdated(Account[] unused) {
-    if (currentAccount == null || SIGNED_OUT_ACCOUNT.equals(currentAccount)) {
-      // We're not signed-in; the update doesn't affect us.
-      return;
-    }
-    // Call getAccounts() since the param may not match the accounts we can access.
-    for (Account account : getAccounts()) {
-      if (account.name.equals(currentAccount.name)) {
-        // The current account is still there; the update doesn't affect us.
-        return;
-      }
-    }
-    // The current account is gone; leave the app so we prompt for sign-in next time.
-    // This should only happen while stopped in the background since we don't delete accounts.
-    stopRequester.run();
-  }
-
-  /**
-   * Calls framework AccountManager.addOnAccountsUpdatedListener().
-   */
-  private void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
-    AccountManager.get(appContext).addOnAccountsUpdatedListener(listener, null, false);
-  }
-
-  /**
-   * Calls framework AccountManager.removeOnAccountsUpdatedListener().
-   */
-  private void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
-    AccountManager.get(appContext).removeOnAccountsUpdatedListener(listener);
-  }
-
-  /**
-   * Calls framework AccountManager.getAccountsByType() for Google accounts.
-   */
-  private Account[] getAccounts() {
-    return AccountManager.get(appContext).getAccountsByType(GOOGLE_ACCOUNT_TYPE);
-  }
-
-  /**
-   * Calls GMS AccountPicker.newChooseAccountIntent().
-   *
-   * Returns an Intent that when started will always show the account picker even if there's just
-   * one account on the device. If there are no accounts on the device it shows the UI to add one.
-   */
-  private Intent newChooseAccountIntent(Account defaultAccount) {
-    AccountChooserOptions chooserOptions = new AccountChooserOptions.Builder()
-        .setSelectedAccount(defaultAccount)
-        .setAllowableAccountsTypes(Arrays.asList(GOOGLE_ACCOUNT_TYPE))
-        .setAlwaysShowAccountPicker(true)
-        .build();
-    return AccountPicker.newChooseAccountIntent(chooserOptions);
-  }
-
-  /**
-   * Calls GMS GoogleAuthUtil.getToken(), without throwing any exceptions.
-   *
-   * Returns an empty string if no token is available for the account.
-   * Returns null if there was an error getting the token.
-   */
-  private String getToken(Account account) {
-    String joinedScopes = "oauth2:" + TextUtils.join(" ", OAUTH_SCOPES);
-    try {
-      return GoogleAuthUtil.getToken(appContext, account, joinedScopes);
-    } catch (UserRecoverableAuthException e) {
-        Log.w(TAG, "Recoverable error getting OAuth token", e);
-        Intent intent = e.getIntent();
-        Activity activity = activityHolder.get();
-        if (intent != null && activity != null) {
-          activity.startActivity(intent);
-        } else {
-          Log.e(TAG, "Failed to recover OAuth token", e);
-        }
-        return null;
-
-    } catch (IOException | GoogleAuthException e) {
-      Log.e(TAG, "Error getting auth token", e);
-      return null;
-    }
-  }
-
-  /**
-   * Calls GMS GoogleAuthUtil.clearToken(), without throwing any exceptions.
-   */
-  private void clearToken(String tokenValue) {
-    try {
-      GoogleAuthUtil.clearToken(appContext, tokenValue);
-    } catch (GoogleAuthException | IOException e) {
-      Log.e(TAG, "Error clearing auth token", e);
-    }
-  }
-
-  /**
-   * Checks whether the app has necessary permissions, asking for them if needed.
-   *
-   * This blocks until permissions are granted/declined, and should not be called on the UI thread.
-   *
-   * Returns true if permissions are granted.
-   */
-  private synchronized boolean checkPermission() {
-    if (ContextCompat.checkSelfPermission(appContext, GET_ACCOUNTS)
-        == PackageManager.PERMISSION_GRANTED) {
-      return true;
-    }
-
-    final Activity activity = activityHolder.get();
-    if (activity == null) {
-      return false;
-    }
-
-    // Check if we have previously been denied permission.
-    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, GET_ACCOUNTS)) {
-      activity.runOnUiThread(new Runnable() {
-        @Override
-        public void run() {
-          Toast.makeText(activity, R.string.starboard_accounts_permission, Toast.LENGTH_LONG)
-              .show();
-        }
-      });
-      return false;
-    }
-
-    // Request permission.
-    waitingForPermission = true;
-    permissionGranted = false;
-    ActivityCompat.requestPermissions(
-        activity, new String[]{GET_ACCOUNTS}, R.id.rc_get_accounts_permission);
-    try {
-      while (waitingForPermission) {
-        wait();
-      }
-    } catch (InterruptedException e) {
-      return false;
-    }
-    return permissionGranted;
-  }
-
-  /**
-   * Callback pass-thru from the Activity with the result from requesting permissions.
-   */
-  @Override
-  public void onRequestPermissionsResult(
-      int requestCode, String[] permissions, int[] grantResults) {
-    if (requestCode == R.id.rc_get_accounts_permission) {
-      synchronized (this) {
-        permissionGranted = grantResults.length > 0
-            && grantResults[0] == PackageManager.PERMISSION_GRANTED;
-        waitingForPermission = false;
-        notifyAll();
-      }
-    }
-  }
-
-  /**
-   * Remember the name of the signed-in account.
-   */
-  private void saveSignedInAccountName(String accountName) {
-    getPreferences().edit().putString(ACCOUNT_NAME_PREF_KEY, accountName).commit();
-  }
-
-  /**
-   * Returns the remembered name of the signed-in account.
-   */
-  private String loadSignedInAccountName() {
-    return getPreferences().getString(ACCOUNT_NAME_PREF_KEY, "");
-  }
-
-  private SharedPreferences getPreferences() {
-    return appContext.getSharedPreferences(SHARED_PREFS_NAME, 0);
-  }
-}
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/android/shared/cobalt/android_user_authorizer.cc b/starboard/android/shared/cobalt/android_user_authorizer.cc
index d8eb6c9..9b19e75 100644
--- a/starboard/android/shared/cobalt/android_user_authorizer.cc
+++ b/starboard/android/shared/cobalt/android_user_authorizer.cc
@@ -14,6 +14,8 @@
 
 #include "starboard/android/shared/cobalt/android_user_authorizer.h"
 
+#include <memory>
+
 #include "base/time/time.h"
 #include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/android/shared/jni_utils.h"
@@ -29,6 +31,17 @@
 namespace shared {
 namespace cobalt {
 
+bool UserAuthorizerIsSupported() {
+  // If using the NoopUserAuthorizer, then user authorizer functionally is not
+  // supported.
+  JniEnvExt* env = JniEnvExt::Get();
+  jobject local_ref = env->CallStarboardObjectMethodOrAbort(
+      "getUserAuthorizer", "()Ldev/cobalt/account/UserAuthorizer;");
+  return !env->IsInstanceOf(
+      local_ref,
+      env->FindClassExtOrAbort("dev/cobalt/account/NoopUserAuthorizer"));
+}
+
 AndroidUserAuthorizer::AndroidUserAuthorizer() : shutdown_(false) {
   JniEnvExt* env = JniEnvExt::Get();
   jobject local_ref = env->CallStarboardObjectMethodOrAbort(
@@ -71,8 +84,8 @@
                                        "()Z");
 }
 
-std::unique_ptr<AccessToken>
-AndroidUserAuthorizer::RefreshAuthorization(SbUser user) {
+std::unique_ptr<AccessToken> AndroidUserAuthorizer::RefreshAuthorization(
+    SbUser user) {
   SB_DCHECK(user == &::starboard::shared::nouser::g_user);
   if (shutdown_) {
     DLOG(WARNING) << "No-op RefreshAuthorization after shutdown";
@@ -85,8 +98,8 @@
   return CreateAccessToken(j_token.Get());
 }
 
-std::unique_ptr<AccessToken>
-AndroidUserAuthorizer::CreateAccessToken(jobject j_token) {
+std::unique_ptr<AccessToken> AndroidUserAuthorizer::CreateAccessToken(
+    jobject j_token) {
   if (!j_token) {
     return std::unique_ptr<AccessToken>();
   }
@@ -101,8 +114,8 @@
         env->GetStringStandardUTFOrAbort(j_token_string.Get());
   }
 
-  jlong j_expiry = env->CallLongMethodOrAbort(
-      j_token, "getExpirySeconds", "()J");
+  jlong j_expiry =
+      env->CallLongMethodOrAbort(j_token, "getExpirySeconds", "()J");
   if (j_expiry > 0) {
     access_token->expiry =
         base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(j_expiry);
@@ -120,7 +133,10 @@
 namespace account {
 
 UserAuthorizer* UserAuthorizer::Create() {
-  return new ::starboard::android::shared::cobalt::AndroidUserAuthorizer();
+  if (::starboard::android::shared::cobalt::UserAuthorizerIsSupported()) {
+    return new ::starboard::android::shared::cobalt::AndroidUserAuthorizer();
+  }
+  return nullptr;
 }
 
 }  // namespace account
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/starboard/common/ref_counted.h b/starboard/common/ref_counted.h
index cd94aa1..4c3c8c3 100644
--- a/starboard/common/ref_counted.h
+++ b/starboard/common/ref_counted.h
@@ -6,6 +6,7 @@
 #define STARBOARD_COMMON_REF_COUNTED_H_
 
 #include <algorithm>
+#include <utility>
 
 #include "starboard/atomic.h"
 #include "starboard/common/log.h"
@@ -148,7 +149,8 @@
     : public starboard::RefCountedThreadSafe<starboard::RefCountedData<T> > {
  public:
   RefCountedData() : data() {}
-  RefCountedData(const T& in_value) : data(in_value) {}
+  RefCountedData(const T& in_value) : data(in_value) {}        // NOLINT
+  RefCountedData(T&& in_value) : data(std::move(in_value)) {}  // NOLINT
 
   T data;
 
@@ -212,7 +214,7 @@
 
   scoped_refptr() : ptr_(NULL) {}
 
-  scoped_refptr(T* p) : ptr_(p) {
+  scoped_refptr(T* p) : ptr_(p) {  // NOLINT
     if (ptr_)
       ptr_->AddRef();
   }
@@ -222,6 +224,20 @@
       ptr_->AddRef();
   }
 
+  // Move constructor. This is required in addition to the move conversion
+  // constructor below.
+  scoped_refptr(scoped_refptr<T>&& r) noexcept : ptr_(r.ptr_) {
+    r.ptr_ = nullptr;
+  }
+
+  // Move conversion constructor.
+  template <typename U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
+  scoped_refptr(scoped_refptr<U>&& r) noexcept : ptr_(r.get()) {
+    r.ptr_ = nullptr;
+  }
+
   template <typename U>
   scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
     if (ptr_)
@@ -275,6 +291,11 @@
 
  protected:
   T* ptr_;
+
+ private:
+  // Friend required for move constructors that set r.ptr_ to null.
+  template <typename U>
+  friend class scoped_refptr;
 };
 
 // Handy utility for creating a scoped_refptr<T> out of a T* explicitly without
diff --git a/third_party/crashpad/wrapper/proto/BUILD.gn b/third_party/crashpad/wrapper/proto/BUILD.gn
new file mode 100644
index 0000000..4819e31
--- /dev/null
+++ b/third_party/crashpad/wrapper/proto/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("crashpad_annotations_proto") {
+  sources = [ "crashpad_annotations.proto" ]
+  generate_python = false
+}
diff --git a/third_party/crashpad/wrapper/proto/crashpad_annotations.proto b/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
new file mode 100644
index 0000000..eb739bc
--- /dev/null
+++ b/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
@@ -0,0 +1,36 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+
+package crashpad.wrapper;
+
+// Annotations that can be shared between Cobalt and Crashpad handler processes.
+// Next id: 5
+message CrashpadAnnotations {
+  // The product name.
+  string product = 1;
+
+  // The product version.
+  string version = 2;
+
+  // The User-Agent string that identifies brand, model, etc.
+  string user_agent_string = 3;
+
+  // Annotations with keys that are unknown at compile time.
+  map<string, string> runtime_annotations = 4;
+}
+
diff --git a/third_party/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