Import Cobalt 12.84472
Change-Id: I5b2e0e0f08b03fa9a5e428a4fb0618e00718d3eb
diff --git a/src/base/allocator/allocator.gyp b/src/base/allocator/allocator.gyp
index 7ab45bd..3c62f47 100644
--- a/src/base/allocator/allocator.gyp
+++ b/src/base/allocator/allocator.gyp
@@ -30,18 +30,6 @@
'NO_HEAP_CHECK',
],
'direct_dependent_settings': {
- 'configurations': {
- 'Common_Base': {
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'IgnoreDefaultLibraryNames': ['libcmtd.lib', 'libcmt.lib'],
- 'AdditionalDependencies': [
- '<(SHARED_INTERMEDIATE_DIR)/allocator/libcmt.lib'
- ],
- },
- },
- },
- },
'conditions': [
['OS=="win"', {
'defines': [
@@ -530,11 +518,6 @@
'include_dirs': [
'../../'
],
- 'configurations': {
- 'Common_Base': {
- 'msvs_target_platform': 'x64',
- },
- },
},
{
'target_name': 'tcmalloc_unittest',
diff --git a/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp b/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp
index 1f1dc49..4021102 100644
--- a/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp
+++ b/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp
@@ -35,11 +35,6 @@
'dynamic_annotations.c',
'dynamic_annotations.h',
],
- 'configurations': {
- 'Common_Base': {
- 'msvs_target_platform': 'x64',
- },
- },
},
],
}],
diff --git a/src/build/common.gypi b/src/build/common.gypi
index e41bd74..901b1eb 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -421,248 +421,5 @@
],
}],
], # target_conditions for 'target_defaults'
- 'configurations': {
- # VCLinkerTool LinkIncremental values below:
- # 0 == default
- # 1 == /INCREMENTAL:NO
- # 2 == /INCREMENTAL
- # Debug links incremental, Release does not.
- #
- # Abstract base configurations to cover common attributes.
- #
- 'Common_Base': {
- 'abstract': 1,
- 'msvs_configuration_attributes': {
- 'OutputDirectory': '<(DEPTH)\\build\\<(build_dir_prefix)$(ConfigurationName)',
- 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)',
- 'CharacterSet': '1',
- },
- },
- 'x86_Base': {
- 'abstract': 1,
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'TargetMachine': '1',
- },
- },
- 'msvs_configuration_platform': 'Win32',
- },
- 'x64_Base': {
- 'abstract': 1,
- 'msvs_configuration_platform': 'x64',
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'TargetMachine': '17', # x86 - 64
- 'AdditionalLibraryDirectories!':
- ['<(windows_sdk_path)/Lib/win8/um/x86'],
- 'AdditionalLibraryDirectories':
- ['<(windows_sdk_path)/Lib/win8/um/x64'],
- },
- 'VCLibrarianTool': {
- 'AdditionalLibraryDirectories!':
- ['<(windows_sdk_path)/Lib/win8/um/x86'],
- 'AdditionalLibraryDirectories':
- ['<(windows_sdk_path)/Lib/win8/um/x64'],
- },
- },
- 'defines': [
- # Not sure if tcmalloc works on 64-bit Windows.
- 'NO_TCMALLOC',
- ],
- },
- 'Debug_Base': {
- 'abstract': 1,
- 'defines': [
- 'DYNAMIC_ANNOTATIONS_ENABLED=1',
- 'WTF_USE_DYNAMIC_ANNOTATIONS=1',
- ],
- 'xcode_settings': {
- 'COPY_PHASE_STRIP': 'NO',
- 'GCC_OPTIMIZATION_LEVEL': '<(mac_debug_optimization)',
- 'OTHER_CFLAGS': [
- '<@(debug_extra_cflags)',
- ],
- },
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '<(win_debug_Optimization)',
- 'PreprocessorDefinitions': ['_DEBUG'],
- 'BasicRuntimeChecks': '<(win_debug_RuntimeChecks)',
- 'RuntimeLibrary': '<(win_debug_RuntimeLibrary)',
- 'conditions': [
- # According to MSVS, InlineFunctionExpansion=0 means
- # "default inlining", not "/Ob0".
- # Thus, we have to handle InlineFunctionExpansion==0 separately.
- ['win_debug_InlineFunctionExpansion==0', {
- 'AdditionalOptions': ['/Ob0'],
- }],
- ['win_debug_InlineFunctionExpansion!=""', {
- 'InlineFunctionExpansion':
- '<(win_debug_InlineFunctionExpansion)',
- }],
- ['win_debug_disable_iterator_debugging==1', {
- 'PreprocessorDefinitions': ['_HAS_ITERATOR_DEBUGGING=0'],
- }],
-
- # if win_debug_OmitFramePointers is blank, leave as default
- ['win_debug_OmitFramePointers==1', {
- 'OmitFramePointers': 'true',
- }],
- ['win_debug_OmitFramePointers==0', {
- 'OmitFramePointers': 'false',
- # The above is not sufficient (http://crbug.com/106711): it
- # simply eliminates an explicit "/Oy", but both /O2 and /Ox
- # perform FPO regardless, so we must explicitly disable.
- # We still want the false setting above to avoid having
- # "/Oy /Oy-" and warnings about overriding.
- 'AdditionalOptions': ['/Oy-'],
- }],
- ],
- 'AdditionalOptions': [ '<@(win_debug_extra_cflags)', ],
- },
- 'VCLinkerTool': {
- 'LinkIncremental': '<(msvs_debug_link_incremental)',
- # ASLR makes debugging with windbg difficult because Chrome.exe and
- # Chrome.dll share the same base name. As result, windbg will
- # name the Chrome.dll module like chrome_<base address>, where
- # <base address> typically changes with each launch. This in turn
- # means that breakpoints in Chrome.dll don't stick from one launch
- # to the next. For this reason, we turn ASLR off in debug builds.
- # Note that this is a three-way bool, where 0 means to pick up
- # the default setting, 1 is off and 2 is on.
- 'RandomizedBaseAddress': 1,
- },
- 'VCResourceCompilerTool': {
- 'PreprocessorDefinitions': ['_DEBUG'],
- },
- },
- 'conditions': [
- # Disabled on iOS because it was causing a crash on startup.
- # TODO(michelea): investigate, create a reduced test and possibly
- # submit a radar.
- ['release_valgrind_build==0 and OS!="ios"', {
- 'xcode_settings': {
- 'OTHER_CFLAGS': [
- '-fstack-protector-all', # Implies -fstack-protector
- ],
- },
- }],
- ],
- },
- 'Release_Base': {
- 'abstract': 1,
- 'defines': [
- 'NDEBUG',
- ],
- 'xcode_settings': {
- 'DEAD_CODE_STRIPPING': 'YES', # -Wl,-dead_strip
- 'GCC_OPTIMIZATION_LEVEL': '<(mac_release_optimization)',
- 'OTHER_CFLAGS': [ '<@(release_extra_cflags)', ],
- },
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'RuntimeLibrary': '<(win_release_RuntimeLibrary)',
- 'Optimization': '<(win_release_Optimization)',
- 'conditions': [
- # According to MSVS, InlineFunctionExpansion=0 means
- # "default inlining", not "/Ob0".
- # Thus, we have to handle InlineFunctionExpansion==0 separately.
- ['win_release_InlineFunctionExpansion==0', {
- 'AdditionalOptions': ['/Ob0'],
- }],
- ['win_release_InlineFunctionExpansion!=""', {
- 'InlineFunctionExpansion':
- '<(win_release_InlineFunctionExpansion)',
- }],
-
- # if win_release_OmitFramePointers is blank, leave as default
- ['win_release_OmitFramePointers==1', {
- 'OmitFramePointers': 'true',
- }],
- ['win_release_OmitFramePointers==0', {
- 'OmitFramePointers': 'false',
- # The above is not sufficient (http://crbug.com/106711): it
- # simply eliminates an explicit "/Oy", but both /O2 and /Ox
- # perform FPO regardless, so we must explicitly disable.
- # We still want the false setting above to avoid having
- # "/Oy /Oy-" and warnings about overriding.
- 'AdditionalOptions': ['/Oy-'],
- }],
- ],
- 'AdditionalOptions': [ '<@(win_release_extra_cflags)', ],
- },
- 'VCLinkerTool': {
- # LinkIncremental is a tri-state boolean, where 0 means default
- # (i.e., inherit from parent solution), 1 means false, and
- # 2 means true.
- 'LinkIncremental': '1',
- # This corresponds to the /PROFILE flag which ensures the PDB
- # file contains FIXUP information (growing the PDB file by about
- # 5%) but does not otherwise alter the output binary. This
- # information is used by the Syzygy optimization tool when
- # decomposing the release image.
- 'Profile': 'true',
- },
- },
- 'conditions': [
- ['release_valgrind_build==0 and tsan==0', {
- 'defines': [
- 'NVALGRIND',
- 'DYNAMIC_ANNOTATIONS_ENABLED=0',
- ],
- }, {
- 'defines': [
- 'DYNAMIC_ANNOTATIONS_ENABLED=1',
- 'WTF_USE_DYNAMIC_ANNOTATIONS=1',
- ],
- }],
- ],
- },
- },
- },
- 'conditions': [
- ['clang==1', {
- 'conditions': [
- ['OS=="android"', {
- # Android could use the goma with clang.
- 'make_global_settings': [
- ['CC', '<!(/bin/echo -n ${ANDROID_GOMA_WRAPPER} ${CHROME_SRC}/<(make_clang_dir)/bin/clang)'],
- ['CXX', '<!(/bin/echo -n ${ANDROID_GOMA_WRAPPER} ${CHROME_SRC}/<(make_clang_dir)/bin/clang++)'],
- ['LINK', '<!(/bin/echo -n ${ANDROID_GOMA_WRAPPER} ${CHROME_SRC}/<(make_clang_dir)/bin/clang++)'],
- ['CC.host', '$(CC)'],
- ['CXX.host', '$(CXX)'],
- ['LINK.host', '$(LINK)'],
- ],
- }, {
- 'make_global_settings': [
- ['CC', '<(make_clang_dir)/bin/clang'],
- ['CXX', '<(make_clang_dir)/bin/clang++'],
- ['LINK', '$(CXX)'],
- ['CC.host', '$(CC)'],
- ['CXX.host', '$(CXX)'],
- ['LINK.host', '$(LINK)'],
- ],
- }],
- ],
- }],
- ],
- 'xcode_settings': {
- # DON'T ADD ANYTHING NEW TO THIS BLOCK UNLESS YOU REALLY REALLY NEED IT!
- # This block adds *project-wide* configuration settings to each project
- # file. It's almost always wrong to put things here. Specify your
- # custom xcode_settings in target_defaults to add them to targets instead.
-
- # The Xcode generator will look for an xcode_settings section at the root
- # of each dict and use it to apply settings on a file-wide basis. Most
- # settings should not be here, they should be in target-specific
- # xcode_settings sections, or better yet, should use non-Xcode-specific
- # settings in target dicts. SYMROOT is a special case, because many other
- # Xcode variables depend on it, including variables such as
- # PROJECT_DERIVED_FILE_DIR. When a source group corresponding to something
- # like PROJECT_DERIVED_FILE_DIR is added to a project, in order for the
- # files to appear (when present) in the UI as actual files and not red
- # red "missing file" proxies, the correct path to PROJECT_DERIVED_FILE_DIR,
- # and therefore SYMROOT, needs to be set at the project level.
- 'SYMROOT': '<(DEPTH)/xcodebuild',
},
}
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 1cbd099..2abcfc8 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -3,6 +3,13 @@
This document records all notable changes made to Cobalt since the last release.
## Version 11
+ - **Splash Screen Customization**
+
+ The Cobalt splash screen is customizable. Documents may use a link element
+ with attribute rel="splashscreen" to reference the splash screen which will
+ be cached if local cache is implemented on the platform. Additionally
+ fallbacks may be specified via command line parmeter or gypi variable.
+ For more information, see [doc/splash_screen.md](doc/splash_screen.md).
- **Introduce C\+\+11**
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
index bb92da0..6f5cd15 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
@@ -28,6 +28,7 @@
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/mozjs_callback_interface.h"
#include "cobalt/script/mozjs-45/util/exception_helpers.h"
+#include "cobalt/script/mozjs-45/util/stack_trace_helpers.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
#include "third_party/mozjs-45/js/src/jscntxt.h"
@@ -68,6 +69,7 @@
base::optional<int32_t > cobalt_return_value;
JSAutoRequest auto_request(context_);
JS::AutoSaveExceptionState auto_save_exception_state(context_);
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
// This could be set to NULL if it was garbage collected.
JS::RootedObject implementing_object(context_, implementing_object_.GetObject());
diff --git a/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template b/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
index 129ff1c..9104deb 100644
--- a/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
@@ -23,6 +23,7 @@
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/mozjs_callback_interface.h"
#include "cobalt/script/mozjs-45/util/exception_helpers.h"
+#include "cobalt/script/mozjs-45/util/stack_trace_helpers.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
#include "third_party/mozjs-45/js/src/jscntxt.h"
{% endblock includes %}
@@ -67,6 +68,7 @@
{% endif %}
JSAutoRequest auto_request(context_);
JS::AutoSaveExceptionState auto_save_exception_state(context_);
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
// This could be set to NULL if it was garbage collected.
JS::RootedObject implementing_object(context_, implementing_object_.GetObject());
diff --git a/src/cobalt/bindings/testing/stack_trace_test.cc b/src/cobalt/bindings/testing/stack_trace_test.cc
index 8dbdb37..b900b72 100644
--- a/src/cobalt/bindings/testing/stack_trace_test.cc
+++ b/src/cobalt/bindings/testing/stack_trace_test.cc
@@ -32,7 +32,7 @@
// 11).
std::string script =
"function bar() {\n"
- "return getStackTrace();\n"
+ " return getStackTrace();\n"
"}\n"
"function foo(depth) {\n"
" if (depth <= 0) {\n"
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 3d88ac6..d97bbbb 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -147,26 +147,27 @@
return GURL(kDefaultURL);
}
-base::optional<GURL> GetSplashScreenURL() {
+base::optional<GURL> GetFallbackSplashScreenURL() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
- std::string splash_screen_string;
+ std::string fallback_splash_screen_string;
if (command_line->HasSwitch(switches::kFallbackSplashScreenURL)) {
- splash_screen_string =
+ fallback_splash_screen_string =
command_line->GetSwitchValueASCII(switches::kFallbackSplashScreenURL);
} else {
- splash_screen_string = COBALT_FALLBACK_SPLASH_SCREEN_URL;
+ fallback_splash_screen_string = COBALT_FALLBACK_SPLASH_SCREEN_URL;
}
- if (IsStringNone(splash_screen_string)) {
+ if (IsStringNone(fallback_splash_screen_string)) {
return base::optional<GURL>();
}
- base::optional<GURL> splash_screen_url = GURL(splash_screen_string);
- if (!splash_screen_url->is_valid() ||
- !(StartsWithASCII(splash_screen_string, "file:///", false) ||
- StartsWithASCII(splash_screen_string, "h5vcc-embedded://", false))) {
+ base::optional<GURL> fallback_splash_screen_url =
+ GURL(fallback_splash_screen_string);
+ if (!fallback_splash_screen_url->is_valid() ||
+ !(fallback_splash_screen_url->SchemeIsFile() ||
+ fallback_splash_screen_url->SchemeIs("h5vcc-embedded"))) {
LOG(FATAL) << "Ignoring invalid fallback splash screen: "
- << splash_screen_string;
+ << fallback_splash_screen_string;
}
- return splash_screen_url;
+ return fallback_splash_screen_url;
}
base::TimeDelta GetTimedTraceDuration() {
@@ -449,10 +450,12 @@
GURL initial_url = GetInitialURL();
DLOG(INFO) << "Initial URL: " << initial_url;
- // Get the splash screen URL.
- base::optional<GURL> splash_screen_url = GetSplashScreenURL();
- DLOG(INFO) << "Splash screen URL: "
- << (splash_screen_url ? splash_screen_url->spec() : "none");
+ // Get the fallback splash screen URL.
+ base::optional<GURL> fallback_splash_screen_url =
+ GetFallbackSplashScreenURL();
+ DLOG(INFO) << "Fallback splash screen URL: "
+ << (fallback_splash_screen_url ? fallback_splash_screen_url->spec()
+ : "none");
// Get the system language and initialize our localized strings.
std::string language = base::GetSystemLanguage();
@@ -472,7 +475,7 @@
options.command_line_auto_mem_settings =
memory_settings::GetSettings(*command_line);
options.build_auto_mem_settings = memory_settings::GetDefaultBuildSettings();
- options.splash_screen_url = splash_screen_url;
+ options.fallback_splash_screen_url = fallback_splash_screen_url;
if (command_line->HasSwitch(browser::switches::kFPSPrint)) {
options.renderer_module_options.enable_fps_stdout = true;
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 321bc20..0ea1fe2 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -86,6 +86,8 @@
'resource_provider_array_buffer_allocator.h',
'splash_screen.cc',
'splash_screen.h',
+ 'splash_screen_cache.cc',
+ 'splash_screen_cache.h',
'storage_upgrade_handler.cc',
'storage_upgrade_handler.h',
'suspend_fuzzer.cc',
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index 061a5df..392a6f5 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -218,6 +218,7 @@
'../audio/audio_node_channel_interpretation.idl',
'../dom/blob_property_bag.idl',
'../dom/device_orientation_event_init.idl',
+ '../dom/document_ready_state.idl',
'../dom/dom_parser_supported_type.idl',
'../dom/event_init.idl',
'../dom/event_modifier_init.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index d744b06..905991b 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -19,8 +19,10 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
+#include "base/file_path.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/optional.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
@@ -238,7 +240,8 @@
render_timeout_count_(0),
#endif
will_quit_(false),
- application_state_(initial_application_state) {
+ application_state_(initial_application_state),
+ splash_screen_cache_(new SplashScreenCache()) {
#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
on_error_triggered_count_ = 0;
@@ -305,7 +308,7 @@
lifecycle_observers_.AddObserver(debug_console_.get());
#endif // defined(ENABLE_DEBUG_CONSOLE)
- splash_screen_url_ = options.splash_screen_url;
+ fallback_splash_screen_url_ = options.fallback_splash_screen_url;
// Synchronously construct our WebModule object.
NavigateInternal(url);
DCHECK(web_module_);
@@ -377,14 +380,18 @@
// Show a splash screen while we're waiting for the web page to load.
const math::Size& viewport_size = GetViewportSize();
+
DestroySplashScreen();
- if (splash_screen_url_) {
- splash_screen_.reset(
- new SplashScreen(application_state_,
- base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
- base::Unretained(this)),
- &network_module_, viewport_size, GetResourceProvider(),
- kLayoutMaxRefreshFrequencyInHz, *splash_screen_url_));
+ base::optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
+ if (fallback_splash_screen_url_ ||
+ (key && splash_screen_cache_->IsSplashScreenCached(*key))) {
+ splash_screen_.reset(new SplashScreen(
+ application_state_,
+ base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
+ base::Unretained(this)),
+ &network_module_, viewport_size, GetResourceProvider(),
+ kLayoutMaxRefreshFrequencyInHz, *fallback_splash_screen_url_, url,
+ splash_screen_cache_.get()));
lifecycle_observers_.AddObserver(splash_screen_.get());
}
@@ -394,6 +401,7 @@
dom::CspDelegateFactory::GetInsecureAllowedToken();
#endif
WebModule::Options options(options_.web_module_options);
+ options.splash_screen_cache = splash_screen_cache_.get();
options.navigation_callback =
base::Bind(&BrowserModule::Navigate, base::Unretained(this));
options.loaded_callbacks.push_back(
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index fcba62b..ba3861d 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -85,7 +85,7 @@
base::Closure web_module_recreated_callback;
memory_settings::AutoMemSettings command_line_auto_mem_settings;
memory_settings::AutoMemSettings build_auto_mem_settings;
- base::optional<GURL> splash_screen_url;
+ base::optional<GURL> fallback_splash_screen_url;
base::optional<math::Size> requested_viewport_size;
};
@@ -453,9 +453,12 @@
// memory.
memory_settings::Checker memory_settings_checker_;
- // The URL to the splash screen. If empty (the default), no splash
+ // The fallback URL to the splash screen. If empty (the default), no splash
// screen will be displayed.
- base::optional<GURL> splash_screen_url_;
+ base::optional<GURL> fallback_splash_screen_url_;
+
+ // The splash screen cache.
+ scoped_ptr<SplashScreenCache> splash_screen_cache_;
};
} // namespace browser
diff --git a/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
index 95313d2..b778049 100644
--- a/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
@@ -23,6 +23,7 @@
#include "cobalt/browser/memory_tracker/tool/params.h"
#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "cobalt/script/util/stack_trace_helpers.h"
#include "nb/memory_scope.h"
#include "starboard/string.h"
@@ -284,8 +285,18 @@
}
const std::string* LeakFinderTool::TryGetJavascriptSymbol() {
- // TODO: Actually get and use a stack trace here.
- return NULL;
+ auto* js_stack_gen = script::util::GetThreadLocalStackTraceGenerator();
+ if (!js_stack_gen || !js_stack_gen->Valid()) {
+ return NULL;
+ }
+
+ // Only get one symbol.
+ char buffer[256];
+ if (!js_stack_gen->GenerateStackTraceString(1, buffer, sizeof(buffer))) {
+ return NULL;
+ }
+ const char* file_name = BaseNameFast(buffer);
+ return &string_pool_.Intern(file_name);
}
void LeakFinderTool::SampleSnapshot(
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index 531ab13..8c78ce2 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -14,8 +14,12 @@
#include "cobalt/browser/splash_screen.h"
+#include <string>
+
#include "base/bind.h"
#include "base/threading/platform_thread.h"
+#include "cobalt/browser/splash_screen_cache.h"
+#include "cobalt/loader/cache_fetcher.h"
namespace cobalt {
namespace browser {
@@ -26,7 +30,10 @@
network::NetworkModule* network_module,
const math::Size& window_dimensions,
render_tree::ResourceProvider* resource_provider,
- float layout_refresh_rate, const GURL& url)
+ float layout_refresh_rate,
+ const GURL& fallback_splash_screen_url,
+ const GURL& initial_main_web_module_url,
+ SplashScreenCache* splash_screen_cache)
: render_tree_produced_callback_(render_tree_produced_callback),
is_ready_(true, false) {
WebModule::Options web_module_options;
@@ -38,9 +45,20 @@
web_module_options.loader_thread_priority = base::kThreadPriority_High;
web_module_options.animated_image_decode_thread_priority =
base::kThreadPriority_High;
+ GURL url_to_pass = fallback_splash_screen_url;
+
+ // Use the cached URL rather than the passed in URL if it exists.
+ base::optional<std::string> key =
+ SplashScreenCache::GetKeyForStartUrl(initial_main_web_module_url);
+ if (key && splash_screen_cache &&
+ splash_screen_cache->IsSplashScreenCached(*key)) {
+ url_to_pass = GURL(loader::kCacheScheme + ("://" + *key));
+ web_module_options.can_fetch_cache = true;
+ web_module_options.splash_screen_cache = splash_screen_cache;
+ }
web_module_.reset(new WebModule(
- url, initial_application_state,
+ url_to_pass, initial_application_state,
base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
base::Bind(&SplashScreen::OnError, base::Unretained(this)),
base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index 1780bd5..0e07915 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -21,6 +21,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "cobalt/browser/lifecycle_observer.h"
+#include "cobalt/browser/splash_screen_cache.h"
#include "cobalt/browser/web_module.h"
#include "cobalt/media/media_module_stub.h"
#include "googleurl/src/gurl.h"
@@ -38,7 +39,10 @@
network::NetworkModule* network_module,
const math::Size& window_dimensions,
render_tree::ResourceProvider* resource_provider,
- float layout_refresh_rate, const GURL& url);
+ float layout_refresh_rate,
+ const GURL& fallback_splash_screen_url,
+ const GURL& initial_main_web_module_url,
+ cobalt::browser::SplashScreenCache* splash_screen_cache);
~SplashScreen();
void SetSize(const math::Size& window_dimensions, float video_pixel_ratio) {
diff --git a/src/cobalt/browser/splash_screen_cache.cc b/src/cobalt/browser/splash_screen_cache.cc
new file mode 100644
index 0000000..ab674df
--- /dev/null
+++ b/src/cobalt/browser/splash_screen_cache.cc
@@ -0,0 +1,157 @@
+// Copyright 2017 Google Inc. 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/browser/splash_screen_cache.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
+#include "base/string_util.h"
+#include "base/synchronization/lock.h"
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#include "starboard/string.h"
+
+namespace cobalt {
+namespace browser {
+namespace {
+bool CreateDirsForKey(const std::string& key) {
+ char path[SB_FILE_MAX_PATH] = {0};
+ if (!SbSystemGetPath(kSbSystemPathCacheDirectory, path, SB_FILE_MAX_PATH)) {
+ return false;
+ }
+ std::size_t prev_found = 0;
+ std::size_t found = key.find(SB_FILE_SEP_STRING);
+ SbStringConcat(path, SB_FILE_SEP_STRING, SB_FILE_MAX_PATH);
+ while (found != std::string::npos) {
+ SbStringConcat(path, key.substr(prev_found, found - prev_found).c_str(),
+ SB_FILE_MAX_PATH);
+ if (!SbDirectoryCreate(path)) {
+ return false;
+ }
+ prev_found = found;
+ found = key.find(SB_FILE_SEP_STRING, prev_found + 1);
+ }
+ return true;
+}
+
+} // namespace
+
+SplashScreenCache::SplashScreenCache() { base::AutoLock lock(lock_); }
+
+bool SplashScreenCache::CacheSplashScreen(const std::string& key,
+ const std::string& content) const {
+ base::AutoLock lock(lock_);
+ if (key.empty()) {
+ return false;
+ }
+
+ char path[SB_FILE_MAX_PATH] = {0};
+ if (!SbSystemGetPath(kSbSystemPathCacheDirectory, path, SB_FILE_MAX_PATH)) {
+ return false;
+ }
+ if (!CreateDirsForKey(key)) {
+ return false;
+ }
+ std::string full_path = path + (SB_FILE_SEP_STRING + key);
+ starboard::ScopedFile cache_file(
+ full_path.c_str(), kSbFileCreateAlways | kSbFileWrite, NULL, NULL);
+
+ return cache_file.WriteAll(content.c_str(),
+ static_cast<int>(content.size())) > 0;
+}
+
+bool SplashScreenCache::IsSplashScreenCached(const std::string& key) const {
+ base::AutoLock lock(lock_);
+ char path[SB_FILE_MAX_PATH] = {0};
+ if (!SbSystemGetPath(kSbSystemPathCacheDirectory, path, SB_FILE_MAX_PATH)) {
+ return false;
+ }
+ std::string full_path = path + (SB_FILE_SEP_STRING + key);
+ return !key.empty() && SbFileExists(full_path.c_str());
+}
+
+int SplashScreenCache::ReadCachedSplashScreen(
+ const std::string& key, scoped_array<char>* result) const {
+ base::AutoLock lock(lock_);
+ if (!result) {
+ return 0;
+ }
+ char path[SB_FILE_MAX_PATH] = {0};
+ if (!SbSystemGetPath(kSbSystemPathCacheDirectory, path, SB_FILE_MAX_PATH)) {
+ result->reset();
+ return 0;
+ }
+ std::string full_path = path + (SB_FILE_SEP_STRING + key);
+ starboard::ScopedFile cache_file(full_path.c_str(),
+ kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+ SbFileInfo info;
+ bool success = SbFileGetPathInfo(full_path.c_str(), &info);
+ if (!success) {
+ result->reset();
+ return 0;
+ }
+ const int kFileSize = static_cast<int>(info.size);
+ result->reset(new char[kFileSize]);
+ int result_size = cache_file.ReadAll(result->get(), kFileSize);
+ return result_size;
+}
+
+// static
+base::optional<std::string> SplashScreenCache::GetKeyForStartUrl(
+ const GURL& url) {
+ std::string encoded_url = "";
+ if (!url.is_valid()) {
+ return base::nullopt;
+ }
+ base::Base64Encode(base::StringPiece(url.host() + url.path()), &encoded_url);
+
+ // Make web-safe.
+ ReplaceChars(encoded_url, "/", "_", &encoded_url);
+ ReplaceChars(encoded_url, "+", "-", &encoded_url);
+
+ char path[SB_FILE_MAX_PATH] = {0};
+ bool has_cache_dir =
+ SbSystemGetPath(kSbSystemPathCacheDirectory, path, SB_FILE_MAX_PATH);
+ if (!has_cache_dir) {
+ return base::nullopt;
+ }
+
+ std::string subpath = "";
+ std::string subcomponent = SB_FILE_SEP_STRING + std::string("splash_screen");
+ if (SbStringConcat(path, subcomponent.c_str(), SB_FILE_MAX_PATH) >=
+ SB_FILE_MAX_PATH) {
+ return base::nullopt;
+ }
+ subpath += "splash_screen";
+ subcomponent = SB_FILE_SEP_STRING + encoded_url;
+ if (SbStringConcat(path, subcomponent.c_str(), SB_FILE_MAX_PATH) >=
+ SB_FILE_MAX_PATH) {
+ return base::nullopt;
+ }
+ subpath += subcomponent;
+ subcomponent = SB_FILE_SEP_STRING + std::string("splash.html");
+ if (SbStringConcat(path, subcomponent.c_str(), SB_FILE_MAX_PATH) >
+ SB_FILE_MAX_PATH) {
+ return base::nullopt;
+ }
+ subpath += subcomponent;
+
+ return subpath;
+}
+
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/splash_screen_cache.h b/src/cobalt/browser/splash_screen_cache.h
new file mode 100644
index 0000000..83e9e7a
--- /dev/null
+++ b/src/cobalt/browser/splash_screen_cache.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. 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_BROWSER_SPLASH_SCREEN_CACHE_H_
+#define COBALT_BROWSER_SPLASH_SCREEN_CACHE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
+#include "base/synchronization/lock.h"
+#include "googleurl/src/gurl.h"
+
+namespace cobalt {
+namespace browser {
+
+// SplashScreenCache saves a splash screen document loaded by the
+// HTMLLinkElement into the system cache, if any, so that it's
+// available to BrowserModule to show on subsequent runs before
+// loading the initial URL. The key for the cached document is based
+// on the passed in initial URL. See cobalt/doc/splash_screen.md for
+// more information.
+class SplashScreenCache {
+ public:
+ SplashScreenCache();
+
+ // Cache the splash screen.
+ bool CacheSplashScreen(const std::string& key,
+ const std::string& content) const;
+
+ // Read the cached the splash screen.
+ int ReadCachedSplashScreen(const std::string& key,
+ scoped_array<char>* result) const;
+
+ // Determine if a splash screen is cached corresponding to the key.
+ bool IsSplashScreenCached(const std::string& key) const;
+
+ // Get the key that corresponds to a starting URL. Optionally create
+ // subdirectories along the path.
+ static base::optional<std::string> GetKeyForStartUrl(const GURL& url);
+
+ private:
+ // Lock to protect access to the cache file.
+ mutable base::Lock lock_;
+};
+
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_SPLASH_SCREEN_CACHE_H_
diff --git a/src/cobalt/browser/testdata/media-element-demo/media-element-demo.js b/src/cobalt/browser/testdata/media-element-demo/media-element-demo.js
index 375e59f..ce74ac8 100644
--- a/src/cobalt/browser/testdata/media-element-demo/media-element-demo.js
+++ b/src/cobalt/browser/testdata/media-element-demo/media-element-demo.js
@@ -44,8 +44,12 @@
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.addEventListener('load', function(e) {
- source_buffer.append(new Uint8Array(e.target.response));
- callback();
+ 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();
diff --git a/src/cobalt/browser/testdata/splash_screen/link_splash_screen.html b/src/cobalt/browser/testdata/splash_screen/link_splash_screen.html
new file mode 100644
index 0000000..6d114b6
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/link_splash_screen.html
@@ -0,0 +1,435 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <meta http-equiv="Content-Security-Policy" content="
+ default-src h5vcc-embedded://*/cobalt_splash_screen.html;
+ style-src 'unsafe-inline';"
+</head>
+
+<body style="background-color: #1f52a5;">
+ <link rel="splashscreen" href="h5vcc-embedded://cobalt_splash_screen.html">
+<h1>Heading</h1>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/testdata/splash_screen/link_splash_screen_network.html b/src/cobalt/browser/testdata/splash_screen/link_splash_screen_network.html
new file mode 100644
index 0000000..1cc9819
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/link_splash_screen_network.html
@@ -0,0 +1,676 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <meta http-equiv="Content-Security-Policy" content="
+ default-src 'unsafe-inline';
+ script-src https://*/storage.googleapis.com/yt-temp/self_contained_splash.html;
+ style-src 'unsafe-inline';"
+</head>
+
+<body style="background-color: #1f52a5;">
+ <link rel="splashscreen" href="https://storage.googleapis.com/yt-temp/self_contained_splash.html">
+<h1>Heading</h1>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 8a4b237..efd1b83 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -15,17 +15,21 @@
#include "cobalt/browser/web_module.h"
#include <sstream>
+#include <string>
#include "base/bind.h"
+#include "base/callback.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop_proxy.h"
#include "base/optional.h"
#include "base/stringprintf.h"
#include "cobalt/base/startup_timer.h"
#include "cobalt/base/tokens.h"
+#include "cobalt/browser/splash_screen_cache.h"
#include "cobalt/browser/stack_size_constants.h"
#include "cobalt/browser/switches.h"
#include "cobalt/browser/web_module_stat_tracker.h"
@@ -85,6 +89,21 @@
"To wipe the box tree and turn partial layout off.";
#endif // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
+base::Callback<bool(const std::string&)> SplashScreenCacheCallback(
+ const GURL& initial_url, SplashScreenCache* splash_screen_cache) {
+ base::Callback<bool(const std::string&)> splash_screen_cache_callback =
+ base::Callback<bool(const std::string&)>();
+ base::optional<std::string> key =
+ SplashScreenCache::GetKeyForStartUrl(initial_url);
+ if (splash_screen_cache && key) {
+ splash_screen_cache_callback =
+ base::Bind(base::Bind(&SplashScreenCache::CacheSplashScreen,
+ base::Unretained(splash_screen_cache)),
+ *key);
+ }
+ return splash_screen_cache_callback;
+}
+
} // namespace
// Private WebModule implementation. Each WebModule owns a single instance of
@@ -396,9 +415,18 @@
blob_registry_.reset(new dom::Blob::Registry);
+ base::Callback<int(const std::string&, scoped_array<char>*)>
+ read_cache_callback;
+ if (data.options.can_fetch_cache) {
+ read_cache_callback =
+ base::Bind(&browser::SplashScreenCache::ReadCachedSplashScreen,
+ base::Unretained(data.options.splash_screen_cache));
+ }
+
fetcher_factory_.reset(new loader::FetcherFactory(
data.network_module, data.options.extra_web_file_dir,
- dom::URL::MakeBlobResolverCallback(blob_registry_.get())));
+ dom::URL::MakeBlobResolverCallback(blob_registry_.get()),
+ read_cache_callback));
DCHECK(fetcher_factory_);
loader_factory_.reset(
@@ -466,6 +494,11 @@
media_source_registry_.reset(new dom::MediaSource::Registry);
media_session_client_ = media_session::MediaSessionClient::Create();
+
+ base::Callback<bool(const std::string&)> splash_screen_cache_callback =
+ SplashScreenCacheCallback(data.initial_url,
+ data.options.splash_screen_cache);
+
window_ = new dom::Window(
data.window_dimensions.width(), data.window_dimensions.height(),
data.video_pixel_ratio, data.initial_application_state, css_parser_.get(),
@@ -492,11 +525,11 @@
#if defined(ENABLE_TEST_RUNNER)
data.options.layout_trigger == layout::LayoutManager::kTestRunnerMode
? dom::Window::kClockTypeTestRunner
- : dom::Window::kClockTypeSystemTime
+ : dom::Window::kClockTypeSystemTime,
#else
- dom::Window::kClockTypeSystemTime
+ dom::Window::kClockTypeSystemTime,
#endif
- ); // NOLINT(whitespace/parens)
+ splash_screen_cache_callback);
DCHECK(window_);
window_weak_ = base::AsWeakPtr(window_.get());
@@ -945,7 +978,8 @@
loader_thread_priority(base::kThreadPriority_Low),
animated_image_decode_thread_priority(base::kThreadPriority_Low),
video_playback_rate_multiplier(1.f),
- enable_image_animations(true) {}
+ enable_image_animations(true),
+ can_fetch_cache(false) {}
WebModule::WebModule(
const GURL& initial_url, base::ApplicationState initial_application_state,
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index ad3ab1e..07d8975 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -30,6 +30,7 @@
#include "cobalt/base/console_commands.h"
#include "cobalt/base/source_location.h"
#include "cobalt/browser/lifecycle_observer.h"
+#include "cobalt/browser/splash_screen_cache.h"
#include "cobalt/css_parser/parser.h"
#if defined(ENABLE_DEBUG_CONSOLE)
#include "cobalt/debug/debug_server.h"
@@ -179,6 +180,13 @@
// Allows image animations to be enabled/disabled. Its default value
// is true to enable them.
bool enable_image_animations;
+
+ // The splash screen cache object, owned by the BrowserModule.
+ SplashScreenCache* splash_screen_cache;
+
+ // Whether or not the WebModule is allowed to fetch from cache via
+ // h5vcc-cache://.
+ bool can_fetch_cache;
};
typedef layout::LayoutManager::LayoutResults LayoutResults;
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 74e0b49..d5c614a 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -60,7 +60,7 @@
'<(DEPTH)/cobalt/render_tree/render_tree.gyp:*',
'<(DEPTH)/cobalt/renderer/renderer.gyp:*',
'<(DEPTH)/cobalt/renderer/sandbox/sandbox.gyp:*',
- '<(DEPTH)/cobalt/samples/samples.gyp:*',
+ '<(DEPTH)/cobalt/samples/simple_example/simple_example.gyp:*',
'<(DEPTH)/cobalt/script/script.gyp:*',
'<(DEPTH)/cobalt/script/engine.gyp:all_engines',
'<(DEPTH)/cobalt/speech/sandbox/sandbox.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 58c7a27..cf4e0e6 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-81256
\ No newline at end of file
+84472
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index 96d4005..0f974b5 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -254,11 +254,9 @@
# Temporary indicator for Tizen - should eventually move to feature defines.
'tizen_os%': 0,
- # URL of default build time splash screen:
- # TODO: Point this to cobalt_splash_screen.html and override it in
- # ports' gyp_configuration.gypi (coordinate transition with
- # partners).
- 'fallback_splash_screen_url%': 'h5vcc-embedded://splash_screen.html',
+ # The URL of default build time splash screen - see
+ # cobalt/doc/splash_screen.md for information about this.
+ 'fallback_splash_screen_url%': 'h5vcc-embedded://youtube_splash_screen.html',
# Cache parameters
diff --git a/src/cobalt/build/copy_icu_data.gypi b/src/cobalt/build/copy_icu_data.gypi
index 2b15d2a..abbff85 100644
--- a/src/cobalt/build/copy_icu_data.gypi
+++ b/src/cobalt/build/copy_icu_data.gypi
@@ -39,15 +39,21 @@
'little_endian%': '<(little_endian)',
'use_icu_dat_file%': '<(use_icu_dat_file)',
},
+ 'little_endian%': '<(little_endian)',
},
- #'inputs_icu%': [ '<(inputs_icu)' ],
- 'inputs_icu%': [ '<(static_contents_source_dir)/icu/' ],
+ 'conditions': [
+ ['little_endian==1', {
+ 'inputs_icu%': [ '<(static_contents_source_dir)/icu/icudt56l' ],
+ }, {
+ 'inputs_icu%': [ '<(static_contents_source_dir)/icu/icudt56b' ],
+ }],
+ ],
},
'copies': [
{
- 'destination': '<(sb_static_contents_output_data_dir)/',
+ 'destination': '<(sb_static_contents_output_data_dir)/icu/',
'files': [ '<(inputs_icu)' ],
},
],
diff --git a/src/cobalt/cssom/css_computed_style_declaration.cc b/src/cobalt/cssom/css_computed_style_declaration.cc
index f19c7d0..169f68d 100644
--- a/src/cobalt/cssom/css_computed_style_declaration.cc
+++ b/src/cobalt/cssom/css_computed_style_declaration.cc
@@ -20,12 +20,10 @@
namespace cobalt {
namespace cssom {
// This returns the result of serializing a CSS declaration block.
-// The current implementation does not handle shorthands.
// https://www.w3.org/TR/cssom/#serialize-a-css-declaration-block
std::string CSSComputedStyleDeclaration::css_text(
script::ExceptionState* /*exception_state*/) const {
- // TODO: This should enumerate all supported properties, not just
- // the declared ones.
+ // The current implementation does not handle shorthands.
NOTIMPLEMENTED();
return data_ ? data_->SerializeCSSDeclarationBlock() : std::string();
}
@@ -59,6 +57,18 @@
if (!data_ || key == kNoneProperty) {
return std::string();
}
+ if (key > kMaxLonghandPropertyKey) {
+ // Shorthand properties are never directly stored as computed style
+ // properties.
+ // TODO: Implement serialization of css values, see
+ // https://www.w3.org/TR/cssom-1/#serializing-css-values
+ DCHECK_LE(key, kMaxEveryPropertyKey);
+ NOTIMPLEMENTED();
+ DLOG(WARNING) << "Unsupported property query for \"" << GetPropertyName(key)
+ << "\": Returning of property value strings is not "
+ "supported for shorthand properties.";
+ return std::string();
+ }
const scoped_refptr<PropertyValue>& property_value =
data_->GetPropertyValueReference(key);
DCHECK(property_value);
diff --git a/src/cobalt/cssom/css_declared_style_declaration.cc b/src/cobalt/cssom/css_declared_style_declaration.cc
index a704fda..149941b 100644
--- a/src/cobalt/cssom/css_declared_style_declaration.cc
+++ b/src/cobalt/cssom/css_declared_style_declaration.cc
@@ -105,6 +105,18 @@
std::string CSSDeclaredStyleDeclaration::GetDeclaredPropertyValueStringByKey(
const PropertyKey key) const {
+ if (key > kMaxLonghandPropertyKey) {
+ // Shorthand properties are never directly stored as declared properties,
+ // but are expanded into their longhand property components during parsing.
+ // TODO: Implement serialization of css values, see
+ // https://www.w3.org/TR/cssom-1/#serializing-css-values
+ DCHECK_LE(key, kMaxEveryPropertyKey);
+ NOTIMPLEMENTED();
+ DLOG(WARNING) << "Unsupported property query for \"" << GetPropertyName(key)
+ << "\": Returning of property value strings is not "
+ "supported for shorthand properties.";
+ return std::string();
+ }
return (data_ && key != kNoneProperty) ? data_->GetPropertyValueString(key)
: std::string();
}
diff --git a/src/cobalt/cssom/css_style_declaration.cc b/src/cobalt/cssom/css_style_declaration.cc
index 7542e1b..7736040 100644
--- a/src/cobalt/cssom/css_style_declaration.cc
+++ b/src/cobalt/cssom/css_style_declaration.cc
@@ -25,8 +25,7 @@
std::string CSSStyleDeclaration::animation(
script::ExceptionState* /*exception_state*/) const {
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kAnimationProperty);
}
void CSSStyleDeclaration::set_animation(
@@ -120,11 +119,7 @@
std::string CSSStyleDeclaration::background(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other background properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBackgroundProperty);
}
void CSSStyleDeclaration::set_background(
@@ -194,11 +189,7 @@
std::string CSSStyleDeclaration::border(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderProperty);
}
void CSSStyleDeclaration::set_border(const std::string& border,
@@ -208,11 +199,7 @@
std::string CSSStyleDeclaration::border_bottom(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border bottom properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderBottomProperty);
}
void CSSStyleDeclaration::set_border_bottom(
@@ -223,11 +210,7 @@
std::string CSSStyleDeclaration::border_left(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border left properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderLeftProperty);
}
void CSSStyleDeclaration::set_border_left(
@@ -238,11 +221,7 @@
std::string CSSStyleDeclaration::border_right(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border right properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderRightProperty);
}
void CSSStyleDeclaration::set_border_right(
@@ -253,11 +232,7 @@
std::string CSSStyleDeclaration::border_top(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border top properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderTopProperty);
}
void CSSStyleDeclaration::set_border_top(
@@ -267,11 +242,7 @@
std::string CSSStyleDeclaration::border_color(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border color properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderColorProperty);
}
void CSSStyleDeclaration::set_border_color(
@@ -330,11 +301,7 @@
std::string CSSStyleDeclaration::border_style(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border style properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderStyleProperty);
}
void CSSStyleDeclaration::set_border_style(
@@ -393,11 +360,7 @@
std::string CSSStyleDeclaration::border_width(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other border width properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kBorderWidthProperty);
}
void CSSStyleDeclaration::set_border_width(
@@ -527,8 +490,7 @@
std::string CSSStyleDeclaration::font(
script::ExceptionState* /*exception_state*/) const {
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kFontProperty);
}
void CSSStyleDeclaration::set_font(const std::string& font,
@@ -611,11 +573,7 @@
std::string CSSStyleDeclaration::margin(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other margin properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kMarginProperty);
}
void CSSStyleDeclaration::set_margin(const std::string& margin,
@@ -782,11 +740,7 @@
std::string CSSStyleDeclaration::padding(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other padding properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kPaddingProperty);
}
void CSSStyleDeclaration::set_padding(const std::string& padding,
@@ -1000,11 +954,7 @@
std::string CSSStyleDeclaration::transition(
script::ExceptionState* /*exception_state*/) const {
- // In order to implement this properly we must either save the incoming string
- // values when they are being set, or combine the results of getting the
- // styles from all the other transition properties.
- NOTIMPLEMENTED();
- return "";
+ return GetDeclaredPropertyValueStringByKey(kTransitionProperty);
}
void CSSStyleDeclaration::set_transition(
@@ -1129,19 +1079,7 @@
std::string CSSStyleDeclaration::GetPropertyValue(
const std::string& property_name) {
- PropertyKey key = GetPropertyKey(property_name);
- if (key > kMaxLonghandPropertyKey) {
- // Shorthand properties are never directly stored as declared properties,
- // but are expanded into their longhand property components during parsing.
- // TODO: Implement serialization of css values, see
- // https://www.w3.org/TR/cssom-1/#serializing-css-values
- DCHECK_LE(key, kMaxEveryPropertyKey);
- DLOG(WARNING) << "Unsupported property query for \"" << property_name
- << "\": Returning of property value strings is only "
- "supported for longhand properties.";
- return std::string();
- }
- return GetDeclaredPropertyValueStringByKey(key);
+ return GetDeclaredPropertyValueStringByKey(GetPropertyKey(property_name));
}
void CSSStyleDeclaration::SetPropertyValueStringByKey(
diff --git a/src/cobalt/doc/splash_screen.md b/src/cobalt/doc/splash_screen.md
new file mode 100644
index 0000000..201d5e1
--- /dev/null
+++ b/src/cobalt/doc/splash_screen.md
@@ -0,0 +1,100 @@
+# Cobalt Splash Screen
+
+## Startup splash screen sequence
+
+There can be up to three splash screens shown when launching web applications on
+Cobalt:
+
+ * one from the system
+ * one from Cobalt
+ * one from the web application itself
+
+The system splash screen is often a transition from the application icon
+on the home screen to a static asset dictated by the platform (which is outside
+of Cobalt's control). The Cobalt splash screen is shown as soon as Cobalt can
+render until the web application is loaded. The web application splash screen is
+the HTML content shown immediately upon loading the web application (this may
+resemble a typical splash screen, but it really can be whatever the application
+chooses to show on starting).
+
+## Cobalt splash screen priority order
+
+The Cobalt splash screen must be specified as a URL to a document. The document
+must be either self-contained or all of its references must be local. That means
+the document should not reference any external CSS, JavaScript, or image files,
+for example. This simplifies the caching process so that only a single document
+must be cached without tracing references. All fallback splash screens must
+refer to local documents. This is so the fallback splash screen can be shown
+without latency and even when there is no network available. Specifically, the
+fallback splash screen URL and its references should start with either
+`file:///` or `h5vcc-embedded://`. Additionally `none` can be used to specify
+that no Cobalt splash screen should be constructed; the system splash sequence
+transitions directly into the application splash sequence once the page is
+loaded.
+
+The Cobalt splash screen is one of the following, in order of precedence:
+
+ 1. **Web cached splash screen:** If a splash screen specified by a web
+ application is cached from a previous instance of Cobalt, it will be loaded
+ at startup. The key for the cache splash screen is based on host & path of
+ the initial URL with no query or hash. If network connectivity is available
+ at startup, when the initial web application URL is processed, a custom
+ `rel="splashscreen"` attribute of the link element is used to specify and
+ cache the splashscreen URL for future runs.
+
+ 2. **Command line fallback splash screen:** This is specified as a command
+ line argument `--fallback_splash_screen_url` via the system and used when
+ cache is unavailable. This is the case when there is no local cache
+ storage, cache has been cleared, or the application is started for the
+ 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.
+
+ 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.
+
+## Web-updatability
+
+Since Cobalt parses the link element's `rel="splashscreen"` attribute for the
+splash screen URL in the content fetched from the initial URL, an application
+developer may update the splash screen by changing that attribute in the link
+element. On the next load of the application, the new splash screen will be
+cached, and on the subsequent load of the application, the new cached splash
+screen will be shown.
+
+For example, the document at the initial URL could contain
+```
+<link rel="splashscreen" href="https://www.example.com/self-contained.html">
+```
+where `"https://www.example.com/self-contained.html"` is the address of some
+self-contained splash screen document. The document must not violate the Content
+Security Policy. The splash screen is treated as a script resource by the CSP.
+
+## Application-specific splash screens
+
+On systems that plan to support multiple Cobalt-based applications, an
+application developer may wish to use the command line arguments for the
+fallback splash screen to display different Cobalt splash screens for different
+applications. The logic for passing in these different command line arguments to
+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.
+
+## Provided embedded resource splash screens
+For convenience, we currently provide the following splash screens as embedded
+resources:
+
+ * `h5vcc-embedded://black_splash_screen.html` - a black splash screen
+ * `h5vcc-embedded://cobalt_splash_screen.html` - a splash screen showing the
+ Cobalt logo
+ * `h5vcc-embedded://youtube_splash_screen.html` - a splash screen showing the
+ YouTube logo
diff --git a/src/cobalt/dom/array_buffer.cc b/src/cobalt/dom/array_buffer.cc
index 9b2d29a..ac89aff 100644
--- a/src/cobalt/dom/array_buffer.cc
+++ b/src/cobalt/dom/array_buffer.cc
@@ -25,6 +25,18 @@
namespace cobalt {
namespace dom {
+namespace {
+
+int ClampIndex(int index, int length) {
+ if (index < 0) {
+ index = length + index;
+ }
+ index = std::max(index, 0);
+ return std::min(index, length);
+}
+
+} // namespace
+
ArrayBuffer::Data::Data(script::EnvironmentSettings* settings, size_t size)
: allocator_(NULL), cache_(NULL), offloaded_(false), data_(NULL), size_(0) {
Initialize(settings, size);
@@ -216,26 +228,12 @@
void ArrayBuffer::ClampRange(int start, int end, int source_length,
int* clamped_start, int* clamped_end) {
- // Clamp out of range start/end to valid indices.
- if (start > source_length) {
- start = source_length;
- }
- if (end > source_length) {
- end = source_length;
- }
-
- // Wrap around negative indices.
- if (start < 0) {
- start = source_length + start;
- }
- if (end < 0) {
- end = source_length + end;
- }
+ start = ClampIndex(start, source_length);
+ end = ClampIndex(end, source_length);
// Clamp the length of the new array to non-negative.
if (end - start < 0) {
- start = 0;
- end = 0;
+ end = start;
}
*clamped_start = start;
*clamped_end = end;
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index aaa034b..d0a5b39 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -135,6 +135,8 @@
&CspDelegate::CanLoad, base::Unretained(csp_delegate_.get()),
CspDelegate::kImage));
}
+
+ ready_state_ = kDocumentReadyStateLoading;
}
// Sample the timeline upon initialization.
@@ -906,6 +908,15 @@
UpdateComputedStyles();
}
+ // Adjust the document ready state to reflect the fact that the document has
+ // finished loading. Performing this update and firing the readystatechange
+ // event before the load event matches Chromium's behavior.
+ ready_state_ = kDocumentReadyStateComplete;
+
+ // Dispatch the readystatechange event (before the load event), since we
+ // have changed the document ready state.
+ DispatchEvent(new Event(base::Tokens::readystatechange()));
+
// Dispatch the document's onload event.
DispatchEvent(new Event(base::Tokens::load()));
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index da896bf..2995d8b 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -33,6 +33,7 @@
#include "cobalt/cssom/selector_tree.h"
#include "cobalt/cssom/style_sheet_list.h"
#include "cobalt/dom/csp_delegate_type.h"
+#include "cobalt/dom/document_ready_state.h"
#include "cobalt/dom/document_timeline.h"
#include "cobalt/dom/event.h"
#include "cobalt/dom/html_element_context.h"
@@ -216,6 +217,11 @@
void set_cookie(const std::string& cookie);
std::string cookie() const;
+ // Returns the document's ready state, i.e. whether the document's 'load'
+ // event has fired yet or not.
+ // https://www.w3.org/TR/html5/dom.html#dom-document-readystate
+ DocumentReadyState ready_state() const { return ready_state_; }
+
// Custom, not in any spec: Node.
//
Document* AsDocument() OVERRIDE { return this; }
@@ -469,6 +475,10 @@
initial_computed_style_declaration_;
scoped_refptr<const cssom::CSSComputedStyleData> initial_computed_style_data_;
+ // The document's current ready state (e.g. has the 'load' event been fired
+ // yet)
+ DocumentReadyState ready_state_;
+
// The max depth of elements that are guaranteed to be rendered.
int dom_max_element_depth_;
diff --git a/src/cobalt/dom/document_html5.idl b/src/cobalt/dom/document_html5.idl
index df87548..1ea8041 100644
--- a/src/cobalt/dom/document_html5.idl
+++ b/src/cobalt/dom/document_html5.idl
@@ -27,4 +27,5 @@
readonly attribute Element? activeElement;
[LenientThis] attribute EventHandler onreadystatechange;
+ readonly attribute DocumentReadyState readyState;
};
diff --git a/src/starboard/win/console/atomic_public.h b/src/cobalt/dom/document_ready_state.idl
similarity index 75%
copy from src/starboard/win/console/atomic_public.h
copy to src/cobalt/dom/document_ready_state.idl
index 51f81a1..2b26c4d 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/cobalt/dom/document_ready_state.idl
@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+// https://www.w3.org/TR/html5/dom.html#the-document-object
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif // STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+enum DocumentReadyState {
+ "loading",
+ "interactive",
+ "complete",
+};
diff --git a/src/cobalt/dom/html_collection.cc b/src/cobalt/dom/html_collection.cc
index 5bff1e3..98381f7 100644
--- a/src/cobalt/dom/html_collection.cc
+++ b/src/cobalt/dom/html_collection.cc
@@ -62,7 +62,7 @@
void MaybeRefreshCollection() const;
// Base node that was used to generate the collection.
- const scoped_refptr<const Node> base_;
+ const base::WeakPtr<const Node> base_;
// Predicate callback.
const Predicate predicate_;
// Generation of the base node that was used to create the cache.
@@ -74,14 +74,19 @@
template <typename NodeIterator>
NodeCollection<NodeIterator>::NodeCollection(
const scoped_refptr<const Node>& base, const Predicate& predicate)
- : base_(base),
+ : base_(base::AsWeakPtr(const_cast<Node*>(base.get()))),
predicate_(predicate),
base_node_generation_(Node::kInvalidNodeGeneration) {}
template <typename NodeIterator>
void NodeCollection<NodeIterator>::MaybeRefreshCollection() const {
- if (base_node_generation_ != base_->node_generation()) {
- NodeIterator iterator(base_);
+ scoped_refptr<const Node> base(base_);
+ if (!base) {
+ return;
+ }
+
+ if (base_node_generation_ != base->node_generation()) {
+ NodeIterator iterator(base);
cached_collection_.clear();
Node* child = iterator.First();
@@ -91,7 +96,7 @@
}
child = iterator.Next();
}
- base_node_generation_ = base_->node_generation();
+ base_node_generation_ = base->node_generation();
}
}
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc
index ade761c..ac0f16f 100644
--- a/src/cobalt/dom/html_link_element.cc
+++ b/src/cobalt/dom/html_link_element.cc
@@ -14,7 +14,9 @@
#include "cobalt/dom/html_link_element.h"
+#include <algorithm>
#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/debug/trace_event.h"
@@ -23,18 +25,41 @@
#include "cobalt/dom/csp_delegate.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/window.h"
#include "googleurl/src/gurl.h"
#include "nb/memory_scope.h"
namespace cobalt {
namespace dom {
+namespace {
+
+CspDelegate::ResourceType GetCspResourceTypeForRel(const std::string& rel) {
+ if (rel == "stylesheet") {
+ return CspDelegate::kStyle;
+ } else if (rel == "splashscreen") {
+ return CspDelegate::kScript;
+ } else {
+ NOTIMPLEMENTED();
+ return CspDelegate::kImage;
+ }
+}
+
+bool IsRelContentCriticalResource(const std::string& rel) {
+ return rel == "stylesheet";
+}
+
+} // namespace
// static
const char HTMLLinkElement::kTagName[] = "link";
+// static
+const std::vector<std::string> HTMLLinkElement::kSupportedRelValues = {
+ "stylesheet", "splashscreen"};
void HTMLLinkElement::OnInsertedIntoDocument() {
HTMLElement::OnInsertedIntoDocument();
- if (rel() == "stylesheet") {
+ if (std::find(kSupportedRelValues.begin(), kSupportedRelValues.end(),
+ rel()) != kSupportedRelValues.end()) {
Obtain();
} else {
LOG(WARNING) << "<link> has unsupported rel value: " << rel() << ".";
@@ -61,7 +86,7 @@
Document* document = node_document();
// If the document has no browsing context, do not obtain, parse or apply the
- // style sheet.
+ // resource.
if (!document->html_element_context()) {
return;
}
@@ -89,7 +114,7 @@
// the default origin behaviour set to taint.
csp::SecurityCallback csp_callback = base::Bind(
&CspDelegate::CanLoad, base::Unretained(document->csp_delegate()),
- CspDelegate::kStyle);
+ GetCspResourceTypeForRel(rel()));
loader_ = make_scoped_ptr(new loader::Loader(
base::Bind(
@@ -100,10 +125,12 @@
base::Bind(&HTMLLinkElement::OnLoadingDone, base::Unretained(this)))),
base::Bind(&HTMLLinkElement::OnLoadingError, base::Unretained(this))));
- // The element must delay the load event of the element's document until all
- // the attempts to obtain the resource and its critical subresources are
- // complete.
- document->IncreaseLoadingCounter();
+ if (IsRelContentCriticalResource(rel())) {
+ // The element must delay the load event of the element's document until all
+ // the attempts to obtain the resource and its critical subresources are
+ // complete.
+ document->IncreaseLoadingCounter();
+ }
}
void HTMLLinkElement::OnLoadingDone(const std::string& content) {
@@ -112,25 +139,29 @@
TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::OnLoadingDone()");
Document* document = node_document();
- scoped_refptr<cssom::CSSStyleSheet> style_sheet =
- document->html_element_context()->css_parser()->ParseStyleSheet(
- content, base::SourceLocation(href(), 1, 1));
- style_sheet->SetLocationUrl(absolute_url_);
- document->style_sheets()->Append(style_sheet);
-
- // Once the attempts to obtain the resource and its critical subresources are
- // complete, the user agent must, if the loads were successful, queue a task
- // to fire a simple event named load at the link element, or, if the resource
- // or one of its critical subresources failed to completely load for any
- // reason (e.g. DNS error, HTTP 404 response, a connection being prematurely
- // closed, unsupported Content-Type), queue a task to fire a simple event
- // named error at the link element.
+ if (rel() == "stylesheet") {
+ OnStylesheetLoaded(document, content);
+ } else if (rel() == "splashscreen") {
+ OnSplashscreenLoaded(document, content);
+ } else {
+ NOTIMPLEMENTED();
+ return;
+ }
+ // Once the attempts to obtain the resource and its critical subresources
+ // are complete, the user agent must, if the loads were successful, queue a
+ // task to fire a simple event named load at the link element, or, if the
+ // resource or one of its critical subresources failed to completely load
+ // for any reason (e.g. DNS error, HTTP 404 response, a connection being
+ // prematurely closed, unsupported Content-Type), queue a task to fire a
+ // simple event named error at the link element.
PostToDispatchEvent(FROM_HERE, base::Tokens::load());
- // The element must delay the load event of the element's document until all
- // the attempts to obtain the resource and its critical subresources are
- // complete.
- document->DecreaseLoadingCounterAndMaybeDispatchLoadEvent();
+ if (IsRelContentCriticalResource(rel())) {
+ // The element must delay the load event of the element's document until all
+ // the attempts to obtain the resource and its critical subresources are
+ // complete.
+ document->DecreaseLoadingCounterAndMaybeDispatchLoadEvent();
+ }
MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&HTMLLinkElement::ReleaseLoader, this));
@@ -160,6 +191,26 @@
FROM_HERE, base::Bind(&HTMLLinkElement::ReleaseLoader, this));
}
+void HTMLLinkElement::OnSplashscreenLoaded(Document* document,
+ const std::string& content) {
+ scoped_refptr<Window> window = document->window();
+
+ const base::optional<base::Callback<bool(const std::string&)>>
+ splash_screen_cache_callback = window->splash_screen_cache_callback();
+ if (splash_screen_cache_callback) {
+ splash_screen_cache_callback->Run(content);
+ }
+}
+
+void HTMLLinkElement::OnStylesheetLoaded(Document* document,
+ const std::string& content) {
+ scoped_refptr<cssom::CSSStyleSheet> style_sheet =
+ document->html_element_context()->css_parser()->ParseStyleSheet(
+ content, base::SourceLocation(href(), 1, 1));
+ style_sheet->SetLocationUrl(absolute_url_);
+ document->style_sheets()->Append(style_sheet);
+}
+
void HTMLLinkElement::ReleaseLoader() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(loader_);
diff --git a/src/cobalt/dom/html_link_element.h b/src/cobalt/dom/html_link_element.h
index 6f2e021..c0abd2c 100644
--- a/src/cobalt/dom/html_link_element.h
+++ b/src/cobalt/dom/html_link_element.h
@@ -16,6 +16,7 @@
#define COBALT_DOM_HTML_LINK_ELEMENT_H_
#include <string>
+#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
@@ -34,6 +35,7 @@
class HTMLLinkElement : public HTMLElement {
public:
static const char kTagName[];
+ static const std::vector<std::string> kSupportedRelValues;
explicit HTMLLinkElement(Document* document)
: HTMLElement(document, base::Token(kTagName)) {}
@@ -68,6 +70,8 @@
void OnLoadingDone(const std::string& content);
void OnLoadingError(const std::string& error);
+ void OnSplashscreenLoaded(Document* document, const std::string& content);
+ void OnStylesheetLoaded(Document* document, const std::string& content);
void ReleaseLoader();
// Thread checker ensures all calls to DOM element are made from the same
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index dd2e52b..169ea07 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -215,15 +215,15 @@
html_element_context()->can_play_type_handler()->CanPlayType(mime_type,
"");
MLOG() << "(" << mime_type << ") => " << result;
- DLOG(INFO) << "HTMLMediaElement::canPlayType(" << mime_type << ") -> "
- << result;
+ LOG(INFO) << "HTMLMediaElement::canPlayType(" << mime_type << ") -> "
+ << result;
#else // defined(COBALT_MEDIA_SOURCE_2016)
std::string result =
html_element_context()->can_play_type_handler()->CanPlayType(mime_type,
key_system);
MLOG() << "(" << mime_type << ", " << key_system << ") => " << result;
- DLOG(INFO) << "HTMLMediaElement::canPlayType(" << mime_type << ", "
- << key_system << ") -> " << result;
+ LOG(INFO) << "HTMLMediaElement::canPlayType(" << mime_type << ", "
+ << key_system << ") -> " << result;
#endif // defined(COBALT_MEDIA_SOURCE_2016)
return result;
}
diff --git a/src/cobalt/dom/keyboard_event.cc b/src/cobalt/dom/keyboard_event.cc
index 8b3356e..5439346 100644
--- a/src/cobalt/dom/keyboard_event.cc
+++ b/src/cobalt/dom/keyboard_event.cc
@@ -52,6 +52,7 @@
KeyboardEvent::KeyboardEvent(UninitializedFlag uninitialized_flag)
: UIEventWithKeyState(uninitialized_flag),
+ key_location_(kDomKeyLocationStandard),
key_code_(0),
char_code_(0),
repeat_(false) {}
diff --git a/src/cobalt/dom/media_source/media_source.cc b/src/cobalt/dom/media_source/media_source.cc
index c3fab91..e90cf46 100644
--- a/src/cobalt/dom/media_source/media_source.cc
+++ b/src/cobalt/dom/media_source/media_source.cc
@@ -319,17 +319,17 @@
SbMediaSupportType support_type =
SbMediaCanPlayMimeAndKeySystem(type.c_str(), "");
if (support_type == kSbMediaSupportTypeNotSupported) {
- DLOG(INFO) << "MediaSource::IsTypeSupported(" << type
- << ") -> not supported/false";
+ LOG(INFO) << "MediaSource::IsTypeSupported(" << type
+ << ") -> not supported/false";
return false;
}
if (support_type == kSbMediaSupportTypeMaybe) {
- DLOG(INFO) << "MediaSource::IsTypeSupported(" << type << ") -> maybe/true";
+ LOG(INFO) << "MediaSource::IsTypeSupported(" << type << ") -> maybe/true";
return true;
}
if (support_type == kSbMediaSupportTypeProbably) {
- DLOG(INFO) << "MediaSource::IsTypeSupported(" << type
- << ") -> probably/true";
+ LOG(INFO) << "MediaSource::IsTypeSupported(" << type
+ << ") -> probably/true";
return true;
}
NOTREACHED();
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index 733bfd2..82f47a1 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -340,8 +340,11 @@
return PreRemove(node);
}
-scoped_refptr<HTMLCollection> Node::children() const {
- return HTMLCollection::CreateWithChildElements(this);
+scoped_refptr<HTMLCollection> Node::children() {
+ if (!children_collection_) {
+ children_collection_ = HTMLCollection::CreateWithChildElements(this);
+ }
+ return children_collection_;
}
Element* Node::first_element_child() const {
@@ -491,7 +494,10 @@
while (node) {
node->parent_ = NULL;
node->next_sibling_ = NULL;
- node = node->previous_sibling_;
+
+ Node* previous_sibling = node->previous_sibling_;
+ node->previous_sibling_ = NULL;
+ node = previous_sibling;
}
--(node_count_log.Get().count);
GlobalStats::GetInstance()->Remove(this);
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index c32f3f1..c1ceb48 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -157,7 +157,7 @@
// objects that can have children.
// https://www.w3.org/TR/dom/#parentnode
//
- scoped_refptr<HTMLCollection> children() const;
+ scoped_refptr<HTMLCollection> children();
Element* first_element_child() const;
Element* last_element_child() const;
unsigned int child_element_count() const;
@@ -315,6 +315,8 @@
RegisteredObserverList registered_observers_;
+ scoped_refptr<HTMLCollection> children_collection_;
+
DISALLOW_COPY_AND_ASSIGN(Node);
};
diff --git a/src/cobalt/dom/pointer_event.idl b/src/cobalt/dom/pointer_event.idl
index 0fd4b20..94a2bfa 100644
--- a/src/cobalt/dom/pointer_event.idl
+++ b/src/cobalt/dom/pointer_event.idl
@@ -12,7 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// https://www.w3.org/TR/2015/REC-pointerevents-20150224/
+// https://www.w3.org/TR/2015/REC-pointerevents-20150224/#pointerevent-interface
-[Constructor(DOMString type)]
-interface PointerEvent : MouseEvent {};
+[Constructor(DOMString type, optional PointerEventInit eventInitDict)]
+interface PointerEvent : MouseEvent {
+ readonly attribute long pointerId;
+ readonly attribute double width;
+ readonly attribute double height;
+ readonly attribute float pressure;
+ readonly attribute long tiltX;
+ readonly attribute long tiltY;
+ readonly attribute DOMString pointerType;
+ readonly attribute boolean isPrimary;
+};
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index eaba14d..7252556 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -104,7 +104,9 @@
const scoped_refptr<input::Camera3D>& camera_3d,
const scoped_refptr<MediaSession>& media_session,
int csp_insecure_allowed_token, int dom_max_element_depth,
- float video_playback_rate_multiplier, ClockType clock_type)
+ float video_playback_rate_multiplier, ClockType clock_type,
+ const base::Callback<bool(const std::string&)>&
+ splash_screen_cache_callback)
: width_(width),
height_(height),
device_pixel_ratio_(device_pixel_ratio),
@@ -154,7 +156,8 @@
ran_animation_frame_callbacks_callback_(
ran_animation_frame_callbacks_callback),
window_close_callback_(window_close_callback),
- window_minimize_callback_(window_minimize_callback) {
+ window_minimize_callback_(window_minimize_callback),
+ splash_screen_cache_callback_(splash_screen_cache_callback) {
#if !defined(ENABLE_TEST_RUNNER)
UNREFERENCED_PARAMETER(clock_type);
#endif
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 8c669e8..8e95a10 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -142,7 +142,10 @@
const scoped_refptr<cobalt::media_session::MediaSession>& media_session,
int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0,
float video_playback_rate_multiplier = 1.f,
- ClockType clock_type = kClockTypeSystemTime);
+ ClockType clock_type = kClockTypeSystemTime,
+ const base::Callback<bool(const std::string&)>&
+ splash_screen_cache_callback =
+ base::Callback<bool(const std::string&)>());
// Web API: Window
//
@@ -316,6 +319,11 @@
DEFINE_WRAPPABLE_TYPE(Window);
+ const base::Callback<bool(const std::string&)> splash_screen_cache_callback()
+ const {
+ return splash_screen_cache_callback_;
+ }
+
private:
void StartDocumentLoad(
loader::FetcherFactory* fetcher_factory, const GURL& url,
@@ -363,6 +371,8 @@
const base::Closure window_close_callback_;
const base::Closure window_minimize_callback_;
+ base::Callback<bool(const std::string&)> splash_screen_cache_callback_;
+
DISALLOW_COPY_AND_ASSIGN(Window);
};
diff --git a/src/cobalt/input/input_device_manager_desktop.cc b/src/cobalt/input/input_device_manager_desktop.cc
index 946c261..7d1eb9a 100644
--- a/src/cobalt/input/input_device_manager_desktop.cc
+++ b/src/cobalt/input/input_device_manager_desktop.cc
@@ -14,6 +14,7 @@
#include "cobalt/input/input_device_manager_desktop.h"
+#include <cmath>
#include <string>
#include "cobalt/base/token.h"
@@ -155,6 +156,11 @@
mouse_event->set_client_y(static_cast<float>(input_event->position().y()));
}
+// Returns the value or the default_value when value is NaN.
+float value_or(float value, float default_value) {
+ return std::isnan(value) ? default_value : value;
+}
+
} // namespace
void InputDeviceManagerDesktop::HandleKeyboardEvent(
@@ -180,11 +186,14 @@
pointer_event.set_pointer_id(input_event->device_id());
#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
- pointer_event.set_width(input_event->size().x());
- pointer_event.set_height(input_event->size().y());
- pointer_event.set_pressure(input_event->pressure());
- pointer_event.set_tilt_x(static_cast<float>(input_event->tilt().x()));
- pointer_event.set_tilt_y(static_cast<float>(input_event->tilt().y()));
+ pointer_event.set_width(value_or(input_event->size().x(), 0.0f));
+ pointer_event.set_height(value_or(input_event->size().y(), 0.0f));
+ pointer_event.set_pressure(value_or(input_event->pressure(),
+ input_event->modifiers() ? 0.5f : 0.0f));
+ pointer_event.set_tilt_x(
+ value_or(static_cast<float>(input_event->tilt().x()), 0.0f));
+ pointer_event.set_tilt_y(
+ value_or(static_cast<float>(input_event->tilt().y()), 0.0f));
#endif
pointer_event.set_is_primary(true);
pointer_event_callback_.Run(type, pointer_event);
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index d07fc5f..a65f43f 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -518,7 +518,7 @@
}
const bool overflow_hidden =
- computed_style()->overflow().get() == cssom::KeywordValue::GetHidden();
+ computed_style()->overflow() == cssom::KeywordValue::GetHidden();
bool overflow_hidden_needs_to_be_applied = overflow_hidden;
@@ -1434,8 +1434,7 @@
const scoped_refptr<render_tree::Node>& content_node,
AnimateNode::Builder* /* animate_node_builder */,
const math::Vector2dF& border_node_offset) {
- DCHECK_EQ(computed_style()->overflow().get(),
- cssom::KeywordValue::GetHidden());
+ DCHECK_EQ(computed_style()->overflow(), cssom::KeywordValue::GetHidden());
// The "overflow" property specifies whether a box is clipped to its padding
// edge. Use a render_tree viewport filter to implement it.
diff --git a/src/cobalt/layout/container_box.cc b/src/cobalt/layout/container_box.cc
index b7edf46..e64f3b2 100644
--- a/src/cobalt/layout/container_box.cc
+++ b/src/cobalt/layout/container_box.cc
@@ -135,41 +135,34 @@
// Invalidate the size now that the children have changed.
update_size_results_valid_ = false;
- // Children are only being removed from this container. As a result, the cross
- // references only need to be invalidated if there is a non-empty cross
- // reference list that can potentially lose an element.
+ // Handle invalidating cross references. Children are only being removed from
+ // this container, so cross references only need to be invalidated if there is
+ // a non-empty cross reference list that can potentially be impacted.
+
+ // If there are any positioned boxes, then they need to be re-generated.
if (!positioned_child_boxes_.empty()) {
are_cross_references_valid_ = false;
}
- if (!negative_z_index_stacking_context_children_.empty() ||
- !non_negative_z_index_stacking_context_children_.empty()) {
- // If this box is a stacking context, then any stacking context children
- // were added directly by it; otherwise, the stacking context children were
- // added by the containing stacking context (only a stacking context can
- // cause stacking context children to be added).
- if (IsStackingContext()) {
- are_cross_references_valid_ = false;
- } else {
- DCHECK(negative_z_index_stacking_context_children_.empty());
- GetStackingContext()->are_cross_references_valid_ = false;
+
+ // There are two cases where the stacking context's cross references can be
+ // impacted by children moving from one container to another. With both cases,
+ // stacking context children must exist or there is nothing to update.
+ // 1. Stacking context children are potentially moving from this child
+ // container to the split sibling child container.
+ // 2. Stacking context children contained within this overflow hidden
+ // container are potentially moving to the split sibling overflow hidden
+ // container.
+ if (HasStackingContextChildren() ||
+ computed_style()->overflow() == cssom::KeywordValue::GetHidden()) {
+ // Walk up the tree until the nearest stacking context is found. If this box
+ // is a stacking context, then it will be used.
+ ContainerBox* nearest_stacking_context = this;
+ while (!nearest_stacking_context->IsStackingContext()) {
+ nearest_stacking_context = nearest_stacking_context->parent();
}
- } else if (computed_style()->overflow().get() ==
- cssom::KeywordValue::GetHidden() &&
- !IsStackingContext()) {
- // If this container box hides overflow and isn't a stacking context, then
- // the nearest ancestor that is a stacking context needs to be invalidated.
- // The reason for this is that the ancestor stacking context may have
- // stacking context children that include this container as an overflow
- // hidden containing block.
- // NOTE: GetStackingContext() can't simply be called, because that will
- // incorrectly return the parent, regardless of whether or not it is a
- // stacking context, in the case where the containing block is not
- // positioned.
- ContainerBox* stacking_context_ancestor = parent();
- while (!stacking_context_ancestor->IsStackingContext()) {
- stacking_context_ancestor = stacking_context_ancestor->parent();
+ if (nearest_stacking_context->HasStackingContextChildren()) {
+ nearest_stacking_context->are_cross_references_valid_ = false;
}
- stacking_context_ancestor->are_cross_references_valid_ = false;
}
// Invalidate the render tree nodes now that the children have changed.
@@ -278,6 +271,11 @@
overflow_hidden_to_apply));
}
+bool ContainerBox::HasStackingContextChildren() const {
+ return !negative_z_index_stacking_context_children_.empty() ||
+ !non_negative_z_index_stacking_context_children_.empty();
+}
+
namespace {
Vector2dLayoutUnit GetOffsetFromContainingBlockToParent(Box* child_box) {
@@ -288,9 +286,8 @@
ancestor_box = ancestor_box->parent()) {
DCHECK(ancestor_box)
<< "Unable to find containing block while traversing parents.";
- // We should not determine a used position through a transform, as
- // rectangles may not remain rectangles past it, and thus obtaining
- // a position may be misleading.
+ // It is not possible for the containing block to be more distant than an
+ // ancestor that is transformed.
DCHECK(!ancestor_box->IsTransformed());
relative_position += ancestor_box->GetContentBoxOffsetFromMarginBox();
@@ -343,8 +340,8 @@
UpdateRectOfAbsolutelyPositionedChildBox(child_box,
absolute_child_layout_params);
} else if (child_box_position == cssom::KeywordValue::GetFixed()) {
- UpdateRectOfFixedPositionedChildBox(child_box,
- relative_child_layout_params);
+ UpdateRectOfAbsolutelyPositionedChildBox(child_box,
+ relative_child_layout_params);
} else {
DCHECK(child_box_position == cssom::KeywordValue::GetRelative());
UpdateOffsetOfRelativelyPositionedChildBox(child_box,
@@ -422,17 +419,6 @@
child_box->set_top(child_box->top() + offset.y());
}
-void ContainerBox::UpdateRectOfFixedPositionedChildBox(
- Box* child_box, const LayoutParams& child_layout_params) {
- Vector2dLayoutUnit offset_from_containing_block_to_parent =
- GetOffsetFromContainingBlockToParent(child_box);
- child_box->SetStaticPositionLeftFromContainingBlockToParent(
- offset_from_containing_block_to_parent.x());
- child_box->SetStaticPositionTopFromContainingBlockToParent(
- offset_from_containing_block_to_parent.y());
- child_box->UpdateSize(child_layout_params);
-}
-
void ContainerBox::UpdateRectOfAbsolutelyPositionedChildBox(
Box* child_box, const LayoutParams& child_layout_params) {
Vector2dLayoutUnit offset_from_containing_block_to_parent =
@@ -440,6 +426,10 @@
// The containing block is formed by the padding box instead of the content
// box, as described in
// http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+ // NOTE: While not explicitly stated in the spec, which specifies that
+ // the containing block of a 'fixed' position element must always be the
+ // viewport, all major browsers use the padding box of a transformed ancestor
+ // as the containing block for 'fixed' position elements.
offset_from_containing_block_to_parent += GetContentBoxOffsetFromPaddingBox();
child_box->SetStaticPositionLeftFromContainingBlockToParent(
offset_from_containing_block_to_parent.x());
@@ -531,11 +521,14 @@
GetOffsetFromChildContainerToContainingBlock(
child_containing_block, child_info.containing_block_relationship) +
child_container_offset_from_parent_node_;
- if (child_info.box->computed_style()->position() ==
- cssom::KeywordValue::GetAbsolute()) {
+ if (child_info.box->IsAbsolutelyPositioned()) {
// The containing block is formed by the padding box instead of the content
// box, as described in
// http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+ // NOTE: While not explicitly stated in the spec, which specifies that
+ // the containing block of a 'fixed' position element must always be the
+ // viewport, all major browsers use the padding box of a transformed
+ // ancestor as the containing block for 'fixed' position elements.
position_offset -=
child_containing_block->GetContentBoxOffsetFromPaddingBox();
}
@@ -583,7 +576,7 @@
OverflowHiddenInfo& overflow_hidden_info = overflow_hidden_stack_.back();
ContainerBox* containing_block = overflow_hidden_info.containing_block;
- DCHECK_EQ(containing_block->computed_style()->overflow().get(),
+ DCHECK_EQ(containing_block->computed_style()->overflow(),
cssom::KeywordValue::GetHidden());
// Determine the offset from the child container to this containing block's
@@ -619,72 +612,66 @@
const Box* containing_block,
const Box::RelationshipToBox
containing_block_relationship_to_child_container) const {
+ if (containing_block_relationship_to_child_container == Box::kIsBox) {
+ DCHECK_EQ(child_container_, containing_block);
+ return Vector2dLayoutUnit();
+ }
+
Vector2dLayoutUnit relative_position;
- if (containing_block_relationship_to_child_container != Box::kIsBox) {
- const Box* current_box =
- containing_block_relationship_to_child_container == Box::kIsBoxAncestor
- ? child_container_
- : containing_block;
- const Box* end_box =
- containing_block_relationship_to_child_container == Box::kIsBoxAncestor
- ? containing_block
- : child_container_;
+ const Box* current_box =
+ containing_block_relationship_to_child_container == Box::kIsBoxAncestor
+ ? child_container_
+ : containing_block;
+ const Box* end_box =
+ containing_block_relationship_to_child_container == Box::kIsBoxAncestor
+ ? containing_block
+ : child_container_;
- while (current_box != end_box) {
-#if !defined(NDEBUG)
- // We should not determine a used position through a transform, as
- // rectangles may not remain rectangles past it, and thus obtaining a
- // position may be misleading.
- if (current_box->IsTransformed()) {
- DLOG(WARNING) << "Containing block offset calculations that include "
- "transforms may not be positioned correctly.";
- }
-#endif
+ // Walk up the containing blocks from |current_box| to |end_box| adding the
+ // offsets from each box to its containing block.
+ // NOTE: |end_box| can be skipped during this walk both when |end_box| is not
+ // positioned and when a fixed position box is encountered during the walk. In
+ // this case, the walk will end when a box is found that either does not have
+ // a parent (meaning that it's the root box) or is transformed (it is
+ // impossible for |end_box| to be a more distant ancestor than a transformed
+ // box).
+ while (current_box != end_box && current_box->parent() &&
+ !current_box->IsTransformed()) {
+ relative_position += current_box->GetContentBoxOffsetFromMarginBox();
+ relative_position += current_box->margin_box_offset_from_containing_block();
- relative_position += current_box->GetContentBoxOffsetFromMarginBox();
- relative_position +=
- current_box->margin_box_offset_from_containing_block();
-
- const Box* next_box = current_box->GetContainingBlock();
- if (!next_box) {
- break;
- }
-
- if (current_box->computed_style()->position() ==
- cssom::KeywordValue::GetAbsolute()) {
- relative_position -= next_box->GetContentBoxOffsetFromPaddingBox();
- }
- current_box = next_box;
+ const Box* next_box = current_box->GetContainingBlock();
+ if (current_box->IsAbsolutelyPositioned()) {
+ relative_position -= next_box->GetContentBoxOffsetFromPaddingBox();
}
+ current_box = next_box;
+ }
- // If |current_box| does not equal |end_box|, then |end_box| was skipped
- // during the walk up the tree. Initiate a second walk up the tree from the
- // end box to the root (which is where the first walk ended).
- // The end box can be skipped during the initial walk both when the end box
- // is not positioned and also when a fixed position box is encountered
- // during the walk. Subtract the offsets during this walk to remove the
- // extra offsets added after passing the end box during the first walk.
- std::swap(current_box, end_box);
- while (current_box != end_box) {
- relative_position -= current_box->GetContentBoxOffsetFromMarginBox();
- relative_position -=
- current_box->margin_box_offset_from_containing_block();
+ // If |current_box| does not equal |end_box|, then |end_box| was skipped
+ // during the walk up the tree. Initiate a second walk up the tree from the
+ // end box to the box where the first walk ended, subtracting the offsets
+ // during this walk to remove the extra offsets added after passing |end_box|
+ // during the first walk.
+ std::swap(current_box, end_box);
+ while (current_box != end_box) {
+ DCHECK(current_box->parent());
+ DCHECK(!current_box->IsTransformed());
- const Box* next_box = current_box->GetContainingBlock();
- if (current_box->computed_style()->position() ==
- cssom::KeywordValue::GetAbsolute()) {
- relative_position += next_box->GetContentBoxOffsetFromPaddingBox();
- }
- current_box = next_box;
+ relative_position -= current_box->GetContentBoxOffsetFromMarginBox();
+ relative_position -= current_box->margin_box_offset_from_containing_block();
+
+ const Box* next_box = current_box->GetContainingBlock();
+ if (current_box->IsAbsolutelyPositioned()) {
+ relative_position += next_box->GetContentBoxOffsetFromPaddingBox();
}
+ current_box = next_box;
+ }
- // If the containing block is an ancestor of the child container, then
- // reverse the relative position now. The earlier calculations were for the
- // containing block being a descendant of the child container.
- if (containing_block_relationship_to_child_container ==
- Box::kIsBoxAncestor) {
- relative_position = -relative_position;
- }
+ // If the containing block is an ancestor of the child container, then
+ // reverse the relative position now. The earlier calculations were for the
+ // containing block being a descendant of the child container.
+ if (containing_block_relationship_to_child_container == Box::kIsBoxAncestor) {
+ relative_position = -relative_position;
}
return relative_position;
@@ -788,7 +775,7 @@
bool has_absolute_position =
computed_style()->position() == cssom::KeywordValue::GetAbsolute();
bool has_overflow_hidden =
- computed_style()->overflow().get() == cssom::KeywordValue::GetHidden();
+ computed_style()->overflow() == cssom::KeywordValue::GetHidden();
stacking_context_container_box_stack->push_back(
StackingContextContainerBoxInfo(
diff --git a/src/cobalt/layout/container_box.h b/src/cobalt/layout/container_box.h
index 810dcc4..3d184c4 100644
--- a/src/cobalt/layout/container_box.h
+++ b/src/cobalt/layout/container_box.h
@@ -172,6 +172,9 @@
const ContainingBlocksWithOverflowHidden&
containing_blocks_with_overflow_hidden_to_apply);
+ // Returns whether or not the container has any stacking context children.
+ bool HasStackingContextChildren() const;
+
// Updates used values of left/top/right/bottom given the child_box's
// 'position' property is set to 'relative'.
// https://www.w3.org/TR/CSS21/visuren.html#relative-positioning
@@ -182,13 +185,6 @@
// This is meant to be called by UpdateRectOfPositionedChildBoxes(), after the
// child has gone through the in-flow layout.
// https://www.w3.org/TR/CSS21/visuren.html#absolute-positioning
- void UpdateRectOfFixedPositionedChildBox(
- Box* child_box, const LayoutParams& child_layout_params);
-
- // Updates the sizes of the absolutely positioned child box.
- // This is meant to be called by UpdateRectOfPositionedChildBoxes(), after the
- // child has gone through the in-flow layout.
- // https://www.w3.org/TR/CSS21/visuren.html#absolute-positioning
void UpdateRectOfAbsolutelyPositionedChildBox(
Box* child_box, const LayoutParams& child_layout_params);
diff --git a/src/cobalt/layout/layout.gyp b/src/cobalt/layout/layout.gyp
index f13959e..6b6031e 100644
--- a/src/cobalt/layout/layout.gyp
+++ b/src/cobalt/layout/layout.gyp
@@ -102,6 +102,11 @@
'export_dependent_settings': [
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
],
+ 'conditions': [
+ ['cobalt_enable_lib == 1', {
+ 'defines' : ['FORCE_VIDEO_EXTERNAL_MESH'],
+ }],
+ ],
},
{
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index e8776e2..c61322a 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -308,13 +308,24 @@
PunchThroughVideoNode::Builder builder(math::RectF(content_box_size()),
set_bounds_cb_);
border_node_builder->AddChild(new PunchThroughVideoNode(builder));
+ } else if (mtm_filter_function) {
+ RenderAndAnimateContentWithMapToMesh(border_node_builder,
+ mtm_filter_function);
} else {
- if (mtm_filter_function) {
- RenderAndAnimateContentWithMapToMesh(border_node_builder,
- mtm_filter_function);
- } else {
- RenderAndAnimateContentWithLetterboxing(border_node_builder);
- }
+#if defined(FORCE_VIDEO_EXTERNAL_MESH)
+ AnimateNode::Builder animate_node_builder;
+ scoped_refptr<ImageNode> image_node = new ImageNode(NULL);
+ animate_node_builder.Add(image_node,
+ base::Bind(&AnimateVideoImage, replace_image_cb_));
+
+ // Attach an empty map to mesh filter node to signal the need for an
+ // external mesh.
+ border_node_builder->AddChild(
+ new FilterNode(MapToMeshFilter(render_tree::kMono),
+ new AnimateNode(animate_node_builder, image_node)));
+#else
+ RenderAndAnimateContentWithLetterboxing(border_node_builder);
+#endif
}
}
@@ -585,9 +596,9 @@
if (spec.mesh_type() == cssom::MapToMeshFunction::kUrls) {
// Custom mesh URLs.
// Set a default mesh (in case no resolution-specific mesh matches).
- cssom::URLValue* spec_url_value =
+ cssom::URLValue* default_url_value =
base::polymorphic_downcast<cssom::URLValue*>(spec.mesh_url().get());
- GURL default_url(spec_url_value->value());
+ GURL default_url(default_url_value->value());
if (!default_url.is_valid()) {
DLOG(WARNING) << kWarningInvalidMeshUrl;
@@ -629,8 +640,8 @@
TRACE_EVENT2("cobalt::layout",
"ReplacedBox::RenderAndAnimateContentWithMapToMesh()",
- "height", meshes[i]->height_match(),
- "crc", mesh_projection->crc().value_or(-1));
+ "height", meshes[i]->height_match(), "crc",
+ mesh_projection->crc().value_or(-1));
builder.AddResolutionMatchedMeshes(
math::Size(meshes[i]->width_match(), meshes[i]->height_match()),
@@ -666,9 +677,9 @@
filter_node, mtm_function->horizontal_fov_in_radians(),
mtm_function->vertical_fov_in_radians()));
#else
- // Camera node unnecessary in VR, since the 3D scene is completely immersive,
- // and the whole render tree is placed within it and subject to camera
- // transforms, not just the map-to-mesh node.
+ // Camera node unnecessary in VR, since the 3D scene is completely
+ // immersive, and the whole render tree is placed within it and subject to
+ // camera transforms, not just the map-to-mesh node.
// TODO: Reconcile both paths with respect to this if Cobalt adopts a global
// camera or a document-wide notion of 3D space layout.
border_node_builder->AddChild(filter_node);
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements-expected.png
new file mode 100644
index 0000000..e7a38ac
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements.html b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements.html
new file mode 100644
index 0000000..e06c01e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!--
+ | If the element has "position: fixed", the containing block is established
+ | by the padding edge of the nearest ancestor with a transform.
+ | https://www.w3.org/TR/CSS21/visudet.html#containing-block-details
+ | NOTE: While this is not explicitly stated in the spec, which states that
+ | the containing block of a fixed position element must always be the viewport
+ | this is how all major browsers handle them.
+ -->
+<html>
+<head>
+ <style>
+ body {
+ margin: 0px;
+ }
+ .fixed-positioned {
+ position: fixed;
+ }
+ .transformed {
+ transform: rotate(0deg);
+ }
+ .level-1 {
+ background-color: #b3e5fc;
+ padding: 10px;
+ }
+ .level-2 {
+ background-color: #40c4ff;
+ padding: 20px;
+ }
+ .level-3 {
+ background-color: #00b0ff;
+ padding: 40px;
+ }
+ .level-4 {
+ background-color: #0091ea;
+ height: 120px;
+ width: 120px;
+ }
+ .top {
+ top: 0;
+ width: 60px;
+ height: 60px;
+ }
+ .fixed-positioned.level-4 {
+ background-color: #01579b;
+ }
+ </style>
+</head>
+<body>
+ <div class="fixed-positioned transformed level-1">
+ <div class="fixed-positioned transformed level-2">
+ <div class="level-3">
+ <div class="level-4"></div>
+ <div class="fixed-positioned top level-4"></div>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees-expected.png
new file mode 100644
index 0000000..6a7d50c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms-expected.png
new file mode 100644
index 0000000..9244d5d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-context-should-take-into-account-intermediate-containing-blocks-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks-expected.png
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-context-should-take-into-account-intermediate-containing-blocks-expected.png
rename to src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-context-should-take-into-account-intermediate-containing-blocks.html b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks.html
similarity index 100%
rename from src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-context-should-take-into-account-intermediate-containing-blocks.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks.html
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
index c0ef673..995b02a 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
@@ -4,6 +4,7 @@
10-1-absolute-positioned-elements-container-block-is-absolute-positioned-ancestor
10-1-absolute-positioned-elements-do-not-effect-containing-block-size
10-1-containing-block-should-be-ancestor-padding-edge-for-absolutely-positioned-elements
+10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements
10-1-containing-block-should-be-ancestor-padding-edge-for-percentage-of-absolutely-positioned-elements
10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements
10-1-non-positioned-stacking-context-above-containing-block-should-apply-proper-offset-to-children
@@ -153,6 +154,8 @@
9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling
9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context
9-9-1-simple-positive-z-indices
-9-9-1-stacking-context-should-take-into-account-intermediate-containing-blocks
+9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees
+9-9-1-stacking-contexts-and-containing-blocks-with-transforms
+9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks
9-9-1-stacking-contexts-differ-from-containing-blocks
9-9-1-z-index-should-only-be-applied-to-positioned-elements
diff --git a/src/cobalt/loader/cache_fetcher.cc b/src/cobalt/loader/cache_fetcher.cc
new file mode 100644
index 0000000..e1e7350
--- /dev/null
+++ b/src/cobalt/loader/cache_fetcher.cc
@@ -0,0 +1,105 @@
+// Copyright 2017 Google Inc. 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/loader/cache_fetcher.h"
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/debug/trace_event.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/stringprintf.h"
+
+namespace cobalt {
+namespace loader {
+
+namespace {
+bool CacheURLToKey(const GURL& url, std::string* key) {
+ DCHECK(url.is_valid() && url.SchemeIs(kCacheScheme));
+ *key = url.path();
+ DCHECK_EQ('/', (*key)[0]);
+ DCHECK_EQ('/', (*key)[1]);
+ (*key).erase(0, 2);
+ return !key->empty();
+}
+} // namespace
+
+const char kCacheScheme[] = "h5vcc-cache";
+
+CacheFetcher::CacheFetcher(
+ const GURL& url, const csp::SecurityCallback& security_callback,
+ Handler* handler,
+ const base::Callback<int(const std::string&, scoped_array<char>*)>&
+ read_cache_callback)
+ : Fetcher(handler),
+ url_(url),
+ security_callback_(security_callback),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+ read_cache_callback_(read_cache_callback) {
+ TRACE_EVENT0("cobalt::loader", "CacheFetcher::CacheFetcher");
+ // Cache assets can be loaded synchronously.
+ Fetch();
+}
+
+CacheFetcher::~CacheFetcher() {}
+
+void CacheFetcher::Fetch() {
+ if (!IsAllowedByCsp()) {
+ std::string msg(base::StringPrintf("URL %s rejected by security policy.",
+ url_.spec().c_str()));
+ handler()->OnError(this, msg);
+ return;
+ }
+
+ std::string key;
+ if (!CacheURLToKey(url_, &key)) {
+ std::string msg(
+ base::StringPrintf("Invalid cache URL: %s.", url_.spec().c_str()));
+ handler()->OnError(this, msg);
+ return;
+ }
+
+ GetCacheData(key);
+}
+
+void CacheFetcher::GetCacheData(const std::string& key) {
+ TRACE_EVENT0("cobalt::loader", "CacheFetcher::GetCacheData");
+ const char kFailedToReadCache[] = "Failed to read cache.";
+
+ DCHECK(!read_cache_callback_.is_null());
+ scoped_array<char> buffer;
+ int buffer_size = read_cache_callback_.Run(key, &buffer);
+ if (!buffer.get()) {
+ handler()->OnError(this, kFailedToReadCache);
+ return;
+ }
+
+ handler()->OnReceived(this, buffer.get(), buffer_size);
+ handler()->OnDone(this);
+}
+
+bool CacheFetcher::IsAllowedByCsp() {
+ bool did_redirect = false;
+ if (security_callback_.is_null() ||
+ security_callback_.Run(url_, did_redirect)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace loader
+} // namespace cobalt
diff --git a/src/cobalt/loader/cache_fetcher.h b/src/cobalt/loader/cache_fetcher.h
new file mode 100644
index 0000000..9c9e274
--- /dev/null
+++ b/src/cobalt/loader/cache_fetcher.h
@@ -0,0 +1,63 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_LOADER_CACHE_FETCHER_H_
+#define COBALT_LOADER_CACHE_FETCHER_H_
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cobalt/csp/content_security_policy.h"
+#include "cobalt/loader/fetcher.h"
+
+namespace cobalt {
+namespace loader {
+
+extern const char kCacheScheme[];
+
+// CacheFetcher is for fetching data in the system cache,
+// kSbSystemPathCacheDirectory. Cached splash screen HTML documents
+// are stored there, for instance. Their subpaths are based on the
+// initial URL they correspond to and must be fully specified in the
+// URL passed into CacheFetcher. See SplashScreenCache for an example usage.
+class CacheFetcher : public Fetcher {
+ public:
+ CacheFetcher(
+ const GURL& url, const csp::SecurityCallback& security_callback,
+ Handler* handler,
+ const base::Callback<int(const std::string&, scoped_array<char>*)>&
+ read_cache_callback =
+ base::Callback<int(const std::string&, scoped_array<char>*)>());
+
+ ~CacheFetcher() OVERRIDE;
+
+ private:
+ void Fetch();
+ void GetCacheData(const std::string& key);
+ bool IsAllowedByCsp();
+
+ GURL url_;
+ csp::SecurityCallback security_callback_;
+ base::WeakPtrFactory<CacheFetcher> weak_ptr_factory_;
+ base::Callback<int(const std::string&, scoped_array<char>*)>
+ read_cache_callback_;
+};
+
+} // namespace loader
+} // namespace cobalt
+
+#endif // COBALT_LOADER_CACHE_FETCHER_H_
diff --git a/src/cobalt/loader/embedded_fetcher.cc b/src/cobalt/loader/embedded_fetcher.cc
index de82486..1f73f16 100644
--- a/src/cobalt/loader/embedded_fetcher.cc
+++ b/src/cobalt/loader/embedded_fetcher.cc
@@ -36,6 +36,8 @@
}
} // namespace
+const char kEmbeddedScheme[] = "h5vcc-embedded";
+
EmbeddedFetcher::EmbeddedFetcher(const GURL& url,
const csp::SecurityCallback& security_callback,
Handler* handler, const Options& options)
diff --git a/src/cobalt/loader/embedded_fetcher.h b/src/cobalt/loader/embedded_fetcher.h
index a577556..20b1295 100644
--- a/src/cobalt/loader/embedded_fetcher.h
+++ b/src/cobalt/loader/embedded_fetcher.h
@@ -25,7 +25,7 @@
namespace cobalt {
namespace loader {
-const char kEmbeddedScheme[] = "h5vcc-embedded";
+extern const char kEmbeddedScheme[];
// EmbeddedFetcher is for fetching data embedded in the binary, probably
// generated using generate_data_header.py. Data will be in the form of a
diff --git a/src/cobalt/loader/embedded_resources/black_splash_screen.html b/src/cobalt/loader/embedded_resources/black_splash_screen.html
new file mode 100644
index 0000000..40e5bcc
--- /dev/null
+++ b/src/cobalt/loader/embedded_resources/black_splash_screen.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ Copyright 2017 Google Inc. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+
+<head>
+ <meta http-equiv="Content-Security-Policy: style-src 'sha256-xmVxpUwZn65/t+v5EPwcm3g3WxZz1bJoPW/GtjJs75I=';">
+</head>
+
+<body style="background-color: #000000;"></body>
+</html>
diff --git a/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html b/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html
index df1ac86..8c06361 100644
--- a/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html
+++ b/src/cobalt/loader/embedded_resources/cobalt_splash_screen.html
@@ -17,10 +17,10 @@
<html>
<head>
-<meta http-equiv="Content-Security-Policy" content="default-src 'none';
- script-src h5vcc-embedded://*/splash_screen.js;
- style-src h5vcc-embedded://*/cobalt_splash_screen.css;
- img-src h5vcc-embedded://*/cobalt_word_logo_1024.png;">
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none';
+ script-src h5vcc-embedded://*/splash_screen.js;
+ style-src h5vcc-embedded://*/cobalt_splash_screen.css;
+ img-src h5vcc-embedded://*/cobalt_word_logo_1024.png;">
<link rel="stylesheet" type="text/css"
href="h5vcc-embedded://cobalt_splash_screen.css">
</head>
diff --git a/src/cobalt/loader/embedded_resources/splash_screen.css b/src/cobalt/loader/embedded_resources/youtube_splash_screen.css
similarity index 100%
rename from src/cobalt/loader/embedded_resources/splash_screen.css
rename to src/cobalt/loader/embedded_resources/youtube_splash_screen.css
diff --git a/src/cobalt/loader/embedded_resources/splash_screen.html b/src/cobalt/loader/embedded_resources/youtube_splash_screen.html
similarity index 80%
rename from src/cobalt/loader/embedded_resources/splash_screen.html
rename to src/cobalt/loader/embedded_resources/youtube_splash_screen.html
index f79d132..d40bf9b 100644
--- a/src/cobalt/loader/embedded_resources/splash_screen.html
+++ b/src/cobalt/loader/embedded_resources/youtube_splash_screen.html
@@ -17,12 +17,12 @@
<html>
<head>
-<meta http-equiv="Content-Security-Policy" content="default-src 'none';
- script-src h5vcc-embedded://*/splash_screen.js;
- style-src h5vcc-embedded://*/splash_screen.css;
- img-src h5vcc-embedded://*/you_tube_logo.png;">
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none';
+ script-src h5vcc-embedded://*/splash_screen.js;
+ style-src h5vcc-embedded://*/youtube_splash_screen.css;
+ img-src h5vcc-embedded://*/you_tube_logo.png;">
<link rel="stylesheet" type="text/css"
- href="h5vcc-embedded://splash_screen.css">
+ href="h5vcc-embedded://youtube_splash_screen.css">
</head>
<body>
diff --git a/src/cobalt/loader/fetcher_factory.cc b/src/cobalt/loader/fetcher_factory.cc
index d9ea1bf..1e70cf4 100644
--- a/src/cobalt/loader/fetcher_factory.cc
+++ b/src/cobalt/loader/fetcher_factory.cc
@@ -19,9 +19,11 @@
#include "base/bind.h"
#include "base/file_path.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "cobalt/loader/about_fetcher.h"
#include "cobalt/loader/blob_fetcher.h"
+#include "cobalt/loader/cache_fetcher.h"
#include "cobalt/loader/embedded_fetcher.h"
#include "cobalt/loader/file_fetcher.h"
#include "cobalt/loader/net_fetcher.h"
@@ -56,7 +58,9 @@
} // namespace
FetcherFactory::FetcherFactory(network::NetworkModule* network_module)
- : file_thread_("File"), network_module_(network_module) {
+ : file_thread_("File"),
+ network_module_(network_module),
+ read_cache_callback_() {
file_thread_.Start();
}
@@ -64,17 +68,21 @@
const FilePath& extra_search_dir)
: file_thread_("File"),
network_module_(network_module),
- extra_search_dir_(extra_search_dir) {
+ extra_search_dir_(extra_search_dir),
+ read_cache_callback_() {
file_thread_.Start();
}
FetcherFactory::FetcherFactory(
network::NetworkModule* network_module, const FilePath& extra_search_dir,
- const BlobFetcher::ResolverCallback& blob_resolver)
+ const BlobFetcher::ResolverCallback& blob_resolver,
+ const base::Callback<int(const std::string&, scoped_array<char>*)>&
+ read_cache_callback)
: file_thread_("File"),
network_module_(network_module),
extra_search_dir_(extra_search_dir),
- blob_resolver_(blob_resolver) {
+ blob_resolver_(blob_resolver),
+ read_cache_callback_(read_cache_callback) {
file_thread_.Start();
}
@@ -121,6 +129,17 @@
"could not fetch the URL: "
<< url;
}
+ } else if (url.SchemeIs(kCacheScheme)) {
+ if (read_cache_callback_.is_null()) {
+ LOG(ERROR) << "read_cache_callback_ must be provided to CacheFetcher for "
+ "accessing h5vcc-cache:// . This is not available in the "
+ "main WebModule.";
+ DCHECK(!read_cache_callback_.is_null());
+ return fetcher.Pass();
+ }
+
+ fetcher.reset(new CacheFetcher(url, url_security_callback, handler,
+ read_cache_callback_));
} else { // NOLINT(readability/braces)
DCHECK(network_module_) << "Network module required.";
NetFetcher::Options options;
diff --git a/src/cobalt/loader/fetcher_factory.h b/src/cobalt/loader/fetcher_factory.h
index bd60a4f..a367df7 100644
--- a/src/cobalt/loader/fetcher_factory.h
+++ b/src/cobalt/loader/fetcher_factory.h
@@ -15,8 +15,11 @@
#ifndef COBALT_LOADER_FETCHER_FACTORY_H_
#define COBALT_LOADER_FETCHER_FACTORY_H_
+#include <string>
+
+#include "base/callback.h"
#include "base/file_path.h"
-#include "base/optional.h"
+#include "base/memory/scoped_ptr.h"
#include "base/threading/thread.h"
#include "cobalt/csp/content_security_policy.h"
#include "cobalt/loader/blob_fetcher.h"
@@ -35,9 +38,12 @@
explicit FetcherFactory(network::NetworkModule* network_module);
FetcherFactory(network::NetworkModule* network_module,
const FilePath& extra_search_dir);
- FetcherFactory(network::NetworkModule* network_module,
- const FilePath& extra_search_dir,
- const BlobFetcher::ResolverCallback& blob_resolver);
+ FetcherFactory(
+ network::NetworkModule* network_module, const FilePath& extra_search_dir,
+ const BlobFetcher::ResolverCallback& blob_resolver,
+ const base::Callback<int(const std::string&, scoped_array<char>*)>&
+ read_cache_callback =
+ base::Callback<int(const std::string&, scoped_array<char>*)>());
// Creates a fetcher. Returns NULL if the creation fails.
scoped_ptr<Fetcher> CreateFetcher(const GURL& url, Fetcher::Handler* handler);
@@ -53,6 +59,8 @@
network::NetworkModule* network_module_;
FilePath extra_search_dir_;
BlobFetcher::ResolverCallback blob_resolver_;
+ base::Callback<int(const std::string&, scoped_array<char>*)>
+ read_cache_callback_;
};
} // namespace loader
diff --git a/src/cobalt/loader/image/image_data_decoder.cc b/src/cobalt/loader/image/image_data_decoder.cc
index 33e939f..0e20ce7 100644
--- a/src/cobalt/loader/image/image_data_decoder.cc
+++ b/src/cobalt/loader/image/image_data_decoder.cc
@@ -23,8 +23,8 @@
namespace image {
namespace {
-// The capacity of data buffer.
-uint32 kMaxBufferSizeBytes = 32 * 1024L;
+// Sanity check max size of data buffer.
+uint32 kMaxBufferSizeBytes = 4 * 1024 * 1024L;
} // namespace
ImageDataDecoder::ImageDataDecoder(
@@ -36,7 +36,6 @@
void ImageDataDecoder::DecodeChunk(const uint8* data, size_t size) {
TRACE_EVENT0("cobalt::loader::image_decoder",
"ImageDataDecoder::DecodeChunk");
-
size_t offset = 0;
while (offset < size) {
if (state_ == kError) {
@@ -48,11 +47,12 @@
size_t input_size;
if (data_buffer_.empty()) {
- // Nothing in the data_buffer, so no data append needs to be performed.
+ // Nothing in |data_buffer_|, so no data append needs to be performed.
input_bytes = data + offset;
input_size = size - offset;
offset += input_size;
} else {
+ DCHECK_GE(kMaxBufferSizeBytes, data_buffer_.size());
size_t fill_buffer_size =
std::min(kMaxBufferSizeBytes - data_buffer_.size(), size - offset);
@@ -67,18 +67,30 @@
}
size_t decoded_size = DecodeChunkInternal(input_bytes, input_size);
- size_t undecoded_size = input_size - decoded_size;
+ if (decoded_size == 0 && offset < size) {
+ LOG(ERROR) << "Unable to make progress decoding image.";
+ state_ = kError;
+ return;
+ }
+ size_t undecoded_size = input_size - decoded_size;
if (undecoded_size == 0) {
// Remove all elements from the data_buffer.
data_buffer_.clear();
} else {
- data_buffer_.reserve(kMaxBufferSizeBytes);
if (data_buffer_.empty()) {
- // data_buffer is empty, so assign the undecoded data to it.
+ if (undecoded_size > kMaxBufferSizeBytes) {
+ LOG(ERROR) << "Max buffer size too small: " << undecoded_size
+ << "bytes required!";
+ state_ = kError;
+ return;
+ }
+
+ // |data_buffer_| is empty, so assign the undecoded data to it.
+ data_buffer_.reserve(undecoded_size);
data_buffer_.assign(data + offset - undecoded_size, data + offset);
} else if (decoded_size != 0) {
- // data_buffer is not empty, so erase the decoded data from it.
+ // |data_buffer_| is not empty, so erase the decoded data from it.
data_buffer_.erase(
data_buffer_.begin(),
data_buffer_.begin() + static_cast<ptrdiff_t>(decoded_size));
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index b4fb1fe..2fad2ec 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -23,6 +23,8 @@
'sources': [
'blob_fetcher.cc',
'blob_fetcher.h',
+ 'cache_fetcher.cc',
+ 'cache_fetcher.h',
'decoder.h',
'embedded_fetcher.cc',
'embedded_fetcher.h',
@@ -176,9 +178,12 @@
'input_directory': 'embedded_resources',
},
'sources': [
+ '<(input_directory)/black_splash_screen.html',
+ '<(input_directory)/cobalt_splash_screen.css',
+ '<(input_directory)/cobalt_splash_screen.html',
'<(input_directory)/equirectangular_40_40.msh',
- '<(input_directory)/splash_screen.css',
- '<(input_directory)/splash_screen.html',
+ '<(input_directory)/youtube_splash_screen.css',
+ '<(input_directory)/youtube_splash_screen.html',
'<(input_directory)/splash_screen.js',
'<(input_directory)/you_tube_logo.png',
],
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index 1a2a762..4ac0fb6 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -62,6 +62,7 @@
'<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
'<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+ '<@(cobalt_platform_dependencies)',
],
},
@@ -100,6 +101,7 @@
'<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
'<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+ '<@(cobalt_platform_dependencies)',
],
},
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h
index 2c126e6..060b080 100644
--- a/src/cobalt/render_tree/map_to_mesh_filter.h
+++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -28,17 +28,17 @@
namespace render_tree {
enum StereoMode {
- kMono,
+ kMono = 0,
// Stereoscopic modes where each half of the video represents the view of
// one eye, and where the texture coordinates of the meshes need to be
// scaled and offset to the appropriate half. The same mesh may be used for
// both eyes, or there may be one for each eye.
- kLeftRight,
- kTopBottom,
+ kLeftRight = 1,
+ kTopBottom = 2,
// Like kLeftRight, but where the texture coordinates already refer to the
// corresponding half of the video and need no adjustment. This can only
// happen when there are distinct meshes for each eye.
- kLeftRightUnadjustedTextureCoords
+ kLeftRightUnadjustedTextureCoords = 3
};
// A MapToMeshFilter can be used to map source content onto a 3D mesh, within a
@@ -106,6 +106,13 @@
}
}
+ // A filter without a an explicit mesh, to represent mesh-mapped content whose
+ // mesh will be supplied externally.
+ explicit MapToMeshFilter(StereoMode stereo_mode)
+ : stereo_mode_(stereo_mode), data_() {
+ DCHECK(stereo_mode != kLeftRightUnadjustedTextureCoords);
+ }
+
StereoMode stereo_mode() const { return stereo_mode_; }
// The omission of the |resolution| parameter will yield the default
diff --git a/src/cobalt/render_tree/mesh.h b/src/cobalt/render_tree/mesh.h
index 8f6c7a2..fdb827f 100644
--- a/src/cobalt/render_tree/mesh.h
+++ b/src/cobalt/render_tree/mesh.h
@@ -45,13 +45,15 @@
Vertex(float x, float y, float z, float u, float v)
: x(x), y(y), z(z), u(u), v(v) {}
};
+ COMPILE_ASSERT(sizeof(Vertex) == sizeof(float) * 5,
+ vertex_struct_size_exceeds_5_floats);
// Defines how the mesh faces are constructed from the ordered list of
// vertices.
enum DrawMode {
- kDrawModeTriangles,
- kDrawModeTriangleStrip,
- kDrawModeTriangleFan
+ kDrawModeTriangles = 0,
+ kDrawModeTriangleStrip = 1,
+ kDrawModeTriangleFan = 2
};
virtual uint32 GetEstimatedSizeInBytes() const = 0;
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc
index c7708af..e29c3f4 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -274,8 +274,13 @@
// with eglMakeCurrent to avoid polluting the previous EGLSurface target.
DCHECK_NE(surface->GetPlatformHandle(), 0);
- egl_surface = null_surface_->GetSurface();
- EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+ // Since we don't care about what surface is backing the default
+ // framebuffer, don't change draw surfaces unless we simply don't have one
+ // already.
+ if (!IsCurrent()) {
+ egl_surface = null_surface_->GetSurface();
+ EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+ }
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, surface->GetPlatformHandle()));
}
@@ -303,7 +308,12 @@
}
void GraphicsContextEGL::MakeCurrent() {
- MakeCurrentWithSurface(null_surface_);
+ // Some GL drivers do *not* handle switching contexts in the middle of a
+ // frame very well, so with this change we avoid making a new surface
+ // current if we don't actually care about what surface is current.
+ if (!IsCurrent()) {
+ MakeCurrentWithSurface(null_surface_);
+ }
}
void GraphicsContextEGL::ReleaseCurrentContext() {
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.h b/src/cobalt/renderer/backend/egl/graphics_context.h
index 6ed533f..ef6d668 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.h
+++ b/src/cobalt/renderer/backend/egl/graphics_context.h
@@ -116,6 +116,9 @@
void Blit(GLuint texture, int x, int y, int width, int height);
+ // Returns if this graphics context is current on the calling thread or not.
+ bool IsCurrent() const { return is_current_; }
+
private:
// Performs a test to determine if the pixel data returned by glReadPixels
// needs to be vertically flipped or not. This test is expensive, so it
diff --git a/src/cobalt/renderer/rasterizer/common/common.gyp b/src/cobalt/renderer/rasterizer/common/common.gyp
index af0e808..9c17122 100644
--- a/src/cobalt/renderer/rasterizer/common/common.gyp
+++ b/src/cobalt/renderer/rasterizer/common/common.gyp
@@ -19,6 +19,8 @@
'type': 'static_library',
'sources': [
+ 'find_node.cc',
+ 'find_node.h',
'offscreen_render_coordinate_mapping.cc',
'offscreen_render_coordinate_mapping.h',
'scratch_surface_cache.cc',
diff --git a/src/cobalt/renderer/rasterizer/common/find_node.cc b/src/cobalt/renderer/rasterizer/common/find_node.cc
new file mode 100644
index 0000000..111aee1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/common/find_node.cc
@@ -0,0 +1,142 @@
+// Copyright 2017 Google Inc. 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/renderer/rasterizer/common/find_node.h"
+
+#include "base/optional.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/render_tree/animations/animate_node.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace common {
+
+class FinderNodeVisitor : public render_tree::NodeVisitor {
+ public:
+ FinderNodeVisitor(NodeFilterFunction<render_tree::Node> filter_function,
+ base::optional<NodeReplaceFunction> replace_function)
+ : filter_function_(filter_function),
+ replace_function_(replace_function) {}
+
+ void Visit(render_tree::animations::AnimateNode* animate) OVERRIDE {
+ VisitNode(animate);
+ }
+ void Visit(render_tree::CompositionNode* composition_node) OVERRIDE {
+ VisitNode(composition_node);
+ }
+ void Visit(render_tree::FilterNode* filter_node) OVERRIDE {
+ VisitNode(filter_node);
+ }
+ void Visit(render_tree::ImageNode* image_node) OVERRIDE {
+ VisitNode(image_node);
+ }
+ void Visit(render_tree::MatrixTransform3DNode* transform_3d_node) OVERRIDE {
+ VisitNode(transform_3d_node);
+ }
+ void Visit(render_tree::MatrixTransformNode* transform_node) OVERRIDE {
+ VisitNode(transform_node);
+ }
+ void Visit(render_tree::PunchThroughVideoNode* punch_through) OVERRIDE {
+ VisitNode(punch_through);
+ }
+ void Visit(render_tree::RectNode* rect) OVERRIDE { VisitNode(rect); }
+ void Visit(render_tree::RectShadowNode* rect) OVERRIDE { VisitNode(rect); }
+ void Visit(render_tree::TextNode* text) OVERRIDE { VisitNode(text); }
+
+ virtual ~FinderNodeVisitor() {}
+
+ scoped_refptr<render_tree::Node> GetFoundNode() const { return found_node_; }
+
+ scoped_refptr<render_tree::Node> GetReplaceWithNode() {
+ return replace_with_;
+ }
+
+ private:
+ template <typename T>
+ void VisitNode(T* node) {
+ if (filter_function_.Run(static_cast<render_tree::Node*>(node))) {
+ found_node_ = node;
+ if (replace_function_) {
+ replace_with_ = replace_function_->Run(node);
+ }
+ } else {
+ VisitNodeChildren(node);
+ }
+ }
+
+ template <typename T>
+ typename base::enable_if<!render_tree::ChildIterator<T>::has_children>::type
+ VisitNodeChildren(T* node) {
+ // No children to visit.
+ }
+
+ template <typename T>
+ typename base::enable_if<render_tree::ChildIterator<T>::has_children>::type
+ VisitNodeChildren(T* node) {
+ render_tree::ChildIterator<T> child_iterator(node);
+ while (render_tree::Node* child = child_iterator.GetCurrent()) {
+ child->Accept(this);
+ if (found_node_) {
+ if (replace_with_) {
+ child_iterator.ReplaceCurrent(replace_with_);
+ }
+ break;
+ }
+ child_iterator.Next();
+ }
+ if (replace_with_) {
+ replace_with_ = new T(child_iterator.TakeReplacedChildrenBuilder());
+ }
+ }
+
+ NodeFilterFunction<render_tree::Node> filter_function_;
+ base::optional<NodeReplaceFunction> replace_function_;
+
+ scoped_refptr<render_tree::Node> found_node_;
+ scoped_refptr<render_tree::Node> replace_with_;
+};
+
+template <>
+NodeSearchResult<render_tree::Node> FindNode<render_tree::Node>(
+ const scoped_refptr<render_tree::Node>& tree,
+ NodeFilterFunction<render_tree::Node> filter_function,
+ base::optional<NodeReplaceFunction> replace_function) {
+ FinderNodeVisitor visitor(filter_function, replace_function);
+ tree->Accept(&visitor);
+
+ NodeSearchResult<render_tree::Node> result;
+ result.found_node = visitor.GetFoundNode();
+ result.replaced_tree = visitor.GetReplaceWithNode();
+
+ if (result.replaced_tree == NULL) {
+ result.replaced_tree = tree;
+ }
+ return result;
+}
+
+bool HasMapToMesh(render_tree::FilterNode* filter_node) {
+ return static_cast<bool>(filter_node->data().map_to_mesh_filter);
+}
+
+render_tree::Node* ReplaceWithEmptyCompositionNode(
+ render_tree::Node* filter_node) {
+ return new render_tree::CompositionNode(
+ render_tree::CompositionNode::Builder());
+}
+
+} // namespace common
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/common/find_node.h b/src/cobalt/renderer/rasterizer/common/find_node.h
new file mode 100644
index 0000000..af694b0
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/common/find_node.h
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. 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_RENDERER_RASTERIZER_COMMON_FIND_NODE_H_
+#define COBALT_RENDERER_RASTERIZER_COMMON_FIND_NODE_H_
+
+#include "base/bind.h"
+#include "cobalt/base/enable_if.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/base/type_id.h"
+#include "cobalt/render_tree/child_iterator.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/render_tree/node_visitor.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace common {
+
+using NodeReplaceFunction =
+ base::Callback<render_tree::Node*(render_tree::Node*)>;
+
+template <typename T = render_tree::Node>
+using NodeFilterFunction = base::Callback<bool(T*)>;
+
+template <typename T = render_tree::Node>
+struct NodeSearchResult {
+ scoped_refptr<T> found_node; // Null if no such node is found.
+ scoped_refptr<render_tree::Node> replaced_tree;
+};
+
+// Finds the first node of type |T| that passes an optional |filter_function|,
+// and optionally replaces it in the tree with the result of |replace_function|.
+//
+// Example usage:
+// auto filter = base::Bind(&CheckSomethingOnNode)
+// auto replace = base::Bind(&ReplaceNode)
+// auto search_results = FindNode<SomeNode>(tree, filter, replace);
+// DoSomethingWithNode(search_results.found_node);
+// DoSomethingWithTree(search_results.replaced_tree);
+template <typename T>
+NodeSearchResult<T> FindNode(
+ const scoped_refptr<render_tree::Node>& tree,
+ NodeFilterFunction<T> typed_filter_function = base::Bind([](T*) {
+ return true;
+ }),
+ base::optional<NodeReplaceFunction> replace_function = base::nullopt) {
+ // Wrap the typed filter with an untyped callback.
+ auto type_checking_filter_function =
+ base::Bind([typed_filter_function](render_tree::Node* node) {
+ if (node->GetTypeId() != base::GetTypeId<T>()) {
+ return false;
+ }
+
+ auto* typed_node = base::polymorphic_downcast<T*>(node);
+ return typed_filter_function.Run(typed_node);
+ });
+ // Call the default untyped FindNode with the above wrap.
+ NodeSearchResult<> untyped_result = FindNode<render_tree::Node>(
+ tree, type_checking_filter_function, replace_function);
+ NodeSearchResult<T> typed_result;
+ typed_result.replaced_tree = untyped_result.replaced_tree;
+ typed_result.found_node =
+ base::polymorphic_downcast<T*>(untyped_result.found_node.get());
+
+ return typed_result;
+}
+
+// Same as the above, but not filtering nodes by type, so the filter can act on
+// nodes of different classes.
+template <>
+NodeSearchResult<render_tree::Node> FindNode<render_tree::Node>(
+ const scoped_refptr<render_tree::Node>& tree,
+ NodeFilterFunction<render_tree::Node> filter_function,
+ base::optional<NodeReplaceFunction> replace_function);
+
+// Checks whether the given filter node has a MapToMesh filter in it.
+bool HasMapToMesh(render_tree::FilterNode* node);
+
+render_tree::Node* ReplaceWithEmptyCompositionNode(render_tree::Node* node);
+
+} // namespace common
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_COMMON_FIND_NODE_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 88d6bdd..8153798 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -125,8 +125,7 @@
return 1.0f;
}
- // The cached contents' sub-pixel offset must be within 0.5 pixels to ensure
- // appropriate positioning.
+ // Use the cached contents' sub-pixel offset as the error rating.
math::PointF desired_offset(
desired_bounds.x() - std::floor(desired_bounds.x()),
desired_bounds.y() - std::floor(desired_bounds.y()));
@@ -135,11 +134,9 @@
cached_bounds.y() - std::floor(cached_bounds.y()));
float error_x = std::abs(desired_offset.x() - cached_offset.x());
float error_y = std::abs(desired_offset.y() - cached_offset.y());
- if (error_x >= 0.5f || error_y >= 0.5f) {
- return 1.0f;
- }
- return error_x + error_y;
+ // Any sub-pixel offset is okay. Return something less than 1.
+ return (error_x + error_y) * 0.49f;
}
} // namespace
@@ -628,6 +625,9 @@
if (!(*out_content_cached)) {
offscreen_target_manager_->AllocateOffscreenTarget(node,
content_size, mapped_bounds, out_target_info);
+ } else {
+ // Maintain the size of the cached contents to avoid scaling artifacts.
+ content_size = out_target_info->region.size();
}
// If no offscreen target could be allocated, then the render tree node will
diff --git a/src/cobalt/renderer/rasterizer/lib/exported/video.h b/src/cobalt/renderer/rasterizer/lib/exported/video.h
new file mode 100644
index 0000000..5cb080c
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/exported/video.h
@@ -0,0 +1,94 @@
+// Copyright 2017 Google Inc. 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_RENDERER_RASTERIZER_LIB_EXPORTED_VIDEO_H_
+#define COBALT_RENDERER_RASTERIZER_LIB_EXPORTED_VIDEO_H_
+
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Functions to be called to set callbacks for the updates to video rendering
+// parameters. When the host application is notified that the graphics context
+// has been created, it should install the callbacks within the same call stack
+// of CbLibOnGraphicsContextCreated. Failing to do so will crash the application
+// on debug mode and no-op on production builds.
+
+typedef enum {
+ kCbLibVideoProjectionTypeNone = 0, // When no offscreen video is playing.
+ kCbLibVideoProjectionTypeRectangular = 1,
+ kCbLibVideoProjectionTypeMesh = 2,
+} CbLibVideoProjectionType;
+
+// If projection_type is Rectangular or Mesh, this signals the start of off-
+// screen video playback. If it is None, this signals the end of playback.
+typedef void (*CbLibVideoUpdateProjectionTypeCallback)(
+ void* context, CbLibVideoProjectionType projection_type);
+
+SB_EXPORT_PLATFORM void CbLibVideoSetOnUpdateProjectionType(
+ void* context, CbLibVideoUpdateProjectionTypeCallback callback);
+
+typedef enum {
+ kCbLibVideoMeshDrawModeTriangles = 0,
+ kCbLibVideoMeshDrawModeTriangleStrip = 1,
+ kCbLibVideoMeshDrawModeTriangleFan = 2,
+} CbLibVideoMeshDrawMode;
+
+typedef struct {
+ int vertex_count;
+ CbLibVideoMeshDrawMode draw_mode;
+ // Memory owned and deleted by Cobalt. The array holds |count| tuples of
+ // interleaved position and texture coordinates in single precision floating
+ // point numbers, X, Y, Z, U, V.
+ const float* vertices;
+} CbLibVideoMesh;
+
+// Called when two new video meshes are detected; each should be applied to the
+// view of the corresponding eye on stereo rendering.
+typedef void (*CbLibVideoUpdateMeshesCallback)(void* context,
+ CbLibVideoMesh left_eye_mesh,
+ CbLibVideoMesh right_eye_mesh);
+
+SB_EXPORT_PLATFORM void CbLibVideoSetOnUpdateMeshes(
+ void* context, CbLibVideoUpdateMeshesCallback callback);
+
+typedef enum {
+ kCbLibVideoStereoModeMono = 0,
+ kCbLibVideoStereoModeStereoLeftRight = 1,
+ kCbLibVideoStereoModeStereoTopBottom = 2,
+ kCbLibVideoStereoModeStereoLeftRightUnadjustedCoordinates = 3,
+} CbLibVideoStereoMode;
+
+// Called when the layout of the different stereo views changes on the video
+// texture.
+typedef void (*CbLibVideoUpdateStereoModeCallback)(
+ void* context, CbLibVideoStereoMode stereo_mode);
+
+SB_EXPORT_PLATFORM void CbLibVideoSetOnUpdateStereoMode(
+ void* context, CbLibVideoUpdateStereoModeCallback callback);
+
+// Called to set the RGB video texture ID.
+typedef void (*CbLibVideoUpdateRgbTextureIdCallback)(void* context, int id);
+
+SB_EXPORT_PLATFORM void CbLibVideoSetOnUpdateRgbTextureId(
+ void* context, CbLibVideoUpdateRgbTextureIdCallback callback);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // COBALT_RENDERER_RASTERIZER_LIB_EXPORTED_VIDEO_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
index d65ebd6..7b8415a 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -14,16 +14,58 @@
#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
+#include <algorithm>
#include <cmath>
#include <vector>
+#include "base/bind.h"
+#include "base/callback.h"
#include "base/threading/thread_checker.h"
+#include "cobalt/math/clamp.h"
+#include "cobalt/render_tree/image.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/render_target.h"
+#include "cobalt/renderer/rasterizer/common/find_node.h"
+#include "cobalt/renderer/rasterizer/lib/exported/video.h"
#include "cobalt/renderer/rasterizer/lib/imported/graphics.h"
+#include "cobalt/renderer/rasterizer/skia/hardware_image.h"
+#include "cobalt/renderer/rasterizer/skia/hardware_mesh.h"
#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
#include "starboard/shared/gles/gl_call.h"
+COMPILE_ASSERT(
+ cobalt::render_tree::kMono == kCbLibVideoStereoModeMono &&
+ cobalt::render_tree::kLeftRight ==
+ kCbLibVideoStereoModeStereoLeftRight &&
+ cobalt::render_tree::kTopBottom ==
+ kCbLibVideoStereoModeStereoTopBottom &&
+ cobalt::render_tree::kLeftRightUnadjustedTextureCoords ==
+ kCbLibVideoStereoModeStereoLeftRightUnadjustedCoordinates,
+ lib_video_and_map_to_mesh_stereo_mode_constants_mismatch);
+
+using cobalt::renderer::rasterizer::lib::ExternalRasterizer;
+
+namespace {
+
+const float kMaxRenderTargetSize = 15360.0f;
+
+ExternalRasterizer::Impl* g_external_rasterizer_impl_;
+
+void DefaultOnUpdateProjectionType(CbLibVideoProjectionType) {
+ LOG(DFATAL) << "CbLibVideoUpdateProjectionTypeCallback not set.";
+}
+void DefaultOnUpdateMeshes(CbLibVideoMesh, CbLibVideoMesh) {
+ LOG(DFATAL) << "CbLibVideoUpdateMeshesCallback not set.";
+}
+void DefaultOnUpdateStereoMode(CbLibVideoStereoMode) {
+ LOG(DFATAL) << "CbLibVideoStereoMode not set.";
+}
+void DefaultOnUpdateRgbTextureId(int) {
+ LOG(DFATAL) << "CbLibVideoUpdateRgbTextureIdCallback not set.";
+}
+
+} // namespace
+
namespace cobalt {
namespace renderer {
namespace rasterizer {
@@ -44,7 +86,34 @@
void MakeCurrent() { hardware_rasterizer_.MakeCurrent(); }
+ // Equivalent to the callback types defined in exported/video.h but with the
+ // context bound.
+ typedef base::Callback<void(CbLibVideoMesh, CbLibVideoMesh)>
+ UpdateMeshesCallback;
+ typedef base::Callback<void(CbLibVideoStereoMode)> UpdateStereoModeCallback;
+ typedef base::Callback<void(int)> UpdateRgbTextureIdCallback;
+ typedef base::Callback<void(CbLibVideoProjectionType projection_type)>
+ UpdateProjectionTypeCallback;
+
+ void SetUpdateMeshesCallback(UpdateMeshesCallback update_meshes) {
+ update_meshes_ = update_meshes;
+ }
+ void SetUpdateStereoModeCallback(
+ UpdateStereoModeCallback update_stereo_mode) {
+ update_stereo_mode_ = update_stereo_mode;
+ }
+ void SetUpdateRgbTextureIdCallback(
+ UpdateRgbTextureIdCallback update_rgb_texture_id) {
+ update_rgb_texture_id_ = update_rgb_texture_id;
+ }
+ void SetUpdateProjectionTypeCallback(
+ UpdateProjectionTypeCallback update_projection_type) {
+ update_projection_type_ = update_projection_type;
+ }
+
private:
+ void RenderOffscreenVideo(render_tree::FilterNode* map_to_mesh_filter_node);
+
base::ThreadChecker thread_checker_;
backend::GraphicsContextEGL* graphics_context_;
@@ -56,7 +125,22 @@
// video.
scoped_refptr<backend::RenderTarget> main_offscreen_render_target_;
scoped_ptr<backend::TextureEGL> main_texture_;
- // TODO: Add in a RenderTarget for the video texture.
+
+ // TODO: do not actually rasterize offscreen video, but just submit it to the
+ // host directly.
+ scoped_refptr<backend::RenderTarget> video_offscreen_render_target_;
+ scoped_ptr<backend::TextureEGL> video_texture_;
+
+ CbLibVideoProjectionType video_projection_type_;
+ scoped_refptr<skia::HardwareMesh> left_eye_video_mesh_;
+ scoped_refptr<skia::HardwareMesh> right_eye_video_mesh_;
+ render_tree::StereoMode video_stereo_mode_;
+ int video_texture_rgb_;
+
+ UpdateMeshesCallback update_meshes_;
+ UpdateStereoModeCallback update_stereo_mode_;
+ UpdateRgbTextureIdCallback update_rgb_texture_id_;
+ UpdateProjectionTypeCallback update_projection_type_;
};
ExternalRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
@@ -71,7 +155,10 @@
hardware_rasterizer_(
graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
- surface_cache_size_in_bytes, purge_skia_font_caches_on_destruction) {
+ surface_cache_size_in_bytes, purge_skia_font_caches_on_destruction),
+ video_projection_type_(kCbLibVideoProjectionTypeNone),
+ video_stereo_mode_(render_tree::StereoMode::kMono),
+ video_texture_rgb_(0) {
options_.flags = skia::HardwareRasterizer::kSubmitFlags_Clear;
graphics_context_->MakeCurrent();
@@ -84,10 +171,22 @@
make_scoped_refptr(base::polymorphic_downcast<backend::RenderTargetEGL*>(
main_offscreen_render_target_.get()))));
+ DCHECK(!g_external_rasterizer_impl_);
+ g_external_rasterizer_impl_ = this;
+
+ // Default parameter update callbacks.
+ update_projection_type_ = base::Bind(&DefaultOnUpdateProjectionType);
+ update_meshes_ = base::Bind(&DefaultOnUpdateMeshes);
+ update_stereo_mode_ = base::Bind(&DefaultOnUpdateStereoMode);
+ update_rgb_texture_id_ = base::Bind(&DefaultOnUpdateRgbTextureId);
+
CbLibOnGraphicsContextCreated();
}
-ExternalRasterizer::Impl::~Impl() { graphics_context_->MakeCurrent(); }
+ExternalRasterizer::Impl::~Impl() {
+ g_external_rasterizer_impl_ = NULL;
+ graphics_context_->MakeCurrent();
+}
void ExternalRasterizer::Impl::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
@@ -95,6 +194,7 @@
backend::RenderTargetEGL* render_target_egl =
base::polymorphic_downcast<backend::RenderTargetEGL*>(
render_target.get());
+
// When the provided RenderTarget is not a window RenderTarget, then this
// implies the rasterized RenderTree should not be shown directly to the user
// and thus should not be rasterized into a texture and sent through to the
@@ -106,17 +206,96 @@
graphics_context_->MakeCurrentWithSurface(render_target_egl);
+ // Attempt to find map to mesh filter node, then render video subtree
+ // offscreen.
+ auto map_to_mesh_search = common::FindNode<render_tree::FilterNode>(
+ render_tree, base::Bind(common::HasMapToMesh),
+ base::Bind(common::ReplaceWithEmptyCompositionNode));
+ if (map_to_mesh_search.found_node != NULL) {
+ base::optional<render_tree::MapToMeshFilter> filter =
+ map_to_mesh_search.found_node->data().map_to_mesh_filter;
+
+ CbLibVideoProjectionType new_projection_type;
+ if (!filter->left_eye_mesh()) {
+ // Video is rectangular. Mesh is provided externally (by host).
+ new_projection_type = kCbLibVideoProjectionTypeRectangular;
+ } else {
+ new_projection_type = kCbLibVideoProjectionTypeMesh;
+ }
+
+ if (video_projection_type_ != new_projection_type) {
+ video_projection_type_ = new_projection_type;
+ update_projection_type_.Run(video_projection_type_);
+ }
+
+ if (filter->stereo_mode() != video_stereo_mode_) {
+ video_stereo_mode_ = filter->stereo_mode();
+ update_stereo_mode_.Run(
+ static_cast<CbLibVideoStereoMode>(video_stereo_mode_));
+ }
+
+ if (video_projection_type_ == kCbLibVideoProjectionTypeMesh) {
+ // Use resolution to lookup custom mesh map.
+ const scoped_refptr<render_tree::Node>& video_render_tree =
+ map_to_mesh_search.found_node->data().source;
+ math::SizeF resolutionf = video_render_tree->GetBounds().size();
+ int width = static_cast<int>(resolutionf.width());
+ int height = static_cast<int>(resolutionf.height());
+
+ math::Size resolution(width, height);
+ scoped_refptr<skia::HardwareMesh> left_eye_video_mesh(
+ base::polymorphic_downcast<skia::HardwareMesh*>(
+ filter->left_eye_mesh(resolution).get()));
+ scoped_refptr<skia::HardwareMesh> right_eye_video_mesh(
+ base::polymorphic_downcast<skia::HardwareMesh*>(
+ filter->right_eye_mesh(resolution).get()));
+
+ DCHECK(left_eye_video_mesh);
+
+ if (left_eye_video_mesh_.get() != left_eye_video_mesh.get() ||
+ right_eye_video_mesh_.get() != right_eye_video_mesh.get()) {
+ left_eye_video_mesh_ = left_eye_video_mesh;
+ right_eye_video_mesh_ = right_eye_video_mesh;
+
+ CbLibVideoMesh left_mesh;
+ left_mesh.vertex_count =
+ static_cast<int>(left_eye_video_mesh_->GetVertexCount());
+ left_mesh.draw_mode = static_cast<CbLibVideoMeshDrawMode>(
+ left_eye_video_mesh_->GetDrawMode());
+ left_mesh.vertices = left_eye_video_mesh_->GetVertices();
+
+ if (right_eye_video_mesh_) {
+ CbLibVideoMesh right_mesh;
+ right_mesh.vertex_count =
+ static_cast<int>(right_eye_video_mesh_->GetVertexCount());
+ right_mesh.draw_mode = static_cast<CbLibVideoMeshDrawMode>(
+ right_eye_video_mesh_->GetDrawMode());
+ right_mesh.vertices = right_eye_video_mesh_->GetVertices();
+ update_meshes_.Run(left_mesh, right_mesh);
+ } else {
+ update_meshes_.Run(left_mesh, left_mesh);
+ }
+ }
+ }
+
+ // Render video to external texture(s) and pass those to the host.
+ RenderOffscreenVideo(map_to_mesh_search.found_node);
+ } else {
+ if (video_projection_type_ != kCbLibVideoProjectionTypeNone) {
+ video_projection_type_ = kCbLibVideoProjectionTypeNone;
+ update_projection_type_.Run(video_projection_type_);
+ }
+ }
+
backend::RenderTargetEGL* main_texture_render_target_egl =
base::polymorphic_downcast<backend::RenderTargetEGL*>(
main_offscreen_render_target_.get());
- hardware_rasterizer_.Submit(render_tree, main_offscreen_render_target_,
- options_);
+ hardware_rasterizer_.Submit(map_to_mesh_search.replaced_tree,
+ main_offscreen_render_target_, options_);
- const intptr_t texture_handle = main_texture_->GetPlatformHandle();
- // TODO: Provide mesh data to clients for map-to-mesh playbacks and a
- // separate video texture handle.
// TODO: Allow clients to specify arbitrary subtrees to render into
// different textures?
+ const intptr_t texture_handle = main_texture_->GetPlatformHandle();
CbLibRenderFrame(texture_handle);
graphics_context_->SwapBuffers(render_target_egl);
@@ -126,6 +305,74 @@
return hardware_rasterizer_.GetResourceProvider();
}
+void ExternalRasterizer::Impl::RenderOffscreenVideo(
+ render_tree::FilterNode* map_to_mesh_filter_node) {
+ DCHECK(map_to_mesh_filter_node);
+ if (!map_to_mesh_filter_node) {
+ return;
+ }
+
+ // Render the mesh-video into a texture to render into 3D space.
+ const scoped_refptr<render_tree::Node>& video_render_tree =
+ map_to_mesh_filter_node->data().source;
+ // Search the video_render_tree for the video frame ImageNode.
+ auto image_node =
+ common::FindNode<render_tree::ImageNode>(video_render_tree).found_node;
+
+ math::SizeF video_size_float = video_render_tree->GetBounds().size();
+ if (image_node.get() && image_node->data().source) {
+ video_size_float = image_node->data().source.get()->GetSize();
+ }
+
+ // Width and height of the video render target are based on the size of the
+ // video clamped to the valid range for creating an offscreen render target.
+ const float target_width = math::Clamp(std::floor(video_size_float.width()),
+ 1.0f, kMaxRenderTargetSize);
+ const float target_height = math::Clamp(std::floor(video_size_float.height()),
+ 1.0f, kMaxRenderTargetSize);
+ const math::Size video_size(target_width, target_height);
+
+ if (!video_offscreen_render_target_ ||
+ video_offscreen_render_target_->GetSize() != video_size) {
+ video_offscreen_render_target_ =
+ graphics_context_->CreateOffscreenRenderTarget(video_size);
+ DLOG(INFO) << "Created new video_offscreen_render_target_: "
+ << video_offscreen_render_target_->GetSize();
+
+ // Note: The TextureEGL this pointer references must first be destroyed by
+ // calling reset() before a new TextureEGL can be constructed.
+ video_texture_.reset();
+ video_texture_.reset(new backend::TextureEGL(
+ graphics_context_,
+ make_scoped_refptr(
+ base::polymorphic_downcast<backend::RenderTargetEGL*>(
+ video_offscreen_render_target_.get()))));
+ }
+
+ if (image_node.get()) {
+ // Create a new ImageNode around the raw image data which will
+ // automatically scale it to the right size.
+ backend::RenderTargetEGL* video_offscreen_render_target_egl =
+ base::polymorphic_downcast<backend::RenderTargetEGL*>(
+ video_offscreen_render_target_.get());
+
+ const scoped_refptr<render_tree::ImageNode> correctly_scaled_image_node(
+ new render_tree::ImageNode(image_node->data().source));
+
+ // TODO: Instead of submitting the image for rendering and producing an
+ // RGB texture, try to cast to HardwareMultiPlaneImage to use the YUV
+ // textures already produced by decode-to-texture.
+ hardware_rasterizer_.Submit(correctly_scaled_image_node,
+ video_offscreen_render_target_, options_);
+
+ const intptr_t video_texture_handle = video_texture_->GetPlatformHandle();
+ if (video_texture_rgb_ != video_texture_handle) {
+ video_texture_rgb_ = video_texture_handle;
+ update_rgb_texture_id_.Run(video_texture_handle);
+ }
+ }
+}
+
ExternalRasterizer::ExternalRasterizer(
backend::GraphicsContext* graphics_context, int skia_atlas_width,
int skia_atlas_height, int skia_cache_size_in_bytes,
@@ -158,3 +405,39 @@
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt
+
+void CbLibVideoSetOnUpdateMeshes(void* context,
+ CbLibVideoUpdateMeshesCallback callback) {
+ if (g_external_rasterizer_impl_) {
+ g_external_rasterizer_impl_->SetUpdateMeshesCallback(
+ callback ? base::Bind(callback, context)
+ : base::Bind(&DefaultOnUpdateMeshes));
+ }
+}
+
+void CbLibVideoSetOnUpdateStereoMode(
+ void* context, CbLibVideoUpdateStereoModeCallback callback) {
+ if (g_external_rasterizer_impl_) {
+ g_external_rasterizer_impl_->SetUpdateStereoModeCallback(
+ callback ? base::Bind(callback, context)
+ : base::Bind(&DefaultOnUpdateStereoMode));
+ }
+}
+
+void CbLibVideoSetOnUpdateRgbTextureId(
+ void* context, CbLibVideoUpdateRgbTextureIdCallback callback) {
+ if (g_external_rasterizer_impl_) {
+ g_external_rasterizer_impl_->SetUpdateRgbTextureIdCallback(
+ callback ? base::Bind(callback, context)
+ : base::Bind(&DefaultOnUpdateRgbTextureId));
+ }
+}
+
+void CbLibVideoSetOnUpdateProjectionType(
+ void* context, CbLibVideoUpdateProjectionTypeCallback callback) {
+ if (g_external_rasterizer_impl_) {
+ g_external_rasterizer_impl_->SetUpdateProjectionTypeCallback(
+ callback ? base::Bind(callback, context)
+ : base::Bind(&DefaultOnUpdateProjectionType));
+ }
+}
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
index 39b7559..9ad1fd0 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
@@ -51,8 +51,9 @@
void MakeCurrent() OVERRIDE;
- private:
class Impl;
+
+ private:
scoped_ptr<Impl> impl_;
};
diff --git a/src/cobalt/renderer/rasterizer/lib/lib.gyp b/src/cobalt/renderer/rasterizer/lib/lib.gyp
index df38bb4..7a3914a 100644
--- a/src/cobalt/renderer/rasterizer/lib/lib.gyp
+++ b/src/cobalt/renderer/rasterizer/lib/lib.gyp
@@ -42,6 +42,11 @@
'<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
'<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
],
+ 'conditions': [
+ ['enable_map_to_mesh == 1', {
+ 'defines' : ['ENABLE_MAP_TO_MESH'],
+ }],
+ ],
}
],
}
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
index 4aa00fb..51a0f84 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
@@ -180,16 +180,27 @@
HardwareBackendImage* backend) {
TRACE_EVENT0("cobalt::renderer",
"HardwareBackendImage::InitializeFromRenderTree()");
+ backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
+ cobalt_context);
scoped_refptr<backend::FramebufferRenderTargetEGL> render_target(
new backend::FramebufferRenderTargetEGL(cobalt_context, size));
+ // The above call to FramebufferRenderTargetEGL() may have dirtied graphics
+ // state, so tell Skia to reset its context.
+ gr_context->resetContext(kRenderTarget_GrGLBackendState |
+ kTextureBinding_GrGLBackendState);
submit_offscreen_callback.Run(root, render_target);
scoped_ptr<backend::TextureEGL> texture(
new backend::TextureEGL(cobalt_context, render_target));
InitializeFromTexture(texture.Pass(), gr_context, backend);
+
+ // Tell Skia that the graphics state is unknown because we issued custom
+ // GL commands above.
+ gr_context->resetContext(kRenderTarget_GrGLBackendState |
+ kTextureBinding_GrGLBackendState);
}
// Initiate all texture initialization code here, which should be executed
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_mesh.h b/src/cobalt/renderer/rasterizer/skia/hardware_mesh.h
index 88f8963..ba251c7 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_mesh.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_mesh.h
@@ -41,6 +41,25 @@
uint32 GetEstimatedSizeInBytes() const OVERRIDE;
+ // Float array of vertices, contiguously interleaved X, Y, Z, U, V coords.
+ const float* GetVertices() const {
+ DCHECK(vertices_ && vertices_->size());
+ return reinterpret_cast<const float*>(vertices_->data());
+ }
+
+ const size_t GetVertexCount() const {
+ DCHECK(vertices_);
+ return vertices_->size();
+ }
+
+ const render_tree::Mesh::DrawMode GetDrawMode() const {
+ return draw_mode_ == GL_TRIANGLE_FAN
+ ? render_tree::Mesh::DrawMode::kDrawModeTriangleFan
+ : draw_mode_ == GL_TRIANGLE_STRIP
+ ? render_tree::Mesh::DrawMode::kDrawModeTriangleStrip
+ : render_tree::Mesh::DrawMode::kDrawModeTriangles;
+ }
+
// Obtains a vertex buffer object from this mesh. Called right before first
// rendering it so that the graphics context has already been made current.
const VertexBufferObject* GetVBO() const;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 9117c4c..603eb8e 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -696,8 +696,6 @@
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_, render_target_egl);
- gr_context_->resetContext();
-
// Create a canvas from the render target.
GrBackendRenderTargetDesc skia_desc =
CobaltRenderTargetToSkiaBackendRenderTargetDesc(render_target.get());
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkAtomics_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkAtomics_cobalt.h
index de8c187..ae812ea 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkAtomics_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkAtomics_cobalt.h
@@ -18,14 +18,6 @@
#include "base/atomicops.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/atomic_type_conversions.h"
-#if defined(STARBOARD)
-/* Don't undefine MemoryBarrier */
-#elif defined(ARCH_CPU_64_BITS) || defined(__LB_XB360__)
-// windows.h #defines this (only on x64). This causes problems because the
-// public API also uses MemoryBarrier at the public name for this fence.
-#undef MemoryBarrier
-#endif
-
static inline int32_t sk_atomic_inc(int32_t* addr) {
return base::subtle::Barrier_AtomicIncrement(addr, 1) - 1;
}
@@ -52,10 +44,10 @@
// to, and after a value has been read. E.g. we don't want previous
// instructions to be moved after this instruction and we don't want after
// instructions to be moved before this one.
- base::subtle::MemoryBarrier();
+ SbAtomicMemoryBarrier();
bool did_swap =
base::subtle::NoBarrier_CompareAndSwap(addr, before, after) == before;
- base::subtle::MemoryBarrier();
+ SbAtomicMemoryBarrier();
return did_swap;
}
@@ -63,13 +55,13 @@
static inline void* sk_atomic_cas(void** addr, void* before, void* after) {
typedef AtomicTraits<void*>::atomic_type AtomicType;
- base::subtle::MemoryBarrier();
+ SbAtomicMemoryBarrier();
void* previous_value =
reinterpret_cast<void*>(base::subtle::NoBarrier_CompareAndSwap(
reinterpret_cast<AtomicType*>(addr),
reinterpret_cast<AtomicType>(before),
reinterpret_cast<AtomicType>(after)));
- base::subtle::MemoryBarrier();
+ SbAtomicMemoryBarrier();
return previous_value;
}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkBarriers_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkBarriers_cobalt.h
index 00b666f..d19d655 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkBarriers_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkBarriers_cobalt.h
@@ -18,14 +18,6 @@
#include "base/atomicops.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/atomic_type_conversions.h"
-#if defined(STARBOARD)
-/* Don't undefine MemoryBarrier */
-#elif defined(ARCH_CPU_64_BITS) || defined(__LB_XB360__) || defined(__LB_XB1__)
-// windows.h #defines this (only on x64). This causes problems because the
-// public API also uses MemoryBarrier at the public name for this fence.
-#undef MemoryBarrier
-#endif
-
template <typename T, bool compatible_with_atomics>
class SkAcquireReleaseWithAtomicCompatibility {};
@@ -36,12 +28,12 @@
// barriers.
static T DoAcquireLoad(T* ptr) {
bool ret = *ptr;
- base::subtle::MemoryBarrier();
+ SbAtomicMemoryBarrier();
return ret;
}
static void DoReleaseStore(T* ptr, T val) {
- base::subtle::MemoryBarrier();
+ SbAtomicMemoryBarrier();
*ptr = val;
}
};
diff --git a/src/cobalt/samples/simple_example.cc b/src/cobalt/samples/simple_example/simple_example.cc
similarity index 93%
rename from src/cobalt/samples/simple_example.cc
rename to src/cobalt/samples/simple_example/simple_example.cc
index fc47fb8..3edb4b8 100644
--- a/src/cobalt/samples/simple_example.cc
+++ b/src/cobalt/samples/simple_example/simple_example.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/samples/simple_example.h"
+#include "cobalt/samples/simple_example/simple_example.h"
namespace cobalt {
namespace samples {
diff --git a/src/cobalt/samples/samples.gyp b/src/cobalt/samples/simple_example/simple_example.gyp
similarity index 94%
rename from src/cobalt/samples/samples.gyp
rename to src/cobalt/samples/simple_example/simple_example.gyp
index 34d1aa9..f1660fb 100644
--- a/src/cobalt/samples/samples.gyp
+++ b/src/cobalt/samples/simple_example/simple_example.gyp
@@ -76,7 +76,7 @@
'variables': {
'executable_name': 'simple_example',
},
- 'includes': [ '../../starboard/build/deploy.gypi' ],
+ 'includes': [ '../../../starboard/build/deploy.gypi' ],
},
# This target will create a test for simple_example.
@@ -107,7 +107,7 @@
],
'output_dir': 'cobalt/samples',
},
- 'includes': [ '../build/copy_test_data.gypi' ],
+ 'includes': [ '../../build/copy_test_data.gypi' ],
},
],
},
@@ -123,7 +123,7 @@
'variables': {
'executable_name': 'simple_example_test',
},
- 'includes': [ '../../starboard/build/deploy.gypi' ],
+ 'includes': [ '../../../starboard/build/deploy.gypi' ],
},
],
}
diff --git a/src/cobalt/samples/simple_example.h b/src/cobalt/samples/simple_example/simple_example.h
similarity index 86%
rename from src/cobalt/samples/simple_example.h
rename to src/cobalt/samples/simple_example/simple_example.h
index cd19184..ea1aea1 100644
--- a/src/cobalt/samples/simple_example.h
+++ b/src/cobalt/samples/simple_example/simple_example.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_SAMPLES_SIMPLE_EXAMPLE_H_
-#define COBALT_SAMPLES_SIMPLE_EXAMPLE_H_
+#ifndef COBALT_SAMPLES_SIMPLE_EXAMPLE_SIMPLE_EXAMPLE_H_
+#define COBALT_SAMPLES_SIMPLE_EXAMPLE_SIMPLE_EXAMPLE_H_
namespace cobalt {
namespace samples {
@@ -37,4 +37,4 @@
} // namespace samples
} // namespace cobalt
-#endif // COBALT_SAMPLES_SIMPLE_EXAMPLE_H_
+#endif // COBALT_SAMPLES_SIMPLE_EXAMPLE_SIMPLE_EXAMPLE_H_
diff --git a/src/cobalt/samples/simple_example_main.cc b/src/cobalt/samples/simple_example/simple_example_main.cc
similarity index 95%
rename from src/cobalt/samples/simple_example_main.cc
rename to src/cobalt/samples/simple_example/simple_example_main.cc
index 7405e68..5cdafee 100644
--- a/src/cobalt/samples/simple_example_main.cc
+++ b/src/cobalt/samples/simple_example/simple_example_main.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/samples/simple_example.h"
+#include "cobalt/samples/simple_example/simple_example.h"
#include "base/logging.h"
#include "base/stringprintf.h"
diff --git a/src/cobalt/samples/simple_example_test.cc b/src/cobalt/samples/simple_example/simple_example_test.cc
similarity index 98%
rename from src/cobalt/samples/simple_example_test.cc
rename to src/cobalt/samples/simple_example/simple_example_test.cc
index cb69e1d..377c436 100644
--- a/src/cobalt/samples/simple_example_test.cc
+++ b/src/cobalt/samples/simple_example/simple_example_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/samples/simple_example.h"
+#include "cobalt/samples/simple_example/simple_example.h"
#include <string>
#include <vector>
diff --git a/src/cobalt/samples/testdata/data.txt b/src/cobalt/samples/simple_example/testdata/data.txt
similarity index 100%
rename from src/cobalt/samples/testdata/data.txt
rename to src/cobalt/samples/simple_example/testdata/data.txt
diff --git a/src/cobalt/samples/testdata/test_cases/case1.txt b/src/cobalt/samples/simple_example/testdata/test_cases/case1.txt
similarity index 100%
rename from src/cobalt/samples/testdata/test_cases/case1.txt
rename to src/cobalt/samples/simple_example/testdata/test_cases/case1.txt
diff --git a/src/cobalt/samples/testdata/test_cases/case1.txt.expected b/src/cobalt/samples/simple_example/testdata/test_cases/case1.txt.expected
similarity index 100%
rename from src/cobalt/samples/testdata/test_cases/case1.txt.expected
rename to src/cobalt/samples/simple_example/testdata/test_cases/case1.txt.expected
diff --git a/src/cobalt/samples/testdata/test_cases/case2.txt b/src/cobalt/samples/simple_example/testdata/test_cases/case2.txt
similarity index 100%
rename from src/cobalt/samples/testdata/test_cases/case2.txt
rename to src/cobalt/samples/simple_example/testdata/test_cases/case2.txt
diff --git a/src/cobalt/samples/testdata/test_cases/case2.txt.expected b/src/cobalt/samples/simple_example/testdata/test_cases/case2.txt.expected
similarity index 100%
rename from src/cobalt/samples/testdata/test_cases/case2.txt.expected
rename to src/cobalt/samples/simple_example/testdata/test_cases/case2.txt.expected
diff --git a/src/cobalt/samples/testdata/test_cases/case3.txt b/src/cobalt/samples/simple_example/testdata/test_cases/case3.txt
similarity index 100%
rename from src/cobalt/samples/testdata/test_cases/case3.txt
rename to src/cobalt/samples/simple_example/testdata/test_cases/case3.txt
diff --git a/src/cobalt/samples/testdata/test_cases/case3.txt.expected b/src/cobalt/samples/simple_example/testdata/test_cases/case3.txt.expected
similarity index 100%
rename from src/cobalt/samples/testdata/test_cases/case3.txt.expected
rename to src/cobalt/samples/simple_example/testdata/test_cases/case3.txt.expected
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index c26b64e..fb0b0da 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -32,6 +32,8 @@
'referenced_object_map.cc',
'util/algorithm_helpers.cc',
'util/exception_helpers.cc',
+ 'util/stack_trace_helpers.cc',
+ 'util/stack_trace_helpers.h',
'weak_heap_object.cc',
'weak_heap_object.h',
'weak_heap_object_manager.cc',
diff --git a/src/cobalt/script/mozjs-45/mozjs_callback_function.h b/src/cobalt/script/mozjs-45/mozjs_callback_function.h
index 9b9972f..a3a841a 100644
--- a/src/cobalt/script/mozjs-45/mozjs_callback_function.h
+++ b/src/cobalt/script/mozjs-45/mozjs_callback_function.h
@@ -26,6 +26,7 @@
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/convert_callback_return_value.h"
#include "cobalt/script/mozjs-45/util/exception_helpers.h"
+#include "cobalt/script/mozjs-45/util/stack_trace_helpers.h"
#include "cobalt/script/mozjs-45/weak_heap_object.h"
#include "nb/memory_scope.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
@@ -64,6 +65,7 @@
CallbackResult<R> Run()
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -127,6 +129,7 @@
CallbackResult<R> Run(
typename base::internal::CallbackParamTraits<A1>::ForwardType a1)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -193,6 +196,7 @@
typename base::internal::CallbackParamTraits<A1>::ForwardType a1,
typename base::internal::CallbackParamTraits<A2>::ForwardType a2)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -261,6 +265,7 @@
typename base::internal::CallbackParamTraits<A2>::ForwardType a2,
typename base::internal::CallbackParamTraits<A3>::ForwardType a3)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -331,6 +336,7 @@
typename base::internal::CallbackParamTraits<A3>::ForwardType a3,
typename base::internal::CallbackParamTraits<A4>::ForwardType a4)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -404,6 +410,7 @@
typename base::internal::CallbackParamTraits<A4>::ForwardType a4,
typename base::internal::CallbackParamTraits<A5>::ForwardType a5)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -479,6 +486,7 @@
typename base::internal::CallbackParamTraits<A5>::ForwardType a5,
typename base::internal::CallbackParamTraits<A6>::ForwardType a6)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
@@ -556,6 +564,7 @@
typename base::internal::CallbackParamTraits<A6>::ForwardType a6,
typename base::internal::CallbackParamTraits<A7>::ForwardType a7)
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
diff --git a/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump b/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump
index 826d064..e8b0804 100644
--- a/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump
+++ b/src/cobalt/script/mozjs-45/mozjs_callback_function.h.pump
@@ -31,6 +31,7 @@
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/convert_callback_return_value.h"
#include "cobalt/script/mozjs-45/util/exception_helpers.h"
+#include "cobalt/script/mozjs-45/util/stack_trace_helpers.h"
#include "cobalt/script/mozjs-45/weak_heap_object.h"
#include "nb/memory_scope.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
@@ -83,6 +84,7 @@
typename base::internal::CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]])
const OVERRIDE {
+ ENABLE_JS_STACK_TRACE_IN_SCOPE(context_);
TRACK_MEMORY_SCOPE("Javascript");
TRACE_EVENT0("cobalt::script::mozjs", "MozjsCallbackFunction::Run");
CallbackResult<R> callback_result;
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index a075221..c850506 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -28,6 +28,7 @@
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/referenced_object_map.h"
#include "cobalt/script/mozjs-45/util/exception_helpers.h"
+#include "cobalt/script/mozjs-45/util/stack_trace_helpers.h"
#include "nb/memory_scope.h"
#include "third_party/mozjs-45/js/public/Initialization.h"
#include "third_party/mozjs-45/js/src/jsfriendapi.h"
@@ -282,7 +283,9 @@
std::vector<StackFrame> MozjsGlobalEnvironment::GetStackTrace(int max_frames) {
DCHECK(thread_checker_.CalledOnValidThread());
- return util::GetStackTrace(context_, max_frames);
+ nb::RewindableVector<StackFrame> stack_frames;
+ util::GetStackTrace(context_, max_frames, &stack_frames);
+ return stack_frames.InternalData();
}
void MozjsGlobalEnvironment::PreventGarbageCollection(
diff --git a/src/cobalt/script/mozjs-45/util/exception_helpers.cc b/src/cobalt/script/mozjs-45/util/exception_helpers.cc
index 21dfb8e..a9523f5 100644
--- a/src/cobalt/script/mozjs-45/util/exception_helpers.cc
+++ b/src/cobalt/script/mozjs-45/util/exception_helpers.cc
@@ -20,7 +20,12 @@
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
#include "third_party/mozjs-45/js/public/UbiNode.h"
+#include "third_party/mozjs-45/js/src/builtin/ModuleObject.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/jscntxt.h"
+#include "third_party/mozjs-45/js/src/jscntxtinlines.h"
+#include "third_party/mozjs-45/js/src/jsfriendapi.h"
+#include "third_party/mozjs-45/js/src/jsprf.h"
namespace cobalt {
namespace script {
@@ -45,15 +50,18 @@
return exception_string;
}
-std::vector<StackFrame> GetStackTrace(JSContext* context, int max_frames) {
+void GetStackTrace(JSContext* context, size_t max_frames,
+ nb::RewindableVector<StackFrame>* output) {
+ DCHECK(output);
+ output->rewindAll();
+
JS::RootedObject stack(context);
- std::vector<StackFrame> stack_frames;
if (!js::GetContextCompartment(context)) {
LOG(WARNING) << "Tried to get stack trace without access to compartment.";
- return stack_frames;
+ return;
}
if (!JS::CaptureCurrentStack(context, &stack, max_frames)) {
- return stack_frames;
+ return;
}
while (stack != NULL) {
@@ -87,11 +95,44 @@
&sf.source_url);
}
- stack_frames.push_back(sf);
+ output->push_back(sf);
JS::GetSavedFrameParent(context, stack, &stack);
}
+}
- return stack_frames;
+void GetStackTraceUsingInternalApi(JSContext* context, size_t max_frames,
+ nb::RewindableVector<StackFrame>* output) {
+ DCHECK(output);
+ output->rewindAll();
+
+ int frames_captured = 0;
+ for (js::AllFramesIter it(context);
+ !it.done() && (max_frames == 0 || frames_captured < max_frames);
+ ++it, ++frames_captured) {
+ const char* filename = JS_GetScriptFilename(it.script());
+ unsigned column = 0;
+ unsigned line = js::PCToLineNumber(it.script(), it.pc(), &column);
+
+ StackFrame sf;
+ sf.source_url = filename;
+ sf.line_number = line;
+ sf.column_number = column;
+ if (it.isFunctionFrame()) {
+ JSFunction* function = it.callee(context);
+ if (function->displayAtom()) {
+ auto* atom = function->displayAtom();
+ JS::AutoCheckCannotGC nogc;
+ auto* chars = atom->latin1Chars(nogc);
+ sf.function_name = std::string(chars, chars + atom->length());
+ } else {
+ sf.function_name = "(anonymous function)";
+ }
+ } else {
+ sf.function_name = "global code";
+ }
+
+ output->push_back(sf);
+ }
}
} // namespace util
diff --git a/src/cobalt/script/mozjs-45/util/exception_helpers.h b/src/cobalt/script/mozjs-45/util/exception_helpers.h
index b950d67..da4aa2d 100644
--- a/src/cobalt/script/mozjs-45/util/exception_helpers.h
+++ b/src/cobalt/script/mozjs-45/util/exception_helpers.h
@@ -18,6 +18,7 @@
#include <vector>
#include "cobalt/script/stack_frame.h"
+#include "nb/rewindable_vector.h"
#include "third_party/mozjs-45/js/public/UbiNode.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
@@ -29,7 +30,20 @@
std::string GetExceptionString(JSContext* context, JS::HandleValue exception);
-std::vector<StackFrame> GetStackTrace(JSContext* context, int max_frames);
+// Retrieves the current stack frame. Values are stored in the output vector.
+// RewindableVector<> will be unconditionally rewound and after this call will
+// contain the number of frames retrieved. The output size will be less than
+// or equal to max_frames.
+void GetStackTrace(JSContext* context, size_t max_frames,
+ nb::RewindableVector<StackFrame>* output);
+
+// The same as |GetStackTrace|, only using internal SpiderMonkey APIs instead.
+// This may be required if attempting to obtain a call stack while SpiderMonkey
+// is deep inside its internals, such as during an allocation. If you decide to
+// call this, make sure you know what you're doing.
+void GetStackTraceUsingInternalApi(JSContext* context, size_t max_frames,
+ nb::RewindableVector<StackFrame>* output);
+
} // namespace util
} // namespace mozjs
} // namespace script
diff --git a/src/cobalt/script/mozjs-45/util/stack_trace_helpers.cc b/src/cobalt/script/mozjs-45/util/stack_trace_helpers.cc
new file mode 100644
index 0000000..79ddf70
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/util/stack_trace_helpers.cc
@@ -0,0 +1,141 @@
+// Copyright 2017 Google Inc. 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/script/mozjs-45/util/stack_trace_helpers.h"
+
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "cobalt/script/mozjs-45/util/exception_helpers.h"
+#include "cobalt/script/stack_frame.h"
+#include "nb/thread_local_object.h"
+#include "starboard/memory.h"
+#include "starboard/once.h"
+#include "starboard/string.h"
+#include "starboard/types.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+namespace util {
+
+namespace {
+
+typedef nb::ThreadLocalObject<MozjsStackTraceGenerator>
+ ThreadLocalJsStackTracer;
+
+SB_ONCE_INITIALIZE_FUNCTION(ThreadLocalJsStackTracer,
+ s_thread_local_js_stack_tracer_singelton);
+
+void ToStringAppend(const StackFrame& sf, std::string* out) {
+ base::SStringPrintf(out, "%s(%d,%d):%s", sf.source_url.c_str(),
+ sf.line_number, sf.column_number,
+ sf.function_name.c_str());
+}
+
+} // namespace
+
+void SetThreadLocalJSContext(JSContext* context) {
+ static_cast<MozjsStackTraceGenerator*>(
+ ::cobalt::script::util::GetThreadLocalStackTraceGenerator())
+ ->set_context(context);
+}
+
+JSContext* GetThreadLocalJSContext() {
+ return static_cast<MozjsStackTraceGenerator*>(
+ ::cobalt::script::util::GetThreadLocalStackTraceGenerator())
+ ->context();
+}
+
+bool MozjsStackTraceGenerator::GenerateStackTrace(
+ int depth, nb::RewindableVector<StackFrame>* out) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ out->rewindAll();
+ if (!Valid()) {
+ return false;
+ }
+ GetStackTraceUsingInternalApi(context_, depth, out);
+ return !out->empty();
+}
+
+bool MozjsStackTraceGenerator::GenerateStackTraceLines(
+ int depth, nb::RewindableVector<std::string>* out) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ out->rewindAll();
+ nb::RewindableVector<StackFrame>& stack_frames = scratch_data_.stack_frames_;
+ if (!GenerateStackTrace(depth, &stack_frames)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < stack_frames.size(); ++i) {
+ std::string& current_string = out->grow(1);
+ current_string.assign(""); // Should not deallocate memory.
+ StackFrame& sf = stack_frames[i];
+ ToStringAppend(sf, ¤t_string);
+ }
+ return true;
+}
+
+bool MozjsStackTraceGenerator::GenerateStackTraceString(int depth,
+ std::string* out) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ out->assign(""); // Should not deallocate memory.
+
+ nb::RewindableVector<StackFrame>& stack_frames = scratch_data_.stack_frames_;
+ if (!GenerateStackTrace(depth, &stack_frames)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < stack_frames.size(); ++i) {
+ cobalt::script::StackFrame& sf = stack_frames[i];
+ ToStringAppend(sf, out);
+ if (i < stack_frames.size() - 1) {
+ base::SStringPrintf(out, "\n");
+ }
+ }
+ return true;
+}
+
+bool MozjsStackTraceGenerator::GenerateStackTraceString(int depth, char* buff,
+ size_t buff_size) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ SbMemorySet(buff, 0, buff_size);
+ std::string& scratch_symbol = scratch_data_.symbol_;
+
+ if (!GenerateStackTraceString(depth, &scratch_symbol)) {
+ return false;
+ }
+
+ SbStringCopy(buff, scratch_symbol.c_str(), buff_size);
+ return true;
+}
+
+} // namespace util
+} // namespace mozjs
+
+namespace util {
+
+// Declared in abstract cobalt script.
+StackTraceGenerator* GetThreadLocalStackTraceGenerator() {
+ return mozjs::util::s_thread_local_js_stack_tracer_singelton()->GetOrCreate();
+}
+
+} // namespace util
+
+} // namespace script
+} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/util/stack_trace_helpers.h b/src/cobalt/script/mozjs-45/util/stack_trace_helpers.h
new file mode 100644
index 0000000..6168902
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/util/stack_trace_helpers.h
@@ -0,0 +1,123 @@
+// Copyright 2017 Google Inc. 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_SCRIPT_MOZJS_45_UTIL_STACK_TRACE_HELPERS_H_
+#define COBALT_SCRIPT_MOZJS_45_UTIL_STACK_TRACE_HELPERS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/script/stack_frame.h"
+#include "cobalt/script/util/stack_trace_helpers.h"
+#include "nb/rewindable_vector.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+#define ENABLE_JS_STACK_TRACE_IN_SCOPE(context) \
+ ::cobalt::script::mozjs::util::StackTraceScope stack_trace_scope_object( \
+ context)
+#else
+#define ENABLE_JS_STACK_TRACE_IN_SCOPE(context)
+#endif
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+namespace util {
+
+// Usage:
+// void Function(JSContext* js_ctx, ...) {
+// ENABLE_JS_STACK_TRACE_IN_SCOPE(js_ctx);
+// ...
+// InvokeOtherFunctions();
+// }
+//
+// void InvokeOtherFunctions() {
+// ...
+// std::string stack_trace_str;
+// if (GetThreadLocalStackTraceGenerator()->GenerateStackTraceString(
+// 2, &stack_trace_str)) {
+// // Prints the stack trace from javascript.
+// SbLogRaw(stack_trace_str.c_str());
+// }
+// }
+class MozjsStackTraceGenerator
+ : public ::cobalt::script::util::StackTraceGenerator {
+ public:
+ MozjsStackTraceGenerator() : context_(NULL) {}
+
+ // Returns |true| if the current StackTraceGenerator can generate information
+ // about the stack.
+ bool Valid() OVERRIDE { return context_ != NULL; }
+
+ // Generates stack traces in the raw form. Returns true if any stack
+ // frames were generated. False otherwise. Output vector will be
+ // unconditionally rewound to being empty.
+ bool GenerateStackTrace(int depth,
+ nb::RewindableVector<StackFrame>* out) OVERRIDE;
+
+ // Returns true if any stack traces were written. The output vector will be
+ // re-wound to being empty.
+ // The first position is the most immediate stack frame.
+ bool GenerateStackTraceLines(int depth,
+ nb::RewindableVector<std::string>* out) OVERRIDE;
+
+ // Prints stack trace. Returns true on success.
+ bool GenerateStackTraceString(int depth, std::string* out) OVERRIDE;
+
+ bool GenerateStackTraceString(int depth, char* buff,
+ size_t buff_size) OVERRIDE;
+
+ // Gets the internal data structure used to generate stack traces.
+ JSContext* context() { return context_; }
+
+ // Internal only, do not set.
+ void set_context(JSContext* context) { context_ = context; }
+
+ private:
+ JSContext* context_;
+
+ // Recycles memory so that stack tracing is efficient.
+ struct Scratch {
+ nb::RewindableVector<StackFrame> stack_frames_;
+ nb::RewindableVector<std::string> strings_stack_frames_;
+ std::string symbol_;
+ };
+ Scratch scratch_data_;
+ // Checks that each instance can only be used within the same thread.
+ base::ThreadChecker thread_checker_;
+};
+
+// This should only be accessed by a scoped object.
+void SetThreadLocalJSContext(JSContext* context);
+JSContext* GetThreadLocalJSContext();
+
+// Useful for updating the most recent stack trace.
+struct StackTraceScope {
+ explicit StackTraceScope(JSContext* js)
+ : prev_context_(GetThreadLocalJSContext()) {
+ SetThreadLocalJSContext(js);
+ }
+ ~StackTraceScope() { SetThreadLocalJSContext(prev_context_); }
+ JSContext* prev_context_;
+};
+
+} // namespace util
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_MOZJS_45_UTIL_STACK_TRACE_HELPERS_H_
diff --git a/src/cobalt/script/mozjs-45/weak_heap_object_manager.cc b/src/cobalt/script/mozjs-45/weak_heap_object_manager.cc
index ebeb413..107d23b 100644
--- a/src/cobalt/script/mozjs-45/weak_heap_object_manager.cc
+++ b/src/cobalt/script/mozjs-45/weak_heap_object_manager.cc
@@ -19,6 +19,7 @@
#include "base/logging.h"
#include "cobalt/script/mozjs-45/weak_heap_object.h"
#include "nb/memory_scope.h"
+#include "starboard/system.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
namespace cobalt {
@@ -39,8 +40,16 @@
}
WeakHeapObjectManager::~WeakHeapObjectManager() {
- // If this is not empty, that means that some WeakHeapObject may outlive this
- // class.
+ // If this is not empty, that means that some WeakHeapObject may outlive
+ // this class.
+#if COBALT_TRACK_WEAK_HEAP_OBJECT_ADDED_FROM_LOCATION
+ for (const auto& object : weak_objects_) {
+ LOG(INFO) << static_cast<void*>(object);
+ for (const auto& location : added_from_[object]) {
+ LOG(INFO) << " " << location;
+ }
+ }
+#endif
DCHECK(weak_objects_.empty());
}
@@ -49,6 +58,13 @@
std::pair<WeakHeapObjects::iterator, bool> pib =
weak_objects_.insert(weak_object);
DCHECK(pib.second) << "WeakHeapObject was already being tracked.";
+
+#if COBALT_TRACK_WEAK_HEAP_OBJECT_ADDED_FROM_LOCATION
+ const int kRequestedStackSize = 100;
+ void* stack[kRequestedStackSize];
+ int stack_size = SbSystemGetStack(stack, kRequestedStackSize);
+ added_from_[weak_object].assign(stack, stack + stack_size);
+#endif
}
void WeakHeapObjectManager::StopTracking(WeakHeapObject* weak_object) {
diff --git a/src/cobalt/script/mozjs-45/weak_heap_object_manager.h b/src/cobalt/script/mozjs-45/weak_heap_object_manager.h
index a452f3d..4b8558c 100644
--- a/src/cobalt/script/mozjs-45/weak_heap_object_manager.h
+++ b/src/cobalt/script/mozjs-45/weak_heap_object_manager.h
@@ -14,8 +14,22 @@
#ifndef COBALT_SCRIPT_MOZJS_45_WEAK_HEAP_OBJECT_MANAGER_H_
#define COBALT_SCRIPT_MOZJS_45_WEAK_HEAP_OBJECT_MANAGER_H_
+#include <vector>
+
#include "base/hash_tables.h"
+// Can be locally set to 1 to track the call stack where a weak heap object
+// was tracked from, and then dump the call stacks of any remaining objects in
+// |WeakHeapObjectManager|'s destructor before hitting the DCHECK that
+// verifies that no objects are remaining.
+//
+// Example workflow for processing the output of this on linux:
+// # Copy a dumped callstack to the clipboard.
+// $ xclip -selection c -o |
+// grep -o '0x[[:xdigit:]]*' |
+// addr2line -e out/linux-x64x11_debug/cobalt
+#define COBALT_TRACK_WEAK_HEAP_OBJECT_ADDED_FROM_LOCATION 0
+
namespace cobalt {
namespace script {
namespace mozjs {
@@ -44,6 +58,10 @@
typedef base::hash_set<WeakHeapObject*> WeakHeapObjects;
WeakHeapObjects weak_objects_;
+#if COBALT_TRACK_WEAK_HEAP_OBJECT_ADDED_FROM_LOCATION
+ base::hash_map<WeakHeapObject*, std::vector<void*>> added_from_;
+#endif
+
friend class WeakHeapObject;
};
diff --git a/src/cobalt/script/mozjs/util/exception_helpers.cc b/src/cobalt/script/mozjs/util/exception_helpers.cc
index fa50573..7ffdbd4 100644
--- a/src/cobalt/script/mozjs/util/exception_helpers.cc
+++ b/src/cobalt/script/mozjs/util/exception_helpers.cc
@@ -49,6 +49,7 @@
void GetStackTrace(JSContext* context, size_t max_frames,
nb::RewindableVector<StackFrame>* output) {
+ DCHECK(output);
output->rewindAll();
JSAutoRequest auto_request(context);
JS::StackDescription* stack_description =
diff --git a/src/cobalt/script/mozjs/util/stack_trace_helpers.cc b/src/cobalt/script/mozjs/util/stack_trace_helpers.cc
index ed83032..1c1fcf1 100644
--- a/src/cobalt/script/mozjs/util/stack_trace_helpers.cc
+++ b/src/cobalt/script/mozjs/util/stack_trace_helpers.cc
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef NB_SCRIPT_STACKTRACE_H_
-#define NB_SCRIPT_STACKTRACE_H_
-
#include "cobalt/script/mozjs/util/stack_trace_helpers.h"
#include <algorithm>
@@ -38,7 +35,8 @@
namespace util {
namespace {
-typedef nb::ThreadLocalObject<StackTraceGenerator> ThreadLocalJsStackTracer;
+typedef nb::ThreadLocalObject<MozjsStackTraceGenerator>
+ ThreadLocalJsStackTracer;
SB_ONCE_INITIALIZE_FUNCTION(ThreadLocalJsStackTracer,
s_thread_local_js_stack_tracer_singelton);
@@ -52,25 +50,25 @@
} // namespace.
void SetThreadLocalJSContext(JSContext* context) {
- GetThreadLocalStackTraceGenerator()->set_js_context(context);
+ static_cast<MozjsStackTraceGenerator*>(
+ ::cobalt::script::util::GetThreadLocalStackTraceGenerator())
+ ->set_js_context(context);
}
JSContext* GetThreadLocalJSContext() {
- return GetThreadLocalStackTraceGenerator()->js_context();
-}
-
-StackTraceGenerator* GetThreadLocalStackTraceGenerator() {
- return s_thread_local_js_stack_tracer_singelton()->GetOrCreate();
+ return static_cast<MozjsStackTraceGenerator*>(
+ ::cobalt::script::util::GetThreadLocalStackTraceGenerator())
+ ->js_context();
}
//////////////////////////////////// IMPL /////////////////////////////////////
-StackTraceGenerator::StackTraceGenerator() : js_context_(NULL) {}
-StackTraceGenerator::~StackTraceGenerator() {}
+MozjsStackTraceGenerator::MozjsStackTraceGenerator() : js_context_(NULL) {}
+MozjsStackTraceGenerator::~MozjsStackTraceGenerator() {}
-bool StackTraceGenerator::Valid() { return js_context_ != NULL; }
+bool MozjsStackTraceGenerator::Valid() { return js_context_ != NULL; }
-bool StackTraceGenerator::GenerateStackTrace(
+bool MozjsStackTraceGenerator::GenerateStackTrace(
int depth, nb::RewindableVector<StackFrame>* out) {
DCHECK(thread_checker_.CalledOnValidThread());
out->rewindAll();
@@ -81,7 +79,7 @@
return !out->empty();
}
-bool StackTraceGenerator::GenerateStackTraceLines(
+bool MozjsStackTraceGenerator::GenerateStackTraceLines(
int depth, nb::RewindableVector<std::string>* out) {
DCHECK(thread_checker_.CalledOnValidThread());
out->rewindAll();
@@ -99,8 +97,8 @@
return true;
}
-bool StackTraceGenerator::GenerateStackTraceString(int depth,
- std::string* out) {
+bool MozjsStackTraceGenerator::GenerateStackTraceString(int depth,
+ std::string* out) {
DCHECK(thread_checker_.CalledOnValidThread());
out->assign(""); // Should not deallocate memory.
@@ -119,8 +117,8 @@
return true;
}
-bool StackTraceGenerator::GenerateStackTraceString(int depth, char* buff,
- size_t buff_size) {
+bool MozjsStackTraceGenerator::GenerateStackTraceString(int depth, char* buff,
+ size_t buff_size) {
DCHECK(thread_checker_.CalledOnValidThread());
SbMemorySet(buff, 0, buff_size);
std::string& scratch_symbol = scratch_data_.symbol_;
@@ -133,15 +131,22 @@
return true;
}
-JSContext* StackTraceGenerator::js_context() { return js_context_; }
+JSContext* MozjsStackTraceGenerator::js_context() { return js_context_; }
-void StackTraceGenerator::set_js_context(JSContext* js_ctx) {
+void MozjsStackTraceGenerator::set_js_context(JSContext* js_ctx) {
js_context_ = js_ctx;
}
} // namespace util
} // namespace mozjs
+
+namespace util {
+
+StackTraceGenerator* GetThreadLocalStackTraceGenerator() {
+ return mozjs::util::s_thread_local_js_stack_tracer_singelton()->GetOrCreate();
+}
+
+} // namespace util
+
} // namespace script
} // namespace cobalt
-
-#endif // NB_SCRIPT_STACKTRACE_H_
diff --git a/src/cobalt/script/mozjs/util/stack_trace_helpers.h b/src/cobalt/script/mozjs/util/stack_trace_helpers.h
index c54a5ee..b7ccbe8 100644
--- a/src/cobalt/script/mozjs/util/stack_trace_helpers.h
+++ b/src/cobalt/script/mozjs/util/stack_trace_helpers.h
@@ -20,6 +20,7 @@
#include "base/threading/thread_checker.h"
#include "cobalt/script/stack_frame.h"
+#include "cobalt/script/util/stack_trace_helpers.h"
#include "nb/rewindable_vector.h"
struct JSContext;
@@ -47,19 +48,20 @@
// void InvokeOtherFunctions() {
// ...
// std::string stack_trace_str;
-// if (GetThreadLocalStackTraceGenerator()->GenerateStackTraceString(
+// if (GetThreadLocalMozjsStackTraceGenerator()->GenerateStackTraceString(
// 2, &stack_trace_str)) {
// // Prints the stack trace from javascript.
// SbLogRaw(stack_trace_str.c_str());
// }
// }
-class StackTraceGenerator {
+class MozjsStackTraceGenerator
+ : public ::cobalt::script::util::StackTraceGenerator {
public:
- StackTraceGenerator();
- virtual ~StackTraceGenerator();
+ MozjsStackTraceGenerator();
+ virtual ~MozjsStackTraceGenerator();
- // Returns |true| if the current StackTraceGenerator can generate information
- // about the stack.
+ // Returns |true| if the current MozjsStackTraceGenerator can generate
+ // information about the stack.
bool Valid();
// Generates stack traces in the raw form. Returns true if any stack
@@ -98,9 +100,6 @@
base::ThreadChecker thread_checker_;
};
-// Get's the thread local StackTraceGenerator.
-StackTraceGenerator* GetThreadLocalStackTraceGenerator();
-
// This should only be accessed by a scoped object.
void SetThreadLocalJSContext(JSContext* context);
JSContext* GetThreadLocalJSContext();
diff --git a/src/cobalt/script/script.gyp b/src/cobalt/script/script.gyp
index 46cc245..a7e9192 100644
--- a/src/cobalt/script/script.gyp
+++ b/src/cobalt/script/script.gyp
@@ -34,6 +34,7 @@
'source_provider.h',
'stack_frame.cc',
'stack_frame.h',
+ 'util/stack_trace_helpers.h',
'wrappable.h',
],
'dependencies': [
diff --git a/src/cobalt/script/util/stack_trace_helpers.h b/src/cobalt/script/util/stack_trace_helpers.h
new file mode 100644
index 0000000..0cdc636
--- /dev/null
+++ b/src/cobalt/script/util/stack_trace_helpers.h
@@ -0,0 +1,62 @@
+// Copyright 2017 Google Inc. 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_SCRIPT_UTIL_STACK_TRACE_HELPERS_H_
+#define COBALT_SCRIPT_UTIL_STACK_TRACE_HELPERS_H_
+
+#include <string>
+
+#include "cobalt/script/stack_frame.h"
+#include "nb/rewindable_vector.h"
+
+namespace cobalt {
+namespace script {
+namespace util {
+
+class StackTraceGenerator {
+ public:
+ virtual ~StackTraceGenerator() {}
+
+ // Returns |true| if the current StackTraceGenerator can generate information
+ // about the stack.
+ virtual bool Valid() = 0;
+
+ // Generates stack traces in the raw form. Returns true if any stack
+ // frames were generated. False otherwise. Output vector will be
+ // unconditionally rewound to being empty.
+ virtual bool GenerateStackTrace(int depth,
+ nb::RewindableVector<StackFrame>* out) = 0;
+
+ // Returns true if any stack traces were written. The output vector will be
+ // re-wound to being empty.
+ // The first position is the most immediate stack frame.
+ virtual bool GenerateStackTraceLines(
+ int depth, nb::RewindableVector<std::string>* out) = 0;
+
+ // Prints stack trace. Returns true on success.
+ virtual bool GenerateStackTraceString(int depth, std::string* out) = 0;
+
+ virtual bool GenerateStackTraceString(int depth, char* buff,
+ size_t buff_size) = 0;
+};
+
+// Get's the thread local StackTraceGenerator.
+// Defined in engine specific implementation.
+StackTraceGenerator* GetThreadLocalStackTraceGenerator();
+
+} // namespace util
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_UTIL_STACK_TRACE_HELPERS_H_
diff --git a/src/cobalt/system_window/system_window.cc b/src/cobalt/system_window/system_window.cc
index 399e107..c7c1c6d 100644
--- a/src/cobalt/system_window/system_window.cc
+++ b/src/cobalt/system_window/system_window.cc
@@ -100,9 +100,21 @@
// uses.
int key_code = static_cast<int>(data.key);
#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ uint32 modifiers = data.key_modifiers;
+ if (((data.device_type == kSbInputDeviceTypeTouchPad) ||
+ (data.device_type == kSbInputDeviceTypeTouchScreen)) &&
+ ((type == InputEvent::kPointerDown) ||
+ (type == InputEvent::kPointerMove))) {
+ // For touch contact input, ensure that the device button state is also
+ // reported as pressed.
+ // https://www.w3.org/TR/2015/REC-pointerevents-20150224/#button-states
+ key_code = kSbKeyMouse1;
+ modifiers |= InputEvent::kLeftButton;
+ }
+
scoped_ptr<InputEvent> input_event(
- new InputEvent(type, data.device_id, key_code, data.key_modifiers,
- is_repeat, math::PointF(data.position.x, data.position.y),
+ new InputEvent(type, data.device_id, key_code, modifiers, is_repeat,
+ math::PointF(data.position.x, data.position.y),
math::PointF(data.delta.x, data.delta.y), data.pressure,
math::PointF(data.size.x, data.size.y),
math::PointF(data.tilt.x, data.tilt.y)));
diff --git a/src/cobalt/xhr/xhr_modify_headers.h b/src/cobalt/xhr/xhr_modify_headers.h
index a1a1afc..c9088a7 100644
--- a/src/cobalt/xhr/xhr_modify_headers.h
+++ b/src/cobalt/xhr/xhr_modify_headers.h
@@ -15,12 +15,14 @@
#ifndef COBALT_XHR_XHR_MODIFY_HEADERS_H_
#define COBALT_XHR_XHR_MODIFY_HEADERS_H_
-#include <net/http/http_request_headers.h>
+#include "googleurl/src/gurl.h"
+#include "net/http/http_request_headers.h"
namespace cobalt {
namespace xhr {
-void CobaltXhrModifyHeader(net::HttpRequestHeaders* headers);
+void CobaltXhrModifyHeader(const GURL& request_url,
+ net::HttpRequestHeaders* headers);
} // namespace xhr
} // namespace cobalt
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index 7af4b99..c1774a4 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -325,7 +325,7 @@
upload_complete_ = false;
#if defined(COBALT_ENABLE_XHR_HEADER_FILTERING)
- CobaltXhrModifyHeader(&request_headers_);
+ CobaltXhrModifyHeader(request_url_, &request_headers_);
#endif
std::string request_body_text;
diff --git a/src/net/url_request/url_request_job_manager.cc b/src/net/url_request/url_request_job_manager.cc
index 1ab6814..ed0f5db 100644
--- a/src/net/url_request/url_request_job_manager.cc
+++ b/src/net/url_request/url_request_job_manager.cc
@@ -39,9 +39,7 @@
static const SchemeToFactory kBuiltinFactories[] = {
{"https", URLRequestHttpJob::Factory},
{"data", URLRequestDataJob::Factory},
-#if !defined(COBALT_FORCE_HTTPS)
{ "http", URLRequestHttpJob::Factory },
-#endif
#if defined(COBALT_ENABLE_FILE_SCHEME)
{ "file", URLRequestFileJob::Factory },
#endif
diff --git a/src/starboard/README.md b/src/starboard/README.md
index 16a8cda..1487a1d 100644
--- a/src/starboard/README.md
+++ b/src/starboard/README.md
@@ -5,12 +5,6 @@
that it does not.
-## Current State
-
-Desktop Linux Cobalt is fully implemented on top of Starboard, and version 1 of
-the Starboard API is mostly locked down.
-
-
## Interesting Source Locations
All source locations are specified relative to `src/starboard/` (this directory).
@@ -19,11 +13,9 @@
the public headers that Starboard defines.
* `examples/` - Example code demonstrating various aspects of Starboard API
usage.
- * `linux/` - The home of the Linux Starboard implementation. This contains a
+ * `stub/` - The home of the Stub Starboard implementation. This contains a
`starboard_platform.gyp` file that defines a library with all the source
- files needed to provide a complete Starboard Linux implementation. Source
- files that are specific to Linux are in this directory, whereas shared
- implementations are pulled from the shared directory.
+ files needed to provide a complete linkable Starboard implementation.
* `nplb/` - "No Platform Left Behind," Starboard's platform verification test
suite.
* `shared/` - The home of all code that can be shared between Starboard
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index f0fa6d8..e9609c7 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -115,7 +115,11 @@
// Pointer-sized atomic operations. Forwards to either 32-bit or 64-bit
// functions as appropriate.
-typedef intptr_t SbAtomicPtr;
+#if SB_HAS(64_BIT_POINTERS)
+typedef SbAtomic64 SbAtomicPtr;
+#else
+typedef SbAtomic32 SbAtomicPtr;
+#endif
static SB_C_FORCE_INLINE SbAtomicPtr
SbAtomicNoBarrier_CompareAndSwapPtr(volatile SbAtomicPtr* ptr,
diff --git a/src/starboard/client_porting/eztime/eztime.cc b/src/starboard/client_porting/eztime/eztime.cc
index 10c86f4..aa4f737 100644
--- a/src/starboard/client_porting/eztime/eztime.cc
+++ b/src/starboard/client_porting/eztime/eztime.cc
@@ -115,8 +115,17 @@
SB_DCHECK(value);
SB_DCHECK(out_exploded);
UErrorCode status = U_ZERO_ERROR;
+
+ // Always query the time using a gregorian calendar. This is
+ // implied in opengroup documentation for tm struct, even though it is not
+ // specified. E.g. in gmtime's documentation, it states that UTC time is
+ // used, and in tm struct's documentation it is specified that year should
+ // be an offset from 1900.
+
+ // See:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/gmtime.html
UCalendar* calendar = ucal_open(GetTimeZoneId(timezone), -1,
- uloc_getDefault(), UCAL_DEFAULT, &status);
+ uloc_getDefault(), UCAL_GREGORIAN, &status);
if (!calendar) {
return false;
}
@@ -157,8 +166,17 @@
EzTimeZone timezone) {
SB_DCHECK(exploded);
UErrorCode status = U_ZERO_ERROR;
+
+ // Always query the time using a gregorian calendar. This is
+ // implied in opengroup documentation for tm struct, even though it is not
+ // specified. E.g. in gmtime's documentation, it states that UTC time is
+ // used, and in tm struct's documentation it is specified that year should
+ // be an offset from 1900.
+
+ // See:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/gmtime.html
UCalendar* calendar = ucal_open(GetTimeZoneId(timezone), -1,
- uloc_getDefault(), UCAL_DEFAULT, &status);
+ uloc_getDefault(), UCAL_GREGORIAN, &status);
if (!calendar) {
EzTimeValue zero_time = {};
return zero_time;
diff --git a/src/starboard/common/new.cc b/src/starboard/common/new.cc
index 7e08ed0..14c0678 100644
--- a/src/starboard/common/new.cc
+++ b/src/starboard/common/new.cc
@@ -21,7 +21,7 @@
return SbMemoryAllocate(size);
}
-void operator delete(void* pointer) {
+void operator delete(void* pointer) noexcept {
SbMemoryDeallocate(pointer);
}
@@ -29,6 +29,6 @@
return SbMemoryAllocate(size);
}
-void operator delete[](void* pointer) {
+void operator delete[](void* pointer) noexcept {
SbMemoryDeallocate(pointer);
}
diff --git a/src/starboard/input.h b/src/starboard/input.h
index 0b2eba4..0959a00 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -105,11 +105,11 @@
// only relative movements are provided.
kSbInputEventTypeMove,
- // Key or button press activation. This could be a key on a keyboard, a
- // button on a mouse or game controller, a push from a touch screen, or
- // a gesture. An |Unpress| event is subsequently delivered when the
- // |Press| event terminates, such as when the key/button/finger is raised.
- // Injecting repeat presses is up to the client.
+ // Key or button press activation. This could be a key on a keyboard, a button
+ // on a mouse or game controller, a push from a touch screen, or a gesture. An
+ // |Unpress| event is subsequently delivered when the |Press| event
+ // terminates, such as when the key/button/finger is raised. Injecting repeat
+ // presses is up to the client.
kSbInputEventTypePress,
#if SB_API_VERSION < 5
@@ -165,41 +165,38 @@
wchar_t character;
// The location of the specified key, in cases where there are multiple
- // instances of the button on the keyboard. For example, some keyboards
- // have more than one "shift" key.
+ // instances of the button on the keyboard. For example, some keyboards have
+ // more than one "shift" key.
SbKeyLocation key_location;
// Key modifiers (e.g. |Ctrl|, |Shift|) held down during this input event.
unsigned int key_modifiers;
- // The (x, y) coordinates of the persistent cursor controlled by this
- // device. The value is |0| if this data is not applicable.
+ // The (x, y) coordinates of the persistent cursor controlled by this device.
+ // The value is |0| if this data is not applicable.
SbInputVector position;
- // The relative motion vector of this input. The value is |0| if this data
- // is not applicable.
+ // The relative motion vector of this input. The value is |0| if this data is
+ // not applicable.
SbInputVector delta;
#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
- // The normalized pressure of the pointer input in the range of
- // [0,1], where 0 and 1 represent the minimum and maximum pressure
- // the hardware is capable of detecting, respectively. Use NaN for
- // devices that do not report pressure. This value is only used for input
- // events with device type mouse or touch screen.
+ // The normalized pressure of the pointer input in the range of [0,1], where 0
+ // and 1 represent the minimum and maximum pressure the hardware is capable of
+ // detecting, respectively. Use NaN for devices that do not report pressure.
+ // This value is used for input events with device type mouse or touch screen.
float pressure;
- // The (width, height) of the contact geometry of the pointer.
- // This defines the limits of the values reported for the pointer
- // coordinates in the 'position' field. If (NaN, NaN) is specified,
- // the width and height of the window will be used. This value is only used
- // for input events with device type mouse or touch screen.
+ // The (width, height) of the contact geometry of the pointer. This defines
+ // the size of the area under the pointer position. If (NaN, NaN) is
+ // specified, the value (0,0) will be used. This value is used for input
+ // events with device type mouse or touch screen.
SbInputVector size;
- // The (x, y) angle in degrees, in the range of [-90, 90] of the
- // pointer, relative to the z axis. Positive values are for tilt
- // to the right (x), and towards the user (y). Use (NaN, NaN) for
- // devices that do not report tilt. This value is only used for input events
- // with device type mouse or touch screen.
+ // The (x, y) angle in degrees, in the range of [-90, 90] of the pointer,
+ // relative to the z axis. Positive values are for tilt to the right (x), and
+ // towards the user (y). Use (NaN, NaN) for devices that do not report tilt.
+ // This value is used for input events with device type mouse or touch screen.
SbInputVector tilt;
#endif
} SbInputData;
diff --git a/src/starboard/linux/__init__.py b/src/starboard/linux/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/starboard/linux/__init__.py
diff --git a/src/starboard/linux/shared/__init__.py b/src/starboard/linux/shared/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/starboard/linux/shared/__init__.py
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index 14401e4..8569596 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -13,6 +13,9 @@
# limitations under the License.
"""Starboard Linux shared platform configuration for gyp_cobalt."""
+import imp
+import os
+
import config.starboard
import gyp_utils
@@ -51,3 +54,10 @@
'CXX': self.host_compiler_environment['CXX_host'],
})
return env_variables
+
+ def GetLauncher(self):
+ """Gets the module used to launch applications on this platform."""
+ module_path = os.path.abspath(os.path.join(
+ os.path.dirname(__file__), 'launcher.py'))
+ launcher_module = imp.load_source('launcher', module_path)
+ return launcher_module
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py
new file mode 100644
index 0000000..73e708a
--- /dev/null
+++ b/src/starboard/linux/shared/launcher.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Google Inc. 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.
+"""Linux implementation of Starboard launcher abstraction."""
+
+import imp
+import os
+import signal
+import socket
+import subprocess
+
+module_path = os.path.abspath(
+ os.path.join(os.path.dirname(__file__),
+ os.pardir, os.pardir, "tools", "abstract_launcher.py"))
+
+abstract_launcher = imp.load_source("abstract_launcher", module_path)
+
+
+class Launcher(abstract_launcher.AbstractLauncher):
+ """Class for launching Cobalt/tools on Linux."""
+
+ def __init__(self, platform, target_name, config, device_id, args):
+ super(Launcher, self).__init__(platform, target_name, config, device_id,
+ args)
+ if not self.device_id:
+ if socket.has_ipv6: # If the device supports IPv6:
+ self.device_id = "::1" # Use the only IPv6 loopback address
+ else:
+ self.device_id = socket.gethostbyname("localhost")
+
+ executable_dir = "{}_{}".format(self.platform, self.config)
+ self.executable = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ os.pardir, os.pardir,
+ os.pardir, "out",
+ executable_dir,
+ target_name))
+
+ self.pid = None
+
+ def Run(self):
+ """Runs launcher's executable."""
+
+ signal.signal(signal.SIGTERM, lambda signum, frame: self.Kill())
+ signal.signal(signal.SIGINT, lambda signum, frame: self.Kill())
+ proc = subprocess.Popen([self.executable] + self.target_command_line_params)
+ self.pid = proc.pid
+ proc.wait()
+
+ def Kill(self):
+ print "\n***Killing Launcher***\n"
+ if self.pid:
+ try:
+ os.kill(self.pid, signal.SIGTERM)
+ except OSError:
+ raise OSError("Process already closed.")
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index 6621841..4ae443b 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -16,6 +16,7 @@
#include <linux/limits.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <cstring>
@@ -23,8 +24,28 @@
#include "starboard/directory.h"
#include "starboard/log.h"
#include "starboard/string.h"
+#include "starboard/user.h"
namespace {
+const int kMaxPathSize = SB_FILE_MAX_PATH;
+
+// Gets the path to the cache directory, using the user's home directory.
+bool GetCacheDirectory(char* out_path, int path_size) {
+ char home_path[kMaxPathSize + 1];
+ if (!SbUserGetProperty(SbUserGetCurrent(), kSbUserPropertyHomeDirectory,
+ home_path, kMaxPathSize)) {
+ return false;
+ }
+ int result =
+ SbStringFormatF(out_path, path_size, "%s/.cache/cobalt", home_path);
+ if (result < 0 || result >= path_size) {
+ out_path[0] = '\0';
+ return false;
+ }
+
+ return true;
+}
+
// Places up to |path_size| - 1 characters of the path to the current
// executable in |out_path|, ensuring it is NULL-terminated. Returns success
// status. The result being greater than |path_size| - 1 characters is a
@@ -34,8 +55,8 @@
return false;
}
- char path[PATH_MAX + 1];
- size_t bytes_read = readlink("/proc/self/exe", path, PATH_MAX);
+ char path[kMaxPathSize + 1];
+ size_t bytes_read = readlink("/proc/self/exe", path, kMaxPathSize);
if (bytes_read < 1) {
return false;
}
@@ -68,6 +89,37 @@
*last_slash = '\0';
return true;
}
+
+// Gets only the name portion of the current executable.
+bool GetExecutableName(char* out_path, int path_size) {
+ char path[kMaxPathSize] = {0};
+ if (!GetExecutablePath(path, kMaxPathSize)) {
+ return false;
+ }
+
+ const char* last_slash = SbStringFindLastCharacter(path, '/');
+ if (SbStringCopy(out_path, last_slash + 1, path_size) >= path_size) {
+ return false;
+ }
+ return true;
+}
+
+// Gets the path to a temporary directory that is unique to this process.
+bool GetTemporaryDirectory(char* out_path, int path_size) {
+ char binary_name[kMaxPathSize] = {0};
+ if (!GetExecutableName(binary_name, kMaxPathSize)) {
+ return false;
+ }
+
+ int result = SbStringFormatF(out_path, path_size, "/tmp/%s-%d", binary_name,
+ static_cast<int>(getpid()));
+ if (result < 0 || result >= path_size) {
+ out_path[0] = '\0';
+ return false;
+ }
+
+ return true;
+}
} // namespace
bool SbSystemGetPath(SbSystemPathId path_id, char* out_path, int path_size) {
@@ -89,13 +141,9 @@
}
break;
case kSbSystemPathCacheDirectory:
- if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) {
+ if (!GetCacheDirectory(path, kPathSize)) {
return false;
}
- if (SbStringConcat(path, "/cache", kPathSize) >= kPathSize) {
- return false;
- }
-
SbDirectoryCreate(path);
break;
case kSbSystemPathDebugOutputDirectory:
@@ -118,7 +166,7 @@
}
break;
case kSbSystemPathTempDirectory:
- if (SbStringCopy(path, "/tmp/cobalt", kPathSize) >= kPathSize) {
+ if (!GetTemporaryDirectory(path, kPathSize)) {
return false;
}
diff --git a/src/starboard/linux/x64x11/gyp_configuration.py b/src/starboard/linux/x64x11/gyp_configuration.py
index 2574baa..6685818 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gyp_configuration.py
@@ -14,19 +14,45 @@
"""Starboard Linux X64 X11 platform configuration for gyp_cobalt."""
import logging
-import os
-import sys
-# Import the shared Linux platform configuration.
-sys.path.append(
- os.path.realpath(
- os.path.join(os.path.dirname(__file__), os.pardir, 'shared')))
-import gyp_configuration
+from starboard.linux.shared import gyp_configuration
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import touch
+
+
+class PlatformConfig(gyp_configuration.PlatformConfig):
+
+ def __init__(self):
+ super(PlatformConfig, self).__init__('linux-x64x11')
+
+ def GetTargetToolchain(self):
+ return self.GetHostToolchain()
+
+ def GetHostToolchain(self):
+ environment_variables = self.GetEnvironmentVariables()
+ cc_path = environment_variables['CC']
+ cxx_path = environment_variables['CXX']
+
+ return [
+ clang.CCompiler(path=cc_path),
+ clang.CxxCompiler(path=cxx_path),
+ clang.AssemblerWithCPreprocessor(path=cc_path),
+ ar.StaticThinLinker(),
+ ar.StaticLinker(),
+ clangxx.ExecutableLinker(path=cxx_path),
+ cp.Copy(),
+ touch.Stamp(),
+ bash.Shell(),
+ ]
def CreatePlatformConfig():
try:
- return gyp_configuration.PlatformConfig('linux-x64x11')
+ return PlatformConfig()
except RuntimeError as e:
logging.critical(e)
return None
diff --git a/src/starboard/linux/x64x11/main.cc b/src/starboard/linux/x64x11/main.cc
index 12cbce1..7924f55 100644
--- a/src/starboard/linux/x64x11/main.cc
+++ b/src/starboard/linux/x64x11/main.cc
@@ -17,6 +17,7 @@
#include "starboard/configuration.h"
#include "starboard/shared/signal/crash_signals.h"
#include "starboard/shared/signal/suspend_signals.h"
+#include "starboard/shared/starboard/link_receiver.h"
#include "starboard/shared/x11/application_x11.h"
extern "C" SB_EXPORT_PLATFORM int main(int argc, char** argv) {
@@ -24,7 +25,11 @@
starboard::shared::signal::InstallCrashSignalHandlers();
starboard::shared::signal::InstallSuspendSignalHandlers();
starboard::shared::x11::ApplicationX11 application;
- int result = application.Run(argc, argv);
+ int result = 0;
+ {
+ starboard::shared::starboard::LinkReceiver receiver(&application);
+ result = application.Run(argc, argv);
+ }
starboard::shared::signal::UninstallSuspendSignalHandlers();
starboard::shared::signal::UninstallCrashSignalHandlers();
return result;
diff --git a/src/starboard/linux/x64x11/starboard_platform.gypi b/src/starboard/linux/x64x11/starboard_platform.gypi
index 05f1e12..6e54ebf 100644
--- a/src/starboard/linux/x64x11/starboard_platform.gypi
+++ b/src/starboard/linux/x64x11/starboard_platform.gypi
@@ -19,6 +19,7 @@
'<(DEPTH)/starboard/linux/x64x11/main.cc',
'<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
'<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
+ '<(DEPTH)/starboard/shared/starboard/link_receiver.cc',
'<(DEPTH)/starboard/shared/x11/application_x11.cc',
'<(DEPTH)/starboard/shared/x11/window_create.cc',
'<(DEPTH)/starboard/shared/x11/window_destroy.cc',
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index ef605f5..6c0c76f 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -176,6 +176,7 @@
'socket_waiter_wait_test.cc',
'socket_waiter_wait_timed_test.cc',
'socket_waiter_wake_up_test.cc',
+ 'socket_wrapper_test.cc',
'speech_recognizer_cancel_test.cc',
'speech_recognizer_create_test.cc',
'speech_recognizer_destroy_test.cc',
diff --git a/src/starboard/nplb/socket_helpers.cc b/src/starboard/nplb/socket_helpers.cc
index c78a21a..115f08f 100644
--- a/src/starboard/nplb/socket_helpers.cc
+++ b/src/starboard/nplb/socket_helpers.cc
@@ -14,6 +14,7 @@
#include "starboard/nplb/socket_helpers.h"
+#include "starboard/common/scoped_ptr.h"
#include "starboard/once.h"
#include "starboard/socket.h"
#include "starboard/socket_waiter.h"
@@ -125,6 +126,23 @@
return server_socket;
}
+scoped_ptr<Socket> CreateServerTcpSocketWrapped(
+ SbSocketAddressType address_type) {
+ scoped_ptr<Socket> server_socket =
+ make_scoped_ptr(new Socket(address_type, kSbSocketProtocolTcp));
+ if (!server_socket->IsValid()) {
+ ADD_FAILURE() << "SbSocketCreate failed";
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ if (!server_socket->SetReuseAddress(true)) {
+ ADD_FAILURE() << "SbSocketSetReuseAddress failed";
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return server_socket.Pass();
+}
+
SbSocket CreateBoundTcpSocket(SbSocketAddressType address_type, int port) {
SbSocket server_socket = CreateServerTcpSocket(address_type);
if (!SbSocketIsValid(server_socket)) {
@@ -142,6 +160,23 @@
return server_socket;
}
+scoped_ptr<Socket> CreateBoundTcpSocketWrapped(SbSocketAddressType address_type,
+ int port) {
+ scoped_ptr<Socket> server_socket = CreateServerTcpSocketWrapped(address_type);
+ if (!server_socket) {
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ SbSocketAddress address = GetUnspecifiedAddress(address_type, port);
+ SbSocketError result = server_socket->Bind(&address);
+ if (result != kSbSocketOk) {
+ ADD_FAILURE() << "SbSocketBind to " << port << " failed: " << result;
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return server_socket.Pass();
+}
+
SbSocket CreateListeningTcpSocket(SbSocketAddressType address_type, int port) {
SbSocket server_socket = CreateBoundTcpSocket(address_type, port);
if (!SbSocketIsValid(server_socket)) {
@@ -158,6 +193,24 @@
return server_socket;
}
+scoped_ptr<Socket> CreateListeningTcpSocketWrapped(
+ SbSocketAddressType address_type,
+ int port) {
+ scoped_ptr<Socket> server_socket =
+ CreateBoundTcpSocketWrapped(address_type, port);
+ if (!server_socket) {
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ SbSocketError result = server_socket->Listen();
+ if (result != kSbSocketOk) {
+ ADD_FAILURE() << "SbSocketListen failed: " << result;
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return server_socket.Pass();
+}
+
namespace {
SbSocket CreateConnectingTcpSocket(SbSocketAddressType address_type, int port) {
SbSocket client_socket = SbSocketCreate(address_type, kSbSocketProtocolTcp);
@@ -180,6 +233,30 @@
return client_socket;
}
+
+scoped_ptr<Socket> CreateConnectingTcpSocketWrapped(
+ SbSocketAddressType address_type,
+ int port) {
+ scoped_ptr<Socket> client_socket =
+ make_scoped_ptr(new Socket(address_type, kSbSocketProtocolTcp));
+ if (!client_socket->IsValid()) {
+ ADD_FAILURE() << "SbSocketCreate failed";
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ // Connect to localhost:<port>.
+ SbSocketAddress address = GetLocalhostAddress(address_type, port);
+
+ // This connect will probably return pending, but we'll assume it will connect
+ // eventually.
+ SbSocketError result = client_socket->Connect(&address);
+ if (result != kSbSocketOk && result != kSbSocketPending) {
+ ADD_FAILURE() << "SbSocketConnect failed: " << result;
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return client_socket.Pass();
+}
} // namespace
SbSocket AcceptBySpinning(SbSocket server_socket, SbTime timeout) {
@@ -205,6 +282,29 @@
return kSbSocketInvalid;
}
+scoped_ptr<Socket> AcceptBySpinning(Socket* server_socket, SbTime timeout) {
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+ while (true) {
+ Socket* accepted_socket = server_socket->Accept();
+ if (accepted_socket && accepted_socket->IsValid()) {
+ return make_scoped_ptr(accepted_socket);
+ }
+
+ // If we didn't get a socket, it should be pending.
+ EXPECT_TRUE(server_socket->IsPending());
+
+ // Check if we have passed our timeout.
+ if (SbTimeGetMonotonicNow() - start >= timeout) {
+ break;
+ }
+
+ // Just being polite.
+ SbThreadYield();
+ }
+
+ return scoped_ptr<Socket>().Pass();
+}
+
bool WriteBySpinning(SbSocket socket,
const char* data,
int data_size,
@@ -232,6 +332,33 @@
return true;
}
+bool WriteBySpinning(Socket* socket,
+ const char* data,
+ int data_size,
+ SbTime timeout) {
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+ int total = 0;
+ while (total < data_size) {
+ int sent = socket->SendTo(data + total, data_size - total, NULL);
+ if (sent >= 0) {
+ total += sent;
+ continue;
+ }
+
+ if (!socket->IsPending()) {
+ return false;
+ }
+
+ if (SbTimeGetMonotonicNow() - start >= timeout) {
+ return false;
+ }
+
+ SbThreadYield();
+ }
+
+ return true;
+}
+
bool ReadBySpinning(SbSocket socket,
char* out_data,
int data_size,
@@ -260,6 +387,106 @@
return true;
}
+bool ReadBySpinning(Socket* socket,
+ char* out_data,
+ int data_size,
+ SbTime timeout) {
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+ int total = 0;
+ while (total < data_size) {
+ int received =
+ socket->ReceiveFrom(out_data + total, data_size - total, NULL);
+ if (received >= 0) {
+ total += received;
+ continue;
+ }
+
+ if (!socket->IsPending()) {
+ return false;
+ }
+
+ if (SbTimeGetMonotonicNow() - start >= timeout) {
+ return false;
+ }
+
+ SbThreadYield();
+ }
+
+ return true;
+}
+
+int Transfer(SbSocket receive_socket,
+ char* out_data,
+ SbSocket send_socket,
+ const char* send_data,
+ int size) {
+ int send_total = 0;
+ int receive_total = 0;
+ while (receive_total < size) {
+ if (send_total < size) {
+ int bytes_sent = SbSocketSendTo(send_socket, send_data + send_total,
+ size - send_total, NULL);
+ if (bytes_sent < 0) {
+ if (SbSocketGetLastError(send_socket) != kSbSocketPending) {
+ return -1;
+ }
+ bytes_sent = 0;
+ }
+
+ send_total += bytes_sent;
+ }
+
+ int bytes_received = SbSocketReceiveFrom(
+ receive_socket, out_data + receive_total, size - receive_total, NULL);
+ if (bytes_received < 0) {
+ if (SbSocketGetLastError(receive_socket) != kSbSocketPending) {
+ return -1;
+ }
+ bytes_received = 0;
+ }
+
+ receive_total += bytes_received;
+ }
+
+ return size;
+}
+
+int Transfer(Socket* receive_socket,
+ char* out_data,
+ Socket* send_socket,
+ const char* send_data,
+ int size) {
+ int send_total = 0;
+ int receive_total = 0;
+ while (receive_total < size) {
+ if (send_total < size) {
+ int bytes_sent =
+ send_socket->SendTo(send_data + send_total, size - send_total, NULL);
+ if (bytes_sent < 0) {
+ if (!send_socket->IsPending()) {
+ return -1;
+ }
+ bytes_sent = 0;
+ }
+
+ send_total += bytes_sent;
+ }
+
+ int bytes_received = receive_socket->ReceiveFrom(
+ out_data + receive_total, size - receive_total, NULL);
+ if (bytes_received < 0) {
+ if (!receive_socket->IsPending()) {
+ return -1;
+ }
+ bytes_received = 0;
+ }
+
+ receive_total += bytes_received;
+ }
+
+ return size;
+}
+
ConnectedTrio CreateAndConnect(SbSocketAddressType server_address_type,
SbSocketAddressType client_address_type,
int port,
@@ -292,6 +519,40 @@
return ConnectedTrio(listen_socket, client_socket, server_socket);
}
+scoped_ptr<ConnectedTrioWrapped> CreateAndConnectWrapped(
+ SbSocketAddressType server_address_type,
+ SbSocketAddressType client_address_type,
+ int port,
+ SbTime timeout) {
+ // Verify the listening socket.
+ scoped_ptr<Socket> listen_socket =
+ CreateListeningTcpSocketWrapped(server_address_type, port);
+ if (!listen_socket || !listen_socket->IsValid()) {
+ ADD_FAILURE() << "Could not create listen socket.";
+ return scoped_ptr<ConnectedTrioWrapped>().Pass();
+ }
+
+ // Verify the socket to connect to the listening socket.
+ scoped_ptr<Socket> client_socket =
+ CreateConnectingTcpSocketWrapped(client_address_type, port);
+ if (!client_socket || !client_socket->IsValid()) {
+ ADD_FAILURE() << "Could not create client socket.";
+ return scoped_ptr<ConnectedTrioWrapped>().Pass();
+ }
+
+ // Spin until the accept happens (or we get impatient).
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+ scoped_ptr<Socket> server_socket =
+ AcceptBySpinning(listen_socket.get(), timeout);
+ if (!server_socket || !server_socket->IsValid()) {
+ ADD_FAILURE() << "Failed to accept within " << timeout;
+ return scoped_ptr<ConnectedTrioWrapped>().Pass();
+ }
+
+ return make_scoped_ptr(new ConnectedTrioWrapped(
+ listen_socket.Pass(), client_socket.Pass(), server_socket.Pass()));
+}
+
SbTimeMonotonic TimedWait(SbSocketWaiter waiter) {
SbTimeMonotonic start = SbTimeGetMonotonicNow();
SbSocketWaiterWait(waiter);
diff --git a/src/starboard/nplb/socket_helpers.h b/src/starboard/nplb/socket_helpers.h
index 8cb8922..38086d4 100644
--- a/src/starboard/nplb/socket_helpers.h
+++ b/src/starboard/nplb/socket_helpers.h
@@ -15,6 +15,7 @@
#ifndef STARBOARD_NPLB_SOCKET_HELPERS_H_
#define STARBOARD_NPLB_SOCKET_HELPERS_H_
+#include "starboard/common/scoped_ptr.h"
#include "starboard/socket.h"
#include "starboard/socket_waiter.h"
#include "starboard/time.h"
@@ -46,31 +47,60 @@
// Creates a TCP/IP server socket (sets Reuse Address option).
SbSocket CreateServerTcpSocket(SbSocketAddressType address_type);
+scoped_ptr<Socket> CreateServerTcpSocketWrapped(
+ SbSocketAddressType address_type);
// Creates a TCP/IP socket bound to all interfaces on the given port.
SbSocket CreateBoundTcpSocket(SbSocketAddressType address_type, int port);
+scoped_ptr<Socket> CreateBoundTcpSocketWrapped(SbSocketAddressType address_type,
+ int port);
// Creates a TCP/IP socket listening on all interfaces on the given port.
SbSocket CreateListeningTcpSocket(SbSocketAddressType address_type, int port);
+scoped_ptr<Socket> CreateListeningTcpSocketWrapped(
+ SbSocketAddressType address_type,
+ int port);
// Tries to accept a new connection from the given listening socket by checking,
// yielding, and retrying for up to timeout. Returns kSbSocketInvalid if no
// socket has been accepted in the given time.
SbSocket AcceptBySpinning(SbSocket listen_socket, SbTime timeout);
+scoped_ptr<Socket> AcceptBySpinning(Socket* listen_socket, SbTime timeout);
// Writes the given data to socket, spinning until success or error.
bool WriteBySpinning(SbSocket socket,
const char* data,
int data_size,
SbTime timeout);
+bool WriteBySpinning(Socket* socket,
+ const char* data,
+ int data_size,
+ SbTime timeout);
// Reads the given amount of data from socket, spinning until success or error.
bool ReadBySpinning(SbSocket socket,
char* out_data,
int data_size,
SbTime timeout);
+bool ReadBySpinning(Socket* socket,
+ char* out_data,
+ int data_size,
+ SbTime timeout);
-typedef struct ConnectedTrio {
+// Transfers data between the two connected local sockets, spinning until |size|
+// has been transfered, or an error occurs.
+int Transfer(SbSocket receive_socket,
+ char* out_data,
+ SbSocket send_socket,
+ const char* send_data,
+ int size);
+int Transfer(Socket* receive_socket,
+ char* out_data,
+ Socket* send_socket,
+ const char* send_data,
+ int size);
+
+struct ConnectedTrio {
ConnectedTrio()
: listen_socket(kSbSocketInvalid),
client_socket(kSbSocketInvalid),
@@ -84,7 +114,20 @@
SbSocket listen_socket;
SbSocket client_socket;
SbSocket server_socket;
-} ConnectedTrio;
+};
+
+struct ConnectedTrioWrapped {
+ ConnectedTrioWrapped() {}
+ ConnectedTrioWrapped(scoped_ptr<Socket> listen_socket,
+ scoped_ptr<Socket> client_socket,
+ scoped_ptr<Socket> server_socket)
+ : listen_socket(listen_socket.Pass()),
+ client_socket(client_socket.Pass()),
+ server_socket(server_socket.Pass()) {}
+ scoped_ptr<Socket> listen_socket;
+ scoped_ptr<Socket> client_socket;
+ scoped_ptr<Socket> server_socket;
+};
// Creates and returns 3 TCP/IP sockets, a connected client and server, and a
// listener on the given port. If anything fails, adds a failure and returns
@@ -93,6 +136,11 @@
SbSocketAddressType client_address_type,
int port,
SbTime timeout);
+scoped_ptr<ConnectedTrioWrapped> CreateAndConnectWrapped(
+ SbSocketAddressType server_address_type,
+ SbSocketAddressType client_address_type,
+ int port,
+ SbTime timeout);
// Waits on the given waiter, and returns the elapsed time.
SbTimeMonotonic TimedWait(SbSocketWaiter waiter);
diff --git a/src/starboard/nplb/socket_wrapper_test.cc b/src/starboard/nplb/socket_wrapper_test.cc
new file mode 100644
index 0000000..1ec2ca4
--- /dev/null
+++ b/src/starboard/nplb/socket_wrapper_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2017 Google Inc. 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 <utility>
+
+#include "starboard/nplb/socket_helpers.h"
+#include "starboard/socket.h"
+#include "starboard/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+class PairSbSocketWrapperTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
+TEST_P(PairSbSocketWrapperTest, SunnyDay) {
+ const int kBufSize = 256 * 1024;
+ const int kSockBufSize = kBufSize / 8;
+
+ scoped_ptr<ConnectedTrioWrapped> trio =
+ CreateAndConnectWrapped(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
+ ASSERT_TRUE(trio->server_socket);
+ ASSERT_TRUE(trio->server_socket->IsValid());
+
+ // Let's set the buffers small to create partial reads and writes.
+ trio->client_socket->SetReceiveBufferSize(kSockBufSize);
+ trio->server_socket->SetReceiveBufferSize(kSockBufSize);
+ trio->client_socket->SetSendBufferSize(kSockBufSize);
+ trio->server_socket->SetSendBufferSize(kSockBufSize);
+
+ // Create the buffers and fill the send buffer with a pattern, the receive
+ // buffer with zeros.
+ scoped_array<char> pattern(new char[kBufSize]);
+ scoped_array<char> send_buf(new char[kBufSize]);
+ scoped_array<char> receive_buf(new char[kBufSize]);
+ for (int i = 0; i < kBufSize; ++i) {
+ pattern[i] = static_cast<char>(i);
+ send_buf[i] = static_cast<char>(i);
+ receive_buf[i] = 0;
+ }
+
+ // Send from server to client and verify the pattern.
+ int transferred =
+ Transfer(trio->client_socket.get(), receive_buf.get(),
+ trio->server_socket.get(), send_buf.get(), kBufSize);
+ EXPECT_EQ(kBufSize, transferred);
+ for (int i = 0; i < kBufSize; ++i) {
+ EXPECT_EQ(pattern[i], send_buf[i]) << "Position " << i;
+ EXPECT_EQ(pattern[i], receive_buf[i]) << "Position " << i;
+ }
+
+ // Try the other way now, first clearing the target buffer again.
+ for (int i = 0; i < kBufSize; ++i) {
+ receive_buf[i] = 0;
+ }
+ transferred = Transfer(trio->server_socket.get(), receive_buf.get(),
+ trio->client_socket.get(), send_buf.get(), kBufSize);
+ EXPECT_EQ(kBufSize, transferred);
+ for (int i = 0; i < kBufSize; ++i) {
+ EXPECT_EQ(pattern[i], send_buf[i]) << "Position " << i;
+ EXPECT_EQ(pattern[i], receive_buf[i]) << "Position " << i;
+ }
+}
+
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketWrapperTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketWrapperTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index 5123449..2954044 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -50,6 +50,9 @@
# Force char to be signed.
'-fsigned-char',
+ # To support large files
+ '-D_FILE_OFFSET_BITS=64',
+
# Suppress some warnings that will be hard to fix.
'-Wno-unused-local-typedefs',
'-Wno-unused-result',
diff --git a/src/starboard/shared/lib/exported/starboard_main.h b/src/starboard/shared/lib/exported/starboard_main.h
new file mode 100644
index 0000000..3a49888
--- /dev/null
+++ b/src/starboard/shared/lib/exported/starboard_main.h
@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file provides a simple API for exposing Starboard as a
+// library to another app.
+
+#ifndef STARBOARD_SHARED_LIB_EXPORTED_STARBOARD_MAIN_H_
+#define STARBOARD_SHARED_LIB_EXPORTED_STARBOARD_MAIN_H_
+
+#include "starboard/export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SB_EXPORT_PLATFORM int StarboardMain(int argc, char** argv);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // STARBOARD_SHARED_LIB_EXPORTED_STARBOARD_MAIN_H_
diff --git a/src/starboard/shared/msvc/uwp/toolchain.py b/src/starboard/shared/msvc/uwp/toolchain.py
index e0d9d3d..7c74f32 100644
--- a/src/starboard/shared/msvc/uwp/toolchain.py
+++ b/src/starboard/shared/msvc/uwp/toolchain.py
@@ -14,9 +14,9 @@
import gyp.MSVSVersion # pylint: disable=g-import-not-at-top
# This tool_chain is starboard/build/toolchain.py
-from starboard.tools.toolchain import CompilerSettings
-from starboard.tools.toolchain import PrecompiledHeader
-from starboard.tools.toolchain import Toolchain
+from starboard.tools.toolchain_deprecated import CompilerSettings
+from starboard.tools.toolchain_deprecated import PrecompiledHeader
+from starboard.tools.toolchain_deprecated import Toolchain
def _LanguageMatchesForPch(source_ext, pch_source_ext):
c_exts = ('.c',)
diff --git a/src/starboard/shared/starboard/link_receiver.cc b/src/starboard/shared/starboard/link_receiver.cc
new file mode 100644
index 0000000..e94db87
--- /dev/null
+++ b/src/starboard/shared/starboard/link_receiver.cc
@@ -0,0 +1,431 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/link_receiver.h"
+
+#include <string>
+#include <unordered_map>
+
+#include "starboard/atomic.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/common/semaphore.h"
+#include "starboard/file.h"
+#include "starboard/log.h"
+#include "starboard/shared/starboard/application.h"
+#include "starboard/socket.h"
+#include "starboard/socket_waiter.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+namespace {
+// Returns an address that means bind to any interface on the given |port|. When
+// |port| is zero, it means the system should choose the port.
+SbSocketAddress GetUnspecifiedAddress(SbSocketAddressType address_type,
+ int port) {
+ SbSocketAddress address = {0};
+ address.type = address_type;
+ address.port = port;
+ return address;
+}
+
+// Returns an address that means bind to the loopback interface on the given
+// |port|. When |port| is zero, it means the system should choose the port.
+SbSocketAddress GetLocalhostAddress(SbSocketAddressType address_type,
+ int port) {
+ SbSocketAddress address = GetUnspecifiedAddress(address_type, port);
+ switch (address_type) {
+ case kSbSocketAddressTypeIpv4: {
+ address.address[0] = 127;
+ address.address[3] = 1;
+ return address;
+ }
+ case kSbSocketAddressTypeIpv6: {
+ address.address[15] = 1;
+ return address;
+ }
+ }
+ SB_LOG(ERROR) << __FUNCTION__ << ": unknown address type: " << address_type;
+ return address;
+}
+
+// Creates a socket that is appropriate for binding and listening, but is not
+// bound and hasn't started listening yet.
+scoped_ptr<Socket> CreateServerSocket(SbSocketAddressType address_type) {
+ scoped_ptr<Socket> socket(new Socket(address_type));
+ if (!socket->IsValid()) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "SbSocketCreate failed";
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ if (!socket->SetReuseAddress(true)) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "SbSocketSetReuseAddress failed";
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return socket.Pass();
+}
+
+// Creates a server socket that is bound to the loopback interface.
+scoped_ptr<Socket> CreateLocallyBoundSocket(SbSocketAddressType address_type,
+ int port) {
+ scoped_ptr<Socket> socket = CreateServerSocket(address_type);
+ if (!socket) {
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ SbSocketAddress address = GetLocalhostAddress(address_type, port);
+ SbSocketError result = socket->Bind(&address);
+ if (result != kSbSocketOk) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "SbSocketBind to " << port << " failed: " << result;
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return socket.Pass();
+}
+
+// Creates a server socket that is bound and listening to the loopback interface
+// on the given port.
+scoped_ptr<Socket> CreateListeningSocket(SbSocketAddressType address_type,
+ int port) {
+ scoped_ptr<Socket> socket = CreateLocallyBoundSocket(address_type, port);
+ if (!socket) {
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ SbSocketError result = socket->Listen();
+ if (result != kSbSocketOk) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "SbSocketListen failed: " << result;
+ return scoped_ptr<Socket>().Pass();
+ }
+
+ return socket.Pass();
+}
+
+// Gets the port socket is bound to.
+bool GetBoundPort(Socket* socket, int* out_port) {
+ SB_DCHECK(out_port);
+ SB_DCHECK(socket);
+
+ SbSocketAddress socket_address = {0};
+ bool result = socket->GetLocalAddress(&socket_address);
+ if (!result) {
+ return false;
+ }
+
+ *out_port = socket_address.port;
+ return true;
+}
+
+std::string GetTemporaryDirectory() {
+ const int kMaxPathLength = SB_FILE_MAX_PATH;
+ scoped_array<char> temp_path(new char[kMaxPathLength]);
+ bool has_temp = SbSystemGetPath(kSbSystemPathTempDirectory, temp_path.get(),
+ kMaxPathLength);
+ if (!has_temp) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "No temporary directory.";
+ return "";
+ }
+
+ return std::string(temp_path.get());
+}
+
+// Writes |size| bytes of |contents| to the file at |name|.
+void CreateTemporaryFile(const char* name, const char* contents, int size) {
+ std::string path = GetTemporaryDirectory();
+ if (path.empty()) {
+ return;
+ }
+
+ path += SB_FILE_SEP_STRING;
+ path += name;
+ ScopedFile file(path.c_str(), kSbFileCreateAlways | kSbFileWrite);
+ if (!file.IsValid()) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "Unable to create: " << path;
+ return;
+ }
+
+ file.WriteAll(contents, size);
+ file.Flush();
+}
+} // namespace
+
+// PImpl of LinkReceiver.
+class LinkReceiver::Impl {
+ public:
+ Impl(Application* application, int port);
+ ~Impl();
+
+ private:
+ // Encapsulates connection state.
+ struct Connection {
+ explicit Connection(scoped_ptr<Socket> socket) : socket(socket.Pass()) {}
+ ~Connection() {}
+ void FlushLink(Application* application) {
+ if (!data.empty()) {
+ application->Link(data.c_str());
+ data.clear();
+ }
+ }
+
+ scoped_ptr<Socket> socket;
+ std::string data;
+ };
+
+ // Runs the server, waiting on an SbSocketWaiter, and blocking until shut
+ // down.
+ void Run();
+
+ // Adds |socket| to the SbSocketWaiter to wait until ready for accepting a new
+ // connection.
+ bool AddForAccept(Socket* socket);
+
+ // Adds the |connection| to the SbSocketWaiter to wait until ready to read
+ // more data.
+ bool AddForRead(Connection* connection);
+
+ // Called when the listening socket has a connection available to accept.
+ void OnAcceptReady();
+
+ // Called when the waiter reports that a socket has more data to read.
+ void OnReadReady(SbSocket sb_socket);
+
+ // Called when the waiter reports that a connection has more data to read.
+ void OnReadReady(Connection* connection);
+
+ // Thread entry point.
+ static void* RunThread(void* context);
+
+ // SbSocketWaiter entry points.
+ static void HandleAccept(SbSocketWaiter waiter,
+ SbSocket socket,
+ void* context,
+ int ready_interests);
+ static void HandleRead(SbSocketWaiter waiter,
+ SbSocket socket,
+ void* context,
+ int ready_interests);
+
+ // The application to dispatch Link() calls to.
+ Application* application_;
+
+ // The port that was specified by the constructor.
+ const int specified_port_;
+
+ // The port that was queried off of the bound socket.
+ int actual_port_;
+
+ // The thread owned by this server.
+ SbThread thread_;
+
+ // An atomic flag that indicates whether to quit to the server thread.
+ atomic_bool quit_;
+
+ // The waiter to register sockets with and block on.
+ SbSocketWaiter waiter_;
+
+ // A semaphore that will be signaled by the internal thread once the waiter
+ // has been initialized, so the external thread can safely wake up the waiter.
+ Semaphore waiter_initialized_;
+
+ // A semaphore that will be signaled by the external thread indicating that it
+ // will no longer reference the waiter, so that the internal thread can safely
+ // destroy the waiter.
+ Semaphore destroy_waiter_;
+
+ // The server socket listening for new connections.
+ scoped_ptr<Socket> listen_socket_;
+
+ // A map of raw SbSockets to Connection objects.
+ std::unordered_map<SbSocket, Connection*> connections_;
+};
+
+LinkReceiver::Impl::Impl(Application* application, int port)
+ : application_(application),
+ specified_port_(port),
+ thread_(kSbThreadInvalid),
+ waiter_(kSbSocketWaiterInvalid) {
+ thread_ =
+ SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true,
+ "LinkReceiver", &LinkReceiver::Impl::RunThread, this);
+
+ // Block until waiter is set.
+ waiter_initialized_.Take();
+}
+
+LinkReceiver::Impl::~Impl() {
+ SB_DCHECK(!SbThreadIsEqual(thread_, SbThreadGetCurrent()));
+ quit_.store(true);
+ SbSocketWaiterWakeUp(waiter_);
+ destroy_waiter_.Put();
+ SbThreadJoin(thread_, NULL);
+}
+
+void LinkReceiver::Impl::Run() {
+ waiter_ = SbSocketWaiterCreate();
+ SB_DCHECK(SbSocketWaiterIsValid(waiter_));
+ listen_socket_ =
+ CreateListeningSocket(kSbSocketAddressTypeIpv4, specified_port_);
+ SB_DCHECK(listen_socket_);
+ actual_port_ = 0;
+ bool result = GetBoundPort(listen_socket_.get(), &actual_port_);
+ SB_DCHECK(result);
+ SB_LOG(INFO) << "LinkReceiver port: " << actual_port_;
+
+ char port_string[32] = {0};
+ SbStringFormatF(port_string, SB_ARRAY_SIZE(port_string), "%d", actual_port_);
+ CreateTemporaryFile("link_receiver_port", port_string,
+ SbStringGetLength(port_string));
+
+ if (!AddForAccept(listen_socket_.get())) {
+ quit_.store(true);
+ }
+
+ waiter_initialized_.Put();
+ while (!quit_.load()) {
+ SbSocketWaiterWait(waiter_);
+ }
+
+ for (auto& entry : connections_) {
+ SbSocketWaiterRemove(waiter_, entry.first);
+ delete entry.second;
+ }
+ connections_.clear();
+
+ SbSocketWaiterRemove(waiter_, listen_socket_->socket());
+
+ // Block until destroying thread will no longer reference waiter.
+ destroy_waiter_.Take();
+ SbSocketWaiterDestroy(waiter_);
+}
+
+bool LinkReceiver::Impl::AddForAccept(Socket* socket) {
+ if (!SbSocketWaiterAdd(waiter_, socket->socket(), this,
+ &LinkReceiver::Impl::HandleAccept,
+ kSbSocketWaiterInterestRead, true)) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "SbSocketWaiterAdd failed.";
+ return false;
+ }
+ return true;
+}
+
+bool LinkReceiver::Impl::AddForRead(Connection* connection) {
+ if (!SbSocketWaiterAdd(waiter_, connection->socket->socket(), this,
+ &LinkReceiver::Impl::HandleRead,
+ kSbSocketWaiterInterestRead, false)) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": "
+ << "SbSocketWaiterAdd failed.";
+ return false;
+ }
+ return true;
+}
+
+void LinkReceiver::Impl::OnAcceptReady() {
+ scoped_ptr<Socket> accepted_socket =
+ make_scoped_ptr(listen_socket_->Accept());
+ SB_DCHECK(accepted_socket);
+ Connection* connection = new Connection(accepted_socket.Pass());
+ connections_.emplace(connection->socket->socket(), connection);
+ AddForRead(connection);
+}
+
+void LinkReceiver::Impl::OnReadReady(SbSocket sb_socket) {
+ auto iter = connections_.find(sb_socket);
+ SB_DCHECK(iter != connections_.end());
+ OnReadReady(iter->second);
+}
+
+void LinkReceiver::Impl::OnReadReady(Connection* connection) {
+ auto socket = connection->socket.get();
+
+ char data[64] = {0};
+ int read = socket->ReceiveFrom(data, SB_ARRAY_SIZE_INT(data), NULL);
+ int last_null = 0;
+ for (int position = 0; position < read; ++position) {
+ if (data[position] == '\0' || data[position] == '\n' ||
+ data[position] == '\r') {
+ int length = position - last_null;
+ if (length) {
+ connection->data.append(&data[last_null], length);
+ connection->FlushLink(application_);
+ }
+ last_null = position + 1;
+ continue;
+ }
+ }
+
+ int remainder = read - last_null;
+ if (remainder > 0) {
+ connection->data.append(&data[last_null], remainder);
+ }
+
+ if (read == 0) {
+ // Terminate connection.
+ connection->FlushLink(application_);
+ connections_.erase(socket->socket());
+ delete connection;
+ return;
+ }
+
+ AddForRead(connection);
+}
+
+// static
+void* LinkReceiver::Impl::RunThread(void* context) {
+ SB_DCHECK(context);
+ reinterpret_cast<LinkReceiver::Impl*>(context)->Run();
+ return NULL;
+}
+
+// static
+void LinkReceiver::Impl::HandleAccept(SbSocketWaiter /*waiter*/,
+ SbSocket /*socket*/,
+ void* context,
+ int ready_interests) {
+ SB_DCHECK(context);
+ reinterpret_cast<LinkReceiver::Impl*>(context)->OnAcceptReady();
+}
+
+// static
+void LinkReceiver::Impl::HandleRead(SbSocketWaiter /*waiter*/,
+ SbSocket socket,
+ void* context,
+ int /*ready_interests*/) {
+ SB_DCHECK(context);
+ reinterpret_cast<LinkReceiver::Impl*>(context)->OnReadReady(socket);
+}
+
+LinkReceiver::LinkReceiver(Application* application)
+ : impl_(new Impl(application, 0)) {}
+
+LinkReceiver::LinkReceiver(Application* application, int port)
+ : impl_(new Impl(application, port)) {}
+
+LinkReceiver::~LinkReceiver() {
+ delete impl_;
+}
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/link_receiver.h b/src/starboard/shared/starboard/link_receiver.h
new file mode 100644
index 0000000..b2cc68d
--- /dev/null
+++ b/src/starboard/shared/starboard/link_receiver.h
@@ -0,0 +1,55 @@
+// Copyright 2017 Google Inc. 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_SHARED_STARBOARD_LINK_RECEIVER_H_
+#define STARBOARD_SHARED_STARBOARD_LINK_RECEIVER_H_
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/application.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+// A loopback-only server that listens to receive null-terminated strings that
+// will be dispatched to the Link() method on the passed in |application|. This
+// will result in kSbEventTypeLink events beind dispatched on the main Starboard
+// dispatch thread.
+//
+// This server Runs on its own thread, joining it on destruction. It must be
+// destroyed before the associated Application is destroyed.
+//
+// When the server is started, it attempts to write a file to the temporary
+// directory with the port that it is listening on. Other programs can then look
+// for this file to find the port to connect to to send links.
+//
+// The script starboard/tools/send_link.py can dispatch links to this server, if
+// running.
+class LinkReceiver {
+ public:
+ explicit LinkReceiver(Application* application);
+ LinkReceiver(Application* application, int port);
+ ~LinkReceiver();
+
+ private:
+ class Impl;
+
+ Impl* impl_;
+};
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_LINK_RECEIVER_H_
diff --git a/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc b/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
index 0a96110..bdf679d 100644
--- a/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
+++ b/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
@@ -257,6 +257,14 @@
}
}
+ std::string cryptoblockformat =
+ mime_type.GetParamStringValue("cryptoblockformat", "");
+ if (!cryptoblockformat.empty()) {
+ if (mime_type.subtype() != "webm" || cryptoblockformat != "subsample") {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
int width = 0;
int height = 0;
int fps = 0;
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index 598f725..dbaa49e 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -379,8 +379,11 @@
scoped_refptr<DecodedAudio> resampled_audio;
scoped_refptr<DecodedAudio> decoded_audio = decoder_->Read();
- SB_DCHECK(decoded_audio);
--pending_decoder_outputs_;
+ SB_DCHECK(decoded_audio);
+ if (!decoded_audio) {
+ continue;
+ }
if (decoded_audio->is_end_of_stream()) {
SB_DCHECK(eos_state_.load() == kEOSWrittenToDecoder) << eos_state_.load();
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index a57c6ac..76b6e53 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -87,6 +87,14 @@
bounds_ = PlayerWorker::Bounds();
}
+bool FilterBasedPlayerWorkerHandler::IsPunchoutMode() const {
+#if SB_API_VERSION >= 4
+ return (output_mode_ == kSbPlayerOutputModePunchOut);
+#else
+ return true;
+#endif // SB_API_VERSION >= 4
+}
+
bool FilterBasedPlayerWorkerHandler::Init(
PlayerWorker* player_worker,
JobQueue* job_queue,
@@ -320,12 +328,9 @@
player_worker_->UpdateDroppedVideoFrames(
video_renderer_->GetDroppedFrames());
-#if SB_API_VERSION >= 4
- if (output_mode_ == kSbPlayerOutputModePunchOut)
-#endif // SB_API_VERSION >= 4
- {
- shared::starboard::Application::Get()->HandleFrame(
- player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
+ if (IsPunchoutMode()) {
+ shared::starboard::Application::Get()->HandleFrame(
+ player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
}
(*player_worker_.*update_media_time_cb_)(audio_renderer_->GetCurrentTime());
@@ -350,13 +355,10 @@
}
video_renderer.reset();
-#if SB_API_VERSION >= 4
- if (output_mode_ == kSbPlayerOutputModePunchOut)
-#endif // SB_API_VERSION >= 4
- {
- // Clear the video frame as we terminate.
- shared::starboard::Application::Get()->HandleFrame(
- player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);
+ if (IsPunchoutMode()) {
+ // Clear the video frame as we terminate.
+ shared::starboard::Application::Get()->HandleFrame(
+ player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);
}
}
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index 0ec1a3d..8715ab9 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -53,6 +53,7 @@
); // NOLINT(whitespace/parens)
private:
+ bool IsPunchoutMode() const;
bool Init(PlayerWorker* player_worker,
JobQueue* job_queue,
SbPlayer player,
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
index ffd6aca..685927f 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
@@ -81,6 +81,7 @@
seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0);
seeking_ = true;
end_of_stream_written_ = false;
+ need_more_input_ = true;
frames_.clear();
}
@@ -158,8 +159,15 @@
return kSbDecodeTargetInvalid;
}
}
+
#endif // SB_API_VERSION >= 4
+::starboard::scoped_refptr<VideoFrame>
+VideoRendererImpl::GetLastDisplayedFrame() {
+ ScopedLock lock(mutex_);
+ return last_displayed_frame_;
+}
+
} // namespace filter
} // namespace player
} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
index b40365e..77a1d31 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
@@ -62,6 +62,8 @@
SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
#endif // SB_API_VERSION >= 4
+ scoped_refptr<VideoFrame> GetLastDisplayedFrame();
+
private:
typedef std::list<scoped_refptr<VideoFrame> > Frames;
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
index 6aa337e..718abd8 100644
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -77,7 +77,7 @@
const int kWinSockVersionMajor = 2;
const int kWinSockVersionMinor = 2;
-const char kYouTubeTVurl[] = "--url=https://www.youtube.com/tv/?";
+const char kDialParamPrefix[] = "cobalt-dial:?";
int main_return_value = 0;
@@ -300,22 +300,19 @@
if (uri->SchemeName->Equals("youtube") ||
uri->SchemeName->Equals("ms-xbl-07459769")) {
std::string uri_string = sbwin32::platformStringToString(uri->RawUri);
- if (previously_activated_) {
- std::unique_ptr<Application::Event> event =
- MakeDeepLinkEvent(uri_string);
- SB_DCHECK(event);
- ApplicationUwp::Get()->Inject(event.release());
- } else {
- SB_DCHECK(!uri_string.empty());
- ApplicationUwp::Get()->SetStartLink(uri_string.c_str());
- }
+ ProcessDeepLinkUri(&uri_string);
}
} else if (args->Kind == ActivationKind::DialReceiver) {
- if (!previously_activated_) {
- DialReceiverActivatedEventArgs^ dial_args =
- dynamic_cast<DialReceiverActivatedEventArgs^>(args);
- SB_CHECK(dial_args);
- Platform::String^ arguments = dial_args->Arguments;
+ DialReceiverActivatedEventArgs^ dial_args =
+ dynamic_cast<DialReceiverActivatedEventArgs^>(args);
+ SB_CHECK(dial_args);
+ Platform::String^ arguments = dial_args->Arguments;
+ if (previously_activated_) {
+ std::string uri_string =
+ kDialParamPrefix + sbwin32::platformStringToString(arguments);
+ ProcessDeepLinkUri(&uri_string);
+ } else {
+ const char kYouTubeTVurl[] = "--url=https://www.youtube.com/tv/?";
std::string activation_args =
kYouTubeTVurl + sbwin32::platformStringToString(arguments);
SB_DLOG(INFO) << "Dial Activation url: " << activation_args;
@@ -348,6 +345,19 @@
previously_activated_ = true;
}
private:
+ void ProcessDeepLinkUri(std::string *uri_string) {
+ SB_DCHECK(uri_string);
+ if (previously_activated_) {
+ std::unique_ptr<Application::Event> event =
+ MakeDeepLinkEvent(*uri_string);
+ SB_DCHECK(event);
+ ApplicationUwp::Get()->Inject(event.release());
+ } else {
+ SB_DCHECK(!uri_string->empty());
+ ApplicationUwp::Get()->SetStartLink(uri_string->c_str());
+ }
+ }
+
bool previously_activated_;
// Only valid if previously_activated_ is true
ActivationKind previous_activation_kind_;
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h
index b326d1e..fda18e2 100644
--- a/src/starboard/shared/uwp/application_uwp.h
+++ b/src/starboard/shared/uwp/application_uwp.h
@@ -16,7 +16,6 @@
#define STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
#include <agile.h>
-
#include <string>
#include <unordered_map>
@@ -26,7 +25,6 @@
#include "starboard/shared/starboard/application.h"
#include "starboard/shared/starboard/command_line.h"
#include "starboard/shared/starboard/localized_strings.h"
-#include "starboard/shared/uwp/winrt_workaround.h"
#include "starboard/types.h"
#include "starboard/window.h"
diff --git a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
index e7206c0..9765ab9 100644
--- a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
+++ b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
@@ -36,7 +36,7 @@
'AdditionalOptions': [
'/ZW', # Windows Runtime
'/ZW:nostdlib', # Windows Runtime, no default #using
- '/EHsx', # C++ exceptions (required with /ZW)
+ '/EHsc', # C++ exceptions (required with /ZW)
'/FU"<(visual_studio_install_path)/lib/x86/store/references/platform.winmd"',
'/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.FoundationContract/3.0.0.0/Windows.Foundation.FoundationContract.winmd"',
'/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.UniversalApiContract/4.0.0.0/Windows.Foundation.UniversalApiContract.winmd"',
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
index 9ec452d..eaf8bd3 100644
--- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
+++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
@@ -19,7 +19,6 @@
#include "starboard/mutex.h"
#include "starboard/shared/uwp/async_utils.h"
-#include "starboard/shared/uwp/winrt_workaround.h"
#include "starboard/shared/win32/wchar_utils.h"
using Windows::Security::Authentication::Web::Core::
@@ -163,12 +162,30 @@
return false;
}
+void AppendUrlPath(const std::string& path, std::string* url_parameter) {
+ DCHECK(url_parameter);
+
+ if (path.empty()) {
+ return;
+ }
+
+ std::string& url(*url_parameter);
+
+ // if path starts with a '/' and url ends with a '/', remove trailing slash
+ // from url before appending the path
+ if (!url.empty() && *(url.rbegin()) == '/' && path[0] == '/') {
+ url.resize(url.size() - 1);
+ }
+ url.append(path);
+}
+
} // namespace
namespace cobalt {
namespace xhr {
-void CobaltXhrModifyHeader(net::HttpRequestHeaders* headers) {
+void CobaltXhrModifyHeader(const GURL& request_url,
+ net::HttpRequestHeaders* headers) {
DCHECK(headers);
std::string relying_party;
@@ -178,6 +195,12 @@
if (!trigger_header_found) {
return;
}
+
+ if (request_url.has_path()) {
+ std::string request_url_path = request_url.path();
+ AppendUrlPath(request_url_path, &relying_party);
+ }
+
std::string out_string;
if (!PopulateToken(relying_party, &out_string)) {
return;
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
index 138579a..88aa32c 100644
--- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
+++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
@@ -23,17 +23,20 @@
using ::cobalt::xhr::CobaltXhrModifyHeader;
using net::HttpRequestHeaders;
+const std::string kUrlString = "https://example.com/abc/xyz";
TEST(XHRModificationTest, EmptyHeaders) {
HttpRequestHeaders headers;
- CobaltXhrModifyHeader(&headers);
+ GURL url(kUrlString);
+ CobaltXhrModifyHeader(url, &headers);
EXPECT_TRUE(headers.IsEmpty());
}
TEST(XHRModificationTest, HeaderNotFound) {
HttpRequestHeaders headers;
headers.SetHeader("Authorization", "ABC");
- CobaltXhrModifyHeader(&headers);
+ GURL url(kUrlString);
+ CobaltXhrModifyHeader(url, &headers);
EXPECT_FALSE(!headers.IsEmpty());
std::string headers_serialized = headers.ToString();
EXPECT_STREQ(headers_serialized.c_str(), "Authorization: ABC\r\n\r\n");
@@ -44,7 +47,8 @@
static const char* kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
headers.SetHeader(kXauthTriggerHeaderName, "ABC");
EXPECT_TRUE(headers.HasHeader(kXauthTriggerHeaderName));
- CobaltXhrModifyHeader(&headers);
+ GURL url(kUrlString);
+ CobaltXhrModifyHeader(url, &headers);
EXPECT_FALSE(headers.HasHeader(kXauthTriggerHeaderName));
std::string headers_serialized = headers.ToString();
EXPECT_TRUE(headers_serialized.find("Authorization: XBL3.0 x=") !=
@@ -58,7 +62,8 @@
headers.SetHeader("H2", "h2");
headers.SetHeader("H3", "h3");
EXPECT_TRUE(headers.HasHeader(kXauthTriggerHeaderName));
- CobaltXhrModifyHeader(&headers);
+ GURL url(kUrlString);
+ CobaltXhrModifyHeader(url, &headers);
EXPECT_TRUE(headers.HasHeader("H1"));
EXPECT_TRUE(headers.HasHeader("H2"));
EXPECT_TRUE(headers.HasHeader("H3"));
diff --git a/src/starboard/shared/uwp/get_home_directory.cc b/src/starboard/shared/uwp/get_home_directory.cc
new file mode 100644
index 0000000..6c08a3b
--- /dev/null
+++ b/src/starboard/shared/uwp/get_home_directory.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 Google Inc. 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 <string>
+
+#include "starboard/log.h"
+#include "starboard/shared/nouser/user_internal.h"
+#include "starboard/shared/win32/wchar_utils.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+using Windows::Storage::ApplicationData;
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace starboard {
+namespace shared {
+namespace nouser {
+
+bool GetHomeDirectory(SbUser user, char* out_path, int path_size) {
+ if (user != SbUserGetCurrent()) {
+ return false;
+ }
+ std::string home_directory = sbwin32::platformStringToString(
+ ApplicationData::Current->LocalFolder->Path);
+ return SbStringCopy(out_path, home_directory.c_str(), path_size);
+}
+
+} // namespace nouser
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
index 5370972..ee1b5bd 100644
--- a/src/starboard/shared/uwp/starboard_platform.gypi
+++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -14,20 +14,22 @@
{
'variables': {
'starboard_platform_dependent_files': [
+ 'application_uwp_key_event.cc',
'application_uwp.cc',
'application_uwp.h',
- 'application_uwp_key_event.cc',
'async_utils.h',
- 'system_get_property.cc',
+ 'get_home_directory.cc',
'system_clear_platform_error.cc',
+ 'system_get_device_type.cc',
+ 'system_get_property.cc',
'system_raise_platform_error.cc',
'window_create.cc',
'window_destroy.cc',
'window_get_platform_handle.cc',
'window_get_size.cc',
- 'window_set_default_options.cc',
'window_internal.cc',
'window_internal.h',
+ 'window_set_default_options.cc',
'winrt_workaround.h',
'<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
'<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
diff --git a/src/starboard/shared/uwp/system_get_device_type.cc b/src/starboard/shared/uwp/system_get_device_type.cc
new file mode 100644
index 0000000..81179ca
--- /dev/null
+++ b/src/starboard/shared/uwp/system_get_device_type.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <string>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/wchar_utils.h"
+
+using Windows::System::Profile::AnalyticsInfo;
+using Windows::System::Profile::AnalyticsVersionInfo;
+
+SbSystemDeviceType SbSystemGetDeviceType() {
+ AnalyticsVersionInfo ^ version_info = AnalyticsInfo::VersionInfo;
+ std::string family = starboard::shared::win32::platformStringToString(
+ version_info->DeviceFamily);
+
+ if (family.compare("Windows.Desktop") == 0) {
+ return kSbSystemDeviceTypeDesktopPC;
+ }
+ if (family.compare("Windows.Xbox") == 0) {
+ return kSbSystemDeviceTypeGameConsole;
+ }
+ SB_NOTREACHED();
+ return kSbSystemDeviceTypeUnknown;
+}
diff --git a/src/starboard/shared/uwp/system_get_property.cc b/src/starboard/shared/uwp/system_get_property.cc
index 85e85c5..0c48971 100644
--- a/src/starboard/shared/uwp/system_get_property.cc
+++ b/src/starboard/shared/uwp/system_get_property.cc
@@ -37,6 +37,37 @@
SbStringCopy(out_value, from_value, value_length);
return true;
}
+
+const std::size_t kOsVersionSize = 128;
+
+struct WindowsVersion {
+ uint16_t major_version;
+ uint16_t minor_version;
+ uint16_t build_version;
+ uint16_t revision;
+};
+
+bool GetWindowsVersion(WindowsVersion* version) {
+ SB_DCHECK(version);
+ AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
+ std::string device_family_version =
+ starboard::shared::win32::platformStringToString(
+ version_info->DeviceFamilyVersion);
+ if (device_family_version.empty()) {
+ return false;
+ }
+ uint64_t version_info_all =
+ SbStringParseUInt64(device_family_version.c_str(), nullptr, 10);
+ if (version_info_all == 0) {
+ return false;
+ }
+ version->major_version = (version_info_all >> 48) & 0xFFFF;
+ version->minor_version = (version_info_all >> 32) & 0xFFFF;
+ version->build_version = (version_info_all >> 16) & 0xFFFF;
+ version->revision = version_info_all & 0xFFFF;
+ return true;
+}
+
} // namespace
bool SbSystemGetProperty(SbSystemPropertyId property_id,
@@ -53,6 +84,7 @@
case kSbSystemPropertyModelYear:
case kSbSystemPropertyNetworkOperatorName:
case kSbSystemPropertySpeechApiKey:
+ case kSbSystemPropertyUserAgentAuxField:
return false;
case kSbSystemPropertyBrandName: {
EasClientDeviceInformation^ current_device_info =
@@ -66,19 +98,19 @@
brand_name.c_str());
}
case kSbSystemPropertyFirmwareVersion: {
- EasClientDeviceInformation ^ current_device_info =
- ref new EasClientDeviceInformation();
- std::string firmware_version =
- platformStringToString(current_device_info->SystemFirmwareVersion);
- if (firmware_version.empty()) {
+ WindowsVersion version = {0};
+ if (!GetWindowsVersion(&version)) {
return false;
}
- return CopyStringAndTestIfSuccess(out_value, value_length,
- firmware_version.c_str());
+ int return_value = SbStringFormatF(
+ out_value, value_length, "%u.%u.%u.%u", version.major_version,
+ version.minor_version, version.build_version, version.revision);
+ return ((return_value > 0) && (return_value < value_length));
}
case kSbSystemPropertyModelName: {
- EasClientDeviceInformation ^ current_device_info =
+ EasClientDeviceInformation^ current_device_info =
ref new EasClientDeviceInformation();
+ // TODO: Use SystemSku and map to friendly names instead.
std::string product_name =
platformStringToString(current_device_info->SystemProductName);
product_name.erase(
@@ -99,17 +131,40 @@
return CopyStringAndTestIfSuccess(out_value, value_length,
friendly_name.c_str());
}
-
case kSbSystemPropertyPlatformName: {
+ EasClientDeviceInformation^ current_device_info =
+ ref new EasClientDeviceInformation();
+ std::string operating_system =
+ platformStringToString(current_device_info->OperatingSystem);
+
AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
- std::string platform_str =
+ std::string os_name_and_version =
starboard::shared::win32::platformStringToString(
- version_info->DeviceFamily);
- if (platform_str.empty()) {
+ current_device_info->OperatingSystem);
+ if (os_name_and_version.empty()) {
return false;
}
+
+ WindowsVersion os_version;
+ if (!GetWindowsVersion(&os_version)) {
+ return false;
+ }
+
+ os_name_and_version += " ";
+ char os_version_buffer[kOsVersionSize];
+ os_version_buffer[0] = '\0';
+
+ int return_value =
+ SbStringFormatF(os_version_buffer, value_length, "%u.%u",
+ os_version.major_version, os_version.minor_version);
+ if ((return_value < 0) || (return_value >= value_length)) {
+ return false;
+ }
+
+ os_name_and_version.append(os_version_buffer);
+
return CopyStringAndTestIfSuccess(out_value, value_length,
- platform_str.c_str());
+ os_name_and_version.c_str());
}
case kSbSystemPropertyPlatformUuid: {
SB_NOTIMPLEMENTED();
diff --git a/src/starboard/shared/uwp/winrt_workaround.h b/src/starboard/shared/uwp/winrt_workaround.h
deleted file mode 100644
index e75b766..0000000
--- a/src/starboard/shared/uwp/winrt_workaround.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Google Inc. 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_SHARED_UWP_WINRT_WORKAROUND_H_
-#define STARBOARD_SHARED_UWP_WINRT_WORKAROUND_H_
-
-namespace __winRT {
-// TODO: without this, we get the following error at CoreApplication::Run:
-// 'long __winRT::__getActivationFactoryByPCWSTR(i
-// void *,Platform::Guid &,void **)':
-// cannot convert argument 1 from 'const wchar_t [46]' to 'void *'
-inline long __getActivationFactoryByPCWSTR(const wchar_t* a,
- Platform::Guid& b,
- void** c) {
- return __getActivationFactoryByPCWSTR(
- static_cast<void*>(const_cast<wchar_t*>(a)), b, c);
-}
-} // namespace __winRT
-
-#endif // STARBOARD_SHARED_UWP_WINRT_WORKAROUND_H_
diff --git a/src/starboard/shared/win32/application_win32.cc b/src/starboard/shared/win32/application_win32.cc
new file mode 100644
index 0000000..956d88b
--- /dev/null
+++ b/src/starboard/shared/win32/application_win32.cc
@@ -0,0 +1,249 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/application_win32.h"
+
+#include <windows.h> // NOLINT(build/include_order)
+
+#include <cstdio>
+#include <string>
+
+#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/win32/dialog.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/thread_private.h"
+#include "starboard/shared/win32/wchar_utils.h"
+#include "starboard/shared/win32/window_internal.h"
+
+using starboard::shared::starboard::Application;
+using starboard::shared::win32::ApplicationWin32;
+using starboard::shared::win32::CStringToWString;
+using starboard::shared::win32::DebugLogWinError;
+
+namespace {
+
+static const TCHAR kWindowClassName[] = L"window_class_name";
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM w_param, LPARAM l_param) {
+ return ApplicationWin32::Get()->WindowProcess(hWnd, msg, w_param, l_param);
+}
+
+bool RegisterWindowClass() {
+ WNDCLASSEX window_class;
+ window_class.cbSize = sizeof(WNDCLASSEX);
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
+ window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ window_class.lpfnWndProc = WndProc;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ // TODO: Add YouTube icon.
+ window_class.hIcon = LoadIcon(window_class.hInstance, IDI_APPLICATION);
+ window_class.hIconSm = LoadIcon(window_class.hInstance, IDI_APPLICATION);
+ window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ window_class.lpszMenuName = NULL;
+ window_class.lpszClassName = kWindowClassName;
+
+ if (!::RegisterClassEx(&window_class)) {
+ SB_LOG(ERROR) << "Failed to register window";
+ DebugLogWinError();
+ return false;
+ }
+ return true;
+}
+
+// Create a Windows window.
+HWND CreateWindowInstance(const SbWindowOptions& options) {
+ DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
+ if (options.windowed) {
+ dwStyle |= WS_MAXIMIZE;
+ }
+ const std::wstring wide_window_name = CStringToWString(options.name);
+ const HWND window = CreateWindow(
+ kWindowClassName, wide_window_name.c_str(), dwStyle, CW_USEDEFAULT,
+ CW_USEDEFAULT, options.size.width, options.size.height, nullptr, nullptr,
+ GetModuleHandle(nullptr), nullptr);
+ SetForegroundWindow(window);
+ if (window == nullptr) {
+ SB_LOG(ERROR) << "Failed to create window.";
+ DebugLogWinError();
+ }
+
+ return window;
+}
+
+} // namespace
+
+// Note that this is a "struct" and not a "class" because
+// that's how it's defined in starboard/system.h
+struct SbSystemPlatformErrorPrivate {
+ SbSystemPlatformErrorPrivate(const SbSystemPlatformErrorPrivate&) = delete;
+ SbSystemPlatformErrorPrivate& operator=(const SbSystemPlatformErrorPrivate&) =
+ delete;
+
+ SbSystemPlatformErrorPrivate(SbSystemPlatformErrorType type,
+ SbSystemPlatformErrorCallback callback,
+ void* user_data)
+ : callback_(callback), user_data_(user_data) {
+ if (type != kSbSystemPlatformErrorTypeConnectionError)
+ SB_NOTREACHED();
+
+ ApplicationWin32* app = ApplicationWin32::Get();
+ const bool created_dialog = starboard::shared::win32::ShowOkCancelDialog(
+ app->GetCoreWindow()->GetWindowHandle(),
+ "", // No title.
+ app->GetLocalizedString("UNABLE_TO_CONTACT_YOUTUBE_1",
+ "Sorry, could not connect to YouTube."),
+ app->GetLocalizedString("RETRY_BUTTON", "Retry"),
+ [this, callback, user_data]() {
+ callback(kSbSystemPlatformErrorResponsePositive, user_data);
+ },
+ app->GetLocalizedString("EXIT_BUTTON", "Exit"),
+ [this, callback, user_data]() {
+ callback(kSbSystemPlatformErrorResponseNegative, user_data);
+ });
+ SB_DCHECK(!created_dialog);
+ if (!created_dialog) {
+ SB_LOG(ERROR) << "Failed to create dialog!";
+ }
+ }
+
+ void Clear() { starboard::shared::win32::CancelDialog(); }
+
+ private:
+ SbSystemPlatformErrorCallback callback_;
+ void* user_data_;
+};
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+ApplicationWin32::ApplicationWin32()
+ : localized_strings_(SbSystemGetLocaleId()) {}
+ApplicationWin32::~ApplicationWin32() {}
+
+SbWindow ApplicationWin32::CreateWindowForWin32(
+ const SbWindowOptions* options) {
+ if (SbWindowIsValid(window_.get())) {
+ SB_LOG(WARNING) << "Returning existing window instance.";
+ return window_.get();
+ }
+
+ RegisterWindowClass();
+ HWND window;
+ if (options) {
+ window = CreateWindowInstance(*options);
+ window_.reset(new SbWindowPrivate(options, window));
+ } else {
+ SbWindowOptions default_options;
+ SbWindowSetDefaultOptions(&default_options);
+ window = CreateWindowInstance(default_options);
+ window_.reset(new SbWindowPrivate(&default_options, window));
+ }
+ ShowWindow(window, SW_SHOW);
+ UpdateWindow(window);
+ return window_.get();
+}
+
+bool ApplicationWin32::DestroyWindow(SbWindow window) {
+ if (!SbWindowIsValid(window) || window != window_.get()) {
+ return false;
+ }
+ window_.reset();
+ return true;
+}
+
+SbSystemPlatformError ApplicationWin32::OnSbSystemRaisePlatformError(
+ SbSystemPlatformErrorType type,
+ SbSystemPlatformErrorCallback callback,
+ void* user_data) {
+ return new SbSystemPlatformErrorPrivate(type, callback, user_data);
+}
+
+void ApplicationWin32::OnSbSystemClearPlatformError(
+ SbSystemPlatformError handle) {
+ if (handle == kSbSystemPlatformErrorInvalid) {
+ return;
+ }
+ static_cast<SbSystemPlatformErrorPrivate*>(handle)->Clear();
+ // TODO: Determine if this should actually be deleted and if so, delete or
+ // don't consistently across platforms.
+ delete handle;
+}
+
+Application::Event* ApplicationWin32::WaitForSystemEventWithTimeout(
+ SbTime time) {
+ ProcessNextSystemMessage();
+ if (pending_event_) {
+ Event* out = pending_event_;
+ pending_event_ = nullptr;
+ return out;
+ }
+
+ ScopedLock lock(stop_waiting_for_system_events_mutex_);
+ if (time <= SbTimeGetMonotonicNow() || stop_waiting_for_system_events_) {
+ stop_waiting_for_system_events_ = false;
+ return nullptr;
+ }
+
+ return WaitForSystemEventWithTimeout(time);
+}
+
+LRESULT ApplicationWin32::WindowProcess(HWND hWnd,
+ UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ switch (msg) {
+ // Input message handling.
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ // TODO: Listen for mouse events as well.
+ pending_event_ =
+ ProcessWinKeyEvent(GetCoreWindow(), msg, w_param, l_param);
+ break;
+ case WM_DESTROY:
+ SB_LOG(INFO) << "Received destroy message; exiting.";
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProcW(hWnd, msg, w_param, l_param);
+ }
+ return 0;
+}
+
+bool ApplicationWin32::ProcessNextSystemMessage() {
+ MSG msg;
+ BOOL get_message_return = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ if (get_message_return == -1) {
+ SB_LOG(INFO) << "Error while getting messages";
+ return false;
+ }
+ if (!DialogHandleMessage(&msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ if (get_message_return == 0) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/application_win32.h b/src/starboard/shared/win32/application_win32.h
new file mode 100644
index 0000000..18b7af2
--- /dev/null
+++ b/src/starboard/shared/win32/application_win32.h
@@ -0,0 +1,119 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_APPLICATION_WIN32_H_
+#define STARBOARD_SHARED_WIN32_APPLICATION_WIN32_H_
+
+// Windows headers.
+#include <windows.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "starboard/mutex.h"
+#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/localized_strings.h"
+#include "starboard/shared/starboard/queue_application.h"
+#include "starboard/shared/win32/window_internal.h"
+#include "starboard/system.h"
+#include "starboard/window.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class ApplicationWin32 : public starboard::QueueApplication {
+ public:
+ ApplicationWin32();
+ ~ApplicationWin32() SB_OVERRIDE;
+
+ static ApplicationWin32* Get() {
+ return static_cast<ApplicationWin32*>(
+ ::starboard::shared::starboard::Application::Get());
+ }
+
+ SbWindow CreateWindowForWin32(const SbWindowOptions* options);
+
+ bool DestroyWindow(SbWindow window);
+
+ void DispatchStart() { starboard::Application::DispatchStart(); }
+
+ SbWindow GetCoreWindow() {
+ return SbWindowIsValid(window_.get()) ? window_.get() : kSbWindowInvalid;
+ }
+
+ SbSystemPlatformError OnSbSystemRaisePlatformError(
+ SbSystemPlatformErrorType type,
+ SbSystemPlatformErrorCallback callback,
+ void* user_data);
+
+ void OnSbSystemClearPlatformError(SbSystemPlatformError handle);
+
+ std::string GetLocalizedString(const char* id, const char* fallback) const {
+ return localized_strings_.GetString(id, fallback);
+ }
+
+ // Returns true if it is valid to poll/query for system events.
+ bool MayHaveSystemEvents() SB_OVERRIDE { return true; }
+
+ // Waits for an event until the timeout |time| runs out. If an event occurs
+ // in this time, it is returned, otherwise NULL is returned. If |time| is zero
+ // or negative, then this should function effectively like a no-wait poll.
+ Event* WaitForSystemEventWithTimeout(SbTime time) SB_OVERRIDE;
+
+ // Wakes up any thread waiting within a call to
+ // WaitForSystemEventWithTimeout().
+ void WakeSystemEventWait() SB_OVERRIDE {
+ ScopedLock lock(stop_waiting_for_system_events_mutex_);
+ stop_waiting_for_system_events_ = true;
+ }
+
+ LRESULT WindowProcess(HWND hWnd, UINT msg, WPARAM w_param, LPARAM l_param);
+ VOID TimedEventCallback(PVOID lp, BOOLEAN timer_or_wait_fired);
+
+ private:
+ // --- Application overrides ---
+ bool IsStartImmediate() SB_OVERRIDE { return true; }
+ void Initialize() SB_OVERRIDE {}
+ void Teardown() SB_OVERRIDE {}
+
+ bool ProcessNextSystemMessage();
+ SbTimeMonotonic GetNextTimedEventTargetTime() SB_OVERRIDE {
+ return SbTimeGetMonotonicNow();
+ }
+
+ // Processes window key events, returning a corresponding Event instance.
+ // This transfers ownership of the returned Event.
+ Event* ProcessWinKeyEvent(SbWindow window,
+ UINT msg,
+ WPARAM w_param,
+ LPARAM l_param);
+
+ Event* pending_event_ = nullptr;
+
+ // The single open window, if any.
+ std::unique_ptr<SbWindowPrivate> window_;
+
+ starboard::LocalizedStrings localized_strings_;
+
+ Mutex stop_waiting_for_system_events_mutex_;
+ bool stop_waiting_for_system_events_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_APPLICATION_WIN32_H_
diff --git a/src/starboard/shared/win32/application_win32_key_event.cc b/src/starboard/shared/win32/application_win32_key_event.cc
new file mode 100644
index 0000000..4e02faf
--- /dev/null
+++ b/src/starboard/shared/win32/application_win32_key_event.cc
@@ -0,0 +1,238 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/application_win32.h"
+
+#include <windows.h>
+
+#include "starboard/event.h"
+#include "starboard/input.h"
+#include "starboard/key.h"
+#include "starboard/shared/starboard/application.h"
+
+using starboard::shared::starboard::Application;
+
+namespace {
+
+const int kSbKeyboardDeviceId = 1;
+
+SbKey VirtualKeyCodeToSbKey(WPARAM virtual_key_code) {
+ // Keyboard code reference:
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
+ switch (virtual_key_code) {
+ case VK_CANCEL: return kSbKeyCancel;
+ case VK_BACK: return kSbKeyBack;
+ case VK_TAB: return kSbKeyTab;
+ case VK_CLEAR: return kSbKeyClear;
+ case VK_RETURN: return kSbKeyReturn;
+ case VK_SHIFT: return kSbKeyShift;
+ case VK_CONTROL: return kSbKeyControl;
+ case VK_MENU: return kSbKeyMenu;
+ case VK_PAUSE: return kSbKeyPause;
+ case VK_CAPITAL: return kSbKeyCapital;
+ // Hangul and Kana have the same VirtualKey constant
+ case VK_KANA: return kSbKeyKana;
+ case VK_JUNJA: return kSbKeyJunja;
+ case VK_FINAL: return kSbKeyFinal;
+ // Hanja and Kanji have the same VirtualKey constant
+ case VK_HANJA: return kSbKeyHanja;
+ case VK_ESCAPE: return kSbKeyEscape;
+ case VK_CONVERT: return kSbKeyConvert;
+ case VK_NONCONVERT: return kSbKeyNonconvert;
+ case VK_ACCEPT: return kSbKeyAccept;
+ case VK_MODECHANGE: return kSbKeyModechange;
+ case VK_SPACE: return kSbKeySpace;
+ case VK_PRIOR: return kSbKeyPrior;
+ case VK_NEXT: return kSbKeyNext;
+ case VK_END: return kSbKeyEnd;
+ case VK_HOME: return kSbKeyHome;
+ case VK_LEFT: return kSbKeyLeft;
+ case VK_UP: return kSbKeyUp;
+ case VK_RIGHT: return kSbKeyRight;
+ case VK_DOWN: return kSbKeyDown;
+ case VK_SELECT: return kSbKeySelect;
+ case VK_PRINT: return kSbKeyPrint;
+ case VK_EXECUTE: return kSbKeyExecute;
+ case VK_SNAPSHOT: return kSbKeySnapshot;
+ case VK_INSERT: return kSbKeyInsert;
+ case VK_DELETE: return kSbKeyDelete;
+ case 0x30: return kSbKey0;
+ case 0x31: return kSbKey1;
+ case 0x32: return kSbKey2;
+ case 0x33: return kSbKey3;
+ case 0x34: return kSbKey4;
+ case 0x35: return kSbKey5;
+ case 0x36: return kSbKey6;
+ case 0x37: return kSbKey7;
+ case 0x38: return kSbKey8;
+ case 0x39: return kSbKey9;
+ case 0x41: return kSbKeyA;
+ case 0x42: return kSbKeyB;
+ case 0x43: return kSbKeyC;
+ case 0x44: return kSbKeyD;
+ case 0x45: return kSbKeyE;
+ case 0x46: return kSbKeyF;
+ case 0x47: return kSbKeyG;
+ case 0x48: return kSbKeyH;
+ case 0x49: return kSbKeyI;
+ case 0x4A: return kSbKeyJ;
+ case 0x4B: return kSbKeyK;
+ case 0x4C: return kSbKeyL;
+ case 0x4D: return kSbKeyM;
+ case 0x4E: return kSbKeyN;
+ case 0x4F: return kSbKeyO;
+ case 0x50: return kSbKeyP;
+ case 0x51: return kSbKeyQ;
+ case 0x52: return kSbKeyR;
+ case 0x53: return kSbKeyS;
+ case 0x54: return kSbKeyT;
+ case 0x55: return kSbKeyU;
+ case 0x56: return kSbKeyV;
+ case 0x57: return kSbKeyW;
+ case 0x58: return kSbKeyX;
+ case 0x59: return kSbKeyY;
+ case 0x5A: return kSbKeyZ;
+ case VK_LWIN: return kSbKeyLwin;
+ case VK_RWIN: return kSbKeyRwin;
+ case VK_APPS: return kSbKeyApps;
+ case VK_SLEEP: return kSbKeySleep;
+ case VK_NUMPAD0: return kSbKeyNumpad0;
+ case VK_NUMPAD1: return kSbKeyNumpad1;
+ case VK_NUMPAD2: return kSbKeyNumpad2;
+ case VK_NUMPAD3: return kSbKeyNumpad3;
+ case VK_NUMPAD4: return kSbKeyNumpad4;
+ case VK_NUMPAD5: return kSbKeyNumpad5;
+ case VK_NUMPAD6: return kSbKeyNumpad6;
+ case VK_NUMPAD7: return kSbKeyNumpad7;
+ case VK_NUMPAD8: return kSbKeyNumpad8;
+ case VK_NUMPAD9: return kSbKeyNumpad9;
+ case VK_MULTIPLY: return kSbKeyMultiply;
+ case VK_ADD: return kSbKeyAdd;
+ case VK_SEPARATOR: return kSbKeySeparator;
+ case VK_SUBTRACT: return kSbKeySubtract;
+ case VK_DECIMAL: return kSbKeyDecimal;
+ case VK_DIVIDE: return kSbKeyDivide;
+ case VK_F1: return kSbKeyF1;
+ case VK_F2: return kSbKeyF2;
+ case VK_F3: return kSbKeyF3;
+ case VK_F4: return kSbKeyF4;
+ case VK_F5: return kSbKeyF5;
+ case VK_F6: return kSbKeyF6;
+ case VK_F7: return kSbKeyF7;
+ case VK_F8: return kSbKeyF8;
+ case VK_F9: return kSbKeyF9;
+ case VK_F10: return kSbKeyF10;
+ case VK_F11: return kSbKeyF11;
+ case VK_F12: return kSbKeyF12;
+ case VK_F13: return kSbKeyF13;
+ case VK_F14: return kSbKeyF14;
+ case VK_F15: return kSbKeyF15;
+ case VK_F16: return kSbKeyF16;
+ case VK_F17: return kSbKeyF17;
+ case VK_F18: return kSbKeyF18;
+ case VK_F19: return kSbKeyF19;
+ case VK_F20: return kSbKeyF20;
+ case VK_F21: return kSbKeyF21;
+ case VK_F22: return kSbKeyF22;
+ case VK_F23: return kSbKeyF23;
+ case VK_F24: return kSbKeyF24;
+ case VK_NUMLOCK: return kSbKeyNumlock;
+ case VK_SCROLL: return kSbKeyScroll;
+ case VK_LSHIFT: return kSbKeyLshift;
+ case VK_RSHIFT: return kSbKeyRshift;
+ case VK_LCONTROL: return kSbKeyLcontrol;
+ case VK_RCONTROL: return kSbKeyRcontrol;
+ case VK_LMENU: return kSbKeyLmenu;
+ case VK_RMENU: return kSbKeyRmenu;
+ case VK_BROWSER_BACK: return kSbKeyBrowserBack;
+ case VK_BROWSER_FORWARD: return kSbKeyBrowserForward;
+ case VK_BROWSER_REFRESH: return kSbKeyBrowserRefresh;
+ case VK_BROWSER_STOP: return kSbKeyBrowserStop;
+ case VK_BROWSER_SEARCH: return kSbKeyBrowserSearch;
+ case VK_BROWSER_FAVORITES: return kSbKeyBrowserFavorites;
+ case VK_BROWSER_HOME: return kSbKeyBrowserHome;
+ case VK_LBUTTON: return kSbKeyMouse1;
+ case VK_RBUTTON: return kSbKeyMouse2;
+ case VK_MBUTTON: return kSbKeyMouse3;
+ case VK_XBUTTON1: return kSbKeyMouse4;
+ case VK_XBUTTON2: return kSbKeyMouse5;
+ default:
+ SB_LOG(WARNING) << "Unrecognized key hit.";
+ return kSbKeyUnknown;
+ }
+}
+
+} // namespace
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// TODO: Plug into XInput APIs for Xbox controller input?
+Application::Event* ApplicationWin32::ProcessWinKeyEvent(SbWindow window,
+ UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ SbInputData* data = new SbInputData();
+ SbMemorySet(data, 0, sizeof(*data));
+
+ data->window = window;
+ data->device_type = kSbInputDeviceTypeKeyboard;
+ // TODO: Do some more intelligent handling logic here to determine
+ // a unique device ID.
+ data->device_id = kSbKeyboardDeviceId;
+ data->key = VirtualKeyCodeToSbKey(w_param);
+
+ const bool was_down = ((l_param & (1 << 30)) != 0);
+ const bool up = msg != WM_KEYDOWN && msg == WM_SYSKEYDOWN;
+
+ data->type = up ? kSbInputEventTypeUnpress : kSbInputEventTypePress;
+
+ if (data->key == kSbKeyShift || data->key == kSbKeyRshift ||
+ data->key == kSbKeyLshift) {
+ data->key_modifiers |= kSbKeyModifiersShift;
+ } else if (data->key == kSbKeyMenu || data->key == kSbKeyRmenu ||
+ data->key == kSbKeyLmenu) {
+ data->key_modifiers |= kSbKeyModifiersAlt;
+ } else if (data->key == kSbKeyControl || data->key == kSbKeyRcontrol ||
+ data->key == kSbKeyLcontrol) {
+ data->key_modifiers |= kSbKeyModifiersCtrl;
+ } else if (data->key == kSbKeyRwin || data->key == kSbKeyLwin) {
+ data->key_modifiers |= kSbKeyModifiersMeta;
+ }
+
+ switch (data->key) {
+ case kSbKeyLshift:
+ case kSbKeyLmenu:
+ case kSbKeyLcontrol:
+ case kSbKeyLwin:
+ data->key_location = kSbKeyLocationLeft;
+ break;
+ case kSbKeyRshift:
+ case kSbKeyRmenu:
+ case kSbKeyRcontrol:
+ case kSbKeyRwin:
+ data->key_location = kSbKeyLocationRight;
+ break;
+ default:
+ break;
+ }
+
+ return new Application::Event(kSbEventTypeInput, data,
+ &Application::DeleteDestructor<SbInputData>);
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/atomic_queue.h b/src/starboard/shared/win32/atomic_queue.h
new file mode 100644
index 0000000..676813a
--- /dev/null
+++ b/src/starboard/shared/win32/atomic_queue.h
@@ -0,0 +1,75 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_ATOMIC_QUEUE_H_
+#define STARBOARD_SHARED_WIN32_ATOMIC_QUEUE_H_
+
+#include <deque>
+
+#include "starboard/mutex.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// A simple thread-safe producer / consumer queue. Elements are added via
+// PopBack() and removed via PopFront().
+template <typename Data>
+class AtomicQueue {
+ public:
+ size_t PushBack(Data data_ptr) {
+ ScopedLock lock(mutex_);
+ data_queue_.push_back(data_ptr);
+ return data_queue_.size();
+ }
+
+ Data PopFront() {
+ ScopedLock lock(mutex_);
+ if (data_queue_.empty()) {
+ Data empty = Data();
+ return empty;
+ }
+ Data data_ptr = data_queue_.front();
+ data_queue_.pop_front();
+ return data_ptr;
+ }
+
+ bool IsEmpty() const {
+ ScopedLock lock(mutex_);
+ return data_queue_.empty();
+ }
+
+ size_t Size() const {
+ ScopedLock lock(mutex_);
+ return data_queue_.size();
+ }
+
+ void Clear() {
+ ScopedLock lock(mutex_);
+ std::deque<Data> empty;
+ data_queue_.swap(empty);
+ }
+
+ private:
+ using Mutex = ::starboard::Mutex;
+ using ScopedLock = ::starboard::ScopedLock;
+ std::deque<Data> data_queue_;
+ ::starboard::Mutex mutex_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_ATOMIC_QUEUE_H_
diff --git a/src/starboard/shared/win32/audio_decoder.cc b/src/starboard/shared/win32/audio_decoder.cc
new file mode 100644
index 0000000..d6749f2
--- /dev/null
+++ b/src/starboard/shared/win32/audio_decoder.cc
@@ -0,0 +1,140 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/audio_decoder.h"
+
+#include "starboard/atomic.h"
+#include "starboard/audio_sink.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/win32/media_common.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class AudioDecoder::CallbackScheduler : private JobOwner {
+ public:
+ CallbackScheduler() : callback_signaled_(false) {}
+
+ void SetCallbackOnce(Closure cb) {
+ SB_DCHECK(cb.is_valid());
+ ::starboard::ScopedLock lock(mutex_);
+ if (!cb_.is_valid()) {
+ cb_ = cb;
+ }
+ }
+
+ void ScheduleCallbackIfNecessary() {
+ ::starboard::ScopedLock lock(mutex_);
+ if (!cb_.is_valid() || callback_signaled_) {
+ return;
+ }
+ callback_signaled_ = true;
+ JobOwner::Schedule(cb_);
+ }
+
+ void OnCallbackSignaled() {
+ ::starboard::ScopedLock lock(mutex_);
+ callback_signaled_ = false;
+ }
+
+ Closure cb_;
+ ::starboard::Mutex mutex_;
+ bool callback_signaled_;
+};
+
+AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header)
+ : sample_type_(kSbMediaAudioSampleTypeFloat32),
+ stream_ended_(false),
+ audio_codec_(audio_codec),
+ audio_header_(audio_header) {
+ SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
+ decoder_impl_ = AbstractWin32AudioDecoder::Create(
+ audio_codec_, GetStorageType(), GetSampleType(), audio_header_);
+ decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
+ callback_scheduler_.reset(new CallbackScheduler());
+}
+
+AudioDecoder::~AudioDecoder() {
+ decoder_thread_.reset(nullptr);
+ decoder_impl_.reset(nullptr);
+ callback_scheduler_.reset(nullptr);
+}
+
+void AudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer,
+ const Closure& consumed_cb) {
+ SB_DCHECK(input_buffer);
+ callback_scheduler_->SetCallbackOnce(consumed_cb);
+ callback_scheduler_->OnCallbackSignaled();
+ const bool can_take_more_data = decoder_thread_->QueueInput(input_buffer);
+ if (can_take_more_data) {
+ callback_scheduler_->ScheduleCallbackIfNecessary();
+ }
+
+ if (stream_ended_) {
+ SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
+ return;
+ }
+}
+
+void AudioDecoder::WriteEndOfStream() {
+ ::starboard::ScopedLock lock(mutex_);
+ stream_ended_ = true;
+ decoder_thread_->QueueEndOfStream();
+}
+
+scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+ DecodedAudioPtr data = decoded_data_.PopFront();
+ SB_DCHECK(data);
+ return data;
+}
+
+void AudioDecoder::Reset() {
+ decoder_thread_.reset(nullptr);
+ decoder_impl_.reset(nullptr);
+ decoder_impl_ = AbstractWin32AudioDecoder::Create(
+ audio_codec_, GetStorageType(), GetSampleType(), audio_header_);
+ decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
+ decoded_data_.Clear();
+ stream_ended_ = false;
+ CancelPendingJobs();
+}
+
+SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
+ return sample_type_;
+}
+
+int AudioDecoder::GetSamplesPerSecond() const {
+ return audio_header_.samples_per_second;
+}
+
+void AudioDecoder::Initialize(const Closure& output_cb) {
+ SB_DCHECK(output_cb.is_valid());
+ SB_DCHECK(!output_cb_.is_valid());
+ output_cb_ = output_cb;
+}
+
+void AudioDecoder::OnAudioDecoded(DecodedAudioPtr data) {
+ decoded_data_.PushBack(data);
+ if (output_cb_.is_valid()) {
+ Schedule(output_cb_);
+ }
+ callback_scheduler_->ScheduleCallbackIfNecessary();
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/audio_decoder.h b/src/starboard/shared/win32/audio_decoder.h
new file mode 100644
index 0000000..f036287
--- /dev/null
+++ b/src/starboard/shared/win32/audio_decoder.h
@@ -0,0 +1,81 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_AUDIO_DECODER_H_
+#define STARBOARD_SHARED_WIN32_AUDIO_DECODER_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/shared/win32/atomic_queue.h"
+#include "starboard/shared/win32/audio_decoder_thread.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/shared/win32/win32_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+using JobQueue = ::starboard::shared::starboard::player::JobQueue;
+using JobOwner = JobQueue::JobOwner;
+
+class AudioDecoder
+ : public ::starboard::shared::starboard::player::filter::AudioDecoder,
+ private JobOwner,
+ private AudioDecodedCallback {
+ public:
+ AudioDecoder(SbMediaAudioCodec audio_codec,
+ const SbMediaAudioHeader& audio_header);
+ ~AudioDecoder() SB_OVERRIDE;
+
+ void Decode(const scoped_refptr<InputBuffer>& input_buffer,
+ const Closure& consumed_cb) SB_OVERRIDE;
+ void WriteEndOfStream() SB_OVERRIDE;
+ scoped_refptr<DecodedAudio> Read() SB_OVERRIDE;
+ void Reset() SB_OVERRIDE;
+ SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE;
+ int GetSamplesPerSecond() const SB_OVERRIDE;
+
+ void Initialize(const Closure& output_cb) SB_OVERRIDE;
+ SbMediaAudioFrameStorageType GetStorageType() const SB_OVERRIDE {
+ return kSbMediaAudioFrameStorageTypeInterleaved;
+ }
+ void OnAudioDecoded(DecodedAudioPtr data) SB_OVERRIDE;
+
+ private:
+ class CallbackScheduler;
+ SbMediaAudioHeader audio_header_;
+ SbMediaAudioSampleType sample_type_;
+ SbMediaAudioCodec audio_codec_;
+ bool stream_ended_;
+
+ AtomicQueue<DecodedAudioPtr> decoded_data_;
+ scoped_ptr<AudioDecoder::CallbackScheduler> callback_scheduler_;
+ scoped_ptr<AbstractWin32AudioDecoder> decoder_impl_;
+ scoped_ptr<AudioDecoderThread> decoder_thread_;
+ Closure output_cb_;
+
+ ::starboard::Mutex mutex_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_AUDIO_DECODER_H_
diff --git a/src/starboard/shared/win32/audio_decoder_thread.cc b/src/starboard/shared/win32/audio_decoder_thread.cc
new file mode 100644
index 0000000..91d28fe
--- /dev/null
+++ b/src/starboard/shared/win32/audio_decoder_thread.cc
@@ -0,0 +1,127 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/audio_decoder_thread.h"
+
+#include <deque>
+#include <vector>
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+namespace {
+
+// Size of the queue for audio units.
+const size_t kMaxProcessingElements = 64;
+
+size_t WriteAsMuchAsPossible(
+ std::deque<scoped_refptr<InputBuffer> >* data_queue,
+ AbstractWin32AudioDecoder* audio_decoder) {
+ const size_t original_size = data_queue->size();
+ while (!data_queue->empty()) {
+ scoped_refptr<InputBuffer> buff = data_queue->front();
+ data_queue->pop_front();
+
+ if (buff) {
+ const bool write_ok = audio_decoder->TryWrite(*buff);
+
+ if (!write_ok) {
+ data_queue->push_front(buff);
+ break;
+ }
+ } else {
+ audio_decoder->WriteEndOfStream();
+ }
+ }
+ return original_size - data_queue->size();
+}
+
+std::vector<DecodedAudioPtr> ReadAllDecodedAudioSamples(
+ AbstractWin32AudioDecoder* audio_decoder) {
+ std::vector<DecodedAudioPtr> decoded_audio_out;
+ while (DecodedAudioPtr decoded_datum = audio_decoder->ProcessAndRead()) {
+ decoded_audio_out.push_back(decoded_datum);
+ }
+ return decoded_audio_out;
+}
+
+} // namespace.
+
+AudioDecoderThread::AudioDecoderThread(AbstractWin32AudioDecoder* decoder_impl,
+ AudioDecodedCallback* callback)
+ : SimpleThread("AudioDecoderThread"),
+ win32_audio_decoder_(decoder_impl),
+ callback_(callback) {
+ Start();
+}
+
+AudioDecoderThread::~AudioDecoderThread() {
+ Join();
+}
+
+bool AudioDecoderThread::QueueInput(const scoped_refptr<InputBuffer>& buffer) {
+ {
+ ::starboard::ScopedLock lock(input_buffer_queue_mutex_);
+ input_buffer_queue_.push_back(buffer);
+ }
+
+ // increment() returns the previous value.
+ size_t element_count = processing_elements_.increment() + 1;
+ semaphore_.Put();
+ return element_count < kMaxProcessingElements;
+}
+
+void AudioDecoderThread::QueueEndOfStream() {
+ scoped_refptr<InputBuffer> empty;
+ QueueInput(empty);
+}
+
+void AudioDecoderThread::Run() {
+ std::deque<scoped_refptr<InputBuffer> > local_queue;
+
+ while (!join_called()) {
+ TransferPendingInputTo(&local_queue);
+ bool work_done = !local_queue.empty();
+ size_t number_written =
+ WriteAsMuchAsPossible(&local_queue, win32_audio_decoder_);
+ processing_elements_.fetch_sub(static_cast<int32_t>(number_written));
+
+ std::vector<DecodedAudioPtr> decoded_audio =
+ ReadAllDecodedAudioSamples(win32_audio_decoder_);
+
+ if (!decoded_audio.empty()) {
+ work_done = true;
+ for (auto it = decoded_audio.begin(); it != decoded_audio.end(); ++it) {
+ callback_->OnAudioDecoded(*it);
+ }
+ }
+
+ if (!work_done) {
+ semaphore_.TakeWait(kSbTimeMillisecond);
+ }
+ }
+}
+
+void AudioDecoderThread::TransferPendingInputTo(
+ std::deque<scoped_refptr<InputBuffer> >* destination) {
+ ::starboard::ScopedLock lock(input_buffer_queue_mutex_);
+ while (!input_buffer_queue_.empty()) {
+ destination->push_back(input_buffer_queue_.front());
+ input_buffer_queue_.pop_front();
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/audio_decoder_thread.h b/src/starboard/shared/win32/audio_decoder_thread.h
new file mode 100644
index 0000000..f595ec4
--- /dev/null
+++ b/src/starboard/shared/win32/audio_decoder_thread.h
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_AUDIO_DECODER_THREAD_H_
+#define STARBOARD_SHARED_WIN32_AUDIO_DECODER_THREAD_H_
+
+#include <deque>
+#include <queue>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/common/semaphore.h"
+#include "starboard/media.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/shared/win32/simple_thread.h"
+#include "starboard/shared/win32/win32_audio_decoder.h"
+#include "starboard/shared/win32/win32_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class AudioDecodedCallback {
+ public:
+ virtual ~AudioDecodedCallback() {}
+ virtual void OnAudioDecoded(DecodedAudioPtr data) = 0;
+};
+
+// This decoder thread simplifies decoding media. Data is pushed in via
+// QueueInput() and QueueEndOfStream() and output data is pushed via
+// the AudioDecodedCallback.
+class AudioDecoderThread : private SimpleThread {
+ public:
+ AudioDecoderThread(AbstractWin32AudioDecoder* decoder_impl,
+ AudioDecodedCallback* callback);
+ ~AudioDecoderThread() SB_OVERRIDE;
+
+ // Returns true if more input can be pushed to this thread.
+ bool QueueInput(const scoped_refptr<InputBuffer>& buffer);
+ void QueueEndOfStream();
+
+ private:
+ void Run() SB_OVERRIDE;
+ void TransferPendingInputTo(
+ std::deque<scoped_refptr<InputBuffer> >* destination);
+ AbstractWin32AudioDecoder* win32_audio_decoder_;
+ AudioDecodedCallback* callback_;
+
+ std::deque<scoped_refptr<InputBuffer> > input_buffer_queue_;
+ ::starboard::Mutex input_buffer_queue_mutex_;
+ atomic_int32_t processing_elements_;
+ Semaphore semaphore_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_AUDIO_DECODER_THREAD_H_
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc
index cc7b530..c5d8de2 100644
--- a/src/starboard/shared/win32/audio_sink.cc
+++ b/src/starboard/shared/win32/audio_sink.cc
@@ -19,6 +19,8 @@
#include <xaudio2.h>
#include <limits>
+#include <sstream>
+#include <string>
#include "starboard/configuration.h"
#include "starboard/log.h"
@@ -35,7 +37,14 @@
}
const int kMaxBuffersSubmittedPerLoop = 2;
+
+std::string GenerateThreadName() {
+ static int s_count = 0;
+ std::stringstream ss;
+ ss << "AudioOut_" << s_count++;
+ return ss.str();
}
+} // namespace.
namespace starboard {
namespace shared {
@@ -110,16 +119,18 @@
wfx_(wfx),
destroying_(false),
playback_rate_(1.0) {
- // TODO: Check MaxFrequencyRadio
+ // TODO: Check MaxFrequencyRatio
CHECK_HRESULT_OK(
type_->x_audio2_->CreateSourceVoice(&source_voice_, &wfx, 0,
- /*MaxFrequencyRadio = */ 1.0));
+ /*MaxFrequencyRatio = */ 1.0));
- CHECK_HRESULT_OK(source_voice_->Start(0));
+ CHECK_HRESULT_OK(source_voice_->Stop(0));
- audio_out_thread_ =
- SbThreadCreate(0, kSbThreadPriorityRealTime, kSbThreadNoAffinity, true,
- "audio_out", &XAudioAudioSink::ThreadEntryPoint, this);
+ std::string thread_name = GenerateThreadName();
+
+ audio_out_thread_ = SbThreadCreate(
+ 0, kSbThreadPriorityRealTime, kSbThreadNoAffinity, true,
+ thread_name.c_str(), &XAudioAudioSink::ThreadEntryPoint, this);
SB_DCHECK(SbThreadIsValid(audio_out_thread_));
}
@@ -165,6 +176,7 @@
int submitted_frames = 0;
uint64_t samples_played = 0;
int queued_buffers = 0;
+ bool was_playing = false; // The player starts out playing by default.
for (;;) {
{
ScopedLock lock(mutex_);
@@ -181,6 +193,18 @@
}
update_source_status_func_(&frames_in_buffer, &offset_in_frames,
&is_playing, &is_eos_reached, context_);
+ if (is_playback_rate_zero) {
+ is_playing = false;
+ }
+
+ if (is_playing != was_playing) {
+ if (is_playing) {
+ CHECK_HRESULT_OK(source_voice_->Start(0));
+ } else {
+ CHECK_HRESULT_OK(source_voice_->Stop(0));
+ }
+ }
+ was_playing = is_playing;
// TODO: make sure that frames_in_buffer is large enough
// that it exceeds the voice state pool interval
diff --git a/src/starboard/shared/win32/decode_target_internal.cc b/src/starboard/shared/win32/decode_target_internal.cc
new file mode 100644
index 0000000..70b8e32
--- /dev/null
+++ b/src/starboard/shared/win32/decode_target_internal.cc
@@ -0,0 +1,204 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/decode_target_internal.h"
+
+#include <D3D11.h>
+#include <Mfidl.h>
+#include <wrl\client.h> // For ComPtr.
+
+#include "starboard/configuration.h"
+#include "starboard/decode_target.h"
+#include "starboard/memory.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/media_common.h"
+#include "third_party/angle/include/EGL/egl.h"
+#include "third_party/angle/include/EGL/eglext.h"
+#include "third_party/angle/include/GLES2/gl2.h"
+
+using Microsoft::WRL::ComPtr;
+using starboard::shared::win32::VideoFramePtr;
+using starboard::shared::win32::CheckResult;
+
+// {3C3A43AB-C69B-46C9-AA8D-B0CFFCD4596D}
+static const GUID kCobaltNv12BindChroma = {
+ 0x3c3a43ab,
+ 0xc69b,
+ 0x46c9,
+ {0xaa, 0x8d, 0xb0, 0xcf, 0xfc, 0xd4, 0x59, 0x6d}};
+
+// {C62BF18D-B5EE-46B1-9C31-F61BD8AE3B0D}
+static const GUID kCobaltDxgiBuffer = {
+ 0Xc62bf18d,
+ 0Xb5ee,
+ 0X46b1,
+ {0X9c, 0X31, 0Xf6, 0X1b, 0Xd8, 0Xae, 0X3b, 0X0d}};
+
+SbDecodeTargetPrivate::SbDecodeTargetPrivate(VideoFramePtr f) : frame(f) {
+ SbMemorySet(&info, 0, sizeof(info));
+ ComPtr<IMFMediaBuffer> media_buffer =
+ static_cast<IMFMediaBuffer*>(frame->native_texture());
+
+ ComPtr<IMFDXGIBuffer> dxgi_buffer;
+ HRESULT hr = media_buffer.As(&dxgi_buffer);
+ CheckResult(hr);
+ SB_DCHECK(dxgi_buffer.Get());
+
+ ComPtr<ID3D11Texture2D> d3texture;
+ hr = dxgi_buffer->GetResource(IID_PPV_ARGS(&d3texture));
+ CheckResult(hr);
+
+ UINT array_index;
+ dxgi_buffer->GetSubresourceIndex(&array_index);
+
+ info.format = kSbDecodeTargetFormat2PlaneYUVNV12;
+ info.is_opaque = true;
+
+ D3D11_TEXTURE2D_DESC texture_desc;
+ d3texture->GetDesc(&texture_desc);
+ info.width = texture_desc.Width;
+ info.height = texture_desc.Height;
+
+ SbDecodeTargetInfoPlane* planeY = &(info.planes[kSbDecodeTargetPlaneY]);
+ SbDecodeTargetInfoPlane* planeUV = &(info.planes[kSbDecodeTargetPlaneUV]);
+
+ planeY->width = texture_desc.Width;
+ planeY->height = texture_desc.Height;
+ planeY->content_region.left = 0;
+ planeY->content_region.top = 0;
+ planeY->content_region.right = texture_desc.Width;
+ planeY->content_region.bottom = texture_desc.Height;
+
+ planeUV->width = texture_desc.Width / 2;
+ planeUV->height = texture_desc.Height / 2;
+ planeUV->content_region.left = 0;
+ planeUV->content_region.top = 0;
+ planeUV->content_region.right = texture_desc.Width / 2;
+ planeUV->content_region.bottom = texture_desc.Height / 2;
+
+ EGLint luma_texture_attributes[] = {EGL_WIDTH,
+ static_cast<EGLint>(texture_desc.Width),
+ EGL_HEIGHT,
+ static_cast<EGLint>(texture_desc.Height),
+ EGL_TEXTURE_TARGET,
+ EGL_TEXTURE_2D,
+ EGL_TEXTURE_FORMAT,
+ EGL_TEXTURE_RGBA,
+ EGL_NONE};
+
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLConfig config;
+ EGLint num_configs;
+ bool ok = eglGetConfigs(display, &config, 1, &num_configs);
+ SB_DCHECK(ok);
+
+ GLuint gl_textures[2] = {0};
+ glGenTextures(2, gl_textures);
+ SB_DCHECK(glGetError() == GL_NO_ERROR);
+
+ // This tells ANGLE that the texture it creates should draw
+ // the luma channel on R8.
+ hr = d3texture->SetPrivateData(kCobaltNv12BindChroma, 0, nullptr);
+ SB_DCHECK(SUCCEEDED(hr));
+
+ // This lets ANGLE find out the subresource index / texture array index
+ // to use.
+ // Note: No AddRef here, since we clear this private data below.
+ hr = d3texture->SetPrivateData(kCobaltDxgiBuffer, sizeof(IMFDXGIBuffer*),
+ dxgi_buffer.GetAddressOf());
+ SB_DCHECK(SUCCEEDED(hr));
+
+ EGLSurface surface = eglCreatePbufferFromClientBuffer(
+ display, EGL_D3D_TEXTURE_ANGLE, d3texture.Get(), config,
+ luma_texture_attributes);
+
+ SB_DCHECK(surface != EGL_NO_SURFACE);
+
+ glBindTexture(GL_TEXTURE_2D, gl_textures[0]);
+ SB_DCHECK(glGetError() == GL_NO_ERROR);
+
+ ok = eglBindTexImage(display, surface, EGL_BACK_BUFFER);
+ SB_DCHECK(ok);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ planeY->texture = gl_textures[0];
+ planeY->gl_texture_target = GL_TEXTURE_2D;
+
+ // This tells ANGLE that the texture it creates should draw
+ // the chroma channel on R8G8.
+ bool bind_chroma = true;
+ hr = d3texture->SetPrivateData(kCobaltNv12BindChroma, 1, &bind_chroma);
+ SB_DCHECK(SUCCEEDED(hr));
+
+ EGLint chroma_texture_attributes[] = {
+ EGL_WIDTH,
+ static_cast<EGLint>(texture_desc.Width) / 2,
+ EGL_HEIGHT,
+ static_cast<EGLint>(texture_desc.Height) / 2,
+ EGL_TEXTURE_TARGET,
+ EGL_TEXTURE_2D,
+ EGL_TEXTURE_FORMAT,
+ EGL_TEXTURE_RGBA,
+ EGL_NONE};
+ surface = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
+ d3texture.Get(), config,
+ chroma_texture_attributes);
+
+ SB_DCHECK(surface != EGL_NO_SURFACE);
+
+ glBindTexture(GL_TEXTURE_2D, gl_textures[1]);
+ SB_DCHECK(glGetError() == GL_NO_ERROR);
+
+ ok = eglBindTexImage(display, surface, EGL_BACK_BUFFER);
+ SB_DCHECK(ok);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ planeUV->texture = gl_textures[1];
+ planeUV->gl_texture_target = GL_TEXTURE_2D;
+
+ hr = d3texture->SetPrivateData(kCobaltDxgiBuffer, 0, nullptr);
+ SB_DCHECK(SUCCEEDED(hr));
+}
+
+SbDecodeTargetPrivate::~SbDecodeTargetPrivate() {
+ glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneY].texture));
+ glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneUV].texture));
+}
+
+void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
+ if (SbDecodeTargetIsValid(decode_target)) {
+ delete decode_target;
+ }
+}
+
+SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget decode_target) {
+ // Note that kSbDecodeTargetFormat2PlaneYUVNV12 represents DXGI_FORMAT_NV12.
+ SB_DCHECK(kSbDecodeTargetFormat2PlaneYUVNV12 ==
+ decode_target->info.format);
+ return decode_target->info.format;
+}
+
+bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
+ SbDecodeTargetInfo* out_info) {
+ if (!out_info || !SbMemoryIsZero(out_info, sizeof(*out_info))) {
+ SB_DCHECK(false) << "out_info must be zeroed out.";
+ return false;
+ }
+ SbMemoryCopy(out_info, &decode_target->info, sizeof(*out_info));
+ return true;
+}
diff --git a/src/starboard/shared/win32/decode_target_internal.h b/src/starboard/shared/win32/decode_target_internal.h
new file mode 100644
index 0000000..6204200
--- /dev/null
+++ b/src/starboard/shared/win32/decode_target_internal.h
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_DECODE_TARGET_INTERNAL_H_
+#define STARBOARD_SHARED_WIN32_DECODE_TARGET_INTERNAL_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/decode_target.h"
+#include "starboard/shared/win32/media_common.h"
+
+struct SbDecodeTargetPrivate {
+ // Publicly accessible information about the decode target.
+ SbDecodeTargetInfo info;
+ ::starboard::shared::win32::VideoFramePtr frame;
+ explicit SbDecodeTargetPrivate(starboard::shared::win32::VideoFramePtr frame);
+ ~SbDecodeTargetPrivate();
+};
+
+#endif // STARBOARD_SHARED_WIN32_DECODE_TARGET_INTERNAL_H_
diff --git a/src/starboard/shared/win32/dialog.cc b/src/starboard/shared/win32/dialog.cc
new file mode 100644
index 0000000..7ac5ae0
--- /dev/null
+++ b/src/starboard/shared/win32/dialog.cc
@@ -0,0 +1,252 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/dialog.h"
+
+#include <windef.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#include <functional>
+#include <vector>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/wchar_utils.h"
+
+typedef std::function<void()> DialogCallback;
+
+using starboard::shared::win32::DebugLogWinError;
+using starboard::shared::win32::CStringToWString;
+
+namespace {
+HWND g_current_dialog_handle = nullptr;
+DialogCallback g_ok_callback;
+DialogCallback g_cancel_callback;
+} // namespace
+
+// Wraps the win32 Dialog interface for building Dialogs at runtime.
+// https://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
+class DialogTemplateBuilder {
+ public:
+ LPCDLGTEMPLATE BuildTemplate() { return (LPCDLGTEMPLATE)&v[0]; }
+ void AlignToDword() {
+ if (v.size() % 4)
+ Write(NULL, 4 - (v.size() % 4));
+ }
+ void Write(LPCVOID pvWrite, DWORD cbWrite) {
+ v.insert(v.end(), cbWrite, 0);
+ if (pvWrite)
+ CopyMemory(&v[v.size() - cbWrite], pvWrite, cbWrite);
+ }
+ template <typename T>
+ void Write(T t) {
+ Write(&t, sizeof(T));
+ }
+ void WriteString(LPCWSTR psz) {
+ Write(psz, (lstrlenW(psz) + 1) * sizeof(WCHAR));
+ }
+
+ private:
+ std::vector<BYTE> v;
+};
+
+INT_PTR CALLBACK DialogProcedureCallback(HWND dialog_handle,
+ UINT message,
+ WPARAM w_param,
+ LPARAM /*l_param*/) {
+ SB_CHECK(!g_current_dialog_handle || dialog_handle == g_current_dialog_handle)
+ << "Received callback on non-active dialog! Only one dialog at a time is "
+ "supported.";
+ switch (message) {
+ case WM_INITDIALOG:
+ return TRUE;
+ case WM_COMMAND:
+ auto command_id = GET_WM_COMMAND_ID(w_param, l_param);
+ if (command_id == IDCANCEL) {
+ g_cancel_callback();
+ } else if (command_id == IDOK) {
+ g_ok_callback();
+ } else {
+ return FALSE;
+ }
+ EndDialog(dialog_handle, 0);
+ g_current_dialog_handle = nullptr;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+bool ShowOkCancelDialog(HWND hwnd,
+ const std::string& title,
+ const std::string& message,
+ const std::string& ok_message,
+ DialogCallback ok_callback,
+ const std::string& cancel_message,
+ DialogCallback cancel_callback) {
+ if (g_current_dialog_handle != nullptr) {
+ SB_LOG(WARNING) << "Already showing a dialog; cancelling existing and "
+ "replacing with new dialog";
+ CancelDialog();
+ }
+ g_ok_callback = ok_callback;
+ g_cancel_callback = cancel_callback;
+ // Get the device context (DC) and from the system so we can scale our fonts
+ // correctly.
+ HDC hdc = GetDC(NULL);
+ SB_CHECK(hdc);
+ NONCLIENTMETRICSW ncm = {sizeof(ncm)};
+ bool retrieved_system_params =
+ SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+
+ if (!retrieved_system_params) {
+ DebugLogWinError();
+ ReleaseDC(NULL, hdc);
+ return false;
+ }
+ DialogTemplateBuilder dialog_template;
+ const int help_id = 0;
+ const int extended_style = 0;
+
+ const int window_width = 200;
+ const int window_height = 80;
+ const int window_x = 32;
+ const int window_y = 32;
+
+ const int edge_padding = 7;
+
+ const int button_height = 14;
+ const int button_width = 50;
+ const int button_y = window_height - button_height - edge_padding;
+ const int left_button_x = window_width / 2 - button_width - edge_padding / 2;
+ const int right_button_x = window_width / 2 + edge_padding / 2;
+ const int text_width = window_width - edge_padding * 2;
+ const int text_height = window_width - edge_padding * 3 - button_height;
+ const int extra_data = 0;
+
+ // Create a dialog template.
+ // The following MSDN blogposts explains how this is all laid out:
+ // https://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753
+ // https://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
+ // More official documentation:
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644996(v=vs.85).aspx#modeless_box
+ dialog_template.Write<WORD>(1); // dialog version
+ dialog_template.Write<WORD>(0xFFFF); // extended dialog template
+ dialog_template.Write<DWORD>(help_id);
+ dialog_template.Write<DWORD>(extended_style);
+ dialog_template.Write<DWORD>(WS_CAPTION | WS_SYSMENU | DS_SETFONT |
+ DS_MODALFRAME);
+ dialog_template.Write<WORD>(3); // number of controls
+ dialog_template.Write<WORD>(window_x);
+ dialog_template.Write<WORD>(window_y);
+ dialog_template.Write<WORD>(window_width);
+ dialog_template.Write<WORD>(window_height);
+ dialog_template.WriteString(L""); // no menu
+ dialog_template.WriteString(L""); // default dialog class
+ // Title.
+ dialog_template.WriteString(
+ (LPCWSTR)starboard::shared::win32::CStringToWString(title.c_str())
+ .c_str());
+
+ // See following for info on how the font styling is calculated:
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ff684173(v=vs.85).aspx
+ if (ncm.lfMessageFont.lfHeight < 0) {
+ ncm.lfMessageFont.lfHeight =
+ -MulDiv(ncm.lfMessageFont.lfHeight, 72, GetDeviceCaps(hdc, LOGPIXELSY));
+ }
+ dialog_template.Write<WORD>((WORD)ncm.lfMessageFont.lfHeight);
+ dialog_template.Write<WORD>((WORD)ncm.lfMessageFont.lfWeight);
+ dialog_template.Write<BYTE>(ncm.lfMessageFont.lfItalic);
+ dialog_template.Write<BYTE>(ncm.lfMessageFont.lfCharSet);
+ dialog_template.WriteString(ncm.lfMessageFont.lfFaceName);
+
+ // Message text.
+ dialog_template.AlignToDword();
+ dialog_template.Write<DWORD>(help_id);
+ dialog_template.Write<DWORD>(extended_style);
+ dialog_template.Write<DWORD>(WS_CHILD | WS_VISIBLE);
+ dialog_template.Write<WORD>(edge_padding);
+ dialog_template.Write<WORD>(edge_padding);
+ dialog_template.Write<WORD>(text_width);
+ dialog_template.Write<WORD>(text_height);
+ dialog_template.Write<DWORD>((DWORD)-1);
+ dialog_template.Write<DWORD>(0x0082FFFF);
+ dialog_template.WriteString(
+ (LPCWSTR)starboard::shared::win32::CStringToWString(message.c_str())
+ .c_str());
+ dialog_template.Write<WORD>(extra_data);
+
+ // Cancel button.
+ dialog_template.AlignToDword();
+ dialog_template.Write<DWORD>(help_id);
+ dialog_template.Write<DWORD>(extended_style);
+ dialog_template.Write<DWORD>(WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP |
+ BS_DEFPUSHBUTTON);
+ dialog_template.Write<WORD>(left_button_x);
+ dialog_template.Write<WORD>(button_y);
+ dialog_template.Write<WORD>(button_width);
+ dialog_template.Write<WORD>(button_height);
+ dialog_template.Write<DWORD>(IDCANCEL);
+ dialog_template.Write<DWORD>(0x0080FFFF);
+ dialog_template.WriteString(
+ (LPCWSTR)starboard::shared::win32::CStringToWString(
+ cancel_message.c_str())
+ .c_str());
+ dialog_template.Write<WORD>(extra_data);
+
+ // Ok button.
+ dialog_template.AlignToDword();
+ dialog_template.Write<DWORD>(help_id);
+ dialog_template.Write<DWORD>(extended_style);
+ dialog_template.Write<DWORD>(WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP |
+ BS_DEFPUSHBUTTON); // style
+ dialog_template.Write<WORD>(right_button_x);
+ dialog_template.Write<WORD>(button_y);
+ dialog_template.Write<WORD>(button_width);
+ dialog_template.Write<WORD>(button_height);
+ dialog_template.Write<DWORD>(IDOK);
+ dialog_template.Write<DWORD>(0x0080FFFF);
+ dialog_template.WriteString(
+ (LPCWSTR)starboard::shared::win32::CStringToWString(ok_message.c_str())
+ .c_str());
+ dialog_template.Write<WORD>(extra_data);
+
+ ReleaseDC(NULL, hdc);
+ // Template is ready - go display it.
+ g_current_dialog_handle = CreateDialogIndirect(
+ GetModuleHandle(nullptr), dialog_template.BuildTemplate(), hwnd,
+ DialogProcedureCallback);
+ ShowWindow(g_current_dialog_handle, SW_SHOW);
+ return g_current_dialog_handle != nullptr;
+}
+
+bool DialogHandleMessage(MSG* msg) {
+ return IsWindow(g_current_dialog_handle) &&
+ IsDialogMessage(g_current_dialog_handle, msg);
+}
+
+void CancelDialog() {
+ if (g_current_dialog_handle != nullptr) {
+ EndDialog(g_current_dialog_handle, 0);
+ g_current_dialog_handle = nullptr;
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/dialog.h b/src/starboard/shared/win32/dialog.h
new file mode 100644
index 0000000..74adf29
--- /dev/null
+++ b/src/starboard/shared/win32/dialog.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_DIALOG_H_
+#define STARBOARD_SHARED_WIN32_DIALOG_H_
+
+#include <windows.h>
+
+#include <functional>
+#include <string>
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+typedef std::function<void()> DialogCallback;
+
+// Shows a modeless OK/Cancel-style dialog. Only one dialog may be shown at a
+// time.
+bool ShowOkCancelDialog(HWND hwnd,
+ const std::string& title,
+ const std::string& message,
+ const std::string& ok_message,
+ DialogCallback ok_callback,
+ const std::string& cancel_message,
+ DialogCallback cancel_callback);
+
+// Cancels the current dialog that is showing, if there is one.
+void CancelDialog();
+
+bool DialogHandleMessage(MSG* msg);
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_DIALOG_H_
diff --git a/src/starboard/shared/win32/dx_context_video_decoder.cc b/src/starboard/shared/win32/dx_context_video_decoder.cc
new file mode 100644
index 0000000..677a133
--- /dev/null
+++ b/src/starboard/shared/win32/dx_context_video_decoder.cc
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/dx_context_video_decoder.h"
+
+#include <D3D11.h>
+#include <D3D11_4.h>
+#include <mfapi.h>
+
+#include "third_party/angle/include/EGL/egl.h"
+#include "third_party/angle/include/EGL/eglext.h"
+
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+HardwareDecoderContext GetDirectXForHardwareDecoding() {
+ HRESULT result = S_OK;
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ PFNEGLQUERYDISPLAYATTRIBEXTPROC query_display;
+
+ query_display = reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
+ eglGetProcAddress("eglQueryDisplayAttribEXT"));
+ SB_DCHECK(query_display != nullptr);
+
+ PFNEGLQUERYDEVICEATTRIBEXTPROC query_device;
+ query_device = reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
+ eglGetProcAddress("eglQueryDeviceAttribEXT"));
+ SB_DCHECK(query_display != nullptr);
+
+ intptr_t egl_device = 0;
+ query_display(display, EGL_DEVICE_EXT, &egl_device);
+ SB_DCHECK(egl_device != 0);
+
+ intptr_t device;
+ query_device(reinterpret_cast<EGLDeviceEXT>(egl_device),
+ EGL_D3D11_DEVICE_ANGLE, &device);
+
+ ID3D11Device* output_dx_device = reinterpret_cast<ID3D11Device*>(device);
+ IMFDXGIDeviceManager* dxgi_device_mgr = nullptr;
+
+ UINT token = 0;
+ result = MFCreateDXGIDeviceManager(&token, &dxgi_device_mgr);
+ SB_DCHECK(result == S_OK);
+ SB_DCHECK(dxgi_device_mgr);
+
+ result = dxgi_device_mgr->ResetDevice(output_dx_device, token);
+ SB_DCHECK(SUCCEEDED(result));
+
+ HardwareDecoderContext output = {
+ output_dx_device,
+ dxgi_device_mgr
+ };
+ return output;
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/dx_context_video_decoder.h b/src/starboard/shared/win32/dx_context_video_decoder.h
new file mode 100644
index 0000000..fddf4ea
--- /dev/null
+++ b/src/starboard/shared/win32/dx_context_video_decoder.h
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_DX_CONTEXT_VIDEO_DECODER_H_
+#define STARBOARD_SHARED_WIN32_DX_CONTEXT_VIDEO_DECODER_H_
+
+#include <wrl\client.h> // For ComPtr.
+
+struct ID3D11Device;
+struct ID3D11DeviceContext;
+struct IMFDXGIDeviceManager;
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+struct HardwareDecoderContext {
+ Microsoft::WRL::ComPtr<ID3D11Device> dx_device_out;
+ Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_manager_out;
+};
+
+HardwareDecoderContext GetDirectXForHardwareDecoding();
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+#endif // STARBOARD_SHARED_WIN32_DX_CONTEXT_VIDEO_DECODER_H_
diff --git a/src/starboard/shared/win32/file_internal.cc b/src/starboard/shared/win32/file_internal.cc
index e6f7927..816483b 100644
--- a/src/starboard/shared/win32/file_internal.cc
+++ b/src/starboard/shared/win32/file_internal.cc
@@ -117,10 +117,6 @@
}
const DWORD last_error = GetLastError();
- if (!starboard::shared::win32::IsValidHandle(file_handle)) {
- SB_DLOG(INFO) << "CreateFile2 failed for " << path << ":"
- << sbwin32::Win32ErrorCode(last_error);
- }
if (out_error) {
if (starboard::shared::win32::IsValidHandle(file_handle)) {
diff --git a/src/starboard/shared/win32/get_home_directory.cc b/src/starboard/shared/win32/get_home_directory.cc
index aa4272f..52423d5 100644
--- a/src/starboard/shared/win32/get_home_directory.cc
+++ b/src/starboard/shared/win32/get_home_directory.cc
@@ -12,19 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <objbase.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+
+// Windows defines GetCommandLine as a macro to GetCommandLineW which
+// breaks our Application::GetCommandLine call below; thus we undefine
+// it after including our Windows headers.
+#undef GetCommandLine
+
+#include <cstdlib>
+#include <cstring>
#include <string>
#include "starboard/log.h"
#include "starboard/shared/nouser/user_internal.h"
-#include "starboard/shared/uwp/winrt_workaround.h"
+#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/command_line.h"
+#include "starboard/shared/win32/application_win32.h"
+#include "starboard/shared/win32/error_utils.h"
#include "starboard/shared/win32/wchar_utils.h"
#include "starboard/string.h"
#include "starboard/system.h"
-using Windows::Storage::ApplicationData;
-
namespace sbwin32 = starboard::shared::win32;
+using starboard::shared::starboard::CommandLine;
+
namespace starboard {
namespace shared {
namespace nouser {
@@ -33,10 +47,46 @@
if (user != SbUserGetCurrent()) {
return false;
}
- std::string home_directory =
- sbwin32::platformStringToString(
- ApplicationData::Current->LocalFolder->Path);
- return SbStringCopy(out_path, home_directory.c_str(), path_size);
+
+ PWSTR local_app_data_path = nullptr;
+
+ if (S_OK == SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
+ &local_app_data_path)) {
+ TCHAR wide_path[MAX_PATH];
+ wcscpy(wide_path, local_app_data_path);
+ CoTaskMemFree(local_app_data_path);
+ // Instead of using the raw local AppData directory, we create a program
+ // app directory if it doesn't exist already.
+ CommandLine* command_line =
+ sbwin32::ApplicationWin32::Get()->GetCommandLine();
+ SB_DCHECK(command_line);
+ const std::string program_name = command_line->argv()[0];
+ PathAppend(
+ wide_path,
+ sbwin32::CStringToWString(program_name.c_str()).c_str());
+ if (!PathFileExists(wide_path)) {
+ SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
+ NULL, TRUE};
+ const BOOL created_directory =
+ CreateDirectory(wide_path, &security_attributes);
+ if (!created_directory) {
+ SB_LOG(ERROR) << "Failed to create home directory";
+ sbwin32::DebugLogWinError();
+ return false;
+ }
+ }
+
+ const size_t actual_path_length = wcslen(wide_path);
+ if (path_size < actual_path_length) {
+ SB_LOG(ERROR) << "Home directory length exceeds max path size";
+ return false;
+ }
+ std::wcstombs(out_path, wide_path, actual_path_length + 1);
+ return true;
+ }
+ SB_LOG(ERROR) << "Unable to open local AppData as home directory.";
+ sbwin32::DebugLogWinError();
+ return false;
}
} // namespace nouser
diff --git a/src/starboard/shared/win32/media_common.cc b/src/starboard/shared/win32/media_common.cc
new file mode 100644
index 0000000..3eca93e
--- /dev/null
+++ b/src/starboard/shared/win32/media_common.cc
@@ -0,0 +1,101 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/media_common.h"
+
+#include <Mfapi.h>
+#include <Mferror.h>
+#include <Mfidl.h>
+#include <Mfobjects.h>
+#include <Rpc.h>
+#include <comutil.h>
+#include <wrl\client.h> // For ComPtr.
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/mutex.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// Converts 90khz to 10Mhz (100ns time).
+int64_t ConvertToWin32Time(SbMediaTime input) {
+ int64_t out = input;
+ out *= 1000;
+ out /= 9;
+ return out;
+}
+
+// Convert the other way around.
+SbMediaTime ConvertToMediaTime(int64_t input) {
+ SbMediaTime out = input;
+ out *= 9;
+ out /= 1000;
+ return out;
+}
+
+std::vector<ComPtr<IMFMediaType>> GetAllOutputMediaTypes(
+ IMFTransform* decoder) {
+ std::vector<ComPtr<IMFMediaType>> output;
+ for (int index = 0;; ++index) {
+ ComPtr<IMFMediaType> media_type;
+ HRESULT hr = decoder->GetOutputAvailableType(0, index, &media_type);
+ if (SUCCEEDED(hr)) {
+ output.push_back(media_type);
+ } else {
+ SB_DCHECK(hr == MF_E_NO_MORE_TYPES);
+ break;
+ }
+ }
+ return output;
+}
+
+std::vector<ComPtr<IMFMediaType>> FilterMediaBySubType(
+ const std::vector<ComPtr<IMFMediaType>>& input,
+ GUID sub_type_filter) {
+ std::vector<ComPtr<IMFMediaType>> output;
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ ComPtr<IMFMediaType> media_type = *it;
+ GUID media_sub_type = {0};
+ media_type->GetGUID(MF_MT_SUBTYPE, &media_sub_type);
+ if (IsEqualGUID(media_sub_type, sub_type_filter)) {
+ output.push_back(media_type);
+ }
+ }
+ return output;
+}
+
+ComPtr<IMFMediaType> FindMediaType(GUID sub_type, IMFTransform* decoder) {
+ std::vector<ComPtr<IMFMediaType>> media_types =
+ GetAllOutputMediaTypes(decoder);
+ media_types = FilterMediaBySubType(media_types, sub_type);
+ if (media_types.empty()) {
+ ComPtr<IMFMediaType> empty;
+ return empty;
+ } else {
+ return media_types[0];
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/media_common.h b/src/starboard/shared/win32/media_common.h
new file mode 100644
index 0000000..59212ee
--- /dev/null
+++ b/src/starboard/shared/win32/media_common.h
@@ -0,0 +1,74 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_MEDIA_COMMON_H_
+#define STARBOARD_SHARED_WIN32_MEDIA_COMMON_H_
+
+#include <Mfapi.h>
+#include <Mferror.h>
+#include <Mfidl.h>
+#include <Mfobjects.h>
+#include <Rpc.h>
+#include <comutil.h>
+#include <wrl\client.h> // For ComPtr.
+#include <vector>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/mutex.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/shared/starboard/player/filter/player_components.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+using Closure = ::starboard::shared::starboard::player::Closure;
+using DecodedAudio = ::starboard::shared::starboard::player::DecodedAudio;
+using DecodedAudioPtr = ::starboard::scoped_refptr<DecodedAudio>;
+using InputBuffer = ::starboard::shared::starboard::player::InputBuffer;
+using PlayerComponents =
+ ::starboard::shared::starboard::player::filter::PlayerComponents;
+using Status =
+ ::starboard::shared::starboard::player::filter::HostedVideoDecoder::Status;
+using VideoFrame = ::starboard::shared::starboard::player::VideoFrame;
+using VideoFramePtr = ::starboard::scoped_refptr<VideoFrame>;
+using VideoParameters =
+ ::starboard::shared::starboard::player::filter::VideoParameters;
+using Microsoft::WRL::ComPtr;
+
+// Converts 90khz to 10Mhz (100ns time).
+int64_t ConvertToWin32Time(SbMediaTime input);
+
+// Convert the other way around.
+SbMediaTime ConvertToMediaTime(int64_t input);
+
+std::vector<ComPtr<IMFMediaType>> GetAllOutputMediaTypes(IMFTransform* decoder);
+
+std::vector<ComPtr<IMFMediaType>> FilterMediaBySubType(
+ const std::vector<ComPtr<IMFMediaType>>& input,
+ GUID sub_type_filter);
+
+ComPtr<IMFMediaType> FindMediaType(GUID sub_type, IMFTransform* decoder);
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_MEDIA_COMMON_H_
diff --git a/src/starboard/shared/win32/media_foundation_utils.cc b/src/starboard/shared/win32/media_foundation_utils.cc
new file mode 100644
index 0000000..08e9f03
--- /dev/null
+++ b/src/starboard/shared/win32/media_foundation_utils.cc
@@ -0,0 +1,191 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/media_foundation_utils.h"
+
+#include <Mfapi.h>
+#include <Mferror.h>
+#include <propvarutil.h>
+
+#include <ios>
+#include <sstream>
+#include <utility>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/error_utils.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+using ::starboard::shared::win32::CheckResult;
+
+namespace {
+
+#define MAKE_GUID_PAIR(X) std::pair<GUID, std::string>(X, #X)
+
+const std::pair<GUID, std::string> kMfMtAudio[] = {
+ MAKE_GUID_PAIR(MF_MT_AAC_PAYLOAD_TYPE),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_AVG_BYTES_PER_SECOND),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_BITS_PER_SAMPLE),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_BLOCK_ALIGNMENT),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_CHANNEL_MASK),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_NUM_CHANNELS),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_SAMPLES_PER_BLOCK),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_SAMPLES_PER_SECOND),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_NUM_CHANNELS),
+ MAKE_GUID_PAIR(MF_MT_MAJOR_TYPE),
+ MAKE_GUID_PAIR(MF_MT_AUDIO_PREFER_WAVEFORMATEX),
+ MAKE_GUID_PAIR(MF_MT_USER_DATA),
+ MAKE_GUID_PAIR(MF_MT_SUBTYPE),
+ MAKE_GUID_PAIR(MFAudioFormat_AAC),
+ MAKE_GUID_PAIR(MFAudioFormat_ADTS),
+ MAKE_GUID_PAIR(MFAudioFormat_ALAC),
+ MAKE_GUID_PAIR(MFAudioFormat_AMR_NB),
+ MAKE_GUID_PAIR(MFAudioFormat_AMR_WB),
+ MAKE_GUID_PAIR(MFAudioFormat_AMR_WP),
+ MAKE_GUID_PAIR(MFAudioFormat_Dolby_AC3),
+ MAKE_GUID_PAIR(MFAudioFormat_Dolby_AC3_SPDIF),
+ MAKE_GUID_PAIR(MFAudioFormat_Dolby_DDPlus),
+ MAKE_GUID_PAIR(MFAudioFormat_DRM),
+ MAKE_GUID_PAIR(MFAudioFormat_DTS),
+ MAKE_GUID_PAIR(MFAudioFormat_FLAC),
+ MAKE_GUID_PAIR(MFAudioFormat_Float),
+ MAKE_GUID_PAIR(MFAudioFormat_Float_SpatialObjects),
+ MAKE_GUID_PAIR(MFAudioFormat_MP3),
+ MAKE_GUID_PAIR(MFAudioFormat_MPEG),
+ MAKE_GUID_PAIR(MFAudioFormat_MSP1),
+ MAKE_GUID_PAIR(MFAudioFormat_Opus),
+ MAKE_GUID_PAIR(MFAudioFormat_PCM),
+ MAKE_GUID_PAIR(MFAudioFormat_WMASPDIF),
+ MAKE_GUID_PAIR(MFAudioFormat_WMAudio_Lossless),
+ MAKE_GUID_PAIR(MFAudioFormat_WMAudioV8),
+ MAKE_GUID_PAIR(MFAudioFormat_WMAudioV9),
+ MAKE_GUID_PAIR(MFAudioFormat_WMAudioV9),
+ MAKE_GUID_PAIR(MFMediaType_Audio),
+};
+#undef MAKE_GUID_PAIR
+
+std::string GuidToFallbackString(GUID guid) {
+ std::stringstream ss;
+ wchar_t* guid_str = nullptr;
+ StringFromCLSID(guid, &guid_str);
+ ss << guid_str;
+ CoTaskMemFree(guid_str);
+ return ss.str();
+}
+
+std::string MfGuidToString(GUID guid) {
+ const size_t n = sizeof(kMfMtAudio) / sizeof(*kMfMtAudio);
+ for (auto i = 0; i < n; ++i) {
+ const auto& elems = kMfMtAudio[i];
+ if (guid == elems.first) {
+ return elems.second;
+ }
+ }
+ return GuidToFallbackString(guid);
+}
+
+std::string ImfAttributesToString(IMFAttributes* type) {
+ std::stringstream ss;
+ UINT32 n = 0;
+ HRESULT hr = type->GetCount(&n);
+ CheckResult(hr);
+ for (UINT32 i = 0; i < n; ++i) {
+ GUID key;
+ PROPVARIANT val;
+ type->GetItemByIndex(i, &key, &val);
+
+ MF_ATTRIBUTE_TYPE attrib_type;
+ hr = type->GetItemType(key, &attrib_type);
+ CheckResult(hr);
+
+ std::string key_str = MfGuidToString(key);
+ ss << key_str << ": ";
+
+ switch (attrib_type) {
+ case MF_ATTRIBUTE_GUID: {
+ GUID value_guid;
+ hr = type->GetGUID(key, &value_guid);
+ ss << MfGuidToString(value_guid) << "\n";
+ break;
+ }
+
+ case MF_ATTRIBUTE_DOUBLE: {
+ double value = 0;
+ hr = type->GetDouble(key, &value);
+ ss << value << "\n";
+ break;
+ }
+
+ case MF_ATTRIBUTE_BLOB: {
+ // Skip.
+ ss << "<BLOB>" << "\n";
+ break;
+ }
+
+ case MF_ATTRIBUTE_UINT32: {
+ UINT32 int_val = 0;
+ hr = type->GetUINT32(key, &int_val);
+ ss << int_val << "\n";
+ break;
+ }
+
+ case MF_ATTRIBUTE_UINT64: {
+ UINT64 int_val = 0;
+ hr = type->GetUINT64(key, &int_val);
+ ss << int_val << "\n";
+ break;
+ }
+
+ case MF_ATTRIBUTE_STRING: {
+ wchar_t buff[128];
+ UINT buff_size = sizeof(buff);
+ hr = type->GetString(key, buff, buff_size, NULL);
+ CheckResult(hr);
+ ss << buff << "\n";
+ break;
+ }
+ default: {
+ SB_NOTIMPLEMENTED();
+ break;
+ }
+ }
+ }
+ ss << "\n";
+ return ss.str();
+}
+
+} // namespace.
+
+void CopyUint32Property(GUID key, const IMFMediaType* source,
+ IMFMediaType* destination) {
+ UINT32 val = 0;
+ const_cast<IMFMediaType*>(source)->GetUINT32(key, &val);
+ HRESULT hr = destination->SetUINT32(key, val);
+ CheckResult(hr);
+}
+
+std::ostream& operator<<(std::ostream& os, const IMFMediaType& media_type) {
+ const IMFAttributes* attribs = &media_type; // Upcast.
+ std::string output_str =
+ ImfAttributesToString(const_cast<IMFAttributes*>(attribs));
+ os << output_str;
+ return os;
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/media_foundation_utils.h b/src/starboard/shared/win32/media_foundation_utils.h
new file mode 100644
index 0000000..088cc5c
--- /dev/null
+++ b/src/starboard/shared/win32/media_foundation_utils.h
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. 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 <wrl/client.h>
+#include <Mfobjects.h>
+
+#include <string>
+
+#ifndef STARBOARD_SHARED_WIN32_MEDIA_FOUNDATION_UTILS_H_
+#define STARBOARD_SHARED_WIN32_MEDIA_FOUNDATION_UTILS_H_
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+std::ostream& operator<<(std::ostream& os, const IMFMediaType& media_type);
+
+std::string ToString(IMFAttributes* type);
+
+void CopyUint32Property(GUID key, const IMFMediaType* source,
+ IMFMediaType* destination);
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_MEDIA_FOUNDATION_UTILS_H_
diff --git a/src/starboard/shared/win32/media_is_audio_supported.cc b/src/starboard/shared/win32/media_is_audio_supported.cc
new file mode 100644
index 0000000..1c6936d
--- /dev/null
+++ b/src/starboard/shared/win32/media_is_audio_supported.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+ int64_t bitrate) {
+ // TODO: Add Opus.
+ if (audio_codec != kSbMediaAudioCodecAac) {
+ return false;
+ }
+ return bitrate <= SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
+}
diff --git a/src/starboard/shared/win32/media_is_supported.cc b/src/starboard/shared/win32/media_is_supported.cc
new file mode 100644
index 0000000..127f625
--- /dev/null
+++ b/src/starboard/shared/win32/media_is_supported.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/string.h"
+
+// TODO: Fill this in for DRM.
+SB_EXPORT bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
+ SbMediaAudioCodec audio_codec,
+ const char* key_system) {
+ SB_UNREFERENCED_PARAMETER(video_codec);
+ SB_UNREFERENCED_PARAMETER(audio_codec);
+ SB_UNREFERENCED_PARAMETER(key_system);
+ return 0 == SbStringCompareAll(key_system, "com.youtube.playready");
+}
diff --git a/src/starboard/shared/win32/media_is_video_supported.cc b/src/starboard/shared/win32/media_is_video_supported.cc
new file mode 100644
index 0000000..8236694
--- /dev/null
+++ b/src/starboard/shared/win32/media_is_video_supported.cc
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+ int frame_width,
+ int frame_height,
+ int64_t bitrate,
+ int fps) {
+ // Is resolution out of range?
+ if (frame_width > 1920 || frame_height > 1080) {
+ return false;
+ }
+
+ // Is bitrate in range?
+ if (bitrate > SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND) {
+ return false;
+ }
+
+ if (fps > 60) {
+ return false;
+ }
+
+ // If this is changed then so should win32_decoder_impl.cc
+ return (video_codec == kSbMediaVideoCodecH264);
+}
diff --git a/src/starboard/shared/win32/player_components_impl.cc b/src/starboard/shared/win32/player_components_impl.cc
new file mode 100644
index 0000000..cc86d10
--- /dev/null
+++ b/src/starboard/shared/win32/player_components_impl.cc
@@ -0,0 +1,57 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/filter/player_components.h"
+
+#include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h"
+#include "starboard/shared/win32/audio_decoder.h"
+#include "starboard/shared/win32/video_decoder.h"
+#include "starboard/shared/win32/video_renderer.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// static
+scoped_ptr<PlayerComponents> PlayerComponents::Create(
+ const AudioParameters& audio_parameters,
+ const VideoParameters& video_parameters) {
+ using AudioDecoderImpl = ::starboard::shared::win32::AudioDecoder;
+ using VideoDecoderImpl = ::starboard::shared::win32::VideoDecoder;
+ using VideoRendererImpl = ::starboard::shared::win32::VideoRendererImpl;
+
+ AudioDecoderImpl* audio_decoder = new AudioDecoderImpl(
+ audio_parameters.audio_codec, audio_parameters.audio_header);
+
+ VideoDecoderImpl* video_decoder = new VideoDecoderImpl(video_parameters);
+
+ AudioRendererImpl* audio_renderer =
+ new AudioRendererImpl(scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
+ audio_parameters.audio_header);
+
+ VideoRendererImpl* video_renderer =
+ new VideoRendererImpl(scoped_ptr<VideoDecoderImpl>(video_decoder).Pass());
+
+ return scoped_ptr<PlayerComponents>(
+ new PlayerComponents(audio_renderer, video_renderer));
+}
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/simple_thread.cc b/src/starboard/shared/win32/simple_thread.cc
new file mode 100644
index 0000000..3d75149
--- /dev/null
+++ b/src/starboard/shared/win32/simple_thread.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "starboard/shared/win32/simple_thread.h"
+
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+SimpleThread::SimpleThread(const std::string& name)
+ : thread_(kSbThreadInvalid), name_(name), join_called_(false) {}
+
+SimpleThread::~SimpleThread() {
+ SB_DCHECK(join_called_.load()) << "Join not called on thread.";
+}
+
+void SimpleThread::Start() {
+ SbThreadEntryPoint entry_point = ThreadEntryPoint;
+
+ thread_ = SbThreadCreate(0, // default stack_size.
+ kSbThreadNoPriority, // default priority.
+ kSbThreadNoAffinity, // default affinity.
+ true, // joinable.
+ name_.c_str(), entry_point, this);
+
+ // SbThreadCreate() above produced an invalid thread handle.
+ SB_DCHECK(thread_ != kSbThreadInvalid);
+ return;
+}
+
+void SimpleThread::Sleep(SbTime microseconds) {
+ SbThreadSleep(microseconds);
+}
+
+void SimpleThread::SleepMilliseconds(int value) {
+ return Sleep(value * kSbTimeMillisecond);
+}
+
+void* SimpleThread::ThreadEntryPoint(void* context) {
+ SimpleThread* this_ptr = static_cast<SimpleThread*>(context);
+ this_ptr->Run();
+ return NULL;
+}
+
+void SimpleThread::Join() {
+ SB_DCHECK(join_called_.load() == false);
+ join_called_.store(true);
+ if (!SbThreadJoin(thread_, NULL)) {
+ SB_DCHECK(false) << "Could not join thread.";
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/simple_thread.h b/src/starboard/shared/win32/simple_thread.h
new file mode 100644
index 0000000..82c8bab
--- /dev/null
+++ b/src/starboard/shared/win32/simple_thread.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 Google Inc. 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_SHARED_WIN32_SIMPLE_THREAD_H_
+#define STARBOARD_SHARED_WIN32_SIMPLE_THREAD_H_
+
+#include <string>
+
+#include "starboard/atomic.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class SimpleThread {
+ public:
+ explicit SimpleThread(const std::string& name);
+ virtual ~SimpleThread();
+
+ // Subclasses should override the Run method.
+ virtual void Run() = 0;
+
+ // Signals to the thread to break out of it's loop.
+ virtual void Cancel() {}
+
+ // Called by the main thread, this will cause Run() to be invoked
+ // on another thread.
+ void Start();
+ // Destroys the threads resources.
+ void Join();
+
+ bool join_called() const { return join_called_.load(); }
+
+ protected:
+ static void Sleep(SbTime microseconds);
+ static void SleepMilliseconds(int value);
+
+ private:
+ static void* ThreadEntryPoint(void* context);
+
+ const std::string name_;
+ SbThread thread_;
+ atomic_bool join_called_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_SIMPLE_THREAD_H_
diff --git a/src/starboard/shared/win32/starboard_main.cc b/src/starboard/shared/win32/starboard_main.cc
new file mode 100644
index 0000000..ccc3552
--- /dev/null
+++ b/src/starboard/shared/win32/starboard_main.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/starboard_main.h"
+
+#include <Objbase.h>
+#include <WinSock2.h>
+#include <mfapi.h>
+#include <windows.h>
+
+#include <cstdio>
+
+#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
+#include "starboard/shared/win32/application_win32.h"
+#include "starboard/shared/win32/thread_private.h"
+
+using starboard::shared::win32::ApplicationWin32;
+
+extern "C" int StarboardMain(int argc, char** argv) {
+ WSAData wsaData;
+ const int kWinSockVersionMajor = 2;
+ const int kWinSockVersionMinor = 2;
+ const int init_result = WSAStartup(
+ MAKEWORD(kWinSockVersionMajor, kWinSockVersionMajor), &wsaData);
+
+ SB_CHECK(init_result == 0);
+ // WSAStartup returns the highest version that is supported up to the version
+ // we request.
+ SB_CHECK(LOBYTE(wsaData.wVersion) == kWinSockVersionMajor &&
+ HIBYTE(wsaData.wVersion) == kWinSockVersionMinor);
+
+ // Initialize COM for XAudio2 APIs.
+ CoInitialize(nullptr);
+ SbAudioSinkPrivate::Initialize();
+ starboard::shared::win32::RegisterMainThread();
+
+ // TODO: Do this with SbOnce when media is first used instead.
+ HRESULT hr = MFStartup(MF_VERSION);
+ SB_DCHECK(SUCCEEDED(hr));
+
+ ApplicationWin32 application;
+ // This will run the message loop.
+ const int main_return_value = application.Run(argc, argv);
+ MFShutdown();
+ WSACleanup();
+ return main_return_value;
+}
diff --git a/src/starboard/win/console/atomic_public.h b/src/starboard/shared/win32/starboard_main.h
similarity index 70%
copy from src/starboard/win/console/atomic_public.h
copy to src/starboard/shared/win32/starboard_main.h
index 51f81a1..7d19ee0 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/starboard/shared/win32/starboard_main.h
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_SHARED_WIN32_STARBOARD_MAIN_H_
+#define STARBOARD_SHARED_WIN32_STARBOARD_MAIN_H_
-#include "starboard/shared/win32/atomic_public.h"
+#include "starboard/export.h"
-#endif // STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+extern "C" SB_EXPORT_PLATFORM int StarboardMain(int argc, char** argv);
+
+#endif // STARBOARD_SHARED_WIN32_STARBOARD_MAIN_H_
diff --git a/src/starboard/win/console/atomic_public.h b/src/starboard/shared/win32/system_clear_platform_error.cc
similarity index 69%
copy from src/starboard/win/console/atomic_public.h
copy to src/starboard/shared/win32/system_clear_platform_error.cc
index 51f81a1..12521bb 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/starboard/shared/win32/system_clear_platform_error.cc
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+#include "starboard/shared/win32/application_win32.h"
+#include "starboard/system.h"
-#include "starboard/shared/win32/atomic_public.h"
+using starboard::shared::win32::ApplicationWin32;
-#endif // STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+void SbSystemClearPlatformError(SbSystemPlatformError handle) {
+ ApplicationWin32::Get()->OnSbSystemClearPlatformError(handle);
+}
diff --git a/src/starboard/shared/win32/system_get_device_type.cc b/src/starboard/shared/win32/system_get_device_type.cc
index 0957c7a..fc51f58 100644
--- a/src/starboard/shared/win32/system_get_device_type.cc
+++ b/src/starboard/shared/win32/system_get_device_type.cc
@@ -17,23 +17,8 @@
#include <string>
#include "starboard/log.h"
-#include "starboard/shared/uwp/winrt_workaround.h"
#include "starboard/shared/win32/wchar_utils.h"
-using Windows::System::Profile::AnalyticsInfo;
-using Windows::System::Profile::AnalyticsVersionInfo;
-
SbSystemDeviceType SbSystemGetDeviceType() {
- AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
- std::string family = starboard::shared::win32::platformStringToString(
- version_info->DeviceFamily);
-
- if (family.compare("Windows.Desktop") == 0) {
- return kSbSystemDeviceTypeDesktopPC;
- }
- if (family.compare("Windows.Xbox") == 0) {
- return kSbSystemDeviceTypeGameConsole;
- }
- SB_NOTREACHED();
- return kSbSystemDeviceTypeUnknown;
+ return kSbSystemDeviceTypeDesktopPC;
}
diff --git a/src/starboard/shared/win32/system_get_property.cc b/src/starboard/shared/win32/system_get_property.cc
new file mode 100644
index 0000000..30e9522
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_property.cc
@@ -0,0 +1,69 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/log.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace {
+
+const char* kFriendlyName = "Windows Desktop";
+const char* kPlatformName = "win32; Windows x86_64";
+
+bool CopyStringAndTestIfSuccess(char* out_value,
+ int value_length,
+ const char* from_value) {
+ if (SbStringGetLength(from_value) + 1 > value_length)
+ return false;
+ SbStringCopy(out_value, from_value, value_length);
+ return true;
+}
+
+} // namespace
+
+bool SbSystemGetProperty(SbSystemPropertyId property_id,
+ char* out_value,
+ int value_length) {
+ if (!out_value || !value_length) {
+ return false;
+ }
+
+ switch (property_id) {
+ case kSbSystemPropertyBrandName:
+ case kSbSystemPropertyChipsetModelNumber:
+ case kSbSystemPropertyFirmwareVersion:
+ case kSbSystemPropertyModelName:
+ case kSbSystemPropertyModelYear:
+ case kSbSystemPropertyNetworkOperatorName:
+ case kSbSystemPropertySpeechApiKey:
+ return false;
+
+ case kSbSystemPropertyFriendlyName:
+ return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
+
+ case kSbSystemPropertyPlatformName:
+ return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
+
+ case kSbSystemPropertyPlatformUuid:
+ SB_NOTIMPLEMENTED();
+ return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
+
+ default:
+ SB_DLOG(WARNING) << __FUNCTION__
+ << ": Unrecognized property: " << property_id;
+ break;
+ }
+
+ return false;
+}
diff --git a/src/starboard/shared/win32/system_raise_platform_error.cc b/src/starboard/shared/win32/system_raise_platform_error.cc
new file mode 100644
index 0000000..e53b245
--- /dev/null
+++ b/src/starboard/shared/win32/system_raise_platform_error.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/application_win32.h"
+#include "starboard/system.h"
+
+using starboard::shared::win32::ApplicationWin32;
+
+SbSystemPlatformError SbSystemRaisePlatformError(
+ SbSystemPlatformErrorType type,
+ SbSystemPlatformErrorCallback callback,
+ void* user_data) {
+ return ApplicationWin32::Get()->OnSbSystemRaisePlatformError(type, callback,
+ user_data);
+}
diff --git a/src/starboard/shared/win32/video_decoder.cc b/src/starboard/shared/win32/video_decoder.cc
new file mode 100644
index 0000000..40fd85d
--- /dev/null
+++ b/src/starboard/shared/win32/video_decoder.cc
@@ -0,0 +1,113 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/video_decoder.h"
+
+#include "starboard/decode_target.h"
+#include "starboard/log.h"
+#include "starboard/shared/win32/decode_target_internal.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/media_common.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+VideoDecoder::VideoDecoder(const VideoParameters& params)
+ : video_codec_(params.video_codec),
+ host_(NULL),
+ output_mode_(params.output_mode),
+ decode_target_graphics_context_provider_(
+ params.decode_target_graphics_context_provider) {
+ impl_ = AbstractWin32VideoDecoder::Create(video_codec_);
+ video_decoder_thread_.reset(new VideoDecoderThread(impl_.get(), this));
+}
+
+VideoDecoder::~VideoDecoder() {
+ video_decoder_thread_.reset(nullptr);
+ impl_.reset(nullptr);
+}
+
+void VideoDecoder::SetHost(Host* host) {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(host != NULL);
+ SB_DCHECK(host_ == NULL);
+ host_ = host;
+}
+
+void VideoDecoder::WriteInputBuffer(
+ const scoped_refptr<InputBuffer>& input_buffer) {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(input_buffer);
+ SB_DCHECK(host_);
+ const bool can_accept_more_input =
+ video_decoder_thread_->QueueInput(input_buffer);
+
+ if (can_accept_more_input) {
+ host_->OnDecoderStatusUpdate(kNeedMoreInput, NULL);
+ }
+}
+
+void VideoDecoder::WriteEndOfStream() {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(host_);
+ scoped_refptr<InputBuffer> empty;
+ video_decoder_thread_->QueueInput(empty);
+}
+
+void VideoDecoder::Reset() {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(host_);
+ video_decoder_thread_.reset(nullptr);
+ impl_.reset(nullptr);
+ impl_ = AbstractWin32VideoDecoder::Create(video_codec_);
+ video_decoder_thread_.reset(new VideoDecoderThread(impl_.get(), this));
+}
+
+// When in decode-to-texture mode, this returns the current decoded video frame.
+SbDecodeTarget VideoDecoder::GetCurrentDecodeTarget() {
+ SB_NOTIMPLEMENTED()
+ << "VideoRendererImpl::GetCurrentDecodeTarget() should be used instead.";
+ return kSbDecodeTargetInvalid;
+}
+
+void VideoDecoder::OnVideoDecoded(VideoFramePtr data) {
+ Status sts = data->IsEndOfStream() ? kBufferFull : kNeedMoreInput;
+ host_->OnDecoderStatusUpdate(sts, data);
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// static
+bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
+ SbMediaVideoCodec codec,
+ SbDrmSystem drm_system) {
+ SB_UNREFERENCED_PARAMETER(codec);
+ SB_UNREFERENCED_PARAMETER(drm_system);
+ return output_mode == kSbPlayerOutputModeDecodeToTexture;
+}
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/video_decoder.h b/src/starboard/shared/win32/video_decoder.h
new file mode 100644
index 0000000..1fd2bfa
--- /dev/null
+++ b/src/starboard/shared/win32/video_decoder.h
@@ -0,0 +1,78 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_VIDEO_DECODER_H_
+#define STARBOARD_SHARED_WIN32_VIDEO_DECODER_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/configuration.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/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/shared/starboard/thread_checker.h"
+#include "starboard/shared/win32/atomic_queue.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/shared/win32/video_decoder_thread.h"
+#include "starboard/shared/win32/win32_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class VideoDecoder
+ : public ::starboard::shared::starboard::player::filter::HostedVideoDecoder,
+ private ::starboard::shared::starboard::player::JobQueue::JobOwner,
+ private VideoDecodedCallback {
+ public:
+ explicit VideoDecoder(const VideoParameters& params);
+ ~VideoDecoder() SB_OVERRIDE;
+
+ void SetHost(Host* host) SB_OVERRIDE;
+ void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer)
+ SB_OVERRIDE;
+ void WriteEndOfStream() SB_OVERRIDE;
+ void Reset() SB_OVERRIDE;
+
+ SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
+
+ // Implements class VideoDecodedCallback.
+ void OnVideoDecoded(VideoFramePtr data) SB_OVERRIDE;
+
+ private:
+ // These variables will be initialized inside ctor or SetHost() and will not
+ // be changed during the life time of this class.
+ const SbMediaVideoCodec video_codec_;
+ Host* host_;
+
+ // Decode-to-texture related state.
+ SbPlayerOutputMode output_mode_;
+
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider_;
+
+ scoped_ptr<AbstractWin32VideoDecoder> impl_;
+ AtomicQueue<VideoFramePtr> decoded_data_;
+ ::starboard::Mutex mutex_;
+ scoped_ptr<VideoDecoderThread> video_decoder_thread_;
+ ::starboard::shared::starboard::ThreadChecker thread_checker_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_VIDEO_DECODER_H_
diff --git a/src/starboard/shared/win32/video_decoder_thread.cc b/src/starboard/shared/win32/video_decoder_thread.cc
new file mode 100644
index 0000000..64a7b2e
--- /dev/null
+++ b/src/starboard/shared/win32/video_decoder_thread.cc
@@ -0,0 +1,128 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/video_decoder_thread.h"
+
+#include <deque>
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+namespace {
+const size_t kMaxSize = 16;
+
+size_t WriteAsMuchAsPossible(
+ std::deque<scoped_refptr<InputBuffer> >* data_queue,
+ AbstractWin32VideoDecoder* video_decoder,
+ bool* is_end_of_stream_reached) {
+ const size_t original_size = data_queue->size();
+ while (!data_queue->empty()) {
+ scoped_refptr<InputBuffer> buff = data_queue->front();
+ data_queue->pop_front();
+
+ if (buff) {
+ const bool write_ok = video_decoder->TryWrite(buff);
+
+ if (!write_ok) {
+ data_queue->push_front(buff);
+ break;
+ }
+ } else {
+ video_decoder->WriteEndOfStream();
+ *is_end_of_stream_reached = true;
+ }
+ }
+ return original_size - data_queue->size();
+}
+
+} // namespace.
+
+VideoDecoderThread::VideoDecoderThread(AbstractWin32VideoDecoder* decoder_impl,
+ VideoDecodedCallback* callback)
+ : SimpleThread("VideoDecoderThread"),
+ win32_video_decoder_(decoder_impl),
+ callback_(callback) {
+ Start();
+}
+
+VideoDecoderThread::~VideoDecoderThread() {
+ Join();
+ SB_DCHECK(join_called());
+}
+
+bool VideoDecoderThread::QueueInput(const scoped_refptr<InputBuffer>& buffer) {
+ {
+ ::starboard::ScopedLock lock(input_buffer_queue_mutex_);
+ input_buffer_queue_.push_back(buffer);
+ }
+
+ // increment() returns the prev value.
+ size_t proc_size = processing_elements_.increment() + 1;
+ semaphore_.Put();
+ return proc_size < kMaxSize;
+}
+
+void VideoDecoderThread::QueueEndOfStream() {
+ scoped_refptr<InputBuffer> empty;
+ QueueInput(empty);
+}
+
+void VideoDecoderThread::TransferPendingInputTo(
+ std::deque<scoped_refptr<InputBuffer> >* output) {
+ // Transfer input buffer to local thread.
+ ::starboard::ScopedLock lock(input_buffer_queue_mutex_);
+ while (!input_buffer_queue_.empty()) {
+ output->push_back(input_buffer_queue_.front());
+ input_buffer_queue_.pop_front();
+ }
+}
+
+void VideoDecoderThread::Run() {
+ std::deque<scoped_refptr<InputBuffer> > local_queue;
+ while (!join_called()) {
+ // Transfer input buffer to local thread.
+ TransferPendingInputTo(&local_queue);
+ bool work_done = !local_queue.empty();
+ bool is_end_of_stream = false;
+ const size_t number_written =
+ WriteAsMuchAsPossible(&local_queue, win32_video_decoder_,
+ &is_end_of_stream);
+ if (number_written > 0) {
+ processing_elements_.fetch_sub(static_cast<int32_t>(number_written));
+ work_done = true;
+ }
+
+ while (VideoFramePtr decoded_datum =
+ win32_video_decoder_->ProcessAndRead()) {
+ if (decoded_datum.get()) {
+ callback_->OnVideoDecoded(decoded_datum);
+ }
+ work_done = true;
+ }
+
+ if (is_end_of_stream) {
+ callback_->OnVideoDecoded(VideoFrame::CreateEOSFrame());
+ work_done = true;
+ }
+
+ if (!work_done) {
+ semaphore_.TakeWait(kSbTimeMillisecond);
+ }
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/video_decoder_thread.h b/src/starboard/shared/win32/video_decoder_thread.h
new file mode 100644
index 0000000..0077756
--- /dev/null
+++ b/src/starboard/shared/win32/video_decoder_thread.h
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_VIDEO_DECODER_THREAD_H_
+#define STARBOARD_SHARED_WIN32_VIDEO_DECODER_THREAD_H_
+
+#include <deque>
+#include <queue>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/semaphore.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/shared/win32/simple_thread.h"
+#include "starboard/shared/win32/win32_decoder_impl.h"
+#include "starboard/shared/win32/win32_video_decoder.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// This class receives decoded video frames.
+class VideoDecodedCallback {
+ public:
+ virtual ~VideoDecodedCallback() {}
+ virtual void OnVideoDecoded(VideoFramePtr data) = 0;
+};
+
+// This decoder thread simplifies decoding media. Data is pushed in via
+// QueueInput() and QueueEndOfStream() and output data is pushed via
+// the AudioDecodedCallback.
+class VideoDecoderThread : private SimpleThread {
+ public:
+ VideoDecoderThread(AbstractWin32VideoDecoder* decoder_impl,
+ VideoDecodedCallback* callback);
+ ~VideoDecoderThread() SB_OVERRIDE;
+
+ // Returns true if more input can be pushed to this thread.
+ bool QueueInput(const scoped_refptr<InputBuffer>& buffer);
+ void QueueEndOfStream();
+
+ private:
+ void TransferPendingInputTo(std::deque<scoped_refptr<InputBuffer> >* output);
+ void Run() SB_OVERRIDE;
+ AbstractWin32VideoDecoder* win32_video_decoder_;
+ VideoDecodedCallback* callback_;
+ std::deque<scoped_refptr<InputBuffer> > input_buffer_queue_;
+ ::starboard::Mutex input_buffer_queue_mutex_;
+ atomic_int32_t processing_elements_;
+ Semaphore semaphore_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_VIDEO_DECODER_THREAD_H_
diff --git a/src/starboard/shared/win32/video_renderer.cc b/src/starboard/shared/win32/video_renderer.cc
new file mode 100644
index 0000000..832dd4e
--- /dev/null
+++ b/src/starboard/shared/win32/video_renderer.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/video_renderer.h"
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/decode_target_internal.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "third_party/angle/include/EGL/egl.h"
+#include "third_party/angle/include/EGL/eglext.h"
+#include "third_party/angle/include/GLES2/gl2.h"
+
+using starboard::scoped_refptr;
+using Microsoft::WRL::ComPtr;
+using starboard::shared::win32::CheckResult;
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+SbDecodeTarget VideoRendererImpl::GetCurrentDecodeTarget() {
+ VideoFramePtr last_frame = Base::GetLastDisplayedFrame();
+ if (!last_frame || last_frame->IsEndOfStream()) {
+ return kSbDecodeTargetInvalid;
+ }
+ return new SbDecodeTargetPrivate(last_frame);
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/video_renderer.h b/src/starboard/shared/win32/video_renderer.h
new file mode 100644
index 0000000..c0e97a4
--- /dev/null
+++ b/src/starboard/shared/win32/video_renderer.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_VIDEO_RENDERER_H_
+#define STARBOARD_SHARED_WIN32_VIDEO_RENDERER_H_
+
+#include "starboard/mutex.h"
+#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h"
+#include "starboard/shared/win32/decode_target_internal.h"
+#include "starboard/shared/win32/video_decoder.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class VideoRendererImpl
+ : public ::starboard::shared::starboard::player::filter::VideoRendererImpl {
+ public:
+ using Base =
+ ::starboard::shared::starboard::player::filter::VideoRendererImpl;
+ using HostedVideoDecoder =
+ ::starboard::shared::starboard::player::filter::HostedVideoDecoder;
+
+ using VideoDecoder = ::starboard::shared::win32::VideoDecoder;
+
+ explicit VideoRendererImpl(scoped_ptr<VideoDecoder> decoder)
+ : Base(decoder.PassAs<HostedVideoDecoder>()) {}
+
+ SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_VIDEO_RENDERER_H_
diff --git a/src/starboard/shared/win32/win32_audio_decoder.cc b/src/starboard/shared/win32/win32_audio_decoder.cc
new file mode 100644
index 0000000..0b85f30
--- /dev/null
+++ b/src/starboard/shared/win32/win32_audio_decoder.cc
@@ -0,0 +1,293 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/win32_audio_decoder.h"
+
+#include <algorithm>
+
+#include "starboard/shared/win32/atomic_queue.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/media_foundation_utils.h"
+#include "starboard/shared/win32/win32_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+namespace {
+using Microsoft::WRL::ComPtr;
+using ::starboard::shared::win32::CheckResult;
+
+const int kStreamId = 0;
+
+std::vector<ComPtr<IMFMediaType>> Filter(
+ GUID subtype_guid, const std::vector<ComPtr<IMFMediaType>>& input) {
+ std::vector<ComPtr<IMFMediaType>> output;
+ for (size_t i = 0; i < input.size(); ++i) {
+ ComPtr<IMFMediaType> curr = input[i];
+ GUID guid_value;
+ HRESULT hr = curr->GetGUID(MF_MT_SUBTYPE, &guid_value);
+ CheckResult(hr);
+ if (subtype_guid == guid_value) {
+ output.push_back(curr);
+ }
+ }
+
+ return output;
+}
+
+std::vector<ComPtr<IMFMediaType>> GetAvailableTypes(IMFTransform* decoder) {
+ std::vector<ComPtr<IMFMediaType>> output;
+ for (DWORD i = 0; ; ++i) {
+ ComPtr<IMFMediaType> curr_type;
+ HRESULT input_hr_success = decoder->GetInputAvailableType(
+ kStreamId,
+ i,
+ curr_type.GetAddressOf());
+ if (!SUCCEEDED(input_hr_success)) {
+ break;
+ }
+ output.push_back(curr_type);
+ }
+
+ return output;
+}
+
+class WinAudioFormat {
+ public:
+ explicit WinAudioFormat(const SbMediaAudioHeader& audio_header) {
+ WAVEFORMATEX* wave_format = WaveFormatTexPtr();
+ wave_format->nAvgBytesPerSec = audio_header.average_bytes_per_second;
+ wave_format->nBlockAlign = audio_header.block_alignment;
+ wave_format->nChannels = audio_header.number_of_channels;
+ wave_format->nSamplesPerSec = audio_header.samples_per_second;
+ wave_format->wBitsPerSample = audio_header.bits_per_sample;
+ wave_format->wFormatTag = audio_header.format_tag;
+
+ // TODO: Investigate this more.
+ wave_format->cbSize = kAudioExtraFormatBytes;
+ std::uint8_t* audio_specific_config = AudioSpecificConfigPtr();
+
+ // These are hard-coded audio specif audio configuration.
+ // Use |SbMediaAudioHeader::audio_specific_config| instead.
+ SB_DCHECK(kAudioExtraFormatBytes == 2);
+ // TODO: What do these values do?
+ audio_specific_config[0] = 0x12;
+ audio_specific_config[1] = 0x10;
+ }
+ WAVEFORMATEX* WaveFormatTexPtr() {
+ return reinterpret_cast<WAVEFORMATEX*>(full_structure);
+ }
+ uint8_t* AudioSpecificConfigPtr() {
+ return full_structure + sizeof(WAVEFORMATEX);
+ }
+
+ UINT32 Size() const {
+ return sizeof(full_structure);
+ }
+
+ private:
+ static const UINT32 kAudioExtraFormatBytes = 2;
+ uint8_t full_structure[sizeof(WAVEFORMATEX) + kAudioExtraFormatBytes];
+};
+
+class AbstractWin32AudioDecoderImpl : public AbstractWin32AudioDecoder,
+ public MediaBufferConsumerInterface {
+ public:
+ AbstractWin32AudioDecoderImpl(SbMediaAudioCodec codec,
+ SbMediaAudioFrameStorageType audio_frame_fmt,
+ SbMediaAudioSampleType sample_type,
+ const SbMediaAudioHeader& audio_header)
+ : codec_(codec),
+ audio_frame_fmt_(audio_frame_fmt),
+ sample_type_(sample_type),
+ audio_header_(audio_header) {
+ MediaBufferConsumerInterface* media_cb = this;
+ impl_.reset(new DecoderImpl("audio", media_cb));
+ EnsureAudioDecoderCreated();
+ }
+
+ static GUID ConvertToWin32AudioCodec(SbMediaAudioCodec codec) {
+ switch (codec) {
+ case kSbMediaAudioCodecNone: { return MFAudioFormat_PCM; }
+ case kSbMediaAudioCodecAac: { return MFAudioFormat_AAC; }
+ case kSbMediaAudioCodecOpus: { return MFAudioFormat_Opus; }
+ case kSbMediaAudioCodecVorbis: {
+ SB_NOTIMPLEMENTED();
+ }
+ }
+ return MFAudioFormat_PCM;
+ }
+
+ virtual void Consume(ComPtr<IMFMediaBuffer> media_buffer,
+ int64_t win32_timestamp) {
+ BYTE* buffer;
+ DWORD length;
+ HRESULT hr = media_buffer->Lock(&buffer, NULL, &length);
+ CheckResult(hr);
+ SB_DCHECK(length);
+
+ const uint8_t* data = reinterpret_cast<uint8_t*>(buffer);
+ const size_t data_size = static_cast<size_t>(length);
+
+ DecodedAudioPtr data_ptr(new DecodedAudio(
+ audio_header_.number_of_channels, sample_type_, audio_frame_fmt_,
+ ConvertToMediaTime(win32_timestamp), data_size));
+
+ std::copy(data, data + data_size, data_ptr->buffer());
+
+ output_queue_.PushBack(data_ptr);
+ media_buffer->Unlock();
+ }
+
+ ComPtr<IMFMediaType> Configure(IMFTransform* decoder) {
+ ComPtr<IMFMediaType> input_type;
+ HRESULT hr = MFCreateMediaType(&input_type);
+ CheckResult(hr);
+
+ WinAudioFormat audio_fmt(audio_header_);
+ hr = MFInitMediaTypeFromWaveFormatEx(
+ input_type.Get(),
+ audio_fmt.WaveFormatTexPtr(),
+ audio_fmt.Size());
+
+ CheckResult(hr);
+
+ hr = input_type->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 0); // raw aac
+ CheckResult(hr);
+
+ GUID subtype = ConvertToWin32AudioCodec(codec_);
+ hr = input_type->SetGUID(MF_MT_SUBTYPE, subtype);
+ CheckResult(hr);
+
+ std::vector<ComPtr<IMFMediaType>> available_types =
+ GetAvailableTypes(decoder);
+
+ GUID audio_fmt_guid = ConvertToWin32AudioCodec(codec_);
+ available_types = Filter(audio_fmt_guid, available_types);
+ SB_DCHECK(available_types.size());
+ ComPtr<IMFMediaType> selected = available_types[0];
+
+ std::vector<GUID> attribs = {
+ MF_MT_AUDIO_BLOCK_ALIGNMENT,
+ MF_MT_AUDIO_SAMPLES_PER_SECOND,
+ MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
+ MF_MT_AUDIO_NUM_CHANNELS,
+ };
+
+ for (auto it = attribs.begin(); it != attribs.end(); ++it) {
+ CopyUint32Property(*it, input_type.Get(), selected.Get());
+ }
+
+ hr = decoder->SetInputType(0, selected.Get(), 0);
+
+ CheckResult(hr);
+ return selected;
+ }
+
+ void EnsureAudioDecoderCreated() SB_OVERRIDE {
+ if (impl_->has_decoder()) {
+ return;
+ }
+
+ ComPtr<IMFTransform> decoder =
+ DecoderImpl::CreateDecoder(CLSID_MSAACDecMFT);
+
+ ComPtr<IMFMediaType> media_type = Configure(decoder.Get());
+
+ SB_DCHECK(decoder);
+
+ impl_->set_decoder(decoder);
+ impl_->ActivateDecryptor(media_type);
+
+ // TODO: MFWinAudioFormat_PCM?
+ ComPtr<IMFMediaType> output_type =
+ FindMediaType(MFAudioFormat_Float, decoder.Get());
+
+ SB_DCHECK(output_type);
+
+ HRESULT hr = decoder->SetOutputType(0, output_type.Get(), 0);
+ CheckResult(hr);
+
+ decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+ }
+
+ bool TryWrite(const InputBuffer& buff) SB_OVERRIDE {
+ EnsureAudioDecoderCreated();
+ if (!impl_->has_decoder()) {
+ return false; // TODO: Signal an error.
+ }
+
+ const void* data = buff.data();
+ const int size = buff.size();
+ const int64_t media_timestamp = buff.pts();
+
+ // These parameters are used for decryption. But these are not used right
+ // now and so remain empty.
+ std::vector<uint8_t> key_id;
+ std::vector<uint8_t> iv;
+ std::vector<Subsample> subsamples;
+
+ const std::int64_t win32_time_stamp = ConvertToWin32Time(media_timestamp);
+
+ // Adjust the offset for 7 bytes to remove the ADTS header.
+ const uint8_t* audio_start = static_cast<const uint8_t*>(data) + 7;
+ const int audio_size = size - 7;
+
+ const bool write_ok = impl_->TryWriteInputBuffer(
+ audio_start, audio_size, win32_time_stamp, key_id, iv, subsamples);
+ impl_->DeliverOutputOnAllTransforms();
+ return write_ok;
+ }
+
+ void WriteEndOfStream() SB_OVERRIDE {
+ if (impl_->has_decoder()) {
+ impl_->DrainDecoder();
+ impl_->DeliverOutputOnAllTransforms();
+ output_queue_.PushBack(new DecodedAudio);
+ } else {
+ // Don't call DrainDecoder() if input data is never queued.
+ // TODO: Send EOS.
+ }
+ }
+
+ scoped_refptr<DecodedAudio> ProcessAndRead() SB_OVERRIDE {
+ impl_->DeliverOutputOnAllTransforms();
+ scoped_refptr<DecodedAudio> output = output_queue_.PopFront();
+ return output;
+ }
+
+ SbMediaAudioCodec codec_;
+ SbMediaAudioHeader audio_header_;
+ SbMediaAudioSampleType sample_type_;
+ SbMediaAudioFrameStorageType audio_frame_fmt_;
+ scoped_ptr<DecoderImpl> impl_;
+ AtomicQueue<DecodedAudioPtr> output_queue_;
+};
+} // anonymous namespace.
+
+scoped_ptr<AbstractWin32AudioDecoder> AbstractWin32AudioDecoder::Create(
+ SbMediaAudioCodec code,
+ SbMediaAudioFrameStorageType audio_frame_fmt,
+ SbMediaAudioSampleType sample_type,
+ const SbMediaAudioHeader& audio_header) {
+ return scoped_ptr<AbstractWin32AudioDecoder>(new
+ AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
+ audio_header));
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/win32_audio_decoder.h b/src/starboard/shared/win32/win32_audio_decoder.h
new file mode 100644
index 0000000..4cc5f27
--- /dev/null
+++ b/src/starboard/shared/win32/win32_audio_decoder.h
@@ -0,0 +1,59 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_WIN32_AUDIO_DECODER_H_
+#define STARBOARD_SHARED_WIN32_WIN32_AUDIO_DECODER_H_
+
+#include <vector>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// AudioDecoder for Win32.
+class AbstractWin32AudioDecoder {
+ public:
+ static scoped_ptr<AbstractWin32AudioDecoder> Create(
+ SbMediaAudioCodec codec,
+ SbMediaAudioFrameStorageType audio_frame_fmt,
+ SbMediaAudioSampleType sample_type,
+ const SbMediaAudioHeader& audio_header);
+ virtual ~AbstractWin32AudioDecoder() {}
+
+ // INPUT:
+ //
+ // ZACH: Note that this deviates from Xiaoming's AudioDecoder as this does
+ // not have the encrypted parameters. This will be added later.
+ virtual bool TryWrite(const InputBuffer& buff) = 0;
+ virtual void WriteEndOfStream() = 0;
+ // OUTPUT
+ //
+ virtual DecodedAudioPtr ProcessAndRead() = 0;
+
+ private:
+ virtual void EnsureAudioDecoderCreated() = 0;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_WIN32_AUDIO_DECODER_H_
diff --git a/src/starboard/shared/win32/win32_decoder_impl.cc b/src/starboard/shared/win32/win32_decoder_impl.cc
new file mode 100644
index 0000000..b06fcdc
--- /dev/null
+++ b/src/starboard/shared/win32/win32_decoder_impl.cc
@@ -0,0 +1,469 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/win32_decoder_impl.h"
+
+#include <D3D11.h>
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfidl.h>
+#include <wrl/client.h>
+
+#include <algorithm>
+#include <numeric>
+
+#include "starboard/byte_swap.h"
+#include "starboard/common/ref_counted.h"
+#include "starboard/log.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/shared/win32/media_foundation_utils.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+using ::starboard::shared::win32::CheckResult;
+
+namespace {
+
+// TODO: merge all kStreamId's.
+const int kStreamId = 0;
+
+ComPtr<IMFSample> CreateSample(const void* data,
+ int size,
+ int64_t win32_timestamp) {
+ ComPtr<IMFMediaBuffer> buffer;
+ HRESULT hr = MFCreateMemoryBuffer(size, &buffer);
+ CheckResult(hr);
+
+ BYTE* buffer_ptr;
+ hr = buffer->Lock(&buffer_ptr, 0, 0);
+ CheckResult(hr);
+
+ SbMemoryCopy(buffer_ptr, data, size);
+
+ hr = buffer->Unlock();
+ CheckResult(hr);
+
+ hr = buffer->SetCurrentLength(size);
+ CheckResult(hr);
+
+ ComPtr<IMFSample> input;
+ hr = MFCreateSample(&input);
+ CheckResult(hr);
+
+ hr = input->AddBuffer(buffer.Get());
+ CheckResult(hr);
+
+ // sample time is in 100 nanoseconds.
+ input->SetSampleTime(win32_timestamp);
+ return input;
+}
+
+bool TryWriteToMediaProcessor(ComPtr<IMFTransform> media_processor,
+ ComPtr<IMFSample> input,
+ int stream_id) {
+ DWORD flag;
+ HRESULT hr = media_processor->GetInputStatus(stream_id, &flag);
+ CheckResult(hr);
+
+ SB_DCHECK(MFT_INPUT_STATUS_ACCEPT_DATA == flag);
+ hr = media_processor->ProcessInput(kStreamId, input.Get(), 0);
+
+ switch (hr) {
+ case S_OK: { // Data was sent to the media processor.
+ return true;
+ }
+ case MF_E_NOTACCEPTING: {
+ return false;
+ }
+ default: {
+ SB_NOTREACHED() << "Unexpected error.";
+ }
+ }
+ return false;
+}
+
+bool StreamAllocatesMemory(DWORD out_stream_flags) {
+ static const DWORD kFlagAutoAllocateMemory =
+ MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
+ MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES;
+
+ const bool output_stream_allocates_memory =
+ (out_stream_flags & kFlagAutoAllocateMemory) != 0;
+ return output_stream_allocates_memory;
+}
+
+template <typename T>
+void ReleaseIfNotNull(T** ptr) {
+ if (*ptr) {
+ (*ptr)->Release();
+ *ptr = NULL;
+ }
+}
+
+} // namespace
+
+DecoderImpl::DecoderImpl(const std::string& type,
+ MediaBufferConsumerInterface* media_buffer_consumer)
+ : type_(type),
+ media_buffer_consumer_(media_buffer_consumer),
+ discontinuity_(false) {
+ SB_DCHECK(media_buffer_consumer_);
+}
+
+DecoderImpl::~DecoderImpl() {
+}
+
+// static
+ComPtr<IMFTransform> DecoderImpl::CreateDecoder(CLSID clsid) {
+ ComPtr<IMFTransform> decoder;
+ LPVOID* ptr_address = reinterpret_cast<LPVOID*>(decoder.GetAddressOf());
+ HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
+ IID_IMFTransform, ptr_address);
+ CheckResult(hr);
+ return decoder;
+}
+
+void DecoderImpl::ActivateDecryptor(ComPtr<IMFMediaType> input_type) {
+ if (!decryptor_) {
+ return;
+ }
+
+ HRESULT hr = decryptor_->SetInputType(kStreamId, input_type.Get(),
+ 0); // MFT_SET_TYPE_TEST_FLAGS.
+ CheckResult(hr);
+
+ // Ensure that the decryptor and the decoder agrees on the protection of
+ // samples transferred between them.
+ ComPtr<IMFSampleProtection> decryption_sample_protection;
+ ComPtr<IMFSampleProtection> decoder_sample_protection;
+ DWORD decryption_protection_version;
+ DWORD decoder_protection_version;
+ DWORD protection_version;
+ BYTE* cert_data = NULL;
+ DWORD cert_data_size = 0;
+ BYTE* crypt_seed = NULL;
+ DWORD crypt_seed_size = 0;
+ ComPtr<IMFMediaType> decoder_input_type;
+ ComPtr<IMFMediaType> decoder_output_type;
+
+ hr = decryptor_.As(&decryption_sample_protection);
+ CheckResult(hr);
+ hr = decryption_sample_protection->GetOutputProtectionVersion(
+ &decryption_protection_version);
+ CheckResult(hr);
+ hr = decoder_.As(&decoder_sample_protection);
+ CheckResult(hr);
+ hr = decoder_sample_protection->GetInputProtectionVersion(
+ &decoder_protection_version);
+ CheckResult(hr);
+ protection_version =
+ std::min(decoder_protection_version, decryption_protection_version);
+ if (protection_version < SAMPLE_PROTECTION_VERSION_RC4) {
+ SB_NOTREACHED();
+ }
+
+ hr = decoder_sample_protection->GetProtectionCertificate(
+ protection_version,
+ &cert_data, &cert_data_size);
+ CheckResult(hr);
+
+ hr = decryption_sample_protection->InitOutputProtection(
+ protection_version, 0, cert_data, cert_data_size,
+ &crypt_seed, &crypt_seed_size);
+ CheckResult(hr);
+
+ hr = decoder_sample_protection->InitInputProtection(
+ protection_version, 0,
+ crypt_seed, crypt_seed_size);
+ CheckResult(hr);
+
+ CoTaskMemFree(cert_data);
+ CoTaskMemFree(crypt_seed);
+
+ // Ensure that the input type of the decoder is the output type of the
+ // decryptor.
+ hr = decryptor_->GetOutputAvailableType(
+ kStreamId,
+ 0, // Type Index
+ decoder_input_type.ReleaseAndGetAddressOf());
+ CheckResult(hr);
+ hr = decryptor_->SetOutputType(kStreamId, decoder_input_type.Get(),
+ 0); // _MFT_SET_TYPE_FLAGS
+ CheckResult(hr);
+ hr = decoder_->SetInputType(kStreamId, decoder_input_type.Get(),
+ 0); // _MFT_SET_TYPE_FLAGS
+ CheckResult(hr);
+
+ // Start the decryptor, note that this should be better abstracted.
+ SendMFTMessage(decryptor_.Get(), MFT_MESSAGE_NOTIFY_BEGIN_STREAMING);
+ SendMFTMessage(decryptor_.Get(), MFT_MESSAGE_NOTIFY_START_OF_STREAM);
+}
+
+bool DecoderImpl::TryWriteInputBuffer(
+ const void* data,
+ int size,
+ std::int64_t win32_timestamp,
+ const std::vector<std::uint8_t>& key_id,
+ const std::vector<std::uint8_t>& iv,
+ const std::vector<Subsample>& subsamples) {
+ // MFSampleExtension_CleanPoint is a key-frame for the video + audio. It is
+ // not set here because the win32 system is smart enough to figure this out.
+ // It will probably be totally ok to not set this at all. Resolution: If
+ // there are problems with win32 video decoding, come back to this and see
+ // if setting this will fix it. THis will be used if
+ // SbMediaVideoSampleInfo::is_key_frame is true inside of the this function
+ // (which will receive an InputBuffer).
+ // Set MFSampleExtension_CleanPoint attributes.
+ SB_DCHECK(decoder_);
+ ComPtr<IMFSample> input = CreateSample(data, size, win32_timestamp);
+ SB_DCHECK(decoder_);
+
+ // Has to check both as sometimes the sample can contain an invalid key id.
+ // Better check the key id size is 16 and iv size is 8 or 16.
+ bool encrypted = !key_id.empty() && !iv.empty();
+ if (encrypted) {
+ size_t iv_size = iv.size();
+ const char kEightZeros[8] = {0};
+ if (iv_size == 16 && SbMemoryCompare(iv.data() + 8, kEightZeros, 8) == 0) {
+ // For iv that is 16 bytes long but the the last 8 bytes is 0, we treat
+ // it as an 8 bytes iv.
+ iv_size = 8;
+ }
+ input->SetBlob(MFSampleExtension_Encryption_SampleID,
+ reinterpret_cast<const UINT8*>(iv.data()),
+ static_cast<UINT32>(iv_size));
+ SB_DCHECK(key_id.size() == sizeof(GUID));
+ GUID guid = *reinterpret_cast<const GUID*>(key_id.data());
+
+ guid.Data1 = SbByteSwapU32(guid.Data1);
+ guid.Data2 = SbByteSwapU16(guid.Data2);
+ guid.Data3 = SbByteSwapU16(guid.Data3);
+
+ input->SetGUID(MFSampleExtension_Content_KeyID, guid);
+
+ std::vector<DWORD> subsamples_data;
+ if (!subsamples.empty()) {
+ for (auto& subsample : subsamples) {
+ subsamples_data.push_back(subsample.clear_bytes);
+ subsamples_data.push_back(subsample.encrypted_bytes);
+ }
+ SB_DCHECK(std::accumulate(subsamples_data.begin(), subsamples_data.end(),
+ 0) == size);
+ } else {
+ subsamples_data.push_back(0);
+ subsamples_data.push_back(size);
+ }
+ input->SetBlob(MFSampleExtension_Encryption_SubSampleMappingSplit,
+ reinterpret_cast<UINT8*>(&subsamples_data[0]),
+ static_cast<UINT32>(subsamples_data.size() * sizeof(DWORD)));
+ }
+
+ ComPtr<IMFTransform> media_processor = encrypted ? decryptor_ : decoder_;
+ const bool write_ok =
+ TryWriteToMediaProcessor(media_processor, input, kStreamId);
+ return write_ok;
+}
+
+void DecoderImpl::DrainDecoder() {
+ // This is used during EOS to get the last few frames.
+ SB_DCHECK(decoder_);
+ SendMFTMessage(decoder_.Get(), MFT_MESSAGE_NOTIFY_END_OF_STREAM);
+ SendMFTMessage(decoder_.Get(), MFT_MESSAGE_COMMAND_DRAIN);
+}
+
+bool DecoderImpl::DeliverOutputOnAllTransforms() {
+ SB_DCHECK(decoder_);
+ bool delivered = false;
+ if (decryptor_) {
+ while (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decryptor_)) {
+ HRESULT hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+ if (hr == MF_E_NOTACCEPTING) {
+ // The protocol says that when ProcessInput() returns MF_E_NOTACCEPTING,
+ // there must be some output available. Retrieve the output and the next
+ // ProcessInput() should succeed.
+ while (ComPtr<IMFSample> sample_inner =
+ DeliverOutputOnTransform(decoder_)) {
+ DeliverDecodedSample(sample_inner);
+ delivered = true;
+ }
+ hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+ }
+ CheckResult(hr);
+ delivered = true;
+ }
+ }
+ while (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decoder_)) {
+ DeliverDecodedSample(sample);
+ delivered = true;
+ }
+ return delivered;
+}
+
+bool DecoderImpl::DeliverOneOutputOnAllTransforms() {
+ SB_DCHECK(decoder_);
+ bool delivered = false;
+ if (decryptor_) {
+ if (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decryptor_)) {
+ HRESULT hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+ if (hr == MF_E_NOTACCEPTING) {
+ // The protocol says that when ProcessInput() returns MF_E_NOTACCEPTING,
+ // there must be some output available. Retrieve the output and the next
+ // ProcessInput() should succeed.
+ ComPtr<IMFSample> sample_inner = DeliverOutputOnTransform(decoder_);
+ if (sample_inner) {
+ DeliverDecodedSample(sample_inner);
+ delivered = true;
+ }
+ hr = decoder_->ProcessInput(kStreamId, sample.Get(), 0);
+ CheckResult(hr);
+ return delivered;
+ }
+ CheckResult(hr);
+ delivered = true;
+ }
+ }
+ if (ComPtr<IMFSample> sample = DeliverOutputOnTransform(decoder_)) {
+ DeliverDecodedSample(sample);
+ delivered = true;
+ }
+ return delivered;
+}
+
+void DecoderImpl::set_decoder(Microsoft::WRL::ComPtr<IMFTransform> decoder) {
+ // Either new object is null or the old one is.
+ SB_DCHECK(!decoder || !decoder_);
+ decoder_ = decoder;
+}
+
+void DecoderImpl::SendMFTMessage(IMFTransform* transform,
+ MFT_MESSAGE_TYPE msg) {
+ if (!transform) {
+ return;
+ }
+ ULONG_PTR data = 0;
+ HRESULT hr = transform->ProcessMessage(msg, data);
+ CheckResult(hr);
+}
+
+void DecoderImpl::PrepareOutputDataBuffer(
+ ComPtr<IMFTransform> transform,
+ MFT_OUTPUT_DATA_BUFFER* output_data_buffer) {
+ output_data_buffer->dwStreamID = kStreamId;
+ output_data_buffer->pSample = NULL;
+ output_data_buffer->dwStatus = 0;
+ output_data_buffer->pEvents = NULL;
+
+ MFT_OUTPUT_STREAM_INFO output_stream_info;
+ HRESULT hr = transform->GetOutputStreamInfo(kStreamId, &output_stream_info);
+ CheckResult(hr);
+
+ // Each media sample (IMFSample interface) of output data from the MFT
+ // contains complete, unbroken units of data. The definition of a unit
+ // of data depends on the media type: For uncompressed video, a video
+ // frame; for compressed data, a compressed packet; for uncompressed audio,
+ // a single audio frame.
+ //
+ // For uncompressed audio formats, this flag is always implied. (It is valid
+ // to set the flag, but not required.) An uncompressed audio frame should
+ // never span more than one media sample.
+ SB_DCHECK((output_stream_info.dwFlags & MFT_OUTPUT_STREAM_WHOLE_SAMPLES) !=
+ 0);
+
+ if (StreamAllocatesMemory(output_stream_info.dwFlags)) {
+ // Try to let the IMFTransform allocate the memory if possible.
+ return;
+ }
+
+ ComPtr<IMFSample> sample;
+ hr = MFCreateSample(&sample);
+ CheckResult(hr);
+
+ ComPtr<IMFMediaBuffer> buffer;
+ hr = MFCreateAlignedMemoryBuffer(output_stream_info.cbSize,
+ output_stream_info.cbAlignment, &buffer);
+
+ CheckResult(hr);
+
+ hr = sample->AddBuffer(buffer.Get());
+ CheckResult(hr);
+
+ output_data_buffer->pSample = sample.Detach();
+}
+
+ComPtr<IMFSample> DecoderImpl::DeliverOutputOnTransform(
+ ComPtr<IMFTransform> transform) {
+ MFT_OUTPUT_DATA_BUFFER output_data_buffer;
+ PrepareOutputDataBuffer(transform, &output_data_buffer);
+
+ const DWORD kFlags = 0;
+ const DWORD kNumberOfBuffers = 1;
+ DWORD status = 0;
+ HRESULT hr = transform->ProcessOutput(kFlags, kNumberOfBuffers,
+ &output_data_buffer, &status);
+
+ SB_DCHECK(!output_data_buffer.pEvents);
+
+ ComPtr<IMFSample> output = output_data_buffer.pSample;
+ ReleaseIfNotNull(&output_data_buffer.pEvents);
+ ReleaseIfNotNull(&output_data_buffer.pSample);
+
+ if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+ ComPtr<IMFMediaType> media_type;
+
+ hr = transform->GetOutputAvailableType(kStreamId,
+ 0, // TypeIndex
+ &media_type);
+ CheckResult(hr);
+
+ hr = transform->SetOutputType(kStreamId, media_type.Get(), 0);
+ CheckResult(hr);
+ return NULL;
+ } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT ||
+ output_data_buffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) {
+ return NULL;
+ }
+
+ SB_DCHECK(output);
+ if (discontinuity_) {
+ output->SetUINT32(MFSampleExtension_Discontinuity, TRUE);
+ discontinuity_ = false;
+ }
+
+ CheckResult(hr);
+ return output;
+}
+
+void DecoderImpl::DeliverDecodedSample(ComPtr<IMFSample> sample) {
+ DWORD buff_count = 0;
+ HRESULT hr = sample->GetBufferCount(&buff_count);
+ CheckResult(hr);
+ SB_DCHECK(buff_count == 1);
+
+ ComPtr<IMFMediaBuffer> media_buffer;
+ hr = sample->GetBufferByIndex(0, &media_buffer);
+
+ if (SUCCEEDED(hr)) {
+ LONGLONG win32_timestamp = 0;
+ hr = sample->GetSampleTime(&win32_timestamp);
+ CheckResult(hr);
+ media_buffer_consumer_->Consume(media_buffer, win32_timestamp);
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/win32_decoder_impl.h b/src/starboard/shared/win32/win32_decoder_impl.h
new file mode 100644
index 0000000..38374ff
--- /dev/null
+++ b/src/starboard/shared/win32/win32_decoder_impl.h
@@ -0,0 +1,97 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_WIN32_DECODER_IMPL_H_
+#define STARBOARD_SHARED_WIN32_WIN32_DECODER_IMPL_H_
+
+#include <D3D11.h>
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfidl.h>
+#include <wrl\client.h>
+
+#include <string>
+#include <vector>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class MediaBufferConsumerInterface {
+ public:
+ virtual void Consume(Microsoft::WRL::ComPtr<IMFMediaBuffer> media_buffer,
+ int64_t win32_timestamp) = 0;
+
+ protected:
+ ~MediaBufferConsumerInterface() {}
+};
+
+struct Subsample {
+ uint32_t clear_bytes;
+ uint32_t encrypted_bytes;
+};
+
+class DecoderImpl {
+ public:
+ DecoderImpl(const std::string& type,
+ MediaBufferConsumerInterface* media_buffer_consumer);
+ ~DecoderImpl();
+
+ static Microsoft::WRL::ComPtr<IMFTransform> CreateDecoder(CLSID clsid);
+
+ void ActivateDecryptor(Microsoft::WRL::ComPtr<IMFMediaType> input_type);
+ bool TryWriteInputBuffer(const void* data,
+ int size,
+ std::int64_t win32_timestamp,
+ const std::vector<uint8_t>& key_id,
+ const std::vector<uint8_t>& iv,
+ const std::vector<Subsample>& subsamples);
+
+ void DrainDecoder();
+
+ bool DeliverOutputOnAllTransforms();
+ bool DeliverOneOutputOnAllTransforms();
+
+ void set_decoder(Microsoft::WRL::ComPtr<IMFTransform> decoder);
+
+ bool has_decoder() const { return decoder_.Get(); }
+
+ private:
+ static void SendMFTMessage(IMFTransform* transform, MFT_MESSAGE_TYPE msg);
+
+ void PrepareOutputDataBuffer(Microsoft::WRL::ComPtr<IMFTransform> transform,
+ MFT_OUTPUT_DATA_BUFFER* output_data_buffer);
+ Microsoft::WRL::ComPtr<IMFSample> DeliverOutputOnTransform(
+ Microsoft::WRL::ComPtr<IMFTransform> transform);
+ void DeliverDecodedSample(Microsoft::WRL::ComPtr<IMFSample> sample);
+
+ const std::string type_; // For debugging purpose.
+ // This is the target for the all completed media buffers.
+ MediaBufferConsumerInterface* media_buffer_consumer_;
+ bool discontinuity_;
+
+ Microsoft::WRL::ComPtr<IMFTransform> decryptor_;
+ Microsoft::WRL::ComPtr<IMFTransform> decoder_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_WIN32_DECODER_IMPL_H_
diff --git a/src/starboard/shared/win32/win32_video_decoder.cc b/src/starboard/shared/win32/win32_video_decoder.cc
new file mode 100644
index 0000000..93bc487
--- /dev/null
+++ b/src/starboard/shared/win32/win32_video_decoder.cc
@@ -0,0 +1,284 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/win32_video_decoder.h"
+
+#include <Codecapi.h>
+#include <utility>
+
+#include "starboard/shared/win32/atomic_queue.h"
+#include "starboard/shared/win32/dx_context_video_decoder.h"
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/shared/win32/media_foundation_utils.h"
+#include "starboard/shared/win32/win32_decoder_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+using Microsoft::WRL::ComPtr;
+using ::starboard::shared::win32::CheckResult;
+
+namespace {
+
+// This magic number is taken directly from sample from MS. Can be further
+// tuned in case if there is playback issues.
+const int kSampleAllocatorFramesMax = 5;
+
+// CLSID_CMSVideoDecoderMFT {62CE7E72-4C71-4D20-B15D-452831A87D9D}
+const GUID CLSID_VideoDecoder = {0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45,
+ 0x28, 0x31, 0xA8, 0x7D, 0x9D};
+
+ComPtr<ID3D11Texture2D> GetTexture2D(ComPtr<IMFMediaBuffer> media_buffer) {
+ ComPtr<IMFDXGIBuffer> dxgi_buffer;
+ HRESULT hr = media_buffer.As(&dxgi_buffer);
+ CheckResult(hr);
+ SB_DCHECK(dxgi_buffer.Get());
+
+ ComPtr<ID3D11Texture2D> dx_texture;
+ hr = dxgi_buffer->GetResource(IID_PPV_ARGS(&dx_texture));
+ CheckResult(hr);
+ return dx_texture;
+}
+
+class VideoFrameFactory {
+ public:
+ static VideoFramePtr Construct(SbMediaTime timestamp,
+ ComPtr<IMFMediaBuffer> media_buffer) {
+ ComPtr<ID3D11Texture2D> texture = GetTexture2D(media_buffer);
+ D3D11_TEXTURE2D_DESC tex_desc;
+ texture->GetDesc(&tex_desc);
+ VideoFramePtr out(new VideoFrame(tex_desc.Width, tex_desc.Height,
+ timestamp, media_buffer.Detach(),
+ nullptr, DeleteTextureFn));
+ frames_in_flight_.increment();
+ return out;
+ }
+ static int frames_in_flight() { return frames_in_flight_.load(); }
+
+ private:
+ static void DeleteTextureFn(void* context, void* texture) {
+ SB_UNREFERENCED_PARAMETER(context);
+ IMFMediaBuffer* data_ptr = static_cast<IMFMediaBuffer*>(texture);
+ data_ptr->Release();
+ frames_in_flight_.decrement();
+ }
+
+ static atomic_int32_t frames_in_flight_;
+};
+
+atomic_int32_t VideoFrameFactory::frames_in_flight_;
+
+class AbstractWin32VideoDecoderImpl : public AbstractWin32VideoDecoder,
+ public MediaBufferConsumerInterface {
+ public:
+ explicit AbstractWin32VideoDecoderImpl(SbMediaVideoCodec codec)
+ : codec_(codec), surface_width_(0), surface_height_(0) {
+ MediaBufferConsumerInterface* media_cb = this;
+ impl_.reset(new DecoderImpl("video", media_cb));
+ EnsureVideoDecoderCreated();
+ }
+
+ virtual void Consume(ComPtr<IMFMediaBuffer> media_buffer,
+ int64_t win32_timestamp) {
+ const SbMediaTime media_timestamp = ConvertToMediaTime(win32_timestamp);
+ VideoFramePtr frame_output =
+ VideoFrameFactory::Construct(media_timestamp, media_buffer);
+ output_queue_.PushBack(frame_output);
+ }
+
+ ComPtr<IMFMediaType> Configure(IMFTransform* decoder) {
+ ComPtr<IMFMediaType> input_type;
+ HRESULT hr = MFCreateMediaType(&input_type);
+ SB_DCHECK(SUCCEEDED(hr));
+
+ hr = input_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ SB_DCHECK(SUCCEEDED(hr));
+
+ GUID selected_guid = {};
+
+ // If this is updated then media_is_video_supported.cc also needs to be
+ // updated.
+ switch (codec_) {
+ case kSbMediaVideoCodecH264: {
+ selected_guid = MFVideoFormat_H264;
+ break;
+ }
+ case kSbMediaVideoCodecH265: {
+ selected_guid = MFVideoFormat_H265;
+ break;
+ }
+ case kSbMediaVideoCodecVp9: {
+ selected_guid = MFVideoFormat_VP90;
+ break;
+ }
+ default: { SB_NOTREACHED(); }
+ }
+
+ hr = input_type->SetGUID(MF_MT_SUBTYPE, selected_guid);
+ SB_DCHECK(SUCCEEDED(hr));
+ hr = decoder->SetInputType(0, input_type.Get(), 0);
+ return input_type;
+ }
+
+ void EnsureVideoDecoderCreated() SB_OVERRIDE {
+ if (impl_->has_decoder()) {
+ return;
+ }
+
+ ComPtr<IMFTransform> decoder =
+ DecoderImpl::CreateDecoder(CLSID_VideoDecoder);
+
+ ComPtr<IMFMediaType> media_type = Configure(decoder.Get());
+
+ SB_DCHECK(decoder);
+
+ impl_->set_decoder(decoder);
+
+ dx_decoder_ctx_ = GetDirectXForHardwareDecoding();
+ SB_UNREFERENCED_PARAMETER(dx_decoder_ctx_);
+
+ HRESULT hr = S_OK;
+
+ DWORD input_stream_count = 0;
+ DWORD output_stream_count = 0;
+ hr = decoder->GetStreamCount(&input_stream_count, &output_stream_count);
+ CheckResult(hr);
+
+ SB_DCHECK(1 == input_stream_count);
+ SB_DCHECK(1 == output_stream_count);
+
+ impl_->ActivateDecryptor(media_type);
+
+ ComPtr<IMFMediaType> output_type =
+ FindMediaType(MFVideoFormat_YV12, decoder.Get());
+
+ SB_DCHECK(output_type);
+
+ hr = decoder->SetOutputType(0, output_type.Get(), 0);
+ CheckResult(hr);
+
+ ComPtr<IMFAttributes> attributes;
+ hr = decoder->GetAttributes(attributes.GetAddressOf());
+ CheckResult(hr);
+
+ UINT32 value = 0;
+ hr = attributes->GetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &value);
+ SB_DCHECK(hr == S_OK || hr == MF_E_ATTRIBUTENOTFOUND);
+
+ // TODO: handle the MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE correctly.
+ // SB_DCHECK(value == TRUE);
+
+ // Enables DirectX video acceleration for video decoding.
+ hr = decoder->ProcessMessage(
+ MFT_MESSAGE_SET_D3D_MANAGER,
+ ULONG_PTR(dx_decoder_ctx_.dxgi_device_manager_out.Get()));
+ CheckResult(hr);
+
+ // Only allowed to set once.
+ SB_DCHECK(0 == surface_width_);
+ SB_DCHECK(0 == surface_height_);
+
+ hr = MFGetAttributeSize(output_type.Get(), MF_MT_FRAME_SIZE,
+ &surface_width_, &surface_height_);
+
+ if (FAILED(hr)) {
+ SB_NOTREACHED() << "could not get width & height, hr = " << hr;
+ return;
+ }
+
+ ComPtr<IMFAttributes> output_attributes;
+ hr = decoder->GetOutputStreamAttributes(0, &output_attributes);
+ SB_DCHECK(SUCCEEDED(hr));
+ // The decoder must output textures that are bound to shader resources,
+ // or we can't draw them later via ANGLE.
+ hr = output_attributes->SetUINT32(
+ MF_SA_D3D11_BINDFLAGS, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DECODER);
+ SB_DCHECK(SUCCEEDED(hr));
+
+ decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+ }
+
+ bool TryWrite(const scoped_refptr<InputBuffer>& buff) {
+ std::pair<int, int> width_height(buff->video_sample_info()->frame_width,
+ buff->video_sample_info()->frame_height);
+ EnsureVideoDecoderCreated();
+ if (!impl_->has_decoder()) {
+ SB_NOTREACHED();
+ return false; // TODO: Signal an error.
+ }
+
+ if (!output_queue_.IsEmpty()) {
+ return false; // Wait for more data.
+ }
+
+ // This would be used for decrypting content.
+ // For now this is empty.
+ std::vector<uint8_t> key_id;
+ std::vector<uint8_t> iv;
+
+ std::vector<Subsample> empty_subsample;
+
+ const SbMediaTime media_timestamp = buff->pts();
+ const int64_t win32_timestamp = ConvertToWin32Time(media_timestamp);
+
+ const bool write_ok = impl_->TryWriteInputBuffer(
+ buff->data(), buff->size(), win32_timestamp,
+ key_id, iv, empty_subsample);
+
+ return write_ok;
+ }
+
+ void WriteEndOfStream() SB_OVERRIDE {
+ if (impl_->has_decoder()) {
+ impl_->DrainDecoder();
+ while (VideoFrameFactory::frames_in_flight() <
+ kSampleAllocatorFramesMax &&
+ impl_->DeliverOneOutputOnAllTransforms()) {
+ }
+ } else {
+ // Don't call DrainDecoder() if input data is never queued.
+ // TODO: Send EOS.
+ }
+ }
+
+ VideoFramePtr ProcessAndRead() SB_OVERRIDE {
+ if (VideoFrameFactory::frames_in_flight() < kSampleAllocatorFramesMax) {
+ impl_->DeliverOneOutputOnAllTransforms();
+ }
+ VideoFramePtr output = output_queue_.PopFront();
+ return output;
+ }
+
+ AtomicQueue<VideoFramePtr> output_queue_;
+ SbMediaVideoCodec codec_;
+ scoped_ptr<DecoderImpl> impl_;
+
+ uint32_t surface_width_;
+ uint32_t surface_height_;
+ HardwareDecoderContext dx_decoder_ctx_;
+};
+} // anonymous namespace.
+
+scoped_ptr<AbstractWin32VideoDecoder> AbstractWin32VideoDecoder::Create(
+ SbMediaVideoCodec codec) {
+ return scoped_ptr<AbstractWin32VideoDecoder>(
+ new AbstractWin32VideoDecoderImpl(codec));
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/win32_video_decoder.h b/src/starboard/shared/win32/win32_video_decoder.h
new file mode 100644
index 0000000..84ff7b7
--- /dev/null
+++ b/src/starboard/shared/win32/win32_video_decoder.h
@@ -0,0 +1,51 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_WIN32_VIDEO_DECODER_H_
+#define STARBOARD_SHARED_WIN32_WIN32_VIDEO_DECODER_H_
+
+#include <deque>
+#include <vector>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/shared/win32/media_common.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// VideoDecoder for Win32.
+class AbstractWin32VideoDecoder {
+ public:
+ static scoped_ptr<AbstractWin32VideoDecoder> Create(SbMediaVideoCodec codec);
+ virtual ~AbstractWin32VideoDecoder() {}
+
+ virtual bool TryWrite(const scoped_refptr<InputBuffer>& buff) = 0;
+ virtual void WriteEndOfStream() = 0;
+ virtual VideoFramePtr ProcessAndRead() = 0;
+
+ private:
+ virtual void EnsureVideoDecoderCreated() = 0;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_WIN32_VIDEO_DECODER_H_
diff --git a/src/starboard/win/console/atomic_public.h b/src/starboard/shared/win32/window_create.cc
similarity index 69%
copy from src/starboard/win/console/atomic_public.h
copy to src/starboard/shared/win32/window_create.cc
index 51f81a1..2f0cbd2 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/starboard/shared/win32/window_create.cc
@@ -12,9 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+#include "starboard/window.h"
-#include "starboard/shared/win32/atomic_public.h"
+#include "starboard/shared/win32/application_win32.h"
-#endif // STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+using ::starboard::shared::win32::ApplicationWin32;
+
+SbWindow SbWindowCreate(const SbWindowOptions* options) {
+ return ApplicationWin32::Get()->CreateWindowForWin32(options);
+}
diff --git a/src/starboard/win/console/atomic_public.h b/src/starboard/shared/win32/window_destroy.cc
similarity index 71%
copy from src/starboard/win/console/atomic_public.h
copy to src/starboard/shared/win32/window_destroy.cc
index 51f81a1..6de112a 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/starboard/shared/win32/window_destroy.cc
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+#include "starboard/shared/win32/application_win32.h"
+#include "starboard/window.h"
-#include "starboard/shared/win32/atomic_public.h"
+using ::starboard::shared::win32::ApplicationWin32;
-#endif // STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+bool SbWindowDestroy(SbWindow window) {
+ return ApplicationWin32::Get()->DestroyWindow(window);
+}
diff --git a/src/starboard/win/console/atomic_public.h b/src/starboard/shared/win32/window_get_platform_handle.cc
similarity index 67%
copy from src/starboard/win/console/atomic_public.h
copy to src/starboard/shared/win32/window_get_platform_handle.cc
index 51f81a1..e0f5919 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/starboard/shared/win32/window_get_platform_handle.cc
@@ -12,9 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+#include <EGL/egl.h>
-#include "starboard/shared/win32/atomic_public.h"
+#include "starboard/shared/win32/window_internal.h"
+#include "starboard/window.h"
-#endif // STARBOARD_WIN_CONSOLE_ATOMIC_PUBLIC_H_
+void* SbWindowGetPlatformHandle(SbWindow window) {
+ if (!SbWindowIsValid(window)) {
+ return NULL;
+ }
+
+ return reinterpret_cast<EGLNativeWindowType>(window->GetWindowHandle());
+}
diff --git a/src/starboard/shared/win32/window_get_size.cc b/src/starboard/shared/win32/window_get_size.cc
new file mode 100644
index 0000000..f100240
--- /dev/null
+++ b/src/starboard/shared/win32/window_get_size.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/window_internal.h"
+#include "starboard/window.h"
+
+bool SbWindowGetSize(SbWindow window, SbWindowSize* size) {
+ if (!SbWindowIsValid(window)) {
+ SB_LOG(ERROR) << __FUNCTION__ << ": Invalid window.";
+ return false;
+ }
+
+ size->width = window->width;
+ size->height = window->height;
+ // The video resolution is the same as the graphics resolution.
+ size->video_pixel_ratio = 1.0f;
+ return true;
+}
diff --git a/src/starboard/shared/win32/window_internal.cc b/src/starboard/shared/win32/window_internal.cc
new file mode 100644
index 0000000..94d63e1
--- /dev/null
+++ b/src/starboard/shared/win32/window_internal.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/window_internal.h"
+
+// TODO: Make sure the width and height here behave well given that we want
+// 1080 video, but perhaps 4k UI where applicable.
+SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options,
+ HWND window_handle)
+ : width(options->size.width),
+ height(options->size.height),
+ window_handle_(window_handle) {}
+
+SbWindowPrivate::~SbWindowPrivate() {}
diff --git a/src/starboard/shared/win32/window_internal.h b/src/starboard/shared/win32/window_internal.h
new file mode 100644
index 0000000..be8a308
--- /dev/null
+++ b/src/starboard/shared/win32/window_internal.h
@@ -0,0 +1,43 @@
+// Copyright 2017 Google Inc. 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_SHARED_WIN32_WINDOW_INTERNAL_H_
+#define STARBOARD_SHARED_WIN32_WINDOW_INTERNAL_H_
+
+#include <EGL/egl.h>
+
+// Windows headers.
+#include <windows.h>
+
+#include "starboard/atomic.h"
+#include "starboard/time.h"
+#include "starboard/window.h"
+
+struct SbWindowPrivate {
+ SbWindowPrivate(const SbWindowOptions* options, HWND window_handle);
+ ~SbWindowPrivate();
+
+ HWND GetWindowHandle() { return window_handle_; }
+
+ // The width of this window.
+ int width;
+
+ // The height of this window.
+ int height;
+
+ private:
+ HWND window_handle_;
+};
+
+#endif // STARBOARD_SHARED_WIN32_WINDOW_INTERNAL_H_
diff --git a/src/starboard/win/console/atomic_public.h b/src/starboard/shared/win32/window_set_default_options.cc
similarity index 69%
copy from src/starboard/win/console/atomic_public.h
copy to src/starboard/shared/win32/window_set_default_options.cc
index 51f81a1..d989f52 100644
--- a/src/starboard/win/console/atomic_public.h
+++ b/src/starboard/shared/win32/win