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