Import Cobalt 21.master.0.289410
diff --git a/src/build/json_schema_bundle_compile.gypi b/src/build/json_schema_bundle_compile.gypi
deleted file mode 100644
index ecefe41..0000000
--- a/src/build/json_schema_bundle_compile.gypi
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'variables': {
-    # When including this gypi, the following variables must be set:
-    #   idl_schema_files: an array of idl files that comprise the api model.
-    #   cc_dir: path to generated files
-    #   root_namespace: the C++ namespace that all generated files go under
-    # Functions and namespaces can be excluded by setting "nocompile" to true.
-    'api_gen_dir': '<(DEPTH)/tools/json_schema_compiler',
-    'api_gen': '<(api_gen_dir)/compiler.py',
-  },
-  'actions': [
-    {
-      'action_name': 'genapi_bundle',
-      'inputs': [
-        '<(api_gen_dir)/cc_generator.py',
-        '<(api_gen_dir)/code.py',
-        '<(api_gen_dir)/compiler.py',
-        '<(api_gen_dir)/cpp_type_generator.py',
-        '<(api_gen_dir)/cpp_util.py',
-        '<(api_gen_dir)/h_generator.py',
-        '<(api_gen_dir)/idl_schema.py',
-        '<(api_gen_dir)/json_schema.py',
-        '<(api_gen_dir)/model.py',
-        '<(api_gen_dir)/schema_bundle_generator.py',
-        '<(api_gen_dir)/util_cc_helper.py',
-        '<@(idl_schema_files)',
-      ],
-      'outputs': [
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/generated_api.h',
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/generated_schemas.h',
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/generated_schemas.cc',
-      ],
-      'action': [
-        'python',
-        '<(api_gen)',
-        '--root=<(DEPTH)',
-        '--destdir=<(SHARED_INTERMEDIATE_DIR)',
-        '--namespace=<(root_namespace)',
-        '--bundle',
-        '<@(idl_schema_files)',
-      ],
-      'message': 'Generating C++ API bundle code',
-      'process_outputs_as_sources': 1,
-    }
-  ],
-  'include_dirs': [
-    '<(SHARED_INTERMEDIATE_DIR)',
-    '<(DEPTH)',
-  ],
-  'direct_dependent_settings': {
-    'include_dirs': [
-      '<(SHARED_INTERMEDIATE_DIR)',
-    ]
-  },
-  # This target exports a hard dependency because it generates header
-  # files.
-  'hard_dependency': 1,
-}
diff --git a/src/build/json_schema_compile.gypi b/src/build/json_schema_compile.gypi
deleted file mode 100644
index 6c8f69c..0000000
--- a/src/build/json_schema_compile.gypi
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'variables': {
-    # When including this gypi, the following variables must be set:
-    #   json_schema_files: a list of json files that comprise the api model.
-    #   idl_schema_files: a list of IDL files that comprise the api model.
-    #   cc_dir: path to generated files
-    #   root_namespace: the C++ namespace that all generated files go under
-    # Functions and namespaces can be excluded by setting "nocompile" to true.
-    'api_gen_dir': '<(DEPTH)/tools/json_schema_compiler',
-    'api_gen': '<(api_gen_dir)/compiler.py',
-  },
-  'rules': [
-    {
-      'rule_name': 'genapi',
-      'extension': 'json',
-      'inputs': [
-        '<(api_gen_dir)/any.cc',
-        '<(api_gen_dir)/any.h',
-        '<(api_gen_dir)/any_helper.py',
-        '<(api_gen_dir)/cc_generator.py',
-        '<(api_gen_dir)/code.py',
-        '<(api_gen_dir)/compiler.py',
-        '<(api_gen_dir)/cpp_type_generator.py',
-        '<(api_gen_dir)/cpp_util.py',
-        '<(api_gen_dir)/h_generator.py',
-        '<(api_gen_dir)/json_schema.py',
-        '<(api_gen_dir)/model.py',
-        '<(api_gen_dir)/util.cc',
-        '<(api_gen_dir)/util.h',
-        '<(api_gen_dir)/util_cc_helper.py',
-        # TODO(calamity): uncomment this when gyp on windows behaves like other
-        # platforms. List expansions of filepaths in inputs expand to different
-        # things.
-        # '<@(json_schema_files)',
-      ],
-      'outputs': [
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).cc',
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).h',
-      ],
-      'action': [
-        'python',
-        '<(api_gen)',
-        '<(RULE_INPUT_PATH)',
-        '--root=<(DEPTH)',
-        '--destdir=<(SHARED_INTERMEDIATE_DIR)',
-        '--namespace=<(root_namespace)',
-      ],
-      'message': 'Generating C++ code from <(RULE_INPUT_PATH) json files',
-      'process_outputs_as_sources': 1,
-    },
-    {
-      'rule_name': 'genapi_idl',
-      'msvs_external_rule': 1,
-      'extension': 'idl',
-      'inputs': [
-        '<(api_gen_dir)/any.cc',
-        '<(api_gen_dir)/any.h',
-        '<(api_gen_dir)/any_helper.py',
-        '<(api_gen_dir)/cc_generator.py',
-        '<(api_gen_dir)/code.py',
-        '<(api_gen_dir)/compiler.py',
-        '<(api_gen_dir)/cpp_type_generator.py',
-        '<(api_gen_dir)/cpp_util.py',
-        '<(api_gen_dir)/h_generator.py',
-        '<(api_gen_dir)/idl_schema.py',
-        '<(api_gen_dir)/model.py',
-        '<(api_gen_dir)/util.cc',
-        '<(api_gen_dir)/util.h',
-        '<(api_gen_dir)/util_cc_helper.py',
-        # TODO(calamity): uncomment this when gyp on windows behaves like other
-        # platforms. List expansions of filepaths in inputs expand to different
-        # things.
-        # '<@(idl_schema_files)',
-      ],
-      'outputs': [
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).cc',
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).h',
-      ],
-      'action': [
-        'python',
-        '<(api_gen)',
-        '<(RULE_INPUT_PATH)',
-        '--root=<(DEPTH)',
-        '--destdir=<(SHARED_INTERMEDIATE_DIR)',
-        '--namespace=<(root_namespace)',
-      ],
-      'message': 'Generating C++ code from <(RULE_INPUT_PATH) IDL files',
-      'process_outputs_as_sources': 1,
-    },
-  ],
-  'include_dirs': [
-    '<(SHARED_INTERMEDIATE_DIR)',
-    '<(DEPTH)',
-  ],
-  'dependencies':[
-    '<(DEPTH)/tools/json_schema_compiler/api_gen_util.gyp:api_gen_util',
-  ],
-  'direct_dependent_settings': {
-    'include_dirs': [
-      '<(SHARED_INTERMEDIATE_DIR)',
-    ]
-  },
-  # This target exports a hard dependency because it generates header
-  # files.
-  'hard_dependency': 1,
-}
diff --git a/src/build/json_to_struct.gypi b/src/build/json_to_struct.gypi
deleted file mode 100644
index 130f6d1..0000000
--- a/src/build/json_to_struct.gypi
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'variables': {
-    # When including this gypi, the following variables must be set:
-    #   schema_file: a json file that comprise the structure model.
-    #   namespace: the C++ namespace that all generated files go under
-    #   cc_dir: path to generated files
-    # Functions and namespaces can be excluded by setting "nocompile" to true.
-    'struct_gen_dir': '<(DEPTH)/tools/json_to_struct',
-    'struct_gen': '<(struct_gen_dir)/json_to_struct.py',
-  },
-  'rules': [
-    {
-      'rule_name': 'genstaticinit',
-      'extension': 'json',
-      'inputs': [
-        '<(struct_gen_dir)/element_generator.py',
-        '<(struct_gen_dir)/json_to_struct.py',
-        '<(struct_gen_dir)/struct_generator.py',
-        '<(schema_file)',
-      ],
-      'outputs': [
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).cc',
-        '<(SHARED_INTERMEDIATE_DIR)/<(cc_dir)/<(RULE_INPUT_ROOT).h',
-      ],
-      'action': [
-        'python',
-        '<(struct_gen)',
-        '<(RULE_INPUT_PATH)',
-        '--destbase=<(SHARED_INTERMEDIATE_DIR)',
-        '--destdir=<(cc_dir)',
-        '--namespace=<(namespace)',
-        '--schema=<(schema_file)',
-      ],
-      'message': 'Generating C++ static initializers from <(RULE_INPUT_PATH)',
-      'process_outputs_as_sources': 1,
-    },
-  ],
-  'include_dirs': [
-    '<(SHARED_INTERMEDIATE_DIR)',
-    '<(DEPTH)',
-  ],
-  # This target exports a hard dependency because it generates header
-  # files.
-  'hard_dependency': 1,
-}
diff --git a/src/build/shim_headers.gypi b/src/build/shim_headers.gypi
deleted file mode 100644
index cf0914d..0000000
--- a/src/build/shim_headers.gypi
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This file is meant to be included into a target to handle shim headers
-# in a consistent manner. To use this the following variables need to be
-# defined:
-#   headers_root_path: string: path to directory containing headers
-#   header_filenames: list: list of header file names
-
-{
-  'variables': {
-    'shim_headers_path': '<(INTERMEDIATE_DIR)/shim_headers',
-  },
-  'direct_dependent_settings': {
-    'include_dirs+': [
-      '<(shim_headers_path)',
-    ],
-  },
-  'actions': [
-    {
-      'variables': {
-        'generator_path': '<(DEPTH)/tools/generate_shim_headers/generate_shim_headers.py',
-        'generator_args': [
-          '--headers-root', '<(headers_root_path)',
-          '--output-directory', '<(shim_headers_path)',
-          '<@(header_filenames)',
-        ],
-      },
-      'action_name': 'generate_<(_target_name)_shim_headers',
-      'inputs': [
-        '<(generator_path)',
-      ],
-      'outputs': [
-        '<!@pymod_do_main(generate_shim_headers <@(generator_args) --outputs)',
-      ],
-      'action': ['python',
-                 '<(generator_path)',
-                 '<@(generator_args)',
-                 '--generate',
-      ],
-      'message': 'Generating <(_target_name) shim headers.',
-    },
-  ],
-}
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index f2872b4..644ecae 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -119,6 +119,12 @@
    the comment of `SbMediaCanPlayMimeAndKeySystem()` in `media.h` for more
    details.
 
+ - **Added support for controlling shutdown behavior of graphics system.**
+
+   Cobalt normally clears the framebuffer to opaque black on suspend or exit.
+   This behavior can now be overridden by implementing the cobalt extension
+   function `CobaltExtensionGraphicsApi::ShouldClearFrameOnShutdown`.
+
 ## Version 20
 
  - **Support for QUIC and SPDY is now enabled.**
diff --git a/src/cobalt/audio/audio_test.gyp b/src/cobalt/audio/audio_test.gyp
index c4e3979..97bd3f9 100644
--- a/src/cobalt/audio/audio_test.gyp
+++ b/src/cobalt/audio/audio_test.gyp
@@ -24,6 +24,7 @@
         'audio_node_input_output_test.cc',
       ],
       'dependencies': [
+        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/media/media.gyp:media',
         '<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/cobalt/bindings/run_cobalt_bindings_tests.py b/src/cobalt/bindings/run_cobalt_bindings_tests.py
index eb0fe24..33f7494 100755
--- a/src/cobalt/bindings/run_cobalt_bindings_tests.py
+++ b/src/cobalt/bindings/run_cobalt_bindings_tests.py
@@ -25,12 +25,12 @@
 import argparse
 import os
 import sys
-from webkitpy.bindings.bindings_tests import run_bindings_tests
-
 import _env  # pylint: disable=unused-import
+
 from cobalt.bindings.idl_compiler_cobalt import IdlCompilerCobalt
 from cobalt.bindings.mozjs45.code_generator_mozjs45 import CodeGeneratorMozjs45
 from cobalt.bindings.v8c.code_generator_v8c import CodeGeneratorV8c
+from webkitpy.bindings.bindings_tests import run_bindings_tests
 
 
 def main(argv):
diff --git a/src/cobalt/bindings/testing/date_bindings_test.cc b/src/cobalt/bindings/testing/date_bindings_test.cc
index 602f2ee..bb58362 100644
--- a/src/cobalt/bindings/testing/date_bindings_test.cc
+++ b/src/cobalt/bindings/testing/date_bindings_test.cc
@@ -14,8 +14,11 @@
 
 #include "base/logging.h"
 
+#include "base/time/time.h"
 #include "cobalt/bindings/testing/bindings_test_base.h"
 #include "cobalt/bindings/testing/interface_with_date.h"
+#include "starboard/client_porting/eztime/eztime.h"
+#include "starboard/time_zone.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -79,6 +82,45 @@
   EXPECT_LT(std::abs(posix_now_ms - js_now_ms), 1000);
 }
 
+TEST_F(DateBindingsTest, StarboardTimeZone) {
+  const char* month_table[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+  std::string result;
+
+  EvaluateScript("new Date().toString();", &result);
+  base::Time now = base::Time::Now();
+
+  SB_LOG(INFO) << "JavaScript Date now is : " << result;
+  SB_LOG(INFO) << "and base::Time is: " << now;
+
+  base::Time::Exploded exploded;
+  now.LocalExplode(&exploded);
+  EXPECT_NE(result.find(std::to_string(exploded.year)), std::string::npos);
+  EXPECT_NE(result.find(month_table[exploded.month - 1]), std::string::npos);
+  EXPECT_NE(result.find(std::to_string(exploded.day_of_month)),
+            std::string::npos);
+  EXPECT_NE(result.find(std::to_string(exploded.hour)), std::string::npos);
+  EXPECT_NE(result.find(std::to_string(exploded.minute)), std::string::npos);
+  EXPECT_NE(result.find(std::to_string(exploded.second)), std::string::npos);
+}
+
+TEST_F(DateBindingsTest, TimezoneOffset) {
+  EzTimeExploded ez_exploded;
+
+  auto eztt = EzTimeTFromSbTime(SbTimeGetNow());
+  EzTimeTExplodeLocal(&eztt, &ez_exploded);
+  // ez_exploded is already local time, use UTC method to convert to
+  // EzTimeT.
+  EzTimeT local_time_minutes = EzTimeTImplodeUTC(&ez_exploded) / 60;
+  EzTimeT utc_minutes = EzTimeTFromSbTime(SbTimeGetNow()) / 60;
+  EzTimeT timezone_offset = utc_minutes - local_time_minutes;
+
+  std::string result;
+  EvaluateScript("new Date().getTimezoneOffset();", &result);
+
+  EXPECT_EQ(result, std::to_string(utc_minutes - local_time_minutes));
+}
+
 }  // namespace
 }  // namespace testing
 }  // namespace bindings
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index 60f5596..271b2cc 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -35,6 +35,7 @@
 _DISABLED_BLACKBOXTEST_CONFIGS = [
     'android-arm/devel',
     'android-arm64/devel',
+    'android-x86/devel',
     'raspi-0/devel',
 ]
 
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index ace1e98..d709ff7 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -637,7 +637,7 @@
 #endif  // ENABLE_DEBUGGER
 
   // Pass down this callback from to Web module.
-  options_.web_module_options.maybe_freeze_callback =
+  options.maybe_freeze_callback =
       base::Bind(&BrowserModule::OnMaybeFreeze, base::Unretained(this));
 
   web_module_.reset(new WebModule(
@@ -1830,7 +1830,8 @@
 #if defined(ENABLE_DEBUGGER)
       debug_console_ready_to_freeze &&
 #endif  // defined(ENABLE_DEBUGGER)
-      web_module_ready_to_freeze) {
+      web_module_ready_to_freeze &&
+      application_state_ == base::kApplicationStateConcealed) {
 #if SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION || \
     SB_HAS(CONCEALED_STATE)
     SbSystemRequestFreeze();
diff --git a/src/cobalt/browser/memory_settings/memory_settings.h b/src/cobalt/browser/memory_settings/memory_settings.h
index 3aee9b5..afaeef6 100644
--- a/src/cobalt/browser/memory_settings/memory_settings.h
+++ b/src/cobalt/browser/memory_settings/memory_settings.h
@@ -107,7 +107,8 @@
  private:
   // Default constructor for MemorySetting is forbidden. Do not use it.
   MemorySetting();
-  SB_DISALLOW_COPY_AND_ASSIGN(MemorySetting);
+  MemorySetting(const MemorySetting&) = delete;
+  void operator=(const MemorySetting&) = delete;
 };
 
 // A memory setting for integer values.
@@ -135,7 +136,8 @@
  private:
   int64_t value_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(IntSetting);
+  IntSetting(const IntSetting&) = delete;
+  void operator=(const IntSetting&) = delete;
 };
 
 // A memory setting for dimensions type values like "2048x4096x2" where
@@ -166,7 +168,8 @@
  private:
   TextureDimensions value_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DimensionSetting);
+  DimensionSetting(const DimensionSetting&) = delete;
+  void operator=(const DimensionSetting&) = delete;
 };
 
 class SkiaGlyphAtlasTextureSetting : public DimensionSetting {
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 9748ca1..10124f4 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -52,6 +52,7 @@
 #include "cobalt/dom/keyboard_event_init.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/navigator.h"
 #include "cobalt/dom/pointer_event.h"
 #include "cobalt/dom/storage.h"
 #include "cobalt/dom/ui_event.h"
@@ -240,7 +241,13 @@
   void CancelSynchronousLoads();
 
   void IsReadyToFreeze(volatile bool* is_ready_to_freeze) {
-    *is_ready_to_freeze = !media_session_client_->is_active();
+    if (window_->media_session()->media_session_client() == NULL) {
+      *is_ready_to_freeze = true;
+      return;
+    }
+
+    *is_ready_to_freeze =
+        !window_->media_session()->media_session_client()->is_active();
   }
 
  private:
@@ -432,8 +439,6 @@
   // DocumentObserver that observes the loading document.
   std::unique_ptr<DocumentLoadedObserver> document_load_observer_;
 
-  std::unique_ptr<media_session::MediaSessionClient> media_session_client_;
-
   std::unique_ptr<layout::TopmostEventTarget> topmost_event_target_;
 
   base::Closure on_before_unload_fired_but_not_handled_;
@@ -592,11 +597,6 @@
       &mutation_observer_task_manager_, data.options.dom_settings_options));
   DCHECK(environment_settings_);
 
-  media_session_client_ = media_session::MediaSessionClient::Create();
-  media_session_client_->SetMediaPlayerFactory(data.web_media_player_factory);
-  media_session_client_->SetMaybeFreezeCallback(
-      data.options.maybe_freeze_callback);
-
   system_caption_settings_ = new cobalt::dom::captions::SystemCaptionSettings(
       environment_settings_.get());
 
@@ -653,7 +653,6 @@
                  base::Unretained(this)),
       data.window_close_callback, data.window_minimize_callback,
       data.options.on_screen_keyboard_bridge, data.options.camera_3d,
-      media_session_client_->GetMediaSession(),
       base::Bind(&WebModule::Impl::OnStartDispatchEvent,
                  base::Unretained(this)),
       base::Bind(&WebModule::Impl::OnStopDispatchEvent, base::Unretained(this)),
@@ -686,6 +685,11 @@
   error_callback_ = data.error_callback;
   DCHECK(!error_callback_.is_null());
 
+  window_->navigator()->set_maybefreeze_callback(
+      data.options.maybe_freeze_callback);
+  window_->navigator()->set_media_player_factory(
+      data.web_media_player_factory);
+
   bool is_concealed =
       (data.initial_application_state == base::kApplicationStateConcealed);
   layout_manager_.reset(new layout::LayoutManager(
@@ -749,7 +753,6 @@
       script::GlobalEnvironment::ReportErrorCallback());
   window_->DispatchEvent(new dom::Event(base::Tokens::unload()));
   document_load_observer_.reset();
-  media_session_client_.reset();
 
 #if defined(ENABLE_DEBUGGER)
   debug_module_.reset();
@@ -1069,7 +1072,6 @@
 void WebModule::Impl::SetWebMediaPlayerFactory(
     media::WebMediaPlayerFactory* web_media_player_factory) {
   window_->set_web_media_player_factory(web_media_player_factory);
-  media_session_client_->SetMediaPlayerFactory(web_media_player_factory);
 }
 
 void WebModule::Impl::SetApplicationState(base::ApplicationState state) {
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 396f316..3fcc752 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-286167
\ No newline at end of file
+289410
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index 7f4f3c6..7880f93 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -523,10 +523,6 @@
     # further reduced on systems with extremely low memory.
     'cobalt_media_source_garbage_collection_duration_threshold_in_seconds%': -1,
 
-    'compiler_flags_host': [
-      '-D__LB_HOST__',  # TODO: Is this still needed?
-    ],
-
     'defines_debug': [
       'ALLOCATOR_STATS_TRACKING',
       'COBALT_BOX_DUMP_ENABLED',
diff --git a/src/cobalt/build/cobalt_configuration.py b/src/cobalt/build/cobalt_configuration.py
index 2cd04f6..b178ed5 100644
--- a/src/cobalt/build/cobalt_configuration.py
+++ b/src/cobalt/build/cobalt_configuration.py
@@ -38,8 +38,17 @@
                          application_directory)
 
   def GetVariables(self, config_name):
+
+    # Use env var to optimize build speed on CI
+    try:
+      # Force to int, so it's easy to pass in an override.
+      use_fastbuild = int(os.environ.get('IS_CI', 0))
+    except (ValueError, TypeError):
+      use_fastbuild = 0
+
     variables = {
-        'cobalt_fastbuild': os.environ.get('LB_FASTBUILD', 0),
+        # This is used to omit large debuginfo in files on CI environment
+        'cobalt_fastbuild': use_fastbuild,
 
         # This is here rather than cobalt_configuration.gypi so that it's
         # available for browser_bindings_gen.gyp.
@@ -119,7 +128,7 @@
         # Disabled because of: Flaky on buildbot across multiple buildconfigs.
         # Non-reproducible with local runs.
         ('xhr/WebPlatformTest.Run/'
-        'XMLHttpRequest_send_entity_body_get_head_async_htm'),
+         'XMLHttpRequest_send_entity_body_get_head_async_htm'),
         'xhr/WebPlatformTest.Run/XMLHttpRequest_status_error_htm',
         'xhr/WebPlatformTest.Run/XMLHttpRequest_response_json_htm',
         'xhr/WebPlatformTest.Run/XMLHttpRequest_send_redirect_to_non_cors_htm',
diff --git a/src/cobalt/css_parser/css_parser.gyp b/src/cobalt/css_parser/css_parser.gyp
index d07f4c8..aa4ae4a 100644
--- a/src/cobalt/css_parser/css_parser.gyp
+++ b/src/cobalt/css_parser/css_parser.gyp
@@ -33,6 +33,8 @@
     {
       'target_name': 'css_grammar',
       'type': 'none',
+      # This target generates header files which may be used by other targets.
+      'hard_dependency': 1,
       'sources': [
         'grammar.h',
         'grammar.y'
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 920cce8..6909469 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -4882,7 +4882,17 @@
   | kCobaltUiNavFocusTransformFunctionToken maybe_whitespace ')'
       maybe_whitespace {
     $<transform_functions>0->emplace_back(
-        new cssom::CobaltUiNavFocusTransformFunction);
+        new cssom::CobaltUiNavFocusTransformFunction(1.0f, 1.0f));
+  }
+  | kCobaltUiNavFocusTransformFunctionToken maybe_whitespace number ')'
+      maybe_whitespace {
+    $<transform_functions>0->emplace_back(
+        new cssom::CobaltUiNavFocusTransformFunction($3, $3));
+  }
+  | kCobaltUiNavFocusTransformFunctionToken maybe_whitespace number comma
+      number ')' maybe_whitespace {
+    $<transform_functions>0->emplace_back(
+        new cssom::CobaltUiNavFocusTransformFunction($3, $5));
   }
   // This Cobalt-specific transform function for hybrid navigation tracks
   // the direction in which focus is moving. This can be used to provide
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index 040c178..d510f2c 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -7223,7 +7223,55 @@
 TEST_F(ParserTest, ParsesCobaltUiNavFocusTransform) {
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
       parser_.ParseStyleDeclarationList(
-          "transform: -cobalt-ui-nav-focus-transform();",
+          "transform: -cobalt-ui-nav-focus-transform();", source_location_);
+
+  ASSERT_TRUE(style->IsDeclared(cssom::kTransformProperty));
+  scoped_refptr<cssom::TransformFunctionListValue> transform_list =
+      dynamic_cast<cssom::TransformFunctionListValue*>(
+          style->GetPropertyValue(cssom::kTransformProperty).get());
+  ASSERT_TRUE(transform_list);
+  EXPECT_TRUE(transform_list->value().HasTrait(kTraitIsDynamic));
+  EXPECT_FALSE(transform_list->value().HasTrait(kTraitUsesRelativeUnits));
+  EXPECT_TRUE(transform_list->value().HasTrait(kTraitUsesUiNavFocus));
+  ASSERT_EQ(1, transform_list->value().size());
+
+  const cssom::CobaltUiNavFocusTransformFunction* focus_function =
+      dynamic_cast<const cssom::CobaltUiNavFocusTransformFunction*>(
+          transform_list->value()[0].get());
+  ASSERT_TRUE(focus_function);
+
+  EXPECT_FLOAT_EQ(1.0f, focus_function->x_translation_scale());
+  EXPECT_FLOAT_EQ(1.0f, focus_function->y_translation_scale());
+}
+
+TEST_F(ParserTest, ParsesCobaltUiNavFocusTransformOneArgument) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList(
+          "transform: -cobalt-ui-nav-focus-transform(2);", source_location_);
+
+  ASSERT_TRUE(style->IsDeclared(cssom::kTransformProperty));
+  scoped_refptr<cssom::TransformFunctionListValue> transform_list =
+      dynamic_cast<cssom::TransformFunctionListValue*>(
+          style->GetPropertyValue(cssom::kTransformProperty).get());
+  ASSERT_TRUE(transform_list);
+  EXPECT_TRUE(transform_list->value().HasTrait(kTraitIsDynamic));
+  EXPECT_FALSE(transform_list->value().HasTrait(kTraitUsesRelativeUnits));
+  EXPECT_TRUE(transform_list->value().HasTrait(kTraitUsesUiNavFocus));
+  ASSERT_EQ(1, transform_list->value().size());
+
+  const cssom::CobaltUiNavFocusTransformFunction* focus_function =
+      dynamic_cast<const cssom::CobaltUiNavFocusTransformFunction*>(
+          transform_list->value()[0].get());
+  ASSERT_TRUE(focus_function);
+
+  EXPECT_FLOAT_EQ(2.0f, focus_function->x_translation_scale());
+  EXPECT_FLOAT_EQ(2.0f, focus_function->y_translation_scale());
+}
+
+TEST_F(ParserTest, ParsesCobaltUiNavFocusTransformTwoArguments) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList(
+          "transform: -cobalt-ui-nav-focus-transform(0.5, 2);",
           source_location_);
 
   ASSERT_TRUE(style->IsDeclared(cssom::kTransformProperty));
@@ -7240,13 +7288,15 @@
       dynamic_cast<const cssom::CobaltUiNavFocusTransformFunction*>(
           transform_list->value()[0].get());
   ASSERT_TRUE(focus_function);
+
+  EXPECT_FLOAT_EQ(0.5f, focus_function->x_translation_scale());
+  EXPECT_FLOAT_EQ(2.0f, focus_function->y_translation_scale());
 }
 
 TEST_F(ParserTest, ParsesCobaltUiNavSpotlightTransform) {
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
       parser_.ParseStyleDeclarationList(
-          "transform: -cobalt-ui-nav-spotlight-transform();",
-          source_location_);
+          "transform: -cobalt-ui-nav-spotlight-transform();", source_location_);
 
   ASSERT_TRUE(style->IsDeclared(cssom::kTransformProperty));
   scoped_refptr<cssom::TransformFunctionListValue> transform_list =
@@ -8532,8 +8582,9 @@
 TEST_F(ParserTest, ParsesAnimatablePropertyNameListWithSingleValue) {
   scoped_refptr<cssom::PropertyKeyListValue> property_name_list =
       dynamic_cast<cssom::PropertyKeyListValue*>(
-          parser_.ParsePropertyValue("transition-property", "color",
-                                     source_location_)
+          parser_
+              .ParsePropertyValue("transition-property", "color",
+                                  source_location_)
               .get());
   ASSERT_TRUE(property_name_list);
 
@@ -8544,9 +8595,10 @@
 TEST_F(ParserTest, ParsesAnimatablePropertyNameListWithMultipleValues) {
   scoped_refptr<cssom::PropertyKeyListValue> property_name_list =
       dynamic_cast<cssom::PropertyKeyListValue*>(
-          parser_.ParsePropertyValue("transition-property",
-                                     "color, transform , background-color",
-                                     source_location_)
+          parser_
+              .ParsePropertyValue("transition-property",
+                                  "color, transform , background-color",
+                                  source_location_)
               .get());
   ASSERT_TRUE(property_name_list);
 
@@ -8559,8 +8611,9 @@
 TEST_F(ParserTest, ParsesTransitionPropertyWithAllValue) {
   scoped_refptr<cssom::PropertyKeyListValue> property_name_list =
       dynamic_cast<cssom::PropertyKeyListValue*>(
-          parser_.ParsePropertyValue("transition-property", "all",
-                                     source_location_)
+          parser_
+              .ParsePropertyValue("transition-property", "all",
+                                  source_location_)
               .get());
   ASSERT_TRUE(property_name_list.get());
 
@@ -8593,8 +8646,9 @@
 TEST_F(ParserTest, ParsesTimeListWithSingleValue) {
   scoped_refptr<cssom::TimeListValue> time_list_value =
       dynamic_cast<cssom::TimeListValue*>(
-          parser_.ParsePropertyValue("transition-duration", "1s",
-                                     source_location_).get());
+          parser_
+              .ParsePropertyValue("transition-duration", "1s", source_location_)
+              .get());
   ASSERT_TRUE(time_list_value.get());
 
   ASSERT_EQ(1, time_list_value->value().size());
@@ -8604,9 +8658,10 @@
 TEST_F(ParserTest, ParsesTimeListWithMultipleValues) {
   scoped_refptr<cssom::TimeListValue> time_list_value =
       dynamic_cast<cssom::TimeListValue*>(
-          parser_.ParsePropertyValue("transition-duration",
-                                     "2s, 1ms, 0, 2ms, 3s, 3ms",
-                                     source_location_).get());
+          parser_
+              .ParsePropertyValue("transition-duration",
+                                  "2s, 1ms, 0, 2ms, 3s, 3ms", source_location_)
+              .get());
   ASSERT_TRUE(time_list_value.get());
 
   ASSERT_EQ(6, time_list_value->value().size());
@@ -8621,8 +8676,10 @@
 TEST_F(ParserTest, ParsesNegativeTimeList) {
   scoped_refptr<cssom::TimeListValue> time_list_value =
       dynamic_cast<cssom::TimeListValue*>(
-          parser_.ParsePropertyValue("transition-duration", "-4s",
-                                     source_location_).get());
+          parser_
+              .ParsePropertyValue("transition-duration", "-4s",
+                                  source_location_)
+              .get());
   ASSERT_TRUE(time_list_value.get());
 
   ASSERT_EQ(1, time_list_value->value().size());
@@ -8780,52 +8837,52 @@
 }
 
 TEST_F(ParserTest, AboveRangeCubicBezierP1XParameterProduceError) {
-  EXPECT_CALL(parser_observer_,
-              OnError(
-                  "[object ParserTest]:1:1: error: cubic-bezier control point "
-                  "x values must be in the range [0, 1]."));
+  EXPECT_CALL(
+      parser_observer_,
+      OnError("[object ParserTest]:1:1: error: cubic-bezier control point "
+              "x values must be in the range [0, 1]."));
   scoped_refptr<cssom::PropertyValue> error_value = parser_.ParsePropertyValue(
       "transition-timing-function", "cubic-bezier(2, 0, 0.5, 0)",
       source_location_);
   // Test that the ease function was returned in place of the error function.
   EXPECT_TRUE(error_value->Equals(*CreateSingleTimingFunctionValue(
-                                      cssom::TimingFunction::GetEase().get())));
+      cssom::TimingFunction::GetEase().get())));
 }
 TEST_F(ParserTest, BelowRangeCubicBezierP1XParameterProduceError) {
-  EXPECT_CALL(parser_observer_,
-              OnError(
-                  "[object ParserTest]:1:1: error: cubic-bezier control point "
-                  "x values must be in the range [0, 1]."));
+  EXPECT_CALL(
+      parser_observer_,
+      OnError("[object ParserTest]:1:1: error: cubic-bezier control point "
+              "x values must be in the range [0, 1]."));
   scoped_refptr<cssom::PropertyValue> error_value = parser_.ParsePropertyValue(
       "transition-timing-function", "cubic-bezier(-1, 0, 0.5, 0)",
       source_location_);
   // Test that the ease function was returned in place of the error function.
   EXPECT_TRUE(error_value->Equals(*CreateSingleTimingFunctionValue(
-                                      cssom::TimingFunction::GetEase().get())));
+      cssom::TimingFunction::GetEase().get())));
 }
 TEST_F(ParserTest, AboveRangeCubicBezierP2XParameterProduceError) {
-  EXPECT_CALL(parser_observer_,
-              OnError(
-                  "[object ParserTest]:1:1: error: cubic-bezier control point "
-                  "x values must be in the range [0, 1]."));
+  EXPECT_CALL(
+      parser_observer_,
+      OnError("[object ParserTest]:1:1: error: cubic-bezier control point "
+              "x values must be in the range [0, 1]."));
   scoped_refptr<cssom::PropertyValue> error_value = parser_.ParsePropertyValue(
       "transition-timing-function", "cubic-bezier(0.5, 0, 2, 0)",
       source_location_);
   // Test that the ease function was returned in place of the error function.
   EXPECT_TRUE(error_value->Equals(*CreateSingleTimingFunctionValue(
-                                      cssom::TimingFunction::GetEase().get())));
+      cssom::TimingFunction::GetEase().get())));
 }
 TEST_F(ParserTest, BelowRangeCubicBezierP2XParameterProduceError) {
-  EXPECT_CALL(parser_observer_,
-              OnError(
-                  "[object ParserTest]:1:1: error: cubic-bezier control point "
-                  "x values must be in the range [0, 1]."));
+  EXPECT_CALL(
+      parser_observer_,
+      OnError("[object ParserTest]:1:1: error: cubic-bezier control point "
+              "x values must be in the range [0, 1]."));
   scoped_refptr<cssom::PropertyValue> error_value = parser_.ParsePropertyValue(
       "transition-timing-function", "cubic-bezier(0.5, 0, -1, 0)",
       source_location_);
   // Test that the ease function was returned in place of the error function.
   EXPECT_TRUE(error_value->Equals(*CreateSingleTimingFunctionValue(
-                                      cssom::TimingFunction::GetEase().get())));
+      cssom::TimingFunction::GetEase().get())));
 }
 
 TEST_F(ParserTest, ParsesTransitionShorthandOfMultipleItemsWithNoDefaults) {
@@ -9156,9 +9213,9 @@
 }
 
 TEST_F(ParserTest, ParsesTransitionShorthandWithErrorBeforeSemicolon) {
-  EXPECT_CALL(parser_observer_, OnError(
-                                    "[object ParserTest]:1:16: error: "
-                                    "unsupported property value for animation"))
+  EXPECT_CALL(parser_observer_,
+              OnError("[object ParserTest]:1:16: error: "
+                      "unsupported property value for animation"))
       .Times(AtLeast(1));
 
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
@@ -9187,9 +9244,9 @@
 }
 
 TEST_F(ParserTest, ParsesTransitionShorthandWithErrorBeforeSpace) {
-  EXPECT_CALL(parser_observer_, OnError(
-                                    "[object ParserTest]:1:16: error: "
-                                    "unsupported property value for animation"))
+  EXPECT_CALL(parser_observer_,
+              OnError("[object ParserTest]:1:16: error: "
+                      "unsupported property value for animation"))
       .Times(AtLeast(1));
 
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
@@ -9215,9 +9272,9 @@
 
 TEST_F(ParserTest,
        ParsesTransitionShorthandIgnoringErrorButProceedingWithNonError) {
-  EXPECT_CALL(parser_observer_, OnError(
-                                    "[object ParserTest]:1:16: error: "
-                                    "unsupported property value for animation"))
+  EXPECT_CALL(parser_observer_,
+              OnError("[object ParserTest]:1:16: error: "
+                      "unsupported property value for animation"))
       .Times(AtLeast(1));
 
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
@@ -9737,8 +9794,9 @@
 
 TEST_F(ParserTest, ParsesValidMediaQuery) {
   scoped_refptr<cssom::MediaQuery> media_query =
-      parser_.ParseMediaQuery("(max-width: 1024px) and (max-height: 512px)",
-                              source_location_)
+      parser_
+          .ParseMediaQuery("(max-width: 1024px) and (max-height: 512px)",
+                           source_location_)
           .get();
   ASSERT_TRUE(media_query.get());
   // TODO: Update when media query serialization is implemented.
@@ -9754,8 +9812,9 @@
 
 TEST_F(ParserTest, ParsesValidMediaList) {
   scoped_refptr<cssom::MediaList> media_list =
-      parser_.ParseMediaList("(max-width: 1024px), (max-height: 512px)",
-                             source_location_)
+      parser_
+          .ParseMediaList("(max-width: 1024px), (max-height: 512px)",
+                          source_location_)
           .get();
   ASSERT_TRUE(media_list.get());
   ASSERT_EQ(media_list->length(), 2);
@@ -9766,9 +9825,10 @@
 
 TEST_F(ParserTest, ParsesValidMediaQueryWithIntegers) {
   scoped_refptr<cssom::MediaQuery> media_query =
-      parser_.ParseMediaQuery(
-                 "(color: 8) and (grid:0) and (color) and (scan: progressive)",
-                 source_location_)
+      parser_
+          .ParseMediaQuery(
+              "(color: 8) and (grid:0) and (color) and (scan: progressive)",
+              source_location_)
           .get();
   ASSERT_TRUE(media_query.get());
 
diff --git a/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.cc b/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.cc
index c250141..b29ec28 100644
--- a/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.cc
+++ b/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/cssom/cobalt_ui_nav_focus_transform_function.h"
 
+#include "base/strings/stringprintf.h"
 #include "cobalt/cssom/transform_function_visitor.h"
 #include "cobalt/math/matrix_interpolation.h"
 
@@ -21,8 +22,11 @@
 namespace cssom {
 
 CobaltUiNavFocusTransformFunction::CobaltUiNavFocusTransformFunction(
+    float x_translation_scale, float y_translation_scale,
     float progress_to_identity)
-    : progress_to_identity_(progress_to_identity) {
+    : x_translation_scale_(x_translation_scale),
+      y_translation_scale_(y_translation_scale),
+      progress_to_identity_(progress_to_identity) {
   traits_ = kTraitIsDynamic | kTraitUsesUiNavFocus;
 }
 
@@ -32,22 +36,22 @@
 }
 
 std::string CobaltUiNavFocusTransformFunction::ToString() const {
-  return "-cobalt-ui-nav-focus-transform()";
+  return base::StringPrintf("-cobalt-ui-nav-focus-transform(%.7g, %.7g)",
+                            x_translation_scale_, y_translation_scale_);
 }
 
 math::Matrix3F CobaltUiNavFocusTransformFunction::ToMatrix(
     const math::SizeF& used_size,
     const scoped_refptr<ui_navigation::NavItem>& used_ui_nav_focus) const {
   ui_navigation::NativeMatrix4 matrix;
-  if (used_ui_nav_focus &&
-      used_ui_nav_focus->GetFocusTransform(&matrix)) {
+  if (used_ui_nav_focus && used_ui_nav_focus->GetFocusTransform(&matrix)) {
     return math::InterpolateMatrices(
         math::Matrix3F::FromValues(
-            matrix.m[ 0], matrix.m[ 1], matrix.m[ 3],
-            matrix.m[ 4], matrix.m[ 5], matrix.m[ 7],
-            matrix.m[12], matrix.m[13], matrix.m[15]),
-        math::Matrix3F::Identity(),
-        progress_to_identity_);
+            // Since the UI is only rendered in 2D, ignore any scaling or
+            // shearing that might be part of a 3D transform.
+            1.0f, 0.0f, matrix.m[3] * x_translation_scale_, 0.0f, 1.0f,
+            matrix.m[7] * y_translation_scale_, 0.0f, 0.0f, 1.0f),
+        math::Matrix3F::Identity(), progress_to_identity_);
   }
   return math::Matrix3F::Identity();
 }
diff --git a/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.h b/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.h
index 210786b..ed31665 100644
--- a/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.h
+++ b/src/cobalt/cssom/cobalt_ui_nav_focus_transform_function.h
@@ -39,26 +39,33 @@
   // resulting in this transform returning the identity matrix. DOM elements
   // will only ever use progress_to_identity == 0, but intermediate animation
   // frames may use other values.
-  explicit CobaltUiNavFocusTransformFunction(
-      float progress_to_identity = 0.0f);
+  CobaltUiNavFocusTransformFunction(float x_translation_scale,
+                                    float y_translation_scale,
+                                    float progress_to_identity = 0.0f);
 
   void Accept(TransformFunctionVisitor* visitor) const override;
 
+  float x_translation_scale() const { return x_translation_scale_; }
+  float y_translation_scale() const { return y_translation_scale_; }
   float progress_to_identity() const { return progress_to_identity_; }
 
   std::string ToString() const override;
 
   math::Matrix3F ToMatrix(const math::SizeF& used_size,
-      const scoped_refptr<ui_navigation::NavItem>& used_ui_nav_focus)
-      const override;
+                          const scoped_refptr<ui_navigation::NavItem>&
+                              used_ui_nav_focus) const override;
 
   bool operator==(const CobaltUiNavFocusTransformFunction& other) const {
-    return progress_to_identity_ == other.progress_to_identity_;
+    return x_translation_scale_ == other.x_translation_scale_ &&
+           y_translation_scale_ == other.y_translation_scale_ &&
+           progress_to_identity_ == other.progress_to_identity_;
   }
 
   DEFINE_POLYMORPHIC_EQUATABLE_TYPE(CobaltUiNavFocusTransformFunction);
 
  private:
+  const float x_translation_scale_;
+  const float y_translation_scale_;
   const float progress_to_identity_;
 };
 
diff --git a/src/cobalt/cssom/interpolate_property_value.cc b/src/cobalt/cssom/interpolate_property_value.cc
index b781e1a..79f0d1f 100644
--- a/src/cobalt/cssom/interpolate_property_value.cc
+++ b/src/cobalt/cssom/interpolate_property_value.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <limits>
 #include <memory>
+#include <utility>
 
 #include "base/memory/ptr_util.h"
 #include "cobalt/base/enable_if.h"
@@ -269,11 +270,23 @@
   // identity matrix when transitioning to transform none. Instead, the
   // focus transform needs to know how close to the identity transform it
   // needs to interpolate its value when evaluated.
-  float progress_to_identity_end =
-      focus_end ? focus_end->progress_to_identity() : 1.0f;
+  float progress_to_identity_end = 1.0f;
+  // Maintain the translation scale values if interpolating to identity since
+  // the interpolation to identity is also phasing out the translation scales.
+  float x_translation_scale_end = focus_function->x_translation_scale();
+  float y_translation_scale_end = focus_function->y_translation_scale();
+  if (focus_end) {
+    progress_to_identity_end = focus_end->progress_to_identity();
+    x_translation_scale_end = focus_end->x_translation_scale();
+    y_translation_scale_end = focus_end->y_translation_scale();
+  }
   animated_.reset(new CobaltUiNavFocusTransformFunction(
-      Lerp(focus_function->progress_to_identity(),
-           progress_to_identity_end, progress_)));
+      Lerp(focus_function->x_translation_scale(), x_translation_scale_end,
+           progress_),
+      Lerp(focus_function->y_translation_scale(), y_translation_scale_end,
+           progress_),
+      Lerp(focus_function->progress_to_identity(), progress_to_identity_end,
+           progress_)));
 }
 
 void AnimateTransformFunction::VisitCobaltUiNavSpotlightTransform(
@@ -289,8 +302,8 @@
   float progress_to_identity_end =
       spotlight_end ? spotlight_end->progress_to_identity() : 1.0f;
   animated_.reset(new CobaltUiNavSpotlightTransformFunction(
-      Lerp(spotlight_function->progress_to_identity(),
-           progress_to_identity_end, progress_)));
+      Lerp(spotlight_function->progress_to_identity(), progress_to_identity_end,
+           progress_)));
 }
 
 // Returns true if two given transform function lists have the same number of
@@ -339,13 +352,11 @@
   }
 
   TransformFunctionListValue* start_transform =
-      base::polymorphic_downcast<TransformFunctionListValue*>(
-          start_value);
+      base::polymorphic_downcast<TransformFunctionListValue*>(start_value);
   TransformFunctionListValue* end_transform =
       end_value->Equals(*KeywordValue::GetNone())
           ? NULL
-          : base::polymorphic_downcast<TransformFunctionListValue*>(
-                end_value);
+          : base::polymorphic_downcast<TransformFunctionListValue*>(end_value);
 
   const TransformFunctionListValue::Builder* start_functions =
       &start_transform->value();
@@ -377,8 +388,8 @@
     // into a matrix and animate the matrix using the algorithm described here:
     //   https://www.w3.org/TR/2012/WD-css3-transforms-20120228/#matrix-decomposition
     DCHECK(end_transform);
-    return new InterpolatedTransformPropertyValue(
-        start_transform, end_transform, progress);
+    return new InterpolatedTransformPropertyValue(start_transform,
+                                                  end_transform, progress);
   }
 }
 }  // namespace
@@ -545,7 +556,7 @@
       // Try to interpolate each transform function in the transform function
       // list. This can only be done if the function types match.
       interpolated_value_ = AnimateTransform(start_transform_property_value,
-          end_value_, progress_);
+                                             end_value_, progress_);
     }
   } else if (end_value_->Equals(*KeywordValue::GetNone())) {
     // Interpolate to identity matrix.
@@ -553,8 +564,7 @@
     builder.emplace_back(new MatrixFunction(math::Matrix3F::Identity()));
     interpolated_value_ = new InterpolatedTransformPropertyValue(
         start_transform_property_value,
-        new TransformFunctionListValue(std::move(builder)),
-        progress_);
+        new TransformFunctionListValue(std::move(builder)), progress_);
   } else {
     interpolated_value_ = new InterpolatedTransformPropertyValue(
         start_transform_property_value,
diff --git a/src/cobalt/cssom/interpolate_property_value_test.cc b/src/cobalt/cssom/interpolate_property_value_test.cc
index 4c49ff1..1550392 100644
--- a/src/cobalt/cssom/interpolate_property_value_test.cc
+++ b/src/cobalt/cssom/interpolate_property_value_test.cc
@@ -133,7 +133,7 @@
     }
     static scoped_refptr<PropertyValue> End() {
       TransformFunctionListValue::Builder functions;
-      functions.emplace_back(new CobaltUiNavFocusTransformFunction);
+      functions.emplace_back(new CobaltUiNavFocusTransformFunction(1.0f, 1.0f));
       return new TransformFunctionListValue(std::move(functions));
     }
   };
@@ -149,6 +149,8 @@
       dynamic_cast<const CobaltUiNavFocusTransformFunction*>(
           interpolated->value()[0].get());
   ASSERT_TRUE(focus_function);
+  EXPECT_NEAR(focus_function->x_translation_scale(), 1.0f, kErrorEpsilon);
+  EXPECT_NEAR(focus_function->y_translation_scale(), 1.0f, kErrorEpsilon);
   EXPECT_NEAR(focus_function->progress_to_identity(), 0.25f, kErrorEpsilon);
 
   math::Matrix3F value = focus_function->ToMatrix(math::SizeF(), nullptr);
@@ -163,12 +165,14 @@
   struct MakeSingleFocusTransform {
     static scoped_refptr<PropertyValue> Start() {
       TransformFunctionListValue::Builder functions;
-      functions.emplace_back(new CobaltUiNavFocusTransformFunction(0.2f));
+      functions.emplace_back(
+          new CobaltUiNavFocusTransformFunction(1.0f, 2.0f, 0.2f));
       return new TransformFunctionListValue(std::move(functions));
     }
     static scoped_refptr<PropertyValue> End() {
       TransformFunctionListValue::Builder functions;
-      functions.emplace_back(new CobaltUiNavFocusTransformFunction(0.6f));
+      functions.emplace_back(
+          new CobaltUiNavFocusTransformFunction(2.0f, 4.0f, 0.6f));
       return new TransformFunctionListValue(std::move(functions));
     }
   };
@@ -184,6 +188,8 @@
       dynamic_cast<const CobaltUiNavFocusTransformFunction*>(
           interpolated->value()[0].get());
   ASSERT_TRUE(focus_function);
+  EXPECT_NEAR(focus_function->x_translation_scale(), 1.5f, kErrorEpsilon);
+  EXPECT_NEAR(focus_function->y_translation_scale(), 3.0f, kErrorEpsilon);
   EXPECT_NEAR(focus_function->progress_to_identity(), 0.4f, kErrorEpsilon);
 
   math::Matrix3F value = focus_function->ToMatrix(math::SizeF(), nullptr);
@@ -720,8 +726,7 @@
           0.75f, MakeMultipleMismatchedTransform::Start(),
           MakeMultipleMismatchedTransform::End());
   EXPECT_TRUE(
-      interpolated
-          ->ToMatrix(math::SizeF(), nullptr)
+      interpolated->ToMatrix(math::SizeF(), nullptr)
           .IsNear(math::TranslateMatrix(3.5f, 0.0f) *
                       math::RotateMatrix(-static_cast<float>(M_PI * 3 / 8)) *
                       math::ScaleMatrix(3.5f, 1.75f),
@@ -759,8 +764,7 @@
       InterpolatePropertyTyped<InterpolatedTransformPropertyValue>(
           0.5f, interpolated, MakeMultipleMismatchedTransform::Start());
 
-  EXPECT_TRUE(next_interpolated
-                  ->ToMatrix(math::SizeF(), nullptr)
+  EXPECT_TRUE(next_interpolated->ToMatrix(math::SizeF(), nullptr)
                   .IsNear(math::RotateMatrix(-static_cast<float>(M_PI / 8)),
                           kErrorEpsilon));
 }
@@ -832,8 +836,8 @@
           0.5f, MakeMultipleMismatchedTransform::Start(),
           MakeMultipleMismatchedTransform::End());
 
-  math::Matrix3F value = interpolated->ToMatrix(
-      math::SizeF(100.0f, 200.0f), nullptr);
+  math::Matrix3F value =
+      interpolated->ToMatrix(math::SizeF(100.0f, 200.0f), nullptr);
 
   EXPECT_NEAR(cos(M_PI / 4), value(0, 0), kErrorEpsilon);
   EXPECT_NEAR(sin(M_PI / 4), value(1, 0), kErrorEpsilon);
diff --git a/src/cobalt/cssom/property_value_is_equal_test.cc b/src/cobalt/cssom/property_value_is_equal_test.cc
index ec073bb..5d64235 100644
--- a/src/cobalt/cssom/property_value_is_equal_test.cc
+++ b/src/cobalt/cssom/property_value_is_equal_test.cc
@@ -81,17 +81,24 @@
 }
 
 TEST(PropertyValueIsEqualTest, CobaltUiNavFocusTransformFunctionsAreEqual) {
-  CobaltUiNavFocusTransformFunction function_a;
-  CobaltUiNavFocusTransformFunction function_b;
+  CobaltUiNavFocusTransformFunction function_a(1.0f, 1.0f);
+  CobaltUiNavFocusTransformFunction function_b(1.0f, 1.0f);
 
   EXPECT_TRUE(function_a.Equals(function_b));
 }
 
 TEST(PropertyValueIsEqualTest, CobaltUiNavFocusTransformFunctionsAreNotEqual) {
-  CobaltUiNavFocusTransformFunction function_a(0.0f);
-  CobaltUiNavFocusTransformFunction function_b(1.0f);
+  CobaltUiNavFocusTransformFunction function_a(1.0f, 1.0f, 1.0f);
+  CobaltUiNavFocusTransformFunction function_b(1.0f, 1.0f, 0.0f);
+  CobaltUiNavFocusTransformFunction function_c(1.0f, 2.0f, 1.0f);
+  CobaltUiNavFocusTransformFunction function_d(2.0f, 1.0f, 1.0f);
 
   EXPECT_FALSE(function_a.Equals(function_b));
+  EXPECT_FALSE(function_a.Equals(function_c));
+  EXPECT_FALSE(function_a.Equals(function_d));
+  EXPECT_FALSE(function_b.Equals(function_c));
+  EXPECT_FALSE(function_b.Equals(function_d));
+  EXPECT_FALSE(function_c.Equals(function_d));
 }
 
 TEST(PropertyValueIsEqualTest, CobaltUiNavSpotlightTransformFunctionsAreEqual) {
@@ -166,17 +173,14 @@
 }
 
 TEST(PropertyValueIsEqualTest, LengthsAreEqual) {
-  scoped_refptr<LengthValue> value_a(
-      new LengthValue(1.5f, kPixelsUnit));
-  scoped_refptr<LengthValue> value_b(
-      new LengthValue(1.5f, kPixelsUnit));
+  scoped_refptr<LengthValue> value_a(new LengthValue(1.5f, kPixelsUnit));
+  scoped_refptr<LengthValue> value_b(new LengthValue(1.5f, kPixelsUnit));
 
   EXPECT_TRUE(value_a->Equals(*value_b));
 }
 
 TEST(PropertyValueIsEqualTest, LengthsAreNotEqual) {
-  scoped_refptr<LengthValue> value_a(
-      new LengthValue(1.5f, kPixelsUnit));
+  scoped_refptr<LengthValue> value_a(new LengthValue(1.5f, kPixelsUnit));
   scoped_refptr<LengthValue> value_b(
       new LengthValue(1.5f, kFontSizesAkaEmUnit));
 
diff --git a/src/cobalt/cssom/property_value_to_string_test.cc b/src/cobalt/cssom/property_value_to_string_test.cc
index 7dc1476..609e2b2 100644
--- a/src/cobalt/cssom/property_value_to_string_test.cc
+++ b/src/cobalt/cssom/property_value_to_string_test.cc
@@ -343,8 +343,8 @@
 }
 
 TEST(PropertyValueToStringTest, CobaltUiNavFocusTransformFunction) {
-  CobaltUiNavFocusTransformFunction function;
-  EXPECT_EQ(function.ToString(), "-cobalt-ui-nav-focus-transform()");
+  CobaltUiNavFocusTransformFunction function(0.5f, 1.0f);
+  EXPECT_EQ(function.ToString(), "-cobalt-ui-nav-focus-transform(0.5, 1)");
 }
 
 TEST(PropertyValueToStringTest, CobaltUiNavSpotlightTransformFunction) {
@@ -362,8 +362,9 @@
   scoped_refptr<TransformFunctionListValue> property(
       new TransformFunctionListValue(std::move(transform_list)));
 
-  EXPECT_EQ(property->ToString(), "translateX(1px) scale(2, 2) rotate(1rad) "
-      "-cobalt-ui-nav-spotlight-transform()");
+  EXPECT_EQ(property->ToString(),
+            "translateX(1px) scale(2, 2) rotate(1rad) "
+            "-cobalt-ui-nav-spotlight-transform()");
 }
 
 TEST(PropertyValueToStringTest, URLValue) {
diff --git a/src/cobalt/cssom/transform_function_visitor_test.cc b/src/cobalt/cssom/transform_function_visitor_test.cc
index 7ca695f..2fd05a5 100644
--- a/src/cobalt/cssom/transform_function_visitor_test.cc
+++ b/src/cobalt/cssom/transform_function_visitor_test.cc
@@ -63,26 +63,25 @@
 }
 
 TEST(TransformFunctionVisitorTest, VisitsTranslateXFunction) {
-  TranslateFunction translate_function(
-      TranslateFunction::kXAxis, new LengthValue(0, kPixelsUnit));
+  TranslateFunction translate_function(TranslateFunction::kXAxis,
+                                       new LengthValue(0, kPixelsUnit));
   MockTransformFunctionVisitor mock_visitor;
   EXPECT_CALL(mock_visitor, VisitTranslate(&translate_function));
   translate_function.Accept(&mock_visitor);
 }
 
 TEST(TransformFunctionVisitorTest, VisitsCobaltUiNavFocusTransform) {
-  CobaltUiNavFocusTransformFunction focus_function;
+  CobaltUiNavFocusTransformFunction focus_function(1.0f, 1.0f);
   MockTransformFunctionVisitor mock_visitor;
-  EXPECT_CALL(mock_visitor, VisitCobaltUiNavFocusTransform(
-      &focus_function));
+  EXPECT_CALL(mock_visitor, VisitCobaltUiNavFocusTransform(&focus_function));
   focus_function.Accept(&mock_visitor);
 }
 
 TEST(TransformFunctionVisitorTest, VisitsCobaltUiNavSpotlightTransform) {
   CobaltUiNavSpotlightTransformFunction spotlight_function;
   MockTransformFunctionVisitor mock_visitor;
-  EXPECT_CALL(mock_visitor, VisitCobaltUiNavSpotlightTransform(
-      &spotlight_function));
+  EXPECT_CALL(mock_visitor,
+              VisitCobaltUiNavSpotlightTransform(&spotlight_function));
   spotlight_function.Accept(&mock_visitor);
 }
 
diff --git a/src/cobalt/demos/content/media-element-demo/README.md b/src/cobalt/demos/content/media-element-demo/README.md
new file mode 100644
index 0000000..ee731e2
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/README.md
@@ -0,0 +1,27 @@
+# Cobalt Media Demo
+
+## Installation
+
+```bash
+# Install node modules
+npm install
+```
+Only needed for the first time.
+
+## Development
+
+```bash
+# Start an http server and build the package
+npm start
+```
+
+It watches script changes and recompiles the script bundle.
+
+
+## Deploy
+```bash
+# Compiles the script bundle.
+npm run build
+```
+
+Copy all files under `dist/` folder to the server.
diff --git a/src/cobalt/demos/content/media-element-demo/README.txt b/src/cobalt/demos/content/media-element-demo/README.txt
deleted file mode 100644
index 6b09933..0000000
--- a/src/cobalt/demos/content/media-element-demo/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-The content of this folder no longer works as local files as the media files
-have to be served in a web server in order to make it work with XMLHttpRequest.
diff --git a/src/cobalt/demos/content/media-element-demo/key-systems.html b/src/cobalt/demos/content/media-element-demo/legacy/key-systems.html
similarity index 100%
rename from src/cobalt/demos/content/media-element-demo/key-systems.html
rename to src/cobalt/demos/content/media-element-demo/legacy/key-systems.html
diff --git a/src/cobalt/demos/content/media-element-demo/key-systems.js b/src/cobalt/demos/content/media-element-demo/legacy/key-systems.js
similarity index 100%
rename from src/cobalt/demos/content/media-element-demo/key-systems.js
rename to src/cobalt/demos/content/media-element-demo/legacy/key-systems.js
diff --git a/src/cobalt/demos/content/media-element-demo/loop-playback.html b/src/cobalt/demos/content/media-element-demo/loop-playback.html
deleted file mode 100644
index 91b04c0..0000000
--- a/src/cobalt/demos/content/media-element-demo/loop-playback.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Loop Playback</title>
-  <style>
-    body {
-      background-color: rgb(255, 255, 255);
-      color: #0047ab;
-      font-size: 100px;
-    }
-    video {
-      transform: translateX(100px) rotate(3deg);
-    }
-  </style>
-</head>
-<body>
-  Loop Playback
-  <script type="text/javascript" src="loop-playback.js"></script>
-</body>
-</html>
diff --git a/src/cobalt/demos/content/media-element-demo/loop-playback.js b/src/cobalt/demos/content/media-element-demo/loop-playback.js
deleted file mode 100644
index b100d12..0000000
--- a/src/cobalt/demos/content/media-element-demo/loop-playback.js
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The page simply plays an audio or a video stream in a loop, it can be used
-// in the following forms:
-//   loop-playback.html?url=video.mp4&type=progressive
-//   loop-playback.html?url=video.webm&type=video
-//   loop-playback.html?url=audio.mp4&type=audio
-// If the stream is adaptive, it has to be fit in memory as this demo will
-// download the whole stream at once.
-
-var video = null;
-var url = null;
-var type = null;
-
-function downloadAndAppend(source_buffer, callback) {
-  var xhr = new XMLHttpRequest;
-  xhr.open('GET', url, true);
-  xhr.responseType = 'arraybuffer';
-  xhr.addEventListener('load', function(e) {
-    var onupdateend = function() {
-      source_buffer.removeEventListener('updateend', onupdateend);
-      callback();
-    };
-    source_buffer.addEventListener('updateend', onupdateend);
-    source_buffer.appendBuffer(new Uint8Array(e.target.response));
-  });
-  xhr.send();
-}
-
-function createVideoElement() {
-  var video = document.createElement('video');
-  video.autoplay = true;
-  video.style.width = '1280px';
-  video.style.height = '720px';
-  document.body.appendChild(video);
-
-  return video;
-}
-
-function onVideoEnded() {
-  console.log('playback ended');
-  startNextVideo();
-}
-
-function startProgressiveVideo() {
-  video.src = '';
-  video.load();
-  video.src = url;
-  video.addEventListener('ended', onVideoEnded.bind());
-}
-
-function startAdaptiveVideo() {
-  video.src = '';
-  video.load();
-  var mediasource = new MediaSource;
-  mediasource.addEventListener('sourceopen', function () {
-    var source_buffer;
-    if (type == "audio") {
-      if (url.indexOf(".mp4") != -1) {
-        source_buffer = mediasource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
-      } else if (url.indexOf(".webm") != -1) {
-        source_buffer = mediasource.addSourceBuffer('audio/webm; codecs="opus"');
-      } else {
-        throw "unknown audio format " + url;
-      }
-    } else {
-      if (url.indexOf(".mp4") != -1) {
-        source_buffer = mediasource.addSourceBuffer('video/mp4; codecs="avc1.640028"');
-      } else if (url.indexOf(".webm") != -1) {
-        source_buffer = mediasource.addSourceBuffer('video/webm; codecs="vp9"');
-      } else {
-        throw "unknown video format " + url;
-      }
-    }
-    downloadAndAppend(source_buffer, function () {
-      mediasource.endOfStream();
-    });
-  })
-
-  video.src = window.URL.createObjectURL(mediasource);
-  video.addEventListener('ended', onVideoEnded);
-}
-
-function startNextVideo() {
-  if (type == "progressive") {
-    startProgressiveVideo();
-  } else {
-    startAdaptiveVideo();
-  }
-}
-
-function main() {
-  var get_parameters = window.location.search.substr(1).split('&');
-  for (var param of get_parameters) {
-    splitted = param.split('=');
-    if (splitted[0] == 'url') {
-      url = splitted[1];
-    } else if (splitted[0] == 'type') {
-      type = splitted[1];
-    }
-  }
-
-  if (!url) {
-    throw "url is not set.";
-  }
-
-  if (type != 'progressive' && type != 'audio' && type != 'video') {
-    throw "invalid type " + type;
-  }
-
-  video = createVideoElement();
-  startNextVideo();
-}
-
-main();
diff --git a/src/cobalt/demos/content/media-element-demo/media-element-demo.html b/src/cobalt/demos/content/media-element-demo/media-element-demo.html
deleted file mode 100644
index 665d953..0000000
--- a/src/cobalt/demos/content/media-element-demo/media-element-demo.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Media Element Demo</title>
-  <style>
-    body {
-      background-color: rgb(255, 255, 255);
-      color: #0047ab;
-      font-size: 100px;
-    }
-    video {
-      transform: translateX(100px) rotate(3deg);
-    }
-  </style>
-</head>
-<body>
-  Media Element Demo
-  <script type="text/javascript" src="media-element-demo.js"></script>
-</body>
-</html>
diff --git a/src/cobalt/demos/content/media-element-demo/media-element-demo.js b/src/cobalt/demos/content/media-element-demo/media-element-demo.js
deleted file mode 100644
index 73cea11..0000000
--- a/src/cobalt/demos/content/media-element-demo/media-element-demo.js
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The demo does the following steps in a loop:
-//  1. Playback a progressive video.
-//  2. Playback a section of a 240p video in adaptive (DASH).
-//  3. Playback the above 240p video in 1080p.
-// All above videos have audio accompanied.
-
-// Description of the streams being played.
-var kProgressiveVideoUrl = 'progressive.mp4';
-
-var kAdaptiveVideoUrl_240p = 'dash-video-240p.mp4';
-var kAdaptiveVideoSize_240p = 2051611;
-// The size of the video that can be played for 10s.
-var kAdaptiveVideo10sChunkSize_240p = 306689;
-
-var kAdaptiveVideoUrl_1080p = 'dash-video-1080p.mp4';
-var kAdaptiveVideoSize_1080p = 22461669;
-
-var kAdaptiveAudioUrl = 'dash-audio.mp4';
-var kAdaptiveAudioSize = 1048531;
-var kAdaptiveAudioChunkSize = 720 * 1024;
-
-var is_progressive = false;
-var video = null;
-
-// Send a range request of [`begin`, `end`] (inclusive) to download content from
-// the `url` and append the downloaded content into the `source_buffer` before
-// calling the `callback` function.
-function downloadAndAppend(url, begin, end, source_buffer, callback) {
-  var xhr = new XMLHttpRequest;
-  xhr.open('GET', url, true);
-  xhr.responseType = 'arraybuffer';
-  xhr.addEventListener('load', function(e) {
-    var onupdateend = function() {
-      source_buffer.removeEventListener('updateend', onupdateend);
-      callback();
-    };
-    source_buffer.addEventListener('updateend', onupdateend);
-    source_buffer.appendBuffer(new Uint8Array(e.target.response));
-  });
-  xhr.setRequestHeader('Range', ('bytes=' + begin +'-' + end));
-  xhr.send();
-}
-
-function createVideoElement() {
-  var video = document.createElement('video');
-  video.autoplay = true;
-  video.style.width = '1280px';
-  video.style.height = '720px';
-  document.body.appendChild(video);
-
-  return video;
-}
-
-function createStatusElement(video) {
-  var status = document.createElement('div');
-  document.body.appendChild(status);
-  video.addEventListener('timeupdate', function () {
-    status.textContent = is_progressive ? 'Progressive' : 'Adaptive';
-    status.textContent += ' / time: ' + video.currentTime.toFixed(2);
-  });
-
-  return status;
-}
-
-function onVideoEnded() {
-  console.log('ended');
-  is_progressive = !is_progressive;
-  startNextVideo();
-}
-
-function startProgressiveVideo(video) {
-  video.src = '';
-  video.load();
-  video.src = kProgressiveVideoUrl;
-  video.addEventListener('ended', onVideoEnded);
-}
-
-function startAdaptiveVideo(video) {
-  video.src = '';
-  video.load();
-  var mediasource = new MediaSource;
-  mediasource.addEventListener('sourceopen', function () {
-    var video_source_buffer = mediasource.addSourceBuffer('video/mp4; codecs="avc1.640028"');
-    var audio_source_buffer = mediasource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
-    downloadAndAppend('dash-video-1080p.mp4', 0, 15 * 1024 * 1024, video_source_buffer, function () {
-      video_source_buffer.abort();
-      // Append the first two segments of the 240p video so we can see the transition.
-      downloadAndAppend('dash-video-240p.mp4', 0, kAdaptiveVideo10sChunkSize_240p, video_source_buffer, function () {
-        video_source_buffer.abort();
-        downloadAndAppend('dash-audio.mp4', 0, kAdaptiveAudioChunkSize, audio_source_buffer, function () {
-          mediasource.endOfStream();
-        });
-      });
-    });
-  })
-
-  video.src = window.URL.createObjectURL(mediasource);
-  video.addEventListener('ended', onVideoEnded);
-}
-
-function startNextVideo() {
-  if (is_progressive) {
-    startProgressiveVideo(video);
-  } else {
-    startAdaptiveVideo(video);
-  }
-}
-
-video = createVideoElement();
-createStatusElement(video);
-startNextVideo(video);
diff --git a/src/cobalt/demos/content/media-element-demo/multi-video-demo.html b/src/cobalt/demos/content/media-element-demo/multi-video-demo.html
deleted file mode 100644
index e79f36b..0000000
--- a/src/cobalt/demos/content/media-element-demo/multi-video-demo.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Multi Video Demo</title>
-  <style>
-    body {
-      background-color: rgb(255, 255, 255);
-      color: #0047ab;
-      font-size: 10px;
-    }
-    video {
-      transform: translateX(100px) rotate(3deg);
-    }
-  </style>
-</head>
-<body>
-  <script type="text/javascript" src="multi-video-demo.js"></script>
-</body>
-</html>
diff --git a/src/cobalt/demos/content/media-element-demo/multi-video-demo.js b/src/cobalt/demos/content/media-element-demo/multi-video-demo.js
deleted file mode 100644
index cab4d58..0000000
--- a/src/cobalt/demos/content/media-element-demo/multi-video-demo.js
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The demo simply plays a video using get parameters in the form of:
-//   .../multi-video-demo.html?audio=filename_in_same_folder&video=filename_in_same_folder&instances=2
-
-var kAudioChunkSize = 512 * 1024;
-var kVideoChunkSize = 2 * 1024 * 1024;
-
-var kEndOfStreamOffset = -1;
-
-class Player {
-  constructor(audio_url, video_url) {
-    this.audio_offset = 0;
-    this.audio_url = audio_url;
-    this.video_offset = 0;
-    this.video_url = video_url;
-    this.video_tag = this.createVideoElement();
-    this.status = this.createStatusElement(this.video_tag);
-
-    this.video_tag.src = '';
-    this.video_tag.load();
-    this.mediasource = new MediaSource;
-    this.mediasource.addEventListener('sourceopen', this.onsourceopen.bind(this));
-    this.video_tag.src = window.URL.createObjectURL(this.mediasource);
-  }
-
-  onsourceopen() {
-    if (this.video_url.endsWith('.mp4')) {
-        this.video_source_buffer = this.mediasource.addSourceBuffer(
-                                       'video/mp4; codecs="avc1.640028"');
-      } else {
-        this.video_source_buffer = this.mediasource.addSourceBuffer(
-                                       'video/webm; codecs="vp9"');
-      }
-
-      this.audio_source_buffer = this.mediasource.addSourceBuffer(
-                                     'audio/mp4; codecs="mp4a.40.2"');
-      this.tryToDownloadAudioData();
-  }
-
-  downloadAndAppend(url, begin, end, source_buffer, callback) {
-    var xhr = new XMLHttpRequest;
-    xhr.open('GET', url, true);
-    xhr.responseType = 'arraybuffer';
-    xhr.addEventListener('load', function(e) {
-      var data = new Uint8Array(e.target.response);
-      var onupdateend = function() {
-        source_buffer.removeEventListener('updateend', onupdateend);
-        callback(data.length);
-      };
-      source_buffer.addEventListener('updateend', onupdateend);
-      source_buffer.appendBuffer(data);
-      console.log('append ' + data.length + ' bytes from '
-                  + url);
-    });
-    xhr.setRequestHeader('Range', ('bytes=' + begin +'-' + end));
-    xhr.send();
-  }
-
-  isTrackNeedAudioData() {
-    if (this.audio_offset == kEndOfStreamOffset) {
-      return false;
-    }
-    var buffer_range_size = this.audio_source_buffer.buffered.length;
-    if (buffer_range_size == 0) {
-      return true;
-    }
-    var start = this.audio_source_buffer.buffered.start(buffer_range_size - 1);
-    var end = this.audio_source_buffer.buffered.end(buffer_range_size - 1);
-    console.log('audio ' + start + '/' + end + ' ' + this.video_tag.currentTime)
-    return end - this.video_tag.currentTime <= 20;
-  }
-
-  isTrackNeedVideoData() {
-    if (this.video_offset == kEndOfStreamOffset) {
-      return false;
-    }
-    var buffer_range_size = this.video_source_buffer.buffered.length;
-    if (buffer_range_size == 0) {
-      return true;
-    }
-    var start = this.video_source_buffer.buffered.start(buffer_range_size - 1);
-    var end = this.video_source_buffer.buffered.end(buffer_range_size - 1);
-    console.log('video ' + start + '/' + end + ' ' + this.video_tag.currentTime)
-    return end - this.video_tag.currentTime <= 20;
-  }
-
-  tryToDownloadAudioData() {
-    if (!this.isTrackNeedAudioData()) {
-      if (this.isTrackNeedVideoData()) {
-        this.tryToDownloadVideoData();
-      } else {
-        window.setTimeout(this.tryToDownloadAudioData.bind(this), 1000);
-      }
-      return;
-    }
-
-    this.downloadAndAppend(
-        this.audio_url, this.audio_offset,
-        this.audio_offset + kAudioChunkSize - 1, this.audio_source_buffer,
-        this.onAudioDataDownloaded.bind(this));
-  }
-
-  onAudioDataDownloaded(length) {
-    if (length != kAudioChunkSize) {
-      this.audio_offset = kEndOfStreamOffset;
-    }
-    if (this.audio_offset != kEndOfStreamOffset) {
-      this.audio_offset += kAudioChunkSize;
-    }
-    this.tryToDownloadVideoData();
-  }
-
-  tryToDownloadVideoData() {
-    if (!this.isTrackNeedVideoData()) {
-      if (this.isTrackNeedAudioData()) {
-        this.tryToDownloadAudioData();
-      } else {
-        window.setTimeout(this.tryToDownloadVideoData.bind(this), 1000);
-      }
-      return;
-    }
-
-    this.downloadAndAppend(
-        this.video_url, this.video_offset,
-        this.video_offset + kVideoChunkSize - 1, this.video_source_buffer,
-        this.onVideoDataDownloaded.bind(this));
-  }
-
-  onVideoDataDownloaded(length) {
-    if (length != kVideoChunkSize) {
-      this.video_offset = kEndOfStreamOffset;
-    }
-    if (this.video_offset != kEndOfStreamOffset) {
-      this.video_offset += kVideoChunkSize;
-    }
-    this.tryToDownloadAudioData();
-  }
-
-  createVideoElement() {
-    var video = document.createElement('video');
-    video.autoplay = true;
-    video.style.width = '320px';
-    video.style.height = '240px';
-    document.body.appendChild(video);
-
-    return video;
-  }
-
-  createStatusElement(video) {
-    var status = document.createElement('div');
-    document.body.appendChild(status);
-    video.addEventListener('timeupdate', function () {
-      status.textContent = 'time: ' + video.currentTime.toFixed(2);
-    });
-
-    return status;
-  }
-}
-
-function main() {
-  var get_parameters = window.location.search.substr(1).split('&');
-  var audio_url, video_url, instances = 2;
-  for (var param of get_parameters) {
-    splitted = param.split('=');
-    if (splitted[0] == 'audio') {
-      audio_url = splitted[1];
-    } else if (splitted[0] == 'video') {
-      video_url = splitted[1];
-    } else if (splitted[0] == 'instances') {
-      instances = splitted[1];
-    }
-  }
-
-  if (audio_url && video_url) {
-    for (var i = 0; i < instances; ++i) {
-      new Player(audio_url, video_url);
-    }
-  } else {
-    status.textContent = "invalid get parameters " +
-                         window.location.search.substr(1);
-  }
-}
-
-main();
diff --git a/src/cobalt/demos/content/media-element-demo/package-lock.json b/src/cobalt/demos/content/media-element-demo/package-lock.json
new file mode 100644
index 0000000..698b6f8
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/package-lock.json
@@ -0,0 +1,4395 @@
+{
+  "name": "media-element-demo",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@nodelib/fs.scandir": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+      "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "2.0.3",
+        "run-parallel": "^1.1.9"
+      }
+    },
+    "@nodelib/fs.stat": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+      "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+      "dev": true
+    },
+    "@nodelib/fs.walk": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+      "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.scandir": "2.1.3",
+        "fastq": "^1.6.0"
+      }
+    },
+    "@npmcli/move-file": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz",
+      "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==",
+      "dev": true,
+      "requires": {
+        "mkdirp": "^1.0.4"
+      },
+      "dependencies": {
+        "mkdirp": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+          "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+          "dev": true
+        }
+      }
+    },
+    "@types/json-schema": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
+      "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
+      "dev": true
+    },
+    "@webassemblyjs/ast": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+      "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/helper-module-context": "1.9.0",
+        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+        "@webassemblyjs/wast-parser": "1.9.0"
+      }
+    },
+    "@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
+      "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-api-error": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+      "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-buffer": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+      "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-code-frame": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
+      "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/wast-printer": "1.9.0"
+      }
+    },
+    "@webassemblyjs/helper-fsm": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
+      "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-module-context": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
+      "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0"
+      }
+    },
+    "@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+      "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-wasm-section": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+      "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/helper-buffer": "1.9.0",
+        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+        "@webassemblyjs/wasm-gen": "1.9.0"
+      }
+    },
+    "@webassemblyjs/ieee754": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+      "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+      "dev": true,
+      "requires": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "@webassemblyjs/leb128": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+      "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+      "dev": true,
+      "requires": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/utf8": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+      "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+      "dev": true
+    },
+    "@webassemblyjs/wasm-edit": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+      "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/helper-buffer": "1.9.0",
+        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+        "@webassemblyjs/helper-wasm-section": "1.9.0",
+        "@webassemblyjs/wasm-gen": "1.9.0",
+        "@webassemblyjs/wasm-opt": "1.9.0",
+        "@webassemblyjs/wasm-parser": "1.9.0",
+        "@webassemblyjs/wast-printer": "1.9.0"
+      }
+    },
+    "@webassemblyjs/wasm-gen": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+      "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+        "@webassemblyjs/ieee754": "1.9.0",
+        "@webassemblyjs/leb128": "1.9.0",
+        "@webassemblyjs/utf8": "1.9.0"
+      }
+    },
+    "@webassemblyjs/wasm-opt": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+      "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/helper-buffer": "1.9.0",
+        "@webassemblyjs/wasm-gen": "1.9.0",
+        "@webassemblyjs/wasm-parser": "1.9.0"
+      }
+    },
+    "@webassemblyjs/wasm-parser": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+      "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/helper-api-error": "1.9.0",
+        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+        "@webassemblyjs/ieee754": "1.9.0",
+        "@webassemblyjs/leb128": "1.9.0",
+        "@webassemblyjs/utf8": "1.9.0"
+      }
+    },
+    "@webassemblyjs/wast-parser": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
+      "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/floating-point-hex-parser": "1.9.0",
+        "@webassemblyjs/helper-api-error": "1.9.0",
+        "@webassemblyjs/helper-code-frame": "1.9.0",
+        "@webassemblyjs/helper-fsm": "1.9.0",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/wast-printer": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+      "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/wast-parser": "1.9.0",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true
+    },
+    "@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true
+    },
+    "acorn": {
+      "version": "6.4.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+      "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+      "dev": true
+    },
+    "aggregate-error": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+      "dev": true,
+      "requires": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      }
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ajv-errors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+      "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+      "dev": true
+    },
+    "ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+      "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+      "dev": true
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true
+    },
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "dev": true
+    },
+    "asn1.js": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+      "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "safer-buffer": "^2.1.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "assert": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+      "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+      "dev": true,
+      "requires": {
+        "object-assign": "^4.1.1",
+        "util": "0.10.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+          "dev": true
+        },
+        "util": {
+          "version": "0.10.3",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+          "dev": true,
+          "requires": {
+            "inherits": "2.0.1"
+          }
+        }
+      }
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true
+    },
+    "async": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+      "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "async-each": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+      "dev": true,
+      "optional": true
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "base64-js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+      "dev": true
+    },
+    "basic-auth": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz",
+      "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=",
+      "dev": true
+    },
+    "big.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+      "dev": true,
+      "optional": true
+    },
+    "bluebird": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
+    },
+    "bn.js": {
+      "version": "5.1.3",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz",
+      "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+      "dev": true,
+      "requires": {
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+      "dev": true
+    },
+    "browserify-aes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+      "dev": true,
+      "requires": {
+        "buffer-xor": "^1.0.3",
+        "cipher-base": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.3",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "browserify-cipher": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+      "dev": true,
+      "requires": {
+        "browserify-aes": "^1.0.4",
+        "browserify-des": "^1.0.0",
+        "evp_bytestokey": "^1.0.0"
+      }
+    },
+    "browserify-des": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "des.js": "^1.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "browserify-rsa": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "randombytes": "^2.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "browserify-sign": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
+      "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^5.1.1",
+        "browserify-rsa": "^4.0.1",
+        "create-hash": "^1.2.0",
+        "create-hmac": "^1.1.7",
+        "elliptic": "^6.5.3",
+        "inherits": "^2.0.4",
+        "parse-asn1": "^5.1.5",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "browserify-zlib": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+      "dev": true,
+      "requires": {
+        "pako": "~1.0.5"
+      }
+    },
+    "buffer": {
+      "version": "4.9.2",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+      "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+      "dev": true,
+      "requires": {
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4",
+        "isarray": "^1.0.0"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+      "dev": true
+    },
+    "builtin-status-codes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+      "dev": true
+    },
+    "cacache": {
+      "version": "12.0.4",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+      "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+      "dev": true,
+      "requires": {
+        "bluebird": "^3.5.5",
+        "chownr": "^1.1.1",
+        "figgy-pudding": "^3.5.1",
+        "glob": "^7.1.4",
+        "graceful-fs": "^4.1.15",
+        "infer-owner": "^1.0.3",
+        "lru-cache": "^5.1.1",
+        "mississippi": "^3.0.0",
+        "mkdirp": "^0.5.1",
+        "move-concurrently": "^1.0.1",
+        "promise-inflight": "^1.0.1",
+        "rimraf": "^2.6.3",
+        "ssri": "^6.0.1",
+        "unique-filename": "^1.1.1",
+        "y18n": "^4.0.0"
+      }
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "chokidar": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
+      "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.5.0"
+      },
+      "dependencies": {
+        "braces": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+          "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "fill-range": "^7.0.1"
+          }
+        },
+        "fill-range": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+          "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "to-regex-range": "^5.0.1"
+          }
+        },
+        "is-number": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+          "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+          "dev": true,
+          "optional": true
+        },
+        "to-regex-range": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+          "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "is-number": "^7.0.0"
+          }
+        }
+      }
+    },
+    "chownr": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+      "dev": true
+    },
+    "chrome-trace-event": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
+      "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
+    "cipher-base": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true
+    },
+    "cliui": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^3.1.0",
+        "strip-ansi": "^5.2.0",
+        "wrap-ansi": "^5.1.0"
+      }
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "colors": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+      "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "commondir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+      "dev": true
+    },
+    "component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "console-browserify": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+      "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+      "dev": true
+    },
+    "constants-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+      "dev": true
+    },
+    "copy-concurrently": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "fs-write-stream-atomic": "^1.0.8",
+        "iferr": "^0.1.5",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.0"
+      }
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
+    "copy-webpack-plugin": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz",
+      "integrity": "sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q==",
+      "dev": true,
+      "requires": {
+        "cacache": "^15.0.5",
+        "fast-glob": "^3.2.4",
+        "find-cache-dir": "^3.3.1",
+        "glob-parent": "^5.1.1",
+        "globby": "^11.0.1",
+        "loader-utils": "^2.0.0",
+        "normalize-path": "^3.0.0",
+        "p-limit": "^3.0.2",
+        "schema-utils": "^3.0.0",
+        "serialize-javascript": "^5.0.1",
+        "webpack-sources": "^1.4.3"
+      },
+      "dependencies": {
+        "cacache": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz",
+          "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==",
+          "dev": true,
+          "requires": {
+            "@npmcli/move-file": "^1.0.1",
+            "chownr": "^2.0.0",
+            "fs-minipass": "^2.0.0",
+            "glob": "^7.1.4",
+            "infer-owner": "^1.0.4",
+            "lru-cache": "^6.0.0",
+            "minipass": "^3.1.1",
+            "minipass-collect": "^1.0.2",
+            "minipass-flush": "^1.0.5",
+            "minipass-pipeline": "^1.2.2",
+            "mkdirp": "^1.0.3",
+            "p-map": "^4.0.0",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^3.0.2",
+            "ssri": "^8.0.0",
+            "tar": "^6.0.2",
+            "unique-filename": "^1.1.1"
+          }
+        },
+        "chownr": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+          "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+          "dev": true
+        },
+        "find-cache-dir": {
+          "version": "3.3.1",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
+          "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^3.0.2",
+            "pkg-dir": "^4.1.0"
+          }
+        },
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "json5": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+          "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.5"
+          }
+        },
+        "loader-utils": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+          "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        },
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^4.1.0"
+          }
+        },
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "make-dir": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+          "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+          "dev": true,
+          "requires": {
+            "semver": "^6.0.0"
+          }
+        },
+        "mkdirp": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+          "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+          "dev": true
+        },
+        "p-limit": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+          "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.2.0"
+          },
+          "dependencies": {
+            "p-limit": {
+              "version": "2.3.0",
+              "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+              "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+              "dev": true,
+              "requires": {
+                "p-try": "^2.0.0"
+              }
+            }
+          }
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+          "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+          "dev": true,
+          "requires": {
+            "find-up": "^4.0.0"
+          }
+        },
+        "rimraf": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+          "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+          "dev": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "schema-utils": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+          "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
+          "dev": true,
+          "requires": {
+            "@types/json-schema": "^7.0.6",
+            "ajv": "^6.12.5",
+            "ajv-keywords": "^3.5.2"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "serialize-javascript": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+          "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
+          "dev": true,
+          "requires": {
+            "randombytes": "^2.1.0"
+          }
+        },
+        "ssri": {
+          "version": "8.0.0",
+          "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz",
+          "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==",
+          "dev": true,
+          "requires": {
+            "minipass": "^3.1.1"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+      "dev": true
+    },
+    "corser": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
+      "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=",
+      "dev": true
+    },
+    "create-ecdh": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+      "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "elliptic": "^6.5.3"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "create-hash": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "md5.js": "^1.3.4",
+        "ripemd160": "^2.0.1",
+        "sha.js": "^2.4.0"
+      }
+    },
+    "create-hmac": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+      "dev": true,
+      "requires": {
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "crypto-browserify": {
+      "version": "3.12.0",
+      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+      "dev": true,
+      "requires": {
+        "browserify-cipher": "^1.0.0",
+        "browserify-sign": "^4.0.0",
+        "create-ecdh": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "create-hmac": "^1.1.0",
+        "diffie-hellman": "^5.0.0",
+        "inherits": "^2.0.1",
+        "pbkdf2": "^3.0.3",
+        "public-encrypt": "^4.0.0",
+        "randombytes": "^2.0.0",
+        "randomfill": "^1.0.3"
+      }
+    },
+    "cyclist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+      "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+      "dev": true
+    },
+    "debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "requires": {
+        "ms": "2.0.0"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "dependencies": {
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "des.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+      "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "detect-file": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+      "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+      "dev": true
+    },
+    "diffie-hellman": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+      "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "miller-rabin": "^4.0.0",
+        "randombytes": "^2.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      }
+    },
+    "domain-browser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+      "dev": true
+    },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "ecstatic": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz",
+      "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==",
+      "dev": true,
+      "requires": {
+        "he": "^1.1.1",
+        "mime": "^1.6.0",
+        "minimist": "^1.1.0",
+        "url-join": "^2.0.5"
+      }
+    },
+    "elliptic": {
+      "version": "6.5.3",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
+      "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.4.0",
+        "brorand": "^1.0.1",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.0",
+        "inherits": "^2.0.1",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.0"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "emoji-regex": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+      "dev": true
+    },
+    "emojis-list": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+      "dev": true
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dev": true,
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "enhanced-resolve": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
+      "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "memory-fs": "^0.5.0",
+        "tapable": "^1.0.0"
+      },
+      "dependencies": {
+        "memory-fs": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+          "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+          "dev": true,
+          "requires": {
+            "errno": "^0.1.3",
+            "readable-stream": "^2.0.1"
+          }
+        }
+      }
+    },
+    "errno": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+      "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+      "dev": true,
+      "requires": {
+        "prr": "~1.0.1"
+      }
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "eslint-scope": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+      "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.1.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+          "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+          "dev": true
+        }
+      }
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true
+    },
+    "eventemitter3": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
+      "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
+      "dev": true
+    },
+    "events": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
+      "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==",
+      "dev": true
+    },
+    "evp_bytestokey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "dev": true,
+      "requires": {
+        "md5.js": "^1.3.4",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "dev": true,
+      "requires": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "expand-tilde": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+      "dev": true,
+      "requires": {
+        "homedir-polyfill": "^1.0.1"
+      }
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "requires": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-glob": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
+      "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.0",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.2",
+        "picomatch": "^2.2.1"
+      },
+      "dependencies": {
+        "braces": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+          "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+          "dev": true,
+          "requires": {
+            "fill-range": "^7.0.1"
+          }
+        },
+        "fill-range": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+          "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+          "dev": true,
+          "requires": {
+            "to-regex-range": "^5.0.1"
+          }
+        },
+        "is-number": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+          "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+          "dev": true
+        },
+        "micromatch": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+          "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+          "dev": true,
+          "requires": {
+            "braces": "^3.0.1",
+            "picomatch": "^2.0.5"
+          }
+        },
+        "to-regex-range": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+          "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+          "dev": true,
+          "requires": {
+            "is-number": "^7.0.0"
+          }
+        }
+      }
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
+      "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "figgy-pudding": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+      "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+      "dev": true
+    },
+    "fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "find-cache-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+      "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+      "dev": true,
+      "requires": {
+        "commondir": "^1.0.1",
+        "make-dir": "^2.0.0",
+        "pkg-dir": "^3.0.0"
+      }
+    },
+    "find-up": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^3.0.0"
+      }
+    },
+    "findup-sync": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+      "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+      "dev": true,
+      "requires": {
+        "detect-file": "^1.0.0",
+        "is-glob": "^4.0.0",
+        "micromatch": "^3.0.4",
+        "resolve-dir": "^1.0.1"
+      }
+    },
+    "flush-write-stream": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.3.6"
+      }
+    },
+    "follow-redirects": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz",
+      "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==",
+      "dev": true
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
+    "from2": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0"
+      }
+    },
+    "fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "fs-write-stream-atomic": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "iferr": "^0.1.5",
+        "imurmurhash": "^0.1.4",
+        "readable-stream": "1 || 2"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "global-modules": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+      "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+      "dev": true,
+      "requires": {
+        "global-prefix": "^3.0.0"
+      },
+      "dependencies": {
+        "global-prefix": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+          "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+          "dev": true,
+          "requires": {
+            "ini": "^1.3.5",
+            "kind-of": "^6.0.2",
+            "which": "^1.3.1"
+          }
+        }
+      }
+    },
+    "global-prefix": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+      "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+      "dev": true,
+      "requires": {
+        "expand-tilde": "^2.0.2",
+        "homedir-polyfill": "^1.0.1",
+        "ini": "^1.3.4",
+        "is-windows": "^1.0.1",
+        "which": "^1.2.14"
+      }
+    },
+    "globby": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
+      "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
+      "dev": true,
+      "requires": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.1.1",
+        "ignore": "^5.1.4",
+        "merge2": "^1.3.0",
+        "slash": "^3.0.0"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "dev": true,
+      "requires": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "hash-base": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+      "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "dependencies": {
+        "readable-stream": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+          "dev": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.2.1",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+          "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+          "dev": true
+        }
+      }
+    },
+    "hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
+    },
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "dev": true,
+      "requires": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "homedir-polyfill": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+      "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+      "dev": true,
+      "requires": {
+        "parse-passwd": "^1.0.0"
+      }
+    },
+    "http-proxy": {
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "dev": true,
+      "requires": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      }
+    },
+    "http-server": {
+      "version": "0.12.3",
+      "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz",
+      "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==",
+      "dev": true,
+      "requires": {
+        "basic-auth": "^1.0.3",
+        "colors": "^1.4.0",
+        "corser": "^2.0.1",
+        "ecstatic": "^3.3.2",
+        "http-proxy": "^1.18.0",
+        "minimist": "^1.2.5",
+        "opener": "^1.5.1",
+        "portfinder": "^1.0.25",
+        "secure-compare": "3.0.1",
+        "union": "~0.5.0"
+      }
+    },
+    "https-browserify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+      "dev": true
+    },
+    "ieee754": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+      "dev": true
+    },
+    "iferr": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+      "dev": true
+    },
+    "ignore": {
+      "version": "5.1.8",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+      "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+      "dev": true
+    },
+    "import-local": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+      "dev": true,
+      "requires": {
+        "pkg-dir": "^3.0.0",
+        "resolve-cwd": "^2.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true
+    },
+    "infer-owner": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "ini": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+      "dev": true
+    },
+    "interpret": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+      "dev": true
+    },
+    "is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
+    "is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "requires": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+          "dev": true
+        }
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+      "dev": true
+    },
+    "is-wsl": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+      "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json5": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+      "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.0"
+      }
+    },
+    "kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true
+    },
+    "loader-runner": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+      "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+      "dev": true
+    },
+    "loader-utils": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+      "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+      "dev": true,
+      "requires": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^1.0.1"
+      }
+    },
+    "locate-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^3.0.0",
+        "path-exists": "^3.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.19",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+      "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
+      "dev": true
+    },
+    "lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "requires": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "dev": true,
+      "requires": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      }
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+      "dev": true
+    },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
+    },
+    "md5.js": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+      "dev": true,
+      "requires": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "memory-fs": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+      "dev": true,
+      "requires": {
+        "errno": "^0.1.3",
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      }
+    },
+    "miller-rabin": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.0.0",
+        "brorand": "^1.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true
+    },
+    "minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+      "dev": true
+    },
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+      "dev": true
+    },
+    "minipass": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+      "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+      "dev": true,
+      "requires": {
+        "yallist": "^4.0.0"
+      },
+      "dependencies": {
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "minipass-collect": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+      "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "minipass-flush": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+      "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "minipass-pipeline": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dev": true,
+      "requires": {
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      },
+      "dependencies": {
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "mississippi": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+      "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+      "dev": true,
+      "requires": {
+        "concat-stream": "^1.5.0",
+        "duplexify": "^3.4.2",
+        "end-of-stream": "^1.1.0",
+        "flush-write-stream": "^1.0.0",
+        "from2": "^2.1.0",
+        "parallel-transform": "^1.1.0",
+        "pump": "^3.0.0",
+        "pumpify": "^1.3.3",
+        "stream-each": "^1.1.0",
+        "through2": "^2.0.0"
+      }
+    },
+    "mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+      "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5"
+      }
+    },
+    "move-concurrently": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1",
+        "copy-concurrently": "^1.0.0",
+        "fs-write-stream-atomic": "^1.0.8",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.5.4",
+        "run-queue": "^1.0.3"
+      }
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+      "dev": true
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      }
+    },
+    "neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "node-libs-browser": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+      "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+      "dev": true,
+      "requires": {
+        "assert": "^1.1.1",
+        "browserify-zlib": "^0.2.0",
+        "buffer": "^4.3.0",
+        "console-browserify": "^1.1.0",
+        "constants-browserify": "^1.0.0",
+        "crypto-browserify": "^3.11.0",
+        "domain-browser": "^1.1.1",
+        "events": "^3.0.0",
+        "https-browserify": "^1.0.0",
+        "os-browserify": "^0.3.0",
+        "path-browserify": "0.0.1",
+        "process": "^0.11.10",
+        "punycode": "^1.2.4",
+        "querystring-es3": "^0.2.0",
+        "readable-stream": "^2.3.3",
+        "stream-browserify": "^2.0.1",
+        "stream-http": "^2.7.2",
+        "string_decoder": "^1.0.0",
+        "timers-browserify": "^2.0.4",
+        "tty-browserify": "0.0.0",
+        "url": "^0.11.0",
+        "util": "^0.11.0",
+        "vm-browserify": "^1.0.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+      "dev": true
+    },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "dev": true,
+      "requires": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.0"
+      }
+    },
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "opener": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz",
+      "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
+      "dev": true
+    },
+    "os-browserify": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+      "dev": true
+    },
+    "p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "requires": {
+        "p-try": "^2.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^2.0.0"
+      }
+    },
+    "p-map": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+      "dev": true,
+      "requires": {
+        "aggregate-error": "^3.0.0"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
+    },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "dev": true
+    },
+    "parallel-transform": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+      "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+      "dev": true,
+      "requires": {
+        "cyclist": "^1.0.1",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.1.5"
+      }
+    },
+    "parse-asn1": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
+      "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
+      "dev": true,
+      "requires": {
+        "asn1.js": "^5.2.0",
+        "browserify-aes": "^1.0.0",
+        "evp_bytestokey": "^1.0.0",
+        "pbkdf2": "^3.0.3",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "parse-passwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+      "dev": true
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "dev": true
+    },
+    "path-browserify": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+      "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+      "dev": true
+    },
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+      "dev": true,
+      "optional": true
+    },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
+    },
+    "path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true
+    },
+    "pbkdf2": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
+      "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==",
+      "dev": true,
+      "requires": {
+        "create-hash": "^1.1.2",
+        "create-hmac": "^1.1.4",
+        "ripemd160": "^2.0.1",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+      "dev": true
+    },
+    "pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true
+    },
+    "pkg-dir": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+      "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+      "dev": true,
+      "requires": {
+        "find-up": "^3.0.0"
+      }
+    },
+    "portfinder": {
+      "version": "1.0.28",
+      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+      "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
+      "dev": true,
+      "requires": {
+        "async": "^2.6.2",
+        "debug": "^3.1.1",
+        "mkdirp": "^0.5.5"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.2.6",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        }
+      }
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true
+    },
+    "process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true
+    },
+    "promise-inflight": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+      "dev": true
+    },
+    "prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+      "dev": true
+    },
+    "public-encrypt": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+      "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+      "dev": true,
+      "requires": {
+        "bn.js": "^4.1.0",
+        "browserify-rsa": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "parse-asn1": "^5.0.0",
+        "randombytes": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.11.9",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+          "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+          "dev": true
+        }
+      }
+    },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "pumpify": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+      "dev": true,
+      "requires": {
+        "duplexify": "^3.6.0",
+        "inherits": "^2.0.3",
+        "pump": "^2.0.0"
+      },
+      "dependencies": {
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "dev": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        }
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.9.4",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz",
+      "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==",
+      "dev": true
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+      "dev": true
+    },
+    "querystring-es3": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+      "dev": true
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "randomfill": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+      "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.0.5",
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "dev": true,
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdirp": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "dev": true,
+      "optional": true
+    },
+    "repeat-element": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+      "dev": true
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "dev": true
+    },
+    "requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+      "dev": true
+    },
+    "resolve-cwd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+      "dev": true,
+      "requires": {
+        "resolve-from": "^3.0.0"
+      }
+    },
+    "resolve-dir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+      "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+      "dev": true,
+      "requires": {
+        "expand-tilde": "^2.0.0",
+        "global-modules": "^1.0.0"
+      },
+      "dependencies": {
+        "global-modules": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+          "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+          "dev": true,
+          "requires": {
+            "global-prefix": "^1.0.1",
+            "is-windows": "^1.0.1",
+            "resolve-dir": "^1.0.0"
+          }
+        }
+      }
+    },
+    "resolve-from": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+      "dev": true
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "dev": true
+    },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+      "dev": true
+    },
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "ripemd160": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+      "dev": true,
+      "requires": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1"
+      }
+    },
+    "run-parallel": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
+      "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
+      "dev": true
+    },
+    "run-queue": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+      "dev": true,
+      "requires": {
+        "aproba": "^1.1.1"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
+      "requires": {
+        "ret": "~0.1.10"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "schema-utils": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+      "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.1.0",
+        "ajv-errors": "^1.0.0",
+        "ajv-keywords": "^3.1.0"
+      }
+    },
+    "secure-compare": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
+      "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=",
+      "dev": true
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true
+    },
+    "serialize-javascript": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+      "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+      "dev": true
+    },
+    "set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+      "dev": true
+    },
+    "sha.js": {
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true
+    },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "dev": true,
+      "requires": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "source-list-map": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+      "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+      "dev": true
+    },
+    "source-map": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+      "dev": true
+    },
+    "source-map-resolve": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+      "dev": true,
+      "requires": {
+        "atob": "^2.1.2",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+      "dev": true
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      }
+    },
+    "ssri": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+      "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+      "dev": true,
+      "requires": {
+        "figgy-pudding": "^3.5.1"
+      }
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "dev": true,
+      "requires": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "stream-browserify": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+      "dev": true,
+      "requires": {
+        "inherits": "~2.0.1",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "stream-each": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+      "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "stream-http": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+      "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+      "dev": true,
+      "requires": {
+        "builtin-status-codes": "^3.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.3.6",
+        "to-arraybuffer": "^1.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+      "dev": true
+    },
+    "string-width": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+      "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^7.0.1",
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^5.1.0"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^4.1.0"
+      }
+    },
+    "supports-color": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+      "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "tapable": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+      "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+      "dev": true
+    },
+    "tar": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz",
+      "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==",
+      "dev": true,
+      "requires": {
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^3.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.0"
+      },
+      "dependencies": {
+        "chownr": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+          "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+          "dev": true
+        },
+        "mkdirp": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+          "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+          "dev": true
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
+      }
+    },
+    "terser": {
+      "version": "4.8.0",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+      "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+      "dev": true,
+      "requires": {
+        "commander": "^2.20.0",
+        "source-map": "~0.6.1",
+        "source-map-support": "~0.5.12"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "terser-webpack-plugin": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+      "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+      "dev": true,
+      "requires": {
+        "cacache": "^12.0.2",
+        "find-cache-dir": "^2.1.0",
+        "is-wsl": "^1.1.0",
+        "schema-utils": "^1.0.0",
+        "serialize-javascript": "^4.0.0",
+        "source-map": "^0.6.1",
+        "terser": "^4.1.2",
+        "webpack-sources": "^1.4.0",
+        "worker-farm": "^1.7.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "timers-browserify": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
+      "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+      "dev": true,
+      "requires": {
+        "setimmediate": "^1.0.4"
+      }
+    },
+    "to-arraybuffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+      "dev": true
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
+      }
+    },
+    "ts-loader": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.3.tgz",
+      "integrity": "sha512-wsqfnVdB7xQiqhqbz2ZPLGHLPZbHVV5Qn/MNFZkCFxRU1miDyxKORucDGxKtsQJ63Rfza0udiUxWF5nHY6bpdQ==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.3.0",
+        "enhanced-resolve": "^4.0.0",
+        "loader-utils": "^1.0.2",
+        "micromatch": "^4.0.0",
+        "semver": "^6.0.0"
+      },
+      "dependencies": {
+        "braces": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+          "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+          "dev": true,
+          "requires": {
+            "fill-range": "^7.0.1"
+          }
+        },
+        "fill-range": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+          "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+          "dev": true,
+          "requires": {
+            "to-regex-range": "^5.0.1"
+          }
+        },
+        "is-number": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+          "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+          "dev": true
+        },
+        "micromatch": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+          "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+          "dev": true,
+          "requires": {
+            "braces": "^3.0.1",
+            "picomatch": "^2.0.5"
+          }
+        },
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        },
+        "to-regex-range": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+          "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+          "dev": true,
+          "requires": {
+            "is-number": "^7.0.0"
+          }
+        }
+      }
+    },
+    "tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "tty-browserify": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+      "dev": true
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "typescript": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
+      "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
+      "dev": true
+    },
+    "union": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
+      "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
+      "dev": true,
+      "requires": {
+        "qs": "^6.4.0"
+      }
+    },
+    "union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      }
+    },
+    "unique-filename": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+      "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+      "dev": true,
+      "requires": {
+        "unique-slug": "^2.0.0"
+      }
+    },
+    "unique-slug": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+      "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+      "dev": true,
+      "requires": {
+        "imurmurhash": "^0.1.4"
+      }
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "dev": true,
+      "requires": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "dev": true,
+          "requires": {
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "dev": true,
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "dev": true
+        }
+      }
+    },
+    "upath": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+      "dev": true,
+      "optional": true
+    },
+    "uri-js": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+      "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "dev": true
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "dev": true,
+      "requires": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+          "dev": true
+        }
+      }
+    },
+    "url-join": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz",
+      "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=",
+      "dev": true
+    },
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "dev": true
+    },
+    "util": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+      "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+      "dev": true,
+      "requires": {
+        "inherits": "2.0.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        }
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
+    },
+    "v8-compile-cache": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
+      "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
+      "dev": true
+    },
+    "vm-browserify": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+      "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+      "dev": true
+    },
+    "watchpack": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
+      "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==",
+      "dev": true,
+      "requires": {
+        "chokidar": "^3.4.1",
+        "graceful-fs": "^4.1.2",
+        "neo-async": "^2.5.0",
+        "watchpack-chokidar2": "^2.0.0"
+      }
+    },
+    "watchpack-chokidar2": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz",
+      "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "chokidar": "^2.1.8"
+      },
+      "dependencies": {
+        "anymatch": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "micromatch": "^3.1.4",
+            "normalize-path": "^2.1.1"
+          },
+          "dependencies": {
+            "normalize-path": {
+              "version": "2.1.1",
+              "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "remove-trailing-separator": "^1.0.1"
+              }
+            }
+          }
+        },
+        "binary-extensions": {
+          "version": "1.13.1",
+          "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+          "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+          "dev": true,
+          "optional": true
+        },
+        "chokidar": {
+          "version": "2.1.8",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+          "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "anymatch": "^2.0.0",
+            "async-each": "^1.0.1",
+            "braces": "^2.3.2",
+            "fsevents": "^1.2.7",
+            "glob-parent": "^3.1.0",
+            "inherits": "^2.0.3",
+            "is-binary-path": "^1.0.0",
+            "is-glob": "^4.0.0",
+            "normalize-path": "^3.0.0",
+            "path-is-absolute": "^1.0.0",
+            "readdirp": "^2.2.1",
+            "upath": "^1.1.1"
+          }
+        },
+        "fsevents": {
+          "version": "1.2.13",
+          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+          "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+          "dev": true,
+          "optional": true
+        },
+        "glob-parent": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "is-glob": "^3.1.0",
+            "path-dirname": "^1.0.0"
+          },
+          "dependencies": {
+            "is-glob": {
+              "version": "3.1.0",
+              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+              "dev": true,
+              "optional": true,
+              "requires": {
+                "is-extglob": "^2.1.0"
+              }
+            }
+          }
+        },
+        "is-binary-path": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "binary-extensions": "^1.0.0"
+          }
+        },
+        "readdirp": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+          "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "graceful-fs": "^4.1.11",
+            "micromatch": "^3.1.10",
+            "readable-stream": "^2.0.2"
+          }
+        }
+      }
+    },
+    "webpack": {
+      "version": "4.44.2",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz",
+      "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.9.0",
+        "@webassemblyjs/helper-module-context": "1.9.0",
+        "@webassemblyjs/wasm-edit": "1.9.0",
+        "@webassemblyjs/wasm-parser": "1.9.0",
+        "acorn": "^6.4.1",
+        "ajv": "^6.10.2",
+        "ajv-keywords": "^3.4.1",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^4.3.0",
+        "eslint-scope": "^4.0.3",
+        "json-parse-better-errors": "^1.0.2",
+        "loader-runner": "^2.4.0",
+        "loader-utils": "^1.2.3",
+        "memory-fs": "^0.4.1",
+        "micromatch": "^3.1.10",
+        "mkdirp": "^0.5.3",
+        "neo-async": "^2.6.1",
+        "node-libs-browser": "^2.2.1",
+        "schema-utils": "^1.0.0",
+        "tapable": "^1.1.3",
+        "terser-webpack-plugin": "^1.4.3",
+        "watchpack": "^1.7.4",
+        "webpack-sources": "^1.4.1"
+      }
+    },
+    "webpack-cli": {
+      "version": "3.3.12",
+      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz",
+      "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.2",
+        "cross-spawn": "^6.0.5",
+        "enhanced-resolve": "^4.1.1",
+        "findup-sync": "^3.0.0",
+        "global-modules": "^2.0.0",
+        "import-local": "^2.0.0",
+        "interpret": "^1.4.0",
+        "loader-utils": "^1.4.0",
+        "supports-color": "^6.1.0",
+        "v8-compile-cache": "^2.1.1",
+        "yargs": "^13.3.2"
+      }
+    },
+    "webpack-sources": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+      "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+      "dev": true,
+      "requires": {
+        "source-list-map": "^2.0.0",
+        "source-map": "~0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+      "dev": true
+    },
+    "worker-farm": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+      "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+      "dev": true,
+      "requires": {
+        "errno": "~0.1.7"
+      }
+    },
+    "wrap-ansi": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.0",
+        "string-width": "^3.0.0",
+        "strip-ansi": "^5.0.0"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "dev": true
+    },
+    "y18n": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+      "dev": true
+    },
+    "yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    },
+    "yargs": {
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+      "dev": true,
+      "requires": {
+        "cliui": "^5.0.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^2.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^2.0.0",
+        "set-blocking": "^2.0.0",
+        "string-width": "^3.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^13.1.2"
+      }
+    },
+    "yargs-parser": {
+      "version": "13.1.2",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      }
+    }
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/package.json b/src/cobalt/demos/content/media-element-demo/package.json
new file mode 100644
index 0000000..bed93af
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/package.json
@@ -0,0 +1,35 @@
+{
+  "name": "media-element-demo",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "start": "npm run watch & npm run server",
+    "build": "webpack",
+    "server": "http-server dist",
+    "watch": "webpack --watch"
+  },
+  "author": "",
+  "license": "ISC",
+  "devDependencies": {
+    "copy-webpack-plugin": "^6.2.1",
+    "http-server": "^0.12.3",
+    "ts-loader": "^8.0.3",
+    "typescript": "^4.0.2",
+    "webpack": "^4.44.2",
+    "webpack-cli": "^3.3.12"
+  },
+  "devDependencies-comments": {
+    "copy-webpack-plugin": "Allows webpack to copy files from public/ to dist/.",
+    "http-server": "An http server to host the site. Used as the dev server.",
+    "ts-loader": "Allows webpack to load ts files.",
+    "typescript": "The famous TypeScript.",
+    "webpack": "Bundles scripts into one file and generate dist/.",
+    "webpack-cli": "Allows us to run `webpack --watch`."
+  },
+  "dependencies": {
+    "bluebird": "^3.7.2"
+  },
+  "dependencies-comments": {
+    "bluebird": "Replaces native Promise in Cobalt because Cobalt doesn't support unhandledrejection natively."
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/progressive-demo.html b/src/cobalt/demos/content/media-element-demo/progressive-demo.html
deleted file mode 100644
index 2d10dde2..0000000
--- a/src/cobalt/demos/content/media-element-demo/progressive-demo.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Progressive Demo</title>
-  <style>
-    body {
-      background-color: rgb(255, 255, 255);
-      color: #0047ab;
-      font-size: 100px;
-    }
-    .small {
-      margin: 100px;
-      border: 10px solid blue;
-      width: 960px;
-      height: 540px;
-    }
-    .big {
-      margin: 10px;
-      border: 10px solid blue;
-      width: 1280px;
-      height: 720px;
-    }
-  </style>
-</head>
-<body>
-  <div>Progressive Demo</div>
-  <video autoplay loop id="v" class="small" src="progressive.mp4"></video>
-  <script>
-    window.setInterval(function() {
-      if (document.getElementById('v').className === 'big')
-        document.getElementById('v').className = 'small';
-      else
-        document.getElementById('v').className = 'big';
-    }, 3000);
-  </script>
-</body>
-</html>
diff --git a/src/cobalt/demos/content/media-element-demo/vp9_720p.webm b/src/cobalt/demos/content/media-element-demo/public/assets/vp9_720p.webm
similarity index 100%
rename from src/cobalt/demos/content/media-element-demo/vp9_720p.webm
rename to src/cobalt/demos/content/media-element-demo/public/assets/vp9_720p.webm
Binary files differ
diff --git a/src/cobalt/demos/content/media-element-demo/public/index.html b/src/cobalt/demos/content/media-element-demo/public/index.html
new file mode 100644
index 0000000..b56dfe7
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/public/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Media Element Demo</title>
+  <link rel="stylesheet" type="text/css" href="styles/app.css">
+</head>
+
+<body>
+  <div id="error-logger"></div>
+  <div id="router"></div>
+  <script async type="text/javascript" src="bundle.js"></script>
+</body>
+
+</html>
diff --git a/src/cobalt/demos/content/media-element-demo/public/styles/app.css b/src/cobalt/demos/content/media-element-demo/public/styles/app.css
new file mode 100644
index 0000000..0b425ab
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/public/styles/app.css
@@ -0,0 +1,14 @@
+body {
+  background: #000000;
+  color: #ffffff;
+  font-size: 20px;
+}
+
+.error {
+  background:#f8d7da;
+  border-radius: .25rem;
+  border: 1px transparent solid;
+  color: #721c24;
+  margin-bottom: .25rem;
+  padding: .75rem 1.25rem;
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/component.ts b/src/cobalt/demos/content/media-element-demo/src/components/component.ts
new file mode 100644
index 0000000..55f9474
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/component.ts
@@ -0,0 +1,60 @@
+type ComponentCtor<Props> = new (props: Props) => Component<Props>;
+type ComponentFunc<Props> = (props: Props) => string;
+
+/** Base component class. */
+export class Component<Props> {
+  el!: Element;
+
+  constructor(readonly props: Props) {
+    this.props = props;
+  }
+
+  /** Lifecycle method to generate static templates. */
+  render(props: Props): string {
+    throw new Error('Must be overridden.');
+  }
+
+  /**
+   * Lifecycle method called after `render()`. It's a good opportunity to add
+   * dynamic content and add event listeners.
+   */
+  afterRender() {}
+}
+
+/** Caches the component instances hooked to DOM elements. */
+const componentMap = new WeakMap<Element, Component<unknown>>();
+
+function isComponentCtor<Props>(
+    CtorOrFunc: ComponentCtor<Props>|
+    ComponentFunc<Props>): CtorOrFunc is ComponentCtor<Props> {
+  if (CtorOrFunc.prototype instanceof Component) {
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Renders a component to the container element.
+ * @param CtorOrFunc Can be either a component contructor or a component
+ *     function. The component function is easier to write but does not keep
+ *     track of internal states.
+ * @param props Component props.
+ * @param container The container element of the component.
+ */
+export function renderComponent<Props>(
+    CtorOrFunc: ComponentCtor<Props>|ComponentFunc<Props>, props: Props,
+    container: Element) {
+  if (!isComponentCtor(CtorOrFunc)) {
+    // Function based component
+    container.innerHTML = CtorOrFunc(props);
+    return;
+  }
+  let component = componentMap.get(container) as Component<Props>| undefined;
+  if (!(component instanceof CtorOrFunc)) {
+    component = new CtorOrFunc(props);
+    componentMap.set(container, component);
+    component.el = container;
+  }
+  container.innerHTML = component.render(props);
+  component.afterRender();
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/download_buffer_info.ts b/src/cobalt/demos/content/media-element-demo/src/components/download_buffer_info.ts
new file mode 100644
index 0000000..ed059d1
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/download_buffer_info.ts
@@ -0,0 +1,23 @@
+import {MediaDownloaderStatus} from '../utils/download_buffer';
+import {Media} from '../utils/media';
+
+interface Props {
+  mediaList: Media[];
+  reportMap: Map<Media, MediaDownloaderStatus>;
+}
+
+/** A component that displays the download buffer info. */
+export function DownloadBufferInfo({mediaList, reportMap}: Props) {
+  const elements = mediaList.map((video) => {
+    const report = reportMap.get(video);
+    if (!report) {
+      throw new Error(`Download buffer info for ${video.url} not found.`);
+    }
+    return `
+      <div>
+        ${video.url}: downloaded ${report.downloadedBytes} bytes ${
+        report.finished ? '[Done]' : ''}, queued ${report.queuedChunks} chunks.
+      </div>`;
+  });
+  return elements.join('');
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/error_logger.ts b/src/cobalt/demos/content/media-element-demo/src/components/error_logger.ts
new file mode 100644
index 0000000..81750dc
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/error_logger.ts
@@ -0,0 +1,46 @@
+import {errors} from '../utils/shared_values';
+
+import {Component} from './component';
+
+/** A component that holds error logs. */
+export class ErrorLogger extends Component<{}> {
+  /** @override */
+  render() {
+    return '';
+  }
+
+  /** @override */
+  afterRender() {
+    errors.observe((messages: string[]) => {
+      this.renderError(messages);
+    });
+    window.addEventListener('error', (message) => {
+      if (typeof message === 'string') {
+        logError(message);
+        return;
+      }
+    });
+    window.addEventListener('unhandledrejection', (evt: any) => {
+      // In cobalt where bluebird is used, the reason is under evt.detail.reason
+      const reason = evt.reason ?? evt.detail.reason;
+      logError(reason);
+    });
+  }
+
+  private renderError(messages: string[]) {
+    const elements = messages.map(message => `
+      <div class="error">${message}</div>
+    `);
+    this.el.innerHTML = elements.join('');
+  }
+}
+
+/**
+ * Logs an error message. The logged error will be automatically picked up by
+ * the error logger.
+ * @param message The error message.
+ */
+export function logError(message: string) {
+  const current = errors.get();
+  errors.set([...current, message]);
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/player.ts b/src/cobalt/demos/content/media-element-demo/src/components/player.ts
new file mode 100644
index 0000000..7bd9c17
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/player.ts
@@ -0,0 +1,240 @@
+import {DownloadBuffer} from '../utils/download_buffer';
+import {PlayMode, SwitchMode} from '../utils/enums';
+import {LimitedSourceBuffer} from '../utils/limited_source_buffer';
+import {Media} from '../utils/media';
+
+import {Component, renderComponent} from './component';
+import {DownloadBufferInfo} from './download_buffer_info';
+import {SourceBufferInfo} from './source_buffer_info';
+import {VideoInfo} from './video_info';
+
+interface Props {
+  video?: string|string[];
+  audio?: string|string[];
+  switchMode?: SwitchMode;
+}
+
+/** A component that controls video playback. */
+export class Player extends Component<Props> {
+  /** The videos to play. */
+  private readonly videos: Media[];
+
+  /** The audios to play. */
+  private readonly audios: Media[];
+
+  /** The <video> element. */
+  private videoEl!: HTMLVideoElement;
+
+  /** The element displaying video download buffer info. */
+  private videoDownloadBufferInfo!: Element;
+
+  /** The element displaying audio download buffer info. */
+  private audioDownloadBufferInfo!: Element;
+
+  /** The element displaying video source buffer info. */
+  private videoSourceBufferInfo!: Element;
+
+  /** The element displaying audio source buffer info. */
+  private audioSourceBufferInfo!: Element;
+
+  /** The element displaying video info. */
+  private videoInfo!: Element;
+
+  /**
+   * Switch mode defines whether to create a new playback session or <video>
+   * element after a media finishes playing.
+   */
+  private switchMode?: SwitchMode;
+
+  /** Play mode defines whether to play progressively or adaptively. */
+  private playMode?: PlayMode;
+
+  constructor(props: Props) {
+    super(props);
+    this.videos = convertToMediaArray(props.video);
+    this.audios = convertToMediaArray(props.audio);
+  }
+
+  /** @override */
+  render() {
+    return `
+      <div class="video-container"></div>
+      <div class="video-info"></div>
+      <div class="video-source-buffer-info"></div>
+      <div class="audio-source-buffer-info"></div>
+      <div class="video-download-buffer-info"></div>
+      <div class="audio-download-buffer-info"></div>
+    `;
+  }
+
+  /** @override */
+  async afterRender() {
+    this.videoInfo = this.el.querySelector('.video-info') as HTMLVideoElement;
+    this.videoEl = document.createElement('video');
+    this.videoEl.style.width = '1280px';
+    this.videoEl.style.height = '720px';
+    this.videoEl.addEventListener('timeupdate', () => {
+      this.renderVideoInfo();
+    });
+    this.videoEl.addEventListener('durationchange', (evt) => {
+      this.renderVideoInfo();
+    });
+    this.el.querySelector('.video-container')!.appendChild(this.videoEl);
+
+    this.videoSourceBufferInfo =
+        this.el.querySelector('.video-source-buffer-info')!;
+    this.audioSourceBufferInfo =
+        this.el.querySelector('.audio-source-buffer-info')!;
+    this.videoDownloadBufferInfo =
+        this.el.querySelector('.video-download-buffer-info')!;
+    this.audioDownloadBufferInfo =
+        this.el.querySelector('.audio-download-buffer-info')!;
+    this.play();
+  }
+
+  private renderVideoInfo() {
+    renderComponent(
+        VideoInfo, {
+          duration: this.videoEl.duration,
+          currentTime: this.videoEl.currentTime,
+        },
+        this.videoInfo);
+  }
+
+  private async play() {
+    // Do not reorder methods.
+    await this.validateParams();
+    await this.initPlayMode();
+    await this.initSwitchMode(this.props.switchMode);
+    if (this.playMode === PlayMode.PROGRESSIVE) {
+      this.playProgressiveVideo();
+    } else {
+      this.playAdaptiveVideo();
+    }
+  }
+
+  private async initSwitchMode(override?: SwitchMode) {
+    // TODO: Add support for RELOAD and RECREATE_ELEMENT.
+    this.switchMode = SwitchMode.NORMAL;
+  }
+
+  private async initPlayMode() {
+    // Because `validateProgressive_` ensures progressive videos cannot be
+    // played together with adaptive audios/videos. We can decide whether to use
+    // progressive mode by checking the type of the first videos.
+    if (this.videos.length > 0 && await this.videos[0].isProgressive()) {
+      this.playMode = PlayMode.PROGRESSIVE;
+    } else {
+      this.playMode = PlayMode.ADAPTIVE;
+    }
+  }
+
+  /**
+   * Plays all videos as progressive videos, assuming only progressive mp4
+   * videos are provided.
+   */
+  private playProgressiveVideo(videoIndex = 0) {
+    const currentMedia = this.videos[videoIndex];
+    if (!currentMedia) {
+      return;
+    }
+    this.videoEl.src = currentMedia.url;
+    const handleVideoEnd = () => {
+      this.videoEl.removeEventListener('ended', handleVideoEnd);
+      this.playProgressiveVideo(videoIndex++);
+    };
+    this.videoEl.addEventListener('ended', handleVideoEnd);
+    this.videoEl.play();
+  }
+
+  /**
+   * Plays all videos as adaptive videos.
+   * TODO: dynmaically calculate the source buffer MIME.
+   */
+  private playAdaptiveVideo() {
+    const ms = new MediaSource();
+    this.videoEl.src = URL.createObjectURL(ms);
+    ms.addEventListener('sourceopen', async () => {
+      if (this.videos.length > 0) {
+        const videoSourceBuffer =
+            ms.addSourceBuffer('video/mp4; codecs="avc1.640028"');
+        videoSourceBuffer.addEventListener('updateend', () => {
+          renderComponent(
+              SourceBufferInfo,
+              {name: 'Video', sourceBuffer: videoSourceBuffer},
+              this.videoSourceBufferInfo);
+        });
+        const downloadBuffer = new DownloadBuffer(this.videos);
+        downloadBuffer.register((reportMap) => {
+          renderComponent(
+              DownloadBufferInfo, {mediaList: this.videos, reportMap},
+              this.videoDownloadBufferInfo);
+        });
+        new LimitedSourceBuffer(
+            this.videoEl, videoSourceBuffer, this.videos, downloadBuffer);
+      }
+      if (this.audios.length > 0) {
+        const audioSourceBuffer =
+            ms.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
+        audioSourceBuffer.addEventListener('updateend', () => {
+          renderComponent(
+              SourceBufferInfo,
+              {name: 'Audio', sourceBuffer: audioSourceBuffer},
+              this.audioSourceBufferInfo);
+        });
+        const downloadBuffer = new DownloadBuffer(this.audios);
+        downloadBuffer.register(
+            (reportMap) => {renderComponent(
+                DownloadBufferInfo, {mediaList: this.audios, reportMap},
+                this.audioDownloadBufferInfo)});
+        new LimitedSourceBuffer(
+            this.videoEl, audioSourceBuffer, this.audios, downloadBuffer);
+      }
+    });
+    this.videoEl.play();
+  }
+
+  private async validateParams() {
+    this.validateMediaExists();
+    await this.validateProgressive_();
+  }
+
+  /** Validates at least one video or audio is provided. */
+  private validateMediaExists() {
+    if (this.videos.length === 0 && this.audios.length === 0) {
+      throw new Error(
+          `No audio or video is specified. Please pass values to 'audio=' or ` +
+          `'video=' params.`);
+    }
+  }
+
+  /**
+   * Validates progressive videos are not played together with adaptive audios.
+   */
+  async validateProgressive_() {
+    let progressiveVideosCount = 0;
+    for (const video of this.videos) {
+      if (await video.isProgressive()) {
+        progressiveVideosCount++;
+      }
+    }
+    if (progressiveVideosCount > 0 &&
+        (progressiveVideosCount !== this.videos.length ||
+         this.audios.length > 0)) {
+      throw new Error(
+          'Progressive video[s] cannot be played together with adaptive ' +
+          'video[s] and audio[s]');
+    }
+  }
+}
+
+/** Converts filenames to a list of media. */
+function convertToMediaArray(filenames?: string|string[]): Media[] {
+  if (!filenames) {
+    return [];
+  }
+  if (!Array.isArray(filenames)) {
+    return [new Media(filenames)];
+  }
+  return filenames.map(filename => new Media(filename));
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/router.ts b/src/cobalt/demos/content/media-element-demo/src/components/router.ts
new file mode 100644
index 0000000..11e3d75
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/router.ts
@@ -0,0 +1,41 @@
+import {Component, renderComponent} from './component';
+
+import {Watch} from './watch';
+
+/** A component that parses URL params and decide what page to load. */
+export class Router extends Component<{}> {
+  /** @override */
+  render() {
+    return `<div class="router-content"></div>`;
+  }
+
+  /** @override */
+  afterRender() {
+    const params = parseParams();
+    renderComponent(Watch, params, this.el.querySelector('.router-content')!);
+  }
+}
+
+/**
+ * Converts the GET params to an object.
+ * @return The parsed params object.
+ */
+function parseParams(): Record<string, string|string[]> {
+  const params: Record<string, string|string[]> = {};
+  if (!location.search) {
+    return params;
+  }
+  const terms = location.search.slice(1).split('&');
+  for (const term of terms) {
+    const [key, value] = term.split('=');
+    if (params[key] !== undefined) {
+      throw new Error(`Detected multiple values for ${key}`);
+    }
+    if (value.includes(',')) {
+      params[key] = value.split(',');
+    } else {
+      params[key] = value;
+    }
+  }
+  return params;
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts b/src/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts
new file mode 100644
index 0000000..92343f3
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts
@@ -0,0 +1,9 @@
+interface Props {
+  sourceBuffer: SourceBuffer;
+  name: string;
+}
+
+/** A component that displays the source buffer info. */
+export function SourceBufferInfo({sourceBuffer, name}: Props) {
+  return `<div>${name} buffered: ${sourceBuffer.buffered.end(0)} sec</div>`;
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/video_info.ts b/src/cobalt/demos/content/media-element-demo/src/components/video_info.ts
new file mode 100644
index 0000000..3f8306f
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/video_info.ts
@@ -0,0 +1,9 @@
+interface Props {
+  duration: number;
+  currentTime: number;
+}
+
+/** A component that displays video info. */
+export function VideoInfo({duration, currentTime}: Props) {
+  return `<div>${currentTime} / ${duration}</div>`;
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/components/watch.ts b/src/cobalt/demos/content/media-element-demo/src/components/watch.ts
new file mode 100644
index 0000000..5503d44
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/components/watch.ts
@@ -0,0 +1,45 @@
+import {isCobalt} from '../utils/shared_values';
+import {Component, renderComponent} from './component';
+import {Player} from './player';
+
+/** TODO: Add correct watch props. */
+interface Props {}
+
+/** The watch page. */
+export class Watch extends Component<Props> {
+  /** @override */
+  render() {
+    return '<div class="player"></div>';
+  }
+
+  /** @override */
+  afterRender() {
+    // Mainstream browsers require users to interact with the document first
+    // before it can start play any video. This rule does not apply to Cobalt.
+    if (isCobalt) {
+      this.boostrapPage();
+    } else {
+      this.waitForInteraction();
+    }
+  }
+
+  /**
+   * Listens to any key event and displays a message. Bootstraps the page once
+   * user interacts with the docuemnt.
+   */
+  private waitForInteraction() {
+    const messageEl = document.createElement('h1');
+    messageEl.textContent = 'Press ANY KEY to start';
+    this.el.appendChild(messageEl);
+    const handler = () => {
+      window.removeEventListener('keyup', handler);
+      messageEl.parentElement!.removeChild(messageEl);
+      this.boostrapPage();
+    };
+    window.addEventListener('keyup', handler);
+  }
+
+  private boostrapPage() {
+    renderComponent(Player, this.props, this.el.querySelector('.player')!);
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/index.ts b/src/cobalt/demos/content/media-element-demo/src/index.ts
new file mode 100644
index 0000000..0554019
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/index.ts
@@ -0,0 +1,15 @@
+import * as Promise from 'bluebird';
+
+import {renderComponent} from './components/component';
+import {ErrorLogger} from './components/error_logger';
+import {Router} from './components/router';
+import {isCobalt} from './utils/shared_values';
+
+if (isCobalt) {
+  // Use bluebird as the default promise because Cobalt does not support
+  // unhandledrejection natively.
+  window.Promise = Promise as any;
+}
+
+renderComponent(ErrorLogger, {}, document.querySelector('#error-logger')!);
+renderComponent(Router, {}, document.querySelector('#router')!);
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/download_buffer.ts b/src/cobalt/demos/content/media-element-demo/src/utils/download_buffer.ts
new file mode 100644
index 0000000..8cbb218
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/download_buffer.ts
@@ -0,0 +1,187 @@
+import {download} from './downloader';
+import {Media} from './media';
+
+const CHUNK_SIZE = 1024 * 1024;  // 1MB
+const DOWNLOAD_BUFFER_MAX_SIZE = 10;
+
+type DownloadBufferListener = (results: Map<Media, MediaDownloaderStatus>) =>
+    void;
+
+/**
+ * Download buffer fetches the passed in media list chunk by chunk. It
+ * automatically pauses the download when the buffer reaches the limit and
+ * resumes when a chunk is taken out from the buffer.
+ */
+export class DownloadBuffer {
+  private isDownloading = false;
+  private currentMediaIndex = 0;
+  private listeners: DownloadBufferListener[] = [];
+  private downloaderMap = new Map<Media, MediaDownloader>();
+
+  constructor(
+      private readonly mediaList: Media[],
+      private readonly limit = DOWNLOAD_BUFFER_MAX_SIZE) {
+    for (const media of mediaList) {
+      this.downloaderMap.set(media, new MediaDownloader(media));
+    }
+    this.download();
+  }
+
+  /** Registers callback to listen to download buffer changes. */
+  register(callback: DownloadBufferListener) {
+    this.listeners.push(callback);
+    return () => {
+      const index = this.listeners.indexOf(callback);
+      if (index !== -1) {
+        this.listeners.splice(index, 1);
+      }
+    }
+  }
+
+  /** Calls listeners with the current media downloaders' status. */
+  private reportChanges() {
+    const result = new Map<Media, MediaDownloaderStatus>();
+    this.downloaderMap.forEach((downloader) => {
+      result.set(downloader.media, downloader.status());
+    });
+    for (const listener of this.listeners) {
+      listener(result);
+    }
+  }
+
+  /** @return The total buffer sizes of all media. */
+  size() {
+    let res = 0;
+    this.downloaderMap.forEach((downloader) => {
+      res += downloader.size();
+    });
+    return res;
+  }
+
+  private async download() {
+    if (this.isDownloading) {
+      return;
+    }
+    const media = this.mediaList[this.currentMediaIndex];
+    if (!media) {
+      // All media are processed.
+      return;
+    }
+    const downloader = this.downloaderMap.get(media);
+    if (!downloader) {
+      throw new Error(
+          `${media.url} does not have a downloader. This should not happen.`);
+    }
+    if (downloader.downloadFinished()) {
+      // The current media has finished downloading. Move on to the next one.
+      this.currentMediaIndex++;
+      this.download();
+      return;
+    }
+    this.isDownloading = true;
+    await downloader.downloadAndEnqueueNextChunk();
+    this.isDownloading = false;
+    this.reportChanges();
+    if (this.size() < this.limit) {
+      this.download();
+    }
+  }
+
+  /** Shifts a chunk of the requested media. */
+  shift(media: Media) {
+    return new Promise<ArrayBuffer|undefined>((resolve, reject) => {
+      const downloader = this.downloaderMap.get(media);
+      if (!downloader) {
+        throw new Error(
+            `${media.url} does not have a downloader. This should not happen.`);
+      }
+      const chunk = downloader.shift();
+      if (chunk) {
+        this.reportChanges();
+        this.download();
+        resolve(chunk);
+        return;
+      }
+      if (downloader.downloadFinished()) {
+        resolve();
+        return;
+      }
+      // The request media has not been downloaded. Wait until it is ready.
+      downloader.registerOnce(() => {
+        const chunk = downloader.shift();
+        if (chunk) {
+          this.reportChanges();
+          this.download();
+          resolve(chunk);
+        } else {
+          reject(new Error('Something is wrong'));
+        }
+      });
+    });
+  }
+}
+
+export interface MediaDownloaderStatus {
+  downloadedBytes: number;
+  queuedChunks: number;
+  finished: boolean;
+}
+
+/** Controls how to download a media. */
+class MediaDownloader {
+  private finished = false;
+  private chunks: ArrayBuffer[] = [];
+  private startingByte = 0;
+  private listeners: Array<() => void> = [];
+
+  constructor(readonly media: Media) {}
+
+  status(): MediaDownloaderStatus {
+    return {
+      downloadedBytes: this.startingByte,
+      queuedChunks: this.size(),
+      finished: this.downloadFinished(),
+    };
+  }
+
+  size() {
+    return this.chunks.length;
+  }
+
+  /**
+   * Listens when a chunk is enqueued. The callback will only be triggered
+   * once.
+   */
+  registerOnce(callback: () => void) {
+    this.listeners.push(callback);
+  }
+
+  downloadFinished(): boolean {
+    return this.finished;
+  }
+
+  shift(): ArrayBuffer|undefined {
+    return this.chunks.shift();
+  }
+
+  /** Downloads next chunk and enqueues the chunk to the buffer. */
+  async downloadAndEnqueueNextChunk() {
+    if (this.finished) {
+      throw new Error(`Attempt to download ${
+          this.media.url} after it has finished downloading.`);
+    }
+    const data = await download(
+        this.media.url, this.startingByte, this.startingByte + CHUNK_SIZE - 1);
+    if (data.byteLength === 0) {
+      console.log(`${this.media.url} is fully downloaded.`);
+      this.finished = true;
+      return;
+    }
+    console.log(`Downloaded ${this.media.url} at ${this.startingByte}`);
+    this.startingByte += data.byteLength;
+    this.chunks.push(data);
+    while (this.listeners.length > 0) {
+      this.listeners.shift()!();
+    }
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/downloader.ts b/src/cobalt/demos/content/media-element-demo/src/utils/downloader.ts
new file mode 100644
index 0000000..8d2cc96
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/downloader.ts
@@ -0,0 +1,20 @@
+import {logError} from '../components/error_logger';
+
+/**
+ * Downloads an asset with the provided URL.
+ * @param url Asset path.
+ * @param begin Start byte.
+ * @param end End byte.
+ */
+export async function download(
+    url: string, begin: number, end: number): Promise<ArrayBuffer> {
+  const response = await fetch(url, {
+    headers: {
+      'Range': `bytes=${begin}-${end}`,
+    },
+  });
+  if (response.status === 404) {
+    logError(`${url} does not exist.`);
+  }
+  return response.arrayBuffer();
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/enums.ts b/src/cobalt/demos/content/media-element-demo/src/utils/enums.ts
new file mode 100644
index 0000000..3877882
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/enums.ts
@@ -0,0 +1,16 @@
+/** A player config defines how to switch between videos. */
+export enum SwitchMode {
+  // Play adaptive videos under the same playback session using one media
+  // source. Play under different playback sessions if adaptive playback is not
+  // possible.
+  NORMAL = 'normal',
+  // Play adaptive videos under different playback sessions.
+  RELOAD = 'reload',
+  // Use a new video element when switching to the next media file.
+  RECREATE_ELEMENT = 'recreate-element',
+}
+
+export enum PlayMode {
+  PROGRESSIVE = 'progressive',
+  ADAPTIVE = 'adaptive',
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/limited_source_buffer.ts b/src/cobalt/demos/content/media-element-demo/src/utils/limited_source_buffer.ts
new file mode 100644
index 0000000..63f5aae
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/limited_source_buffer.ts
@@ -0,0 +1,87 @@
+import {DownloadBuffer} from './download_buffer';
+import {Media} from './media';
+
+const UNPLAYED_BUFFER_MAX_SIZE = 10;  // Only buffer 10 seconds maximum.
+
+/**
+ * A source buffer wrapper that limits the array buffers that can be added to
+ * the source buffer. If the bufferred length exceeds the limit, it will
+ * automatically pauses and waits until the remaining playback time is below the
+ * limit.
+ */
+export class LimitedSourceBuffer {
+  private scheduleTimeoutId = -1;
+  private currentMediaIndex = 0;
+
+  constructor(
+      private readonly videoEl: HTMLVideoElement,
+      private readonly sourceBuffer: SourceBuffer,
+      private readonly mediaList: Media[],
+      private readonly downloadBuffer: DownloadBuffer) {
+    this.appendNext();
+  }
+
+  /** Appends the next array buffer to the source buffer. */
+  async appendNext() {
+    const unplayedBufferLength = this.unplayedBufferLength();
+    if (unplayedBufferLength > UNPLAYED_BUFFER_MAX_SIZE) {
+      // Buffered too many content. Wait until we need to add buffer again.
+      const timeout = unplayedBufferLength - UNPLAYED_BUFFER_MAX_SIZE;
+      clearTimeout(this.scheduleTimeoutId);
+      console.log(`Schedule appendNext in ${timeout} sec.`);
+      this.scheduleTimeoutId =
+          setTimeout(() => this.appendNext(), timeout * 1000);
+      return;
+    }
+    const media = this.mediaList[this.currentMediaIndex];
+    if (!media) {
+      // All media are appended.
+      console.log('All media are appended');
+      return;
+    }
+    const chunk = await this.downloadBuffer.shift(media);
+    if (!chunk) {
+      // No more buffer left to append in the current media. Move on to the next
+      // one.
+      console.log(`${media.url} finished appending. Move on to the next one.`);
+      this.currentMediaIndex++;
+      const bufferedLength = this.sourceBuffer.buffered.end(0);
+      this.sourceBuffer.timestampOffset = bufferedLength;
+      this.appendNext();
+      return;
+    }
+    console.log(`Appending new chunk from ${media.url}`);
+    await this.appendChunkToBuffer(chunk);
+    this.appendNext();
+  }
+
+  /**
+   * Appends a chunk to the source buffer. Waits until the update is completed.
+   */
+  private async appendChunkToBuffer(chunk: ArrayBuffer) {
+    return new Promise((resolve, reject) => {
+      this.sourceBuffer.appendBuffer(chunk);
+      const unsubscribe = () => {
+        this.sourceBuffer.removeEventListener('updateend', onupdateend);
+        this.sourceBuffer.removeEventListener('error', onerror);
+      };
+      const onerror = () => {
+        unsubscribe();
+        reject(new Error('Append to buffer failed.'));
+      };
+      const onupdateend = () => {
+        unsubscribe();
+        resolve();
+      };
+      this.sourceBuffer.addEventListener('updateend', onupdateend);
+      this.sourceBuffer.addEventListener('error', onerror);
+    });
+  }
+
+  private unplayedBufferLength() {
+    if (this.sourceBuffer.buffered.length === 0) {
+      return 0;
+    }
+    return this.sourceBuffer.buffered.end(0) - this.videoEl.currentTime;
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/media.ts b/src/cobalt/demos/content/media-element-demo/src/utils/media.ts
new file mode 100644
index 0000000..5502b56
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/media.ts
@@ -0,0 +1,84 @@
+import {logError} from '../components/error_logger';
+
+import {download} from './downloader';
+
+/** A class that holds media metadata. */
+export class Media {
+  readonly url: string;
+  private metadata?: MediaMetadata;
+
+  constructor(filename: string) {
+    /** @type {string} Asset url. */
+    this.url = `assets/${filename}`;
+  }
+
+  async getMetadata(): Promise<MediaMetadata> {
+    if (!this.metadata) {
+      this.metadata = await this.fetchMetadata();
+    }
+    return this.metadata;
+  }
+
+  /**
+   * @return Whether the video is a progressive video.
+   */
+  async isProgressive(): Promise<boolean> {
+    const metadata = await this.getMetadata();
+    return metadata.type === 'mp42';
+  }
+
+  /** Downloads the head and builds metadata. */
+  private async fetchMetadata(): Promise<MediaMetadata> {
+    if (this.url.substr(-3) !== 'mp4') {
+      // TODO: Add more media type support.
+      return {};
+    }
+    // Metadata should be included in the first 10K.
+    const buffer = await download(this.url, 0, 1024 * 10);
+    if (buffer.byteLength === 0) {
+      return {};
+    }
+    const headBuffer = buffer.slice(0, getSize(buffer));
+    const type = getFtypType(headBuffer);
+    return {
+      type,
+    };
+  }
+}
+
+interface MediaMetadata {
+  /** The ftyp type. */
+  type?: string;
+}
+
+/**
+ * Gets the buffer size from the first 4 bytes.
+ * @return Buffer size.
+ */
+function getSize(buffer: ArrayBuffer): number {
+  const dv = new DataView(buffer.slice(0, 4));
+  return dv.getUint32(0);
+}
+
+/**
+ * Turns the buffer into a string.
+ * @return The generated string.
+ */
+function stringifyBuffer(
+    buffer: ArrayBuffer, begin: number, end: number): string {
+  return String.fromCharCode.apply(
+      null, Array.from(new Uint8Array(buffer.slice(begin, end))));
+}
+
+/**
+ * Gets the ftyp type from the buffer.
+ * @return The ftyp type. Undefined if there is an error.
+ */
+function getFtypType(buffer: ArrayBuffer): string|undefined {
+  const code = stringifyBuffer(buffer, 4, 8);
+  if (code !== 'ftyp') {
+    logError(`unknown type code ${code}`);
+    return;
+  }
+  return stringifyBuffer(buffer, 8, 12);
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/observable.ts b/src/cobalt/demos/content/media-element-demo/src/utils/observable.ts
new file mode 100644
index 0000000..63c451f
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/observable.ts
@@ -0,0 +1,50 @@
+type ObservableCallback<T> = (next: T, prev: T) => void;
+
+/**
+ * An observable value. Observers can attach a callback that gets called when
+ * the value changes.
+ */
+export class Observable<T> {
+  /** The actual value. */
+  private value: T;
+
+  /** Callbacks to trigger when the value changes. */
+  private callbacks: Array<ObservableCallback<T>> = [];
+
+  constructor(initValue: T) {
+    this.value = initValue;
+  }
+
+  /**
+   * Sets a new value and triggers the observer callbacks if the value changes.
+   */
+  set(value: T) {
+    if (this.value === value) {
+      return;
+    }
+
+    const oldValue = value;
+    this.value = value;
+    for (const callback of this.callbacks) {
+      callback(this.value, oldValue);
+    }
+  }
+
+  get(): T {
+    return this.value;
+  }
+
+  /**
+   * Triggers the callback on value changes.
+   * @return The unsubscriber.
+   */
+  observe(callback: ObservableCallback<T>): () => void {
+    this.callbacks.push(callback);
+    return () => {
+      const index = this.callbacks.indexOf(callback);
+      if (index !== -1) {
+        this.callbacks.splice(index, 1);
+      }
+    }
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/src/utils/shared_values.ts b/src/cobalt/demos/content/media-element-demo/src/utils/shared_values.ts
new file mode 100644
index 0000000..031b96c
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/src/utils/shared_values.ts
@@ -0,0 +1,5 @@
+/** This file contains singleton values shared by the application. */
+import {Observable} from './observable';
+
+export const errors = new Observable<string[]>([]);
+export const isCobalt = navigator.userAgent.indexOf('Cobalt') >= 0;
diff --git a/src/cobalt/demos/content/media-element-demo/tsconfig.json b/src/cobalt/demos/content/media-element-demo/tsconfig.json
new file mode 100644
index 0000000..afc9053
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/tsconfig.json
@@ -0,0 +1,15 @@
+{
+  "compilerOptions": {
+    "outDir": "./dist/",
+    "sourceMap": true,
+    "noImplicitAny": true,
+    "module": "es6",
+    "target": "es5",
+    "allowJs": true,
+    "strict": true,
+    "lib": [
+      "esnext",
+      "dom"
+    ],
+  }
+}
diff --git a/src/cobalt/demos/content/media-element-demo/webpack.config.js b/src/cobalt/demos/content/media-element-demo/webpack.config.js
new file mode 100644
index 0000000..ab9687c
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/webpack.config.js
@@ -0,0 +1,29 @@
+const path = require('path');
+const CopyPlugin = require('copy-webpack-plugin');
+
+module.exports = {
+  mode: 'development',
+  devtool: 'source-map',
+  module: {
+    rules: [{
+      test: /\.ts$/,
+      use: 'ts-loader',
+      exclude: /node_modules/,
+    }]
+  },
+  resolve: {
+    extensions: ['.ts', '.js'],
+  },
+  entry: './src/index.ts',
+  output: {
+    path: path.resolve(__dirname, 'dist'),
+    filename: 'bundle.js',
+  },
+  plugins: [
+    new CopyPlugin({
+      patterns: [
+        {from: 'public'},
+      ]
+    }),
+  ]
+};
diff --git a/src/cobalt/doc/splash_screen.md b/src/cobalt/doc/splash_screen.md
index 9122555..fbb6b6d 100644
--- a/src/cobalt/doc/splash_screen.md
+++ b/src/cobalt/doc/splash_screen.md
@@ -49,15 +49,15 @@
      first time.
 
   3. **Build-time fallback splash screen:** If a web cached splash screen is
-     unavailable and command line parameters are not passed by the system, a
-     `gyp_configuration.gypi` fallback splash screen may be used. Porters should
-     set the gypi variable `fallback_splash_screen_url` to the splash screen
-     URL.
+     unavailable and command line parameters are not passed by the system,
+     a CobaltExtensionConfigurationApi fallback splash screen may be used.
+     Porters should set the `CobaltFallbackSplashScreenUrl` value in
+     `configuration.cc` to the splash screen URL.
 
-  4. **Default splash screen:** If no web cached splash screen is
-     available, and command line and `gyp_configuration.gypi` fallbacks are not
-     set, a default splash screen will be used. This is set in `base.gypi` via
-     `fallback_splash_screen_url%` to refer to a black splash screen.
+  4. **Default splash screen:** If no web cached splash screen is available, and
+     command line and CobaltExtensionConfigurationApi fallbacks are not set, a
+     default splash screen will be used. This is set in
+     `configuration_defaults.cc` to refer to a black splash screen.
 
 ## Web-updatability
 
@@ -85,6 +85,40 @@
 Cobalt will also need to read the cached splash screen from the cache directory
 when starting up.
 
+## Topic-specific splash screens
+
+It is possible to specify multiple splash screens for a given Cobalt-based
+application, using a start-up 'topic' to select between the available splash
+screens. This can be useful when an application has multiple entry points that
+require different splash screens. The topic may be specified in the start-up url
+or deeplink as a query parameter. For example,
+`https://www.example.com/path?topic=foo`. If a splash-screen has been specified
+for topic 'foo', it will be used. Otherwise, the topic is ignored. Topic values
+should be URL encoded and limited to alphanumeric characters, hyphens,
+underscores, and percent signs.
+
+There are three ways to specify topic-specific splash screens. These methods mirror
+the types of splash screens listed above, and unless specified, the rules here
+are the same as for non-topic-based splash screens.
+
+  1. **Web cached splash screen:** A custom `rel="<topic>_splashscreen"`
+     attribute on a link element is used to specify a topic-specific splash
+     screen. There can be any number of these elements with different topics, in
+     addition to the topic-neutral `rel="splashscreen"`.
+
+  2. **Command line fallback splash screen:** The command line argument
+     `--fallback_splash_screen_topics` can be used if the cache is unavailable.
+     The argument accepts a list of topic/file parameters. If a file is not a
+     valid URL path, then it will be used as a filename at the path specified by
+     `--fallback_splash_screen_url`. For example,
+     `foo_topic=file:///foo.html&bar=bar.html`.
+
+  3. **Build-time fallback splash screen:** If a web cached splash screen is
+     unavailable and command line parameters are not passed by the system, a
+     CobaltExtensionConfigurationApi fallback splash screen may be used. Porters
+     should set the `CobaltFallbackSplashScreenTopics` value in
+     `configuration.cc` and this value should look like the command line option.
+
 ## Application-specific splash screens
 
 On systems that plan to support multiple Cobalt-based applications, an
@@ -94,9 +128,8 @@
 the Cobalt binary must be handled by the system.
 
 Alternatively, an application developer may use the default black splash screen
-specified in base.gypi whenever a cached splash screen is not available and rely
-on the web application to specify an application-specific cached splash screen
-otherwise.
+whenever a cached splash screen is not available and rely on the web application
+to specify an application-specific cached splash screen otherwise.
 
 ## Provided embedded resource splash screens
 For convenience, we currently provide the following splash screens as embedded
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
index ee0b08f..083c08f 100644
--- a/src/cobalt/dom/custom_event_test.cc
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -31,7 +31,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/loader_factory.h"
-#include "cobalt/media_session/media_session.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/source_code.h"
@@ -84,7 +83,7 @@
         kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
         dom::Window::CloseCallback() /* window_close */,
-        base::Closure() /* window_minimize */, NULL, NULL, NULL,
+        base::Closure() /* window_minimize */, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
         dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
index 937cd45..7f431a7 100644
--- a/src/cobalt/dom/error_event_test.cc
+++ b/src/cobalt/dom/error_event_test.cc
@@ -31,7 +31,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/loader_factory.h"
-#include "cobalt/media_session/media_session.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/source_code.h"
@@ -86,7 +85,7 @@
         kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
         dom::Window::CloseCallback() /* window_close */,
-        base::Closure() /* window_minimize */, NULL, NULL, NULL,
+        base::Closure() /* window_minimize */, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
         dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 1db3c22..e3f92a6 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <map>
 #include <memory>
+#include <utility>
 
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop_task_runner.h"
@@ -1032,7 +1033,6 @@
   // themselves still need to have their computed style updated, in case the
   // value of display is changed.
   if (computed_style()->display() == cssom::KeywordValue::GetNone()) {
-    ReleaseUiNavigationItem();
     return;
   }
 
@@ -1079,6 +1079,7 @@
     }
   }
 
+  ReleaseUiNavigationItem();
   MarkNotDisplayedOnDescendants();
 }
 
@@ -1521,8 +1522,7 @@
     if (property == U_LEFT_TO_RIGHT) {
       return HTMLElement::kDirLeftToRight;
     }
-    if (property == U_RIGHT_TO_LEFT ||
-        property == U_RIGHT_TO_LEFT_ARABIC) {
+    if (property == U_RIGHT_TO_LEFT || property == U_RIGHT_TO_LEFT_ARABIC) {
       return HTMLElement::kDirRightToLeft;
     }
   }
@@ -1615,11 +1615,11 @@
     return kDirLeftToRight;
   }
 
-  // Although the spec says to use the parent's directionality, the W3C test
-  // (the-dir-attribute-069.html) says to default to LTR. Chrome follows the
-  // W3C expectation, so follow Chrome. Additional discussion here:
-  //   https://github.com/w3c/i18n-drafts/issues/235
-  // The following code block which implements the spec is left for reference.
+// Although the spec says to use the parent's directionality, the W3C test
+// (the-dir-attribute-069.html) says to default to LTR. Chrome follows the
+// W3C expectation, so follow Chrome. Additional discussion here:
+//   https://github.com/w3c/i18n-drafts/issues/235
+// The following code block which implements the spec is left for reference.
 #if 0
   // Otherwise, the directionality of the element is the same as the element's
   //   parent element's directionality.
@@ -2106,8 +2106,7 @@
        element = element->next_element_sibling()) {
     HTMLElement* html_element = element->AsHTMLElement();
     if (html_element) {
-      HTMLMediaElement* media_html_element =
-          html_element->AsHTMLMediaElement();
+      HTMLMediaElement* media_html_element = html_element->AsHTMLMediaElement();
       if (media_html_element) {
         html_media_elements->push_back(media_html_element);
       }
@@ -2160,7 +2159,7 @@
     ui_nav_item_type = ui_navigation::kNativeItemTypeContainer;
   }
 
-  if (ui_nav_item_type) {
+  if (ui_nav_item_type && IsDisplayed()) {
     ui_navigation::NativeItemDir ui_nav_item_dir;
     ui_nav_item_dir.is_left_to_right =
         directionality() == kLeftToRightDirectionality;
@@ -2175,12 +2174,7 @@
       // The current navigation item isn't of the correct type. Disable it so
       // that callbacks won't be invoked for it. The object will be destroyed
       // when all references to it are released.
-      if (g_ui_nav_focus_ == this) {
-        g_ui_nav_focus_ = nullptr;
-        ui_nav_item_->UnfocusAll();
-      }
-      ui_nav_item_->SetEnabled(false);
-      ui_nav_item_ = nullptr;
+      ReleaseUiNavigationItem();
     }
 
     ui_nav_item_ = new ui_navigation::NavItem(
@@ -2204,12 +2198,7 @@
     return false;
   } else if (ui_nav_item_) {
     // This navigation item is no longer relevant.
-    if (g_ui_nav_focus_ == this) {
-      g_ui_nav_focus_ = nullptr;
-      ui_nav_item_->UnfocusAll();
-    }
-    ui_nav_item_->SetEnabled(false);
-    ui_nav_item_ = nullptr;
+    ReleaseUiNavigationItem();
     return false;
   }
 
@@ -2218,13 +2207,6 @@
 
 void HTMLElement::ReleaseUiNavigationItem() {
   if (ui_nav_item_) {
-    // Make sure layout updates this element.
-    InvalidateLayoutBoxesOfNodeAndAncestors();
-    if (ui_nav_item_->IsContainer()) {
-      // Make sure layout updates any focus items that may be in this container.
-      InvalidateLayoutBoxesOfDescendants();
-    }
-
     // Disable the UI navigation item so it won't receive anymore callbacks
     // while being released.
     if (g_ui_nav_focus_ == this) {
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index a6d3ce3..8b8d5f6 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -145,14 +145,13 @@
 
 Navigator::Navigator(
     script::EnvironmentSettings* settings, const std::string& user_agent,
-    const std::string& language, scoped_refptr<MediaSession> media_session,
+    const std::string& language,
     scoped_refptr<cobalt::dom::captions::SystemCaptionSettings> captions,
     script::ScriptValueFactory* script_value_factory)
     : user_agent_(user_agent),
       language_(language),
       mime_types_(new MimeTypeArray()),
       plugins_(new PluginArray()),
-      media_session_(media_session),
       media_devices_(
           new media_capture::MediaDevices(settings, script_value_factory)),
       system_caption_settings_(captions),
@@ -223,8 +222,20 @@
   return plugins_;
 }
 
-const scoped_refptr<media_session::MediaSession>& Navigator::media_session()
-    const {
+const scoped_refptr<media_session::MediaSession>& Navigator::media_session() {
+  if (media_session_ == nullptr) {
+    media_session_ =
+        scoped_refptr<media_session::MediaSession>(new MediaSession());
+
+    if (media_player_factory_ != nullptr) {
+      media_session_->EnsureMediaSessionClient();
+      DCHECK(media_session_->media_session_client());
+      media_session_->media_session_client()
+          ->SetMaybeFreezeCallback(maybe_freeze_callback_);
+      media_session_->media_session_client()
+          ->SetMediaPlayerFactory(media_player_factory_);
+    }
+  }
   return media_session_;
 }
 
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 1489f78..348f624 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -23,6 +23,7 @@
 #include "cobalt/dom/eme/media_key_system_configuration.h"
 #include "cobalt/dom/mime_type_array.h"
 #include "cobalt/dom/plugin_array.h"
+#include "cobalt/media/web_media_player_factory.h"
 #include "cobalt/media_capture/media_devices.h"
 #include "cobalt/media_session/media_session.h"
 #include "cobalt/script/promise.h"
@@ -42,7 +43,6 @@
   Navigator(
       script::EnvironmentSettings* settings, const std::string& user_agent,
       const std::string& language,
-      scoped_refptr<cobalt::media_session::MediaSession> media_session,
       scoped_refptr<cobalt::dom::captions::SystemCaptionSettings> captions,
       script::ScriptValueFactory* script_value_factory);
 
@@ -67,8 +67,17 @@
   const scoped_refptr<MimeTypeArray>& mime_types() const;
   const scoped_refptr<PluginArray>& plugins() const;
 
-  const scoped_refptr<cobalt::media_session::MediaSession>& media_session()
-      const;
+  const scoped_refptr<media_session::MediaSession>& media_session();
+
+  // Set maybe freeze callback.
+  void set_maybefreeze_callback(const base::Closure& maybe_freeze_callback) {
+    maybe_freeze_callback_ = maybe_freeze_callback;
+  }
+
+  void set_media_player_factory(
+      const media::WebMediaPlayerFactory* factory) {
+    media_player_factory_ = factory;
+  }
 
   // Web API: extension defined in Encrypted Media Extensions (16 March 2017).
   using InterfacePromise = script::Promise<scoped_refptr<script::Wrappable>>;
@@ -124,6 +133,9 @@
   script::ScriptValueFactory* script_value_factory_;
   base::Optional<bool> key_system_with_attributes_supported_;
 
+  base::Closure maybe_freeze_callback_;
+  const media::WebMediaPlayerFactory* media_player_factory_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(Navigator);
 };
 
diff --git a/src/cobalt/dom/navigator_licenses_test.cc b/src/cobalt/dom/navigator_licenses_test.cc
index d7f2172..cc55b6b 100644
--- a/src/cobalt/dom/navigator_licenses_test.cc
+++ b/src/cobalt/dom/navigator_licenses_test.cc
@@ -24,7 +24,7 @@
   testing::StubEnvironmentSettings environment_settings;
   scoped_refptr<cobalt::dom::Navigator> navigator =
       new cobalt::dom::Navigator(&environment_settings, std::string(),
-                                 std::string(), nullptr, nullptr, nullptr);
+                                 std::string(), nullptr, nullptr);
 
   ASSERT_TRUE(navigator != nullptr);
   EXPECT_FALSE(navigator->licenses().empty());
diff --git a/src/cobalt/dom/on_screen_keyboard_test.cc b/src/cobalt/dom/on_screen_keyboard_test.cc
index 9493146..701c842 100644
--- a/src/cobalt/dom/on_screen_keyboard_test.cc
+++ b/src/cobalt/dom/on_screen_keyboard_test.cc
@@ -30,7 +30,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/loader_factory.h"
-#include "cobalt/media_session/media_session.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/source_code.h"
@@ -222,7 +221,7 @@
             base::Closure() /* ran_animation_frame_callbacks */,
             dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */,
-            on_screen_keyboard_bridge_.get(), NULL, NULL,
+            on_screen_keyboard_bridge_.get(), NULL,
             dom::Window::OnStartDispatchEventCallback(),
             dom::Window::OnStopDispatchEventCallback(),
             dom::ScreenshotManager::ProvideScreenshotFunctionCallback(),
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index 7377579..2631d15 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -32,7 +32,6 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/loader_factory.h"
-#include "cobalt/media_session/media_session.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
 #include "starboard/window.h"
@@ -80,7 +79,7 @@
         dom::kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
         dom::Window::CloseCallback() /* window_close */,
-        base::Closure() /* window_minimize */, NULL, NULL, NULL,
+        base::Closure() /* window_minimize */, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
         dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 4f89140..2ee9432 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -124,7 +124,6 @@
     const base::Closure& window_minimize_callback,
     OnScreenKeyboardBridge* on_screen_keyboard_bridge,
     const scoped_refptr<input::Camera3D>& camera_3d,
-    const scoped_refptr<MediaSession>& media_session,
     const OnStartDispatchEventCallback& on_start_dispatch_event_callback,
     const OnStopDispatchEventCallback& on_stop_dispatch_event_callback,
     const ScreenshotManager::ProvideScreenshotFunctionCallback&
@@ -168,7 +167,7 @@
               csp_insecure_allowed_token, dom_max_element_depth)))),
       document_loader_(nullptr),
       history_(new History()),
-      navigator_(new Navigator(settings, user_agent, language, media_session,
+      navigator_(new Navigator(settings, user_agent, language,
                                captions, script_value_factory)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           relay_on_load_event_(new RelayLoadEvent(this))),
@@ -705,6 +704,11 @@
   tracer->Trace(on_screen_keyboard_);
 }
 
+const scoped_refptr<media_session::MediaSession>
+    Window::media_session() const {
+  return navigator_->media_session();
+}
+
 void Window::CacheSplashScreen(const std::string& content,
                                const base::Optional<std::string>& topic) {
   if (splash_screen_cache_callback_.is_null()) {
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index de6fce2..c572407 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -167,7 +167,6 @@
       const base::Closure& window_minimize_callback,
       OnScreenKeyboardBridge* on_screen_keyboard_bridge,
       const scoped_refptr<input::Camera3D>& camera_3d,
-      const scoped_refptr<cobalt::media_session::MediaSession>& media_session,
       const OnStartDispatchEventCallback&
           start_tracking_dispatch_event_callback,
       const OnStopDispatchEventCallback& stop_tracking_dispatch_event_callback,
@@ -355,9 +354,9 @@
   void SetCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
 
   void set_web_media_player_factory(
-      media::WebMediaPlayerFactory* web_media_player_factory) {
-    html_element_context_->set_web_media_player_factory(
-        web_media_player_factory);
+    media::WebMediaPlayerFactory* web_media_player_factory) {
+  html_element_context_->set_web_media_player_factory(
+      web_media_player_factory);
   }
 
   // Sets the current application state, forwarding on to the
@@ -408,6 +407,9 @@
 
   bool enable_map_to_mesh() { return enable_map_to_mesh_; }
 
+  const scoped_refptr<media_session::MediaSession>
+      media_session() const;
+
   DEFINE_WRAPPABLE_TYPE(Window);
 
  private:
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index 8e44fb0..2b1b96f 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -29,7 +29,6 @@
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
-#include "cobalt/media_session/media_session.h"
 #include "cobalt/network_bridge/net_poster.h"
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/javascript_engine.h"
@@ -74,7 +73,7 @@
         kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
         dom::Window::CloseCallback() /* window_close */,
-        base::Closure() /* window_minimize */, NULL, NULL, NULL,
+        base::Closure() /* window_minimize */, NULL, NULL,
         dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
         dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
diff --git a/src/cobalt/extension/extension_test.cc b/src/cobalt/extension/extension_test.cc
index 38b7c83..d92acd1 100644
--- a/src/cobalt/extension/extension_test.cc
+++ b/src/cobalt/extension/extension_test.cc
@@ -30,23 +30,22 @@
   typedef CobaltExtensionPlatformServiceApi ExtensionApi;
   const char* kExtensionName = kCobaltExtensionPlatformServiceName;
 
-  const ExtensionApi* extension_api = static_cast<const ExtensionApi*>(
-      SbSystemGetExtension(kExtensionName));
+  const ExtensionApi* extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
   if (!extension_api) {
     return;
   }
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
-  EXPECT_TRUE(extension_api->version == 1 ||
-              extension_api->version == 2 ||
-              extension_api->version == 3) << "Invalid version";
-  EXPECT_TRUE(extension_api->Has != NULL);
-  EXPECT_TRUE(extension_api->Open != NULL);
-  EXPECT_TRUE(extension_api->Close != NULL);
-  EXPECT_TRUE(extension_api->Send != NULL);
+  EXPECT_GE(extension_api->version, 1u);
+  EXPECT_LE(extension_api->version, 3u);
+  EXPECT_NE(extension_api->Has, nullptr);
+  EXPECT_NE(extension_api->Open, nullptr);
+  EXPECT_NE(extension_api->Close, nullptr);
+  EXPECT_NE(extension_api->Send, nullptr);
 
-  const ExtensionApi* second_extension_api = static_cast<const ExtensionApi*>(
-      SbSystemGetExtension(kExtensionName));
+  const ExtensionApi* second_extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
   EXPECT_EQ(second_extension_api, extension_api)
       << "Extension struct should be a singleton";
 }
@@ -55,35 +54,50 @@
   typedef CobaltExtensionGraphicsApi ExtensionApi;
   const char* kExtensionName = kCobaltExtensionGraphicsName;
 
-  const ExtensionApi* extension_api = static_cast<const ExtensionApi*>(
-      SbSystemGetExtension(kExtensionName));
+  const ExtensionApi* extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
   if (!extension_api) {
     return;
   }
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
-  EXPECT_TRUE(extension_api->version == 1 ||
-              extension_api->version == 2 ||
-              extension_api->version == 3) << "Invalid version";
-  EXPECT_TRUE(extension_api->GetMaximumFrameIntervalInMilliseconds != NULL);
-  if (extension_api->version >= 2) {
-    EXPECT_TRUE(extension_api->GetMinimumFrameIntervalInMilliseconds != NULL);
-  }
-  if (extension_api->version >= 3) {
-    EXPECT_TRUE(extension_api->IsMapToMeshEnabled != NULL);
-  }
+  EXPECT_GE(extension_api->version, 1u);
+  EXPECT_LE(extension_api->version, 4u);
 
+  EXPECT_NE(extension_api->GetMaximumFrameIntervalInMilliseconds, nullptr);
   float maximum_frame_interval =
       extension_api->GetMaximumFrameIntervalInMilliseconds();
   EXPECT_FALSE(std::isnan(maximum_frame_interval));
 
   if (extension_api->version >= 2) {
+    EXPECT_NE(extension_api->GetMinimumFrameIntervalInMilliseconds, nullptr);
     float minimum_frame_interval =
-      extension_api->GetMinimumFrameIntervalInMilliseconds();
+        extension_api->GetMinimumFrameIntervalInMilliseconds();
     EXPECT_GT(minimum_frame_interval, 0);
   }
-  const ExtensionApi* second_extension_api = static_cast<const ExtensionApi*>(
-      SbSystemGetExtension(kExtensionName));
+
+  if (extension_api->version >= 3) {
+    EXPECT_NE(extension_api->IsMapToMeshEnabled, nullptr);
+  }
+
+  if (extension_api->version >= 4) {
+    EXPECT_NE(extension_api->ShouldClearFrameOnShutdown, nullptr);
+    float clear_color_r, clear_color_g, clear_color_b, clear_color_a;
+    if (extension_api->ShouldClearFrameOnShutdown(
+            &clear_color_r, &clear_color_g, &clear_color_b, &clear_color_a)) {
+      EXPECT_GE(clear_color_r, 0.0f);
+      EXPECT_LE(clear_color_r, 1.0f);
+      EXPECT_GE(clear_color_g, 0.0f);
+      EXPECT_LE(clear_color_g, 1.0f);
+      EXPECT_GE(clear_color_b, 0.0f);
+      EXPECT_LE(clear_color_b, 1.0f);
+      EXPECT_GE(clear_color_a, 0.0f);
+      EXPECT_LE(clear_color_a, 1.0f);
+    }
+  }
+
+  const ExtensionApi* second_extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
   EXPECT_EQ(second_extension_api, extension_api)
       << "Extension struct should be a singleton";
 }
@@ -99,18 +113,17 @@
   }
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
-  EXPECT_TRUE(extension_api->version == 1 ||
-              extension_api->version == 2 ||
-              extension_api->version == 3) << "Invalid version";
-  EXPECT_TRUE(extension_api->GetCurrentInstallationIndex != NULL);
-  EXPECT_TRUE(extension_api->MarkInstallationSuccessful != NULL);
-  EXPECT_TRUE(extension_api->RequestRollForwardToInstallation != NULL);
-  EXPECT_TRUE(extension_api->GetInstallationPath != NULL);
-  EXPECT_TRUE(extension_api->SelectNewInstallationIndex != NULL);
-  EXPECT_TRUE(extension_api->GetAppKey != NULL);
-  EXPECT_TRUE(extension_api->GetMaxNumberInstallations != NULL);
-  EXPECT_TRUE(extension_api->ResetInstallation != NULL);
-  EXPECT_TRUE(extension_api->Reset != NULL);
+  EXPECT_GE(extension_api->version, 1u);
+  EXPECT_LE(extension_api->version, 3u);
+  EXPECT_NE(extension_api->GetCurrentInstallationIndex, nullptr);
+  EXPECT_NE(extension_api->MarkInstallationSuccessful, nullptr);
+  EXPECT_NE(extension_api->RequestRollForwardToInstallation, nullptr);
+  EXPECT_NE(extension_api->GetInstallationPath, nullptr);
+  EXPECT_NE(extension_api->SelectNewInstallationIndex, nullptr);
+  EXPECT_NE(extension_api->GetAppKey, nullptr);
+  EXPECT_NE(extension_api->GetMaxNumberInstallations, nullptr);
+  EXPECT_NE(extension_api->ResetInstallation, nullptr);
+  EXPECT_NE(extension_api->Reset, nullptr);
   const ExtensionApi* second_extension_api =
       static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
   EXPECT_EQ(second_extension_api, extension_api)
@@ -128,29 +141,31 @@
   }
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
-  EXPECT_TRUE(extension_api->version == 1 || extension_api->version == 2);
-  EXPECT_TRUE(extension_api->CobaltUserOnExitStrategy != NULL);
-  EXPECT_TRUE(extension_api->CobaltRenderDirtyRegionOnly != NULL);
-  EXPECT_TRUE(extension_api->CobaltEglSwapInterval != NULL);
-  EXPECT_TRUE(extension_api->CobaltFallbackSplashScreenUrl != NULL);
-  EXPECT_TRUE(extension_api->CobaltEnableQuic != NULL);
-  EXPECT_TRUE(extension_api->CobaltSkiaCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltOffscreenTargetCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltEncodedImageCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltImageCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltLocalTypefaceCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltRemoteTypefaceCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltMeshCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltSoftwareSurfaceCacheSizeInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltImageCacheCapacityMultiplierWhenPlayingVideo != NULL);
-  EXPECT_TRUE(extension_api->CobaltSkiaGlyphAtlasWidth != NULL);
-  EXPECT_TRUE(extension_api->CobaltSkiaGlyphAtlasHeight != NULL);
-  EXPECT_TRUE(extension_api->CobaltJsGarbageCollectionThresholdInBytes != NULL);
-  EXPECT_TRUE(extension_api->CobaltReduceCpuMemoryBy != NULL);
-  EXPECT_TRUE(extension_api->CobaltReduceGpuMemoryBy != NULL);
-  EXPECT_TRUE(extension_api->CobaltGcZeal != NULL);
+  EXPECT_GE(extension_api->version, 1u);
+  EXPECT_LE(extension_api->version, 2u);
+  EXPECT_NE(extension_api->CobaltUserOnExitStrategy, nullptr);
+  EXPECT_NE(extension_api->CobaltRenderDirtyRegionOnly, nullptr);
+  EXPECT_NE(extension_api->CobaltEglSwapInterval, nullptr);
+  EXPECT_NE(extension_api->CobaltFallbackSplashScreenUrl, nullptr);
+  EXPECT_NE(extension_api->CobaltEnableQuic, nullptr);
+  EXPECT_NE(extension_api->CobaltSkiaCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltOffscreenTargetCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltEncodedImageCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltImageCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltLocalTypefaceCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltRemoteTypefaceCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltMeshCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltSoftwareSurfaceCacheSizeInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltImageCacheCapacityMultiplierWhenPlayingVideo,
+            nullptr);
+  EXPECT_NE(extension_api->CobaltSkiaGlyphAtlasWidth, nullptr);
+  EXPECT_NE(extension_api->CobaltSkiaGlyphAtlasHeight, nullptr);
+  EXPECT_NE(extension_api->CobaltJsGarbageCollectionThresholdInBytes, nullptr);
+  EXPECT_NE(extension_api->CobaltReduceCpuMemoryBy, nullptr);
+  EXPECT_NE(extension_api->CobaltReduceGpuMemoryBy, nullptr);
+  EXPECT_NE(extension_api->CobaltGcZeal, nullptr);
   if (extension_api->version >= 2) {
-    EXPECT_TRUE(extension_api->CobaltFallbackSplashScreenTopics != NULL);
+    EXPECT_NE(extension_api->CobaltFallbackSplashScreenTopics, nullptr);
   }
 
   const ExtensionApi* second_extension_api =
@@ -170,8 +185,8 @@
   }
 
   EXPECT_STREQ(extension_api->name, kExtensionName);
-  EXPECT_TRUE(extension_api->version == 1);
-  EXPECT_TRUE(extension_api->OnMediaSessionStateChanged != NULL);
+  EXPECT_EQ(extension_api->version, 1u);
+  EXPECT_NE(extension_api->OnMediaSessionStateChanged, nullptr);
 
   const ExtensionApi* second_extension_api =
       static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
diff --git a/src/cobalt/extension/graphics.h b/src/cobalt/extension/graphics.h
index 9c993c5..a1a3b4e 100644
--- a/src/cobalt/extension/graphics.h
+++ b/src/cobalt/extension/graphics.h
@@ -23,8 +23,7 @@
 extern "C" {
 #endif
 
-#define kCobaltExtensionGraphicsName \
-  "dev.cobalt.extension.Graphics"
+#define kCobaltExtensionGraphicsName "dev.cobalt.extension.Graphics"
 
 typedef struct CobaltExtensionGraphicsApi {
   // Name should be the string kCobaltExtensionGraphicsName.
@@ -62,6 +61,20 @@
 
   // Get whether the renderer should support 360 degree video or not.
   bool (*IsMapToMeshEnabled)();
+
+  // The fields below this point were added in version 4 or later.
+
+  // Specify whether the framebuffer should be cleared when the graphics
+  // system is shutdown and color to use for clearing. The graphics system
+  // is shutdown on suspend or exit. The clear color values should be in the
+  // range of [0,1]; color values are only used if this function returns true.
+  //
+  // The default behavior is to clear to opaque black on shutdown unless this
+  // API specifies otherwise.
+  bool (*ShouldClearFrameOnShutdown)(float* clear_color_red,
+                                     float* clear_color_green,
+                                     float* clear_color_blue,
+                                     float* clear_color_alpha);
 } CobaltExtensionGraphicsApi;
 
 #ifdef __cplusplus
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
index df2cd6a..8801fef 100644
--- a/src/cobalt/layout/topmost_event_target.cc
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -241,9 +241,11 @@
         for (scoped_refptr<dom::Element> element = target_element;
              element != nearest_common_ancestor;
              element = element->parent_element()) {
-          element->DispatchEvent(new dom::PointerEvent(
-              base::Tokens::pointerenter(), dom::Event::kNotBubbles,
-              dom::Event::kNotCancelable, view, *event_init));
+          if (element) {
+            element->DispatchEvent(new dom::PointerEvent(
+                base::Tokens::pointerenter(), dom::Event::kNotBubbles,
+                dom::Event::kNotCancelable, view, *event_init));
+          }
         }
       }
 
@@ -254,9 +256,11 @@
       for (scoped_refptr<dom::Element> element = target_element;
            element != nearest_common_ancestor;
            element = element->parent_element()) {
-        element->DispatchEvent(new dom::MouseEvent(
-            base::Tokens::mouseenter(), dom::Event::kNotBubbles,
-            dom::Event::kNotCancelable, view, *event_init));
+        if (element) {
+          element->DispatchEvent(new dom::MouseEvent(
+              base::Tokens::mouseenter(), dom::Event::kNotBubbles,
+              dom::Event::kNotCancelable, view, *event_init));
+        }
       }
     }
   }
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 68abc49..143b238 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -280,8 +280,13 @@
 
   VideoFrameProvider* video_frame_provider_;
 
+  // Read audio from the stream if |timestamp_of_last_written_audio_| is less
+  // than |seek_time_| + |kAudioPrerollLimit|, this effectively allows 10
+  // seconds of audio to be written to the SbPlayer after playback startup or
+  // seek.
+  static const SbTime kAudioPrerollLimit = 10 * kSbTimeSecond;
   // Don't read audio from the stream more than |kAudioLimit| ahead of the
-  // current media time.
+  // current media time during playing.
   static const SbTime kAudioLimit = kSbTimeSecond;
   // Only call GetMediaTime() from OnNeedData if it has been
   // |kMediaTimeCheckInterval| since the last call to GetMediaTime().
@@ -1121,22 +1126,28 @@
         kMediaTimeCheckInterval) {
       GetMediaTime();
     }
-    // The estimated time ahead of playback may be negative if no audio has been
-    // written.
-    SbTime time_ahead_of_playback =
-        timestamp_of_last_written_audio_ - last_media_time_;
-    // Delay reading audio more than |kAudioLimit| ahead of playback, taking
-    // into account that our estimate of playback time might be behind by
+
+    // Delay reading audio more than |kAudioLimit| ahead of playback after the
+    // player has received enough audio for preroll, taking into account that
+    // our estimate of playback time might be behind by
     // |kMediaTimeCheckInterval|.
-    if (time_ahead_of_playback > (kAudioLimit + kMediaTimeCheckInterval)) {
-      SbTime delay_time = (time_ahead_of_playback - kAudioLimit) /
-                          std::max(playback_rate_, 1.0f);
-      task_runner_->PostDelayedTask(
-          FROM_HERE, base::Bind(&SbPlayerPipeline::DelayedNeedData, this),
-          base::TimeDelta::FromMicroseconds(delay_time));
-      audio_read_delayed_ = true;
-      return;
+    if (timestamp_of_last_written_audio_ - seek_time_.ToSbTime() >
+        kAudioPrerollLimit) {
+      // The estimated time ahead of playback may be negative if no audio has
+      // been written.
+      SbTime time_ahead_of_playback =
+          timestamp_of_last_written_audio_ - last_media_time_;
+      if (time_ahead_of_playback > (kAudioLimit + kMediaTimeCheckInterval)) {
+        SbTime delay_time = (time_ahead_of_playback - kAudioLimit) /
+                            std::max(playback_rate_, 1.0f);
+        task_runner_->PostDelayedTask(
+            FROM_HERE, base::Bind(&SbPlayerPipeline::DelayedNeedData, this),
+            base::TimeDelta::FromMicroseconds(delay_time));
+        audio_read_delayed_ = true;
+        return;
+      }
     }
+
     audio_read_delayed_ = false;
 #endif  // SB_API_VERSION >= 11
     audio_read_in_progress_ = true;
diff --git a/src/cobalt/media_session/default_media_session_client.cc b/src/cobalt/media_session/default_media_session_client.cc
index 0b4fbac..b1a7c47 100644
--- a/src/cobalt/media_session/default_media_session_client.cc
+++ b/src/cobalt/media_session/default_media_session_client.cc
@@ -36,4 +36,4 @@
 }  // namespace media_session
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
\ No newline at end of file
+#endif  // COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
diff --git a/src/cobalt/media_session/media_session.cc b/src/cobalt/media_session/media_session.cc
index b9d04a0..acc4070 100644
--- a/src/cobalt/media_session/media_session.cc
+++ b/src/cobalt/media_session/media_session.cc
@@ -19,9 +19,8 @@
 namespace cobalt {
 namespace media_session {
 
-MediaSession::MediaSession(MediaSessionClient* client)
-    : media_session_client_(client),
-      playback_state_(kMediaSessionPlaybackStateNone),
+MediaSession::MediaSession()
+    : playback_state_(kMediaSessionPlaybackStateNone),
       task_runner_(base::MessageLoop::current()->task_runner()),
       is_change_task_queued_(false),
       last_position_updated_time_(0) {}
@@ -34,6 +33,13 @@
   action_map_.clear();
 }
 
+MediaSession::MediaSession(MediaSessionClient* client)
+    : media_session_client_(client),
+      playback_state_(kMediaSessionPlaybackStateNone),
+      task_runner_(base::MessageLoop::current()->task_runner()),
+      is_change_task_queued_(false),
+      last_position_updated_time_(0) {}
+
 void MediaSession::set_metadata(scoped_refptr<MediaMetadata> value) {
   metadata_ = value;
   MaybeQueueChangeTask(base::TimeDelta());
@@ -77,6 +83,14 @@
   return is_change_task_queued_;
 }
 
+void MediaSession::EnsureMediaSessionClient() {
+  if (media_session_client_ == nullptr) {
+    media_session_client_ = media_session::MediaSessionClient::Create();
+    DCHECK(media_session_client_);
+    media_session_client_->set_media_session(this);
+  }
+}
+
 void MediaSession::MaybeQueueChangeTask(base::TimeDelta delay) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   if (is_change_task_queued_) {
diff --git a/src/cobalt/media_session/media_session.h b/src/cobalt/media_session/media_session.h
index 6c1db11..c83b1df 100644
--- a/src/cobalt/media_session/media_session.h
+++ b/src/cobalt/media_session/media_session.h
@@ -21,6 +21,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
+#include "cobalt/media/web_media_player_factory.h"
 #include "cobalt/media_session/media_metadata.h"
 #include "cobalt/media_session/media_position_state.h"
 #include "cobalt/media_session/media_session_action.h"
@@ -58,9 +59,11 @@
       ActionMap;
 
  public:
-  explicit MediaSession(MediaSessionClient* client);
+  MediaSession();
   ~MediaSession() override;
 
+  explicit MediaSession(MediaSessionClient* client);
+
   scoped_refptr<MediaMetadata> metadata() const { return metadata_; }
 
   void set_metadata(scoped_refptr<MediaMetadata> value);
@@ -81,6 +84,12 @@
   // unit tests.
   bool IsChangeTaskQueuedForTesting() const;
 
+  MediaSessionClient* media_session_client() {
+    return media_session_client_.get();
+  }
+
+  void EnsureMediaSessionClient();
+
  private:
   void MaybeQueueChangeTask(base::TimeDelta delay);
   void OnChanged();
@@ -91,7 +100,7 @@
   }
 
   ActionMap action_map_;
-  MediaSessionClient* media_session_client_;
+  std::unique_ptr<MediaSessionClient> media_session_client_;
   scoped_refptr<MediaMetadata> metadata_;
   MediaSessionPlaybackState playback_state_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
index 9243556..dc767a3 100644
--- a/src/cobalt/media_session/media_session_client.cc
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -67,7 +67,7 @@
 }  // namespace
 
 MediaSessionClient::MediaSessionClient(
-    scoped_refptr<MediaSession> media_session)
+    MediaSession* media_session)
     : media_session_(media_session),
       platform_playback_state_(kMediaSessionPlaybackStateNone) {
 #if SB_API_VERSION < 11
@@ -91,8 +91,6 @@
 
 MediaSessionClient::~MediaSessionClient() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // Prevent any outstanding MediaSession::OnChanged tasks from calling this.
-  media_session_->media_session_client_ = nullptr;
 
   // Destroy the platform's MediaSessionClient, if it exists.
   if (extension_ != NULL &&
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
index 76883d9..4961a91 100644
--- a/src/cobalt/media_session/media_session_client.h
+++ b/src/cobalt/media_session/media_session_client.h
@@ -35,10 +35,9 @@
   friend class MediaSession;
 
  public:
-  MediaSessionClient() : MediaSessionClient(new MediaSession(this)) {}
-
+  MediaSessionClient(): MediaSessionClient(nullptr) {}
   // Injectable MediaSession for tests.
-  explicit MediaSessionClient(scoped_refptr<MediaSession> media_session);
+  explicit MediaSessionClient(MediaSession* media_session);
 
   virtual ~MediaSessionClient();
 
@@ -46,7 +45,7 @@
   static std::unique_ptr<MediaSessionClient> Create();
 
   // Retrieves the singleton MediaSession associated with this client.
-  scoped_refptr<MediaSession>& GetMediaSession() { return media_session_; }
+  MediaSession* GetMediaSession() { return media_session_; }
 
   // The web app should set the MediaPositionState of the MediaSession object.
   // However, if that is not done, then query the web media player factory to
@@ -115,7 +114,8 @@
   // Indicate the media session client is active or not depending on the
   // media session playback state.
   bool is_active() {
-    return platform_playback_state_ != kMediaSessionPlaybackStateNone;
+    return session_state_.actual_playback_state() !=
+        kMediaSessionPlaybackStateNone;
   }
 
   // Set maybe freeze callback.
@@ -123,9 +123,13 @@
     maybe_freeze_callback_ = maybe_freeze_callback;
   }
 
+  void set_media_session(MediaSession* media_session) {
+    media_session_ = media_session;
+  }
+
  private:
   THREAD_CHECKER(thread_checker_);
-  scoped_refptr<MediaSession> media_session_;
+  MediaSession* media_session_;
   MediaSessionState session_state_;
   MediaSessionPlaybackState platform_playback_state_;
   const media::WebMediaPlayerFactory* media_player_factory_ = nullptr;
diff --git a/src/cobalt/media_session/media_session_test.cc b/src/cobalt/media_session/media_session_test.cc
index 36e6660..fcdd413 100644
--- a/src/cobalt/media_session/media_session_test.cc
+++ b/src/cobalt/media_session/media_session_test.cc
@@ -46,25 +46,20 @@
 namespace media_session {
 namespace {
 
+class MockMediaSessionClient;
+
 class MockCallbackFunction : public MediaSession::MediaSessionActionHandler {
  public:
   MOCK_CONST_METHOD1(
       Run, ReturnValue(const MediaSessionActionDetails& action_details));
 };
 
-class MockMediaSession : public MediaSession {
- public:
-  explicit MockMediaSession(MediaSessionClient* client)
-      : MediaSession(client) {}
-  MOCK_CONST_METHOD0(GetMonotonicNow, SbTimeMonotonic());
-};
-
 class MockMediaSessionClient : public MediaSessionClient {
  public:
-  MockMediaSessionClient() : MediaSessionClient(new MockMediaSession(this)) {}
-  MockMediaSession& mock_session() {
-    return static_cast<MockMediaSession&>(*GetMediaSession().get());
+  explicit MockMediaSessionClient(MediaSession* media_session) :
+      MediaSessionClient(media_session) {
   }
+
   void OnMediaSessionStateChanged(const MediaSessionState& session_state)
       override {
     session_state_ = session_state;
@@ -87,6 +82,18 @@
   size_t session_change_count_ = 0;
 };
 
+class MockMediaSession : public MediaSession {
+ public:
+  explicit MockMediaSession(MockMediaSessionClient* client)
+      : MediaSession(client) {}
+
+  MockMediaSessionClient* mock_session_client() {
+    return static_cast<MockMediaSessionClient*>(media_session_client());
+  }
+
+  MOCK_CONST_METHOD0(GetMonotonicNow, SbTimeMonotonic());
+};
+
 MATCHER_P(SeekTime, time, "") {
   return arg.action() == kMediaSessionActionSeekto && arg.seek_time() == time;
 }
@@ -102,85 +109,95 @@
 TEST(MediaSessionTest, MediaSessionTest) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  scoped_refptr<MediaSession> session = client.GetMediaSession();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
 
   EXPECT_EQ(kMediaSessionPlaybackStateNone, session->playback_state());
 
   session->set_playback_state(kMediaSessionPlaybackStatePlaying);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStatePlaying,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
-  EXPECT_EQ(client.GetMediaSessionChangeCount(), 1);
+  EXPECT_EQ(session->mock_session_client()->GetMediaSessionChangeCount(), 1);
 }
 
 TEST(MediaSessionTest, ActualPlaybackState) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  scoped_refptr<MediaSession> session = client.GetMediaSession();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
 
   // Trigger a session state change without impacting playback state.
   session->set_metadata(new MediaMetadata);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(client.GetMediaSessionChangeCount(), 1);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(session->mock_session_client()->GetMediaSessionChangeCount(), 1);
 
-  EXPECT_EQ(kMediaSessionPlaybackStateNone,
-            client.GetMediaSessionState().actual_playback_state());
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
-  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePlaying);
+  session->mock_session_client()->UpdatePlatformPlaybackState(
+      kMediaSessionPlaybackStatePlaying);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStatePlaying,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
   session->set_playback_state(kMediaSessionPlaybackStatePlaying);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStatePlaying,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
   session->set_playback_state(kMediaSessionPlaybackStatePaused);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStatePlaying,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
-  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePaused);
+  session->mock_session_client()->UpdatePlatformPlaybackState(
+      kMediaSessionPlaybackStatePaused);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStatePaused,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStatePaused, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
   session->set_playback_state(kMediaSessionPlaybackStateNone);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStateNone,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
-  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStateNone);
+  session->mock_session_client()->UpdatePlatformPlaybackState(
+      kMediaSessionPlaybackStateNone);
 
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(kMediaSessionPlaybackStateNone,
-            client.GetMediaSessionState().actual_playback_state());
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, session->mock_session_client()
+      ->GetMediaSessionState().actual_playback_state());
 
-  EXPECT_GE(client.GetMediaSessionChangeCount(), 2);
+  EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 2);
 }
 
 TEST(MediaSessionTest, NullActionClears) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  scoped_refptr<MediaSession> session = client.GetMediaSession();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
 
   // Trigger a session state change without impacting playback state.
   session->set_metadata(new MediaMetadata);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(client.GetMediaSessionChangeCount(), 1);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(session->mock_session_client()->GetMediaSessionChangeCount(), 1);
 
-  MediaSessionState state = client.GetMediaSessionState();
+  MediaSessionState state =
+      session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStateNone, state.actual_playback_state());
   EXPECT_EQ(0, state.available_actions().to_ulong());
 
@@ -193,31 +210,38 @@
   FakeScriptValue<MediaSession::MediaSessionActionHandler> null_holder(NULL);
 
   session->SetActionHandler(kMediaSessionActionPlay, holder);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(1, client.GetMediaSessionState().available_actions().to_ulong());
-  client.InvokeAction(kCobaltExtensionMediaSessionActionPlay);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(1, session->mock_session_client()
+      ->GetMediaSessionState().available_actions().to_ulong());
+  session->mock_session_client()->InvokeAction(
+      kCobaltExtensionMediaSessionActionPlay);
 
   session->SetActionHandler(kMediaSessionActionPlay, null_holder);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(0, client.GetMediaSessionState().available_actions().to_ulong());
-  client.InvokeAction(kCobaltExtensionMediaSessionActionPlay);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(0, session->mock_session_client()
+      ->GetMediaSessionState().available_actions().to_ulong());
+  session->mock_session_client()->InvokeAction(
+      kCobaltExtensionMediaSessionActionPlay);
 
-  EXPECT_GE(client.GetMediaSessionChangeCount(), 3);
+  EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 3);
 }
 
 TEST(MediaSessionTest, AvailableActions) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
   MediaSessionState state;
-  scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
 
   // Trigger a session state change without impacting playback state.
   session->set_metadata(new MediaMetadata);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(client.GetMediaSessionChangeCount(), 1);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(session->mock_session_client()->GetMediaSessionChangeCount(), 1);
 
-  state = client.GetMediaSessionState();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStateNone, state.actual_playback_state());
   EXPECT_EQ(0, state.available_actions().to_ulong());
 
@@ -227,83 +251,86 @@
 
   session->SetActionHandler(kMediaSessionActionPlay, holder);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(1 << kMediaSessionActionPlay,
             state.available_actions().to_ulong());
 
   session->SetActionHandler(kMediaSessionActionPause, holder);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(1 << kMediaSessionActionPlay,
             state.available_actions().to_ulong());
 
   session->SetActionHandler(kMediaSessionActionSeekto, holder);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(1 << kMediaSessionActionPlay, state.available_actions().to_ulong());
 
-  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePlaying);
+  session->mock_session_client()->UpdatePlatformPlaybackState(
+      kMediaSessionPlaybackStatePlaying);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePlaying, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPause | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
 
   session->set_playback_state(kMediaSessionPlaybackStatePlaying);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePlaying, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPause | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
 
   session->set_playback_state(kMediaSessionPlaybackStatePaused);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePlaying, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPause | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
 
   session->set_playback_state(kMediaSessionPlaybackStatePlaying);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePlaying, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPause | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
 
-  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePaused);
+  session->mock_session_client()->UpdatePlatformPlaybackState(
+      kMediaSessionPlaybackStatePaused);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePlaying, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPause | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
 
   session->set_playback_state(kMediaSessionPlaybackStateNone);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStateNone, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPlay, state.available_actions().to_ulong());
 
   session->set_playback_state(kMediaSessionPlaybackStatePaused);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePaused, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPlay | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
 
-  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStateNone);
+  session->mock_session_client()->UpdatePlatformPlaybackState(
+      kMediaSessionPlaybackStateNone);
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(kMediaSessionPlaybackStatePaused, state.actual_playback_state());
   EXPECT_EQ(1 << kMediaSessionActionPlay | 1 << kMediaSessionActionSeekto,
             state.available_actions().to_ulong());
@@ -312,8 +339,10 @@
 TEST(MediaSessionTest, InvokeAction) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  scoped_refptr<MediaSession> session = client.GetMediaSession();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
 
   MockCallbackFunction cf;
   FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
@@ -325,14 +354,16 @@
 
   details->set_action(kMediaSessionActionSeekto);
   details->set_seek_time(1.2);
-  client.InvokeAction(std::move(details));
+  session->mock_session_client()->InvokeAction(std::move(details));
 }
 
 TEST(MediaSessionTest, SeekDetails) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  scoped_refptr<MediaSession> session = client.GetMediaSession();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
 
   MockCallbackFunction cf;
   FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
@@ -344,41 +375,46 @@
 
   EXPECT_CALL(cf, Run(SeekNoOffset(kMediaSessionActionSeekforward)))
       .WillOnce(Return(CallbackResult<void>()));
-  client.InvokeAction(kCobaltExtensionMediaSessionActionSeekforward);
+  session->mock_session_client()->InvokeAction(
+      kCobaltExtensionMediaSessionActionSeekforward);
 
   EXPECT_CALL(cf, Run(SeekNoOffset(kMediaSessionActionSeekbackward)))
       .WillOnce(Return(CallbackResult<void>()));
-  client.InvokeAction(kCobaltExtensionMediaSessionActionSeekbackward);
+  session->mock_session_client()->InvokeAction(
+      kCobaltExtensionMediaSessionActionSeekbackward);
 
   EXPECT_CALL(cf, Run(SeekTime(1.2))).WillOnce(Return(CallbackResult<void>()));
   CobaltExtensionMediaSessionActionDetailsInit(
       &details, kCobaltExtensionMediaSessionActionSeekto);
   details.seek_time = 1.2;
-  client.InvokeCobaltExtensionAction(details);
+  session->mock_session_client()->InvokeCobaltExtensionAction(details);
 
   EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekforward, 3.4)))
       .WillOnce(Return(CallbackResult<void>()));
   CobaltExtensionMediaSessionActionDetailsInit(
       &details, kCobaltExtensionMediaSessionActionSeekforward);
   details.seek_offset = 3.4;
-  client.InvokeCobaltExtensionAction(details);
+  session->mock_session_client()->InvokeCobaltExtensionAction(details);
 
   EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekbackward, 5.6)))
       .WillOnce(Return(CallbackResult<void>()));
   CobaltExtensionMediaSessionActionDetailsInit(
       &details, kCobaltExtensionMediaSessionActionSeekbackward);
   details.seek_offset = 5.6;
-  client.InvokeCobaltExtensionAction(details);
+  session->mock_session_client()->InvokeCobaltExtensionAction(details);
 
-  client.WaitForSessionStateChange();
-  EXPECT_GE(client.GetMediaSessionChangeCount(), 0);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 0);
 }
 
 TEST(MediaSessionTest, PositionState) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  MockMediaSession& session = client.mock_session();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
+
   MediaSessionState state;
 
   SbTimeMonotonic start_time = 1111111111;
@@ -389,23 +425,23 @@
   position_state->set_position(10.0);
 
   // Trigger a session state change without impacting playback state.
-  session.set_metadata(new MediaMetadata);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(client.GetMediaSessionChangeCount(), 1);
+  session->set_metadata(new MediaMetadata);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(session->mock_session_client()->GetMediaSessionChangeCount(), 1);
 
   // Position state not yet reported
-  state = client.GetMediaSessionState();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(0,
             state.GetCurrentPlaybackPosition(start_time + 999 * kSbTimeSecond));
   EXPECT_EQ(0, state.duration());
   EXPECT_EQ(0.0, state.actual_playback_rate());
 
   // Forward playback
-  EXPECT_CALL(session, GetMonotonicNow()).WillOnce(Return(start_time));
+  EXPECT_CALL(*session, GetMonotonicNow()).WillOnce(Return(start_time));
   position_state->set_playback_rate(1.0);
-  session.SetPositionState(position_state);
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->SetPositionState(position_state);
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ((10 + 50) * kSbTimeSecond,
             state.GetCurrentPlaybackPosition(start_time + 50 * kSbTimeSecond));
   EXPECT_EQ(100 * kSbTimeSecond,
@@ -414,11 +450,11 @@
   EXPECT_EQ(1.0, state.actual_playback_rate());
 
   // Fast playback
-  EXPECT_CALL(session, GetMonotonicNow()).WillOnce(Return(start_time));
+  EXPECT_CALL(*session, GetMonotonicNow()).WillOnce(Return(start_time));
   position_state->set_playback_rate(2.0);
-  session.SetPositionState(position_state);
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->SetPositionState(position_state);
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ((10 + 2 * 20) * kSbTimeSecond,
             state.GetCurrentPlaybackPosition(start_time + 20 * kSbTimeSecond));
   EXPECT_EQ(100 * kSbTimeSecond,
@@ -427,11 +463,11 @@
   EXPECT_EQ(2.0, state.actual_playback_rate());
 
   // Reverse playback
-  EXPECT_CALL(session, GetMonotonicNow()).WillOnce(Return(start_time));
+  EXPECT_CALL(*session, GetMonotonicNow()).WillOnce(Return(start_time));
   position_state->set_playback_rate(-1.0);
-  session.SetPositionState(position_state);
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->SetPositionState(position_state);
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(0 * kSbTimeSecond,
             state.GetCurrentPlaybackPosition(start_time + 20 * kSbTimeSecond));
   EXPECT_EQ((10 - 3) * kSbTimeSecond,
@@ -440,12 +476,12 @@
   EXPECT_EQ(-1.0, state.actual_playback_rate());
 
   // Indefinite duration (live) playback
-  EXPECT_CALL(session, GetMonotonicNow()).WillOnce(Return(start_time));
+  EXPECT_CALL(*session, GetMonotonicNow()).WillOnce(Return(start_time));
   position_state->set_duration(std::numeric_limits<double>::infinity());
   position_state->set_playback_rate(1.0);
-  session.SetPositionState(position_state);
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->SetPositionState(position_state);
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(10 * kSbTimeSecond + 1 * kSbTimeDay,
             state.GetCurrentPlaybackPosition(start_time + 1 * kSbTimeDay));
   EXPECT_EQ(kSbTimeMax, state.duration());
@@ -454,45 +490,47 @@
   // Paused playback
   // (Actual playback rate is 0.0, so position is the last reported position.
   //  The web app should update position and playback states together.)
-  session.set_playback_state(kMediaSessionPlaybackStatePaused);
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->set_playback_state(kMediaSessionPlaybackStatePaused);
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(10 * kSbTimeSecond,
             state.GetCurrentPlaybackPosition(start_time + 999 * kSbTimeSecond));
   EXPECT_EQ(kSbTimeMax, state.duration());
   EXPECT_EQ(0.0, state.actual_playback_rate());
-  session.set_playback_state(kMediaSessionPlaybackStatePlaying);
+  session->set_playback_state(kMediaSessionPlaybackStatePlaying);
 
   // Position state cleared
-  EXPECT_CALL(session, GetMonotonicNow()).WillOnce(Return(start_time));
-  session.SetPositionState(base::nullopt);
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  EXPECT_CALL(*session, GetMonotonicNow()).WillOnce(Return(start_time));
+  session->SetPositionState(base::nullopt);
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   EXPECT_EQ(0,
             state.GetCurrentPlaybackPosition(start_time + 999 * kSbTimeSecond));
   EXPECT_EQ(0, state.duration());
   EXPECT_EQ(0.0, state.actual_playback_rate());
 
-  EXPECT_GE(client.GetMediaSessionChangeCount(), 3);
+  EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 3);
 }
 
 TEST(MediaSessionTest, Metadata) {
   base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
 
-  MockMediaSessionClient client;
-  MockMediaSession& session = client.mock_session();
+  scoped_refptr<MockMediaSession> session =
+      scoped_refptr<MockMediaSession>(new MockMediaSession(
+          new MockMediaSessionClient(nullptr)));
+  session->media_session_client()->set_media_session(session);
   MediaSessionState state;
 
   MediaMetadataInit init_metadata;
   base::Optional<MediaMetadataInit> state_metadata;
 
   // Trigger a session state change without impacting metadata.
-  session.set_playback_state(kMediaSessionPlaybackStateNone);
-  client.WaitForSessionStateChange();
-  EXPECT_EQ(client.GetMediaSessionChangeCount(), 1);
+  session->set_playback_state(kMediaSessionPlaybackStateNone);
+  session->mock_session_client()->WaitForSessionStateChange();
+  EXPECT_EQ(session->mock_session_client()->GetMediaSessionChangeCount(), 1);
 
   // Metadata not yet set
-  state = client.GetMediaSessionState();
+  state = session->mock_session_client()->GetMediaSessionState();
   state_metadata = state.metadata();
   EXPECT_FALSE(state.has_metadata());
   EXPECT_FALSE(state_metadata.has_value());
@@ -507,11 +545,11 @@
   script::Sequence<MediaImage> artwork;
   artwork.push_back(art_image);
   init_metadata.set_artwork(artwork);
-  session.set_metadata(
+  session->set_metadata(
       scoped_refptr<MediaMetadata>(new MediaMetadata(init_metadata)));
 
-  client.WaitForSessionStateChange();
-  state = client.GetMediaSessionState();
+  session->mock_session_client()->WaitForSessionStateChange();
+  state = session->mock_session_client()->GetMediaSessionState();
   state_metadata = state.metadata();
   EXPECT_TRUE(state.has_metadata());
   EXPECT_TRUE(state_metadata.has_value());
@@ -521,7 +559,7 @@
   EXPECT_EQ(1, state_metadata->artwork().size());
   EXPECT_EQ("http://art.image", state_metadata->artwork().at(0).src());
 
-  EXPECT_GE(client.GetMediaSessionChangeCount(), 2);
+  EXPECT_GE(session->mock_session_client()->GetMediaSessionChangeCount(), 2);
 }
 
 }  // namespace
diff --git a/src/cobalt/media_stream/media_stream_test.gyp b/src/cobalt/media_stream/media_stream_test.gyp
index a7cc5b1..aac127c 100644
--- a/src/cobalt/media_stream/media_stream_test.gyp
+++ b/src/cobalt/media_stream/media_stream_test.gyp
@@ -30,6 +30,7 @@
         'testing/mock_media_stream_audio_track.h',
       ],
       'dependencies': [
+        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/media_stream/media_stream.gyp:media_stream',
         '<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 97bef85..a78f0ed 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -24,12 +24,14 @@
 #include "cobalt/base/address_sanitizer.h"
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/extension/graphics.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/brush.h"
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/dump_render_tree_to_string.h"
 #include "cobalt/render_tree/rect_node.h"
 #include "nb/memory_scope.h"
+#include "starboard/system.h"
 
 using cobalt::render_tree::Node;
 using cobalt::render_tree::animations::AnimateNode;
@@ -38,6 +40,7 @@
 namespace renderer {
 
 namespace {
+
 #if !defined(COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS)
 // This default value has been moved from cobalt/build/cobalt_configuration.gypi
 // in favor of the usage of
@@ -76,6 +79,34 @@
   }
 }
 
+bool ShouldClearFrameOnShutdown(render_tree::ColorRGBA* out_clear_color) {
+#if SB_API_VERSION >= 11
+  const CobaltExtensionGraphicsApi* graphics_extension =
+      static_cast<const CobaltExtensionGraphicsApi*>(
+          SbSystemGetExtension(kCobaltExtensionGraphicsName));
+  if (graphics_extension &&
+      strcmp(graphics_extension->name, kCobaltExtensionGraphicsName) == 0 &&
+      graphics_extension->version >= 4) {
+    float r, g, b, a;
+    if (graphics_extension->ShouldClearFrameOnShutdown(&r, &g, &b, &a)) {
+      out_clear_color->set_r(r);
+      out_clear_color->set_g(g);
+      out_clear_color->set_b(b);
+      out_clear_color->set_a(a);
+      return true;
+    }
+    return false;
+  }
+#endif
+
+  // Default is to clear to opaque black.
+  out_clear_color->set_r(0.0f);
+  out_clear_color->set_g(0.0f);
+  out_clear_color->set_b(0.0f);
+  out_clear_color->set_a(1.0f);
+  return true;
+}
+
 }  // namespace
 
 Pipeline::Pipeline(const CreateRasterizerFunction& create_rasterizer_function,
@@ -329,13 +360,13 @@
       minimum_frame_interval_milliseconds =
           COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS;
     } else {
-      DLOG(ERROR) <<
-          "COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS and "
-          "CobaltExtensionGraphicsApi::GetMinimumFrameIntervalInMilliseconds"
-          "are both defined."
-          "Remove the 'cobalt_minimum_frame_time_in_milliseconds' ";
-          "from ../gyp_configuration.gypi in favor of the usage of "
-          "CobaltExtensionGraphicsApi::GetMinimumFrameIntervalInMilliseconds."
+      DLOG(ERROR)
+          << "COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS and "
+             "CobaltExtensionGraphicsApi::GetMinimumFrameIntervalInMilliseconds"
+             "are both defined."
+             "Remove the 'cobalt_minimum_frame_time_in_milliseconds' ";
+      "from ../gyp_configuration.gypi in favor of the usage of "
+      "CobaltExtensionGraphicsApi::GetMinimumFrameIntervalInMilliseconds."
     }
 #else
     if (minimum_frame_interval_milliseconds < 0.0f) {
@@ -373,11 +404,13 @@
   bool is_new_render_tree = submission.render_tree != last_render_tree_;
   bool has_render_tree_changed =
       !last_animations_expired_ || is_new_render_tree;
-  bool force_rasterize = submit_even_if_render_tree_is_unchanged_ ||
-      fps_overlay_update_pending_;
+  bool force_rasterize =
+      submit_even_if_render_tree_is_unchanged_ || fps_overlay_update_pending_;
 
-  float maximum_frame_interval_milliseconds = graphics_context_ ?
-      graphics_context_->GetMaximumFrameIntervalInMilliseconds() : -1.0f;
+  float maximum_frame_interval_milliseconds =
+      graphics_context_
+          ? graphics_context_->GetMaximumFrameIntervalInMilliseconds()
+          : -1.0f;
   if (maximum_frame_interval_milliseconds >= 0.0f) {
     base::TimeDelta max_time_between_rasterize =
         base::TimeDelta::FromMillisecondsD(maximum_frame_interval_milliseconds);
@@ -617,18 +650,18 @@
   // Shutdown the FPS overlay which may reference render trees.
   fps_overlay_ = base::nullopt;
 
-  // Submit a black fullscreen rect node to clear the display before shutting
+  // Submit a fullscreen rect node to clear the display before shutting
   // down.  This can be helpful if we quit while playing a video via
   // punch-through, which may result in unexpected images/colors appearing for
   // a flicker behind the display.
-  if (render_target_ && (clear_on_shutdown_mode_ == kClearToBlack)) {
-    rasterizer_->Submit(
-        new render_tree::RectNode(
-            math::RectF(render_target_->GetSize()),
-            std::unique_ptr<render_tree::Brush>(
-                new render_tree::SolidColorBrush(
-                    render_tree::ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)))),
-        render_target_);
+  render_tree::ColorRGBA clear_color;
+  if (render_target_ && clear_on_shutdown_mode_ == kClearAccordingToPlatform &&
+      ShouldClearFrameOnShutdown(&clear_color)) {
+    rasterizer_->Submit(new render_tree::RectNode(
+                            math::RectF(render_target_->GetSize()),
+                            std::unique_ptr<render_tree::Brush>(
+                                new render_tree::SolidColorBrush(clear_color))),
+                        render_target_);
   }
 
   // This potential reference to a render tree whose animations may have ended
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index bbb9658..5848d95 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -58,7 +58,12 @@
       RasterizationCompleteCallback;
 
   enum ShutdownClearMode {
-    kClearToBlack,
+    // Query CobaltExtensionGraphicsApi's ShouldClearFrameOnShutdown for
+    // shutdown behavior.
+    kClearAccordingToPlatform,
+
+    // Do not clear regardless of what CobaltExtensionGraphicsApi's
+    // ShouldClearFrameOnShutdown specifies.
     kNoClear,
   };
 
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index 801112c..1f04c7a 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -234,7 +234,7 @@
   uint32_t untouched_states =
       kMSAAEnable_GrGLBackendState | kStencil_GrGLBackendState |
       kPixelStore_GrGLBackendState | kFixedFunction_GrGLBackendState |
-      kPathRendering_GrGLBackendState | kMisc_GrGLBackendState;
+      kPathRendering_GrGLBackendState;
 
   GetFallbackContext()->resetContext(~untouched_states & kAll_GrBackendState);
 }
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index 6b52456..9783414 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -102,7 +102,7 @@
         // deprecate the submit_even_if_render_tree_is_unchanged.
         false,
 #endif
-        renderer::Pipeline::kClearToBlack, pipeline_options));
+        renderer::Pipeline::kClearAccordingToPlatform, pipeline_options));
   }
 }
 
diff --git a/src/cobalt/site/docs/reference/starboard/modules/configuration.md b/src/cobalt/site/docs/reference/starboard/modules/configuration.md
index fff4d9a..5e41790 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/configuration.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/configuration.md
@@ -53,11 +53,6 @@
 SB_DEPRECATED_EXTERNAL(...) annotates the function as deprecated for external
 clients, but not deprecated for starboard.
 
-### SB_DISALLOW_COPY_AND_ASSIGN(TypeName) ###
-
-A macro to disallow the copy constructor and operator= functions This should be
-used in the private: declarations for a class
-
 ### SB_EXPERIMENTAL_API_VERSION ###
 
 The API version that is currently open for changes, and therefore is not stable
diff --git a/src/cobalt/speech/sandbox/sandbox.gyp b/src/cobalt/speech/sandbox/sandbox.gyp
index 03be453..25aaed0 100644
--- a/src/cobalt/speech/sandbox/sandbox.gyp
+++ b/src/cobalt/speech/sandbox/sandbox.gyp
@@ -28,6 +28,7 @@
         'speech_sandbox_main.cc',
       ],
       'dependencies': [
+        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/audio/audio.gyp:audio',
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/debug/debug.gyp:console_command_manager',
diff --git a/src/cobalt/tools/automated_testing/cobalt_runner.py b/src/cobalt/tools/automated_testing/cobalt_runner.py
index 1efd7dd..003edf5 100644
--- a/src/cobalt/tools/automated_testing/cobalt_runner.py
+++ b/src/cobalt/tools/automated_testing/cobalt_runner.py
@@ -9,10 +9,10 @@
 import os
 import re
 import sys
-import thread
 import threading
 import time
 import traceback
+import thread
 
 import _env  # pylint: disable=unused-import
 from cobalt.tools.automated_testing import c_val_names
@@ -26,7 +26,8 @@
 RE_WEBDRIVER_FAILED = re.compile(r'Could not start WebDriver server')
 # Pattern to match Cobalt log line for when a WindowDriver has been created.
 RE_WINDOWDRIVER_CREATED = re.compile(
-    r'^\[[\d:]+/[\d.]+:INFO:browser_module\.cc\(\d+\)\] Created WindowDriver: ID=\S+'
+    (r'^\[[\d:]+/[\d.]+:INFO:browser_module\.cc\(\d+\)\] Created WindowDriver: '
+     r'ID=\S+')
 )
 # Pattern to match Cobalt log line for when a WebModule is has been loaded.
 RE_WEBMODULE_LOADED = re.compile(
@@ -247,7 +248,7 @@
       self.WaitForStart()
     except KeyboardInterrupt:
       # potentially from thread.interrupt_main(). We will treat as
-      # a timeout regardless
+      # a timeout regardless.
 
       self.Exit(should_fail=True)
       raise TimeoutException
@@ -286,8 +287,8 @@
       self.runner_thread.join(COBALT_EXIT_TIMEOUT_SECONDS)
     if self.runner_thread.isAlive():
       sys.stderr.write(
-          '***Runner thread still alive after sending graceful shutdown command, try again by killing app***\n'
-      )
+          '***Runner thread still alive after sending graceful shutdown '
+          'command, try again by killing app***\n')
       self.launcher.Kill()
     # Once the write end of the pipe has been closed by the launcher, the reader
     # thread will get EOF and exit.
@@ -328,7 +329,7 @@
       logging.info('Cobalt terminated.')
       if not self.failed and self.success_message:
         print('{}\n'.format(self.success_message))
-        logging.info('{}\n'.format(self.success_message))
+        logging.info('%s\n', self.success_message)
     # pylint: disable=broad-except
     except Exception as ex:
       sys.stderr.write('Exception running Cobalt ' + str(ex))
@@ -370,15 +371,14 @@
     """
     javascript_code = 'return h5vcc.cVal.getValue(\'{}\')'.format(cval_name)
     cval_string = self.ExecuteJavaScript(javascript_code)
-    if cval_string is None:
-      return None
-    else:
+    if cval_string:
       try:
         # Try to parse numbers and booleans.
         return json.loads(cval_string)
       except ValueError:
         # If we can't parse a value, return the cval string as-is.
         return cval_string
+    return None
 
   def GetCvalBatch(self, cval_name_list):
     """Retrieves a batch of cvals.
@@ -465,6 +465,8 @@
     Returns:
       Array of selected elements
     """
+    elements = None
+
     # The retry part below is a temporary workaround to handle command
     # failures during a short window of stale Cobalt WindowDriver
     # after navigation. We only introduced it because of limited time budget
diff --git a/src/cobalt/updater/configurator.cc b/src/cobalt/updater/configurator.cc
index aacee6a..f48c66a 100644
--- a/src/cobalt/updater/configurator.cc
+++ b/src/cobalt/updater/configurator.cc
@@ -28,50 +28,35 @@
 // Default time constants.
 const int kDelayOneMinute = 60;
 const int kDelayOneHour = kDelayOneMinute * 60;
-
-#if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
-const std::set<std::string> valid_channels = {"dev"};
-const std::string kDefaultUpdaterChannel = "dev";
-#elif defined(COBALT_BUILD_TYPE_QA)
 const std::set<std::string> valid_channels = {
-    // Default channel for qa builds
-    "qa",
-    // Test an update with higher version than qa channel
-    "test",
-    // Test an update with mismatched sabi
-    "tmsabi",
-    // Test an update that does nothing
-    "tnoop",
-    // Test an update that crashes
-    "tcrash",
-    // Test an update that fails verification
-    "tfailv",
-    // Test a series of continuous updates with two channels
-    "tseries1", "tseries2",
-};
-const std::string kDefaultUpdaterChannel = "qa";
-#elif defined(COBALT_BUILD_TYPE_GOLD)
-const std::set<std::string> valid_channels = {
-    // Default channel for gold builds
-    "prod",
-    // Channel for dogfooders
+    // Default channel for debug/devel builds.
+    "dev",
+    // Channel for dogfooders.
     "dogfood",
+    // Default channel for gold builds.
+    "prod",
     // Default channel for qa builds. A gold build can switch to this channel to
     // get an official qa build.
     "qa",
-    // Test an update with higher version than prod channel
+    // Test an update with higher version than prod channel.
     "test",
-    // Test an update with mismatched sabi
+    // Test an update with mismatched sabi.
     "tmsabi",
-    // Test an update that does nothing
+    // Test an update that does nothing.
     "tnoop",
-    // Test an update that crashes
+    // Test an update that crashes.
     "tcrash",
-    // Test an update that fails verification
+    // Test an update that fails verification.
     "tfailv",
-    // Test a series of continuous updates with two channels
+    // Test a series of continuous updates with two channels.
     "tseries1", "tseries2",
 };
+
+#if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
+const std::string kDefaultUpdaterChannel = "dev";
+#elif defined(COBALT_BUILD_TYPE_QA)
+const std::string kDefaultUpdaterChannel = "qa";
+#elif defined(COBALT_BUILD_TYPE_GOLD)
 const std::string kDefaultUpdaterChannel = "prod";
 #endif
 
diff --git a/src/cobalt/updater/noop_sandbox.cc b/src/cobalt/updater/noop_sandbox.cc
index d9ce028..5f63965 100644
--- a/src/cobalt/updater/noop_sandbox.cc
+++ b/src/cobalt/updater/noop_sandbox.cc
@@ -15,7 +15,12 @@
 // This is a test app for Evergreen that does nothing.
 
 #include "starboard/event.h"
+#include "starboard/system.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
 
 void SbEventHandle(const SbEvent* event) {
-  // noop
+  // No-op app. Exit after 1s.
+  SbThreadSleep(kSbTimeSecond);
+  SbSystemRequestStop(0);
 }
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index c3bfb3a..4ba8d9a 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -33,6 +33,7 @@
         'web_socket_impl.h',
       ],
       'dependencies': [
+        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
diff --git a/src/cobalt/xhr/url_fetcher_buffer_writer.cc b/src/cobalt/xhr/url_fetcher_buffer_writer.cc
index a6eec44..fd67bb4 100644
--- a/src/cobalt/xhr/url_fetcher_buffer_writer.cc
+++ b/src/cobalt/xhr/url_fetcher_buffer_writer.cc
@@ -28,6 +28,7 @@
 const int64_t kDefaultPreAllocateSizeInBytes = 64 * 1024;
 // Set max allocate size to avoid erroneous size estimate.
 const int64_t kMaxPreAllocateSizeInBytes = 10 * 1024 * 1024;
+const uint8_t kResizingMultiplier = 2;
 
 void ReleaseMemory(std::string* str) {
   DCHECK(str);
@@ -167,6 +168,9 @@
   } else {
     capacity_known_ = true;
   }
+  // Record the desired_capacity_ to avoid reserving unused memory during
+  // resizing.
+  desired_capacity_ = static_cast<size_t>(capacity);
 
   if (capacity == 0) {
     return;
@@ -218,7 +222,16 @@
       SB_LOG(WARNING) << "Data written is larger than the preset capacity "
                       << data_as_array_buffer_.byte_length();
     }
-    data_as_array_buffer_.Resize(data_as_array_buffer_size_ + num_bytes);
+    size_t new_size = std::max(
+        std::min(data_as_array_buffer_.byte_length() * kResizingMultiplier,
+                 desired_capacity_),
+        data_as_array_buffer_size_ + num_bytes);
+    if (new_size > desired_capacity_) {
+      // Content-length is wrong, response size is completely unknown.
+      // Double the capacity to avoid frequent resizing.
+      new_size *= kResizingMultiplier;
+    }
+    data_as_array_buffer_.Resize(new_size);
   }
 
   auto destination = static_cast<uint8_t*>(data_as_array_buffer_.data()) +
diff --git a/src/cobalt/xhr/url_fetcher_buffer_writer.h b/src/cobalt/xhr/url_fetcher_buffer_writer.h
index 8eb9736..35058ab 100644
--- a/src/cobalt/xhr/url_fetcher_buffer_writer.h
+++ b/src/cobalt/xhr/url_fetcher_buffer_writer.h
@@ -74,6 +74,7 @@
     Type type_;
     bool allow_preallocate_ = true;
     bool capacity_known_ = false;
+    size_t desired_capacity_ = 0;
 
     // This class can be accessed by both network and MainWebModule threads.
     mutable base::Lock lock_;
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index 051822e..c3ebc4c 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -23,8 +23,6 @@
       'sources': [
         'url_fetcher_buffer_writer.cc',
         'url_fetcher_buffer_writer.h',
-        'xhr_response_data.cc',
-        'xhr_response_data.h',
         'xml_http_request.cc',
         'xml_http_request.h',
         'xml_http_request_event_target.cc',
@@ -58,10 +56,10 @@
       'target_name': 'xhr_test',
       'type': '<(gtest_target_type)',
       'sources': [
-        'xhr_response_data_test.cc',
         'xml_http_request_test.cc',
       ],
       'dependencies': [
+        '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/cobalt/xhr/xhr_response_data.cc b/src/cobalt/xhr/xhr_response_data.cc
deleted file mode 100644
index d2e5051..0000000
--- a/src/cobalt/xhr/xhr_response_data.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/xhr/xhr_response_data.h"
-
-#include <algorithm>
-
-#include "cobalt/dom/global_stats.h"
-
-namespace cobalt {
-namespace xhr {
-
-namespace {
-
-// When we don't have any data, we still want to return a non-null pointer to a
-// valid memory location.  Because even it will never be accessed, a null
-// pointer may trigger undefined behavior in functions like memcpy.  So we
-// create this dummy value here and return its address when we don't have any
-// data.
-uint8 s_dummy;
-
-// We are using std::string to store binary data so we want to ensure that char
-// occupies one byte.
-COMPILE_ASSERT(sizeof(char) == 1, char_should_occupy_one_byte);
-
-}  // namespace
-
-XhrResponseData::XhrResponseData() { IncreaseMemoryUsage(); }
-
-XhrResponseData::~XhrResponseData() { DecreaseMemoryUsage(); }
-
-void XhrResponseData::Clear() {
-  DecreaseMemoryUsage();
-  // Use swap to force free the memory allocated.
-  std::string dummy;
-  data_.swap(dummy);
-  IncreaseMemoryUsage();
-}
-
-void XhrResponseData::Reserve(size_t new_capacity_bytes) {
-  DecreaseMemoryUsage();
-  data_.reserve(new_capacity_bytes);
-  IncreaseMemoryUsage();
-}
-
-void XhrResponseData::Append(const uint8* source_data, size_t size_bytes) {
-  if (size_bytes == 0) {
-    return;
-  }
-  DecreaseMemoryUsage();
-  data_.resize(data_.size() + size_bytes);
-  memcpy(&data_[data_.size() - size_bytes], source_data, size_bytes);
-  IncreaseMemoryUsage();
-}
-
-const uint8* XhrResponseData::data() const {
-  return data_.empty() ? &s_dummy : reinterpret_cast<const uint8*>(&data_[0]);
-}
-
-uint8* XhrResponseData::data() {
-  return data_.empty() ? &s_dummy : reinterpret_cast<uint8*>(&data_[0]);
-}
-
-void XhrResponseData::IncreaseMemoryUsage() {
-  dom::GlobalStats::GetInstance()->IncreaseXHRMemoryUsage(capacity());
-}
-
-void XhrResponseData::DecreaseMemoryUsage() {
-  dom::GlobalStats::GetInstance()->DecreaseXHRMemoryUsage(capacity());
-}
-
-}  // namespace xhr
-}  // namespace cobalt
diff --git a/src/cobalt/xhr/xhr_response_data.h b/src/cobalt/xhr/xhr_response_data.h
deleted file mode 100644
index 894cd3f..0000000
--- a/src/cobalt/xhr/xhr_response_data.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef COBALT_XHR_XHR_RESPONSE_DATA_H_
-#define COBALT_XHR_XHR_RESPONSE_DATA_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-namespace cobalt {
-namespace xhr {
-
-// Simple wrapper for an array of data.
-// Used by XMLHttpRequest to construct the response body.
-class XhrResponseData {
- public:
-  XhrResponseData();
-  ~XhrResponseData();
-
-  // Destroy the data_ and reset the size and capacity to 0.
-  void Clear();
-  // Allocate storage for |new_capacity_bytes| of data.
-  void Reserve(size_t new_capacity_bytes);
-  // Append |source_data|, |size_bytes| in length, to the data array.
-  void Append(const uint8* source_data, size_t size_bytes);
-
-  const uint8* data() const;
-  uint8* data();
-
-  const std::string& string() const { return data_; }
-
-  size_t size() const { return data_.size(); }
-  size_t capacity() const { return data_.capacity(); }
-
- private:
-  void IncreaseMemoryUsage();
-  void DecreaseMemoryUsage();
-
-  std::string data_;
-};
-
-}  // namespace xhr
-}  // namespace cobalt
-
-#endif  // COBALT_XHR_XHR_RESPONSE_DATA_H_
diff --git a/src/cobalt/xhr/xhr_response_data_test.cc b/src/cobalt/xhr/xhr_response_data_test.cc
deleted file mode 100644
index f63d70e..0000000
--- a/src/cobalt/xhr/xhr_response_data_test.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/xhr/xhr_response_data.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cobalt {
-namespace xhr {
-
-namespace {
-
-TEST(XhrResponseData, InitialState) {
-  XhrResponseData empty;
-  EXPECT_EQ(0u, empty.size());
-  EXPECT_TRUE(empty.data() != NULL);
-}
-
-TEST(XhrResponseData, Append) {
-  XhrResponseData data;
-  uint8 raw_data[64];
-  for (int i = 0; i < 64; ++i) {
-    raw_data[i] = static_cast<uint8>(i);
-  }
-  data.Append(raw_data, 64);
-  EXPECT_EQ(64u, data.size());
-  EXPECT_LE(64u, data.capacity());
-
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(raw_data[i], data.data()[i]);
-  }
-}
-
-TEST(XhrResponseData, Reserve) {
-  XhrResponseData data;
-  data.Reserve(1);
-  EXPECT_LE(1u, data.capacity());
-  EXPECT_EQ(0u, data.size());
-  EXPECT_TRUE(data.data() != NULL);
-}
-
-}  // namespace
-}  // namespace xhr
-}  // namespace cobalt
diff --git a/src/glimp/gles/context.cc b/src/glimp/gles/context.cc
index aa11665..21db890 100644
--- a/src/glimp/gles/context.cc
+++ b/src/glimp/gles/context.cc
@@ -1495,8 +1495,7 @@
 
   // The incoming pixel data should be aligned as the client has specified
   // that it will be.
-  SB_DCHECK(nb::IsAligned(nb::AsInteger(pixels),
-                          static_cast<uintptr_t>(unpack_alignment_)));
+  SB_DCHECK(nb::IsAligned(pixels, static_cast<size_t>(unpack_alignment_)));
 
   // Determine pitch taking into account glPixelStorei() settings.
   int pitch_in_bytes = GetPitchForTextureData(width, pixel_format);
@@ -1578,8 +1577,7 @@
 
   // The incoming pixel data should be aligned as the client has specified
   // that it will be.
-  SB_DCHECK(nb::IsAligned(nb::AsInteger(pixels),
-                          static_cast<uintptr_t>(unpack_alignment_)));
+  SB_DCHECK(nb::IsAligned(pixels, static_cast<size_t>(unpack_alignment_)));
 
   // Determine pitch taking into account glPixelStorei() settings.
   int pitch_in_bytes = GetPitchForTextureData(width, pixel_format);
diff --git a/src/nb/allocator.cc b/src/nb/allocator.cc
new file mode 100644
index 0000000..668c977
--- /dev/null
+++ b/src/nb/allocator.cc
@@ -0,0 +1,21 @@
+// Copyright 2020 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 "nb/allocator.h"
+
+namespace nb {
+
+const size_t Allocator::kMinAlignment = 16;
+
+}  // namespace nb
diff --git a/src/nb/allocator.h b/src/nb/allocator.h
index 07ee8e0..028e25d 100644
--- a/src/nb/allocator.h
+++ b/src/nb/allocator.h
@@ -17,9 +17,7 @@
 #ifndef NB_ALLOCATOR_H_
 #define NB_ALLOCATOR_H_
 
-// TODO: Include "starboard/types.h" once legacy platforms are ported to
-// starboard.  Currently including <vector> is used as a platform independent
-// way to introduce std::size_t.
+#include <cstddef>
 #include <vector>
 
 namespace nb {
@@ -31,6 +29,11 @@
 // through derived classes.
 class Allocator {
  public:
+  // Using a minimum value for alignment keeps things rounded and aligned
+  // and help us avoid creating tiny and/or badly misaligned free blocks.  Also
+  // ensures even for a 0-byte request will get a unique block.
+  static const size_t kMinAlignment;
+
   virtual ~Allocator() {}
 
   // Allocates a range of memory of the given size, without any alignment
diff --git a/src/nb/analytics/memory_tracker.h b/src/nb/analytics/memory_tracker.h
index 182488a..cf6e0eb 100644
--- a/src/nb/analytics/memory_tracker.h
+++ b/src/nb/analytics/memory_tracker.h
@@ -32,8 +32,11 @@
 class AllocationGroup;
 
 struct MemoryStats {
-  MemoryStats() : total_cpu_memory(0), used_cpu_memory(0),
-                  total_gpu_memory(0), used_gpu_memory(0) {}
+  MemoryStats()
+      : total_cpu_memory(0),
+        used_cpu_memory(0),
+        total_gpu_memory(0),
+        used_gpu_memory(0) {}
   int64_t total_cpu_memory;
   int64_t used_cpu_memory;
   int64_t total_gpu_memory;
@@ -143,7 +146,8 @@
  protected:
   virtual ~MemoryTracker() {}
 
-  SB_DISALLOW_COPY_AND_ASSIGN(MemoryTracker);
+  MemoryTracker(const MemoryTracker&) = delete;
+  void operator=(const MemoryTracker&) = delete;
 };
 
 // A visitor class which is useful for inspecting data.
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
index 448b9ca..124bfaa 100644
--- a/src/nb/analytics/memory_tracker_helpers.h
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -42,9 +42,7 @@
  public:
   NoReportAllocator() {}
   NoReportAllocator(const NoReportAllocator&) {}
-  static void* Allocate(size_t n) {
-    return SbMemoryAllocateNoReport(n);
-  }
+  static void* Allocate(size_t n) { return SbMemoryAllocateNoReport(n); }
   // Second argument can be used for accounting, but is otherwise optional.
   static void Deallocate(void* ptr, size_t /* not used*/) {
     SbMemoryDeallocateNoReport(ptr);
@@ -73,7 +71,8 @@
   starboard::atomic_int64_t allocation_bytes_;
   starboard::atomic_int32_t num_allocations_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroup);
+  AllocationGroup(const AllocationGroup&) = delete;
+  void operator=(const AllocationGroup&) = delete;
 };
 
 // A self locking data structure that maps strings -> AllocationGroups. This is
@@ -96,14 +95,16 @@
       std::string,
       AllocationGroup*,
       std::less<std::string>,
-      nb::StdAllocator<
-          std::pair<const std::string, AllocationGroup*>,
-          NoReportAllocator> > Map;
+      nb::StdAllocator<std::pair<const std::string, AllocationGroup*>,
+                       NoReportAllocator> >
+      Map;
   Map group_map_;
   AllocationGroup* unaccounted_group_;
   mutable starboard::Mutex mutex_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(AtomicStringAllocationGroupMap);
+  AtomicStringAllocationGroupMap(const AtomicStringAllocationGroupMap&) =
+      delete;
+  void operator=(const AtomicStringAllocationGroupMap&) = delete;
 };
 
 class AllocationGroupStack {
@@ -127,7 +128,9 @@
   const AllocationGroupPtrVec& data() const { return alloc_group_stack_; }
 
  private:
-  SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroupStack);
+  AllocationGroupStack(const AllocationGroupStack&) = delete;
+  void operator=(const AllocationGroupStack&) = delete;
+
   AllocationGroupPtrVec alloc_group_stack_, debug_stack_;
 };
 
@@ -161,14 +164,15 @@
       const void*,
       AllocationRecord,
       std::less<const void*>,  // required, when specifying allocator.
-      nb::StdAllocator<
-          std::pair<const void* const, AllocationRecord>,
-          NoReportAllocator> > PointerMap;
+      nb::StdAllocator<std::pair<const void* const, AllocationRecord>,
+                       NoReportAllocator> >
+      PointerMap;
 
   PointerMap pointer_map_;
   mutable starboard::Mutex mutex_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap);
+  AtomicAllocationMap(const AtomicAllocationMap&) = delete;
+  void operator=(const AtomicAllocationMap&) = delete;
 };
 
 // A per-pointer map of allocations to AllocRecords. This is a hybrid data
@@ -203,7 +207,9 @@
   const AtomicAllocationMap& GetMapForPointer(const void* ptr) const;
 
  private:
-  SB_DISALLOW_COPY_AND_ASSIGN(ConcurrentAllocationMap);
+  ConcurrentAllocationMap(const ConcurrentAllocationMap&) = delete;
+  void operator=(const ConcurrentAllocationMap&) = delete;
+
   // Takes a pointer and generates a hash.
   static uint32_t hash_ptr(const void* ptr);
 
diff --git a/src/nb/bidirectional_fit_reuse_allocator_test.cc b/src/nb/bidirectional_fit_reuse_allocator_test.cc
index 6c08b9b..21d841a 100644
--- a/src/nb/bidirectional_fit_reuse_allocator_test.cc
+++ b/src/nb/bidirectional_fit_reuse_allocator_test.cc
@@ -17,18 +17,15 @@
 #include "nb/bidirectional_fit_reuse_allocator.h"
 
 #include "nb/fixed_no_free_allocator.h"
+#include "nb/pointer_arithmetic.h"
 #include "nb/scoped_ptr.h"
+#include "nb/starboard_aligned_memory_deleter.h"
 #include "starboard/configuration.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-inline bool IsAligned(void* ptr, std::size_t boundary) {
-  uintptr_t ptr_as_int = reinterpret_cast<uintptr_t>(ptr);
-  return ptr_as_int % boundary == 0;
-}
-
 class BidirectionalFitReuseAllocatorTest : public ::testing::Test {
  public:
   static const int kBufferSize = 1 * 1024 * 1024;
@@ -39,18 +36,19 @@
   void ResetAllocator(std::size_t initial_capacity = 0,
                       std::size_t small_allocation_threshold = 0,
                       std::size_t allocation_increment = 0) {
-    nb::scoped_array<uint8_t> buffer(new uint8_t[kBufferSize]);
+    buffer_.reset(static_cast<uint8_t*>(
+        SbMemoryAllocateAligned(nb::Allocator::kMinAlignment, kBufferSize)));
+
     nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator(
-        new nb::FixedNoFreeAllocator(buffer.get(), kBufferSize));
+        new nb::FixedNoFreeAllocator(buffer_.get(), kBufferSize));
     allocator_.reset(new nb::BidirectionalFitReuseAllocator(
         fallback_allocator.get(), initial_capacity, small_allocation_threshold,
         allocation_increment));
 
     fallback_allocator_.swap(fallback_allocator);
-    buffer_.swap(buffer);
   }
 
-  nb::scoped_array<uint8_t> buffer_;
+  std::unique_ptr<uint8_t, nb::AlignedMemoryDeleter> buffer_;
   nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator_;
   nb::scoped_ptr<nb::BidirectionalFitReuseAllocator> allocator_;
 };
@@ -64,7 +62,7 @@
     for (int j = 0; j < SB_ARRAY_SIZE(kBlockSizes); ++j) {
       void* p = allocator_->Allocate(kBlockSizes[j], kAlignments[i]);
       EXPECT_TRUE(p != NULL);
-      EXPECT_EQ(IsAligned(p, kAlignments[i]), true);
+      EXPECT_EQ(nb::IsAligned(p, kAlignments[i]), true);
       allocator_->Free(p);
     }
   }
diff --git a/src/nb/concurrent_map.h b/src/nb/concurrent_map.h
index 39e2b4e..c066872 100644
--- a/src/nb/concurrent_map.h
+++ b/src/nb/concurrent_map.h
@@ -299,7 +299,8 @@
     typename InnerMap::iterator iterator_;
     bool iterator_valid_;
 
-    SB_DISALLOW_COPY_AND_ASSIGN(EntryHandle);
+    EntryHandle(const EntryHandle&) = delete;
+    void operator=(const EntryHandle&) = delete;
   };
 
   class ConstEntryHandle {
@@ -349,7 +350,8 @@
     typename InnerMap::const_iterator iterator_;
     bool iterator_valid_;
 
-    SB_DISALLOW_COPY_AND_ASSIGN(ConstEntryHandle);
+    ConstEntryHandle(const ConstEntryHandle&) = delete;
+    void operator=(const ConstEntryHandle&) = delete;
   };
 
   struct Bucket {
diff --git a/src/nb/first_fit_reuse_allocator_test.cc b/src/nb/first_fit_reuse_allocator_test.cc
index 2e51bd2..dd70c6d 100644
--- a/src/nb/first_fit_reuse_allocator_test.cc
+++ b/src/nb/first_fit_reuse_allocator_test.cc
@@ -17,18 +17,15 @@
 #include "nb/first_fit_reuse_allocator.h"
 
 #include "nb/fixed_no_free_allocator.h"
+#include "nb/pointer_arithmetic.h"
 #include "nb/scoped_ptr.h"
+#include "nb/starboard_aligned_memory_deleter.h"
 #include "starboard/configuration.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
-inline bool IsAligned(void* ptr, std::size_t boundary) {
-  uintptr_t ptr_as_int = reinterpret_cast<uintptr_t>(ptr);
-  return ptr_as_int % boundary == 0;
-}
-
 class FirstFitReuseAllocatorTest : public ::testing::Test {
  public:
   static const int kBufferSize = 1 * 1024 * 1024;
@@ -38,17 +35,17 @@
  protected:
   void ResetAllocator(std::size_t initial_capacity = 0,
                       std::size_t allocation_increment = 0) {
-    nb::scoped_array<uint8_t> buffer(new uint8_t[kBufferSize]);
+    buffer_.reset(static_cast<uint8_t*>(
+        SbMemoryAllocateAligned(nb::Allocator::kMinAlignment, kBufferSize)));
     nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator(
-        new nb::FixedNoFreeAllocator(buffer.get(), kBufferSize));
+        new nb::FixedNoFreeAllocator(buffer_.get(), kBufferSize));
     allocator_.reset(new nb::FirstFitReuseAllocator(
         fallback_allocator.get(), initial_capacity, allocation_increment));
 
     fallback_allocator_.swap(fallback_allocator);
-    buffer_.swap(buffer);
   }
 
-  nb::scoped_array<uint8_t> buffer_;
+  std::unique_ptr<uint8_t, nb::AlignedMemoryDeleter> buffer_;
   nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator_;
   nb::scoped_ptr<nb::FirstFitReuseAllocator> allocator_;
 };
@@ -62,7 +59,7 @@
     for (int j = 0; j < SB_ARRAY_SIZE(kBlockSizes); ++j) {
       void* p = allocator_->Allocate(kBlockSizes[j], kAlignments[i]);
       EXPECT_TRUE(p != NULL);
-      EXPECT_EQ(IsAligned(p, kAlignments[i]), true);
+      EXPECT_EQ(nb::IsAligned(p, kAlignments[i]), true);
       allocator_->Free(p);
     }
   }
diff --git a/src/nb/fixed_no_free_allocator.cc b/src/nb/fixed_no_free_allocator.cc
index f4f8e1c..c7fa52a 100644
--- a/src/nb/fixed_no_free_allocator.cc
+++ b/src/nb/fixed_no_free_allocator.cc
@@ -25,6 +25,7 @@
                                            std::size_t memory_size)
     : memory_start_(memory_start),
       memory_end_(AsPointer(AsInteger(memory_start) + memory_size)) {
+  SB_CHECK(IsAligned(memory_start, kMinAlignment));
   next_memory_ = memory_start_;
 }
 
diff --git a/src/nb/fixed_no_free_allocator.h b/src/nb/fixed_no_free_allocator.h
index 31bfc60..b2c53f7 100644
--- a/src/nb/fixed_no_free_allocator.h
+++ b/src/nb/fixed_no_free_allocator.h
@@ -36,6 +36,7 @@
 // memory and we would like to wrap it in an allocator.
 class FixedNoFreeAllocator : public Allocator {
  public:
+  // Requires aligned memory to at least |nb::kMinAlignment|.
   FixedNoFreeAllocator(void* memory_start, std::size_t memory_size);
   void* Allocate(std::size_t size) { return Allocate(&size, 1, true); }
 
diff --git a/src/nb/fixed_no_free_allocator_test.cc b/src/nb/fixed_no_free_allocator_test.cc
index 89b4545..5789fb9 100644
--- a/src/nb/fixed_no_free_allocator_test.cc
+++ b/src/nb/fixed_no_free_allocator_test.cc
@@ -17,6 +17,7 @@
 #include "nb/fixed_no_free_allocator.h"
 
 #include "nb/pointer_arithmetic.h"
+#include "nb/starboard_aligned_memory_deleter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class FixedNoFreeAllocatorTest : public ::testing::Test {
@@ -29,30 +30,32 @@
   static const std::size_t kMaxAllocations = 64;
   static const std::size_t kBufferSize = kAllocationSize * kMaxAllocations;
 
-  char buffer_[kBufferSize];
+  std::unique_ptr<uint8_t, nb::AlignedMemoryDeleter> buffer_;
   nb::FixedNoFreeAllocator allocator_;
 };
 
 FixedNoFreeAllocatorTest::FixedNoFreeAllocatorTest()
-    : allocator_(buffer_, kBufferSize) {}
+    : buffer_(static_cast<uint8_t*>(
+          SbMemoryAllocateAligned(nb::Allocator::kMinAlignment, kBufferSize))),
+      allocator_(buffer_.get(), kBufferSize) {}
 
 TEST_F(FixedNoFreeAllocatorTest, CanDoSimpleAllocations) {
   void* allocation = allocator_.Allocate(kAllocationSize);
 
-  EXPECT_GE(allocation, buffer_);
-  EXPECT_LE(
-      reinterpret_cast<uintptr_t>(allocation),
-      reinterpret_cast<uintptr_t>(buffer_) + kBufferSize - kAllocationSize);
+  EXPECT_GE(allocation, buffer_.get());
+  EXPECT_LE(reinterpret_cast<uintptr_t>(allocation),
+            reinterpret_cast<uintptr_t>(buffer_.get()) + kBufferSize -
+                kAllocationSize);
 }
 
 TEST_F(FixedNoFreeAllocatorTest, CanDoMultipleAllocationsProperly) {
   void* buffers[kMaxAllocations];
   for (int i = 0; i < kMaxAllocations; ++i) {
     buffers[i] = allocator_.Allocate(kAllocationSize);
-    EXPECT_GE(buffers[i], buffer_);
-    EXPECT_LE(
-        reinterpret_cast<uintptr_t>(buffers[i]),
-        reinterpret_cast<uintptr_t>(buffer_) + kBufferSize - kAllocationSize);
+    EXPECT_GE(buffers[i], buffer_.get());
+    EXPECT_LE(reinterpret_cast<uintptr_t>(buffers[i]),
+              reinterpret_cast<uintptr_t>(buffer_.get()) + kBufferSize -
+                  kAllocationSize);
 
     // Make sure this allocation doesn't overlap with any previous ones.
     for (int j = 0; j < i; ++j) {
@@ -72,10 +75,10 @@
   for (int i = 0; i < kMaxAllocations; ++i) {
     void* current_allocation = allocator_.Allocate(kAllocationSize);
 
-    EXPECT_GE(current_allocation, buffer_);
-    EXPECT_LE(
-        reinterpret_cast<uintptr_t>(current_allocation),
-        reinterpret_cast<uintptr_t>(buffer_) + kBufferSize - kAllocationSize);
+    EXPECT_GE(current_allocation, buffer_.get());
+    EXPECT_LE(reinterpret_cast<uintptr_t>(current_allocation),
+              reinterpret_cast<uintptr_t>(buffer_.get()) + kBufferSize -
+                  kAllocationSize);
 
     allocator_.Free(current_allocation);
   }
@@ -85,10 +88,10 @@
   for (int i = 0; i < kMaxAllocations; ++i) {
     void* current_allocation = allocator_.Allocate(kAllocationSize);
 
-    EXPECT_GE(current_allocation, buffer_);
-    EXPECT_LE(
-        reinterpret_cast<uintptr_t>(current_allocation),
-        reinterpret_cast<uintptr_t>(buffer_) + kBufferSize - kAllocationSize);
+    EXPECT_GE(current_allocation, buffer_.get());
+    EXPECT_LE(reinterpret_cast<uintptr_t>(current_allocation),
+              reinterpret_cast<uintptr_t>(buffer_.get()) + kBufferSize -
+                  kAllocationSize);
 
     allocator_.Free(current_allocation);
   }
@@ -109,10 +112,10 @@
     EXPECT_EQ(0, reinterpret_cast<uintptr_t>(current_allocation) %
                      kAllocationAlignment);
 
-    EXPECT_GE(current_allocation, buffer_);
-    EXPECT_LE(
-        reinterpret_cast<uintptr_t>(current_allocation),
-        reinterpret_cast<uintptr_t>(buffer_) + kBufferSize - kAllocationSize);
+    EXPECT_GE(current_allocation, buffer_.get());
+    EXPECT_LE(reinterpret_cast<uintptr_t>(current_allocation),
+              reinterpret_cast<uintptr_t>(buffer_.get()) + kBufferSize -
+                  kAllocationSize);
 
     allocator_.Free(current_allocation);
   }
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index ce9e71d..6078f07 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -36,6 +36,7 @@
       'conditions': [
         ['OS=="starboard"', {
           'sources': [
+            'allocator.cc',
             'allocator.h',
             'analytics/memory_tracker.cc',
             'analytics/memory_tracker.h',
@@ -75,6 +76,7 @@
             'simple_thread.h',
             'simple_profiler.cc',
             'simple_profiler.h',
+            'starboard_aligned_memory_deleter.h',
             'std_allocator.h',
             'string_interner.cc',
             'string_interner.h',
diff --git a/src/nb/pointer_arithmetic.h b/src/nb/pointer_arithmetic.h
index cb1539a..d34cba0 100644
--- a/src/nb/pointer_arithmetic.h
+++ b/src/nb/pointer_arithmetic.h
@@ -61,8 +61,8 @@
 // Helper method for subclasses to determine if a given address or value
 // is aligned or not.
 template <typename T>
-static bool IsAligned(T value, T alignment) {
-  return value % alignment == 0;
+static bool IsAligned(T value, size_t alignment) {
+  return AsInteger(value) % alignment == 0;
 }
 
 }  // namespace nb
diff --git a/src/nb/reuse_allocator_base.cc b/src/nb/reuse_allocator_base.cc
index 95189ac..2d6943a 100644
--- a/src/nb/reuse_allocator_base.cc
+++ b/src/nb/reuse_allocator_base.cc
@@ -29,10 +29,6 @@
 // Minimum block size to avoid extremely small blocks inside the block list and
 // to ensure that a zero sized allocation will return a non-zero sized block.
 const std::size_t kMinBlockSizeBytes = 16;
-// Using a minimum value for size and alignment keeps things rounded and aligned
-// and help us avoid creating tiny and/or badly misaligned free blocks.  Also
-// ensures even for a 0-byte request will get a unique block.
-const std::size_t kMinAlignment = 16;
 // The max lines of allocation to print inside PrintAllocations().  Set to 0 to
 // print all allocations.
 const int kMaxAllocationLinesToPrint = 0;
diff --git a/src/nb/simple_thread.h b/src/nb/simple_thread.h
index b01a58c..1f9132d 100644
--- a/src/nb/simple_thread.h
+++ b/src/nb/simple_thread.h
@@ -56,7 +56,8 @@
   SbThread thread_;

   starboard::atomic_bool join_called_;

 

-  SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);

+  SimpleThread(const SimpleThread&) = delete;

+  void operator=(const SimpleThread&) = delete;

 };

 

 }  // namespace nb

diff --git a/src/nb/starboard_aligned_memory_deleter.h b/src/nb/starboard_aligned_memory_deleter.h
new file mode 100644
index 0000000..4f307c1
--- /dev/null
+++ b/src/nb/starboard_aligned_memory_deleter.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NB_STARBOARD_ALIGNED_MEMORY_DELETER_H_
+#define NB_STARBOARD_ALIGNED_MEMORY_DELETER_H_
+
+#include "starboard/memory.h"
+
+namespace nb {
+
+struct AlignedMemoryDeleter {
+  void operator()(uint8_t* p) { SbMemoryDeallocateAligned(p); }
+};
+
+}  // namespace nb
+
+#endif  // NB_STARBOARD_ALIGNED_MEMORY_DELETER_H_
diff --git a/src/nb/string_interner.h b/src/nb/string_interner.h
index e739b69..f842449 100644
--- a/src/nb/string_interner.h
+++ b/src/nb/string_interner.h
@@ -65,13 +65,15 @@
   mutable starboard::Mutex mutex_;
   mutable std::string scratch_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(StringInterner);
+  StringInterner(const StringInterner&) = delete;
+  void operator=(const StringInterner&) = delete;
 };
 
 class ConcurrentStringInterner {
  public:
   explicit ConcurrentStringInterner(size_t table_size = 32);
-  ~ConcurrentStringInterner();  // All outstanding const std::string* are invalidated.
+  ~ConcurrentStringInterner();  // All outstanding const std::string* are
+                                // invalidated.
 
   // Returns an equivalent string to the input. If the input is missing from
   // the data store then a copy-by-value is made.
@@ -90,7 +92,8 @@
   const StringInterner& GetBucket(const char* string, size_t n) const;
   std::vector<StringInterner*> string_interner_table_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ConcurrentStringInterner);
+  ConcurrentStringInterner(const ConcurrentStringInterner&) = delete;
+  void operator=(const ConcurrentStringInterner&) = delete;
 };
 
 }  // namespace nb
diff --git a/src/nb/test_thread.h b/src/nb/test_thread.h
index 0ea5768..4f2da5d 100644
--- a/src/nb/test_thread.h
+++ b/src/nb/test_thread.h
@@ -35,14 +35,11 @@
   void Start() {
     SbThreadEntryPoint entry_point = ThreadEntryPoint;
 
-    thread_ = SbThreadCreate(
-        0,                     // default stack_size.
-        kSbThreadNoPriority,   // default priority.
-        kSbThreadNoAffinity,   // default affinity.
-        true,                  // joinable.
-        "TestThread",
-        entry_point,
-        this);
+    thread_ = SbThreadCreate(0,                    // default stack_size.
+                             kSbThreadNoPriority,  // default priority.
+                             kSbThreadNoAffinity,  // default affinity.
+                             true,                 // joinable.
+                             "TestThread", entry_point, this);
 
     if (kSbThreadInvalid == thread_) {
       ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread.";
@@ -65,7 +62,8 @@
 
   SbThread thread_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(TestThread);
+  TestThread(const TestThread&) = delete;
+  void operator=(const TestThread&) = delete;
 };
 
 }  // namespace nb.
diff --git a/src/nb/thread_local_boolean.h b/src/nb/thread_local_boolean.h
index d91a744..c27baf6 100644
--- a/src/nb/thread_local_boolean.h
+++ b/src/nb/thread_local_boolean.h
@@ -44,7 +44,8 @@
   ThreadLocalPointer<void> tlp_;
   const bool default_value_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean);
+  ThreadLocalBoolean(const ThreadLocalBoolean&) = delete;
+  void operator=(const ThreadLocalBoolean&) = delete;
 };
 
 }  // namespace nb.
diff --git a/src/nb/thread_local_object.h b/src/nb/thread_local_object.h
index 6299f51..741227f 100644
--- a/src/nb/thread_local_object.h
+++ b/src/nb/thread_local_object.h
@@ -77,11 +77,11 @@
     CheckCurrentThreadAllowedToDestruct();
     if (SB_DLOG_IS_ON(FATAL)) {
       SB_DCHECK(entry_set_.size() < 2)
-        << "Logic error: Some threads may still be accessing the objects that "
-        << "are about to be destroyed. Only one object is expected and that "
-        << "should be for the main thread. The caller should ensure that "
-        << "other threads that access this object are externally "
-        << "synchronized.";
+          << "Logic error: Some threads may still be accessing the objects "
+          << "that are about to be destroyed. Only one object is expected "
+          << "and that should be for the main thread. The caller should "
+          << "ensure that other threads that access this object are"
+          << " externally synchronized.";
     }
     // No locking is done because the entries should not be accessed by
     // different threads while this object is shutting down. If access is
@@ -101,20 +101,20 @@
   // Warns if there is a misuse of this object.
   void CheckCurrentThreadAllowedToDestruct() const {
     if (kSbThreadInvalidId == constructing_thread_id_) {
-      return;   // EnableDestructionByAnyThread() called.
+      return;  // EnableDestructionByAnyThread() called.
     }
     const SbThreadId curr_thread_id = SbThreadGetId();
     if (curr_thread_id == constructing_thread_id_) {
-      return;   // Same thread that constructed this.
+      return;  // Same thread that constructed this.
     }
 
     if (SB_DLOG_IS_ON(FATAL)) {
-      SB_DCHECK(false)
-          << "ThreadLocalObject<T> was created in thread "
-          << constructing_thread_id_ << "\nbut was destroyed by "
-          << curr_thread_id << ". If this is intentional then call "
-          << "EnableDestructionByAnyThread() to silence this "
-          << "warning.";
+      SB_DCHECK(false) << "ThreadLocalObject<T> was created in thread "
+                       << constructing_thread_id_ << "\nbut was destroyed by "
+                       << curr_thread_id
+                       << ". If this is intentional then call "
+                       << "EnableDestructionByAnyThread() to silence this "
+                       << "warning.";
     }
   }
 
@@ -158,7 +158,9 @@
   // Returns the pointer if it exists in the current thread, otherwise NULL.
   Type* GetIfExists() const {
     Entry* entry = GetEntryIfExists();
-    if (!entry) { return NULL; }
+    if (!entry) {
+      return NULL;
+    }
     return entry->ptr_;
   }
 
@@ -178,8 +180,7 @@
 
  private:
   struct Entry {
-    Entry(ThreadLocalObject* own, Type* ptr) : owner_(own), ptr_(ptr) {
-    }
+    Entry(ThreadLocalObject* own, Type* ptr) : owner_(own), ptr_(ptr) {}
     ~Entry() {
       ptr_ = NULL;
       owner_ = NULL;
@@ -228,7 +229,8 @@
   // thread that destroyed this object.
   SbThreadId constructing_thread_id_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalObject<Type>);
+  ThreadLocalObject<Type>(const ThreadLocalObject<Type>&) = delete;
+  void operator=(const ThreadLocalObject<Type>&) = delete;
 };
 
 }  // namespace nb
diff --git a/src/nb/thread_local_pointer.h b/src/nb/thread_local_pointer.h
index d21f81f..5d0e7f3 100644
--- a/src/nb/thread_local_pointer.h
+++ b/src/nb/thread_local_pointer.h
@@ -46,7 +46,8 @@
 
  private:
   SbThreadLocalKey slot_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);
+  ThreadLocalPointer<Type>(const ThreadLocalPointer<Type>&) = delete;
+  void operator=(const ThreadLocalPointer<Type>&) = delete;
 };
 
 }  // namespace nb.
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
index ac9be3c..89b4be8 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
@@ -27,6 +27,7 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewParent;
 import android.widget.FrameLayout;
+import dev.cobalt.media.MediaCodecUtil;
 import dev.cobalt.media.VideoSurfaceView;
 import dev.cobalt.util.Log;
 import dev.cobalt.util.UsedByNative;
@@ -45,7 +46,9 @@
   private static final java.lang.String META_DATA_APP_URL = "cobalt.APP_URL";
 
   private static final String SPLASH_URL_ARG = "--fallback_splash_screen_url=";
+  private static final String SPLASH_TOPICS_ARG = "--fallback_splash_screen_topics=";
   private static final java.lang.String META_DATA_SPLASH_URL = "cobalt.SPLASH_URL";
+  private static final java.lang.String META_DATA_SPLASH_TOPICS = "cobalt.SPLASH_TOPIC";
 
   private static final String FORCE_MIGRATION_FOR_STORAGE_PARTITIONING =
       "--force_migration_for_storage_partitioning";
@@ -107,6 +110,9 @@
 
   @Override
   protected void onStart() {
+    if (!isReleaseBuild()) {
+      MediaCodecUtil.dumpAllDecoders();
+    }
     if (forceCreateNewVideoSurfaceView) {
       Log.w(TAG, "Force to create a new video surface.");
       createNewSurfaceView();
@@ -174,7 +180,9 @@
     boolean hasUrlArg = hasArg(args, URL_ARG);
     // If the splash screen url arg isn't specified, get it from AndroidManifest.xml.
     boolean hasSplashUrlArg = hasArg(args, SPLASH_URL_ARG);
-    if (!hasUrlArg || !hasSplashUrlArg) {
+    // If the splash screen topics arg isn't specified, get it from AndroidManifest.xml.
+    boolean hasSplashTopicsArg = hasArg(args, SPLASH_TOPICS_ARG);
+    if (!hasUrlArg || !hasSplashUrlArg || !hasSplashTopicsArg) {
       try {
         ActivityInfo ai =
             getPackageManager()
@@ -192,6 +200,12 @@
               args.add(SPLASH_URL_ARG + splashUrl);
             }
           }
+          if (!hasSplashTopicsArg) {
+            String splashTopics = ai.metaData.getString(META_DATA_SPLASH_TOPICS);
+            if (splashTopics != null) {
+              args.add(SPLASH_TOPICS_ARG + splashTopics);
+            }
+          }
           if (ai.metaData.getBoolean(META_FORCE_MIGRATION_FOR_STORAGE_PARTITIONING)) {
             args.add(FORCE_MIGRATION_FOR_STORAGE_PARTITIONING);
           }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
index 2fb2c95..b6de9b0 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
@@ -41,6 +41,10 @@
 public void onCreate() {
   super.onCreate();
   Log.i(TAG, "Creating a Media playback foreground service.");
+  if (getStarboardBridge() == null) {
+    Log.e(TAG, "StarboardBridge already destroyed.");
+    return;
+  }
   getStarboardBridge().onServiceStart(this);
   context = getApplicationContext();
 }
@@ -61,10 +65,14 @@
 
 @Override
 public void onDestroy() {
+  if (getStarboardBridge() == null) {
+    Log.e(TAG, "StarboardBridge already destroyed.");
+    return;
+  }
   getStarboardBridge().onServiceDestroy(this);
   context = null;
   super.onDestroy();
-  Log.i(TAG, "Destorying the Media playback service.");
+  Log.i(TAG, "Destroying the Media playback service.");
 }
 
 public void startService() {
@@ -139,6 +147,10 @@
 
 @UsedByNative
 protected StarboardBridge getStarboardBridge() {
+  if (getApplication() == null) {
+    Log.e(TAG, "Application already destroyed.");
+    return null;
+  }
   return ((StarboardBridge.HostApplication) getApplication()).getStarboardBridge();
 }
 
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VoiceRecognizer.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VoiceRecognizer.java
index 06cc02f..799aef9 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VoiceRecognizer.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VoiceRecognizer.java
@@ -122,7 +122,8 @@
     this.nativeSpeechRecognizerImpl = nativeSpeechRecognizer;
 
     if (this.audioPermissionRequester.requestRecordAudioPermission(
-        this.nativeSpeechRecognizerImpl)) {
+        this.nativeSpeechRecognizerImpl) &&
+        SpeechRecognizer.isRecognitionAvailable(context)) {
       startRecognitionInternal();
     } else {
       mainHandler.post(
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
index 554d8d5..b80fa03 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
@@ -48,9 +48,18 @@
   @SuppressWarnings("unused")
   @UsedByNative
   AudioTrackBridge createAudioTrackBridge(
-      int sampleType, int sampleRate, int channelCount, int preferredBufferSizeInBytes) {
+      int sampleType,
+      int sampleRate,
+      int channelCount,
+      int preferredBufferSizeInBytes,
+      int tunnelModeAudioSessionId) {
     AudioTrackBridge audioTrackBridge =
-        new AudioTrackBridge(sampleType, sampleRate, channelCount, preferredBufferSizeInBytes);
+        new AudioTrackBridge(
+            sampleType,
+            sampleRate,
+            channelCount,
+            preferredBufferSizeInBytes,
+            tunnelModeAudioSessionId);
     if (!audioTrackBridge.isAudioTrackValid()) {
       Log.e(TAG, "AudioTrackBridge has invalid audio track");
       return null;
@@ -128,4 +137,29 @@
     }
     return AudioTrack.getMinBufferSize(sampleRate, channelConfig, sampleType);
   }
+
+  /** Generate audio session id used by tunneled playback. */
+  @SuppressWarnings("unused")
+  @UsedByNative
+  int generateTunnelModeAudioSessionId(int numberOfChannels) {
+    // Android 9.0 (Build.VERSION.SDK_INT >= 28) support v2 sync header that
+    // aligns sync header with audio frame size. V1 sync header has alignment
+    // issues for multi-channel audio.
+    if (Build.VERSION.SDK_INT < 28) {
+      // Currently we only support int16 under tunnel mode.
+      final int sampleSizeInBytes = 2;
+      final int frameSizeInBytes = sampleSizeInBytes * numberOfChannels;
+      if (AudioTrackBridge.AV_SYNC_HEADER_V1_SIZE % frameSizeInBytes != 0) {
+        Log.w(
+            TAG,
+            String.format(
+                "Disable tunnel mode due to sampleSizeInBytes (%d) * numberOfChannels (%d) isn't"
+                    + " aligned to AV_SYNC_HEADER_V1_SIZE (%d).",
+                sampleSizeInBytes, numberOfChannels, AudioTrackBridge.AV_SYNC_HEADER_V1_SIZE));
+        return -1;
+      }
+    }
+    AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+    return audioManager.generateAudioSessionId();
+  }
 }
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
index 419f5d8..b8d5a53 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -26,18 +26,45 @@
 import dev.cobalt.util.Log;
 import dev.cobalt.util.UsedByNative;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 
-// A wrapper of the android AudioTrack class.
-// Android AudioTrack would not start playing until the buffer is fully
-// filled once.
+/**
+ * A wrapper of the android AudioTrack class. Android AudioTrack would not start playing until the
+ * buffer is fully filled once.
+ */
 @UsedByNative
 public class AudioTrackBridge {
+  // Also used by AudioOutputManager.
+  static final int AV_SYNC_HEADER_V1_SIZE = 16;
+
   private AudioTrack audioTrack;
   private AudioTimestamp audioTimestamp = new AudioTimestamp();
   private long maxFramePositionSoFar = 0;
 
+  private final boolean tunnelModeEnabled;
+  // The following variables are used only when |tunnelModeEnabled| is true.
+  private ByteBuffer avSyncHeader;
+  private int avSyncPacketBytesRemaining;
+
+  private static int getBytesPerSample(int audioFormat) {
+    switch (audioFormat) {
+      case AudioFormat.ENCODING_PCM_16BIT:
+        return 2;
+      case AudioFormat.ENCODING_PCM_FLOAT:
+        return 4;
+      case AudioFormat.ENCODING_INVALID:
+      default:
+        throw new RuntimeException("Unsupport audio format " + audioFormat);
+    }
+  }
+
   public AudioTrackBridge(
-      int sampleType, int sampleRate, int channelCount, int preferredBufferSizeInBytes) {
+      int sampleType,
+      int sampleRate,
+      int channelCount,
+      int preferredBufferSizeInBytes,
+      int tunnelModeAudioSessionId) {
+    tunnelModeEnabled = tunnelModeAudioSessionId != -1;
     int channelConfig;
     switch (channelCount) {
       case 1:
@@ -53,11 +80,40 @@
         throw new RuntimeException("Unsupported channel count: " + channelCount);
     }
 
-    AudioAttributes attributes =
-        new AudioAttributes.Builder()
-            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-            .setUsage(AudioAttributes.USAGE_MEDIA)
-            .build();
+    AudioAttributes attributes;
+    if (tunnelModeEnabled) {
+      // Android 9.0 (Build.VERSION.SDK_INT >= 28) support v2 sync header that aligns sync header
+      // with audio frame size. V1 sync header has alignment issues for multi-channel audio.
+      if (Build.VERSION.SDK_INT < 28) {
+        int frameSize = getBytesPerSample(sampleType) * channelCount;
+        // This shouldn't happen as it should have been checked in
+        // AudioOutputManager.generateTunnelModeAudioSessionId().
+        if (AV_SYNC_HEADER_V1_SIZE % frameSize != 0) {
+          audioTrack = null;
+          String errorMessage =
+              String.format(
+                  "Enable tunnel mode when frame size is unaligned, "
+                      + "sampleType: %d, channel: %d, sync header size: %d.",
+                  sampleType, channelCount, AV_SYNC_HEADER_V1_SIZE);
+          Log.e(TAG, errorMessage);
+          throw new RuntimeException(errorMessage);
+        }
+      }
+      attributes =
+          new AudioAttributes.Builder()
+              .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
+              .setFlags(AudioAttributes.FLAG_HW_AV_SYNC)
+              .setUsage(AudioAttributes.USAGE_MEDIA)
+              .build();
+    } else {
+      // TODO: Investigate if we can use |CONTENT_TYPE_MOVIE| for AudioTrack
+      //       used by video playback.
+      attributes =
+          new AudioAttributes.Builder()
+              .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+              .setUsage(AudioAttributes.USAGE_MEDIA)
+              .build();
+    }
     AudioFormat format =
         new AudioFormat.Builder()
             .setEncoding(sampleType)
@@ -66,6 +122,10 @@
             .build();
 
     int audioTrackBufferSize = preferredBufferSizeInBytes;
+    // TODO: Investigate if this implementation could be refined.
+    // It is not necessary to loop until 0 since there is new implementation based on
+    // AudioTrack.getMinBufferSize(). Especially for tunnel mode, it would fail if audio HAL does
+    // not support tunnel mode and then it is not helpful to retry.
     while (audioTrackBufferSize > 0) {
       try {
         audioTrack =
@@ -74,7 +134,9 @@
                 format,
                 audioTrackBufferSize,
                 AudioTrack.MODE_STREAM,
-                AudioManager.AUDIO_SESSION_ID_GENERATE);
+                tunnelModeEnabled
+                    ? tunnelModeAudioSessionId
+                    : AudioManager.AUDIO_SESSION_ID_GENERATE);
       } catch (Exception e) {
         audioTrack = null;
       }
@@ -104,6 +166,8 @@
       audioTrack.release();
     }
     audioTrack = null;
+    avSyncHeader = null;
+    avSyncPacketBytesRemaining = 0;
   }
 
   @SuppressWarnings("unused")
@@ -144,15 +208,22 @@
       return;
     }
     audioTrack.flush();
+    avSyncHeader = null;
+    avSyncPacketBytesRemaining = 0;
   }
 
   @SuppressWarnings("unused")
   @UsedByNative
-  private int write(byte[] audioData, int sizeInBytes) {
+  private int write(byte[] audioData, int sizeInBytes, long presentationTimeInMicroseconds) {
     if (audioTrack == null) {
       Log.e(TAG, "Unable to write with NULL audio track.");
       return 0;
     }
+
+    if (tunnelModeEnabled) {
+      return writeWithAvSync(audioData, sizeInBytes, presentationTimeInMicroseconds);
+    }
+
     if (Build.VERSION.SDK_INT >= 23) {
       return audioTrack.write(audioData, 0, sizeInBytes, AudioTrack.WRITE_NON_BLOCKING);
     } else {
@@ -161,6 +232,66 @@
     }
   }
 
+  private int writeWithAvSync(
+      byte[] audioData, int sizeInBytes, long presentationTimeInMicroseconds) {
+    if (audioTrack == null) {
+      throw new RuntimeException("writeWithAvSync() is called when audioTrack is null.");
+    }
+
+    if (!tunnelModeEnabled) {
+      throw new RuntimeException("writeWithAvSync() is called when tunnelModeEnabled is false.");
+    }
+
+    long presentationTimeInNanoseconds = presentationTimeInMicroseconds * 1000;
+
+    // Android support tunnel mode from 5.0 (API level 21), but the app has to manually write the
+    // sync header before API 23, where the write() function with presentation timestamp is
+    // introduced.
+    // Set the following constant to |false| to test manual sync header writing in API level 23 or
+    // later.  Note that the code to write sync header manually only supports v1 sync header.
+    final boolean useAutoSyncHeaderWrite = false;
+    if (useAutoSyncHeaderWrite && Build.VERSION.SDK_INT >= 23) {
+      ByteBuffer byteBuffer = ByteBuffer.wrap(audioData);
+      return audioTrack.write(
+          byteBuffer, sizeInBytes, AudioTrack.WRITE_NON_BLOCKING, presentationTimeInNanoseconds);
+    }
+
+    if (avSyncHeader == null) {
+      avSyncHeader = ByteBuffer.allocate(AV_SYNC_HEADER_V1_SIZE);
+      avSyncHeader.order(ByteOrder.BIG_ENDIAN);
+      avSyncHeader.putInt(0x55550001);
+    }
+
+    if (avSyncPacketBytesRemaining == 0) {
+      avSyncHeader.putInt(4, sizeInBytes);
+      avSyncHeader.putLong(8, presentationTimeInNanoseconds);
+      avSyncHeader.position(0);
+      avSyncPacketBytesRemaining = sizeInBytes;
+    }
+
+    if (avSyncHeader.remaining() > 0) {
+      int ret =
+          audioTrack.write(avSyncHeader, avSyncHeader.remaining(), AudioTrack.WRITE_NON_BLOCKING);
+      if (ret < 0) {
+        avSyncPacketBytesRemaining = 0;
+        return ret;
+      }
+      if (avSyncHeader.remaining() > 0) {
+        return 0;
+      }
+    }
+
+    int sizeToWrite = Math.min(avSyncPacketBytesRemaining, sizeInBytes);
+    ByteBuffer byteBuffer = ByteBuffer.wrap(audioData);
+    int ret = audioTrack.write(byteBuffer, sizeToWrite, AudioTrack.WRITE_NON_BLOCKING);
+    if (ret < 0) {
+      avSyncPacketBytesRemaining = 0;
+      return ret;
+    }
+    avSyncPacketBytesRemaining -= ret;
+    return ret;
+  }
+
   @SuppressWarnings("unused")
   @UsedByNative
   private int write(float[] audioData, int sizeInFloats) {
@@ -168,6 +299,9 @@
       Log.e(TAG, "Unable to write with NULL audio track.");
       return 0;
     }
+    if (tunnelModeEnabled) {
+      throw new RuntimeException("Float sample is not supported under tunnel mode.");
+    }
     return audioTrack.write(audioData, 0, sizeInFloats, AudioTrack.WRITE_NON_BLOCKING);
   }
 
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
index 71e6857..03fb7cc 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -682,52 +682,69 @@
    * Debug utility function that can be locally added to dump information about all decoders on a
    * particular system.
    */
-  @SuppressWarnings("unused")
-  private static void dumpAllDecoders() {
+  public static void dumpAllDecoders() {
+    String decoderDumpString = "";
     for (MediaCodecInfo info : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
       if (info.isEncoder()) {
         continue;
       }
       for (String supportedType : info.getSupportedTypes()) {
         String name = info.getName();
-        CodecCapabilities codecCapabilities = info.getCapabilitiesForType(supportedType);
-        Log.v(TAG, "==================================================");
-        Log.v(TAG, String.format("name: %s", name));
-        Log.v(TAG, String.format("supportedType: %s", supportedType));
-        Log.v(
-            TAG, String.format("codecBlackList.contains(name): %b", codecBlackList.contains(name)));
-        Log.v(
-            TAG,
+        decoderDumpString +=
             String.format(
-                "FEATURE_SecurePlayback: %b",
-                codecCapabilities.isFeatureSupported(
-                    MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback)));
+                "name: %s (%s, %s):",
+                name,
+                supportedType,
+                codecBlackList.contains(name) ? "blacklisted" : "not blacklisted");
+        CodecCapabilities codecCapabilities = info.getCapabilitiesForType(supportedType);
         VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
         if (videoCapabilities != null) {
-          Log.v(
-              TAG,
+          decoderDumpString +=
               String.format(
-                  "videoCapabilities.getSupportedWidths(): %s",
-                  videoCapabilities.getSupportedWidths().toString()));
-          Log.v(
-              TAG,
-              String.format(
-                  "videoCapabilities.getSupportedHeights(): %s",
-                  videoCapabilities.getSupportedHeights().toString()));
-          Log.v(
-              TAG,
-              String.format(
-                  "videoCapabilities.getBitrateRange(): %s",
-                  videoCapabilities.getBitrateRange().toString()));
-          Log.v(
-              TAG,
-              String.format(
-                  "videoCapabilities.getSupportedFrameRates(): %s",
-                  videoCapabilities.getSupportedFrameRates().toString()));
+                  "\n\t\t"
+                      + "widths: %s, "
+                      + "heights: %s, "
+                      + "bitrates: %s, "
+                      + "framerates: %s, ",
+                  videoCapabilities.getSupportedWidths().toString(),
+                  videoCapabilities.getSupportedHeights().toString(),
+                  videoCapabilities.getBitrateRange().toString(),
+                  videoCapabilities.getSupportedFrameRates().toString());
         }
-        Log.v(TAG, "==================================================");
-        Log.v(TAG, "");
+        boolean adaptivePlayback =
+            codecCapabilities.isFeatureSupported(
+                MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
+        boolean securePlayback =
+            codecCapabilities.isFeatureSupported(
+                MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+        boolean tunneledPlayback =
+            codecCapabilities.isFeatureSupported(
+                MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
+        if (adaptivePlayback || securePlayback || tunneledPlayback) {
+          decoderDumpString +=
+              String.format(
+                  "(%s%s%s",
+                  adaptivePlayback ? "AdaptivePlayback, " : "",
+                  securePlayback ? "SecurePlayback, " : "",
+                  tunneledPlayback ? "TunneledPlayback, " : "");
+          // Remove trailing space and comma
+          decoderDumpString = decoderDumpString.substring(0, decoderDumpString.length() - 2);
+          decoderDumpString += ")";
+        } else {
+          decoderDumpString += " No extra features supported";
+        }
+        decoderDumpString += "\n";
       }
     }
+    Log.v(
+        TAG,
+        String.format(
+            " \n"
+                + "==================================================\n"
+                + "Full list of decoder features: [AdaptivePlayback, SecurePlayback,"
+                + " TunneledPlayback]\n"
+                + "Unsupported features for each codec are not listed\n"
+                + decoderDumpString
+                + "=================================================="));
   }
 }
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id
new file mode 100644
index 0000000..d3b971d
--- /dev/null
+++ b/src/starboard/android/apk/build.id
@@ -0,0 +1 @@
+289402
\ No newline at end of file
diff --git a/src/starboard/android/shared/audio_sink_min_required_frames_tester.cc b/src/starboard/android/shared/audio_sink_min_required_frames_tester.cc
index a5a77a7..88f3762 100644
--- a/src/starboard/android/shared/audio_sink_min_required_frames_tester.cc
+++ b/src/starboard/android/shared/audio_sink_min_required_frames_tester.cc
@@ -123,7 +123,8 @@
         min_required_frames_ * task.number_of_channels *
             GetSampleSize(task.sample_type),
         &MinRequiredFramesTester::UpdateSourceStatusFunc,
-        &MinRequiredFramesTester::ConsumeFramesFunc, this);
+        &MinRequiredFramesTester::ConsumeFramesFunc,
+        &MinRequiredFramesTester::ErrorFunc, 0, -1, this);
     {
       ScopedLock scoped_lock(mutex_);
       wait_timeout = !condition_variable_.WaitTimed(kSbTimeSecond * 5);
@@ -174,6 +175,14 @@
   tester->ConsumeFrames(frames_consumed);
 }
 
+// static
+void MinRequiredFramesTester::ErrorFunc(bool capability_changed,
+                                        void* context) {
+  // TODO: Handle errors during minimum frames test, maybe by terminating the
+  //       test earlier.
+  SB_NOTREACHED();
+}
+
 void MinRequiredFramesTester::UpdateSourceStatus(int* frames_in_buffer,
                                                  int* offset_in_frames,
                                                  bool* is_playing,
diff --git a/src/starboard/android/shared/audio_sink_min_required_frames_tester.h b/src/starboard/android/shared/audio_sink_min_required_frames_tester.h
index 290011d..e689e96 100644
--- a/src/starboard/android/shared/audio_sink_min_required_frames_tester.h
+++ b/src/starboard/android/shared/audio_sink_min_required_frames_tester.h
@@ -85,6 +85,7 @@
   static void ConsumeFramesFunc(int frames_consumed,
                                 SbTime frames_consumed_at,
                                 void* context);
+  static void ErrorFunc(bool capability_changed, void* context);
   void UpdateSourceStatus(int* frames_in_buffer,
                           int* offset_in_frames,
                           bool* is_playing,
diff --git a/src/starboard/android/shared/audio_track_audio_sink_type.cc b/src/starboard/android/shared/audio_track_audio_sink_type.cc
index 44dffc8..cf9d744 100644
--- a/src/starboard/android/shared/audio_track_audio_sink_type.cc
+++ b/src/starboard/android/shared/audio_track_audio_sink_type.cc
@@ -29,11 +29,23 @@
 namespace shared {
 namespace {
 
+// The same as AudioTrack.ERROR_DEAD_OBJECT.
+const int kAudioTrackErrorDeadObject = -6;
+
 // The maximum number of frames that can be written to android audio track per
 // write request. If we don't set this cap for writing frames to audio track,
 // we will repeatedly allocate a large byte array which cannot be consumed by
 // audio track completely.
 const int kMaxFramesPerRequest = 65536;
+
+// Most Android audio HAL updates audio time for A/V synchronization on audio
+// sync frames. For example, audio HAL may try to render when it gets an entire
+// sync frame and then update audio time. Shorter duration of sync frame
+// improves the accuracy of audio time, especially at the beginning of a
+// playback, as otherwise the audio time during the initial update may be too
+// large (non zero) and results in dropped video frames.
+const SbTime kMaxDurationPerRequestInTunnelMode = 16 * kSbTimeMillisecond;
+
 const jint kNoOffset = 0;
 const size_t kSilenceFramesPerAppend = 1024;
 
@@ -74,6 +86,12 @@
   return static_cast<uint8_t*>(pointer) + offset;
 }
 
+int GetMaxFramesPerRequestForTunnelMode(int sampling_frequency_hz) {
+  auto max_frames = kMaxDurationPerRequestInTunnelMode * sampling_frequency_hz /
+                    kSbTimeSecond;
+  return (max_frames + 15) / 16 * 16;  // align to 16
+}
+
 }  // namespace
 
 AudioTrackAudioSink::AudioTrackAudioSink(
@@ -86,6 +104,9 @@
     int preferred_buffer_size_in_bytes,
     SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
     ConsumeFramesFunc consume_frames_func,
+    SbAudioSinkPrivate::ErrorFunc error_func,
+    SbTime start_time,
+    int tunnel_mode_audio_session_id,
     void* context)
     : type_(type),
       channels_(channels),
@@ -95,40 +116,56 @@
       frames_per_channel_(frames_per_channel),
       update_source_status_func_(update_source_status_func),
       consume_frames_func_(consume_frames_func),
-      context_(context),
-      last_playback_head_position_(0),
-      j_audio_track_bridge_(NULL),
-      j_audio_data_(NULL),
-      quit_(false),
-      audio_out_thread_(kSbThreadInvalid),
-      playback_rate_(1.0f),
-      written_frames_(0) {
+      error_func_(error_func),
+      start_time_(start_time),
+      tunnel_mode_audio_session_id_(tunnel_mode_audio_session_id),
+      max_frames_per_request_(
+          tunnel_mode_audio_session_id_ == -1
+              ? kMaxFramesPerRequest
+              : GetMaxFramesPerRequestForTunnelMode(sampling_frequency_hz_)),
+      context_(context) {
   SB_DCHECK(update_source_status_func_);
   SB_DCHECK(consume_frames_func_);
   SB_DCHECK(frame_buffer_);
   SB_DCHECK(SbAudioSinkIsAudioSampleTypeSupported(sample_type));
 
+  // TODO: Support query if platform supports float type for tunnel mode.
+  if (tunnel_mode_audio_session_id_ != -1) {
+    SB_DCHECK(sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated);
+  }
+
+  SB_LOG(INFO) << "Creating audio sink starts at " << start_time_;
+
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jobject> j_audio_output_manager(
       env->CallStarboardObjectMethodOrAbort(
           "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
   jobject j_audio_track_bridge = env->CallObjectMethodOrAbort(
       j_audio_output_manager.Get(), "createAudioTrackBridge",
-      "(IIII)Ldev/cobalt/media/AudioTrackBridge;",
+      "(IIIII)Ldev/cobalt/media/AudioTrackBridge;",
       GetAudioFormatSampleType(sample_type_), sampling_frequency_hz_, channels_,
-      preferred_buffer_size_in_bytes);
+      preferred_buffer_size_in_bytes, tunnel_mode_audio_session_id_);
   if (!j_audio_track_bridge) {
+    // One of the cases that this may hit is when output happened to be switched
+    // to a device that doesn't support tunnel mode.
+    // TODO: Find a way to exclude the device from tunnel mode playback, to
+    //       avoid infinite loop in creating the audio sink on a device
+    //       claims to support tunnel mode but fails to create the audio sink.
+    // TODO: Currently this will be reported as a general decode error,
+    //       investigate if this can be reported as a capability changed error.
     return;
   }
   j_audio_track_bridge_ = env->ConvertLocalRefToGlobalRef(j_audio_track_bridge);
   if (sample_type_ == kSbMediaAudioSampleTypeFloat32) {
-    j_audio_data_ = env->NewFloatArray(channels_ * kMaxFramesPerRequest);
+    j_audio_data_ = env->NewFloatArray(channels_ * max_frames_per_request_);
   } else if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated) {
     j_audio_data_ = env->NewByteArray(channels_ * GetSampleSize(sample_type_) *
-                                      kMaxFramesPerRequest);
+                                      max_frames_per_request_);
   } else {
     SB_NOTREACHED();
   }
+  SB_CHECK(j_audio_data_) << "Failed to allocate |j_audio_data_|";
+
   j_audio_data_ = env->ConvertLocalRefToGlobalRef(j_audio_data_);
 
   audio_out_thread_ = SbThreadCreate(
@@ -183,15 +220,21 @@
   return NULL;
 }
 
+// TODO: Break down the function into manageable pieces.
 void AudioTrackAudioSink::AudioThreadFunc() {
   JniEnvExt* env = JniEnvExt::Get();
   bool was_playing = false;
 
   SB_LOG(INFO) << "AudioTrackAudioSink thread started.";
 
-#if defined(SB_PLAYER_FILTER_ENABLE_STATE_CHECK)
+  int accumulated_written_frames = 0;
+  // TODO: |last_playback_head_changed_at| is also resetted when a warning is
+  //       logged after the playback head position hasn't been updated for a
+  //       while.  We should refine the name to make it better reflect its
+  //       usage.
   SbTime last_playback_head_changed_at = -1;
-#endif  // defined(SB_PLAYER_FILTER_ENABLE_STATE_CHECK)
+  SbTime playback_head_not_changed_duration = 0;
+  SbTime last_written_succeeded_at = -1;
 
   while (!quit_) {
     int playback_head_position = 0;
@@ -214,12 +257,15 @@
           std::max(playback_head_position, last_playback_head_position_);
       int frames_consumed =
           playback_head_position - last_playback_head_position_;
+      SbTime now = SbTimeGetMonotonicNow();
 
-#if defined(SB_PLAYER_FILTER_ENABLE_STATE_CHECK)
+      if (last_playback_head_changed_at == -1) {
+        last_playback_head_changed_at = now;
+      }
       if (last_playback_head_position_ == playback_head_position) {
-        auto now = SbTimeGetMonotonicNow();
         SbTime elapsed = now - last_playback_head_changed_at;
-        if (was_playing && elapsed > 5 * kSbTimeSecond) {
+        if (elapsed > 5 * kSbTimeSecond) {
+          playback_head_not_changed_duration += elapsed;
           last_playback_head_changed_at = now;
           SB_LOG(INFO) << "last playback head position is "
                        << last_playback_head_position_
@@ -227,9 +273,9 @@
                        << " microseconds.";
         }
       } else {
-        last_playback_head_changed_at = SbTimeGetMonotonicNow();
+        last_playback_head_changed_at = now;
+        playback_head_not_changed_duration = 0;
       }
-#endif  // defined(SB_PLAYER_FILTER_ENABLE_STATE_CHECK)
 
       last_playback_head_position_ = playback_head_position;
       frames_consumed = std::min(frames_consumed, written_frames_);
@@ -260,6 +306,9 @@
       SB_LOG(INFO) << "AudioTrackAudioSink paused.";
     } else if (!was_playing && is_playing) {
       was_playing = true;
+      last_playback_head_changed_at = -1;
+      playback_head_not_changed_duration = 0;
+      last_written_succeeded_at = -1;
       env->CallVoidMethodOrAbort(j_audio_track_bridge_, "play", "()V");
       SB_LOG(INFO) << "AudioTrackAudioSink playing.";
     }
@@ -281,7 +330,7 @@
     }
 
     expected_written_frames =
-        std::min(expected_written_frames, kMaxFramesPerRequest);
+        std::min(expected_written_frames, max_frames_per_request_);
     if (expected_written_frames == 0) {
       // It is possible that all the frames in buffer are written to the
       // soundcard, but those are not being consumed. If eos is reached,
@@ -292,24 +341,84 @@
         // Currently AudioDevice and AudioRenderer will write tail silence.
         // It should be reached only in tests. It's not ideal to allocate
         // a new silence buffer every time.
-        std::vector<uint8_t> silence_buffer(
-            channels_ * GetSampleSize(sample_type_) * kSilenceFramesPerAppend);
-        WriteData(env, silence_buffer.data(), kSilenceFramesPerAppend);
+        const int silence_frames_per_append =
+            std::min<int>(kSilenceFramesPerAppend, max_frames_per_request_);
+        std::vector<uint8_t> silence_buffer(channels_ *
+                                            GetSampleSize(sample_type_) *
+                                            silence_frames_per_append);
+        auto sync_time = start_time_ + accumulated_written_frames *
+                                           kSbTimeSecond /
+                                           sampling_frequency_hz_;
+        // Not necessary to handle error of WriteData(), even for
+        // kAudioTrackErrorDeadObject, as the audio has reached the end of
+        // stream.
+        // TODO: Ensure that the audio stream can still reach the end when an
+        //       error occurs.
+        WriteData(env, silence_buffer.data(), silence_frames_per_append,
+                  sync_time);
       }
+
+      // While WriteData() returns kAudioTrackErrorDeadObject on dead object,
+      // getAudioTimestamp() doesn't, it just no longer updates its return
+      // value.  If the dead object error occurs when there isn't any audio data
+      // to write, there is no way to detect it by checking the return value of
+      // getAudioTimestamp().  Instead, we have to check if its return value has
+      // been changed during a certain period of time, to detect the underlying
+      // dead object error.
+      // Note that dead object would occur while switching audio end points in
+      // tunnel mode.  Under non-tunnel mode, the Android native AudioTrack will
+      // handle audio track dead object automatically if the new end point can
+      // support current audio format.
+      // TODO: Investigate to handle this error in non-tunnel mode.
+      if (tunnel_mode_audio_session_id_ != -1 &&
+          last_written_succeeded_at != -1) {
+        const SbTime kAudioSinkBlockedDuration = kSbTimeSecond;
+        auto time_since_last_written_success =
+            SbTimeGetMonotonicNow() - last_written_succeeded_at;
+        if (time_since_last_written_success > kAudioSinkBlockedDuration &&
+            playback_head_not_changed_duration > kAudioSinkBlockedDuration &&
+            tunnel_mode_audio_session_id_ != -1) {
+          SB_LOG(INFO) << "Over one second without frames written and consumed";
+          consume_frames_func_(written_frames_, SbTimeGetMonotonicNow(),
+                               context_);
+          error_func_(kSbPlayerErrorCapabilityChanged, context_);
+          break;
+        }
+      }
+
       SbThreadSleep(10 * kSbTimeMillisecond);
       continue;
     }
     SB_DCHECK(expected_written_frames > 0);
+    auto sync_time = start_time_ + accumulated_written_frames * kSbTimeSecond /
+                                       sampling_frequency_hz_;
+
+    SB_CHECK(start_position + expected_written_frames <= frames_per_channel_);
     int written_frames = WriteData(
         env,
         IncrementPointerByBytes(frame_buffer_, start_position * channels_ *
                                                    GetSampleSize(sample_type_)),
-        expected_written_frames);
+        expected_written_frames, sync_time);
+    SbTime now = SbTimeGetMonotonicNow();
+
+    if (written_frames < 0) {
+      // Take all |written_frames_| as consumed since audio track could be dead.
+      consume_frames_func_(written_frames_, now, context_);
+      error_func_(written_frames == kAudioTrackErrorDeadObject
+                      ? kSbPlayerErrorCapabilityChanged
+                      : kSbPlayerErrorDecode,
+                  context_);
+      break;
+    } else if (written_frames > 0) {
+      last_written_succeeded_at = now;
+    }
     written_frames_ += written_frames;
+    accumulated_written_frames += written_frames;
+
     bool written_fully = (written_frames == expected_written_frames);
     auto unplayed_frames_in_time =
         written_frames_ * kSbTimeSecond / sampling_frequency_hz_ -
-        (SbTimeGetMonotonicNow() - frames_consumed_at);
+        (now - frames_consumed_at);
     // As long as there is enough data in the buffer, run the loop in lower
     // frequency to avoid taking too much CPU.  Note that the threshold should
     // be big enough to account for the unstable playback head reported at the
@@ -332,8 +441,9 @@
 }
 
 int AudioTrackAudioSink::WriteData(JniEnvExt* env,
-                                   const void* buffer,
-                                   int expected_written_frames) {
+                                   void* buffer,
+                                   int expected_written_frames,
+                                   SbTime sync_time) {
   if (sample_type_ == kSbMediaAudioSampleTypeFloat32) {
     int expected_written_size = expected_written_frames * channels_;
     env->SetFloatArrayRegion(static_cast<jfloatArray>(j_audio_data_), kNoOffset,
@@ -342,7 +452,10 @@
     int written =
         env->CallIntMethodOrAbort(j_audio_track_bridge_, "write", "([FI)I",
                                   j_audio_data_, expected_written_size);
-    SB_DCHECK(written >= 0);
+    if (written < 0) {
+      // Error code returned as negative value, like kAudioTrackErrorDeadObject.
+      return written;
+    }
     SB_DCHECK(written % channels_ == 0);
     return written / channels_;
   }
@@ -352,10 +465,14 @@
     env->SetByteArrayRegion(static_cast<jbyteArray>(j_audio_data_), kNoOffset,
                             expected_written_size,
                             static_cast<const jbyte*>(buffer));
-    int written =
-        env->CallIntMethodOrAbort(j_audio_track_bridge_, "write", "([BI)I",
-                                  j_audio_data_, expected_written_size);
-    SB_DCHECK(written >= 0);
+
+    int written = env->CallIntMethodOrAbort(j_audio_track_bridge_, "write",
+                                            "([BIJ)I", j_audio_data_,
+                                            expected_written_size, sync_time);
+    if (written < 0) {
+      // Error code returned as negative value, like kAudioTrackErrorDeadObject.
+      return written;
+    }
     SB_DCHECK(written % (channels_ * GetSampleSize(sample_type_)) == 0);
     return written / (channels_ * GetSampleSize(sample_type_));
   }
@@ -417,6 +534,27 @@
     SbAudioSinkPrivate::ConsumeFramesFunc consume_frames_func,
     SbAudioSinkPrivate::ErrorFunc error_func,
     void* context) {
+  const SbTime kStartTime = 0;
+  const int kTunnelModeAudioSessionId = -1;  // disable tunnel mode
+  return Create(channels, sampling_frequency_hz, audio_sample_type,
+                audio_frame_storage_type, frame_buffers, frames_per_channel,
+                update_source_status_func, consume_frames_func, error_func,
+                kStartTime, kTunnelModeAudioSessionId, context);
+}
+
+SbAudioSink AudioTrackAudioSinkType::Create(
+    int channels,
+    int sampling_frequency_hz,
+    SbMediaAudioSampleType audio_sample_type,
+    SbMediaAudioFrameStorageType audio_frame_storage_type,
+    SbAudioSinkFrameBuffers frame_buffers,
+    int frames_per_channel,
+    SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+    SbAudioSinkPrivate::ConsumeFramesFunc consume_frames_func,
+    SbAudioSinkPrivate::ErrorFunc error_func,
+    SbTime start_media_time,
+    int tunnel_mode_audio_session_id,
+    void* context) {
   int min_required_frames = SbAudioSinkGetMinBufferSizeInFrames(
       channels, audio_sample_type, sampling_frequency_hz);
   SB_DCHECK(frames_per_channel >= min_required_frames);
@@ -425,7 +563,8 @@
   AudioTrackAudioSink* audio_sink = new AudioTrackAudioSink(
       this, channels, sampling_frequency_hz, audio_sample_type, frame_buffers,
       frames_per_channel, preferred_buffer_size_in_bytes,
-      update_source_status_func, consume_frames_func, context);
+      update_source_status_func, consume_frames_func, error_func,
+      start_media_time, tunnel_mode_audio_session_id, context);
   if (!audio_sink->IsAudioTrackValid()) {
     SB_DLOG(ERROR)
         << "AudioTrackAudioSinkType::Create failed to create audio track";
diff --git a/src/starboard/android/shared/audio_track_audio_sink_type.h b/src/starboard/android/shared/audio_track_audio_sink_type.h
index 43b0c86..c5b1ba7 100644
--- a/src/starboard/android/shared/audio_track_audio_sink_type.h
+++ b/src/starboard/android/shared/audio_track_audio_sink_type.h
@@ -18,6 +18,7 @@
 #include <atomic>
 #include <functional>
 #include <map>
+#include <vector>
 
 #include "starboard/android/shared/audio_sink_min_required_frames_tester.h"
 #include "starboard/android/shared/jni_env_ext.h"
@@ -54,6 +55,19 @@
       SbAudioSinkPrivate::ConsumeFramesFunc consume_frames_func,
       SbAudioSinkPrivate::ErrorFunc error_func,
       void* context) override;
+  SbAudioSink Create(
+      int channels,
+      int sampling_frequency_hz,
+      SbMediaAudioSampleType audio_sample_type,
+      SbMediaAudioFrameStorageType audio_frame_storage_type,
+      SbAudioSinkFrameBuffers frame_buffers,
+      int frames_per_channel,
+      SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+      SbAudioSinkPrivate::ConsumeFramesFunc consume_frames_func,
+      SbAudioSinkPrivate::ErrorFunc error_func,
+      SbTime start_time,
+      int tunnel_mode_audio_session_id,
+      void* context);
 
   bool IsValid(SbAudioSink audio_sink) override {
     return audio_sink != kSbAudioSinkInvalid && audio_sink->IsType(this);
@@ -92,6 +106,9 @@
       int preferred_buffer_size,
       SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
       ConsumeFramesFunc consume_frames_func,
+      SbAudioSinkPrivate::ErrorFunc error_func,
+      SbTime start_media_time,
+      int tunnel_mode_audio_session_id,
       void* context);
   ~AudioTrackAudioSink() override;
 
@@ -106,8 +123,9 @@
   static void* ThreadEntryPoint(void* context);
   void AudioThreadFunc();
 
-  int WriteData(JniEnvExt* env, const void* buffer, int size);
+  int WriteData(JniEnvExt* env, void* buffer, int size, SbTime sync_time);
 
+  // TODO: Add const to the following variables where appropriate.
   Type* type_;
   int channels_;
   int sampling_frequency_hz_;
@@ -116,18 +134,25 @@
   int frames_per_channel_;
   SbAudioSinkUpdateSourceStatusFunc update_source_status_func_;
   ConsumeFramesFunc consume_frames_func_;
+  SbAudioSinkPrivate::ErrorFunc error_func_;
+  const SbTime start_time_;
+  const int tunnel_mode_audio_session_id_;
+  const int max_frames_per_request_;
+
   void* context_;
-  int last_playback_head_position_;
-  jobject j_audio_track_bridge_;
-  jobject j_audio_data_;
+  int last_playback_head_position_ = 0;
+  jobject j_audio_track_bridge_ = nullptr;
+  jobject j_audio_data_ = nullptr;
 
-  volatile bool quit_;
-  SbThread audio_out_thread_;
+  volatile bool quit_ = false;
+  SbThread audio_out_thread_ = kSbThreadInvalid;
 
-  starboard::Mutex mutex_;
-  double playback_rate_;
+  Mutex mutex_;
+  double playback_rate_ = 1.0;
 
-  int written_frames_;
+  // TODO: Rename to |frames_in_audio_track| and move it into AudioThreadFunc()
+  //       as a local variable.
+  int written_frames_ = 0;
 };
 
 }  // namespace shared
diff --git a/src/starboard/android/shared/configuration_constants.cc b/src/starboard/android/shared/configuration_constants.cc
index bbf77e7..a7a09fd 100644
--- a/src/starboard/android/shared/configuration_constants.cc
+++ b/src/starboard/android/shared/configuration_constants.cc
@@ -83,19 +83,6 @@
 // video.
 const uint32_t kSbMediaMaxVideoBitrateInBitsPerSecond = 200 * 1024 * 1024;
 
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-const uint32_t kSbMediaMaximumVideoFrames = 12;
-
-// The encoded video frames are compressed in different ways, their decoding
-// time can vary a lot.  Occasionally a single frame can take longer time to
-// decode than the average time per frame.  The player has to cache some frames
-// to account for such inconsistency.  The number of frames being cached are
-// controlled by the following two macros.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that set this value too large may increase the playback start delay.
-const uint32_t kSbMediaMaximumVideoPrerollFrames = 4;
 
 // Specifies how video frame buffers must be aligned on this platform.
 const uint32_t kSbMediaVideoFrameAlignment = 256;
diff --git a/src/starboard/android/shared/configuration_public.h b/src/starboard/android/shared/configuration_public.h
index e428200..30374c7 100644
--- a/src/starboard/android/shared/configuration_public.h
+++ b/src/starboard/android/shared/configuration_public.h
@@ -31,10 +31,6 @@
 
 // --- Architecture Configuration --------------------------------------------
 
-// Indicates that there is no support for alignment at greater than 16 bytes for
-// items on the stack.
-#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
-
 // --- System Header Configuration -------------------------------------------
 
 // Any system headers listed here that are not provided by the platform will be
@@ -163,13 +159,21 @@
 
 // --- User Configuration ----------------------------------------------------
 
+// --- Platform Specific Configuration ---------------------------------------
+
+// Enable SB_HAS_CONCEALED_STATE support.
+#define SB_HAS_CONCEALED_STATE 1
+
 // --- Platform Specific Audits ----------------------------------------------
 
 #if !defined(__GNUC__)
 #error "Android builds need a GCC-like compiler (for the moment)."
 #endif
 
-// Enable SB_HAS_CONCEALED_STATE support.
-#define SB_HAS_CONCEALED_STATE 1
+// --- Platform Specific Quirks ----------------------------------------------
+
+// Indicates that there is no support for alignment at greater than 16 bytes for
+// items on the stack.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
 
 #endif  // STARBOARD_ANDROID_SHARED_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/android/shared/gyp_configuration.py b/src/starboard/android/shared/gyp_configuration.py
index 3474905..7aecf6e 100644
--- a/src/starboard/android/shared/gyp_configuration.py
+++ b/src/starboard/android/shared/gyp_configuration.py
@@ -305,10 +305,7 @@
           'SbFileGetPathInfoTest.WorksOnStaticContentDirectories',
           # Android doesn't currently support specifying a bitrate under 8000
           'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
-          # There are issues with playback of heeac format files where the input
-          # |frames_per_channel| is 6912, instead of 12544 (as with other
-          # formats).
-          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/6',
+
           # These tests are disabled due to not receiving the kEndOfStream
           # player state update within the specified timeout.
           'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/7',
diff --git a/src/starboard/android/shared/jni_utils.h b/src/starboard/android/shared/jni_utils.h
index 811a309..2dae0c1 100644
--- a/src/starboard/android/shared/jni_utils.h
+++ b/src/starboard/android/shared/jni_utils.h
@@ -53,7 +53,8 @@
  private:
   JT jt_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedLocalJavaRef);
+  ScopedLocalJavaRef(const ScopedLocalJavaRef&) = delete;
+  void operator=(const ScopedLocalJavaRef&) = delete;
 };
 
 // Convenience class to manage the lifetime of a local Java ByteBuffer
@@ -78,7 +79,8 @@
  private:
   ScopedLocalJavaRef<jobject> j_byte_buffer_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedJavaByteBuffer);
+  ScopedJavaByteBuffer(const ScopedJavaByteBuffer&) = delete;
+  void operator=(const ScopedJavaByteBuffer&) = delete;
 };
 
 }  // namespace shared
diff --git a/src/starboard/android/shared/launcher.py b/src/starboard/android/shared/launcher.py
index 566e595..106b2b9 100644
--- a/src/starboard/android/shared/launcher.py
+++ b/src/starboard/android/shared/launcher.py
@@ -117,6 +117,8 @@
       line = CleanLine(self.process.stdout.readline())
       if not line:
         return
+      # Show the crash lines reported by "am monitor".
+      sys.stderr.write(line)
       if re.search(_RE_ADB_AM_MONITOR_ERROR, line):
         self.done_queue.put(_QUEUE_CODE_CRASHED)
         # This log line will wake up the main thread
@@ -311,6 +313,7 @@
         'raw',
         '-s',
         '*:F',
+        '*:E',
         'DEBUG:*',
         'System.err:*',
         'starboard:*',
diff --git a/src/starboard/android/shared/media_codec_bridge.h b/src/starboard/android/shared/media_codec_bridge.h
index 77452c1..e68c69a 100644
--- a/src/starboard/android/shared/media_codec_bridge.h
+++ b/src/starboard/android/shared/media_codec_bridge.h
@@ -162,7 +162,8 @@
   // |GetOutputDimensions|.
   jobject j_reused_get_output_format_result_ = NULL;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(MediaCodecBridge);
+  MediaCodecBridge(const MediaCodecBridge&) = delete;
+  void operator=(const MediaCodecBridge&) = delete;
 };
 
 }  // namespace shared
diff --git a/src/starboard/android/shared/player_components_factory.cc b/src/starboard/android/shared/player_components_factory.cc
index 6ac984a..01dc7a6 100644
--- a/src/starboard/android/shared/player_components_factory.cc
+++ b/src/starboard/android/shared/player_components_factory.cc
@@ -12,23 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/android/shared/audio_decoder.h"
-#include "starboard/android/shared/video_decoder.h"
-#include "starboard/android/shared/video_render_algorithm.h"
-#include "starboard/common/log.h"
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/media.h"
-#include "starboard/shared/opus/opus_audio_decoder.h"
-#include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
-#include "starboard/shared/starboard/player/filter/player_components.h"
-#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
-#include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
-#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
+#include "starboard/android/shared/player_components_factory.h"
 
 namespace starboard {
 namespace shared {
@@ -36,121 +20,10 @@
 namespace player {
 namespace filter {
 
-namespace {
-
-const int kAudioSinkFramesAlignment = 256;
-const int kDefaultAudioSinkMinFramesPerAppend = 1024;
-const int kDefaultAudioSinkMaxCachedFrames =
-    8 * kDefaultAudioSinkMinFramesPerAppend;
-
-int AlignUp(int value, int alignment) {
-  return (value + alignment - 1) / alignment * alignment;
-}
-
-class PlayerComponentsFactory : public PlayerComponents::Factory {
-  bool CreateSubComponents(
-      const CreationParameters& creation_parameters,
-      scoped_ptr<AudioDecoder>* audio_decoder,
-      scoped_ptr<AudioRendererSink>* audio_renderer_sink,
-      scoped_ptr<VideoDecoder>* video_decoder,
-      scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
-      scoped_refptr<VideoRendererSink>* video_renderer_sink,
-      std::string* error_message) override {
-    SB_DCHECK(error_message);
-
-    if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone) {
-      SB_DCHECK(audio_decoder);
-      SB_DCHECK(audio_renderer_sink);
-
-      auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
-                                SbDrmSystem drm_system) {
-        if (audio_sample_info.codec == kSbMediaAudioCodecAac) {
-          scoped_ptr<android::shared::AudioDecoder> audio_decoder_impl(
-              new android::shared::AudioDecoder(audio_sample_info.codec,
-                                                audio_sample_info, drm_system));
-          if (audio_decoder_impl->is_valid()) {
-            return audio_decoder_impl.PassAs<AudioDecoder>();
-          }
-        } else if (audio_sample_info.codec == kSbMediaAudioCodecOpus) {
-          scoped_ptr<opus::OpusAudioDecoder> audio_decoder_impl(
-              new opus::OpusAudioDecoder(audio_sample_info));
-          if (audio_decoder_impl->is_valid()) {
-            return audio_decoder_impl.PassAs<AudioDecoder>();
-          }
-        } else {
-          SB_NOTREACHED();
-        }
-        return scoped_ptr<AudioDecoder>();
-      };
-
-      audio_decoder->reset(new AdaptiveAudioDecoder(
-          creation_parameters.audio_sample_info(),
-          creation_parameters.drm_system(), decoder_creator));
-      audio_renderer_sink->reset(new AudioRendererSinkImpl);
-    }
-
-    if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
-      SB_DCHECK(video_decoder);
-      SB_DCHECK(video_render_algorithm);
-      SB_DCHECK(video_renderer_sink);
-      SB_DCHECK(error_message);
-
-      scoped_ptr<android::shared::VideoDecoder> video_decoder_impl(
-          new android::shared::VideoDecoder(
-              creation_parameters.video_codec(),
-              creation_parameters.drm_system(),
-              creation_parameters.output_mode(),
-              creation_parameters.decode_target_graphics_context_provider(),
-              creation_parameters.max_video_capabilities(), error_message));
-      if (video_decoder_impl->is_valid()) {
-        video_render_algorithm->reset(new android::shared::VideoRenderAlgorithm(
-            video_decoder_impl.get()));
-        *video_renderer_sink = video_decoder_impl->GetSink();
-        video_decoder->reset(video_decoder_impl.release());
-      } else {
-        video_decoder->reset();
-        *video_renderer_sink = NULL;
-        *error_message =
-            "Failed to create video decoder with error: " + *error_message;
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  void GetAudioRendererParams(const CreationParameters& creation_parameters,
-                              int* max_cached_frames,
-                              int* min_frames_per_append) const override {
-    SB_DCHECK(max_cached_frames);
-    SB_DCHECK(min_frames_per_append);
-    SB_DCHECK(kDefaultAudioSinkMinFramesPerAppend % kAudioSinkFramesAlignment ==
-              0);
-    *min_frames_per_append = kDefaultAudioSinkMinFramesPerAppend;
-
-    // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
-    // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
-    int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
-        creation_parameters.audio_sample_info().number_of_channels,
-        SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
-            ? kSbMediaAudioSampleTypeFloat32
-            : kSbMediaAudioSampleTypeInt16Deprecated,
-        creation_parameters.audio_sample_info().samples_per_second);
-    // On Android 5.0, the size of audio renderer sink buffer need to be two
-    // times larger than AudioTrack minBufferSize. Otherwise, AudioTrack may
-    // stop working after pause.
-    *max_cached_frames =
-        min_frames_required * 2 + kDefaultAudioSinkMinFramesPerAppend;
-    *max_cached_frames = AlignUp(*max_cached_frames, kAudioSinkFramesAlignment);
-  }
-};
-
-}  // namespace
-
 // static
 scoped_ptr<PlayerComponents::Factory> PlayerComponents::Factory::Create() {
   return make_scoped_ptr<PlayerComponents::Factory>(
-      new PlayerComponentsFactory);
+      new android::shared::PlayerComponentsFactory);
 }
 
 // static
diff --git a/src/starboard/android/shared/player_components_factory.h b/src/starboard/android/shared/player_components_factory.h
new file mode 100644
index 0000000..350b00f
--- /dev/null
+++ b/src/starboard/android/shared/player_components_factory.h
@@ -0,0 +1,173 @@
+// Copyright 2017 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_ANDROID_SHARED_PLAYER_COMPONENTS_FACTORY_H_
+#define STARBOARD_ANDROID_SHARED_PLAYER_COMPONENTS_FACTORY_H_
+
+#include <string>
+
+#include "starboard/android/shared/audio_decoder.h"
+#include "starboard/android/shared/video_decoder.h"
+#include "starboard/android/shared/video_render_algorithm.h"
+#include "starboard/common/log.h"
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/opus/opus_audio_decoder.h"
+#include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
+#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
+#include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
+#include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+class PlayerComponentsFactory : public starboard::shared::starboard::player::
+                                    filter::PlayerComponents::Factory {
+  typedef starboard::shared::opus::OpusAudioDecoder OpusAudioDecoder;
+  typedef starboard::shared::starboard::player::filter::AdaptiveAudioDecoder
+      AdaptiveAudioDecoder;
+  typedef starboard::shared::starboard::player::filter::AudioDecoder
+      AudioDecoderBase;
+  typedef starboard::shared::starboard::player::filter::AudioRendererSink
+      AudioRendererSink;
+  typedef starboard::shared::starboard::player::filter::AudioRendererSinkImpl
+      AudioRendererSinkImpl;
+  typedef starboard::shared::starboard::player::filter::VideoDecoder
+      VideoDecoderBase;
+  typedef starboard::shared::starboard::player::filter::VideoRenderAlgorithm
+      VideoRenderAlgorithmBase;
+  typedef starboard::shared::starboard::player::filter::VideoRendererSink
+      VideoRendererSink;
+
+  const int kAudioSinkFramesAlignment = 256;
+  const int kDefaultAudioSinkMinFramesPerAppend = 1024;
+  const int kDefaultAudioSinkMaxCachedFrames =
+      8 * kDefaultAudioSinkMinFramesPerAppend;
+
+  virtual SbDrmSystem GetExtendedDrmSystem(SbDrmSystem drm_system) {
+    return drm_system;
+  }
+
+  static int AlignUp(int value, int alignment) {
+    return (value + alignment - 1) / alignment * alignment;
+  }
+
+  bool CreateSubComponents(
+      const CreationParameters& creation_parameters,
+      scoped_ptr<AudioDecoderBase>* audio_decoder,
+      scoped_ptr<AudioRendererSink>* audio_renderer_sink,
+      scoped_ptr<VideoDecoderBase>* video_decoder,
+      scoped_ptr<VideoRenderAlgorithmBase>* video_render_algorithm,
+      scoped_refptr<VideoRendererSink>* video_renderer_sink,
+      std::string* error_message) override {
+    SB_DCHECK(error_message);
+
+    if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone) {
+      SB_DCHECK(audio_decoder);
+      SB_DCHECK(audio_renderer_sink);
+
+      auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+                                SbDrmSystem drm_system) {
+        if (audio_sample_info.codec == kSbMediaAudioCodecAac) {
+          scoped_ptr<AudioDecoder> audio_decoder_impl(new AudioDecoder(
+              audio_sample_info.codec, audio_sample_info, drm_system));
+          if (audio_decoder_impl->is_valid()) {
+            return audio_decoder_impl.PassAs<AudioDecoderBase>();
+          }
+        } else if (audio_sample_info.codec == kSbMediaAudioCodecOpus) {
+          scoped_ptr<OpusAudioDecoder> audio_decoder_impl(
+              new OpusAudioDecoder(audio_sample_info));
+          if (audio_decoder_impl->is_valid()) {
+            return audio_decoder_impl.PassAs<AudioDecoderBase>();
+          }
+        } else {
+          SB_NOTREACHED();
+        }
+        return scoped_ptr<AudioDecoderBase>();
+      };
+
+      audio_decoder->reset(new AdaptiveAudioDecoder(
+          creation_parameters.audio_sample_info(),
+          GetExtendedDrmSystem(creation_parameters.drm_system()),
+          decoder_creator));
+      audio_renderer_sink->reset(new AudioRendererSinkImpl);
+    }
+
+    if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
+      SB_DCHECK(video_decoder);
+      SB_DCHECK(video_render_algorithm);
+      SB_DCHECK(video_renderer_sink);
+      SB_DCHECK(error_message);
+
+      scoped_ptr<VideoDecoder> video_decoder_impl(new VideoDecoder(
+          creation_parameters.video_codec(),
+          GetExtendedDrmSystem(creation_parameters.drm_system()),
+          creation_parameters.output_mode(),
+          creation_parameters.decode_target_graphics_context_provider(),
+          creation_parameters.max_video_capabilities(), error_message));
+      if (video_decoder_impl->is_valid()) {
+        video_render_algorithm->reset(
+            new VideoRenderAlgorithm(video_decoder_impl.get()));
+        *video_renderer_sink = video_decoder_impl->GetSink();
+        video_decoder->reset(video_decoder_impl.release());
+      } else {
+        video_decoder->reset();
+        *video_renderer_sink = NULL;
+        *error_message =
+            "Failed to create video decoder with error: " + *error_message;
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  void GetAudioRendererParams(const CreationParameters& creation_parameters,
+                              int* max_cached_frames,
+                              int* min_frames_per_append) const override {
+    SB_DCHECK(max_cached_frames);
+    SB_DCHECK(min_frames_per_append);
+    SB_DCHECK(kDefaultAudioSinkMinFramesPerAppend % kAudioSinkFramesAlignment ==
+              0);
+    *min_frames_per_append = kDefaultAudioSinkMinFramesPerAppend;
+
+    // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
+    // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
+    int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
+        creation_parameters.audio_sample_info().number_of_channels,
+        SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
+            ? kSbMediaAudioSampleTypeFloat32
+            : kSbMediaAudioSampleTypeInt16Deprecated,
+        creation_parameters.audio_sample_info().samples_per_second);
+    // On Android 5.0, the size of audio renderer sink buffer need to be two
+    // times larger than AudioTrack minBufferSize. Otherwise, AudioTrack may
+    // stop working after pause.
+    *max_cached_frames =
+        min_frames_required * 2 + kDefaultAudioSinkMinFramesPerAppend;
+    *max_cached_frames = AlignUp(*max_cached_frames, kAudioSinkFramesAlignment);
+  }
+};
+
+}  // namespace shared
+}  // namespace android
+}  // namespace starboard
+
+#endif  // STARBOARD_ANDROID_SHARED_PLAYER_COMPONENTS_FACTORY_H_
diff --git a/src/starboard/android/shared/player_create.cc b/src/starboard/android/shared/player_create.cc
index a3a9a2a..a3ff14b 100644
--- a/src/starboard/android/shared/player_create.cc
+++ b/src/starboard/android/shared/player_create.cc
@@ -14,7 +14,6 @@
 
 #include "starboard/player.h"
 
-#include "starboard/android/shared/android_media_session_client.h"
 #include "starboard/android/shared/video_decoder.h"
 #include "starboard/android/shared/video_window.h"
 #include "starboard/common/log.h"
@@ -28,8 +27,6 @@
 using starboard::shared::starboard::player::filter::
     FilterBasedPlayerWorkerHandler;
 using starboard::shared::starboard::player::PlayerWorker;
-using starboard::android::shared::kPlaying;
-using starboard::android::shared::UpdateActiveSessionPlatformPlaybackState;
 using starboard::android::shared::VideoDecoder;
 
 SbPlayer SbPlayerCreate(SbWindow window,
@@ -125,8 +122,6 @@
         kMaxNumberOfHardwareDecoders) {
       return kSbPlayerInvalid;
     }
-    // Only update session state for main player.
-    UpdateActiveSessionPlatformPlaybackState(kPlaying);
   }
 
   if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture &&
diff --git a/src/starboard/android/shared/player_destroy.cc b/src/starboard/android/shared/player_destroy.cc
index bdfbba3..6a1a293 100644
--- a/src/starboard/android/shared/player_destroy.cc
+++ b/src/starboard/android/shared/player_destroy.cc
@@ -14,16 +14,12 @@
 
 #include "starboard/player.h"
 
-#include "starboard/android/shared/android_media_session_client.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-using starboard::android::shared::kNone;
-using starboard::android::shared::UpdateActiveSessionPlatformPlaybackState;
-
 void SbPlayerDestroy(SbPlayer player) {
   if (!SbPlayerIsValid(player)) {
     return;
   }
-  UpdateActiveSessionPlatformPlaybackState(kNone);
+
   delete player;
 }
diff --git a/src/starboard/android/shared/player_set_playback_rate.cc b/src/starboard/android/shared/player_set_playback_rate.cc
index 233cd92..d18c197 100644
--- a/src/starboard/android/shared/player_set_playback_rate.cc
+++ b/src/starboard/android/shared/player_set_playback_rate.cc
@@ -14,14 +14,9 @@
 
 #include "starboard/player.h"
 
-#include "starboard/android/shared/android_media_session_client.h"
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-using starboard::android::shared::kPaused;
-using starboard::android::shared::kPlaying;
-using starboard::android::shared::UpdateActiveSessionPlatformPlaybackState;
-
 bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate) {
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
@@ -32,8 +27,6 @@
                      << playback_rate << '.';
     return false;
   }
-  bool paused = (playback_rate == 0.0);
-  UpdateActiveSessionPlatformPlaybackState(paused ? kPaused : kPlaying);
 
   player->SetPlaybackRate(playback_rate);
   return true;
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 65c5044..d38907b 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -135,6 +135,7 @@
         'media_is_audio_supported.cc',
         'media_is_video_supported.cc',
         'microphone_impl.cc',
+        'player_components_factory.h',
         'player_create.cc',
         'player_destroy.cc',
         'player_get_preferred_output_mode.cc',
diff --git a/src/starboard/android/x86/cobalt/configuration.py b/src/starboard/android/x86/cobalt/configuration.py
index 5e11fd8..64509ee 100644
--- a/src/starboard/android/x86/cobalt/configuration.py
+++ b/src/starboard/android/x86/cobalt/configuration.py
@@ -43,14 +43,6 @@
           'CSS3FontsLayoutTests/Layout.Test'
           '/synthetic_bolding_should_occur_on_non_bold_font',
       ],
-      'nb_test': [
-          'BidirectionalFitReuseAllocatorTest.FallbackBlockMerge',
-          'BidirectionalFitReuseAllocatorTest.FreeBlockMergingLeft',
-          'BidirectionalFitReuseAllocatorTest.FreeBlockMergingRight',
-          'FirstFitReuseAllocatorTest.FallbackBlockMerge',
-          'FirstFitReuseAllocatorTest.FreeBlockMergingLeft',
-          'FirstFitReuseAllocatorTest.FreeBlockMergingRight',
-      ],
       'net_unittests': [  # Net tests are very unstable on Android L
           test_filter.FILTER_ALL
       ],
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index ebb054e..eaf4088 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -22,7 +22,7 @@
 from starboard.build.application_configuration import ApplicationConfiguration
 from starboard.optional import get_optional_tests
 from starboard.sabi import sabi
-from starboard.tools import ccache
+from starboard.tools import cache
 from starboard.tools import environment
 from starboard.tools import paths
 from starboard.tools import platform
@@ -70,15 +70,19 @@
       self._directory = os.path.realpath(os.path.dirname(__file__))
     self._application_configuration = None
     self._application_configuration_search_path = [self._directory]
+    # Default build accelerator is ccache.
+    self.build_accelerator = self.GetBuildAccelerator(cache.Accelerator.CCACHE)
 
-    # Specifies the build accelerator to be used. Default is ccache.
-    build_accelerator = ccache.Ccache()
+  def GetBuildAccelerator(self, accelerator):
+    """Returns the build accelerator name."""
+    build_accelerator = cache.Cache(accelerator)
+    name = build_accelerator.GetName()
     if build_accelerator.Use():
-      self.build_accelerator = build_accelerator.GetName()
-      logging.info('Using %sbuild accelerator.', self.build_accelerator)
+      logging.info('Using %s build accelerator.', name)
+      return name
     else:
-      self.build_accelerator = ''
-      logging.info('Not using a build accelerator.')
+      logging.info('Not using %s build accelerator.', name)
+      return ''
 
   def GetBuildFormat(self):
     """Returns the desired build format."""
@@ -354,7 +358,8 @@
     raise NotImplementedError()
 
   def GetPathToSabiJsonFile(self):
-    """Gets the path to the JSON file with Starboard ABI information for the build.
+    """Gets the path to the JSON file with Starboard ABI information for the
+       build.
 
     Examples:
         'starboard/sabi/arm64/sabi-v12.json'
diff --git a/src/starboard/common/configuration_defaults.cc b/src/starboard/common/configuration_defaults.cc
index d603e4f..6a933b9 100644
--- a/src/starboard/common/configuration_defaults.cc
+++ b/src/starboard/common/configuration_defaults.cc
@@ -30,7 +30,7 @@
 }
 
 const char* CobaltFallbackSplashScreenUrlDefault() {
-  return "none";
+  return "h5vcc-embedded://black_splash_screen.html";
 }
 
 const char* CobaltFallbackSplashScreenTopicsDefault() {
diff --git a/src/starboard/common/mutex.h b/src/starboard/common/mutex.h
index 67f1b68..6012c55 100644
--- a/src/starboard/common/mutex.h
+++ b/src/starboard/common/mutex.h
@@ -52,7 +52,8 @@
   SbMutex* mutex() const;
   mutable SbMutex mutex_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(Mutex);
+  Mutex(const Mutex&) = delete;
+  void operator=(const Mutex&) = delete;
 };
 
 // Scoped lock holder that works on starboard::Mutex.
@@ -63,7 +64,9 @@
 
  private:
   const Mutex& mutex_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedLock);
+
+  ScopedLock(const ScopedLock&) = delete;
+  void operator=(const ScopedLock&) = delete;
 };
 
 // Scoped lock holder that works on starboard::Mutex which uses AcquireTry()
@@ -78,7 +81,9 @@
  private:
   const Mutex& mutex_;
   bool is_locked_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedTryLock);
+
+  ScopedTryLock(const ScopedTryLock&) = delete;
+  void operator=(const ScopedTryLock&) = delete;
 };
 
 }  // namespace starboard
diff --git a/src/starboard/common/recursive_mutex.h b/src/starboard/common/recursive_mutex.h
index b949da9..6806858 100644
--- a/src/starboard/common/recursive_mutex.h
+++ b/src/starboard/common/recursive_mutex.h
@@ -48,7 +48,8 @@
   // Only the owner is able to modify recurse_count_.
   size_t recurse_count_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(RecursiveMutex);
+  RecursiveMutex(const RecursiveMutex&) = delete;
+  void operator=(const RecursiveMutex&) = delete;
 };
 
 class ScopedRecursiveLock {
@@ -58,7 +59,9 @@
 
  private:
   RecursiveMutex& mutex_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveLock);
+
+  ScopedRecursiveLock(const ScopedRecursiveLock&) = delete;
+  void operator=(const ScopedRecursiveLock&) = delete;
 };
 
 }  // namespace starboard
diff --git a/src/starboard/common/rwlock.h b/src/starboard/common/rwlock.h
index 2ec8d05..73aafdd 100644
--- a/src/starboard/common/rwlock.h
+++ b/src/starboard/common/rwlock.h
@@ -68,7 +68,8 @@
   int32_t readers_;
   bool writing_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(RWLock);
+  RWLock(const RWLock&) = delete;
+  void operator=(const RWLock&) = delete;
 };
 
 class ScopedReadLock {
@@ -80,7 +81,9 @@
 
  private:
   RWLock* rw_lock_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedReadLock);
+
+  ScopedReadLock(const ScopedReadLock&) = delete;
+  void operator=(const ScopedReadLock&) = delete;
 };
 
 class ScopedWriteLock {
@@ -92,7 +95,9 @@
 
  private:
   RWLock* rw_lock_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ScopedWriteLock);
+
+  ScopedWriteLock(const ScopedWriteLock&) = delete;
+  void operator=(const ScopedWriteLock&) = delete;
 };
 
 }  // namespace starboard
diff --git a/src/starboard/common/semaphore.h b/src/starboard/common/semaphore.h
index 35ad34a..ce0f800 100644
--- a/src/starboard/common/semaphore.h
+++ b/src/starboard/common/semaphore.h
@@ -52,7 +52,8 @@
   ConditionVariable condition_;
   int permits_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(Semaphore);
+  Semaphore(const Semaphore&) = delete;
+  void operator=(const Semaphore&) = delete;
 };
 
 }  // namespace starboard.
diff --git a/src/starboard/common/thread.h b/src/starboard/common/thread.h
index 1a878a0..c01b817 100644
--- a/src/starboard/common/thread.h
+++ b/src/starboard/common/thread.h
@@ -77,7 +77,8 @@
   struct Data;
   scoped_ptr<Data> d_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(Thread);
+  Thread(const Thread&) = delete;
+  void operator=(const Thread&) = delete;
 };
 
 }  // namespace starboard
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index e82f471..9a4477a 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -31,9 +31,6 @@
 #error "You must define STARBOARD in Starboard builds."
 #endif
 
-#define SB_TRUE 1
-#define SB_FALSE 0
-
 // --- Common Defines --------------------------------------------------------
 
 // The minimum API version allowed by this version of the Starboard headers,
@@ -77,6 +74,13 @@
 // Deprecated the SB_OVERRIDE macro.
 #define SB_OVERRIDE_DEPRECATED_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// Deprecated the SB_DISALLOW_COPY_AND_ASSIGN macro.
+#define SB_DISALLOW_COPY_AND_ASSIGN_DEPRECATED_VERSION \
+  SB_EXPERIMENTAL_API_VERSION
+
+// Deprecated SB_TRUE and SB_FALSE.
+#define SB_TRUE_FALSE_DEPRECATED_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 // --- Common Detected Features ----------------------------------------------
@@ -89,6 +93,14 @@
 
 // --- Common Helper Macros --------------------------------------------------
 
+#if SB_API_VERSION < SB_TRUE_FALSE_DEPRECATED_VERSION
+#define SB_TRUE 1
+#define SB_FALSE 0
+#else
+#define SB_TRUE #error "The macro SB_TRUE is deprecated."
+#define SB_FALSE #error "The macro SB_FALSE is deprecated."
+#endif
+
 // Determines a compile-time capability of the system.
 #define SB_CAN(SB_FEATURE) \
   ((defined SB_CAN_##SB_FEATURE) && SB_CAN_##SB_FEATURE)
@@ -146,11 +158,16 @@
 #define SB_STRINGIFY(x) SB_STRINGIFY2(x)
 #define SB_STRINGIFY2(x) #x
 
+#if SB_API_VERSION < SB_DISALLOW_COPY_AND_ASSIGN_DEPRECATED_VERSION
 // A macro to disallow the copy constructor and operator= functions
 // This should be used in the private: declarations for a class
 #define SB_DISALLOW_COPY_AND_ASSIGN(TypeName) \
   TypeName(const TypeName&) = delete;         \
   void operator=(const TypeName&) = delete
+#else
+#define SB_DISALLOW_COPY_AND_ASSIGN \
+  #error "The SB_DISALLOW_COPY_AND_ASSIGN macro is deprecated."
+#endif  // SB_DISALLOW_COPY_AND_ASSIGN_DEPRECATED_VERSION < SB_API_VERSION
 
 // An enumeration of values for the kSbPreferredByteOrder configuration
 // variable.  Setting this up properly means avoiding slow color swizzles when
@@ -598,16 +615,13 @@
 #if defined(SB_MEDIA_MAXIMUM_VIDEO_FRAMES)
 #error \
     "SB_MEDIA_MAXIMUM_VIDEO_FRAMES should not be defined in Starboard " \
-"versions 12 and later. Instead, define kSbMediaMaximumVideoFrames in " \
-"starboard/<PLATFORM_PATH>/configuration_constants.cc."
+"versions 12 and later."
 #endif
 
 #if defined(SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES)
 #error \
     "SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES should not be defined in " \
-"Starboard versions 12 and later. Instead, define " \
-"kSbMediaMaximumVideoPrerollFrames in " \
-"starboard/<PLATFORM_PATH>/configuration_constants.cc."
+"Starboard versions 12 and later."
 #endif
 
 #if defined(SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND)
diff --git a/src/starboard/configuration_constants.h b/src/starboard/configuration_constants.h
index 961affe..1a1aac4 100644
--- a/src/starboard/configuration_constants.h
+++ b/src/starboard/configuration_constants.h
@@ -93,21 +93,6 @@
 // video.
 extern const uint32_t kSbMediaMaxVideoBitrateInBitsPerSecond;
 
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-extern const uint32_t kSbMediaMaximumVideoFrames;
-
-// The encoded video frames are compressed in different ways, so their decoding
-// time can vary a lot.  Occasionally a single frame can take longer time to
-// decode than the average time per frame.  The player has to cache some frames
-// to account for such inconsistency.  The number of frames being cached are
-// controlled by SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES and
-// SB_MEDIA_MAXIMUM_VIDEO_FRAMES.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that setting this value too large may increase the playback start delay.
-extern const uint32_t kSbMediaMaximumVideoPrerollFrames;
-
 // Specifies how video frame buffers must be aligned on this platform.
 extern const uint32_t kSbMediaVideoFrameAlignment;
 
diff --git a/src/starboard/contrib/tizen/armv7l/configuration_public.h b/src/starboard/contrib/tizen/armv7l/configuration_public.h
index d3c0233..c4902db 100644
--- a/src/starboard/contrib/tizen/armv7l/configuration_public.h
+++ b/src/starboard/contrib/tizen/armv7l/configuration_public.h
@@ -126,14 +126,6 @@
 // decode than the average time per frame.  The player has to cache some frames
 // to account for such inconsistency.  The number of frames being cached are
 // controlled by the following two macros.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that set this value too large may increase the playback start delay.
-#define SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES 4
-
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
 
 // --- Timing API ------------------------------------------------------------
 
diff --git a/src/starboard/elf_loader/dynamic_section.h b/src/starboard/elf_loader/dynamic_section.h
index 8d22bf1..6619b90 100644
--- a/src/starboard/elf_loader/dynamic_section.h
+++ b/src/starboard/elf_loader/dynamic_section.h
@@ -90,7 +90,8 @@
   linker_function_t init_func_;
   linker_function_t fini_func_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DynamicSection);
+  DynamicSection(const DynamicSection&) = delete;
+  void operator=(const DynamicSection&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/elf_hash_table.h b/src/starboard/elf_loader/elf_hash_table.h
index 14bb9ca..7a867c1 100644
--- a/src/starboard/elf_loader/elf_hash_table.h
+++ b/src/starboard/elf_loader/elf_hash_table.h
@@ -53,7 +53,8 @@
   const Word* hash_chain_;
   size_t hash_chain_size_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ElfHashTable);
+  ElfHashTable(const ElfHashTable&) = delete;
+  void operator=(const ElfHashTable&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/elf_header.h b/src/starboard/elf_loader/elf_header.h
index a8b666c..647a1bf 100644
--- a/src/starboard/elf_loader/elf_header.h
+++ b/src/starboard/elf_loader/elf_header.h
@@ -36,7 +36,9 @@
 
  private:
   scoped_ptr<Ehdr> elf_header_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ElfHeader);
+
+  ElfHeader(const ElfHeader&) = delete;
+  void operator=(const ElfHeader&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/elf_loader.h b/src/starboard/elf_loader/elf_loader.h
index 7119651..587c7a6 100644
--- a/src/starboard/elf_loader/elf_loader.h
+++ b/src/starboard/elf_loader/elf_loader.h
@@ -66,7 +66,8 @@
   // The single ELF Loader instance.
   static ElfLoader* g_instance;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoader);
+  ElfLoader(const ElfLoader&) = delete;
+  void operator=(const ElfLoader&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/elf_loader_impl.h b/src/starboard/elf_loader/elf_loader_impl.h
index da9c5f3..84c43c7 100644
--- a/src/starboard/elf_loader/elf_loader_impl.h
+++ b/src/starboard/elf_loader/elf_loader_impl.h
@@ -46,7 +46,8 @@
   scoped_ptr<ExportedSymbols> exported_symbols_;
   scoped_ptr<Relocations> relocations_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoaderImpl);
+  ElfLoaderImpl(const ElfLoaderImpl&) = delete;
+  void operator=(const ElfLoaderImpl&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/elf_loader_sys_impl.h b/src/starboard/elf_loader/elf_loader_sys_impl.h
index bcc7af0..7f148a5 100644
--- a/src/starboard/elf_loader/elf_loader_sys_impl.h
+++ b/src/starboard/elf_loader/elf_loader_sys_impl.h
@@ -32,7 +32,8 @@
  private:
   void* handle_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ElfLoaderImpl);
+  ElfLoaderImpl(const ElfLoaderImpl&) = delete;
+  void operator=(const ElfLoaderImpl&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/evergreen_config.h b/src/starboard/elf_loader/evergreen_config.h
index a2ca212..6078b43 100644
--- a/src/starboard/elf_loader/evergreen_config.h
+++ b/src/starboard/elf_loader/evergreen_config.h
@@ -48,7 +48,8 @@
   EvergreenConfig(const char* library_path,
                   const char* content_path,
                   const void* (*custom_get_extension_)(const char* name));
-  SB_DISALLOW_COPY_AND_ASSIGN(EvergreenConfig);
+  EvergreenConfig(const EvergreenConfig&) = delete;
+  void operator=(const EvergreenConfig&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/exported_symbols.cc b/src/starboard/elf_loader/exported_symbols.cc
index b07c021..33e24f0 100644
--- a/src/starboard/elf_loader/exported_symbols.cc
+++ b/src/starboard/elf_loader/exported_symbols.cc
@@ -447,8 +447,6 @@
   REGISTER_SYMBOL(kSbMaxThreadNameLength);
   REGISTER_SYMBOL(kSbMediaMaxAudioBitrateInBitsPerSecond);
   REGISTER_SYMBOL(kSbMediaMaxVideoBitrateInBitsPerSecond);
-  REGISTER_SYMBOL(kSbMediaMaximumVideoFrames);
-  REGISTER_SYMBOL(kSbMediaMaximumVideoPrerollFrames);
   REGISTER_SYMBOL(kSbMediaVideoFrameAlignment);
   REGISTER_SYMBOL(kSbMemoryLogPath);
   REGISTER_SYMBOL(kSbMemoryPageSize);
diff --git a/src/starboard/elf_loader/exported_symbols.h b/src/starboard/elf_loader/exported_symbols.h
index 1ffc732..e246214 100644
--- a/src/starboard/elf_loader/exported_symbols.h
+++ b/src/starboard/elf_loader/exported_symbols.h
@@ -41,7 +41,8 @@
  private:
   std::map<std::string, const void*> map_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ExportedSymbols);
+  ExportedSymbols(const ExportedSymbols&) = delete;
+  void operator=(const ExportedSymbols&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/file_impl.h b/src/starboard/elf_loader/file_impl.h
index 9407c95..8545195 100644
--- a/src/starboard/elf_loader/file_impl.h
+++ b/src/starboard/elf_loader/file_impl.h
@@ -35,7 +35,8 @@
  private:
   SbFile file_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(FileImpl);
+  FileImpl(const FileImpl&) = delete;
+  void operator=(const FileImpl&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/gnu_hash_table.h b/src/starboard/elf_loader/gnu_hash_table.h
index 5bdf199..bf08c17 100644
--- a/src/starboard/elf_loader/gnu_hash_table.h
+++ b/src/starboard/elf_loader/gnu_hash_table.h
@@ -57,7 +57,8 @@
   const uint32_t* buckets_;
   const uint32_t* chain_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(GnuHashTable);
+  GnuHashTable(const GnuHashTable&) = delete;
+  void operator=(const GnuHashTable&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/program_table.h b/src/starboard/elf_loader/program_table.h
index d08301c..3741002 100644
--- a/src/starboard/elf_loader/program_table.h
+++ b/src/starboard/elf_loader/program_table.h
@@ -90,7 +90,8 @@
   // from the ELF file are offsets from this address.
   Addr base_memory_address_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(ProgramTable);
+  ProgramTable(const ProgramTable&) = delete;
+  void operator=(const ProgramTable&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/elf_loader/relocations.h b/src/starboard/elf_loader/relocations.h
index fad8a4e..1058c19 100644
--- a/src/starboard/elf_loader/relocations.h
+++ b/src/starboard/elf_loader/relocations.h
@@ -84,7 +84,8 @@
 
   ExportedSymbols* exported_symbols_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(Relocations);
+  Relocations(const Relocations&) = delete;
+  void operator=(const Relocations&) = delete;
 };
 
 }  // namespace elf_loader
diff --git a/src/starboard/evergreen/arm64/configuration_public.h b/src/starboard/evergreen/arm64/configuration_public.h
index 211ccc1..243ed7c 100644
--- a/src/starboard/evergreen/arm64/configuration_public.h
+++ b/src/starboard/evergreen/arm64/configuration_public.h
@@ -23,10 +23,6 @@
 
 // --- Architecture Configuration --------------------------------------------
 
-// Indicates that there is no support for alignment at greater than 16 bytes for
-// items on the stack.
-#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
-
 // --- System Header Configuration -------------------------------------------
 
 // Any system headers listed here that are not provided by the platform will be
@@ -141,16 +137,16 @@
 
 // --- Memory Configuration --------------------------------------------------
 
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
 // Whether this platform has and should use an MMAP function to map physical
 // memory to the virtual address space.
 #if SB_API_VERSION < 12
 #define SB_HAS_MMAP 1
 #endif
 
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
-
 // --- Network Configuration -------------------------------------------------
 
 // Specifies whether this platform supports IPV6.
@@ -177,4 +173,10 @@
 #error "Evergreen-arm64 builds need a GCC-like compiler (for the moment)."
 #endif
 
+// --- Platform Specific Quirks ----------------------------------------------
+
+// Indicates that there is no support for alignment at greater than 16 bytes for
+// items on the stack.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
 #endif  // STARBOARD_EVERGREEN_ARM64_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh b/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh
index 7610aba..3061fb6 100755
--- a/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh
+++ b/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "Created drain file"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "Created drain file at"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to create a drain file for the test package"
diff --git a/src/starboard/evergreen/testing/tests/alternative_content_test.sh b/src/starboard/evergreen/testing/tests/alternative_content_test.sh
index 51ac20b..c962b8e 100755
--- a/src/starboard/evergreen/testing/tests/alternative_content_test.sh
+++ b/src/starboard/evergreen/testing/tests/alternative_content_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/evergreen/testing/tests/crashing_binary_test.sh b/src/starboard/evergreen/testing/tests/crashing_binary_test.sh
index 67a3f9a..c485690 100755
--- a/src/starboard/evergreen/testing/tests/crashing_binary_test.sh
+++ b/src/starboard/evergreen/testing/tests/crashing_binary_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=tcrash" "${TEST_NAME}.0.log" "update from tcrash channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=tcrash" "${TEST_NAME}.0.log" "update from tcrash channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the tcrash package"
diff --git a/src/starboard/evergreen/testing/tests/disabled_updater_test.sh b/src/starboard/evergreen/testing/tests/disabled_updater_test.sh
index 93275c5..9ba2528 100755
--- a/src/starboard/evergreen/testing/tests/disabled_updater_test.sh
+++ b/src/starboard/evergreen/testing/tests/disabled_updater_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh b/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh
index fdb73b0..d0b206f 100755
--- a/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh
+++ b/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh b/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh
index 00cc6da..6a90b12 100755
--- a/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh
+++ b/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=tmsabi" "${TEST_NAME}.0.log" "update from tmsabi channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=tmsabi" "${TEST_NAME}.0.log" "update from tmsabi channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the tmsabi package"
diff --git a/src/starboard/evergreen/testing/tests/noop_binary_test.sh b/src/starboard/evergreen/testing/tests/noop_binary_test.sh
index 0eb7cae..fd33059 100755
--- a/src/starboard/evergreen/testing/tests/noop_binary_test.sh
+++ b/src/starboard/evergreen/testing/tests/noop_binary_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=tnoop" "${TEST_NAME}.0.log" "update from tnoop channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=tnoop" "${TEST_NAME}.0.log" "update from tnoop channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the tnoop package"
diff --git a/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh b/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh
index 7dc28fe..789cbb6 100755
--- a/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh
+++ b/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/evergreen/testing/tests/racing_updaters_test.sh b/src/starboard/evergreen/testing/tests/racing_updaters_test.sh
index 2865095..6d3cde6 100755
--- a/src/starboard/evergreen/testing/tests/racing_updaters_test.sh
+++ b/src/starboard/evergreen/testing/tests/racing_updaters_test.sh
@@ -44,7 +44,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "Created drain file"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "Created drain file at"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to create a drain file for the test package"
@@ -60,7 +60,7 @@
 
   clear_storage
 
-  wait_and_force_race_condition "Created drain file" "${LOG_PATH}/${TEST_NAME}.1.log" "${FILENAME}" &
+  wait_and_force_race_condition "Created drain file at" "${LOG_PATH}/${TEST_NAME}.1.log" "${FILENAME}" &
 
   start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.1.log" "failed to lock slot"
 
diff --git a/src/starboard/evergreen/testing/tests/test.html b/src/starboard/evergreen/testing/tests/test.html
index 81a943c..d0b47f3 100644
--- a/src/starboard/evergreen/testing/tests/test.html
+++ b/src/starboard/evergreen/testing/tests/test.html
@@ -37,8 +37,10 @@
       return;
     }
 
-    if (currentChannel == targetChannel) {
-      console.log("update from " + targetChannel + " channel installed");
+    if (status == "Update installed, pending restart") {
+      clearInterval(changeChannel);
+      console.log("update from " + currentChannel + " channel was installed");
+      return;
     }
   }
 
diff --git a/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh b/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh
index 65e5ad0..7565d99 100755
--- a/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh
+++ b/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh b/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh
index 5fe2228..57379c4 100755
--- a/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh
+++ b/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh b/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh
index 1d7e1c9..0bf6f36 100755
--- a/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh
+++ b/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh
@@ -25,7 +25,7 @@
 function run_test() {
   clear_storage
 
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel installed"
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "update from test channel was installed"
 
   if [[ $? -ne 0 ]]; then
     error "Failed to download and install the test package"
diff --git a/src/starboard/linux/shared/configuration_constants.cc b/src/starboard/linux/shared/configuration_constants.cc
index e3ad2c7..4311059 100644
--- a/src/starboard/linux/shared/configuration_constants.cc
+++ b/src/starboard/linux/shared/configuration_constants.cc
@@ -84,20 +84,6 @@
 // video.
 const uint32_t kSbMediaMaxVideoBitrateInBitsPerSecond = 200 * 1024 * 1024;
 
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-const uint32_t kSbMediaMaximumVideoFrames = 12;
-
-// The encoded video frames are compressed in different ways, their decoding
-// time can vary a lot.  Occasionally a single frame can take longer time to
-// decode than the average time per frame.  The player has to cache some frames
-// to account for such inconsistency.  The number of frames being cached are
-// controlled by the following two macros.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that set this value too large may increase the playback start delay.
-const uint32_t kSbMediaMaximumVideoPrerollFrames = 4;
-
 // Specifies how video frame buffers must be aligned on this platform.
 const uint32_t kSbMediaVideoFrameAlignment = 256;
 
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index 1990c44..0e847e4 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -199,6 +199,20 @@
 // --- Media Configuration ---------------------------------------------------
 
 #if SB_API_VERSION < 12
+// Allow ac3 and ec3 support
+#define SB_HAS_AC3_AUDIO 1
+#endif  // SB_API_VERSION < 12
+
+#if SB_API_VERSION < 12
+// Specifies whether this platform updates audio frames asynchronously.  In such
+// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
+// indicate the absolute time that the consumed audio frames are reported.
+// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
+// details.
+#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 0
+#endif  // SB_API_VERSION <  12
+
+#if SB_API_VERSION < 12
 // The maximum audio bitrate the platform can decode.  The following value
 // equals to 5M bytes per seconds which is more than enough for compressed
 // audio.
@@ -219,26 +233,12 @@
 #endif  // SB_API_VERSION < 12
 
 #if SB_API_VERSION < 12
-// Specifies whether this platform updates audio frames asynchronously.  In such
-// case an extra parameter will be added to |SbAudioSinkConsumeFramesFunc| to
-// indicate the absolute time that the consumed audio frames are reported.
-// Check document for |SbAudioSinkConsumeFramesFunc| in audio_sink.h for more
-// details.
-#define SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING 0
-#endif  // SB_API_VERSION <  12
-
-#if SB_API_VERSION < 12
 // Specifies the stack size for threads created inside media stack.  Set to 0 to
 // use the default thread stack size.  Set to non-zero to explicitly set the
 // stack size for media stack threads.
 #define SB_MEDIA_THREAD_STACK_SIZE 0U
 #endif  // SB_API_VERSION < 12
 
-#if SB_API_VERSION < 12
-// Allow ac3 and ec3 support
-#define SB_HAS_AC3_AUDIO 1
-#endif  // SB_API_VERSION < 12
-
 // --- Decoder-only Params ---
 
 #if SB_API_VERSION < 12
@@ -253,24 +253,6 @@
 #define SB_MEDIA_VIDEO_FRAME_ALIGNMENT 256U
 #endif  // SB_API_VERSION < 12
 
-#if SB_API_VERSION < 12
-// The encoded video frames are compressed in different ways, their decoding
-// time can vary a lot.  Occasionally a single frame can take longer time to
-// decode than the average time per frame.  The player has to cache some frames
-// to account for such inconsistency.  The number of frames being cached are
-// controlled by the following two macros.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that set this value too large may increase the playback start delay.
-#define SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES 4
-#endif  // SB_API_VERSION < 12
-
-#if SB_API_VERSION < 12
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
-#endif  // SB_API_VERSION < 12
-
 // --- Memory Configuration --------------------------------------------------
 
 #if SB_API_VERSION < 12
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h b/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h
index 3356def..65a0411 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h
+++ b/src/starboard/linux/x64x11/blittergles/sbversion/10/configuration_public.h
@@ -88,11 +88,11 @@
 // microphone supported.
 #define SB_HAS_MICROPHONE 1
 
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
 // Whether the current platform has speech synthesis.
 #undef SB_HAS_SPEECH_SYNTHESIS
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
 #endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_10_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/blittergles/sbversion/11/configuration_public.h b/src/starboard/linux/x64x11/blittergles/sbversion/11/configuration_public.h
index 6aa1686..733c9d8 100644
--- a/src/starboard/linux/x64x11/blittergles/sbversion/11/configuration_public.h
+++ b/src/starboard/linux/x64x11/blittergles/sbversion/11/configuration_public.h
@@ -84,11 +84,11 @@
 // microphone supported.
 #define SB_HAS_MICROPHONE 1
 
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
 // Whether the current platform has speech synthesis.
 #undef SB_HAS_SPEECH_SYNTHESIS
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
 #endif  // STARBOARD_LINUX_X64X11_BLITTERGLES_SBVERSION_11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/blittergles/shared/configuration_public.h b/src/starboard/linux/x64x11/blittergles/shared/configuration_public.h
index 7a312fd..5661880 100644
--- a/src/starboard/linux/x64x11/blittergles/shared/configuration_public.h
+++ b/src/starboard/linux/x64x11/blittergles/shared/configuration_public.h
@@ -18,6 +18,8 @@
 #ifndef STARBOARD_LINUX_X64X11_BLITTERGLES_SHARED_CONFIGURATION_PUBLIC_H_
 #define STARBOARD_LINUX_X64X11_BLITTERGLES_SHARED_CONFIGURATION_PUBLIC_H_
 
+// --- Graphics Configuration ------------------------------------------------
+
 // This configuration supports the blitter API (implemented via GLES).
 #undef SB_HAS_BLITTER
 #define SB_HAS_BLITTER 1
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h
index 38bcc2e..3802b49 100644
--- a/src/starboard/linux/x64x11/configuration_public.h
+++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -57,14 +57,13 @@
 #define SB_HAS_MICROPHONE 1
 #endif  // SB_API_VERSION < 12
 
-// Whether the current platform has speech synthesis.
-#undef SB_HAS_SPEECH_SYNTHESIS
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
 #if SB_API_VERSION >= 8
 // Whether the current platform implements the on screen keyboard interface.
 #define SB_HAS_ON_SCREEN_KEYBOARD 0
-
 #endif  // SB_API_VERSION >= 8
 
+// Whether the current platform has speech synthesis.
+#undef SB_HAS_SPEECH_SYNTHESIS
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
 #endif  // STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/10/configuration_public.h b/src/starboard/linux/x64x11/sbversion/10/configuration_public.h
index 484c00a..8f0475e 100644
--- a/src/starboard/linux/x64x11/sbversion/10/configuration_public.h
+++ b/src/starboard/linux/x64x11/sbversion/10/configuration_public.h
@@ -84,13 +84,13 @@
 // microphone supported.
 #define SB_HAS_MICROPHONE 1
 
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
 // Whether the current platform has speech synthesis.
 #undef SB_HAS_SPEECH_SYNTHESIS
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
 // Whether the current platform has JIT support
 #define ENGINE_SUPPORTS_JIT 1
 
diff --git a/src/starboard/linux/x64x11/sbversion/11/configuration_public.h b/src/starboard/linux/x64x11/sbversion/11/configuration_public.h
index 3513fc5..fc32b78 100644
--- a/src/starboard/linux/x64x11/sbversion/11/configuration_public.h
+++ b/src/starboard/linux/x64x11/sbversion/11/configuration_public.h
@@ -80,11 +80,11 @@
 // microphone supported.
 #define SB_HAS_MICROPHONE 1
 
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
 // Whether the current platform has speech synthesis.
 #undef SB_HAS_SPEECH_SYNTHESIS
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
 #endif  // STARBOARD_LINUX_X64X11_SBVERSION_11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/12/configuration_public.h b/src/starboard/linux/x64x11/sbversion/12/configuration_public.h
index b38e6b0..e9d00ac 100644
--- a/src/starboard/linux/x64x11/sbversion/12/configuration_public.h
+++ b/src/starboard/linux/x64x11/sbversion/12/configuration_public.h
@@ -33,11 +33,11 @@
 // Include the Linux configuration that's common between all Desktop Linuxes.
 #include "starboard/linux/shared/configuration_public.h"
 
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
 // Whether the current platform has speech synthesis.
 #undef SB_HAS_SPEECH_SYNTHESIS
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
 #endif  // STARBOARD_LINUX_X64X11_SBVERSION_12_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/loader_app/installation_manager.cc b/src/starboard/loader_app/installation_manager.cc
index 9325c94..0401b50 100644
--- a/src/starboard/loader_app/installation_manager.cc
+++ b/src/starboard/loader_app/installation_manager.cc
@@ -574,20 +574,23 @@
 
 bool InstallationManager::SaveInstallationStore() {
   ValidatePriorities();
-  char buf[IM_MAX_INSTALLATION_STORE_SIZE];
+
   if (IM_MAX_INSTALLATION_STORE_SIZE < installation_store_.ByteSize()) {
     SB_LOG(ERROR) << "SaveInstallationStore: Data too large"
                   << installation_store_.ByteSize();
     return false;
   }
 
+  const size_t buf_size = installation_store_.ByteSize();
+  std::vector<char> buf(buf_size, 0);
   loader_app::SetPendingRestart(
       installation_store_.roll_forward_to_installation() != -1);
 
-  installation_store_.SerializeToArray(buf, installation_store_.ByteSize());
+  installation_store_.SerializeToArray(buf.data(),
+                                       installation_store_.ByteSize());
 
 #if SB_API_VERSION >= 12
-  if (!SbFileAtomicReplace(store_path_.c_str(), buf,
+  if (!SbFileAtomicReplace(store_path_.c_str(), buf.data(),
                            installation_store_.ByteSize())) {
     SB_LOG(ERROR)
         << "SaveInstallationStore: Failed to store installation store: "
diff --git a/src/starboard/nplb/atomic_base_test.cc b/src/starboard/nplb/atomic_base_test.cc
index f5fb925..3128e5a 100644
--- a/src/starboard/nplb/atomic_base_test.cc
+++ b/src/starboard/nplb/atomic_base_test.cc
@@ -68,7 +68,8 @@
 
   SbThread thread_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(TestThread);
+  TestThread(const TestThread&) = delete;
+  void operator=(const TestThread&) = delete;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc b/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc
index 60b6d6e..ac659ac 100644
--- a/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/sabi_test.cc
@@ -155,6 +155,12 @@
 };
 
 TEST_F(SabiTest, VerifySABI) {
+  SB_LOG(INFO) << "Using SB_API_VERSION=" << SB_API_VERSION;
+  SB_LOG(INFO) << "Using SABI=" << SB_SABI_JSON_ID;
+
+  ASSERT_LT(SB_API_VERSION, SB_EXPERIMENTAL_API_VERSION)
+      << "Evergreen should use SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION";
+
   std::set<std::string> sabi_set;
   sabi_set.insert(kSabiJsonIdArmHardfp);
   sabi_set.insert(kSabiJsonIdArmSoftfp);
diff --git a/src/starboard/nplb/system_get_stack_test.cc b/src/starboard/nplb/system_get_stack_test.cc
index 89c273a..9b0f3db 100644
--- a/src/starboard/nplb/system_get_stack_test.cc
+++ b/src/starboard/nplb/system_get_stack_test.cc
@@ -21,12 +21,14 @@
 namespace {
 
 SB_C_NOINLINE int GetStackWithAnExtraFrame(void** out_stack, int stack_size) {
-  // This EXPECT_NE is just enough to avoid inlining with optimizations on.
-  // This may not be enough on other platforms, so we'll have to keep an eye on
-  // it.
+  // These EXPECT_NE and EXPECT_LT should be enough to make function complicated
+  // and avoid inlining with optimizations on some platforms. But we'll have to
+  // keep an eye on it as this may not be enough on some other platforms.
   void** const kNullVpp = NULL;
   EXPECT_NE(kNullVpp, out_stack);
-  return SbSystemGetStack(out_stack, stack_size);
+  int ret = SbSystemGetStack(out_stack, stack_size);
+  EXPECT_LT(1, ret);
+  return ret;
 }
 
 SB_C_NOINLINE void WowThatsADeepStack() {
diff --git a/src/starboard/nplb/thread_helpers.h b/src/starboard/nplb/thread_helpers.h
index a646aa3..c3851f9 100644
--- a/src/starboard/nplb/thread_helpers.h
+++ b/src/starboard/nplb/thread_helpers.h
@@ -160,14 +160,11 @@
   void Start() {
     SbThreadEntryPoint entry_point = ThreadEntryPoint;
 
-    thread_ = SbThreadCreate(
-        0,                     // default stack_size.
-        kSbThreadNoPriority,   // default priority.
-        kSbThreadNoAffinity,   // default affinity.
-        true,                  // joinable.
-        "AbstractTestThread",
-        entry_point,
-        this);
+    thread_ = SbThreadCreate(0,                    // default stack_size.
+                             kSbThreadNoPriority,  // default priority.
+                             kSbThreadNoAffinity,  // default affinity.
+                             true,                 // joinable.
+                             "AbstractTestThread", entry_point, this);
 
     if (kSbThreadInvalid == thread_) {
       ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread.";
@@ -192,7 +189,8 @@
 
   SbThread thread_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(AbstractTestThread);
+  AbstractTestThread(const AbstractTestThread&) = delete;
+  void operator=(const AbstractTestThread&) = delete;
 };
 
 }  // namespace nplb
diff --git a/src/starboard/raspi/2/sbversion/12/configuration_public.h b/src/starboard/raspi/2/sbversion/12/configuration_public.h
index c91229a..a1a55da 100644
--- a/src/starboard/raspi/2/sbversion/12/configuration_public.h
+++ b/src/starboard/raspi/2/sbversion/12/configuration_public.h
@@ -131,13 +131,6 @@
 // required for platforms that want to JIT.
 #define SB_CAN_MAP_EXECUTABLE_MEMORY 1
 
-// The Raspberry Pi does not apparently align fields in a heap-allocated struct
-// by over 16 bytes.
-#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
-
-// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
-#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
-
 // --- Network Configuration -------------------------------------------------
 
 // Specifies whether this platform supports IPV6.
@@ -158,4 +151,13 @@
 #error "RasPi builds need a GCC-like compiler (for the moment)."
 #endif
 
+// --- Platform Specific Quirks ----------------------------------------------
+
+// The Raspberry Pi does not apparently align fields in a heap-allocated struct
+// by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
+
+// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
 #endif  // STARBOARD_RASPI_2_SBVERSION_12_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/shared/configuration_constants.cc b/src/starboard/raspi/shared/configuration_constants.cc
index 6816391..f733b23 100644
--- a/src/starboard/raspi/shared/configuration_constants.cc
+++ b/src/starboard/raspi/shared/configuration_constants.cc
@@ -84,20 +84,6 @@
 // video.
 const uint32_t kSbMediaMaxVideoBitrateInBitsPerSecond = 200 * 1024 * 1024;
 
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-const uint32_t kSbMediaMaximumVideoFrames = 12;
-
-// The encoded video frames are compressed in different ways, their decoding
-// time can vary a lot.  Occasionally a single frame can take longer time to
-// decode than the average time per frame.  The player has to cache some frames
-// to account for such inconsistency.  The number of frames being cached are
-// controlled by the following two macros.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that set this value too large may increase the playback start delay.
-const uint32_t kSbMediaMaximumVideoPrerollFrames = 4;
-
 // Specifies how video frame buffers must be aligned on this platform.
 const uint32_t kSbMediaVideoFrameAlignment = 256;
 
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 05988f6..8c437cf 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -146,13 +146,6 @@
 // required for platforms that want to JIT.
 #define SB_CAN_MAP_EXECUTABLE_MEMORY 1
 
-// The Raspberry Pi does not apparently align fields in a heap-allocated struct
-// by over 16 bytes.
-#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
-
-// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
-#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
-
 // --- Network Configuration -------------------------------------------------
 
 // Specifies whether this platform supports IPV6.
@@ -179,4 +172,13 @@
 #error "RasPi builds need a GCC-like compiler (for the moment)."
 #endif
 
+// --- Platform Specific Quirks ----------------------------------------------
+
+// The Raspberry Pi does not apparently align fields in a heap-allocated struct
+// by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
+
+// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
 #endif  // STARBOARD_RASPI_SHARED_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/shared/dispmanx_util.cc b/src/starboard/raspi/shared/dispmanx_util.cc
index 906cfb0..076c1a5 100644
--- a/src/starboard/raspi/shared/dispmanx_util.cc
+++ b/src/starboard/raspi/shared/dispmanx_util.cc
@@ -49,7 +49,8 @@
  private:
   DISPMANX_UPDATE_HANDLE_T handle_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DispmanxAutoUpdate);
+  DispmanxAutoUpdate(const DispmanxAutoUpdate&) = delete;
+  void operator=(const DispmanxAutoUpdate&) = delete;
 };
 
 }  // namespace
diff --git a/src/starboard/raspi/shared/dispmanx_util.h b/src/starboard/raspi/shared/dispmanx_util.h
index 4556323..5646d24 100644
--- a/src/starboard/raspi/shared/dispmanx_util.h
+++ b/src/starboard/raspi/shared/dispmanx_util.h
@@ -56,7 +56,8 @@
  private:
   DISPMANX_DISPLAY_HANDLE_T handle_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DispmanxDisplay);
+  DispmanxDisplay(const DispmanxDisplay&) = delete;
+  void operator=(const DispmanxDisplay&) = delete;
 };
 
 class DispmanxResource {
@@ -90,7 +91,8 @@
   uint32_t visible_width_;
   uint32_t visible_height_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DispmanxResource);
+  DispmanxResource(const DispmanxResource&) = delete;
+  void operator=(const DispmanxResource&) = delete;
 };
 
 class DispmanxYUV420Resource : public DispmanxResource {
@@ -140,7 +142,8 @@
  private:
   DISPMANX_ELEMENT_HANDLE_T handle_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DispmanxElement);
+  DispmanxElement(const DispmanxElement&) = delete;
+  void operator=(const DispmanxElement&) = delete;
 };
 
 class DispmanxVideoFrame
@@ -183,7 +186,8 @@
   // console does not show through.
   DispmanxRGB565Resource black_frame_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DispmanxVideoRenderer);
+  DispmanxVideoRenderer(const DispmanxVideoRenderer&) = delete;
+  void operator=(const DispmanxVideoRenderer&) = delete;
 };
 
 }  // namespace shared
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
index cba4c7e..b786f9c 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
@@ -46,7 +46,7 @@
 #if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
 
 void ReleaseBuffer(void* opaque, uint8_t* data) {
-  SbMemoryDeallocate(data);
+  SbMemoryDeallocateAligned(data);
 }
 
 int AllocateBufferCallback(AVCodecContext* codec_context,
@@ -66,7 +66,7 @@
 }
 
 void ReleaseBuffer(AVCodecContext*, AVFrame* frame) {
-  SbMemoryDeallocate(frame->opaque);
+  SbMemoryDeallocateAligned(frame->opaque);
   frame->opaque = NULL;
 
   // The FFmpeg API expects us to zero the data pointers in this callback.
@@ -275,8 +275,9 @@
   int codec_aligned_width = av_frame_->width;
   int codec_aligned_height = av_frame_->height;
   int codec_linesize_align[AV_NUM_DATA_POINTERS];
-  ffmpeg_->avcodec_align_dimensions2(codec_context_,
-      &codec_aligned_width, &codec_aligned_height, codec_linesize_align);
+  ffmpeg_->avcodec_align_dimensions2(codec_context_, &codec_aligned_width,
+                                     &codec_aligned_height,
+                                     codec_linesize_align);
 
   int pitch = AlignUp(av_frame_->width, codec_linesize_align[0] * 2);
 
@@ -425,8 +426,9 @@
   int codec_aligned_width = codec_context->width;
   int codec_aligned_height = codec_context->height;
   int codec_linesize_align[AV_NUM_DATA_POINTERS];
-  ffmpeg_->avcodec_align_dimensions2(codec_context,
-      &codec_aligned_width, &codec_aligned_height, codec_linesize_align);
+  ffmpeg_->avcodec_align_dimensions2(codec_context, &codec_aligned_width,
+                                     &codec_aligned_height,
+                                     codec_linesize_align);
 
   // Align to linesize alignment * 2 as we will divide y_stride by 2 for
   // u and v planes.
@@ -478,8 +480,9 @@
   int codec_aligned_width = codec_context->width;
   int codec_aligned_height = codec_context->height;
   int codec_linesize_align[AV_NUM_DATA_POINTERS];
-  ffmpeg_->avcodec_align_dimensions2(codec_context,
-      &codec_aligned_width, &codec_aligned_height, codec_linesize_align);
+  ffmpeg_->avcodec_align_dimensions2(codec_context, &codec_aligned_width,
+                                     &codec_aligned_height,
+                                     codec_linesize_align);
 
   // Align to linesize alignment * 2 as we will divide y_stride by 2 for
   // u and v planes.
diff --git a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
index 384b38a..3526b0a 100644
--- a/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
+++ b/src/starboard/shared/libdav1d/dav1d_video_decoder.cc
@@ -188,7 +188,7 @@
   SB_DCHECK(picture->data[0] < picture->data[1]);
   SB_DCHECK(picture->data[1] < picture->data[2]);
 
-  SbMemoryDeallocate(picture->data[0]);
+  SbMemoryDeallocateAligned(picture->data[0]);
   for (int i = 0; i < 3; ++i) {
     picture->data[i] = NULL;
   }
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index 406e4aa..50f622a 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -316,6 +316,7 @@
         case kStateStopped:
           return true;
         case kStateFrozen:
+          OnResume();
           Inject(new Event(kSbEventTypeUnfreeze, NULL, NULL));
           Inject(scoped_event.release());
           return true;
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
index bffc40a..c1cf847 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
@@ -27,6 +27,7 @@
   // When |capability_changed| is true, it hints that the error is caused by a
   // a transisent capability on the platform.  The app should retry playback to
   // recover from the error.
+  // TODO: Allow to pass an error message.
   typedef void (*ErrorFunc)(bool capability_changed, void* context);
 #endif  // SB_API_VERSION >= 12
 
diff --git a/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h b/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
index 0952566..1aff514 100644
--- a/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
+++ b/src/starboard/shared/starboard/configuration_constants_compatibility_defines.h
@@ -61,10 +61,6 @@
 #define kSbMediaMaxVideoBitrateInBitsPerSecond \
   SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND
 
-#define kSbMediaMaximumVideoFrames SB_MEDIA_MAXIMUM_VIDEO_FRAMES
-
-#define kSbMediaMaximumVideoPrerollFrames SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES
-
 #define kSbMediaVideoFrameAlignment SB_MEDIA_VIDEO_FRAME_ALIGNMENT
 
 #define kSbMemoryLogPath SB_MEMORY_LOG_PATH
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
index 7dab6c0..8005cd8 100644
--- a/src/starboard/shared/starboard/media/media_util.cc
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -627,6 +627,20 @@
   return "Invalid";
 }
 
+#if SB_API_VERSION >= 11
+bool IsAudioSampleInfoSubstantiallyDifferent(
+    const SbMediaAudioSampleInfo& left,
+    const SbMediaAudioSampleInfo& right) {
+  return left.codec != right.codec ||
+         left.samples_per_second != right.samples_per_second ||
+         left.number_of_channels != right.number_of_channels ||
+         left.audio_specific_config_size != right.audio_specific_config_size ||
+         SbMemoryCompare(left.audio_specific_config,
+                         right.audio_specific_config,
+                         left.audio_specific_config_size) != 0;
+}
+#endif  // SB_API_VERSION < 11
+
 }  // namespace media
 }  // namespace starboard
 }  // namespace shared
diff --git a/src/starboard/shared/starboard/media/media_util.h b/src/starboard/shared/starboard/media/media_util.h
index 72edc89..15a9742 100644
--- a/src/starboard/shared/starboard/media/media_util.h
+++ b/src/starboard/shared/starboard/media/media_util.h
@@ -104,6 +104,14 @@
 const char* GetMatrixIdName(SbMediaMatrixId matrix_id);
 const char* GetRangeIdName(SbMediaRangeId range_id);
 
+#if SB_API_VERSION >= 11
+//  When this function returns true, usually indicates that the two sample info
+//  cannot be processed by the same audio decoder.
+bool IsAudioSampleInfoSubstantiallyDifferent(
+    const SbMediaAudioSampleInfo& left,
+    const SbMediaAudioSampleInfo& right);
+#endif  // SB_API_VERSION < 11
+
 }  // namespace media
 }  // namespace starboard
 }  // namespace shared
diff --git a/src/starboard/shared/starboard/net_log.cc b/src/starboard/shared/starboard/net_log.cc
index c63c27b..24f733a 100644
--- a/src/starboard/shared/starboard/net_log.cc
+++ b/src/starboard/shared/starboard/net_log.cc
@@ -66,20 +66,16 @@
 
 class FunctionThread : public Thread {
  public:
-  static scoped_ptr<Thread> Create(
-      const std::string& thread_name,
-      RunFunction run_function) {
+  static scoped_ptr<Thread> Create(const std::string& thread_name,
+                                   RunFunction run_function) {
     scoped_ptr<Thread> out(new FunctionThread(thread_name, run_function));
     return out.Pass();
   }
 
   FunctionThread(const std::string& name, RunFunction run_function)
-      : Thread(name), run_function_(run_function) {
-  }
+      : Thread(name), run_function_(run_function) {}
 
-  void Run() override {
-    run_function_(join_sema());
-  }
+  void Run() override { run_function_(join_sema()); }
 
  private:
   RunFunction run_function_;
@@ -87,17 +83,22 @@
 
 std::string ToString(SbSocketError error) {
   switch (error) {
-    case kSbSocketOk: { return "kSbSocketOk"; }
-    case kSbSocketPending: { return "kSbSocketErrorConnectionReset"; }
-    case kSbSocketErrorFailed: { return "kSbSocketErrorFailed"; }
+    case kSbSocketOk: {
+      return "kSbSocketOk";
+    }
+    case kSbSocketPending: {
+      return "kSbSocketErrorConnectionReset";
+    }
+    case kSbSocketErrorFailed: {
+      return "kSbSocketErrorFailed";
+    }
 
-#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
-  SB_API_VERSION >= 9
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || SB_API_VERSION >= 9
     case kSbSocketErrorConnectionReset: {
       return "kSbSocketErrorConnectionReset";
     }
 #endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) ||
-      // SB_API_VERSION >= 9
+        // SB_API_VERSION >= 9
   }
   SB_NOTREACHED() << "Unexpected case " << error;
   std::stringstream ss;
@@ -167,10 +168,7 @@
       }
     };
 
-    SbSocketWaiterAdd(waiter,
-                      sock,
-                      NULL,
-                      &F::WakeUp,
+    SbSocketWaiterAdd(waiter, sock, NULL, &F::WakeUp,
                       kSbSocketWaiterInterestWrite,
                       false);  // false means one shot.
 
@@ -180,8 +178,7 @@
   }
 
   bool IsConnectionReset(SbSocketError err) {
-#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
-    SB_API_VERSION >= 9
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || SB_API_VERSION >= 9
     return err == kSbSocketErrorConnectionReset;
 #else
     return false;
@@ -250,9 +247,7 @@
 
   void PrependData(const std::string& curr_write_block) {
     ScopedLock lock_log(log_mutex_);
-    log_.insert(log_.begin(),
-                curr_write_block.begin(),
-                curr_write_block.end());
+    log_.insert(log_.begin(), curr_write_block.begin(), curr_write_block.end());
   }
 
   int max_memory_buffer_size_;
@@ -270,16 +265,13 @@
   typedef std::function<void(scoped_ptr<Socket>)> Callback;
 
   SocketListener(Socket* listen_socket, Callback cb)
-      : listen_socket_(listen_socket),
-        callback_(cb) {
+      : listen_socket_(listen_socket), callback_(cb) {
     auto run_cb = [this](Semaphore* sema) { this->Run(sema); };
     thread_ = FunctionThread::Create("NetLogSocketListener", run_cb);
     thread_->Start();
   }
 
-  ~SocketListener() {
-    thread_->Join();
-  }
+  ~SocketListener() { thread_->Join(); }
 
  private:
   void Run(Semaphore* joined_sema) {
@@ -301,15 +293,16 @@
 class NetLogServer {
  public:
   static NetLogServer* Instance();
-  NetLogServer() : buffered_socket_writer_(NET_LOG_MAX_IN_MEMORY_BUFFER,
-                                           NET_LOG_SOCKET_SEND_SIZE) {
+  NetLogServer()
+      : buffered_socket_writer_(NET_LOG_MAX_IN_MEMORY_BUFFER,
+                                NET_LOG_SOCKET_SEND_SIZE) {
     ScopedLock lock(socket_mutex_);
     listen_socket_ = CreateListenSocket();
     ListenForClient();
 
     writer_thread_ = FunctionThread::Create(
-      "NetLogSocketWriter",
-      [this](Semaphore* sema) { this->WriterTask(sema); });
+        "NetLogSocketWriter",
+        [this](Semaphore* sema) { this->WriterTask(sema); });
     writer_thread_->Start();
   }
 
@@ -319,9 +312,8 @@
   }
 
   void ListenForClient() {
-    SocketListener::Callback cb = std::bind(&NetLogServer::OnClientConnect,
-                                            this,
-                                            std::placeholders::_1);
+    SocketListener::Callback cb =
+        std::bind(&NetLogServer::OnClientConnect, this, std::placeholders::_1);
     socket_listener_.reset();
     socket_listener_.reset(new SocketListener(listen_socket_.get(), cb));
   }
@@ -413,9 +405,12 @@
     void* ptr = SbThreadGetLocalValue(slot_);
     return ptr != nullptr;
   }
+
  private:
   SbThreadLocalKey slot_;
-  SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean);
+
+  ThreadLocalBoolean(const ThreadLocalBoolean&) = delete;
+  void operator=(const ThreadLocalBoolean&) = delete;
 };
 
 SB_ONCE_INITIALIZE_FUNCTION(NetLogServer, NetLogServer::Instance);
@@ -430,13 +425,10 @@
     tlb_->Set(true);
   }
 
-  ~ScopeGuard() {
-    tlb_->Set(disabled_);
-  }
+  ~ScopeGuard() { tlb_->Set(disabled_); }
 
-  bool IsEnabled() {
-    return !disabled_;
-  }
+  bool IsEnabled() { return !disabled_; }
+
  private:
   bool disabled_;
   ThreadLocalBoolean* tlb_;
@@ -450,9 +442,9 @@
 #if !SB_LOGGING_IS_OFFICIAL_BUILD
   ScopeGuard guard;
   if (guard.IsEnabled()) {
-    SbTimeMonotonic expire_time = (timeout >= 0) && (timeout < kSbTimeMax)?
-                                  SbTimeGetMonotonicNow() + timeout :
-                                  kSbTimeMax;
+    SbTimeMonotonic expire_time = (timeout >= 0) && (timeout < kSbTimeMax)
+                                      ? SbTimeGetMonotonicNow() + timeout
+                                      : kSbTimeMax;
     while (true) {
       if (NetLogServer::Instance()->HasClientConnected()) {
         break;
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.cc b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
index 1e21a40..aca60f5 100644
--- a/src/starboard/shared/starboard/player/decoded_audio_internal.cc
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -81,10 +81,11 @@
 
   if (samples_per_second == 0 || frames_to_remove < 0 ||
       frames_to_remove >= frames()) {
-    SB_LOG(WARNING) << "AdjustForSeekTime failed for seeking_to_time at "
-                    << seeking_to_time << " for samples_per_second "
-                    << samples_per_second << ", and there are " << frames()
-                    << " frames in the DecodedAudio object.";
+    SB_LOG(WARNING) << "AdjustForSeekTime failed for seeking_to_time: "
+                    << seeking_to_time
+                    << ", samples_per_second: " << samples_per_second
+                    << ", timestamp: " << timestamp() << ", and there are "
+                    << frames() << " frames in the DecodedAudio object.";
     return;
   }
 
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.h b/src/starboard/shared/starboard/player/decoded_audio_internal.h
index 0ac7681..521bf95 100644
--- a/src/starboard/shared/starboard/player/decoded_audio_internal.h
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -73,7 +73,8 @@
   scoped_array<uint8_t> buffer_;
   size_t size_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DecodedAudio);
+  DecodedAudio(const DecodedAudio&) = delete;
+  void operator=(const DecodedAudio&) = delete;
 };
 
 }  // namespace player
diff --git a/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc b/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
index ae3b448..8eba0ca 100644
--- a/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
@@ -17,6 +17,7 @@
 #include "starboard/audio_sink.h"
 #include "starboard/common/log.h"
 #include "starboard/common/reset_and_return.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 
 namespace starboard {
@@ -34,29 +35,6 @@
       kDefaultOutputSamplesPerSecond);
 }
 
-bool IsResetDecoderNecessary(const SbMediaAudioSampleInfo& current_info,
-                             const SbMediaAudioSampleInfo& new_info) {
-  if (current_info.codec != new_info.codec) {
-    return true;
-  }
-  if (current_info.samples_per_second != new_info.samples_per_second) {
-    return true;
-  }
-  if (current_info.number_of_channels != new_info.number_of_channels) {
-    return true;
-  }
-  if (current_info.audio_specific_config_size !=
-      new_info.audio_specific_config_size) {
-    return true;
-  }
-  if (SbMemoryCompare(current_info.audio_specific_config,
-                      new_info.audio_specific_config,
-                      current_info.audio_specific_config_size) != 0) {
-    return true;
-  }
-  return false;
-}
-
 AdaptiveAudioDecoder::AdaptiveAudioDecoder(
     const SbMediaAudioSampleInfo& audio_sample_info,
     SbDrmSystem drm_system,
@@ -107,8 +85,8 @@
     }
     return;
   }
-  if (IsResetDecoderNecessary(input_audio_sample_info_,
-                              input_buffer->audio_sample_info())) {
+  if (starboard::media::IsAudioSampleInfoSubstantiallyDifferent(
+          input_audio_sample_info_, input_buffer->audio_sample_info())) {
     flushing_ = true;
     pending_input_buffer_ = input_buffer;
     pending_consumed_cb_ = consumed_cb;
diff --git a/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
index 3170a01..e0bbe8b 100644
--- a/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
+++ b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
@@ -218,7 +218,8 @@
   int initial_capacity_;
   int max_capacity_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(AudioTimeStretcher);
+  AudioTimeStretcher(const AudioTimeStretcher&) = delete;
+  void operator=(const AudioTimeStretcher&) = delete;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h
index 83ca887..abf2c7b 100644
--- a/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h
+++ b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h
@@ -97,7 +97,8 @@
   // Number of frames available to be read in the buffer.
   int frames_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(DecodedAudioQueue);
+  DecodedAudioQueue(const DecodedAudioQueue&) = delete;
+  void operator=(const DecodedAudioQueue&) = delete;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/interleaved_sinc_resampler.h b/src/starboard/shared/starboard/player/filter/interleaved_sinc_resampler.h
index b7f2b15..be29e2d 100644
--- a/src/starboard/shared/starboard/player/filter/interleaved_sinc_resampler.h
+++ b/src/starboard/shared/starboard/player/filter/interleaved_sinc_resampler.h
@@ -86,7 +86,8 @@
     scoped_array<float> data_;
     int frames_ = 0;
 
-    SB_DISALLOW_COPY_AND_ASSIGN(Buffer);
+    Buffer(const Buffer&) = delete;
+    void operator=(const Buffer&) = delete;
   };
 
   // The kernel size can be adjusted for quality (higher is better) at the
@@ -164,7 +165,8 @@
   float* r4_;
   float* r5_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(InterleavedSincResampler);
+  InterleavedSincResampler(const InterleavedSincResampler&) = delete;
+  void operator=(const InterleavedSincResampler&) = delete;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index e4c0eef..543ca34 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -113,12 +113,8 @@
       }
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
-      SbPlayer player() const {
-        return player_;
-      }
-      SbPlayerOutputMode output_mode() const {
-        return output_mode_;
-      }
+      SbPlayer player() const { return player_; }
+      SbPlayerOutputMode output_mode() const { return output_mode_; }
       SbDecodeTargetGraphicsContextProvider*
       decode_target_graphics_context_provider() const {
         SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
@@ -203,7 +199,8 @@
         int* min_frames_per_append) const;
 
    private:
-    SB_DISALLOW_COPY_AND_ASSIGN(Factory);
+    Factory(const Factory&) = delete;
+    void operator=(const Factory&) = delete;
   };
 
   PlayerComponents() = default;
@@ -214,7 +211,8 @@
   virtual VideoRenderer* GetVideoRenderer() = 0;
 
  private:
-  SB_DISALLOW_COPY_AND_ASSIGN(PlayerComponents);
+  PlayerComponents(const PlayerComponents&) = delete;
+  void operator=(const PlayerComponents&) = delete;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_factory.h b/src/starboard/shared/starboard/player/filter/stub_player_components_factory.h
index 1a9f49e..5753f27 100644
--- a/src/starboard/shared/starboard/player/filter/stub_player_components_factory.h
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_factory.h
@@ -54,7 +54,8 @@
  private:
   StubPlayerComponentsFactory() {}
 
-  SB_DISALLOW_COPY_AND_ASSIGN(StubPlayerComponentsFactory);
+  StubPlayerComponentsFactory(const StubPlayerComponentsFactory&) = delete;
+  void operator=(const StubPlayerComponentsFactory&) = delete;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_frame_internal.h b/src/starboard/shared/starboard/player/filter/video_frame_internal.h
index b3705ef..08084eb 100644
--- a/src/starboard/shared/starboard/player/filter/video_frame_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_frame_internal.h
@@ -49,7 +49,8 @@
  private:
   SbTime timestamp_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(VideoFrame);
+  VideoFrame(const VideoFrame&) = delete;
+  void operator=(const VideoFrame&) = delete;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.h b/src/starboard/shared/starboard/player/input_buffer_internal.h
index 718c5bb..efca7f7 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.h
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.h
@@ -93,7 +93,8 @@
   std::vector<SbDrmSubSampleMapping> subsamples_;
   std::vector<uint8_t> flattened_data_;
 
-  SB_DISALLOW_COPY_AND_ASSIGN(InputBuffer);
+  InputBuffer(const InputBuffer&) = delete;
+  void operator=(const InputBuffer&) = delete;
 };
 
 }  // namespace player
diff --git a/src/starboard/shared/starboard/player/player.gyp b/src/starboard/shared/starboard/player/player.gyp
index 2f8e366..8e3c9eb 100644
--- a/src/starboard/shared/starboard/player/player.gyp
+++ b/src/starboard/shared/starboard/player/player.gyp
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 {
-
   'targets': [
     {
       'target_name': 'video_dmp',
@@ -38,24 +37,33 @@
       'target_name': 'player_copy_test_data',
       'type': 'none',
       'variables': {
-        'content_test_input_files': ['<!@pymod_do_main(starboard.build.gyp_functions file_glob_sub <(DEPTH)/starboard/shared/starboard/player/testdata *.dmp.sha1 dmp.sha1 dmp)'],
+        'content_test_input_files': [
+          '<!@pymod_do_main(starboard.build.gyp_functions file_glob_sub <(DEPTH)/starboard/shared/starboard/player/testdata *.dmp.sha1 dmp.sha1 dmp)'
+        ],
         'content_test_output_subdir': 'starboard/shared/starboard/player/testdata',
-        'download_from_google_storage_path': ['<!@pymod_do_main(starboard.build.gyp_functions find_program download_from_google_storage)'],
       },
       'actions' : [
         {
-          # This action requires depot_tools to be in path
-          # (https://cobalt.googlesource.com/depot_tools).
+          'variables': {
+            'sha_dir': '<(DEPTH)/starboard/shared/starboard/player/testdata',
+            # GYP will remove an argument to an action that looks like a duplicate, so
+            # we change it semantically here.
+            'out_dir': '<(DEPTH)/starboard/../starboard/shared/starboard/player/testdata',
+          },
           'action_name': 'player_download_test_data',
-          'action': [ '<(download_from_google_storage_path)',
-                      '--no_resume',
-                      '--no_auth',
-                      '--num_threads', '8',
-                      '--bucket', 'cobalt-static-storage',
-                      '-d', '<(DEPTH)/starboard/shared/starboard/player/testdata',
+          'action': [
+            'python',
+            '<(DEPTH)/tools/download_from_gcs.py',
+            '--bucket', 'cobalt-static-storage',
+            '--sha1', '<(sha_dir)',
+            '--output', '<(out_dir)',
           ],
-          'inputs': [],
-          'outputs': ['<!@pymod_do_main(starboard.build.gyp_functions file_glob_sub <(DEPTH)/starboard/shared/starboard/player/testdata *.dmp.sha1 dmp.sha1 dmp)'],
+          'inputs': [
+            '<!@pymod_do_main(starboard.build.gyp_functions file_glob <(sha_dir) *.dmp.sha1)',
+          ],
+          'outputs': [
+            '<!@pymod_do_main(starboard.build.gyp_functions file_glob_sub <(DEPTH)/starboard/shared/starboard/player/testdata *.dmp.sha1 dmp.sha1 dmp)',
+          ],
         },
       ],
       'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'],
diff --git a/src/starboard/stub/configuration_constants.cc b/src/starboard/stub/configuration_constants.cc
index c3e1c74..1ef685f 100644
--- a/src/starboard/stub/configuration_constants.cc
+++ b/src/starboard/stub/configuration_constants.cc
@@ -81,21 +81,6 @@
 // video.
 const uint32_t kSbMediaMaxVideoBitrateInBitsPerSecond = 200 * 1024 * 1024;
 
-// Specify the number of video frames to be cached during playback.  A large
-// value leads to more stable fps but also causes the app to use more memory.
-const uint32_t kSbMediaMaximumVideoFrames = 12;
-
-// The encoded video frames are compressed in different ways, so their decoding
-// time can vary a lot.  Occasionally a single frame can take longer time to
-// decode than the average time per frame.  The player has to cache some frames
-// to account for such inconsistency.  The number of frames being cached are
-// controlled by SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES and
-// SB_MEDIA_MAXIMUM_VIDEO_FRAMES.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that setting this value too large may increase the playback start delay.
-const uint32_t kSbMediaMaximumVideoPrerollFrames = 4;
-
 // Specifies how video frame buffers must be aligned on this platform.
 const uint32_t kSbMediaVideoFrameAlignment = 256;
 
diff --git a/src/starboard/tools/app_launcher_packager.py b/src/starboard/tools/app_launcher_packager.py
index 902d8e0..d978de4 100644
--- a/src/starboard/tools/app_launcher_packager.py
+++ b/src/starboard/tools/app_launcher_packager.py
@@ -24,6 +24,7 @@
 import logging
 import os
 import shutil
+import string
 import sys
 import tempfile
 
@@ -32,7 +33,6 @@
 from paths import THIRD_PARTY_ROOT
 sys.path.append(THIRD_PARTY_ROOT)
 # pylint: disable=g-import-not-at-top,g-bad-import-order
-import jinja2
 from starboard.tools import command_line
 from starboard.tools import log_level
 from starboard.tools import port_symlink
@@ -40,15 +40,12 @@
 
 # Default python directories to app launcher resources.
 _INCLUDE_FILE_PATTERNS = [
-    ('buildbot', '*.py'),
+    ('buildbot', '_env.py'),  # Only needed for device_server to execute
+    ('buildbot', '__init__.py'),  # Only needed for device_server to execute
+    ('buildbot/device_server', '*.py'),
     ('buildbot/device_server/shared/ssl_certs', '*'),
     ('cobalt', '*.py'),
-    # TODO: Test and possibly prune.
-    ('lbshell', '*.py'),
     ('starboard', '*.py'),
-    # jinja2 required by this app_launcher_packager.py script.
-    ('third_party/jinja2', '*.py'),
-    ('third_party/markupsafe', '*.py'),  # Required by third_party/jinja2
 ]
 
 _INCLUDE_BLACK_BOX_TESTS_PATTERNS = [
@@ -134,10 +131,11 @@
     # Store posix paths even on Windows so MH Linux hosts can use them.
     # The template has code to re-normalize them when used on Windows hosts.
     platforms_map[p] = platform_path.replace('\\', '/')
-  template = jinja2.Template(
+  template = string.Template(
       open(os.path.join(current_dir, 'platform.py.template')).read())
   with open(os.path.join(dest_dir, 'platform.py'), 'w+') as f:
-    template.stream(platforms_map=platforms_map).dump(f, encoding='utf-8')
+    sub = template.substitute(platforms_map=platforms_map)
+    f.write(sub.encode('utf-8'))
   logging.info('Finished baking in platform info files.')
 
 
@@ -258,9 +256,12 @@
       action='store_true',
       help='List to stdout the application resources relative to the current '
       'directory.')
-  parser.add_argument('-v', '--verbose', action='store_true',
-                      help='Enables verbose logging. For more control over the '
-                      "logging level use '--log_level' instead.")
+  parser.add_argument(
+      '-v',
+      '--verbose',
+      action='store_true',
+      help='Enables verbose logging. For more control over the '
+      "logging level use '--log_level' instead.")
   args = parser.parse_args(command_args)
 
   log_level.InitializeLogging(args)
diff --git a/src/starboard/tools/cache.py b/src/starboard/tools/cache.py
new file mode 100644
index 0000000..4839ffc
--- /dev/null
+++ b/src/starboard/tools/cache.py
@@ -0,0 +1,64 @@
+#
+# Copyright 2020 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.
+#
+"""Cache specific definitions and helper functions.
+
+Override settings to enable/disable cache using environment variables. Default
+is None.
+"""
+
+import logging
+import os
+
+from starboard.tools import build_accelerator
+from starboard.tools import util
+
+
+class Accelerator:
+  CCACHE = ('ccache', 'USE_CCACHE')
+  # Windows compatible ccache.
+  SCCACHE = ('sccache.exe', 'USE_SCCACHE')
+
+
+class Cache(build_accelerator.BuildAccelerator):
+  """Cache based build accelerator."""
+
+  def __init__(self, accelerator):
+    self.binary, self.env_var = accelerator
+
+  def GetName(self):
+    return self.binary
+
+  def Use(self):
+    return self.EnvOverride() and self.BinaryInstalled()
+
+  def EnvOverride(self):
+    """Checks env_var to enable/disable cache.
+
+    Returns:
+      env_var boolean if exists, defaults to True.
+    """
+    if self.env_var in os.environ:
+      return os.environ[self.env_var] == '1'
+    return True
+
+  def BinaryInstalled(self):
+    """Returns True if binary is installed, otherwise is False."""
+    binary_path = util.Which(self.binary)
+    if binary_path is not None:
+      logging.info('Using %s installed at: %s', self.binary, binary_path)
+      return True
+    logging.info('Unable to find %s.', self.binary)
+    return False
diff --git a/src/starboard/tools/ccache.py b/src/starboard/tools/ccache.py
deleted file mode 100644
index 5f1928c..0000000
--- a/src/starboard/tools/ccache.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Copyright 2020 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.
-#
-"""Ccache specific definitions and helper functions.
-
-Override settings using environment variables.
-USE_CCACHE  To enable/disable ccache. Default is None.
-"""
-
-import logging
-import os
-import util
-
-from starboard.tools import build_accelerator
-
-
-class Ccache(build_accelerator.BuildAccelerator):
-  """Ccache is a cache based build accelerator."""
-
-  def GetName(self):
-    return 'ccache'
-
-  def Use(self):
-    return CcacheEnvOverride() and CcacheInstalled()
-
-
-def CcacheEnvOverride():
-  """Checks USE_CCACHE to enable/disable ccache.
-
-  Returns:
-    USE_CCACHE boolean if exists, defaults to True.
-  """
-  if 'USE_CCACHE' in os.environ:
-    return os.environ['USE_CCACHE'] == '1'
-  return True
-
-
-def CcacheInstalled():
-  """Returns True if ccache is installed, otherwise is False."""
-  ccache_path = util.Which('ccache')
-  if ccache_path is not None:
-    logging.info('Using ccache installed at: %s', ccache_path)
-    return True
-  logging.error('Unable to find ccache.')
-  return False
diff --git a/src/starboard/tools/find_private_files.py b/src/starboard/tools/find_private_files.py
deleted file mode 100644
index b63ee10..0000000
--- a/src/starboard/tools/find_private_files.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python
-# 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.
-"""Gyp helper to find private files if present.
-
-The first argument is the result of "<(DEPTH)" from gyp, the second argument
-is the path/pattern from <(DEPTH)/starboard/private/. Patterns should be given
-using Unix path style '/'.
-
-python find_private_files.py "../.." "*.h"
-Would return files matching ../../starboard/private/*.h if any exist.
-
-python find_private_files.py "../.." "nplb/*_test.cc"
-Would return files matching ../../starboard/private/nplb/*_test.cc if any exist.
-
-NOTE: gyp errors often produce no warnings. Be sure to structure usages of this
-script by gyp files like the line below, where the'<!@' is a gyp command
-expansion that will process the results into a list of returned file paths.
-Quoting arguments protects against wildcard expansion and other undesirable
-gyp/shell behavior.
-'<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "*.h")',
-"""
-
-import glob
-import os
-import sys
-
-
-def find_private_files(depth,
-                       target_pattern,
-                       private_dir_path='starboard/private'):
-  """Assembles search glob and finds files matching the target pattern.
-
-  Args:
-    depth: The string result of "<(DEPTH)"" from gyp.
-    target_pattern: The string path/pattern from <(DEPTH)/|private_dir_path|
-    private_dir_path: Optional The path to the private directory, which
-      defaults to 'starboard/private'.
-  """
-  path = os.path.normpath(os.path.join(depth, private_dir_path, target_pattern))
-  for f in glob.iglob(path):
-    # Switch to Unix style '/' for gyp.
-    print f.replace('\\', '/')
-
-
-if __name__ == '__main__':
-  depth_arg = sys.argv[1]
-  target_pattern_arg = sys.argv[2]
-  if len(sys.argv) > 3:
-    private_dir_path_arg = sys.argv[3]
-    find_private_files(depth_arg, target_pattern_arg, private_dir_path_arg)
-  else:
-    find_private_files(depth_arg, target_pattern_arg)
-
-  sys.exit(0)
diff --git a/src/starboard/tools/platform.py.template b/src/starboard/tools/platform.py.template
index 17d55b5..204d415 100644
--- a/src/starboard/tools/platform.py.template
+++ b/src/starboard/tools/platform.py.template
@@ -20,7 +20,7 @@
 from starboard.tools import environment
 
 # The name->platform path mapping.
-_PATH_MAP = {{platforms_map}}
+_PATH_MAP = $platforms_map
 _PATH_MAP = {k:os.path.normpath(v) for k,v in _PATH_MAP.iteritems()}
 
 
diff --git a/src/starboard/tools/toolchain/msvc.py b/src/starboard/tools/toolchain/msvc.py
index 401ba5b..18032c4 100644
--- a/src/starboard/tools/toolchain/msvc.py
+++ b/src/starboard/tools/toolchain/msvc.py
@@ -50,24 +50,22 @@
     return 'msvc'
 
   def GetRspFilePath(self):
-    return '$out.rsp'
+    return None
 
   def GetRspFileContent(self):
-    return self._command_flags
+    return None
 
   def GetCommand(self, path, extra_flags, flags, shell):
     del extra_flags  # Not used.
     del shell  # Not used.
     self._command_flags = flags
-    return ('{path} /nologo /showIncludes /FC @$out.rsp /c $in /Fo$out '
-            '/Fd$pdbname'.format(path=path))
+    return ('{path} /nologo /showIncludes {flags} /c $in /Fo$out'.format(
+        path=path, flags=flags))
 
   def GetFlags(self, defines, include_dirs, cflags):
     defines = defines + self._gyp_defines
     quoted_defines = QuoteArguments(defines)
-    define_flags = [
-        '/D{0}'.format(define) for define in quoted_defines
-    ]
+    define_flags = ['/D{0}'.format(define) for define in quoted_defines]
     include_dirs = include_dirs + self._gyp_include_dirs
     quoted_include_dirs = QuoteArguments(include_dirs)
     include_dir_flags = [
@@ -128,8 +126,8 @@
     del extra_flags  # Not used.
     del shell  # Not used.
     return ('{python} gyp-win-tool asm-wrapper {arch} {path} {flags} /c /Fo '
-            '$out $in'.format(python=sys.executable, arch=self._arch,
-                              path=path, flags=flags))
+            '$out $in'.format(
+                python=sys.executable, arch=self._arch, path=path, flags=flags))
 
   def GetDescription(self):
     return 'ASM $in'
@@ -138,9 +136,7 @@
     del cflags  # Not used.
     defines = defines + self._gyp_defines
     quoted_defines = QuoteArguments(defines)
-    define_flags = [
-        '/D{0}'.format(define) for define in quoted_defines
-    ]
+    define_flags = ['/D{0}'.format(define) for define in quoted_defines]
     include_dirs = include_dirs + self._gyp_include_dirs
     quoted_include_dirs = QuoteArguments(include_dirs)
     include_dir_flags = [
@@ -156,6 +152,8 @@
     self._path = common.GetPath('ar', **kwargs)
     self._arch = kwargs.get('arch', 'environment.x64')
     self._gyp_libflags = kwargs.get('gyp_libflags', [])
+    self._max_concurrent_processes = kwargs.get('max_concurrent_processes',
+                                                None)
 
   def GetPath(self):
     return self._path
@@ -165,8 +163,7 @@
     return []
 
   def GetMaxConcurrentProcesses(self):
-    # Run as much concurrent processes as possible.
-    return None
+    return self._max_concurrent_processes
 
   def GetDescription(self):
     return 'LIB $out'
@@ -182,9 +179,8 @@
     del shell  # Not used.
     self._command_flags = flags
     return ('{python} gyp-win-tool link-wrapper {arch} {path} /nologo '
-            '/ignore:4221 /OUT:$out @$out.rsp'.format(python=sys.executable,
-                                                      arch=self._arch,
-                                                      path=path))
+            '/ignore:4221 /OUT:$out @$out.rsp'.format(
+                python=sys.executable, arch=self._arch, path=path))
 
   def GetFlags(self):
     return self._gyp_libflags
@@ -249,9 +245,8 @@
     del shell  # Not used.
     self._command_flags = flags
     return ('{python} gyp-win-tool link-wrapper {arch} {path} /nologo '
-            '/OUT:$out /PDB:$out.pdb @$out.rsp'.format(python=sys.executable,
-                                                       arch=self._arch,
-                                                       path=path))
+            '/OUT:$out /PDB:$out.pdb @$out.rsp'.format(
+                python=sys.executable, arch=self._arch, path=path))
 
 
 class SharedLibraryLinker(DynamicLinkerBase, abstract.SharedLibraryLinker):
@@ -274,9 +269,8 @@
     del shell  # Not used.
     self._command_flags = flags
     return ('{python} gyp-win-tool link-wrapper {arch} {path} /nologo '
-            '$implibflag /DLL /OUT:$dll /PDB:$dll.pdb @$dll.rsp'.format(python=sys.executable,
-                                                      arch=self._arch,
-                                                      path=path))
+            '$implibflag /DLL /OUT:$dll /PDB:$dll.pdb @$dll.rsp'.format(
+                python=sys.executable, arch=self._arch, path=path))
 
   def GetRestat(self):
     return True
diff --git a/src/starboard/ui_navigation.h b/src/starboard/ui_navigation.h
index e110c43..11efefa 100644
--- a/src/starboard/ui_navigation.h
+++ b/src/starboard/ui_navigation.h
@@ -241,6 +241,15 @@
   //
   // Essentially, content items should be drawn at:
   //   [container position] + [content position] - [container content offset]
+#if SB_API_VERSION >= SB_UI_NAVIGATION2_VERSION
+  //
+  // Content items may overlap within a container. This can cause obscured items
+  // to be unfocusable. The only rule that needs to be followed is that contents
+  // which are focus items can obscure other contents which are containers, but
+  // not vice versa. The caller must ensure that content focus items do not
+  // overlap other content focus items and content container items do not
+  // overlap other content container items.
+#endif
   void (*set_item_container_item)(SbUiNavItem item, SbUiNavItem container);
 
   // Set the current content offset for the given container. This may be used
diff --git a/src/third_party/angle/angle.gyp b/src/third_party/angle/angle.gyp
index 8bb6ad8..b34eea7 100644
--- a/src/third_party/angle/angle.gyp
+++ b/src/third_party/angle/angle.gyp
@@ -55,6 +55,13 @@
                 './src',
                 './src/common/third_party/base',
             ],
+            'msvs_settings':
+            {
+                  'VCCLCompilerTool':
+                  {
+                        'WarnAsError': 'false',
+                  },
+            },
             'direct_dependent_settings':
             {
                 'include_dirs':
diff --git a/src/third_party/angle/src/compiler.gypi b/src/third_party/angle/src/compiler.gypi
index 7be0ac3..4c87123 100644
--- a/src/third_party/angle/src/compiler.gypi
+++ b/src/third_party/angle/src/compiler.gypi
@@ -353,6 +353,13 @@
             'dependencies': [ 'angle_common' ],
             'includes': [ '../gyp/common_defines.gypi', ],
             'sources': [ '<@(angle_preprocessor_sources)', ],
+            'msvs_settings':
+            {
+                  'VCCLCompilerTool':
+                  {
+                        'WarnAsError': 'false',
+                  },
+            },
         },
         {
             'target_name': 'translator',
@@ -370,10 +377,14 @@
             ],
             'msvs_settings':
             {
-              'VCLibrarianTool':
-              {
-                'AdditionalOptions': ['/ignore:4221']
-              },
+                  'VCCLCompilerTool':
+                  {
+                        'WarnAsError': 'false',
+                  },
+                  'VCLibrarianTool':
+                  {
+                        'AdditionalOptions': ['/ignore:4221'],
+                  },
             },
             'conditions':
             [
diff --git a/src/third_party/freetype2/src/tools/Jamfile b/src/third_party/freetype2/src/tools/Jamfile
new file mode 100644
index 0000000..475161e
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/Jamfile
@@ -0,0 +1,5 @@
+# Jamfile for src/tools
+#
+SubDir FT2_TOP src tools ;
+
+Main  apinames : apinames.c ;
diff --git a/src/third_party/freetype2/src/tools/afblue.pl b/src/third_party/freetype2/src/tools/afblue.pl
new file mode 100644
index 0000000..bbc4f47
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/afblue.pl
@@ -0,0 +1,551 @@
+#! /usr/bin/perl -w
+# -*- Perl -*-
+#
+# afblue.pl
+#
+# Process a blue zone character data file.
+#
+# Copyright (C) 2013-2020 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used,
+# modified, and distributed under the terms of the FreeType project
+# license, LICENSE.TXT.  By continuing to use, modify, or distribute
+# this file you indicate that you have read the license and
+# understand and accept it fully.
+
+use strict;
+use warnings;
+use English '-no_match_vars';
+use open ':std', ':encoding(UTF-8)';
+
+
+my $prog = $PROGRAM_NAME;
+$prog =~ s| .* / ||x;      # Remove path.
+
+die "usage: $prog datafile < infile > outfile\n" if $#ARGV != 0;
+
+
+my $datafile = $ARGV[0];
+
+my %diversions;        # The extracted and massaged data from `datafile'.
+my @else_stack;        # Booleans to track else-clauses.
+my @name_stack;        # Stack of integers used for names of aux. variables.
+
+my $curr_enum;         # Name of the current enumeration.
+my $curr_array;        # Name of the current array.
+my $curr_max;          # Name of the current maximum value.
+
+my $curr_enum_element; # Name of the current enumeration element.
+my $curr_offset;       # The offset relative to current aux. variable.
+my $curr_elem_size;    # The number of non-space characters in the current string or
+                       # the number of elements in the current block.
+
+my $have_sections = 0; # Boolean; set if start of a section has been seen.
+my $have_strings;      # Boolean; set if current section contains strings.
+my $have_blocks;       # Boolean; set if current section contains blocks.
+
+my $have_enum_element; # Boolean; set if we have an enumeration element.
+my $in_string;         # Boolean; set if a string has been parsed.
+
+my $num_sections = 0;  # Number of sections seen so far.
+
+my $last_aux;          # Name of last auxiliary variable.
+
+
+# Regular expressions.
+
+# [<ws>] <enum_name> <ws> <array_name> <ws> <max_name> [<ws>] ':' [<ws>] '\n'
+my $section_re = qr/ ^ \s* (\S+) \s+ (\S+) \s+ (\S+) \s* : \s* $ /x;
+
+# [<ws>] <enum_element_name> [<ws>] '\n'
+my $enum_element_re = qr/ ^ \s* ( [A-Za-z0-9_]+ ) \s* $ /x;
+
+# '#' <preprocessor directive> '\n'
+my $preprocessor_re = qr/ ^ \# /x;
+
+# [<ws>] '/' '/' <comment> '\n'
+my $comment_re = qr| ^ \s* // |x;
+
+# empty line
+my $whitespace_only_re = qr/ ^ \s* $ /x;
+
+# [<ws>] '"' <string> '"' [<ws>] '\n'  (<string> doesn't contain newlines)
+my $string_re = qr/ ^ \s*
+                       " ( (?> (?: (?> [^"\\]+ ) | \\. )* ) ) "
+                       \s* $ /x;
+
+# [<ws>] '{' <block> '}' [<ws>] '\n'  (<block> can contain newlines)
+my $block_start_re = qr/ ^ \s* \{ /x;
+
+# We need the capturing group for `split' to make it return the separator
+# tokens (i.e., the opening and closing brace) also.
+my $brace_re = qr/ ( [{}] ) /x;
+
+
+sub Warn
+{
+  my $message = shift;
+  warn "$datafile:$INPUT_LINE_NUMBER: warning: $message\n";
+}
+
+
+sub Die
+{
+  my $message = shift;
+  die "$datafile:$INPUT_LINE_NUMBER: error: $message\n";
+}
+
+
+my $warned_before = 0;
+
+sub warn_before
+{
+  Warn("data before first section gets ignored") unless $warned_before;
+  $warned_before = 1;
+}
+
+
+sub strip_newline
+{
+  chomp;
+  s/ \x0D $ //x;
+}
+
+
+sub end_curr_string
+{
+  # Append final null byte to string.
+  if ($have_strings)
+  {
+    push @{$diversions{$curr_array}}, "    '\\0',\n" if $in_string;
+
+    $curr_offset++;
+    $in_string = 0;
+  }
+}
+
+
+sub update_max_elem_size
+{
+  if ($curr_elem_size)
+  {
+    my $max = pop @{$diversions{$curr_max}};
+    $max = $curr_elem_size if $curr_elem_size > $max;
+    push @{$diversions{$curr_max}}, $max;
+  }
+}
+
+
+sub convert_non_ascii_char
+{
+  # A UTF-8 character outside of the printable ASCII range, with possibly a
+  # leading backslash character.
+  my $s = shift;
+
+  # Here we count characters, not bytes.
+  $curr_elem_size += length $s;
+
+  utf8::encode($s);
+  $s = uc unpack 'H*', $s;
+
+  $curr_offset += $s =~ s/\G(..)/'\\x$1', /sg;
+
+  return $s;
+}
+
+
+sub convert_ascii_chars
+{
+  # A series of ASCII characters in the printable range.
+  my $s = shift;
+
+  # We reduce multiple space characters to a single one.
+  $s =~ s/ +/ /g;
+
+  # Count all non-space characters.  Note that `()' applies a list context
+  # to the capture that is used to count the elements.
+  $curr_elem_size += () = $s =~ /[^ ]/g;
+
+  $curr_offset += $s =~ s/\G(.)/'$1', /g;
+
+  return $s;
+}
+
+
+sub convert_literal
+{
+  my $s = shift;
+  my $orig = $s;
+
+  # ASCII printables and space
+  my $safe_re = '\x20-\x7E';
+  # ASCII printables and space, no backslash
+  my $safe_no_backslash_re = '\x20-\x5B\x5D-\x7E';
+
+  $s =~ s{
+           (?: \\? ( [^$safe_re] )
+               | ( (?: [$safe_no_backslash_re]
+                       | \\ [$safe_re] )+ ) )
+         }
+         {
+           defined($1) ? convert_non_ascii_char($1)
+                       : convert_ascii_chars($2)
+         }egx;
+
+   # We assume that `$orig' doesn't contain `*/'
+   return $s . " /* $orig */";
+}
+
+
+sub aux_name
+{
+  return "af_blue_" . $num_sections. "_" . join('_', @name_stack);
+}
+
+
+sub aux_name_next
+{
+  $name_stack[$#name_stack]++;
+  my $name = aux_name();
+  $name_stack[$#name_stack]--;
+
+  return $name;
+}
+
+
+sub enum_val_string
+{
+  # Build string that holds code to save the current offset in an
+  # enumeration element.
+  my $aux = shift;
+
+  my $add = ($last_aux eq "af_blue_" . $num_sections . "_0" )
+              ? ""
+              : "$last_aux + ";
+
+  return "    $aux = $add$curr_offset,\n";
+}
+
+
+
+# Process data file.
+
+open(DATA, $datafile) || die "$prog: can't open \`$datafile': $OS_ERROR\n";
+
+while (<DATA>)
+{
+  strip_newline();
+
+  next if /$comment_re/;
+  next if /$whitespace_only_re/;
+
+  if (/$section_re/)
+  {
+    Warn("previous section is empty") if ($have_sections
+                                          && !$have_strings
+                                          && !$have_blocks);
+
+    end_curr_string();
+    update_max_elem_size();
+
+    # Save captured groups from `section_re'.
+    $curr_enum = $1;
+    $curr_array = $2;
+    $curr_max = $3;
+
+    $curr_enum_element = "";
+    $curr_offset = 0;
+
+    Warn("overwriting already defined enumeration \`$curr_enum'")
+      if exists($diversions{$curr_enum});
+    Warn("overwriting already defined array \`$curr_array'")
+      if exists($diversions{$curr_array});
+    Warn("overwriting already defined maximum value \`$curr_max'")
+      if exists($diversions{$curr_max});
+
+    $diversions{$curr_enum} = [];
+    $diversions{$curr_array} = [];
+    $diversions{$curr_max} = [];
+
+    push @{$diversions{$curr_max}}, 0;
+
+    @name_stack = ();
+    push @name_stack, 0;
+
+    $have_sections = 1;
+    $have_strings = 0;
+    $have_blocks = 0;
+
+    $have_enum_element = 0;
+    $in_string = 0;
+
+    $num_sections++;
+    $curr_elem_size = 0;
+
+    $last_aux = aux_name();
+
+    next;
+  }
+
+  if (/$preprocessor_re/)
+  {
+    if ($have_sections)
+    {
+      # Having preprocessor conditionals complicates the computation of
+      # correct offset values.  We have to introduce auxiliary enumeration
+      # elements with the name `af_blue_<s>_<n1>_<n2>_...' that store
+      # offsets to be used in conditional clauses.  `<s>' is the number of
+      # sections seen so far, `<n1>' is the number of `#if' and `#endif'
+      # conditionals seen so far in the topmost level, `<n2>' the number of
+      # `#if' and `#endif' conditionals seen so far one level deeper, etc.
+      # As a consequence, uneven values are used within a clause, and even
+      # values after a clause, since the C standard doesn't allow the
+      # redefinition of an enumeration value.  For example, the name
+      # `af_blue_5_1_6' is used to construct enumeration values in the fifth
+      # section after the third (second-level) if-clause within the first
+      # (top-level) if-clause.  After the first top-level clause has
+      # finished, `af_blue_5_2' is used.  The current offset is then
+      # relative to the value stored in the current auxiliary element.
+
+      if (/ ^ \# \s* if /x)
+      {
+        push @else_stack, 0;
+
+        $name_stack[$#name_stack]++;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name());
+        $last_aux = aux_name();
+
+        push @name_stack, 0;
+
+        $curr_offset = 0;
+      }
+      elsif (/ ^ \# \s* elif /x)
+      {
+        Die("unbalanced #elif") unless @else_stack;
+
+        pop @name_stack;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next());
+        $last_aux = aux_name();
+
+        push @name_stack, 0;
+
+        $curr_offset = 0;
+      }
+      elsif (/ ^ \# \s* else /x)
+      {
+        my $prev_else = pop @else_stack;
+        Die("unbalanced #else") unless defined($prev_else);
+        Die("#else already seen") if $prev_else;
+        push @else_stack, 1;
+
+        pop @name_stack;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next());
+        $last_aux = aux_name();
+
+        push @name_stack, 0;
+
+        $curr_offset = 0;
+      }
+      elsif (/ ^ (\# \s*) endif /x)
+      {
+        my $prev_else = pop @else_stack;
+        Die("unbalanced #endif") unless defined($prev_else);
+
+        pop @name_stack;
+
+        # If there is no else-clause for an if-clause, we add one.  This is
+        # necessary to have correct offsets.
+        if (!$prev_else)
+        {
+          # Use amount of whitespace from `endif'.
+          push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next())
+                                           . $1 . "else\n";
+          $last_aux = aux_name();
+
+          $curr_offset = 0;
+        }
+
+        $name_stack[$#name_stack]++;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name());
+        $last_aux = aux_name();
+
+        $curr_offset = 0;
+      }
+
+      # Handle (probably continued) preprocessor lines.
+    CONTINUED_LOOP:
+      {
+        do
+        {
+          strip_newline();
+
+          push @{$diversions{$curr_enum}}, $ARG . "\n";
+          push @{$diversions{$curr_array}}, $ARG . "\n";
+
+          last CONTINUED_LOOP unless / \\ $ /x;
+
+        } while (<DATA>);
+      }
+    }
+    else
+    {
+      warn_before();
+    }
+
+    next;
+  }
+
+  if (/$enum_element_re/)
+  {
+    end_curr_string();
+    update_max_elem_size();
+
+    $curr_enum_element = $1;
+    $have_enum_element = 1;
+    $curr_elem_size = 0;
+
+    next;
+  }
+
+  if (/$string_re/)
+  {
+    if ($have_sections)
+    {
+      Die("strings and blocks can't be mixed in a section") if $have_blocks;
+
+      # Save captured group from `string_re'.
+      my $string = $1;
+
+      if ($have_enum_element)
+      {
+        push @{$diversions{$curr_enum}}, enum_val_string($curr_enum_element);
+        $have_enum_element = 0;
+      }
+
+      $string = convert_literal($string);
+
+      push @{$diversions{$curr_array}}, "    $string\n";
+
+      $have_strings = 1;
+      $in_string = 1;
+    }
+    else
+    {
+      warn_before();
+    }
+
+    next;
+  }
+
+  if (/$block_start_re/)
+  {
+    if ($have_sections)
+    {
+      Die("strings and blocks can't be mixed in a section") if $have_strings;
+
+      my $depth = 0;
+      my $block = "";
+      my $block_end = 0;
+
+      # Count braces while getting the block.
+    BRACE_LOOP:
+      {
+        do
+        {
+          strip_newline();
+
+          foreach my $substring (split(/$brace_re/))
+          {
+            if ($block_end)
+            {
+              Die("invalid data after last matching closing brace")
+                if $substring !~ /$whitespace_only_re/;
+            }
+
+            $block .= $substring;
+
+            if ($substring eq '{')
+            {
+              $depth++;
+            }
+            elsif ($substring eq '}')
+            {
+              $depth--;
+
+              $block_end = 1 if $depth == 0;
+            }
+          }
+
+          # If we are here, we have run out of substrings, so get next line
+          # or exit.
+          last BRACE_LOOP if $block_end;
+
+          $block .= "\n";
+
+        } while (<DATA>);
+      }
+
+      if ($have_enum_element)
+      {
+        push @{$diversions{$curr_enum}}, enum_val_string($curr_enum_element);
+        $have_enum_element = 0;
+      }
+
+      push @{$diversions{$curr_array}}, $block . ",\n";
+
+      $curr_offset++;
+      $curr_elem_size++;
+
+      $have_blocks = 1;
+    }
+    else
+    {
+      warn_before();
+    }
+
+    next;
+  }
+
+  # Garbage.  We weren't able to parse the data.
+  Die("syntax error");
+}
+
+# Finalize data.
+end_curr_string();
+update_max_elem_size();
+
+
+# Filter stdin to stdout, replacing `@...@' templates.
+
+sub emit_diversion
+{
+  my $diversion_name = shift;
+  return (exists($diversions{$1})) ? "@{$diversions{$1}}"
+                                   : "@" . $diversion_name . "@";
+}
+
+
+$LIST_SEPARATOR = '';
+
+my $s1 = "This file has been generated by the Perl script \`$prog',";
+my $s1len = length $s1;
+my $s2 = "using data from file \`$datafile'.";
+my $s2len = length $s2;
+my $slen = ($s1len > $s2len) ? $s1len : $s2len;
+
+print "/* " . $s1 . " " x ($slen - $s1len) . " */\n"
+      . "/* " . $s2 . " " x ($slen - $s2len) . " */\n"
+      . "\n";
+
+while (<STDIN>)
+{
+  s/ @ ( [A-Za-z0-9_]+? ) @ / emit_diversion($1) /egx;
+  print;
+}
+
+# EOF
diff --git a/src/third_party/freetype2/src/tools/apinames.c b/src/third_party/freetype2/src/tools/apinames.c
new file mode 100644
index 0000000..aeecf88
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/apinames.c
@@ -0,0 +1,514 @@
+/*
+ * This little program is used to parse the FreeType headers and
+ * find the declaration of all public APIs.  This is easy, because
+ * they all look like the following:
+ *
+ *   FT_EXPORT( return_type )
+ *   function_name( function arguments );
+ *
+ * You must pass the list of header files as arguments.  Wildcards are
+ * accepted if you are using GCC for compilation (and probably by
+ * other compilers too).
+ *
+ * Author: FreeType team, 2005-2019
+ *
+ * This code is explicitly placed into the public domain.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define  PROGRAM_NAME     "apinames"
+#define  PROGRAM_VERSION  "0.3"
+
+#define  LINEBUFF_SIZE  1024
+
+
+typedef enum  OutputFormat_
+{
+  OUTPUT_LIST = 0,      /* output the list of names, one per line             */
+  OUTPUT_WINDOWS_DEF,   /* output a Windows .DEF file for Visual C++ or Mingw */
+  OUTPUT_BORLAND_DEF,   /* output a Windows .DEF file for Borland C++         */
+  OUTPUT_WATCOM_LBC,    /* output a Watcom Linker Command File                */
+  OUTPUT_NETWARE_IMP,   /* output a NetWare ImportFile                        */
+  OUTPUT_GNU_VERMAP     /* output a version map for GNU or Solaris linker     */
+
+} OutputFormat;
+
+
+static void
+panic( const char*  message )
+{
+  fprintf( stderr, "PANIC: %s\n", message );
+  exit(2);
+}
+
+
+typedef struct  NameRec_
+{
+  char*         name;
+  unsigned int  hash;
+
+} NameRec, *Name;
+
+
+static Name  the_names;
+static int   num_names;
+static int   max_names;
+
+
+static void
+names_add( const char*  name,
+           const char*  end )
+{
+  unsigned int  h;
+  int           nn, len;
+  Name          nm;
+
+
+  if ( end <= name )
+    return;
+
+  /* compute hash value */
+  len = (int)( end - name );
+  h   = 0;
+
+  for ( nn = 0; nn < len; nn++ )
+    h = h * 33 + name[nn];
+
+  /* check for an pre-existing name */
+  for ( nn = 0; nn < num_names; nn++ )
+  {
+    nm = the_names + nn;
+
+    if ( (int)nm->hash                 == h &&
+         memcmp( name, nm->name, len ) == 0 &&
+         nm->name[len]                 == 0 )
+      return;
+  }
+
+  /* add new name */
+  if ( num_names >= max_names )
+  {
+    max_names += ( max_names >> 1 ) + 4;
+    the_names  = (NameRec*)realloc( the_names,
+                                    sizeof ( the_names[0] ) * max_names );
+    if ( !the_names )
+      panic( "not enough memory" );
+  }
+  nm = &the_names[num_names++];
+
+  nm->hash = h;
+  nm->name = (char*)malloc( len + 1 );
+  if ( !nm->name )
+    panic( "not enough memory" );
+
+  memcpy( nm->name, name, len );
+  nm->name[len] = 0;
+}
+
+
+static int
+name_compare( const void*  name1,
+              const void*  name2 )
+{
+  Name  n1 = (Name)name1;
+  Name  n2 = (Name)name2;
+
+  return strcmp( n1->name, n2->name );
+}
+
+
+static void
+names_sort( void )
+{
+  qsort( the_names, (size_t)num_names,
+         sizeof ( the_names[0] ), name_compare );
+}
+
+
+static void
+names_dump( FILE*         out,
+            OutputFormat  format,
+            const char*   dll_name )
+{
+  int  nn;
+
+
+  switch ( format )
+  {
+  case OUTPUT_WINDOWS_DEF:
+    if ( dll_name )
+      fprintf( out, "LIBRARY %s\n", dll_name );
+
+    fprintf( out, "DESCRIPTION  FreeType 2 DLL\n" );
+    fprintf( out, "EXPORTS\n" );
+
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "  %s\n", the_names[nn].name );
+
+    break;
+
+  case OUTPUT_BORLAND_DEF:
+    if ( dll_name )
+      fprintf( out, "LIBRARY %s\n", dll_name );
+
+    fprintf( out, "DESCRIPTION  FreeType 2 DLL\n" );
+    fprintf( out, "EXPORTS\n" );
+
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "  _%s\n", the_names[nn].name );
+
+    break;
+
+  case OUTPUT_WATCOM_LBC:
+    {
+      const char*  dot;
+      char         temp[512];
+
+
+      if ( !dll_name )
+      {
+        fprintf( stderr,
+                 "you must provide a DLL name with the -d option!\n" );
+        exit( 4 );
+      }
+
+      /* we must omit the `.dll' suffix from the library name */
+      dot = strchr( dll_name, '.' );
+      if ( dot )
+      {
+        int  len = dot - dll_name;
+
+
+        if ( len > (int)( sizeof ( temp ) - 1 ) )
+          len = sizeof ( temp ) - 1;
+
+        memcpy( temp, dll_name, len );
+        temp[len] = 0;
+
+        dll_name = (const char*)temp;
+      }
+
+      for ( nn = 0; nn < num_names; nn++ )
+        fprintf( out, "++_%s.%s.%s\n",
+                      the_names[nn].name, dll_name, the_names[nn].name );
+    }
+
+    break;
+
+  case OUTPUT_NETWARE_IMP:
+    if ( dll_name )
+      fprintf( out, "  (%s)\n", dll_name );
+
+    for ( nn = 0; nn < num_names - 1; nn++ )
+      fprintf( out, "  %s,\n", the_names[nn].name );
+    fprintf( out, "  %s\n", the_names[num_names - 1].name );
+
+    break;
+
+  case OUTPUT_GNU_VERMAP:
+    fprintf( out, "{\n\tglobal:\n" );
+
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "\t\t%s;\n", the_names[nn].name );
+
+    fprintf( out, "\tlocal:\n\t\t*;\n};\n" );
+
+    break;
+
+  default:  /* LIST */
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "%s\n", the_names[nn].name );
+
+    break;
+  }
+}
+
+
+/* states of the line parser */
+
+typedef enum  State_
+{
+  STATE_START = 0,  /* waiting for FT_EXPORT keyword and return type */
+  STATE_TYPE        /* type was read, waiting for function name      */
+
+} State;
+
+
+static int
+read_header_file( FILE*  file,
+                  int    verbose )
+{
+  static char  buff[LINEBUFF_SIZE + 1];
+  State        state = STATE_START;
+
+
+  while ( !feof( file ) )
+  {
+    char*  p;
+
+
+    if ( !fgets( buff, LINEBUFF_SIZE, file ) )
+      break;
+
+    p = buff;
+
+    /* skip leading whitespace */
+    while ( *p && ( *p == ' ' || *p == '\\' ) )
+      p++;
+
+    /* skip empty lines */
+    if ( *p == '\n' || *p == '\r' )
+      continue;
+
+    switch ( state )
+    {
+    case STATE_START:
+      if ( memcmp( p, "FT_EXPORT(", 10 ) != 0 )
+        break;
+
+      p += 10;
+      for (;;)
+      {
+        if ( *p == 0 || *p == '\n' || *p == '\r' )
+          goto NextLine;
+
+        if ( *p == ')' )
+        {
+          p++;
+          break;
+        }
+
+        p++;
+      }
+
+      state = STATE_TYPE;
+
+      /*
+       * Sometimes, the name is just after `FT_EXPORT(...)', so skip
+       * whitespace and fall-through if we find an alphanumeric character.
+       */
+      while ( *p == ' ' || *p == '\t' )
+        p++;
+
+      if ( !isalpha( *p ) )
+        break;
+
+      /* fall-through */
+
+    case STATE_TYPE:
+      {
+        char*   name = p;
+
+
+        while ( isalnum( *p ) || *p == '_' )
+          p++;
+
+        if ( p > name )
+        {
+          if ( verbose )
+            fprintf( stderr, ">>> %.*s\n", (int)( p - name ), name );
+
+          names_add( name, p );
+        }
+
+        state = STATE_START;
+      }
+
+      break;
+
+    default:
+      ;
+    }
+
+NextLine:
+    ;
+  } /* end of while loop */
+
+  return 0;
+}
+
+
+static void
+usage( void )
+{
+  static const char* const  format =
+    "%s %s: extract FreeType API names from header files\n"
+    "\n"
+    "This program extracts the list of public FreeType API functions.\n"
+    "It receives a list of header files as an argument and\n"
+    "generates a sorted list of unique identifiers in various formats.\n"
+    "\n"
+    "usage: %s header1 [options] [header2 ...]\n"
+    "\n"
+    "options:   -       parse the contents of stdin, ignore arguments\n"
+    "           -v      verbose mode, output sent to standard error\n"
+    "           -oFILE  write output to FILE instead of standard output\n"
+    "           -dNAME  indicate DLL file name, 'freetype.dll' by default\n"
+    "           -w      output .DEF file for Visual C++ and Mingw\n"
+    "           -wB     output .DEF file for Borland C++\n"
+    "           -wW     output Watcom Linker Response File\n"
+    "           -wN     output NetWare Import File\n"
+    "           -wL     output version map for GNU or Solaris linker\n"
+    "\n";
+
+  fprintf( stderr,
+           format,
+           PROGRAM_NAME,
+           PROGRAM_VERSION,
+           PROGRAM_NAME );
+
+  exit( 1 );
+}
+
+
+int
+main( int                 argc,
+      const char* const*  argv )
+{
+  int           from_stdin   = 0;
+  int           verbose      = 0;
+  OutputFormat  format       = OUTPUT_LIST;  /* the default */
+  FILE*         out          = stdout;
+  const char*   library_name = NULL;
+
+
+  if ( argc < 2 )
+    usage();
+
+  /* `-' used as a single argument means read source file from stdin */
+  while ( argc > 1 && argv[1][0] == '-' )
+  {
+    const char*  arg = argv[1];
+
+
+    switch ( arg[1] )
+    {
+    case 'v':
+      verbose = 1;
+
+      break;
+
+    case 'o':
+      if ( arg[2] == 0 )
+      {
+        if ( argc < 2 )
+          usage();
+
+        arg = argv[2];
+        argv++;
+        argc--;
+      }
+      else
+        arg += 2;
+
+      out = fopen( arg, "wt" );
+      if ( !out )
+      {
+        fprintf( stderr, "could not open '%s' for writing\n", arg );
+        exit( 3 );
+      }
+
+      break;
+
+    case 'd':
+      if ( arg[2] == 0 )
+      {
+        if ( argc < 2 )
+          usage();
+
+        arg = argv[2];
+        argv++;
+        argc--;
+      }
+      else
+        arg += 2;
+
+      library_name = arg;
+
+      break;
+
+    case 'w':
+      format = OUTPUT_WINDOWS_DEF;
+
+      switch ( arg[2] )
+      {
+      case 'B':
+        format = OUTPUT_BORLAND_DEF;
+        break;
+
+      case 'W':
+        format = OUTPUT_WATCOM_LBC;
+        break;
+
+      case 'N':
+        format = OUTPUT_NETWARE_IMP;
+        break;
+
+      case 'L':
+        format = OUTPUT_GNU_VERMAP;
+        break;
+
+      case 0:
+        break;
+
+      default:
+        usage();
+      }
+
+      break;
+
+    case 0:
+      from_stdin = 1;
+
+      break;
+
+    default:
+      usage();
+    }
+
+    argc--;
+    argv++;
+
+  } /* end of while loop */
+
+  if ( from_stdin )
+    read_header_file( stdin, verbose );
+  else
+  {
+    for ( --argc, argv++; argc > 0; argc--, argv++ )
+    {
+      FILE*  file = fopen( argv[0], "rb" );
+
+
+      if ( !file )
+        fprintf( stderr, "unable to open '%s'\n", argv[0] );
+      else
+      {
+        if ( verbose )
+          fprintf( stderr, "opening '%s'\n", argv[0] );
+
+        read_header_file( file, verbose );
+        fclose( file );
+      }
+    }
+  }
+
+  if ( num_names == 0 )
+    panic( "could not find exported functions\n" );
+
+  names_sort();
+  names_dump( out, format, library_name );
+
+  if ( out != stdout )
+    fclose( out );
+
+  return 0;
+}
+
+
+/* END */
diff --git a/src/third_party/freetype2/src/tools/ftrandom/Makefile b/src/third_party/freetype2/src/tools/ftrandom/Makefile
new file mode 100644
index 0000000..24dc49c
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/ftrandom/Makefile
@@ -0,0 +1,45 @@
+# TOP_DIR and OBJ_DIR should be set by the user to the right directories,
+# if necessary.
+
+TOP_DIR ?= ../../..
+OBJ_DIR ?= $(TOP_DIR)/objs
+
+
+# The setup below is for gcc on a Unix-like platform,
+# where FreeType has been set up to create a static library
+# (which is the default).
+
+VPATH = $(OBJ_DIR) \
+        $(OBJ_DIR)/.libs
+
+SRC_DIR = $(TOP_DIR)/src/tools/ftrandom
+
+CC = gcc
+WFLAGS = -Wmissing-prototypes \
+         -Wunused \
+         -Wimplicit \
+         -Wreturn-type \
+         -Wparentheses \
+         -pedantic \
+         -Wformat \
+         -Wchar-subscripts \
+         -Wsequence-point
+CFLAGS = $(WFLAGS) \
+         -g
+INCLUDES = -I $(TOP_DIR)/include
+LDFLAGS =
+LIBS = -lm \
+       -lz \
+       -lpng \
+       -lbz2 \
+       -lharfbuzz
+
+all: $(OBJ_DIR)/ftrandom
+
+$(OBJ_DIR)/ftrandom.o: $(SRC_DIR)/ftrandom.c
+	$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+
+$(OBJ_DIR)/ftrandom: $(OBJ_DIR)/ftrandom.o libfreetype.a
+	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+# EOF
diff --git a/src/third_party/freetype2/src/tools/ftrandom/README b/src/third_party/freetype2/src/tools/ftrandom/README
new file mode 100644
index 0000000..7c61086
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/ftrandom/README
@@ -0,0 +1,69 @@
+ftrandom
+========
+
+This program expects a set of directories containing good fonts, and a set
+of extensions of fonts to be tested.  It will randomly pick a font, copy it,
+introduce an error and then test it.
+
+The FreeType tests are quite basic; for each erroneous font ftrandom
+
+  . forks off a new tester,
+  . initializes the library,
+  . opens each font in the file,
+  . loads each glyph,
+  . optionally reviews the contours of the glyph,
+  . optionally rasterizes the glyph, and
+  . closes the face.
+
+If a tester takes longer than 20 seconds, ftrandom saves the erroneous font
+and continues.  If the tester exits normally or with an error, then the
+superstructure removes the test font and continues.
+
+
+Command line options
+--------------------
+
+  --all                    Test every font in the directory(ies) no matter
+                           what its extension.
+  --check-outlines         Call `FT_Outline_Decompose' on each glyph.
+  --dir <dir>              Append <dir> to the list of directories to search
+                           for good fonts.  No recursive search.
+  --error-count <cnt>      Introduce <cnt> single-byte errors into the
+                           erroneous fonts (default: 1).
+  --error-fraction <frac>  Multiply the file size of the font by <frac> and
+                           introduce that many errors into the erroneous
+                           font file.  <frac> should be in the range [0;1]
+                           (default: 0.0).
+  --ext <ext>              Add <ext> to the set of font types tested.
+  --help                   Print out this list of options.
+  --nohints                Specify FT_LOAD_NO_HINTING when loading glyphs.
+  --rasterize              Call `FT_Render_Glyph' as well as loading it.
+  --result <dir>           This is the directory in which test files are
+                           placed.
+  --test <file>            Run a single test on a pre-generated testcase.
+                           This is done in the current process so it can be
+                           debugged more easily.
+
+The default font extensions tested by ftrandom are
+
+  .ttf .otf .ttc .cid .pfb .pfa .bdf .pcf .pfr .fon .otb .cff
+
+The default font directory is controlled by the macro `GOOD_FONTS_DIR' in
+the source code (and can be thus specified during compilation); its default
+value is
+
+  /usr/local/share/fonts
+
+The default result directory is `results' (in the current directory).
+
+
+Compilation
+-----------
+
+Two possible solutions.
+
+. Run ftrandom within a debugging tool like `valgrind' to catch various
+  memory issues.
+
+. Compile FreeType with sanitizer flags as provided by gcc or clang, for
+  example, then link it with ftrandom.
diff --git a/src/third_party/freetype2/src/tools/ftrandom/ftrandom.c b/src/third_party/freetype2/src/tools/ftrandom/ftrandom.c
new file mode 100644
index 0000000..ab5cfc9
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/ftrandom/ftrandom.c
@@ -0,0 +1,720 @@
+/* Copyright (C) 2005, 2007, 2008, 2013 by George Williams */
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* modified by Werner Lemberg <wl@gnu.org>       */
+/* This file is now part of the FreeType library */
+
+
+#define _XOPEN_SOURCE 500 /* for `kill', `strdup', `random', and `srandom' */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <signal.h>
+#include <time.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+
+#define true     1
+#define false    0
+#define forever  for (;;)
+
+
+  static int    check_outlines = false;
+  static int    nohints        = false;
+  static int    rasterize      = false;
+  static char*  results_dir    = "results";
+
+#define GOOD_FONTS_DIR  "/usr/local/share/fonts"
+
+  static char*  default_dir_list[] =
+  {
+    GOOD_FONTS_DIR,
+    NULL
+  };
+
+  static char*  default_ext_list[] =
+  {
+    "ttf",
+    "otf",
+    "ttc",
+    "cid",
+    "pfb",
+    "pfa",
+    "bdf",
+    "pcf",
+    "pfr",
+    "fon",
+    "otb",
+    "cff",
+    NULL
+  };
+
+  static unsigned int  error_count    = 1;
+  static double        error_fraction = 0.0;
+
+  static FT_F26Dot6  font_size = 12 * 64;
+
+  static struct fontlist
+  {
+    char*         name;
+    long          len;
+    unsigned int  isbinary: 1;
+    unsigned int  isascii: 1;
+    unsigned int  ishex: 1;
+
+  } *fontlist;
+
+  static unsigned int  fcnt;
+
+
+  static int
+  FT_MoveTo( const FT_Vector  *to,
+             void             *user )
+  {
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static int
+  FT_LineTo( const FT_Vector  *to,
+             void             *user )
+  {
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static int
+  FT_ConicTo( const FT_Vector  *_cp,
+              const FT_Vector  *to,
+              void             *user )
+  {
+    FT_UNUSED( _cp );
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static int
+  FT_CubicTo( const FT_Vector  *cp1,
+              const FT_Vector  *cp2,
+              const FT_Vector  *to,
+              void             *user )
+  {
+    FT_UNUSED( cp1 );
+    FT_UNUSED( cp2 );
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static FT_Outline_Funcs outlinefuncs =
+  {
+    FT_MoveTo,
+    FT_LineTo,
+    FT_ConicTo,
+    FT_CubicTo,
+    0, 0          /* No shift, no delta */
+  };
+
+
+  static void
+  TestFace( FT_Face  face )
+  {
+    unsigned int  gid;
+    int           load_flags = FT_LOAD_DEFAULT;
+
+
+    if ( check_outlines         &&
+         FT_IS_SCALABLE( face ) )
+      load_flags = FT_LOAD_NO_BITMAP;
+
+    if ( nohints )
+      load_flags |= FT_LOAD_NO_HINTING;
+
+    FT_Set_Char_Size( face, 0, font_size, 72, 72 );
+
+    for ( gid = 0; gid < face->num_glyphs; gid++ )
+    {
+      if ( check_outlines         &&
+           FT_IS_SCALABLE( face ) )
+      {
+        if ( !FT_Load_Glyph( face, gid, load_flags ) )
+          FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL );
+      }
+      else
+        FT_Load_Glyph( face, gid, load_flags );
+
+      if ( rasterize )
+        FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+    }
+
+    FT_Done_Face( face );
+  }
+
+
+  static void
+  ExecuteTest( char*  testfont )
+  {
+    FT_Library  context;
+    FT_Face     face;
+
+
+    if ( FT_Init_FreeType( &context ) )
+    {
+      fprintf( stderr, "Can't initialize FreeType.\n" );
+      exit( 1 );
+    }
+
+    if ( FT_New_Face( context, testfont, 0, &face ) )
+    {
+      /* The font is erroneous, so if this fails that's ok. */
+      exit( 0 );
+    }
+
+    if ( face->num_faces == 1 )
+      TestFace( face );
+    else
+    {
+      long  i, num;
+
+
+      num = face->num_faces;
+      FT_Done_Face( face );
+
+      for ( i = 0; i < num; i++ )
+      {
+        if ( !FT_New_Face( context, testfont, i, &face ) )
+          TestFace( face );
+      }
+    }
+
+    FT_Done_FreeType( context );
+
+    exit( 0 );
+  }
+
+
+  static int
+  extmatch( char*   filename,
+            char**  extensions )
+  {
+    int    i;
+    char*  pt;
+
+
+    if ( !extensions )
+      return true;
+
+    pt = strrchr( filename, '.' );
+    if ( !pt )
+      return false;
+    if ( pt < strrchr( filename, '/' ) )
+      return false;
+
+    for ( i = 0; extensions[i] != NULL; i++ )
+      if ( strcasecmp( pt + 1, extensions[i] ) == 0 ||
+           strcasecmp( pt,     extensions[i] ) == 0 )
+        return true;
+
+    return false;
+  }
+
+
+  static void
+  figurefiletype( struct fontlist*  item )
+  {
+    FILE*  foo;
+
+
+    item->isbinary = item->isascii = item->ishex = false;
+
+    foo = fopen( item->name, "rb" );
+    if ( foo )
+    {
+      /* Try to guess the file type from the first few characters... */
+      int  ch1 = getc( foo );
+      int  ch2 = getc( foo );
+      int  ch3 = getc( foo );
+      int  ch4 = getc( foo );
+
+
+      fclose( foo );
+
+      if ( ( ch1 == 0   && ch2 == 1   && ch3 == 0   && ch4 == 0   ) ||
+           ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) ||
+           ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) ||
+           ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) )
+      {
+        /* ttf, otf, ttc files */
+        item->isbinary = true;
+      }
+      else if ( ch1 == 0x80 && ch2 == '\01' )
+      {
+        /* PFB header */
+        item->isbinary = true;
+      }
+      else if ( ch1 == '%' && ch2 == '!' )
+      {
+        /* Random PostScript */
+        if ( strstr( item->name, ".pfa" ) ||
+             strstr( item->name, ".PFA" ) )
+          item->ishex = true;
+        else
+          item->isascii = true;
+      }
+      else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 )
+      {
+        /* Bare CFF */
+        item->isbinary = true;
+      }
+      else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' )
+      {
+        /* BDF */
+        item->ishex = true;
+      }
+      else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' )
+      {
+        /* PFR */
+        item->isbinary = true;
+      }
+      else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) ||
+                ( ch1 == 'M'  && ch2 == 'Z' )                             )
+      {
+        /* Windows FON */
+        item->isbinary = true;
+      }
+      else
+      {
+        fprintf( stderr,
+                 "Can't recognize file type of `%s', assuming binary\n",
+                 item->name );
+        item->isbinary = true;
+      }
+    }
+    else
+    {
+      fprintf( stderr, "Can't open `%s' for typing the file.\n",
+               item->name );
+      item->isbinary = true;
+    }
+  }
+
+
+  static void
+  FindFonts( char**  fontdirs,
+             char**  extensions )
+  {
+    int           i;
+    unsigned int  max;
+    char          buffer[1025];
+    struct stat   statb;
+
+
+    max  = 0;
+    fcnt = 0;
+
+    for ( i = 0; fontdirs[i] != NULL; i++ )
+    {
+      DIR*            examples;
+      struct dirent*  ent;
+
+
+      examples = opendir( fontdirs[i] );
+      if ( !examples )
+      {
+        fprintf( stderr,
+                 "Can't open example font directory `%s'\n",
+                 fontdirs[i] );
+        exit( 1 );
+      }
+
+      while ( ( ent = readdir( examples ) ) != NULL )
+      {
+        snprintf( buffer, sizeof ( buffer ),
+                  "%s/%s", fontdirs[i], ent->d_name );
+        if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) )
+          continue;
+        if ( !extensions || extmatch( buffer, extensions ) )
+        {
+          if ( fcnt >= max )
+          {
+            max += 100;
+            fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) );
+            if ( !fontlist )
+            {
+              fprintf( stderr, "Can't allocate memory\n" );
+              exit( 1 );
+            }
+          }
+
+          fontlist[fcnt].name = strdup( buffer );
+          fontlist[fcnt].len  = statb.st_size;
+
+          figurefiletype( &fontlist[fcnt] );
+          fcnt++;
+        }
+      }
+
+      closedir( examples );
+    }
+
+    if ( fcnt == 0 )
+    {
+      fprintf( stderr, "Can't find matching font files.\n" );
+      exit( 1 );
+    }
+
+    fontlist[fcnt].name = NULL;
+  }
+
+
+  static unsigned int
+  getErrorCnt( struct fontlist*  item )
+  {
+    if ( error_count == 0 && error_fraction == 0.0 )
+      return 0;
+
+    return error_count + (unsigned int)( error_fraction * item->len );
+  }
+
+
+  static int
+  getRandom( int  low,
+             int  high )
+  {
+    if ( low - high < 0x10000L )
+      return low + ( ( random() >> 8 ) % ( high + 1 - low ) );
+
+    return low + ( random() % ( high + 1 - low ) );
+  }
+
+
+  static int
+  copyfont( struct fontlist*  item,
+            char*             newfont )
+  {
+    static char   buffer[8096];
+    FILE          *good, *newf;
+    size_t        len;
+    unsigned int  i, err_cnt;
+
+
+    good = fopen( item->name, "r" );
+    if ( !good )
+    {
+      fprintf( stderr, "Can't open `%s'\n", item->name );
+      return false;
+    }
+
+    newf = fopen( newfont, "w+" );
+    if ( !newf )
+    {
+      fprintf( stderr, "Can't create temporary output file `%s'\n",
+               newfont );
+      exit( 1 );
+    }
+
+    while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 )
+      fwrite( buffer, 1, len, newf );
+
+    fclose( good );
+
+    err_cnt = getErrorCnt( item );
+    for ( i = 0; i < err_cnt; i++ )
+    {
+      fseek( newf, getRandom( 0, (int)( item->len - 1 ) ), SEEK_SET );
+
+      if ( item->isbinary )
+        putc( getRandom( 0, 0xFF ), newf );
+      else if ( item->isascii )
+        putc( getRandom( 0x20, 0x7E ), newf );
+      else
+      {
+        int  hex = getRandom( 0, 15 );
+
+
+        if ( hex < 10 )
+          hex += '0';
+        else
+          hex += 'A' - 10;
+
+        putc( hex, newf );
+      }
+    }
+
+    if ( ferror( newf ) )
+    {
+      fclose( newf );
+      unlink( newfont );
+      return false;
+    }
+
+    fclose( newf );
+
+    return true;
+  }
+
+
+  static int  child_pid;
+
+  static void
+  abort_test( int  sig )
+  {
+    FT_UNUSED( sig );
+
+    /* If a time-out happens, then kill the child */
+    kill( child_pid, SIGFPE );
+    write( 2, "Timeout... ", 11 );
+  }
+
+
+  static void
+  do_test( void )
+  {
+    int         i        = getRandom( 0, (int)( fcnt - 1 ) );
+    static int  test_num = 0;
+    char        buffer[1024];
+
+
+    sprintf( buffer, "%s/test%d", results_dir, test_num++ );
+
+    if ( copyfont ( &fontlist[i], buffer ) )
+    {
+      signal( SIGALRM, abort_test );
+      /* Anything that takes more than 20 seconds */
+      /* to parse and/or rasterize is an error.   */
+      alarm( 20 );
+      if ( ( child_pid = fork() ) == 0 )
+        ExecuteTest( buffer );
+      else if ( child_pid != -1 )
+      {
+        int  status;
+
+
+        waitpid( child_pid, &status, 0 );
+        alarm( 0 );
+        if ( WIFSIGNALED ( status ) )
+          printf( "Error found in file `%s'\n", buffer );
+        else
+          unlink( buffer );
+      }
+      else
+      {
+        fprintf( stderr, "Can't fork test case.\n" );
+        exit( 1 );
+      }
+      alarm( 0 );
+    }
+  }
+
+
+  static void
+  usage( FILE*  out,
+         char*  name )
+  {
+    char**  d = default_dir_list;
+    char**  e = default_ext_list;
+
+
+    fprintf( out, "%s [options] -- Generate random erroneous fonts\n"
+                  "  and attempt to parse them with FreeType.\n\n", name );
+
+    fprintf( out, "  --all                    All non-directory files are assumed to be fonts.\n" );
+    fprintf( out, "  --check-outlines         Make sure we can parse the outlines of each glyph.\n" );
+    fprintf( out, "  --dir <path>             Append <path> to list of font search directories\n"
+                  "                           (no recursive search).\n" );
+    fprintf( out, "  --error-count <cnt>      Introduce <cnt> single byte errors into each font\n"
+                  "                           (default: 1)\n" );
+    fprintf( out, "  --error-fraction <frac>  Introduce <frac>*filesize single byte errors\n"
+                  "                           into each font (default: 0.0).\n" );
+    fprintf( out, "  --ext <ext>              Add <ext> to list of extensions indicating fonts.\n" );
+    fprintf( out, "  --help                   Print this.\n" );
+    fprintf( out, "  --nohints                Turn off hinting.\n" );
+    fprintf( out, "  --rasterize              Attempt to rasterize each glyph.\n" );
+    fprintf( out, "  --results <path>         Place the created test fonts into <path>\n"
+                  "                           (default: `results')\n" );
+    fprintf( out, "  --size <float>           Use the given font size for the tests.\n" );
+    fprintf( out, "  --test <file>            Run a single test on an already existing file.\n" );
+    fprintf( out, "\n" );
+
+    fprintf( out, "Default font extensions:\n" );
+    fprintf( out, " " );
+    while ( *e )
+      fprintf( out, " .%s", *e++ );
+    fprintf( out, "\n" );
+
+    fprintf( out, "Default font directories:\n" );
+    fprintf( out, " " );
+    while ( *d )
+      fprintf( out, " %s", *d++ );
+    fprintf( out, "\n" );
+  }
+
+
+  int
+  main( int     argc,
+        char**  argv )
+  {
+    char    **dirs, **exts;
+    int     dcnt = 0, ecnt = 0, rset = false, allexts = false;
+    int     i;
+    time_t  now;
+    char*   testfile = NULL;
+
+
+    dirs = calloc( (size_t)( argc + 1 ), sizeof ( char ** ) );
+    exts = calloc( (size_t)( argc + 1 ), sizeof ( char ** ) );
+
+    for ( i = 1; i < argc; i++ )
+    {
+      char*  pt = argv[i];
+      char*  end;
+
+
+      if ( pt[0] == '-' && pt[1] == '-' )
+        pt++;
+
+      if ( strcmp( pt, "-all" ) == 0 )
+        allexts = true;
+      else if ( strcmp( pt, "-check-outlines" ) == 0 )
+        check_outlines = true;
+      else if ( strcmp( pt, "-dir" ) == 0 )
+        dirs[dcnt++] = argv[++i];
+      else if ( strcmp( pt, "-error-count" ) == 0 )
+      {
+        if ( !rset )
+          error_fraction = 0.0;
+        rset = true;
+        error_count = (unsigned int)strtoul( argv[++i], &end, 10 );
+        if ( *end != '\0' )
+        {
+          fprintf( stderr, "Bad value for error-count: %s\n", argv[i] );
+          exit( 1 );
+        }
+      }
+      else if ( strcmp( pt, "-error-fraction" ) == 0 )
+      {
+        if ( !rset )
+          error_count = 0;
+        rset = true;
+        error_fraction = strtod( argv[++i], &end );
+        if ( *end != '\0' )
+        {
+          fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] );
+          exit( 1 );
+        }
+        if ( error_fraction < 0.0 || error_fraction > 1.0 )
+        {
+          fprintf( stderr, "error-fraction must be in the range [0;1]\n" );
+          exit( 1 );
+        }
+      }
+      else if ( strcmp( pt, "-ext" ) == 0 )
+        exts[ecnt++] = argv[++i];
+      else if ( strcmp( pt, "-help" ) == 0 )
+      {
+        usage( stdout, argv[0] );
+        exit( 0 );
+      }
+      else if ( strcmp( pt, "-nohints" ) == 0 )
+        nohints = true;
+      else if ( strcmp( pt, "-rasterize" ) == 0 )
+        rasterize = true;
+      else if ( strcmp( pt, "-results" ) == 0 )
+        results_dir = argv[++i];
+      else if ( strcmp( pt, "-size" ) == 0 )
+      {
+        font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 );
+        if ( *end != '\0' || font_size < 64 )
+        {
+          fprintf( stderr, "Bad value for size: %s\n", argv[i] );
+          exit( 1 );
+        }
+      }
+      else if ( strcmp( pt, "-test" ) == 0 )
+        testfile = argv[++i];
+      else
+      {
+        usage( stderr, argv[0] );
+        exit( 1 );
+      }
+    }
+
+    if ( allexts )
+    {
+      free( exts );
+      exts = NULL;
+    }
+    else if ( ecnt == 0 )
+    {
+      free( exts );
+      exts = default_ext_list;
+    }
+
+    if ( dcnt == 0 )
+    {
+      free( dirs );
+      dirs = default_dir_list;
+    }
+
+    if ( testfile )
+      ExecuteTest( testfile );         /* This should never return */
+
+    time( &now );
+    srandom( (unsigned int)now );
+
+    FindFonts( dirs, exts );
+    mkdir( results_dir, 0755 );
+
+    forever
+      do_test();
+
+    return 0;
+  }
+
+
+/* EOF */
diff --git a/src/third_party/freetype2/src/tools/no-copyright b/src/third_party/freetype2/src/tools/no-copyright
new file mode 100644
index 0000000..d639aa4
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/no-copyright
@@ -0,0 +1,65 @@
+# Files that don't get a copyright, or which are taken from elsewhere.
+#
+# All lines in this file are patterns, including the comment lines; this
+# means that e.g. `FTL.TXT' matches all files that have this string in
+# the file name (including the path relative to the current directory,
+# always starting with `./').
+#
+# Don't put empty lines into this file!
+#
+.gitignore
+#
+builds/unix/pkg.m4
+#
+docs/FTL.TXT
+docs/GPLv2.TXT
+#
+include/freetype/internal/fthash.h
+#
+src/base/fthash.c
+src/base/md5.c
+src/base/md5.h
+#
+src/bdf/bdf.c
+src/bdf/bdf.h
+src/bdf/bdfdrivr.c
+src/bdf/bdfdrivr.h
+src/bdf/bdferror.h
+src/bdf/bdflib.c
+src/bdf/module.mk
+src/bdf/README
+src/bdf/rules.mk
+#
+src/pcf/module.mk
+src/pcf/pcf.c
+src/pcf/pcf.h
+src/pcf/pcfdrivr.c
+src/pcf/pcfdrivr.h
+src/pcf/pcferror.h
+src/pcf/pcfread.c
+src/pcf/pcfread.h
+src/pcf/pcfutil.c
+src/pcf/pcfutil.h
+src/pcf/README
+src/pcf/rules.mk
+#
+src/gzip/adler32.c
+src/gzip/infblock.c
+src/gzip/infblock.h
+src/gzip/infcodes.c
+src/gzip/infcodes.h
+src/gzip/inffixed.h
+src/gzip/inflate.c
+src/gzip/inftrees.c
+src/gzip/inftrees.h
+src/gzip/infutil.c
+src/gzip/infutil.h
+src/gzip/zconf.h
+src/gzip/zlib.h
+src/gzip/zutil.c
+src/gzip/zutil.h
+#
+src/tools/apinames.c
+src/tools/ftrandom/ftrandom.c
+#
+# EOF
diff --git a/src/third_party/freetype2/src/tools/test_afm.c b/src/third_party/freetype2/src/tools/test_afm.c
new file mode 100644
index 0000000..8de619b
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/test_afm.c
@@ -0,0 +1,157 @@
+/*
+ * gcc -DFT2_BUILD_LIBRARY -I../../include -o test_afm test_afm.c \
+ *     -L../../objs/.libs -lfreetype -lz -static
+ */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+  void dump_fontinfo( AFM_FontInfo  fi )
+  {
+    FT_UInt  i;
+
+
+    printf( "This AFM is for %sCID font.\n\n",
+            ( fi->IsCIDFont ) ? "" : "non-" );
+
+    printf( "FontBBox: %.2f %.2f %.2f %.2f\n", fi->FontBBox.xMin / 65536.,
+                                               fi->FontBBox.yMin / 65536.,
+                                               fi->FontBBox.xMax / 65536.,
+                                               fi->FontBBox.yMax / 65536. );
+    printf( "Ascender: %.2f\n", fi->Ascender / 65536. );
+    printf( "Descender: %.2f\n\n", fi->Descender / 65536. );
+
+    if ( fi->NumTrackKern )
+      printf( "There are %d sets of track kernings:\n",
+              fi->NumTrackKern );
+    else
+      printf( "There is no track kerning.\n" );
+
+    for ( i = 0; i < fi->NumTrackKern; i++ )
+    {
+      AFM_TrackKern  tk = fi->TrackKerns + i;
+
+
+      printf( "\t%2d: %5.2f %5.2f %5.2f %5.2f\n", tk->degree,
+                                                  tk->min_ptsize / 65536.,
+                                                  tk->min_kern / 65536.,
+                                                  tk->max_ptsize / 65536.,
+                                                  tk->max_kern / 65536. );
+    }
+
+    printf( "\n" );
+
+    if ( fi->NumKernPair )
+      printf( "There are %d kerning pairs:\n",
+              fi->NumKernPair );
+    else
+      printf( "There is no kerning pair.\n" );
+
+    for ( i = 0; i < fi->NumKernPair; i++ )
+    {
+      AFM_KernPair  kp = fi->KernPairs + i;
+
+
+      printf( "\t%3d + %3d => (%4d, %4d)\n", kp->index1,
+                                             kp->index2,
+                                             kp->x,
+                                             kp->y );
+    }
+
+  }
+
+  int
+  dummy_get_index( const char*  name,
+                   FT_Offset    len,
+                   void*        user_data )
+  {
+    if ( len )
+      return name[0];
+    else
+      return 0;
+  }
+
+  FT_Error
+  parse_afm( FT_Library    library,
+             FT_Stream     stream,
+             AFM_FontInfo  fi )
+  {
+    PSAux_Service  psaux;
+    AFM_ParserRec  parser;
+    FT_Error       error = FT_Err_Ok;
+
+
+    psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" );
+    if ( !psaux || !psaux->afm_parser_funcs )
+      return -1;
+
+    error = FT_Stream_EnterFrame( stream, stream->size );
+    if ( error )
+      return error;
+
+    error = psaux->afm_parser_funcs->init( &parser,
+                                           library->memory,
+                                           stream->cursor,
+                                           stream->limit );
+    if ( error )
+      return error;
+
+    parser.FontInfo = fi;
+    parser.get_index = dummy_get_index;
+
+    error = psaux->afm_parser_funcs->parse( &parser );
+
+    psaux->afm_parser_funcs->done( &parser );
+
+    return error;
+  }
+
+
+  int main( int    argc,
+            char** argv )
+  {
+    FT_Library       library;
+    FT_StreamRec     stream;
+    FT_Error         error = FT_Err_Ok;
+    AFM_FontInfoRec  fi;
+
+
+    if ( argc < 2 )
+      return FT_ERR( Invalid_Argument );
+
+    error = FT_Init_FreeType( &library );
+    if ( error )
+      return error;
+
+    FT_ZERO( &stream );
+    error = FT_Stream_Open( &stream, argv[1] );
+    if ( error )
+      goto Exit;
+    stream.memory = library->memory;
+
+    FT_ZERO( &fi );
+    error = parse_afm( library, &stream, &fi );
+
+    if ( !error )
+    {
+      FT_Memory  memory = library->memory;
+
+
+      dump_fontinfo( &fi );
+
+      if ( fi.KernPairs )
+        FT_FREE( fi.KernPairs );
+      if ( fi.TrackKerns )
+        FT_FREE( fi.TrackKerns );
+    }
+    else
+      printf( "parse error\n" );
+
+    FT_Stream_Close( &stream );
+
+  Exit:
+    FT_Done_FreeType( library );
+
+    return error;
+  }
diff --git a/src/third_party/freetype2/src/tools/test_bbox.c b/src/third_party/freetype2/src/tools/test_bbox.c
new file mode 100644
index 0000000..64b82c3
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/test_bbox.c
@@ -0,0 +1,188 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_BBOX_H
+
+
+#include <time.h>    /* for clock() */
+
+/* SunOS 4.1.* does not define CLOCKS_PER_SEC, so include <sys/param.h> */
+/* to get the HZ macro which is the equivalent.                         */
+#if defined(__sun__) && !defined(SVR4) && !defined(__SVR4)
+#include <sys/param.h>
+#define CLOCKS_PER_SEC HZ
+#endif
+
+  static long
+  get_time( void )
+  {
+    return clock() * 10000L / CLOCKS_PER_SEC;
+  }
+
+
+
+
+  /* test bbox computations */
+
+#define  XSCALE    65536
+#define  XX(x)     ((FT_Pos)(x*XSCALE))
+#define  XVEC(x,y)  { XX(x), XX(y) }
+#define  XVAL(x)   ((x)/(1.0*XSCALE))
+
+  /* dummy outline #1 */
+  static FT_Vector  dummy_vec_1[4] =
+  {
+#if 1
+    XVEC( 408.9111, 535.3164 ),
+    XVEC( 455.8887, 634.396  ),
+    XVEC( -37.8765, 786.2207 ),
+    XVEC( 164.6074, 535.3164 )
+#else
+    { (FT_Int32)0x0198E93DL , (FT_Int32)0x021750FFL },  /* 408.9111, 535.3164 */
+    { (FT_Int32)0x01C7E312L , (FT_Int32)0x027A6560L },  /* 455.8887, 634.3960 */
+    { (FT_Int32)0xFFDA1F9EL , (FT_Int32)0x0312387FL },  /* -37.8765, 786.2207 */
+    { (FT_Int32)0x00A49B7EL , (FT_Int32)0x021750FFL }   /* 164.6074, 535.3164 */
+#endif
+   };
+
+  static char  dummy_tag_1[4] =
+  {
+    FT_CURVE_TAG_ON,
+    FT_CURVE_TAG_CUBIC,
+    FT_CURVE_TAG_CUBIC,
+    FT_CURVE_TAG_ON
+  };
+
+  static short  dummy_contour_1[1] =
+  {
+    3
+  };
+
+  static FT_Outline  dummy_outline_1 =
+  {
+    1,
+    4,
+    dummy_vec_1,
+    dummy_tag_1,
+    dummy_contour_1,
+    0
+  };
+
+
+  /* dummy outline #2 */
+  static FT_Vector  dummy_vec_2[4] =
+  {
+    XVEC( 100.0, 100.0 ),
+    XVEC( 100.0, 200.0 ),
+    XVEC( 200.0, 200.0 ),
+    XVEC( 200.0, 133.0 )
+  };
+
+  static FT_Outline  dummy_outline_2 =
+  {
+    1,
+    4,
+    dummy_vec_2,
+    dummy_tag_1,
+    dummy_contour_1,
+    0
+  };
+
+
+  /* dummy outline #3 with bbox of [0 100 128 128] precisely */
+  static FT_Vector  dummy_vec_3[4] =
+  {
+    XVEC( 100.0, 127.0 ),
+    XVEC( 200.0, 127.0 ),
+    XVEC(   0.0, 136.0 ),
+    XVEC(   0.0, 100.0 )
+  };
+
+  static FT_Outline  dummy_outline_3 =
+  {
+    1,
+    4,
+    dummy_vec_3,
+    dummy_tag_1,
+    dummy_contour_1,
+    0
+  };
+
+
+  static void
+  dump_outline( FT_Outline*  outline )
+  {
+    FT_BBox  bbox;
+
+    /* compute and display cbox */
+    FT_Outline_Get_CBox( outline, &bbox );
+    printf( "cbox = [%.2f %.2f %.2f %.2f]\n",
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+
+    /* compute and display bbox */
+    FT_Outline_Get_BBox( outline, &bbox );
+    printf( "bbox = [%.2f %.2f %.2f %.2f]\n",
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+  }
+
+
+
+  static void
+  profile_outline( FT_Outline*   outline,
+                   long          repeat )
+  {
+    FT_BBox  bbox;
+    long     count;
+    long     time0;
+
+    time0 = get_time();
+    for ( count = repeat; count > 0; count-- )
+      FT_Outline_Get_CBox( outline, &bbox );
+
+    time0 = get_time() - time0;
+    printf( "time = %6.3f cbox = [%8.4f %8.4f %8.4f %8.4f]\n",
+             ((double)time0/10000.0),
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+    printf( "cbox_hex = [%08X %08X %08X %08X]\n",
+             bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax );
+
+
+    time0 = get_time();
+    for ( count = repeat; count > 0; count-- )
+      FT_Outline_Get_BBox( outline, &bbox );
+
+    time0 = get_time() - time0;
+    printf( "time = %6.3f bbox = [%8.4f %8.4f %8.4f %8.4f]\n",
+             ((double)time0/10000.0),
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+    printf( "bbox_hex = [%08X %08X %08X %08X]\n",
+             bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax );
+  }
+
+#define REPEAT  1000000L
+
+  int  main( int  argc, char**  argv )
+  {
+    printf( "outline #1\n" );
+    profile_outline( &dummy_outline_1, REPEAT );
+
+    printf( "outline #2\n" );
+    profile_outline( &dummy_outline_2, REPEAT );
+
+    printf( "outline #3\n" );
+    profile_outline( &dummy_outline_3, REPEAT );
+
+    return 0;
+  }
+
diff --git a/src/third_party/freetype2/src/tools/test_trig.c b/src/third_party/freetype2/src/tools/test_trig.c
new file mode 100644
index 0000000..99ac1cf
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/test_trig.c
@@ -0,0 +1,258 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRIGONOMETRY_H
+
+#include <math.h>
+#include <stdio.h>
+
+#define  PI   3.14159265358979323846
+#define  SPI  (PI/FT_ANGLE_PI)
+
+/* the precision in 16.16 fixed-point checks. Expect between 2 and 5 */
+/* noise LSB bits during operations, due to rounding errors..        */
+#define  THRESHOLD  64
+
+  static  error = 0;
+
+  static void
+  test_cos( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Fixed  f1, f2;
+      double    d2;
+
+
+      f1 = FT_Cos(i);
+      d2 = cos( i*SPI );
+      f2 = (FT_Fixed)(d2*65536.0);
+
+      if ( abs( f2-f1 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Cos[%3d] = %.7f  cos[%3d] = %.7f\n",
+                (i >> 16), f1/65536.0, (i >> 16), d2 );
+      }
+    }
+  }
+
+
+  static void
+  test_sin( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Fixed  f1, f2;
+      double    d2;
+
+
+      f1 = FT_Sin(i);
+      d2 = sin( i*SPI );
+      f2 = (FT_Fixed)(d2*65536.0);
+
+      if ( abs( f2-f1 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Sin[%3d] = %.7f  sin[%3d] = %.7f\n",
+                (i >> 16), f1/65536.0, (i >> 16), d2 );
+      }
+    }
+  }
+
+
+  static void
+  test_tan( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_PI2 - 0x2000000L; i += 0x10000L )
+    {
+      FT_Fixed  f1, f2;
+      double    d2;
+
+
+      f1 = FT_Tan(i);
+      d2 = tan( i*SPI );
+      f2 = (FT_Fixed)(d2*65536.0);
+
+      if ( abs( f2-f1 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Tan[%3d] = %.7f  tan[%3d] = %.7f\n",
+                (i >> 16), f1/65536.0, (i >> 16), d2 );
+      }
+    }
+  }
+
+
+  static void
+  test_atan2( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Fixed  c2, s2;
+      double    l, a, c1, s1;
+      int       j;
+
+
+      l  = 5.0;
+      a  = i*SPI;
+
+      c1 = l * cos(a);
+      s1 = l * sin(a);
+
+      c2 = (FT_Fixed)(c1*65536.0);
+      s2 = (FT_Fixed)(s1*65536.0);
+
+      j  = FT_Atan2( c2, s2 );
+      if ( j < 0 )
+        j += FT_ANGLE_2PI;
+
+      if ( abs( i - j ) > 1 )
+      {
+        printf( "FT_Atan2( %.7f, %.7f ) = %.5f, atan = %.5f\n",
+                c2/65536.0, s2/65536.0, j/65536.0, i/65536.0 );
+      }
+    }
+  }
+
+
+  static void
+  test_unit( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Vector  v;
+      double     a, c1, s1;
+      FT_Fixed   c2, s2;
+
+
+      FT_Vector_Unit( &v, i );
+      a  = ( i*SPI );
+      c1 = cos(a);
+      s1 = sin(a);
+      c2 = (FT_Fixed)(c1*65536.0);
+      s2 = (FT_Fixed)(s1*65536.0);
+
+      if ( abs( v.x-c2 ) > THRESHOLD ||
+           abs( v.y-s2 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Vector_Unit[%3d] = ( %.7f, %.7f )  vec = ( %.7f, %.7f )\n",
+                (i >> 16),
+                v.x/65536.0, v.y/65536.0,
+                c1, s1 );
+      }
+    }
+  }
+
+
+  static void
+  test_length( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Vector  v;
+      FT_Fixed   l, l2;
+
+
+      l   = (FT_Fixed)(500.0*65536.0);
+      v.x = (FT_Fixed)( l * cos( i*SPI ) );
+      v.y = (FT_Fixed)( l * sin( i*SPI ) );
+      l2  = FT_Vector_Length( &v );
+
+      if ( abs( l2-l ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Length( %.7f, %.7f ) = %.5f, length = %.5f\n",
+                v.x/65536.0, v.y/65536.0, l2/65536.0, l/65536.0 );
+      }
+    }
+  }
+
+
+  static void
+  test_rotate( void )
+  {
+    int  rotate;
+
+
+    for ( rotate = 0; rotate < FT_ANGLE_2PI; rotate += 0x10000L )
+    {
+      double  ra, cra, sra;
+      int     i;
+
+
+      ra  = rotate*SPI;
+      cra = cos( ra );
+      sra = sin( ra );
+
+      for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+      {
+        FT_Fixed   c2, s2, c4, s4;
+        FT_Vector  v;
+        double     l, a, c1, s1, c3, s3;
+
+
+        l  = 500.0;
+        a  = i*SPI;
+
+        c1 = l * cos(a);
+        s1 = l * sin(a);
+
+        v.x = c2 = (FT_Fixed)(c1*65536.0);
+        v.y = s2 = (FT_Fixed)(s1*65536.0);
+
+        FT_Vector_Rotate( &v, rotate );
+
+        c3 = c1 * cra - s1 * sra;
+        s3 = c1 * sra + s1 * cra;
+
+        c4 = (FT_Fixed)(c3*65536.0);
+        s4 = (FT_Fixed)(s3*65536.0);
+
+        if ( abs( c4 - v.x ) > THRESHOLD ||
+             abs( s4 - v.y ) > THRESHOLD )
+        {
+          error = 1;
+          printf( "FT_Rotate( (%.7f,%.7f), %.5f ) = ( %.7f, %.7f ), rot = ( %.7f, %.7f )\n",
+                  c1, s1, ra,
+                  c2/65536.0, s2/65536.0,
+                  c4/65536.0, s4/65536.0 );
+        }
+      }
+    }
+  }
+
+
+  int main( void )
+  {
+    test_cos();
+    test_sin();
+    test_tan();
+    test_atan2();
+    test_unit();
+    test_length();
+    test_rotate();
+
+    if (!error)
+      printf( "trigonometry test ok !\n" );
+
+    return !error;
+  }
diff --git a/src/third_party/freetype2/src/tools/update-copyright b/src/third_party/freetype2/src/tools/update-copyright
new file mode 100755
index 0000000..4a8bf9b
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/update-copyright
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Run the `update-copyright-year' script on all files in the git repository,
+# taking care of exceptions stored in file `no-copyright'.
+
+topdir=`git rev-parse --show-toplevel`
+toolsdir=$topdir/src/tools
+
+git ls-files --full-name $topdir        \
+| sed 's|^|../../|'                     \
+| grep -vFf $toolsdir/no-copyright      \
+| xargs $toolsdir/update-copyright-year
+
+# EOF
diff --git a/src/third_party/freetype2/src/tools/update-copyright-year b/src/third_party/freetype2/src/tools/update-copyright-year
new file mode 100755
index 0000000..c659bba
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/update-copyright-year
@@ -0,0 +1,138 @@
+eval '(exit $?0)' && eval 'exec perl -wS -i "$0" ${1+"$@"}'
+  & eval 'exec perl -wS -i "$0" $argv:q'
+    if 0;
+
+# Copyright (C) 2015-2020 by
+# Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+# [Note: This script is expected to be called by the shell, which in turn
+#  calls perl automatically.  The nifty start-up code above is based on
+#  gnulib's `update-copyright' script; it is a more portable replacement for
+#  the shebang, using the first `perl' program in the shell's path instead.]
+
+# Usage:
+#
+#   update-copyright-year file1 [file2 ...]
+
+
+# This script handles copyright entries like
+#
+#   Copyright  2000   by
+#   foobar
+#
+# or
+#
+#   /* Copyright 2000,  2001, 2004-2007 by    */
+#   /* foobar                                 */
+#
+# and replaces them uniformly with
+#
+#   Copyright 2000-2015
+#   foobar
+#
+# and
+#
+#   /* Copyright 2000-2015 by                 */
+#   /* foobar                                 */
+#
+# (assuming that the current year is 2015).  As can be seen, the line length
+# is retained if there is non-whitespace after the word `by' on the same
+# line.
+
+use strict;
+
+
+my (undef, undef, undef,
+    undef, undef, $year,
+    undef, undef, undef) = localtime(time);
+$year += 1900;
+
+my $replaced = 0;
+
+
+# Loop over all input files; option `-i' (issued at the very beginning of
+# this script) makes perl edit them in-place.
+while (<>)
+{
+  # Only handle the first copyright notice in a file.
+  if (!$replaced)
+  {
+    # First try: Search multiple copyright years.
+    s {
+        (?<begin>.*)
+        Copyright
+        (?<space1>(\ +
+                   | \ +\(C\)\ +))
+        (?<first>[12][0-9][0-9][0-9])
+        (?<middle>.+)
+        (?<last>[12][0-9][0-9][0-9])
+        (?<space2>\ +)
+        by
+        (?<space3>\ *)
+        (?<end>.*)
+      }
+      {
+        # Fill line to the same length (if appropriate); we skip the middle
+        # part but insert `(C)', three spaces, and `-'.
+        my $space = length($+{space1}) - 1
+                    + length($+{middle}) - 1
+                    + length($+{space2}) - 1
+                    + length($+{space3})
+                    - (length("(C)") + 1);
+
+        print "$+{begin}";
+        print "Copyright\ (C)\ $+{first}-$year\ by";
+        print ' ' x $space if length($+{end});
+        print "$+{end}\n";
+        $replaced = 1;
+      }ex
+    ||
+    # Second try: Search a single copyright year.
+    s {
+        (?<begin>.*)
+        Copyright
+        (?<space1>(\ +
+                   | \ +\(C\)\ +))
+        (?<first>[12][0-9][0-9][0-9])
+        (?<space2>\ +)
+        by
+        (?<space3>\ *)
+        (?<end>.*)
+      }
+      {
+        # Fill line to the same length (if appropriate); we insert three
+        # spaces, a `-', and the current year.
+        my $space = length($+{space1}) - 1
+                    + length($+{space2}) - 1
+                    + length($+{space3})
+                    - (length($year) + 1);
+
+        print "$+{begin}";
+        print "Copyright\ (C)\ $+{first}-$year\ by";
+        # If $space is negative this inserts nothing.
+        print ' ' x $space if length($+{end});
+        print "$+{end}\n";
+        $replaced = 1;
+      }ex
+    ||
+    # Otherwise print line unaltered.
+    print;
+  }
+  else
+  {
+    print;
+  }
+}
+continue
+{
+  # Reset $replaced before processing the next file.
+  $replaced = 0 if eof;
+}
+
+# EOF
diff --git a/src/third_party/libpng/libpng.gyp b/src/third_party/libpng/libpng.gyp
index 79153b2..ad2358c 100644
--- a/src/third_party/libpng/libpng.gyp
+++ b/src/third_party/libpng/libpng.gyp
@@ -137,9 +137,6 @@
               'pngconf.h',
             ],
           },
-          'includes': [
-            '../../build/shim_headers.gypi',
-          ],
         },
       ],
     }],
diff --git a/src/third_party/precommit-hooks/custom_hooks/check_copyright_year.py b/src/third_party/precommit-hooks/custom_hooks/check_copyright_year.py
index 0b4b516..692e44f 100755
--- a/src/third_party/precommit-hooks/custom_hooks/check_copyright_year.py
+++ b/src/third_party/precommit-hooks/custom_hooks/check_copyright_year.py
@@ -53,7 +53,7 @@
   ]
 
   copyright_re = re.compile(r'Copyright (?P<created>\d{4})'
-                            r'(?P<current>-\d{4})? (?P<author>[\w\s]+)')
+                            r'(-(?P<current>\d{4}))? (?P<author>[\w\s]+)')
   current_year = datetime.datetime.today().year
 
   for filename in filenames:
diff --git a/src/tools/__init__.py b/src/tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tools/__init__.py
diff --git a/src/tools/download_from_gcs.py b/src/tools/download_from_gcs.py
new file mode 100755
index 0000000..85a93f7
--- /dev/null
+++ b/src/tools/download_from_gcs.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+# Copyright 2020 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 argparse
+import hashlib
+import logging
+import os
+import shutil
+import ssl
+import stat
+import tempfile
+try:
+  import urllib.request as urllib
+except ImportError:
+  import urllib2 as urllib
+
+# ssl.SSLContext (and thus ssl.create_default_context) was introduced in
+# python 2.7.9. depot_tools provides python 2.7.6, so we wrap this import.
+try:
+  from ssl import create_default_context
+except ImportError:
+  create_default_context = lambda: None
+
+_BASE_GCS_URL = 'https://storage.googleapis.com'
+_BUFFER_SIZE = 2 * 1024 * 1024
+
+
+def AddExecutableBits(filename):
+  st = os.stat(filename)
+  os.chmod(filename, st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+
+
+def ExtractSha1(filename):
+  with open(filename, 'rb') as f:
+    sha1 = hashlib.sha1()
+    buf = f.read(_BUFFER_SIZE)
+    while buf:
+      sha1.update(buf)
+      buf = f.read(_BUFFER_SIZE)
+  return sha1.hexdigest()
+
+
+def _DownloadFromGcsAndCheckSha1(bucket, sha1):
+  url = '{}/{}/{}'.format(_BASE_GCS_URL, bucket, sha1)
+  context = create_default_context()
+
+  res = urllib.urlopen(url, context=context) if context else urllib.urlopen(url)
+  if not res:
+    logging.error('Could not reach %s', url)
+    return None
+
+  with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
+    shutil.copyfileobj(res, tmp_file)
+
+  if ExtractSha1(tmp_file.name) != sha1:
+    logging.error('Local and remote sha1s do not match. Skipping download.')
+    return None
+
+  return tmp_file
+
+
+def MaybeDownloadFileFromGcs(bucket, sha1_file, output_file, force=False):
+  """Download file from GCS if it doesn't already exist.
+
+  Args:
+    bucket: The GCS bucket that the file is stored in.
+    sha1_file: The file containing the sha1 of the file to download
+    output_file: The file to output to
+    force: If true, will overwrite an existing output_file
+  Returns:
+    True if it writes a file, False otherwise.
+  """
+  if not os.path.exists(sha1_file):
+    logging.error("Provided sha1 file %s doesn't exist", sha1_file)
+  with open(sha1_file) as fd:
+    sha1 = fd.read().strip()
+
+  if not force and os.path.exists(output_file):
+    with open(output_file, 'rb') as f:
+      if hashlib.sha1(f.read()).hexdigest() == sha1:
+        logging.info('%s exists and sha1s match, skipping download',
+                     output_file)
+        return False
+
+  tmp_file = _DownloadFromGcsAndCheckSha1(bucket, sha1)
+  if not tmp_file:
+    return False
+
+  shutil.move(tmp_file.name, output_file)
+  AddExecutableBits(output_file)
+  return True
+
+
+def MaybeDownloadDirectoryFromGcs(bucket,
+                                  sha1_directory,
+                                  output_directory,
+                                  force=False):
+  res = True
+  for filename in os.listdir(sha1_directory):
+    filebase, ext = os.path.splitext(filename)
+    if ext == '.sha1':
+      filepath = os.path.join(sha1_directory, filename)
+      output_filepath = os.path.join(output_directory, filebase)
+      if not MaybeDownloadFileFromGcs(bucket, filepath, output_filepath, force):
+        res = False
+  return res
+
+
+if __name__ == "__main__":
+  logging_format = '[%(levelname)s:%(filename)s:%(lineno)s] %(message)s'
+  logging.basicConfig(
+      level=logging.INFO, format=logging_format, datefmt='%H:%M:%S')
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '-b',
+      '--bucket',
+      required=True,
+      help='GCS bucket the resource is located in.')
+  parser.add_argument(
+      '-s',
+      '--sha1',
+      required=True,
+      help='Path to file or directory containing sha1 checksum(s).')
+  parser.add_argument(
+      '-o',
+      '--output',
+      required=True,
+      help='Path to file or directory to download file(s) to.')
+  parser.add_argument(
+      '-f',
+      '--force',
+      action='store_true',
+      help='Replace an existing resource.')
+  args = parser.parse_args()
+
+  if os.path.isdir(args.sha1):
+    MaybeDownloadDirectoryFromGcs(args.bucket, args.sha1, args.output,
+                                  args.force)
+  else:
+    MaybeDownloadFileFromGcs(args.bucket, args.sha1, args.output, args.force)
diff --git a/src/tools/download_from_gcs_test.py b/src/tools/download_from_gcs_test.py
new file mode 100755
index 0000000..6abad17
--- /dev/null
+++ b/src/tools/download_from_gcs_test.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# Copyright 2020 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 argparse
+import hashlib
+import logging
+import os
+import sys
+import tempfile
+import unittest
+
+import tools.download_from_gcs as download_from_gcs
+
+_BUCKET = 'chromium-clang-format'
+_HASH_FILE_EXT = '.sha1'
+_TEST_PATH = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)), 'testing/download_from_gcs')
+_TEST_DIRECTORY = 'test_dir'
+_TEST_FILE = 'clang-format.sha1'
+
+logging_format = '[%(levelname)s:%(filename)s:%(lineno)s] %(message)s'
+logging.basicConfig(
+    level=logging.INFO, format=logging_format, datefmt='%H:%M:%S')
+
+
+def _Sha1sMatch(file_to_read, file_to_hash):
+  if not os.path.exists(file_to_read):
+    logging.error('%s does not exist.', file_to_read)
+    return False
+
+  if not os.path.exists(file_to_hash):
+    logging.error('%s does not exist.', file_to_hash)
+    return False
+
+  with open(file_to_read) as f:
+    sha1 = f.read().strip()
+
+  return download_from_gcs.ExtractSha1(file_to_hash) == sha1
+
+
+class TestFileDownload(unittest.TestCase):
+
+  def setUp(self):
+    self.test_file = os.path.join(_TEST_PATH, _TEST_FILE)
+    self.output_file = tempfile.NamedTemporaryFile()
+    self.bucket = _BUCKET
+
+  def downloadFile(self, test_file, output_file):
+    return download_from_gcs.MaybeDownloadFileFromGcs(
+        self.bucket, sha1_file=test_file, output_file=output_file)
+
+  def testDownloadSingleFile(self):
+    self.assertTrue(self.downloadFile(self.test_file, self.output_file.name))
+    self.assertTrue(_Sha1sMatch(self.test_file, self.output_file.name))
+
+
+class DirectoryDownloadTest(unittest.TestCase):
+
+  def setUp(self):
+    self.test_directory = os.path.join(_TEST_PATH, _TEST_DIRECTORY)
+    self.output_directory = tempfile.TemporaryDirectory()
+    self.bucket = _BUCKET
+
+  def tearDown(self):
+    self.output_directory.cleanup()
+
+  def downloadFiles(self, test_directory, output_directory):
+    return download_from_gcs.MaybeDownloadDirectoryFromGcs(
+        self.bucket,
+        sha1_directory=test_directory,
+        output_directory=output_directory)
+
+  def testDownloadMultipleFiles(self):
+    self.assertTrue(
+        self.downloadFiles(self.test_directory, self.output_directory.name))
+
+    sha1_files = list(os.listdir(self.test_directory))
+    output_files = list(os.listdir(self.output_directory.name))
+    self.assertEqual(len(sha1_files), len(output_files))
+
+    for output_file in output_files:
+      sha1_file = output_file + _HASH_FILE_EXT
+      self.assertIn(sha1_file, sha1_files)
+
+      sha1_file = os.path.join(self.test_directory, sha1_file)
+      output_file = os.path.join(self.output_directory.name, output_file)
+      self.assertTrue(_Sha1sMatch(sha1_file, output_file))
diff --git a/src/tools/gyp/pylib/gyp/MSVSSettings.py b/src/tools/gyp/pylib/gyp/MSVSSettings.py
index d0d4990..8526389 100644
--- a/src/tools/gyp/pylib/gyp/MSVSSettings.py
+++ b/src/tools/gyp/pylib/gyp/MSVSSettings.py
@@ -532,7 +532,6 @@
 _Same(_compile, 'ForcedUsingFiles', _file_list)  # /FU
 _Same(_compile, 'GenerateXMLDocumentationFiles', _boolean)  # /doc
 _Same(_compile, 'IgnoreStandardIncludePath', _boolean)  # /X
-_Same(_compile, 'MinimalRebuild', _boolean)  # /Gm
 _Same(_compile, 'OmitDefaultLibName', _boolean)  # /Zl
 _Same(_compile, 'OmitFramePointers', _boolean)  # /Oy
 _Same(_compile, 'PreprocessorDefinitions', _string_list)  # /D
diff --git a/src/tools/gyp/pylib/gyp/MSVSSettings_test.py b/src/tools/gyp/pylib/gyp/MSVSSettings_test.py
index 4e06da3..52ff7c3 100755
--- a/src/tools/gyp/pylib/gyp/MSVSSettings_test.py
+++ b/src/tools/gyp/pylib/gyp/MSVSSettings_test.py
@@ -79,7 +79,6 @@
             'IgnoreStandardIncludePath': 'true',
             'InlineFunctionExpansion': '1',
             'KeepComments': 'true',
-            'MinimalRebuild': 'true',
             'ObjectFile': 'a_file_name',
             'OmitDefaultLibName': 'true',
             'OmitFramePointers': 'true',
@@ -318,7 +317,6 @@
             'IgnoreStandardIncludePath': 'true',
             'InlineFunctionExpansion': 'OnlyExplicitInline',
             'IntrinsicFunctions': 'false',
-            'MinimalRebuild': 'true',
             'MultiProcessorCompilation': 'true',
             'ObjectFileName': 'a_file_name',
             'OmitDefaultLibName': 'true',
@@ -690,7 +688,6 @@
             'IgnoreStandardIncludePath': 'true',
             'InlineFunctionExpansion': '2',
             'KeepComments': 'true',
-            'MinimalRebuild': 'true',
             'ObjectFile': 'a_file_name',
             'OmitDefaultLibName': 'true',
             'OmitFramePointers': 'true',
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 010fbe9..c3f4ec9 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python2
 # Copyright (c) 2013 Google Inc. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -77,7 +78,9 @@
 is_windows = platform.system() == 'Windows'
 
 microsoft_flavors = [
-    'win', 'win-win32', 'win-win32-lib',
+    'win',
+    'win-win32',
+    'win-win32-lib',
 ]
 sony_flavors = []
 
@@ -139,8 +142,7 @@
 def GetGeneratorVariables(flavor):
   generator_variables = copy.copy(generator_default_variables)
   if GetToolchainOrNone(flavor):
-    GetToolchainOrNone(
-        flavor).SetAdditionalGypVariables(generator_variables)
+    GetToolchainOrNone(flavor).SetAdditionalGypVariables(generator_variables)
   else:
     CalculateVariables(generator_variables, {'flavor': flavor})
   return generator_variables
@@ -778,8 +780,8 @@
 
         inputs = [self.GypPathToNinja(i, env) for i in inputs]
         outputs = [self.GypPathToNinja(o, env) for o in outputs]
-        extra_bindings.append(('unique_name',
-                               hashlib.md5(outputs[0]).hexdigest()))
+        extra_bindings.append(
+            ('unique_name', hashlib.md5(outputs[0]).hexdigest()))
         self.ninja.build(
             outputs,
             rule_name,
@@ -895,11 +897,11 @@
     shell = GetShell(self.flavor)
 
     if self.toolset == 'target':
-      toolchain = GetTargetToolchain(self.flavor, spec=spec,
-                                     config_name=config_name)
+      toolchain = GetTargetToolchain(
+          self.flavor, spec=spec, config_name=config_name)
     else:
-      toolchain = GetHostToolchain(self.flavor, spec=spec,
-                                   config_name=config_name)
+      toolchain = GetHostToolchain(
+          self.flavor, spec=spec, config_name=config_name)
 
     defines = config.get('defines', [])
     include_dirs = [
@@ -1007,8 +1009,7 @@
           gyp_path_to_ninja=self.GypPathToNinja,
           expand_special=self.ExpandSpecial,
           gyp_path_to_unique_output=self.GypPathToUniqueOutput,
-          compute_output_file_name=self.ComputeOutputFileName
-      )
+          compute_output_file_name=self.ComputeOutputFileName)
     else:
       toolchain = GetHostToolchain(
           self.flavor,
@@ -1017,8 +1018,7 @@
           gyp_path_to_ninja=self.GypPathToNinja,
           expand_special=self.ExpandSpecial,
           gyp_path_to_unique_output=self.GypPathToUniqueOutput,
-          compute_output_file_name=self.ComputeOutputFileName
-      )
+          compute_output_file_name=self.ComputeOutputFileName)
 
     shell = GetShell(self.flavor)
     extra_bindings = []
@@ -1039,8 +1039,8 @@
       libraries = spec.get(libraries_keyword, []) + config.get(
           libraries_keyword, [])
 
-      ldflags_executable = GetConfigFlags(
-          config, self.toolset, 'ldflags_executable')
+      ldflags_executable = GetConfigFlags(config, self.toolset,
+                                          'ldflags_executable')
       if not ldflags_executable:
         ldflags_executable = GetConfigFlags(config, self.toolset, 'ldflags')
 
@@ -1051,8 +1051,8 @@
       self.ninja.variable('{0}_flags'.format(rule_name),
                           shell.Join(executable_linker_flags))
     elif target_type == 'shared_library':
-      shared_library_linker = FindFirstInstanceOf(
-          abstract.SharedLibraryLinker, toolchain)
+      shared_library_linker = FindFirstInstanceOf(abstract.SharedLibraryLinker,
+                                                  toolchain)
       assert shared_library_linker, (
           'Toolchain must provide shared library linker '
           'for {0} platform.').format(self.toolset)
@@ -1081,12 +1081,11 @@
       extra_bindings.append(('soname', os.path.split(output)[1]))
       extra_bindings.append(('dll', output))
       if '/NOENTRY' not in shared_library_linker_flags:
-        extra_bindings.append(('implibflag',
-                               '/IMPLIB:%s' % output + '.lib'))
+        extra_bindings.append(('implibflag', '/IMPLIB:%s' % output + '.lib'))
 
     else:
-      raise Exception('Target type {0} is not supported for target {1}.'
-                      .format(target_type, spec['target_name']))
+      raise Exception('Target type {0} is not supported for target {1}.'.format(
+          target_type, spec['target_name']))
 
     order_only_deps = set()
 
@@ -1146,15 +1145,13 @@
             self.flavor,
             spec=spec,
             config_name=config_name,
-            gyp_path_to_ninja=self.GypPathToNinja
-        )
+            gyp_path_to_ninja=self.GypPathToNinja)
       else:
         toolchain = GetHostToolchain(
             self.flavor,
             spec=spec,
             config_name=config_name,
-            gyp_path_to_ninja=self.GypPathToNinja
-        )
+            gyp_path_to_ninja=self.GypPathToNinja)
 
       shell = GetShell(self.flavor)
       static_linker = FindFirstInstanceOf(abstract.StaticLinker, toolchain)
@@ -1510,8 +1507,8 @@
     default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
     default_variables.setdefault('SHARED_LIB_DIR',
                                  os.path.join('$!PRODUCT_DIR', 'lib'))
-    default_variables.setdefault('LIB_DIR', os.path.join(
-        '$!PRODUCT_DIR', 'obj'))
+    default_variables.setdefault('LIB_DIR',
+                                 os.path.join('$!PRODUCT_DIR', 'obj'))
 
 
 def ComputeOutputDir(params):
@@ -1549,6 +1546,7 @@
     pass
   return open(path, mode)
 
+
 def MaybeWritePathVariable(ninja, tool, toolset):
   if tool.GetPath():
     ninja.variable('{0}_path'.format(GetNinjaRuleName(tool, toolset)),
@@ -1653,9 +1651,22 @@
           toplevel_build, generator_flags, OpenOutput)
     else:
       gyp.msvs_emulation.GenerateEnvironmentFiles(toplevel_build,
-          generator_flags, OpenOutput)
-    master_ninja.variable('python', sys.executable)
-    master_ninja.newline()
+                                                  generator_flags, OpenOutput)
+
+  # Write python executables to the master ninja.
+  # Let 'python' resolve to what environment is active.
+  # Assume this file is executed using python2.
+  master_ninja.variable('python2', sys.executable)
+  master_ninja.newline()
+
+  # Don't write python3 exectuable until we ensure it's in docker containers.
+  #
+  # python3_executable = 'py -3' if is_windows else 'python3'
+  # cmd = ('{} -c "import os; import sys; '
+  #        'print(os.path.dirname(sys.executable))"').format(python3_executable)
+  # python3_location = subprocess.check_output(cmd, shell=True).strip()
+  # master_ninja.variable('python3', python3_location)
+  # master_ninja.newline()
 
   all_targets = set()
   for build_file in params['build_files']:
@@ -1748,7 +1759,7 @@
   for config in configurations:
     builddir = os.path.join(options.toplevel_dir, 'out', config)
     arguments = ['ninja', '-C', builddir]
-    print 'Building [%s]: %s' % (config, arguments)
+    print('Building [%s]: %s' % (config, arguments))
     subprocess.check_call(arguments)
 
 
@@ -1776,10 +1787,10 @@
         pool = multiprocessing.Pool(len(config_names))
         arglists = []
         for config_name in config_names:
-          arglists.append((target_list, target_dicts, data, params,
-                           config_name))
+          arglists.append(
+              (target_list, target_dicts, data, params, config_name))
           pool.map(CallGenerateOutputForConfig, arglists)
-      except KeyboardInterrupt, e:
+      except KeyboardInterrupt as e:
         pool.terminate()
         raise e
     else:
diff --git a/src/tools/testing/download_from_gcs/clang-format.sha1 b/src/tools/testing/download_from_gcs/clang-format.sha1
new file mode 100644
index 0000000..e6b99ea
--- /dev/null
+++ b/src/tools/testing/download_from_gcs/clang-format.sha1
@@ -0,0 +1 @@
+5349d1954e17f6ccafb6e6663b0f13cdb2bb33c8
diff --git a/src/tools/testing/download_from_gcs/test_dir/clang-format.exe.sha1 b/src/tools/testing/download_from_gcs/test_dir/clang-format.exe.sha1
new file mode 100644
index 0000000..fbb0ae5
--- /dev/null
+++ b/src/tools/testing/download_from_gcs/test_dir/clang-format.exe.sha1
@@ -0,0 +1 @@
+c8455d43d052eb79f65d046c6b02c169857b963b
diff --git a/src/tools/testing/download_from_gcs/test_dir/clang-format.sha1 b/src/tools/testing/download_from_gcs/test_dir/clang-format.sha1
new file mode 100644
index 0000000..8a00b61
--- /dev/null
+++ b/src/tools/testing/download_from_gcs/test_dir/clang-format.sha1
@@ -0,0 +1 @@
+0679b295e2ce2fce7919d1e8d003e497475f24a3
diff --git a/src/v8/BUILD.gn b/src/v8/BUILD.gn
index 11588e9..f70448f 100644
--- a/src/v8/BUILD.gn
+++ b/src/v8/BUILD.gn
@@ -3501,6 +3501,8 @@
     "src/base/platform/semaphore.h",
     "src/base/platform/time.cc",
     "src/base/platform/time.h",
+    "src/base/platform/wrappers.h",
+    "src/base/platform/wrappers_std.cc",
     "src/base/region-allocator.cc",
     "src/base/region-allocator.h",
     "src/base/ring-buffer.h",
diff --git a/src/v8/gypfiles/standalone.gypi b/src/v8/gypfiles/standalone.gypi
index 8f893ce..a2955f8 100644
--- a/src/v8/gypfiles/standalone.gypi
+++ b/src/v8/gypfiles/standalone.gypi
@@ -944,7 +944,6 @@
         ],
         'msvs_settings': {
           'VCCLCompilerTool': {
-            'MinimalRebuild': 'false',
             'BufferSecurityCheck': 'true',
             'EnableFunctionLevelLinking': 'true',
             'RuntimeTypeInfo': 'false',
diff --git a/src/v8/include/v8.h b/src/v8/include/v8.h
index a3cb6dd..ca96c32 100644
--- a/src/v8/include/v8.h
+++ b/src/v8/include/v8.h
@@ -15,11 +15,9 @@
 #ifndef INCLUDE_V8_H_
 #define INCLUDE_V8_H_
 
-#if !V8_OS_STARBOARD
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
-#endif // !V8_OS_STARBOARD
 #include <memory>
 #include <utility>
 #include <vector>
diff --git a/src/v8/src/base/atomicops_internals_portable.h b/src/v8/src/base/atomicops_internals_portable.h
index c2a467c..67c5b51 100644
--- a/src/v8/src/base/atomicops_internals_portable.h
+++ b/src/v8/src/base/atomicops_internals_portable.h
@@ -31,10 +31,6 @@
 
 #include <atomic>
 
-#if defined(V8_OS_STARBOARD)
-#include "starboard/atomic.h"
-#endif
-
 #include "src/base/build_config.h"
 #include "src/base/macros.h"
 
@@ -45,9 +41,6 @@
 // atomicops.h.
 
 inline void SeqCst_MemoryFence() {
-#if defined(V8_OS_STARBOARD)
-  SbAtomicMemoryBarrier();
-#else
 #if defined(__GLIBCXX__)
   // Work around libstdc++ bug 51038 where atomic_thread_fence was declared but
   // not defined, leading to the linker complaining about undefined references.
@@ -55,7 +48,6 @@
 #else
   std::atomic_thread_fence(std::memory_order_seq_cst);
 #endif  //  defined(__GLIBCXX__)
-#endif  //  defined(V8_OS_STARBOARD)
 }
 
 inline Atomic16 Relaxed_CompareAndSwap(volatile Atomic16* ptr,
@@ -67,82 +59,50 @@
 
 inline Atomic32 Relaxed_CompareAndSwap(volatile Atomic32* ptr,
                                        Atomic32 old_value, Atomic32 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value);
-#else
   __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                               __ATOMIC_RELAXED, __ATOMIC_RELAXED);
   return old_value;
-#endif
 }
 
 inline Atomic32 Relaxed_AtomicExchange(volatile Atomic32* ptr,
                                        Atomic32 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Exchange(ptr, new_value);
-#else
   return __atomic_exchange_n(ptr, new_value, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic32 Relaxed_AtomicIncrement(volatile Atomic32* ptr,
                                         Atomic32 increment) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Increment(ptr, increment);
-#else
   return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
                                         Atomic32 increment) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicBarrier_Increment(ptr, increment);
-#else
   return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_SEQ_CST);
-#endif
 }
 
 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
                                        Atomic32 old_value, Atomic32 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicAcquire_CompareAndSwap(ptr, old_value, new_value);
-#else
   __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                               __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
   return old_value;
-#endif
 }
 
 inline Atomic8 Release_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value,
                                       Atomic8 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicRelease_CompareAndSwap8(ptr, old_value, new_value);
-#else
   bool result = __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                                             __ATOMIC_RELEASE, __ATOMIC_RELAXED);
   USE(result);  // Make gcc compiler happy.
   return old_value;
-#endif
 }
 
 inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
                                        Atomic32 old_value, Atomic32 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicRelease_CompareAndSwap(ptr, old_value, new_value);
-#else
   __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                               __ATOMIC_RELEASE, __ATOMIC_RELAXED);
   return old_value;
-#endif
 }
 
 inline void Relaxed_Store(volatile Atomic8* ptr, Atomic8 value) {
-#if defined(V8_OS_STARBOARD)
-  SbAtomicNoBarrier_Store8(ptr, value);
-#else
   __atomic_store_n(ptr, value, __ATOMIC_RELAXED);
-#endif
 }
 
 inline void Relaxed_Store(volatile Atomic16* ptr, Atomic16 value) {
@@ -150,27 +110,15 @@
 }
 
 inline void Relaxed_Store(volatile Atomic32* ptr, Atomic32 value) {
-#if defined(V8_OS_STARBOARD)
-  SbAtomicNoBarrier_Store(ptr, value);
-#else
   __atomic_store_n(ptr, value, __ATOMIC_RELAXED);
-#endif
 }
 
 inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
-#if defined(V8_OS_STARBOARD)
-  SbAtomicRelease_Store(ptr, value);
-#else
   __atomic_store_n(ptr, value, __ATOMIC_RELEASE);
-#endif
 }
 
 inline Atomic8 Relaxed_Load(volatile const Atomic8* ptr) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Load8(ptr);
-#else
   return __atomic_load_n(ptr, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic16 Relaxed_Load(volatile const Atomic16* ptr) {
@@ -178,113 +126,65 @@
 }
 
 inline Atomic32 Relaxed_Load(volatile const Atomic32* ptr) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Load(ptr);
-#else
   return __atomic_load_n(ptr, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicAcquire_Load(ptr);
-#else
   return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
-#endif
 }
 
 #if defined(V8_HOST_ARCH_64_BIT)
 
 inline Atomic64 Relaxed_CompareAndSwap(volatile Atomic64* ptr,
                                        Atomic64 old_value, Atomic64 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value);
-#else
   __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                               __ATOMIC_RELAXED, __ATOMIC_RELAXED);
   return old_value;
-#endif
 }
 
 inline Atomic64 Relaxed_AtomicExchange(volatile Atomic64* ptr,
                                        Atomic64 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Exchange64(ptr, new_value);
-#else
   return __atomic_exchange_n(ptr, new_value, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic64 Relaxed_AtomicIncrement(volatile Atomic64* ptr,
                                         Atomic64 increment) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Increment64(ptr, increment);
-#else
   return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
                                         Atomic64 increment) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicBarrier_Increment64(ptr, increment);
-#else
   return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_SEQ_CST);
-#endif
 }
 
 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
                                        Atomic64 old_value, Atomic64 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicAcquire_CompareAndSwap64(ptr, old_value, new_value);
-#else
   __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                               __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
   return old_value;
-#endif
 }
 
 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
                                        Atomic64 old_value, Atomic64 new_value) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicRelease_CompareAndSwap64(ptr, old_value, new_value);
-#else
   __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
                               __ATOMIC_RELEASE, __ATOMIC_RELAXED);
   return old_value;
-#endif
 }
 
 inline void Relaxed_Store(volatile Atomic64* ptr, Atomic64 value) {
-#if defined(V8_OS_STARBOARD)
-  SbAtomicNoBarrier_Store64(ptr, value);
-#else
   __atomic_store_n(ptr, value, __ATOMIC_RELAXED);
-#endif
 }
 
 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
-#if defined(V8_OS_STARBOARD)
-  SbAtomicRelease_Store64(ptr, value);
-#else
   __atomic_store_n(ptr, value, __ATOMIC_RELEASE);
-#endif
 }
 
 inline Atomic64 Relaxed_Load(volatile const Atomic64* ptr) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicNoBarrier_Load64(ptr);
-#else
   return __atomic_load_n(ptr, __ATOMIC_RELAXED);
-#endif
 }
 
 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
-#if defined(V8_OS_STARBOARD)
-  return SbAtomicAcquire_Load64(ptr);
-#else
   return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
-#endif
 }
 
 #endif  // defined(V8_HOST_ARCH_64_BIT)
diff --git a/src/v8/src/base/hashmap.h b/src/v8/src/base/hashmap.h
index ae0cdd4..ab2811f 100644
--- a/src/v8/src/base/hashmap.h
+++ b/src/v8/src/base/hashmap.h
@@ -15,27 +15,22 @@
 #include "src/base/hashmap-entry.h"
 #include "src/base/logging.h"
 
-#if V8_OS_STARBOARD
-
-#include "starboard/common/string.h"
+#if defined(V8_OS_STARBOARD)
 #include "starboard/memory.h"
-
-#define malloc(x) SbMemoryAllocate(x)
-#define realloc(x, y) SbMemoryReallocate(x, y)
-#define free(x) SbMemoryDeallocate(x)
-#define memcpy(x, y, z) SbMemoryCopy(x, y, z)
-#define calloc(x, y) SbMemoryCalloc(x, y)
-#define strdup(s) SbStringDuplicate(s)
-
-#endif  // V8_OS_STARBOARD
+#endif
 
 namespace v8 {
 namespace base {
 
 class DefaultAllocationPolicy {
  public:
+#if defined(V8_OS_STARBOARD)
+  V8_INLINE void* New(size_t size) { return SbMemoryAllocate(size); }
+  V8_INLINE static void Delete(void* p) { SbMemoryDeallocate(p); }
+#else
   V8_INLINE void* New(size_t size) { return malloc(size); }
   V8_INLINE static void Delete(void* p) { free(p); }
+#endif
 };
 
 template <typename Key, typename Value, class MatchFun, class AllocationPolicy>
@@ -510,13 +505,4 @@
 }  // namespace base
 }  // namespace v8
 
-#if V8_OS_STARBOARD
-#undef malloc
-#undef realloc
-#undef free
-#undef memcpy
-#undef calloc
-#undef strdup
-#endif
-
 #endif  // V8_BASE_HASHMAP_H_
diff --git a/src/v8/src/base/logging.cc b/src/v8/src/base/logging.cc
index 1f8035b..c7ffdaa 100644
--- a/src/v8/src/base/logging.cc
+++ b/src/v8/src/base/logging.cc
@@ -5,15 +5,17 @@
 #include "src/base/logging.h"
 
 #include <cctype>
-#if !V8_OS_STARBOARD
 #include <cstdarg>
 #include <cstdio>
 #include <cstdlib>
-#endif
 
 #include "src/base/debug/stack_trace.h"
 #include "src/base/platform/platform.h"
 
+#if defined(V8_OS_STARBOARD)
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace base {
 
@@ -165,10 +167,8 @@
   FailureMessage message(format, arguments);
   va_end(arguments);
 
-#if !V8_OS_STARBOARD
   fflush(stdout);
   fflush(stderr);
-#endif
   // Print the formatted message to stdout without cropping the output.
   v8::base::OS::PrintError("\n\n#\n# Fatal error in %s, line %d\n# ", file,
                            line);
@@ -182,9 +182,7 @@
 
   if (v8::base::g_print_stack_trace) v8::base::g_print_stack_trace();
 
-#if !V8_OS_STARBOARD
   fflush(stderr);
-#endif
   v8::base::OS::Abort();
 }
 
diff --git a/src/v8/src/base/platform/platform-starboard.cc b/src/v8/src/base/platform/platform-starboard.cc
index 0caa0d2..711fca5 100644
--- a/src/v8/src/base/platform/platform-starboard.cc
+++ b/src/v8/src/base/platform/platform-starboard.cc
@@ -12,6 +12,7 @@
 #include "src/base/platform/time.h"
 #include "src/base/timezone-cache.h"
 #include "src/base/utils/random-number-generator.h"
+#include "starboard/client_porting/eztime/eztime.h"
 #include "starboard/common/condition_variable.h"
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
@@ -149,37 +150,6 @@
   return result;
 }
 
-// The following code was taken from old v8 to deal with rounding up pointers.
-namespace {
-// Compute the 0-relative offset of some absolute value x of type T.
-// This allows conversion of Addresses and integral types into
-// 0-relative int offsets.
-template <typename T>
-constexpr inline intptr_t OffsetFrom(T x) {
-  return x - static_cast<T>(0);
-}
-
-// Compute the absolute value of type T for some 0-relative offset x.
-// This allows conversion of 0-relative int offsets into Addresses and
-// integral types.
-template <typename T>
-constexpr inline T AddressFrom(intptr_t x) {
-  return static_cast<T>(static_cast<T>(0) + x);
-}
-
-template <typename T>
-inline T RoundDown(T x, intptr_t m) {
-  // m must be a power of two.
-  DCHECK(m != 0 && ((m & (m - 1)) == 0));
-  return AddressFrom<T>(OffsetFrom(x) & -m);
-}
-
-template <typename T>
-inline T RoundUpOld(T x, intptr_t m) {
-  return RoundDown<T>(static_cast<T>(x + m - 1), m);
-}
-}  // namespace
-
 // static
 void* OS::Allocate(void* address, size_t size, size_t alignment,
                    MemoryPermission access) {
@@ -195,7 +165,8 @@
 
   // Unmap memory allocated before the aligned base address.
   uint8_t* base = static_cast<uint8_t*>(result);
-  uint8_t* aligned_base = RoundUpOld(base, alignment);
+  uint8_t* aligned_base = reinterpret_cast<uint8_t*>(
+      RoundUp(reinterpret_cast<uintptr_t>(base), alignment));
   if (aligned_base != base) {
     DCHECK_LT(base, aligned_base);
     size_t prefix_size = static_cast<size_t>(aligned_base - base);
@@ -457,7 +428,6 @@
 
 class StarboardTimezoneCache : public TimezoneCache {
  public:
-  double DaylightSavingsOffset(double time_ms) override { return 0.0; }
   void Clear(TimeZoneDetection time_zone_detection) override {}
   ~StarboardTimezoneCache() override {}
 
@@ -471,7 +441,18 @@
     return SbTimeZoneGetName();
   }
   double LocalTimeOffset(double time_ms, bool is_utc) override {
-    return SbTimeZoneGetCurrent() * 60000.0;
+    // SbTimeZOneGetCurrent returns an offset west of Greenwich, which has the
+    // opposite sign V8 expects.
+    // The starboard function returns offset in minutes. We convert to return
+    // value in milliseconds.
+    return SbTimeZoneGetCurrent() * 60.0 * msPerSecond * (-1);
+  }
+  double DaylightSavingsOffset(double time_ms) override {
+    EzTimeValue value = EzTimeValueFromSbTime(SbTimeGetNow());
+    EzTimeExploded ez_exploded;
+    bool result = EzTimeValueExplode(&value, kEzTimeZoneLocal, &ez_exploded,
+                                     NULL);
+    return ez_exploded.tm_isdst > 0 ? 3600 * msPerSecond : 0;
   }
 
   ~StarboardDefaultTimezoneCache() override {}
diff --git a/src/v8/src/base/platform/wrappers.h b/src/v8/src/base/platform/wrappers.h
new file mode 100644
index 0000000..521b06e
--- /dev/null
+++ b/src/v8/src/base/platform/wrappers.h
@@ -0,0 +1,31 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_BASE_PLATFORM_WRAPPERS_H_
+#define V8_BASE_PLATFORM_WRAPPERS_H_
+
+#include <stddef.h>
+#include <stdio.h>
+
+namespace v8 {
+namespace base {
+
+void* Malloc(size_t size);
+
+void* Realloc(void* memory, size_t size);
+
+void Free(void* memory);
+
+void* Calloc(size_t count, size_t size);
+
+void* Memcpy(void* dest, const void* source, size_t count);
+
+FILE* Fopen(const char* filename, const char* mode);
+
+int Fclose(FILE* stream);
+
+}  // namespace base
+}  // namespace v8
+
+#endif  // V8_BASE_PLATFORM_WRAPPERS_H_
diff --git a/src/v8/src/base/platform/wrappers_starboard.cc b/src/v8/src/base/platform/wrappers_starboard.cc
new file mode 100644
index 0000000..199e753
--- /dev/null
+++ b/src/v8/src/base/platform/wrappers_starboard.cc
@@ -0,0 +1,31 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "starboard/memory.h"
+
+#include "src/base/platform/wrappers.h"
+
+namespace v8 {
+namespace base {
+
+void* Malloc(size_t size) { return SbMemoryAlloc(size); }
+
+void* Realloc(void* memory, size_t size) {
+  return SbMemoryReallocate(memory, size);
+}
+
+void Free(void* memory) { return SbMemoryDeallocate(memory); }
+
+void* Calloc(size_t count, size_t size) { return SbMemoryCalloc(count, size); }
+
+void* Memcpy(void* dest, const void* source, size_t count) {
+  return SbMemoryCopy(dest, source, count);
+}
+
+FILE* Fopen(const char* filename, const char* mode) { return NULL; }
+
+int Fclose(FILE* stream) { return -1; }
+
+}  // namespace base
+}  // namespace v8
diff --git a/src/v8/src/base/platform/wrappers_std.cc b/src/v8/src/base/platform/wrappers_std.cc
new file mode 100644
index 0000000..27ddbd6
--- /dev/null
+++ b/src/v8/src/base/platform/wrappers_std.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/base/platform/wrappers.h"
+
+namespace v8 {
+namespace base {
+
+void* Malloc(size_t size) { return malloc(size); }
+
+void* Realloc(void* memory, size_t size) { return realloc(memory, size); }
+
+void Free(void* memory) { return free(memory); }
+
+void* Calloc(size_t count, size_t size) { return calloc(count, size); }
+
+void* Memcpy(void* dest, const void* source, size_t count) {
+  return memcpy(dest, source, count);
+}
+
+FILE* Fopen(const char* filename, const char* mode) {
+  return fopen(filename, mode);
+}
+
+int Fclose(FILE* stream) { return fclose(stream); }
+
+}  // namespace base
+}  // namespace v8
diff --git a/src/v8/src/base/small-vector.h b/src/v8/src/base/small-vector.h
index 8542808..0350ade 100644
--- a/src/v8/src/base/small-vector.h
+++ b/src/v8/src/base/small-vector.h
@@ -12,18 +12,19 @@
 #include "src/base/bits.h"
 #include "src/base/macros.h"
 
-#if defined(STARBOARD)
-// These common Starboard API replacements are not needed for evergreen but
-// some builds can not find definitions for free/malloc above Starboard.
+#if defined(V8_OS_STARBOARD)
 #include "starboard/memory.h"
-#include "starboard/string.h"
 
-#define malloc(x) SbMemoryAllocate(x)
-#define realloc(x, y) SbMemoryReallocate(x, y)
-#define free(x) SbMemoryDeallocate(x)
-#define memcpy(x, y, z) SbMemoryCopy(x, y, z)
-#define calloc(x, y) SbMemoryCalloc(x, y)
-#endif
+struct AllocationPolicy {
+  V8_INLINE static void* Malloc(size_t size) { return SbMemoryAllocate(size); }
+  V8_INLINE static void Free(void* p) { SbMemoryDeallocate(p); }
+};
+#else
+struct AllocationPolicy {
+  V8_INLINE static void* Malloc(size_t size) { return malloc(size); }
+  V8_INLINE static void Free(void* p) { return free(p); }
+};
+#endif  // V8_OS_STARBOARD
 
 namespace v8 {
 namespace base {
@@ -50,7 +51,7 @@
   }
 
   ~SmallVector() {
-    if (is_big()) free(begin_);
+    if (is_big()) AllocationPolicy::Free(begin_);
   }
 
   SmallVector& operator=(const SmallVector& other) V8_NOEXCEPT {
@@ -58,8 +59,8 @@
     size_t other_size = other.size();
     if (capacity() < other_size) {
       // Create large-enough heap-allocated storage.
-      if (is_big()) free(begin_);
-      begin_ = reinterpret_cast<T*>(malloc(sizeof(T) * other_size));
+      if (is_big()) AllocationPolicy::Free(begin_);
+      begin_ = reinterpret_cast<T*>((sizeof(T) * other_size));
       end_of_storage_ = begin_ + other_size;
     }
     memcpy(begin_, other.begin_, sizeof(T) * other_size);
@@ -70,7 +71,7 @@
   SmallVector& operator=(SmallVector&& other) V8_NOEXCEPT {
     if (this == &other) return *this;
     if (other.is_big()) {
-      if (is_big()) free(begin_);
+      if (is_big()) AllocationPolicy::Free(begin_);
       begin_ = other.begin_;
       end_ = other.end_;
       end_of_storage_ = other.end_of_storage_;
@@ -164,9 +165,9 @@
     size_t in_use = end_ - begin_;
     size_t new_capacity =
         base::bits::RoundUpToPowerOfTwo(std::max(min_capacity, 2 * capacity()));
-    T* new_storage = reinterpret_cast<T*>(malloc(sizeof(T) * new_capacity));
+    T* new_storage = reinterpret_cast<T*>(AllocationPolicy::Malloc(sizeof(T) * new_capacity));
     memcpy(new_storage, begin_, sizeof(T) * in_use);
-    if (is_big()) free(begin_);
+    if (is_big()) AllocationPolicy::Free(begin_);
     begin_ = new_storage;
     end_ = new_storage + in_use;
     end_of_storage_ = new_storage + new_capacity;
@@ -184,12 +185,4 @@
 }  // namespace base
 }  // namespace v8
 
-#if defined(STARBOARD)
-#undef malloc
-#undef realloc
-#undef free
-#undef memcpy
-#undef calloc
-#endif
-
 #endif  // V8_BASE_SMALL_VECTOR_H_
diff --git a/src/v8/src/builtins/builtins.cc b/src/v8/src/builtins/builtins.cc
index 3eb939f..c222ca2 100644
--- a/src/v8/src/builtins/builtins.cc
+++ b/src/v8/src/builtins/builtins.cc
@@ -20,6 +20,10 @@
 #include "src/snapshot/embedded/embedded-data.h"
 #include "src/utils/ostreams.h"
 
+#if defined(V8_OS_STARBOARD)
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace internal {
 
@@ -213,13 +217,8 @@
     const char* builtin_name = name(i);
     const char* kind = KindNameOf(i);
     Code code = builtin(i);
-#if defined(V8_OS_STARBOARD)
-    PrintF((FILE*)nullptr, "%s Builtin, %s, %d\n", kind, builtin_name,
-           code.InstructionSize());
-#else
     PrintF(stdout, "%s Builtin, %s, %d\n", kind, builtin_name,
            code.InstructionSize());
-#endif
   }
 }
 
diff --git a/src/v8/src/compiler/graph-reducer.cc b/src/v8/src/compiler/graph-reducer.cc
index 780c04c..9a0dea6 100644
--- a/src/v8/src/compiler/graph-reducer.cc
+++ b/src/v8/src/compiler/graph-reducer.cc
@@ -93,24 +93,20 @@
         // {replacement} == {node} represents an in-place reduction. Rerun
         // all the other reducers for this node, as now there may be more
         // opportunities for reduction.
-#ifndef V8_OS_STARBOARD
         if (FLAG_trace_turbo_reduction) {
           StdoutStream{} << "- In-place update of " << *node << " by reducer "
                          << (*i)->reducer_name() << std::endl;
         }
-#endif
         skip = i;
         i = reducers_.begin();
         continue;
       } else {
         // {node} was replaced by another node.
-#ifndef V8_OS_STARBOARD
         if (FLAG_trace_turbo_reduction) {
           StdoutStream{} << "- Replacement of " << *node << " with "
                          << *(reduction.replacement()) << " by reducer "
                          << (*i)->reducer_name() << std::endl;
         }
-#endif
         return reduction;
       }
     }
diff --git a/src/v8/src/compiler/node.cc b/src/v8/src/compiler/node.cc
index e447b9c..bc8d77c 100644
--- a/src/v8/src/compiler/node.cc
+++ b/src/v8/src/compiler/node.cc
@@ -4,6 +4,10 @@
 
 #include "src/compiler/node.h"
 
+#if defined(V8_OS_STARBOARD)
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace internal {
 namespace compiler {
@@ -370,9 +374,7 @@
 #if DEBUG
 void Node::Verify() {
   // Check basic sanity of input data structures.
-#ifndef V8_OS_STARBOARD
   fflush(stdout);
-#endif
   int count = this->InputCount();
   // Avoid quadratic explosion for mega nodes; only verify if the input
   // count is less than 200 or is a round number of 100s.
diff --git a/src/v8/src/diagnostics/objects-printer.cc b/src/v8/src/diagnostics/objects-printer.cc
index 57db1af..3e60c65 100644
--- a/src/v8/src/diagnostics/objects-printer.cc
+++ b/src/v8/src/diagnostics/objects-printer.cc
@@ -71,7 +71,7 @@
 
 #if defined(STARBOARD)
 #include "starboard/common/log.h"
-#define printf(format) SbLogRaw(format)
+#include "src/poems.h"
 #endif
 
 namespace v8 {
diff --git a/src/v8/src/execution/isolate.cc b/src/v8/src/execution/isolate.cc
index 409c89c..58cc241 100644
--- a/src/v8/src/execution/isolate.cc
+++ b/src/v8/src/execution/isolate.cc
@@ -4,14 +4,10 @@
 
 #include "src/execution/isolate.h"
 
-#if !V8_OS_STARBOARD
 #include <stdlib.h>
-#endif  // V8_OS_STARBOARD
 
 #include <atomic>
-#if !defined(V8_OS_STARBOARD)
 #include <fstream>  // NOLINT(readability/streams)
-#endif
 #include <memory>
 #include <sstream>
 #include <unordered_map>
diff --git a/src/v8/src/flags/flags.h b/src/v8/src/flags/flags.h
index 3fe8b1b..5f0a6d0 100644
--- a/src/v8/src/flags/flags.h
+++ b/src/v8/src/flags/flags.h
@@ -8,9 +8,7 @@
 #include <vector>
 
 #include "src/common/globals.h"
-#if !defined(DISABLE_WASM_STARBOARD)
 #include "src/wasm/wasm-limits.h"
-#endif
 
 namespace v8 {
 namespace internal {
diff --git a/src/v8/src/heap/heap.cc b/src/v8/src/heap/heap.cc
index 04c4113..99539a9 100644
--- a/src/v8/src/heap/heap.cc
+++ b/src/v8/src/heap/heap.cc
@@ -79,6 +79,10 @@
 // Has to be the last include (doesn't have include guards):
 #include "src/objects/object-macros.h"
 
+#if defined(V8_OS_STARBOARD)
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace internal {
 
@@ -3777,9 +3781,7 @@
 
 void Heap::Print() {
   if (!HasBeenSetUp()) return;
-#ifndef V8_OS_STARBOARD
   isolate()->PrintStack(stdout);
-#endif
 
   for (SpaceIterator it(this); it.HasNext();) {
     it.Next()->Print();
diff --git a/src/v8/src/heap/heap.h b/src/v8/src/heap/heap.h
index 898bc07..81f2b0d 100644
--- a/src/v8/src/heap/heap.h
+++ b/src/v8/src/heap/heap.h
@@ -29,9 +29,7 @@
 #include "src/objects/visitors.h"
 #include "src/roots/roots.h"
 #include "src/utils/allocation.h"
-#if !defined(STARBOARD)
 #include "testing/gtest/include/gtest/gtest_prod.h"
-#endif
 
 namespace v8 {
 
diff --git a/src/v8/src/init/v8.cc b/src/v8/src/init/v8.cc
index 9175e2d..8edd816 100644
--- a/src/v8/src/init/v8.cc
+++ b/src/v8/src/init/v8.cc
@@ -6,10 +6,6 @@
 
 #include <fstream>
 
-#if V8_OS_STARBOARD
-#include "src/poems.h"
-#endif
-
 #include "src/api/api.h"
 #include "src/base/atomicops.h"
 #include "src/base/once.h"
@@ -32,6 +28,10 @@
 #include "src/tracing/tracing-category-observer.h"
 #include "src/wasm/wasm-engine.h"
 
+#if V8_OS_STARBOARD
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace internal {
 
@@ -74,7 +74,7 @@
     FLAG_max_semi_space_size = 1;
   }
 
-#if !defined(DISABLE_GRAPHS_STARBOARD)
+#if !defined(V8_OS_STARBOARD)
   if (FLAG_trace_turbo) {
     // Create an empty file shared by the process (e.g. the wasm engine).
     std::ofstream(Isolate::GetTurboCfgFileName(nullptr).c_str(),
diff --git a/src/v8/src/logging/log-utils.cc b/src/v8/src/logging/log-utils.cc
index 934afdd..8047329 100644
--- a/src/v8/src/logging/log-utils.cc
+++ b/src/v8/src/logging/log-utils.cc
@@ -4,10 +4,6 @@
 
 #include "src/logging/log-utils.h"
 
-#if V8_OS_STARBOARD
-#include "starboard/common/log.h"
-#endif
-
 #include "src/base/platform/platform.h"
 #include "src/common/assert-scope.h"
 #include "src/objects/objects-inl.h"
@@ -16,6 +12,10 @@
 #include "src/utils/vector.h"
 #include "src/utils/version.h"
 
+#if V8_OS_STARBOARD
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace internal {
 
@@ -27,10 +27,8 @@
   // If we're logging anything, we need to open the log file.
   if (!Log::InitLogAtStart()) {
     return nullptr;
-#ifndef V8_OS_STARBOARD
   } else if (strcmp(file_name, kLogToConsole) == 0) {
     return stdout;
-#endif
   } else if (strcmp(file_name, kLogToTemporaryFile) == 0) {
     return base::OS::OpenTemporaryFile();
   } else {
@@ -41,11 +39,7 @@
 Log::Log(Logger* logger, const char* file_name)
     : is_stopped_(false),
       output_handle_(Log::CreateOutputHandle(file_name)),
-#if defined(V8_OS_STARBOARD)
-      os_(output_handle_),
-#else
       os_(output_handle_ == nullptr ? stdout : output_handle_),
-#endif
       format_buffer_(NewArray<char>(kMessageBufferSize)),
       logger_(logger) {
   // --log-all enables all the log flags.
diff --git a/src/v8/src/logging/log-utils.h b/src/v8/src/logging/log-utils.h
index e6c34a8..bc5b09d 100644
--- a/src/v8/src/logging/log-utils.h
+++ b/src/v8/src/logging/log-utils.h
@@ -5,9 +5,7 @@
 #ifndef V8_LOGGING_LOG_UTILS_H_
 #define V8_LOGGING_LOG_UTILS_H_
 
-#if !V8_OS_STARBOARD
 #include <stdio.h>
-#endif
 
 #include <cstdarg>
 
diff --git a/src/v8/src/poems.h b/src/v8/src/poems.h
index fec8c0a..4ac3cae 100644
--- a/src/v8/src/poems.h
+++ b/src/v8/src/poems.h
@@ -12,67 +12,28 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Specialized poems for porting V8 on top of Starboard.  It would have been
+// Specialized defines for porting V8 on top of Starboard.  It would have been
 // nice to have been able to use the shared poems, however they are too
 // aggressive for V8 (such as grabbing identifiers that will come after std::).
 
 #ifndef V8_SRC_POEMS_H_
 #define V8_SRC_POEMS_H_
 
-#if !defined(STARBOARD)
-#error "Including V8 poems without STARBOARD defined."
-#endif
-
 #if !defined(V8_OS_STARBOARD)
 #error "Including V8 poems without V8_OS_STARBOARD defined."
 #endif
 
 #include "starboard/memory.h"
 #include "starboard/string.h"
-
-#ifdef __cplusplus
-
-// Declaring the following functions static inline is not necessary in C++. See:
-// http://stackoverflow.com/questions/10847176/should-i-define-static-inline-methods-in-header-file
-
-// Finds the first occurrence of |character| in |str|, returning a pointer to
-// the found character in the given string, or NULL if not found.
-// Meant to be a drop-in replacement for strchr
-inline char* PoemFindCharacterInString(char* str, int character) {
-  const char* const_str = static_cast<const char*>(str);
-  const char c = static_cast<char>(character);
-  return const_cast<char*>(SbStringFindCharacter(const_str, c));
-}
-
-// Finds the first occurrence of |character| in |str|, returning a pointer to
-// the found character in the given string, or NULL if not found.
-// Meant to be a drop-in replacement for strchr
-inline const char* PoemFindCharacterInString(const char* str, int character) {
-  const char c = static_cast<char>(character);
-  return SbStringFindCharacter(str, c);
-}
-
-#else
-
-// Finds the first occurrence of |character| in |str|, returning a pointer to
-// the found character in the given string, or NULL if not found.
-// Meant to be a drop-in replacement for strchr
-static SB_C_INLINE char* PoemFindCharacterInString(const char* str,
-                                                   int character) {
-  // C-style cast used for C code
-  return (char*)(SbStringFindCharacter(str, character));
-}
-#endif
+#include "starboard/common/log.h"
 
 #define malloc(x) SbMemoryAllocate(x)
 #define realloc(x, y) SbMemoryReallocate(x, y)
 #define free(x) SbMemoryDeallocate(x)
-#define memcpy(x, y, z) SbMemoryCopy(x, y, z)
 #define calloc(x, y) SbMemoryCalloc(x, y)
 #define strdup(s) SbStringDuplicate(s)
-#define snprintf(a, b, c, ...) SbStringFormatF(a, b, c, __VA_ARGS__)
-#define strchr(s, c) PoemFindCharacterInString(s, c)
-#define memchr(s, c, n) SbMemoryFindByte(s, c, n)
+#define printf(x) SbLogRaw(x)
+#define __builtin_abort SbSystemBreakIntoDebugger
 
 // No-ops for now
 #define fopen(x)
@@ -85,6 +46,13 @@
 #define ftell(x) -1L
 #define puts(x)
 #define fputs(x)
+#define fprintf(x, y, z)
 
+static FILE* null_file_ptr = nullptr;
+#undef stderr
+#define stderr null_file_ptr
+#undef stdout
+#define stdout null_file_ptr
+#define fflush(x)
 
 #endif  // V8_SRC_POEMS_H_
diff --git a/src/v8/src/runtime/runtime-atomics.cc b/src/v8/src/runtime/runtime-atomics.cc
index 3898a26..a3a75fa 100644
--- a/src/v8/src/runtime/runtime-atomics.cc
+++ b/src/v8/src/runtime/runtime-atomics.cc
@@ -11,10 +11,6 @@
 #include "src/objects/js-array-buffer-inl.h"
 #include "src/runtime/runtime-utils.h"
 
-#if defined(V8_OS_STARBOARD)
-#include "starboard/common/log.h"
-#endif  // V8_OS_STARBOARD
-
 // Implement Atomic accesses to SharedArrayBuffers as defined in the
 // SharedArrayBuffer draft spec, found here
 // https://github.com/tc39/ecmascript_sharedmem
@@ -32,43 +28,43 @@
 
 template <typename T>
 inline T ExchangeSeqCst(T* p, T value) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
 template <typename T>
 inline T CompareExchangeSeqCst(T* p, T oldval, T newval) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
 template <typename T>
 inline T AddSeqCst(T* p, T value) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
 template <typename T>
 inline T SubSeqCst(T* p, T value) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
 template <typename T>
 inline T AndSeqCst(T* p, T value) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
 template <typename T>
 inline T OrSeqCst(T* p, T value) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
 template <typename T>
 inline T XorSeqCst(T* p, T value) {
-  SB_NOTREACHED();
+  CHECK(false);
   return 0;
 }
 
diff --git a/src/v8/src/runtime/runtime-test.cc b/src/v8/src/runtime/runtime-test.cc
index f0caaaa..e605c74 100644
--- a/src/v8/src/runtime/runtime-test.cc
+++ b/src/v8/src/runtime/runtime-test.cc
@@ -35,6 +35,10 @@
 #include "src/wasm/wasm-objects-inl.h"
 #include "src/wasm/wasm-serialization.h"
 
+#if defined(V8_OS_STARBOARD)
+#include "src/poems.h"
+#endif
+
 namespace v8 {
 namespace internal {
 
diff --git a/src/v8/src/snapshot/embedded/embedded-file-writer.h b/src/v8/src/snapshot/embedded/embedded-file-writer.h
index b886e42..c26465a 100644
--- a/src/v8/src/snapshot/embedded/embedded-file-writer.h
+++ b/src/v8/src/snapshot/embedded/embedded-file-writer.h
@@ -6,9 +6,7 @@
 #define V8_SNAPSHOT_EMBEDDED_EMBEDDED_FILE_WRITER_H_
 
 #include <cinttypes>
-#if !defined(V8_OS_STARBOARD)
 #include <cstdio>
-#endif
 #include <cstring>
 
 #include "src/common/globals.h"
diff --git a/src/v8/src/snapshot/embedded/platform-embedded-file-writer-base.h b/src/v8/src/snapshot/embedded/platform-embedded-file-writer-base.h
index f9416c2..b0d1714 100644
--- a/src/v8/src/snapshot/embedded/platform-embedded-file-writer-base.h
+++ b/src/v8/src/snapshot/embedded/platform-embedded-file-writer-base.h
@@ -6,9 +6,7 @@
 #define V8_SNAPSHOT_EMBEDDED_PLATFORM_EMBEDDED_FILE_WRITER_BASE_H_
 
 #include <cinttypes>
-#if !defined(V8_OS_STARBOARD)
 #include <cstdio>  // For FILE.
-#endif
 #include <memory>
 
 namespace v8 {
diff --git a/src/v8/src/trap-handler/handler-outside.cc b/src/v8/src/trap-handler/handler-outside.cc
index f87c1ff..55e3c6f 100644
--- a/src/v8/src/trap-handler/handler-outside.cc
+++ b/src/v8/src/trap-handler/handler-outside.cc
@@ -19,11 +19,6 @@
 //
 // For the code that runs in the trap handler itself, see handler-inside.cc.
 
-#if defined(V8_OS_STARBOARD)
-#include "starboard/system.h"
-#define __builtin_abort SbSystemBreakIntoDebugger
-#endif
-
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -39,11 +34,6 @@
 #include "src/poems.h"
 #endif
 
-#if defined(V8_OS_STARBOARD)
-#include "starboard/system.h"
-#define abort SbSystemBreakIntoDebugger
-#endif
-
 namespace {
 size_t gNextCodeObject = 0;
 
diff --git a/src/v8/src/utils/allocation.cc b/src/v8/src/utils/allocation.cc
index a1e8468..104b176 100644
--- a/src/v8/src/utils/allocation.cc
+++ b/src/v8/src/utils/allocation.cc
@@ -148,6 +148,8 @@
 #elif V8_LIBC_BIONIC
   // Using free is not correct in general, but for V8_LIBC_BIONIC it is.
   free(ptr);
+#elif V8_OS_STARBOARD
+  SbMemoryDeallocateAligned(ptr);
 #else
   free(ptr);
 #endif
diff --git a/src/v8/src/utils/ostreams.h b/src/v8/src/utils/ostreams.h
index 5ad1018..620b51e 100644
--- a/src/v8/src/utils/ostreams.h
+++ b/src/v8/src/utils/ostreams.h
@@ -5,11 +5,9 @@
 #ifndef V8_UTILS_OSTREAMS_H_
 #define V8_UTILS_OSTREAMS_H_
 
-#if !defined(V8_OS_STARBOARD)
 #include <cstddef>
 #include <cstdio>
 #include <cstring>
-#endif
 #include <ostream>  // NOLINT
 #include <streambuf>
 
diff --git a/src/v8/test/unittests/profiler/strings-storage-unittest.cc b/src/v8/test/unittests/profiler/strings-storage-unittest.cc
index 3b561ba..31225f4 100644
--- a/src/v8/test/unittests/profiler/strings-storage-unittest.cc
+++ b/src/v8/test/unittests/profiler/strings-storage-unittest.cc
@@ -4,9 +4,7 @@
 
 #include "src/profiler/strings-storage.h"
 
-#if !V8_OS_STARBOARD
 #include <cstdio>
-#endif
 
 #include "test/unittests/test-utils.h"
 #include "testing/gtest/include/gtest/gtest.h"