Import Cobalt 10.52708
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md new file mode 100644 index 0000000..085b9a2 --- /dev/null +++ b/src/cobalt/CHANGELOG.md
@@ -0,0 +1,11 @@ +# Cobalt Version Changelog + +This document records all notable changes made to Cobalt since the last release. + +## Version 10 + +### Dummy example change +Here we would put a description of a notable change that was made in Cobalt 10 +but did not make it into Cobalt 9. This is currently the only item in a list +that will expand to multiple items as we work on Cobalt. When the first +legitimate change is documented here, it should replace this example change.
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc index c06e9fb..5d740ab 100644 --- a/src/cobalt/audio/audio_device.cc +++ b/src/cobalt/audio/audio_device.cc
@@ -45,7 +45,7 @@ namespace { const int kRenderBufferSizeFrames = 1024; -const int kFramesPerChannel = kRenderBufferSizeFrames * 4; +const int kFramesPerChannel = kRenderBufferSizeFrames * 8; const int kStandardOutputSampleRate = 48000; } // namespace
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc index 108115a..0bd7afe 100644 --- a/src/cobalt/browser/application.cc +++ b/src/cobalt/browser/application.cc
@@ -52,7 +52,6 @@ #include "lbshell/src/lb_memory_pages.h" #endif // defined(__LB_SHELL__) #if defined(OS_STARBOARD) -#include "nb/lexical_cast.h" #include "starboard/configuration.h" #include "starboard/log.h" #endif // defined(OS_STARBOARD) @@ -269,31 +268,6 @@ &options->scratch_surface_cache_size_in_bytes); } -void ApplyCommandLineSettingsToWebModuleOptions(WebModule::Options* options) { - SetIntegerIfSwitchIsSet(browser::switches::kRemoteTypefaceCacheSizeInBytes, - &options->remote_typeface_cache_capacity); -} - -template <typename T> -base::optional<T> ParseSetting(const CommandLine* command_line, - const char* switch_name) { - base::optional<T> output; - if (!command_line->HasSwitch(switch_name)) { - return output; - } - std::string switch_value = command_line->GetSwitchValueNative(switch_name); - - bool parse_ok = false; - T value = nb::lexical_cast<T>(switch_value.c_str(), &parse_ok); - - if (parse_ok) { - output = static_cast<T>(value); - } else { - LOG(ERROR) << "Invalid value for command line setting: " << switch_name; - } - return output; -} - // Restrict navigation to a couple of whitelisted URLs by default. const char kYouTubeTvLocationPolicy[] = "h5vcc-location-src " @@ -373,6 +347,10 @@ math::Size(skia_glyph_atlas_texture_dimensions.width(), skia_glyph_atlas_texture_dimensions.height()); + options->web_module_options.remote_typeface_cache_capacity = + static_cast<int>( + auto_mem.remote_typeface_cache_size_in_bytes()->value()); + options->web_module_options.javascript_options.gc_threshold_bytes = static_cast<size_t>(auto_mem.javascript_gc_threshold_in_bytes()->value()); @@ -436,7 +414,6 @@ options.network_module_options.preferred_language = language; ApplyCommandLineSettingsToRendererOptions(&options.renderer_module_options); - ApplyCommandLineSettingsToWebModuleOptions(&options.web_module_options); if (command_line->HasSwitch(browser::switches::kDisableJavaScriptJit)) { options.web_module_options.javascript_options.disable_jit = true;
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp index 9ec2956..3e5aad6 100644 --- a/src/cobalt/browser/browser.gyp +++ b/src/cobalt/browser/browser.gyp
@@ -53,8 +53,14 @@ 'memory_tracker/tool/leak_finder_tool.h', 'memory_tracker/tool/log_writer_tool.cc', 'memory_tracker/tool/log_writer_tool.h', + 'memory_tracker/tool/memory_size_binner_tool.cc', + 'memory_tracker/tool/memory_size_binner_tool.h', 'memory_tracker/tool/params.cc', 'memory_tracker/tool/params.h', + 'memory_tracker/tool/print_csv_tool.cc', + 'memory_tracker/tool/print_csv_tool.h', + 'memory_tracker/tool/print_tool.cc', + 'memory_tracker/tool/print_tool.h', 'memory_tracker/tool/tool_impl.cc', 'memory_tracker/tool/tool_impl.h', 'memory_tracker/tool/tool_thread.cc', @@ -89,6 +95,8 @@ 'COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO=<(image_cache_capacity_multiplier_when_playing_video)', 'COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES=<(software_surface_cache_size_in_bytes)', 'COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES=<(mozjs_garbage_collection_threshold_in_bytes)', + 'COBALT_MAX_CPU_USAGE_IN_BYTES=<(max_cobalt_cpu_usage)', + 'COBALT_MAX_GPU_USAGE_IN_BYTES=<(max_cobalt_gpu_usage)', ], 'dependencies': [ '<@(cobalt_platform_dependencies)',
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc index a3df89c..71a4570 100644 --- a/src/cobalt/browser/memory_settings/auto_mem.cc +++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -27,24 +27,16 @@ #include "cobalt/browser/memory_settings/build_settings.h" #include "cobalt/browser/memory_settings/calculations.h" #include "cobalt/browser/memory_settings/constants.h" +#include "cobalt/browser/memory_settings/memory_settings.h" #include "cobalt/browser/memory_settings/pretty_print.h" #include "cobalt/browser/switches.h" +#include "nb/lexical_cast.h" namespace cobalt { namespace browser { namespace memory_settings { namespace { -int64_t GetTotalCpuMemory() { return SbSystemGetTotalCPUMemory(); } - -base::optional<int64_t> GetTotalGpuMemory() { - base::optional<int64_t> total_gpu_memory; - if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) { - total_gpu_memory = SbSystemGetTotalGPUMemory(); - } - return total_gpu_memory; -} - // Determines if the string value signals "autoset". bool StringValueSignalsAutoset(const std::string& value) { std::string value_lower_case = value; @@ -92,6 +84,35 @@ return output.Pass(); } +scoped_ptr<IntSetting> CreateSystemMemorySetting( + const char* setting_name, + MemorySetting::MemoryType memory_type, + const CommandLine& command_line, + const base::optional<int64_t>& build_setting, + const base::optional<int64_t>& starboard_value) { + scoped_ptr<IntSetting> setting(new IntSetting(setting_name)); + setting->set_memory_type(memory_type); + if (command_line.HasSwitch(setting_name)) { + const std::string value = command_line.GetSwitchValueNative(setting_name); + if (setting->TryParseValue(MemorySetting::kCmdLine, value)) { + return setting.Pass(); + } + } + + if (build_setting) { + setting->set_value(MemorySetting::kBuildSetting, *build_setting); + return setting.Pass(); + } + + if (starboard_value) { + setting->set_value(MemorySetting::kStarboardAPI, *starboard_value); + return setting.Pass(); + } + + setting->set_value(MemorySetting::kStarboardAPI, -1); + return setting.Pass(); +} + void EnsureValuePositive(IntSetting* setting) { if (setting->value() < 0) { setting->set_value(setting->source_type(), 0); @@ -130,16 +151,51 @@ return sum; } + +// Creates the GPU setting. +// This setting is unique because it may not be defined by command line, or +// build. In this was, it can be unset. +scoped_ptr<IntSetting> CreateGpuSetting(const CommandLine& command_line, + const BuildSettings& build_settings) { + // Bind to the starboard api, if applicable. + base::optional<int64_t> starboard_setting; + if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) { + starboard_setting = SbSystemGetTotalGPUMemory(); + } + + scoped_ptr<IntSetting> gpu_setting = + CreateSystemMemorySetting( + switches::kMaxCobaltGpuUsage, + MemorySetting::kGPU, + command_line, + build_settings.max_gpu_in_bytes, + starboard_setting); + + EnsureValuePositive(gpu_setting.get()); + return gpu_setting.Pass(); +} + +scoped_ptr<IntSetting> CreateCpuSetting(const CommandLine& command_line, + const BuildSettings& build_settings) { + scoped_ptr<IntSetting> cpu_setting = + CreateSystemMemorySetting( + switches::kMaxCobaltGpuUsage, + MemorySetting::kCPU, + command_line, + build_settings.max_cpu_in_bytes, + SbSystemGetTotalCPUMemory()); + + EnsureValuePositive(cpu_setting.get()); + return cpu_setting.Pass(); +} + } // namespace AutoMem::AutoMem(const math::Size& ui_resolution, const CommandLine& command_line, const BuildSettings& build_settings) { - // Set the misc cobalt engine size to a specific size. - misc_cobalt_cpu_size_in_bytes_.reset( - new IntSetting("misc_cobalt_cpu_size_in_bytes")); - misc_cobalt_cpu_size_in_bytes_->set_value( - MemorySetting::kAutoSet, kMiscCobaltSizeInBytes); + max_cpu_bytes_ = CreateCpuSetting(command_line, build_settings); + max_gpu_bytes_ = CreateGpuSetting(command_line, build_settings); // Set the ImageCache image_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>( @@ -158,6 +214,21 @@ kDefaultJsGarbageCollectionThresholdSize); EnsureValuePositive(javascript_gc_threshold_in_bytes_.get()); + // Set the misc cobalt size to a specific size. + misc_cobalt_cpu_size_in_bytes_.reset( + new IntSetting("misc_cobalt_cpu_size_in_bytes")); + misc_cobalt_cpu_size_in_bytes_->set_value( + MemorySetting::kAutoSet, kMiscCobaltSizeInBytes); + + // Set remote_type_face_cache size. + remote_typeface_cache_size_in_bytes_ = + CreateMemorySetting<IntSetting, int64_t>( + switches::kRemoteTypefaceCacheSizeInBytes, + command_line, + build_settings.remote_typeface_cache_capacity_in_bytes, + kDefaultRemoteTypeFaceCacheSize); + EnsureValuePositive(remote_typeface_cache_size_in_bytes_.get()); + // Set skia_atlas_texture_dimensions skia_atlas_texture_dimensions_ = CreateMemorySetting<DimensionSetting, TextureDimensions>( @@ -212,6 +283,10 @@ return misc_cobalt_cpu_size_in_bytes_.get(); } +const IntSetting* AutoMem::remote_typeface_cache_size_in_bytes() const { + return remote_typeface_cache_size_in_bytes_.get(); +} + const IntSetting* AutoMem::image_cache_size_in_bytes() const { return image_cache_size_in_bytes_.get(); } @@ -250,6 +325,7 @@ all_settings.push_back(image_cache_size_in_bytes_.get()); all_settings.push_back(javascript_gc_threshold_in_bytes_.get()); all_settings.push_back(misc_cobalt_cpu_size_in_bytes_.get()); + all_settings.push_back(remote_typeface_cache_size_in_bytes_.get()); all_settings.push_back(skia_atlas_texture_dimensions_.get()); all_settings.push_back(skia_cache_size_in_bytes_.get()); all_settings.push_back(software_surface_cache_size_in_bytes_.get()); @@ -266,7 +342,7 @@ int64_t gpu_consumption = SumMemoryConsumption(MemorySetting::kGPU, all_settings); - ss << GenerateMemoryTable(GetTotalCpuMemory(), GetTotalGpuMemory(), + ss << GenerateMemoryTable(*max_cpu_bytes_, *max_gpu_bytes_, cpu_consumption, gpu_consumption); std::string output_str = ss.str();
diff --git a/src/cobalt/browser/memory_settings/auto_mem.h b/src/cobalt/browser/memory_settings/auto_mem.h index 4d81db6..ad3e143 100644 --- a/src/cobalt/browser/memory_settings/auto_mem.h +++ b/src/cobalt/browser/memory_settings/auto_mem.h
@@ -45,10 +45,12 @@ const IntSetting* image_cache_size_in_bytes() const; const IntSetting* javascript_gc_threshold_in_bytes() const; const IntSetting* misc_engine_cpu_size_in_bytes() const; + const IntSetting* remote_typeface_cache_size_in_bytes() const; const DimensionSetting* skia_atlas_texture_dimensions() const; const IntSetting* skia_cache_size_in_bytes() const; const IntSetting* software_surface_cache_size_in_bytes() const; + // AllMemorySettings - does not include cpu & gpu max memory. std::vector<const MemorySetting*> AllMemorySettings() const; std::vector<MemorySetting*> AllMemorySettingsMutable(); @@ -61,9 +63,14 @@ scoped_ptr<IntSetting> image_cache_size_in_bytes_; scoped_ptr<IntSetting> javascript_gc_threshold_in_bytes_; scoped_ptr<IntSetting> misc_cobalt_cpu_size_in_bytes_; + scoped_ptr<IntSetting> remote_typeface_cache_size_in_bytes_; scoped_ptr<DimensionSetting> skia_atlas_texture_dimensions_; scoped_ptr<IntSetting> skia_cache_size_in_bytes_; scoped_ptr<IntSetting> software_surface_cache_size_in_bytes_; + + // These settings are used for constraining the memory. + scoped_ptr<IntSetting> max_cpu_bytes_; + scoped_ptr<IntSetting> max_gpu_bytes_; }; } // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/build_settings.cc b/src/cobalt/browser/memory_settings/build_settings.cc index 32c151e..dcc75a3 100644 --- a/src/cobalt/browser/memory_settings/build_settings.cc +++ b/src/cobalt/browser/memory_settings/build_settings.cc
@@ -16,6 +16,7 @@ #include "cobalt/browser/memory_settings/build_settings.h" +#include "base/optional.h" #include "cobalt/browser/memory_settings/constants.h" namespace cobalt { @@ -46,6 +47,7 @@ } return output; } + } // namespace BuildSettings GetDefaultBuildSettings() { @@ -57,15 +59,24 @@ settings.javascript_garbage_collection_threshold_in_bytes = MakeValidIfGreaterThanOrEqualToZero( COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES); + settings.remote_typeface_cache_capacity_in_bytes = + MakeValidIfGreaterThanOrEqualToZero( + COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES); settings.skia_cache_size_in_bytes = MakeValidIfGreaterThanOrEqualToZero(COBALT_SKIA_CACHE_SIZE_IN_BYTES); settings.skia_texture_atlas_dimensions = - MakeDimensionsIfValid(TextureDimensions(COBALT_SKIA_GLYPH_ATLAS_WIDTH, - COBALT_SKIA_GLYPH_ATLAS_HEIGHT, - kSkiaGlyphAtlasTextureBytesPerPixel)); + MakeDimensionsIfValid( + TextureDimensions(COBALT_SKIA_GLYPH_ATLAS_WIDTH, + COBALT_SKIA_GLYPH_ATLAS_HEIGHT, + kSkiaGlyphAtlasTextureBytesPerPixel)); settings.software_surface_cache_size_in_bytes = MakeValidIfGreaterThanOrEqualToZero( COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES); + + settings.max_cpu_in_bytes = + MakeValidIfGreaterThanOrEqualToZero(COBALT_MAX_CPU_USAGE_IN_BYTES); + settings.max_gpu_in_bytes = + MakeValidIfGreaterThanOrEqualToZero(COBALT_MAX_GPU_USAGE_IN_BYTES); return settings; }
diff --git a/src/cobalt/browser/memory_settings/build_settings.h b/src/cobalt/browser/memory_settings/build_settings.h index 3d71e31..aff96ee 100644 --- a/src/cobalt/browser/memory_settings/build_settings.h +++ b/src/cobalt/browser/memory_settings/build_settings.h
@@ -27,13 +27,17 @@ namespace memory_settings { struct BuildSettings { - BuildSettings() : has_blitter(false) {} + BuildSettings() : has_blitter(false), max_cpu_in_bytes(0) {} bool has_blitter; base::optional<int64_t> cobalt_image_cache_size_in_bytes; base::optional<int64_t> javascript_garbage_collection_threshold_in_bytes; + base::optional<int64_t> remote_typeface_cache_capacity_in_bytes; base::optional<int64_t> skia_cache_size_in_bytes; base::optional<TextureDimensions> skia_texture_atlas_dimensions; base::optional<int64_t> software_surface_cache_size_in_bytes; + + base::optional<int64_t> max_cpu_in_bytes; + base::optional<int64_t> max_gpu_in_bytes; }; BuildSettings GetDefaultBuildSettings();
diff --git a/src/cobalt/browser/memory_settings/constants.h b/src/cobalt/browser/memory_settings/constants.h index 2826296..9f397eb 100644 --- a/src/cobalt/browser/memory_settings/constants.h +++ b/src/cobalt/browser/memory_settings/constants.h
@@ -32,7 +32,7 @@ kMinSkiaGlyphTextureAtlasWidth = 2048, kMinSkiaGlyphTextureAtlasHeight = 2048, kSkiaGlyphAtlasTextureBytesPerPixel = 2, - + kDefaultRemoteTypeFaceCacheSize = 4 * 1024 * 1024, // 4mb. kDefaultJsGarbageCollectionThresholdSize = 1 * 1024 * 1024, // 1mb kMinSkiaCacheSize = 4 * 1024 * 1024, // 4mb.
diff --git a/src/cobalt/browser/memory_settings/memory_settings.h b/src/cobalt/browser/memory_settings/memory_settings.h index c1e3d80..6d27358 100644 --- a/src/cobalt/browser/memory_settings/memory_settings.h +++ b/src/cobalt/browser/memory_settings/memory_settings.h
@@ -39,7 +39,7 @@ // SourceType defines the location where the setting was set from. // kNotApplicable means the setting is not supported on the current system // configuration. - enum SourceType { kUnset, kCmdLine, kBuildSetting, kAutoSet }; + enum SourceType { kUnset, kStarboardAPI, kBuildSetting, kCmdLine, kAutoSet }; enum ClassType { kInt, kDimensions }; enum MemoryType { kCPU, kGPU, kNotApplicable }; @@ -84,6 +84,11 @@ virtual int64_t MemoryConsumption() const OVERRIDE; int64_t value() const { return valid() ? value_ : 0; } + base::optional<int64_t> optional_value() const { + base::optional<int64_t> output; + if (valid()) { output = value_; } + return output; + } void set_value(SourceType source_type, int64_t val) { source_type_ = source_type; value_ = val; @@ -109,6 +114,12 @@ TextureDimensions value() const { return valid() ? value_ : TextureDimensions(); } + base::optional<TextureDimensions> optional_value() const { + base::optional<TextureDimensions> output; + if (valid()) { output = value_; } + return output; + } + void set_value(SourceType source_type, const TextureDimensions& val) { source_type_ = source_type; value_ = val;
diff --git a/src/cobalt/browser/memory_settings/pretty_print.cc b/src/cobalt/browser/memory_settings/pretty_print.cc index 298fe3f..c8fef61 100644 --- a/src/cobalt/browser/memory_settings/pretty_print.cc +++ b/src/cobalt/browser/memory_settings/pretty_print.cc
@@ -39,12 +39,15 @@ case MemorySetting::kUnset: { return "Unset"; } - case MemorySetting::kCmdLine: { - return "CmdLine"; + case MemorySetting::kStarboardAPI: { + return "Starboard API"; } case MemorySetting::kBuildSetting: { return "Build"; } + case MemorySetting::kCmdLine: { + return "CmdLine"; + } case MemorySetting::kAutoSet: { return "AutoSet"; } @@ -96,7 +99,7 @@ TablePrinter printer; std::vector<std::string> header; - header.push_back("NAME"); + header.push_back("SETTING NAME"); header.push_back("VALUE"); header.push_back(""); header.push_back("TYPE"); @@ -123,31 +126,33 @@ return table_string; } -std::string GenerateMemoryTable(int64_t total_cpu_memory, - base::optional<int64_t> total_gpu_memory, +std::string GenerateMemoryTable(const IntSetting& total_cpu_memory, + const IntSetting& total_gpu_memory, int64_t settings_cpu_consumption, int64_t settings_gpu_consumption) { TablePrinter printer; std::vector<std::string> header; - header.push_back("TYPE"); + header.push_back("MEMORY"); + header.push_back("SOURCE"); header.push_back("TOTAL"); - header.push_back("SETTINGS"); + header.push_back("SETTINGS CONSUME"); printer.AddRow(header); std::vector<std::string> data_row; - data_row.push_back("CPU"); - data_row.push_back(ToMegabyteString(total_cpu_memory)); + data_row.push_back(total_cpu_memory.name()); + data_row.push_back(StringifySourceType(&total_cpu_memory)); + data_row.push_back(ToMegabyteString(total_cpu_memory.value())); data_row.push_back(ToMegabyteString(settings_cpu_consumption)); printer.AddRow(data_row); data_row.clear(); - data_row.push_back("GPU"); - + data_row.push_back(total_gpu_memory.name()); + data_row.push_back(StringifySourceType(&total_gpu_memory)); std::string total_gpu_consumption_str; - if (!total_gpu_memory) { + if (total_gpu_memory.value() <= 0) { total_gpu_consumption_str = "<UNKNOWN>"; } else { - total_gpu_consumption_str = ToMegabyteString(*total_gpu_memory); + total_gpu_consumption_str = ToMegabyteString(total_gpu_memory.value()); } data_row.push_back(total_gpu_consumption_str);
diff --git a/src/cobalt/browser/memory_settings/pretty_print.h b/src/cobalt/browser/memory_settings/pretty_print.h index be70421..1960e75 100644 --- a/src/cobalt/browser/memory_settings/pretty_print.h +++ b/src/cobalt/browser/memory_settings/pretty_print.h
@@ -64,8 +64,8 @@ // |______|__________|__________| // When optional total_gpu_memory is null then the the value in the output // table will be <UNKNOWN>. -std::string GenerateMemoryTable(int64_t total_cpu_memory, - base::optional<int64_t> total_gpu_memory, +std::string GenerateMemoryTable(const IntSetting& total_cpu_memory, + const IntSetting& total_gpu_memory, int64_t settings_cpu_consumption, int64_t settings_gpu_consumption);
diff --git a/src/cobalt/browser/memory_settings/pretty_print_test.cc b/src/cobalt/browser/memory_settings/pretty_print_test.cc index ad5108e..f0bffb5 100644 --- a/src/cobalt/browser/memory_settings/pretty_print_test.cc +++ b/src/cobalt/browser/memory_settings/pretty_print_test.cc
@@ -126,7 +126,7 @@ GeneratePrettyPrintTable(setting_group.AsConstVector()); const char* expected_string = - " NAME VALUE TYPE SOURCE \n" + " SETTING NAME VALUE TYPE SOURCE \n" " _______________________________________________________________________________ \n" "| | | | | |\n" "| image_cache_size_in_bytes | 1234 | 0.0 MB | GPU | CmdLine |\n" @@ -147,43 +147,54 @@ EXPECT_STREQ(expected_string, actual_string.c_str()); } -TEST(MemorySettingsPrettyPrint, GenerateMemoryTableWithNoGpuMemory) { - const base::optional<int64_t> no_gpu_memory; +TEST(MemorySettingsPrettyPrint, GenerateMemoryTableWithUnsetGpuMemory) { + IntSetting cpu_memory_setting("max_cpu_memory"); + cpu_memory_setting.set_value( + MemorySetting::kBuildSetting, 256 * 1024 * 1024); + IntSetting gpu_memory_setting("max_gpu_memory"); + std::string actual_output = - GenerateMemoryTable(256 * 1024 * 1024, // 256 MB CPU available - no_gpu_memory, + GenerateMemoryTable(cpu_memory_setting, // 256 MB CPU available + gpu_memory_setting, 128 * 1024 * 1024, // 128 MB CPU consumption 0); // 0 MB GPU consumption. const char* expected_output = - " TYPE TOTAL SETTINGS \n" - " _____________________________ \n" - "| | | |\n" - "| CPU | 256.0 MB | 128.0 MB |\n" - "|______|___________|__________|\n" - "| | | |\n" - "| GPU | <UNKNOWN> | 0.0 MB |\n" - "|______|___________|__________|\n"; + " MEMORY SOURCE TOTAL SETTINGS CONSUME \n" + " ________________________________________________________ \n" + "| | | | |\n" + "| max_cpu_memory | Build | 256.0 MB | 128.0 MB |\n" + "|________________|________|___________|__________________|\n" + "| | | | |\n" + "| max_gpu_memory | Unset | <UNKNOWN> | 0.0 MB |\n" + "|________________|________|___________|__________________|\n"; EXPECT_STREQ(expected_output, actual_output.c_str()) << actual_output; } TEST(MemorySettingsPrettyPrint, GenerateMemoryTableWithGpuMemory) { + IntSetting cpu_memory_setting("max_cpu_memory"); + cpu_memory_setting.set_value( + MemorySetting::kBuildSetting, 256 * 1024 * 1024); + IntSetting gpu_memory_setting("max_gpu_memory"); + gpu_memory_setting.set_value( + MemorySetting::kBuildSetting, 64 * 1024 * 1024); + std::string actual_output = - GenerateMemoryTable(256 * 1024 * 1024, // 256 MB CPU available. - 64 * 1024 * 1024, // 64 MB GPU available. + GenerateMemoryTable(cpu_memory_setting, // 256 MB CPU available. + gpu_memory_setting, // 64 MB GPU available. 128 * 1024 * 1024, // 128 MB CPU consumption. 23592960); // 22.5 MB GPU consumption. const char* expected_output = - " TYPE TOTAL SETTINGS \n" - " ____________________________ \n" - "| | | |\n" - "| CPU | 256.0 MB | 128.0 MB |\n" - "|______|__________|__________|\n" - "| | | |\n" - "| GPU | 64.0 MB | 22.5 MB |\n" - "|______|__________|__________|\n"; + " MEMORY SOURCE TOTAL SETTINGS CONSUME \n" + " _______________________________________________________ \n" + "| | | | |\n" + "| max_cpu_memory | Build | 256.0 MB | 128.0 MB |\n" + "|________________|________|__________|__________________|\n" + "| | | | |\n" + "| max_gpu_memory | Build | 64.0 MB | 22.5 MB |\n" + "|________________|________|__________|__________________|\n"; EXPECT_STREQ(expected_output, actual_output.c_str()) << actual_output; } @@ -196,7 +207,7 @@ test_setting_group.AsConstVector()); const char* expected_string = - " NAME VALUE TYPE SOURCE \n" + " SETTING NAME VALUE TYPE SOURCE \n" " _______________________________________________________________________________ \n" "| | | | | |\n" "| image_cache_size_in_bytes | 1234 | 0.0 MB | GPU | CmdLine |\n" @@ -218,23 +229,29 @@ } TEST(MemorySettingsPrettyPrint, GenerateMemoryWithInvalidGpuMemoryConsumption) { + IntSetting cpu_memory_setting("max_cpu_memory"); + cpu_memory_setting.set_value( + MemorySetting::kBuildSetting, 256 * 1024 * 1024); + IntSetting gpu_memory_setting("max_gpu_memory"); + gpu_memory_setting.set_value(MemorySetting::kStarboardAPI, 0); + const base::optional<int64_t> no_gpu_memory; std::string actual_output = GenerateMemoryTable( - 256 * 1024 * 1024, // 256 MB CPU available. - no_gpu_memory, // Signals that no gpu memory is available - // on this system. - 128 * 1024 * 1024, // 128 MB CPU consumption. - 16 * 1024 * 1024); // 16 MB GPU consumption. + cpu_memory_setting, // 256 MB CPU available. + gpu_memory_setting, // Signals that no gpu memory is available + // on this system. + 128 * 1024 * 1024, // 128 MB CPU consumption. + 16 * 1024 * 1024); // 16 MB GPU consumption. const char* expected_output = - " TYPE TOTAL SETTINGS \n" - " _____________________________ \n" - "| | | |\n" - "| CPU | 256.0 MB | 128.0 MB |\n" - "|______|___________|__________|\n" - "| | | |\n" - "| GPU | <UNKNOWN> | 16.0 MB |\n" - "|______|___________|__________|\n"; + " MEMORY SOURCE TOTAL SETTINGS CONSUME \n" + " _______________________________________________________________ \n" + "| | | | |\n" + "| max_cpu_memory | Build | 256.0 MB | 128.0 MB |\n" + "|________________|_______________|___________|__________________|\n" + "| | | | |\n" + "| max_gpu_memory | Starboard API | <UNKNOWN> | 16.0 MB |\n" + "|________________|_______________|___________|__________________|\n"; EXPECT_STREQ(expected_output, actual_output.c_str()) << actual_output; }
diff --git a/src/cobalt/browser/memory_tracker/tool.cc b/src/cobalt/browser/memory_tracker/tool.cc index 72cb675..e37356a 100644 --- a/src/cobalt/browser/memory_tracker/tool.cc +++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -23,6 +23,9 @@ #include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h" #include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h" #include "cobalt/browser/memory_tracker/tool/log_writer_tool.h" +#include "cobalt/browser/memory_tracker/tool/memory_size_binner_tool.h" +#include "cobalt/browser/memory_tracker/tool/print_csv_tool.h" +#include "cobalt/browser/memory_tracker/tool/print_tool.h" #include "cobalt/browser/memory_tracker/tool/tool_impl.h" #include "cobalt/browser/memory_tracker/tool/tool_thread.h" #include "nb/analytics/memory_tracker_helpers.h" @@ -315,7 +318,7 @@ memory_tracker->InstallGlobalTrackingHooks(); // Create a thread that will continuously report javascript memory // analytics. - tool_ptr.reset(new MemorySizeBinner(tool_arg)); + tool_ptr.reset(new MemorySizeBinnerTool(tool_arg)); break; } case kAllocationLogger: {
diff --git a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h index caa5a6b..9852be4 100644 --- a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h +++ b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h
@@ -18,6 +18,7 @@ #include <string> #include "cobalt/browser/memory_tracker/tool/tool_impl.h" +#include "cobalt/browser/memory_tracker/tool/util.h" #include "nb/analytics/memory_tracker.h" namespace cobalt {
diff --git a/src/cobalt/browser/memory_tracker/tool/memory_size_binner_tool.cc b/src/cobalt/browser/memory_tracker/tool/memory_size_binner_tool.cc new file mode 100644 index 0000000..1865fd9 --- /dev/null +++ b/src/cobalt/browser/memory_tracker/tool/memory_size_binner_tool.cc
@@ -0,0 +1,112 @@ +// 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/memory_tracker/tool/memory_size_binner_tool.h" + +#include <sstream> +#include <string> +#include <vector> + +#include "base/threading/platform_thread.h" +#include "base/time.h" +#include "cobalt/base/c_val.h" +#include "cobalt/browser/memory_tracker/tool/util.h" +#include "nb/analytics/memory_tracker.h" +#include "nb/analytics/memory_tracker_helpers.h" +#include "starboard/log.h" +#include "starboard/types.h" + +namespace cobalt { +namespace browser { +namespace memory_tracker { +namespace { + +const nb::analytics::AllocationGroup* FindAllocationGroup( + const std::string& name, + nb::analytics::MemoryTracker* memory_tracker) { + std::vector<const nb::analytics::AllocationGroup*> groups; + memory_tracker->GetAllocationGroups(&groups); + // Find by exact string match. + for (size_t i = 0; i < groups.size(); ++i) { + const nb::analytics::AllocationGroup* group = groups[i]; + if (group->name().compare(name) == 0) { + return group; + } + } + return NULL; +} +} // namespace. + +MemorySizeBinnerTool::MemorySizeBinnerTool(const std::string& memory_scope_name) + : memory_scope_name_(memory_scope_name) {} + +void MemorySizeBinnerTool::Run(Params* params) { + const nb::analytics::AllocationGroup* target_group = NULL; + + while (!params->finished()) { + if (target_group == NULL && !memory_scope_name_.empty()) { + target_group = + FindAllocationGroup(memory_scope_name_, params->memory_tracker()); + } + + std::stringstream ss; + ss.precision(2); + if (target_group || memory_scope_name_.empty()) { + AllocationSizeBinner visitor_binner = AllocationSizeBinner(target_group); + params->memory_tracker()->Accept(&visitor_binner); + + size_t min_size = 0; + size_t max_size = 0; + + visitor_binner.GetLargestSizeRange(&min_size, &max_size); + + FindTopSizes top_size_visitor = + FindTopSizes(min_size, max_size, target_group); + params->memory_tracker()->Accept(&top_size_visitor); + + ss << kNewLine; + ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):"; + ss << kNewLine; + if (!memory_scope_name_.empty()) { + ss << "Tracking Memory Scope \"" << memory_scope_name_ << "\", "; + } else { + ss << "Tracking whole program, "; + } + ss << "first row is allocation size range, second row is number of " + << kNewLine << "allocations in that range." << kNewLine; + ss << visitor_binner.ToCSVString(); + ss << kNewLine; + ss << "Largest allocation range: \"" << min_size << "..." << max_size + << "\"" << kNewLine; + ss << "Printing out top allocations from this range: " << kNewLine; + ss << top_size_visitor.ToString(5) << kNewLine; + } else { + ss << "No allocations for \"" << memory_scope_name_ << "\"."; + } + + params->logger()->Output(ss.str().c_str()); + params->logger()->Flush(); + + // Sleep until the next sample. + base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); + } +} + +std::string MemorySizeBinnerTool::tool_name() const { + return "MemoryTrackerCompressedTimeSeries"; +} + +} // namespace memory_tracker +} // namespace browser +} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/memory_size_binner_tool.h b/src/cobalt/browser/memory_tracker/tool/memory_size_binner_tool.h new file mode 100644 index 0000000..b98a421 --- /dev/null +++ b/src/cobalt/browser/memory_tracker/tool/memory_size_binner_tool.h
@@ -0,0 +1,49 @@ +// 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_MEMORY_TRACKER_TOOL_MEMORY_SIZE_BINNER_TOOL_H_ +#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_MEMORY_SIZE_BINNER_TOOL_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "cobalt/browser/memory_tracker/tool/params.h" +#include "cobalt/browser/memory_tracker/tool/tool_impl.h" + +namespace cobalt { +namespace browser { +namespace memory_tracker { + +// This tool inspects a memory scope and reports on the memory usage. +// The output will be a CSV file printed to stdout representing +// the number of memory allocations for objects. The objects are binned +// according to the size of the memory allocation. Objects within the same +// power of two are binned together. For example 1024 will be binned with 1025. +class MemorySizeBinnerTool : public AbstractTool { + public: + // memory_scope_name represents the memory scope that is to be investigated. + explicit MemorySizeBinnerTool(const std::string& memory_scope_name); + + virtual void Run(Params* params) OVERRIDE; + virtual std::string tool_name() const OVERRIDE; + + private: + std::string memory_scope_name_; +}; + +} // namespace memory_tracker +} // namespace browser +} // namespace cobalt + +#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_MEMORY_SIZE_BINNER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc b/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc new file mode 100644 index 0000000..51ef41f --- /dev/null +++ b/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc
@@ -0,0 +1,262 @@ +// 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/memory_tracker/tool/print_csv_tool.h" + +#include <sstream> +#include <string> +#include <vector> + +#include "cobalt/base/c_val.h" +#include "cobalt/browser/memory_tracker/tool/util.h" +#include "nb/analytics/memory_tracker.h" +#include "nb/analytics/memory_tracker_helpers.h" +#include "starboard/log.h" +#include "starboard/types.h" + +namespace cobalt { +namespace browser { +namespace memory_tracker { + +PrintCSVTool::PrintCSVTool(int sampling_interval_ms, int sampling_time_ms) + : sample_interval_ms_(sampling_interval_ms), + sampling_time_ms_(sampling_time_ms) {} + +std::string PrintCSVTool::ToCsvString(const MapAllocationSamples& samples_in) { + typedef MapAllocationSamples Map; + typedef Map::const_iterator MapIt; + + size_t largest_sample_size = 0; + size_t smallest_sample_size = INT_MAX; + + // Sanitize samples_in and store as samples. + MapAllocationSamples samples; + for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) { + std::string name = it->first; + const AllocationSamples& value = it->second; + + if (value.allocated_bytes_.size() != value.number_allocations_.size()) { + SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__; + return "ERROR"; + } + + const size_t n = value.allocated_bytes_.size(); + if (n > largest_sample_size) { + largest_sample_size = n; + } + if (n < smallest_sample_size) { + smallest_sample_size = n; + } + + const bool duplicate_found = (samples.end() != samples.find(name)); + if (duplicate_found) { + SB_NOTREACHED() << "Error, duplicate found for entry: " << name + << kNewLine; + } + // Store value as a sanitized sample. + samples[name] = value; + } + + SB_DCHECK(largest_sample_size == smallest_sample_size); + + std::stringstream ss; + + // Begin output to CSV. + // Sometimes we need to skip the CPU memory entry. + const MapIt total_cpu_memory_it = samples.find(UntrackedMemoryKey()); + + // Preamble + ss << kNewLine << "//////////////////////////////////////////////"; + ss << kNewLine << "// CSV of bytes / allocation" << kNewLine; + // HEADER. + ss << "Name" << kDelimiter << kQuote << "Bytes/Alloc" << kQuote << kNewLine; + // DATA. + for (MapIt it = samples.begin(); it != samples.end(); ++it) { + if (total_cpu_memory_it == it) { + continue; + } + + const AllocationSamples& samples = it->second; + if (samples.allocated_bytes_.empty() || + samples.number_allocations_.empty()) { + SB_NOTREACHED() << "Should not be here"; + return "ERROR"; + } + const int64 n_allocs = samples.number_allocations_.back(); + const int64 n_bytes = samples.allocated_bytes_.back(); + int64 bytes_per_alloc = 0; + if (n_allocs > 0) { + bytes_per_alloc = n_bytes / n_allocs; + } + const std::string& name = it->first; + ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter + << bytes_per_alloc << kNewLine; + } + ss << kNewLine; + + // Preamble + ss << kNewLine << "//////////////////////////////////////////////" << kNewLine + << "// CSV of bytes allocated per region (MB's)." << kNewLine + << "// Units are in Megabytes. This is designed" << kNewLine + << "// to be used in a stacked graph." << kNewLine; + + // HEADER. + for (MapIt it = samples.begin(); it != samples.end(); ++it) { + if (total_cpu_memory_it == it) { + continue; + } + // Strip out any characters that could make parsing the csv difficult. + const std::string name = SanitizeCSVKey(it->first); + ss << kQuote << name << kQuote << kDelimiter; + } + // Save the total for last. + if (total_cpu_memory_it != samples.end()) { + const std::string& name = SanitizeCSVKey(total_cpu_memory_it->first); + ss << kQuote << name << kQuote << kDelimiter; + } + ss << kNewLine; + + // Print out the values of each of the samples. + for (size_t i = 0; i < smallest_sample_size; ++i) { + for (MapIt it = samples.begin(); it != samples.end(); ++it) { + if (total_cpu_memory_it == it) { + continue; + } + const int64 alloc_bytes = it->second.allocated_bytes_[i]; + // Convert to float megabytes with decimals of precision. + double n = alloc_bytes / (1000 * 10); + n = n / (100.); + ss << n << kDelimiter; + } + if (total_cpu_memory_it != samples.end()) { + const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i]; + // Convert to float megabytes with decimals of precision. + double n = alloc_bytes / (1000 * 10); + n = n / (100.); + ss << n << kDelimiter; + } + ss << kNewLine; + } + + ss << kNewLine; + // Preamble + ss << kNewLine << "//////////////////////////////////////////////"; + ss << kNewLine << "// CSV of number of allocations per region." << kNewLine; + + // HEADER + for (MapIt it = samples.begin(); it != samples.end(); ++it) { + if (total_cpu_memory_it == it) { + continue; + } + const std::string& name = SanitizeCSVKey(it->first); + ss << kQuote << name << kQuote << kDelimiter; + } + ss << kNewLine; + for (size_t i = 0; i < smallest_sample_size; ++i) { + for (MapIt it = samples.begin(); it != samples.end(); ++it) { + if (total_cpu_memory_it == it) { + continue; + } + const int64 n_allocs = it->second.number_allocations_[i]; + ss << n_allocs << kDelimiter; + } + ss << kNewLine; + } + std::string output = ss.str(); + return output; +} + +const char* PrintCSVTool::UntrackedMemoryKey() { return "Untracked Memory"; } + +void PrintCSVTool::Run(Params* params) { + params->logger()->Output("\nMemoryTrackerPrintCSVThread is sampling...\n"); + int sample_count = 0; + MapAllocationSamples map_samples; + + while (!TimeExpiredYet(*params) && !params->finished()) { + // Sample total memory used by the system. + nb::analytics::MemoryStats mem_stats = + nb::analytics::GetProcessMemoryStats(); + int64 untracked_used_memory = + mem_stats.used_cpu_memory + mem_stats.used_gpu_memory; + + std::vector<const nb::analytics::AllocationGroup*> vector_output; + params->memory_tracker()->GetAllocationGroups(&vector_output); + + // Sample all known memory scopes. + for (size_t i = 0; i < vector_output.size(); ++i) { + const nb::analytics::AllocationGroup* group = vector_output[i]; + const std::string& name = group->name(); + + const bool first_creation = + map_samples.find(group->name()) == map_samples.end(); + + AllocationSamples* new_entry = &(map_samples[name]); + + // Didn't see it before so create new entry. + if (first_creation) { + // Make up for lost samples... + new_entry->allocated_bytes_.resize(sample_count, 0); + new_entry->number_allocations_.resize(sample_count, 0); + } + + int32 num_allocs = -1; + int64 allocation_bytes = -1; + group->GetAggregateStats(&num_allocs, &allocation_bytes); + + new_entry->allocated_bytes_.push_back(allocation_bytes); + new_entry->number_allocations_.push_back(num_allocs); + + untracked_used_memory -= allocation_bytes; + } + + // Now push in remaining total. + AllocationSamples* process_sample = &(map_samples[UntrackedMemoryKey()]); + if (untracked_used_memory < 0) { + // On some platforms, total GPU memory may not be correctly reported. + // However the allocations from the GPU memory may be reported. In this + // case untracked_used_memory will go negative. To protect the memory + // reporting the untracked_used_memory is set to 0 so that it doesn't + // cause an error in reporting. + untracked_used_memory = 0; + } + process_sample->allocated_bytes_.push_back(untracked_used_memory); + process_sample->number_allocations_.push_back(-1); + + ++sample_count; + base::PlatformThread::Sleep( + base::TimeDelta::FromMilliseconds(sample_interval_ms_)); + } + + std::stringstream ss; + ss.precision(2); + ss << "Time now: " << params->TimeInMinutesString() << ",\n"; + ss << ToCsvString(map_samples); + params->logger()->Output(ss.str().c_str()); + params->logger()->Flush(); + // Prevents the "thread exited code 0" from being interleaved into the + // output. This happens if flush is not implemented correctly in the system. + base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); +} + +bool PrintCSVTool::TimeExpiredYet(const Params& params) { + base::TimeDelta dt = params.time_since_start(); + int64 dt_ms = dt.InMilliseconds(); + const bool expired_time = dt_ms > sampling_time_ms_; + return expired_time; +} + +} // namespace memory_tracker +} // namespace browser +} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/print_csv_tool.h b/src/cobalt/browser/memory_tracker/tool/print_csv_tool.h new file mode 100644 index 0000000..cd54b8e --- /dev/null +++ b/src/cobalt/browser/memory_tracker/tool/print_csv_tool.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_BROWSER_MEMORY_TRACKER_TOOL_PRINT_CSV_TOOL_H_ +#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_PRINT_CSV_TOOL_H_ + +#include <map> +#include <string> + +#include "base/compiler_specific.h" +#include "cobalt/browser/memory_tracker/tool/params.h" +#include "cobalt/browser/memory_tracker/tool/tool_impl.h" + +namespace cobalt { +namespace browser { +namespace memory_tracker { + +// Generates CSV values of the engine. +// There are three sections of data including: +// 1. average bytes / alloc +// 2. # Bytes allocated per memory scope. +// 3. # Allocations per memory scope. +// This data can be pasted directly into a Google spreadsheet and visualized. +// Note that this thread will implicitly call Start() is called during +// construction and Cancel() & Join() during destruction. +class PrintCSVTool : public AbstractTool { + public: + // This tool will only produce on CSV dump of the engine. This is useful + // for profiling startup memory consumption. + PrintCSVTool(int sampling_interval_ms, int sampling_time_ms); + + // Overridden so that the thread can exit gracefully. + virtual void Run(Params* params) OVERRIDE; + virtual std::string tool_name() const OVERRIDE { + return "MemoryTrackerPrintCSV"; + } + + private: + typedef std::map<std::string, AllocationSamples> MapAllocationSamples; + static std::string ToCsvString(const MapAllocationSamples& samples); + static const char* UntrackedMemoryKey(); + bool TimeExpiredYet(const Params& params); + + const int sample_interval_ms_; + const int sampling_time_ms_; +}; + +} // namespace memory_tracker +} // namespace browser +} // namespace cobalt + +#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_PRINT_CSV_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/print_tool.cc b/src/cobalt/browser/memory_tracker/tool/print_tool.cc new file mode 100644 index 0000000..f896f2f --- /dev/null +++ b/src/cobalt/browser/memory_tracker/tool/print_tool.cc
@@ -0,0 +1,189 @@ +// 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/memory_tracker/tool/print_tool.h" + +#include <map> +#include <sstream> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/threading/platform_thread.h" +#include "cobalt/base/c_val.h" +#include "cobalt/browser/memory_tracker/tool/util.h" +#include "nb/analytics/memory_tracker.h" +#include "nb/analytics/memory_tracker_helpers.h" +#include "starboard/log.h" +#include "starboard/types.h" + +namespace cobalt { +namespace browser { +namespace memory_tracker { + +using nb::analytics::AllocationGroup; + +class PrintTool::CvalsMap { + public: + typedef base::CVal<base::cval::SizeInBytes> CValType; + + ~CvalsMap() { + while (!map_.empty()) { + MapCvals::iterator it = map_.begin(); + delete it->second; + map_.erase(it); + } + } + + void Update(const std::string& name, size_t value) { + std::string cval_name = GetCvalName(name); + CValType*& val = map_[cval_name]; + if (!val) { + // Create if not found. + val = new CValType(cval_name, 0, + "Automatically generated by the " + "browser::memory_tracker system."); + } + (*val) = value; + } + + static std::string GetCvalName(const std::string& name) { + std::stringstream ss; + ss << "Memory.Scope." << name; + return ss.str(); + } + + private: + typedef std::map<std::string, CValType*> MapCvals; + MapCvals map_; +}; + +PrintTool::PrintTool() : cvals_map_(new CvalsMap) {} + +PrintTool::~PrintTool() {} + +void PrintTool::Run(Params* params) { + const std::string kSeperator = + "--------------------------------------------------"; + + while (!params->finished()) { + std::vector<const AllocationGroup*> vector_output; + params->memory_tracker()->GetAllocationGroups(&vector_output); + + typedef std::map<std::string, const AllocationGroup*> Map; + typedef Map::const_iterator MapIt; + + Map output; + for (size_t i = 0; i < vector_output.size(); ++i) { + const AllocationGroup* group = vector_output[i]; + output[group->name()] = group; + } + + int32 num_allocs = 0; + int64 total_bytes = 0; + + struct F { + static void PrintRow(std::stringstream* ss, const std::string& v1, + const std::string& v2, const std::string& v3) { + ss->width(25); + *ss << std::left << v1; + ss->width(13); + *ss << std::right << v2 << " "; + ss->width(10); + *ss << std::right << v3 << "\n"; + } + }; + + if (params->memory_tracker()->IsMemoryTrackingEnabled()) { + // If this isn't true then it would cause an infinite loop. The + // following will likely crash. + SB_DCHECK(false) << "Unexpected, memory tracking should be disabled."; + } + + std::stringstream ss; + + ss << kNewLine; + ss << "TimeNow " << params->TimeInMinutesString() + << " (minutes):" << kNewLine << kNewLine; + + ss << kSeperator << kNewLine; + nb::analytics::MemoryStats memstats = + nb::analytics::GetProcessMemoryStats(); + + F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", ""); + ss << kSeperator << kNewLine; + F::PrintRow(&ss, "Total CPU Reserved", + NumberFormatWithCommas(memstats.total_cpu_memory), ""); + + F::PrintRow(&ss, "Total CPU Used", + NumberFormatWithCommas(memstats.used_cpu_memory), ""); + + F::PrintRow(&ss, "Total GPU Reserved", + NumberFormatWithCommas(memstats.total_gpu_memory), ""); + + F::PrintRow(&ss, "Total GPU Used", + NumberFormatWithCommas(memstats.used_gpu_memory), ""); + + ss << kSeperator << kNewLine << kNewLine; + + ss << kSeperator << kNewLine; + F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS"); + ss << kSeperator << kNewLine; + + for (MapIt it = output.begin(); it != output.end(); ++it) { + const AllocationGroup* group = it->second; + if (!group) { + continue; + } + + int32 num_group_allocs = -1; + int64 total_group_bytes = -1; + + group->GetAggregateStats(&num_group_allocs, &total_group_bytes); + SB_DCHECK(-1 != num_group_allocs); + SB_DCHECK(-1 != total_group_bytes); + + cvals_map_->Update(group->name(), static_cast<size_t>(total_group_bytes)); + + num_allocs += num_group_allocs; + total_bytes += total_group_bytes; + + F::PrintRow(&ss, it->first, NumberFormatWithCommas(total_group_bytes), + NumberFormatWithCommas(num_group_allocs)); + } + + cvals_map_->Update("Total", static_cast<size_t>(total_bytes)); + + ss << kNewLine; + + F::PrintRow(&ss, "Total (in groups above)", + NumberFormatWithCommas(total_bytes), + NumberFormatWithCommas(num_allocs)); + + ss << kSeperator << kNewLine; + ss << kNewLine << kNewLine; + + params->logger()->Output(ss.str().c_str()); + // Output once every 5 seconds. + base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5)); + } +} + +std::string PrintTool::tool_name() const { + return "MemoryTrackerPrintThread"; +} + +} // namespace memory_tracker +} // namespace browser +} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/print_tool.h b/src/cobalt/browser/memory_tracker/tool/print_tool.h new file mode 100644 index 0000000..d4bdcdd --- /dev/null +++ b/src/cobalt/browser/memory_tracker/tool/print_tool.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 COBALT_BROWSER_MEMORY_TRACKER_TOOL_PRINT_TOOL_H_ +#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_PRINT_TOOL_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "cobalt/browser/memory_tracker/tool/params.h" +#include "cobalt/browser/memory_tracker/tool/tool_impl.h" + +namespace cobalt { +namespace browser { +namespace memory_tracker { + +// Start() is called when this object is created, and Cancel() & Join() are +// called during destruction. +class PrintTool : public AbstractTool { + public: + PrintTool(); + ~PrintTool() OVERRIDE; + + // Overridden so that the thread can exit gracefully. + virtual void Run(Params* params) OVERRIDE; + virtual std::string tool_name() const OVERRIDE; + + private: + class CvalsMap; + scoped_ptr<CvalsMap> cvals_map_; +}; + +} // namespace memory_tracker +} // namespace browser +} // namespace cobalt + +#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_PRINT_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc index 3f32416..01634e0 100644 --- a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc +++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
@@ -52,447 +52,6 @@ using nb::analytics::MemoryStats; using nb::analytics::MemoryTracker; -class PrintTool::CvalsMap { - public: - typedef base::CVal<base::cval::SizeInBytes> CValType; - - ~CvalsMap() { - while (!map_.empty()) { - MapCvals::iterator it = map_.begin(); - delete it->second; - map_.erase(it); - } - } - - void Update(const std::string& name, size_t value) { - std::string cval_name = GetCvalName(name); - CValType*& val = map_[cval_name]; - if (!val) { - // Create if not found. - val = new CValType(cval_name, 0, - "Automatically generated by the " - "browser::memory_tracker system."); - } - (*val) = value; - } - - static std::string GetCvalName(const std::string& name) { - std::stringstream ss; - ss << "Memory.Scope." << name; - return ss.str(); - } - - private: - typedef std::map<std::string, CValType*> MapCvals; - MapCvals map_; -}; - -PrintTool::PrintTool() : cvals_map_(new CvalsMap) {} - -PrintTool::~PrintTool() {} - -void PrintTool::Run(Params* params) { - const std::string kSeperator = - "--------------------------------------------------"; - - while (!params->finished()) { - std::vector<const AllocationGroup*> vector_output; - params->memory_tracker()->GetAllocationGroups(&vector_output); - - typedef std::map<std::string, const AllocationGroup*> Map; - typedef Map::const_iterator MapIt; - - Map output; - for (size_t i = 0; i < vector_output.size(); ++i) { - const AllocationGroup* group = vector_output[i]; - output[group->name()] = group; - } - - int32 num_allocs = 0; - int64 total_bytes = 0; - - struct F { - static void PrintRow(std::stringstream* ss, const std::string& v1, - const std::string& v2, const std::string& v3) { - ss->width(25); - *ss << std::left << v1; - ss->width(13); - *ss << std::right << v2 << " "; - ss->width(10); - *ss << std::right << v3 << "\n"; - } - }; - - if (params->memory_tracker()->IsMemoryTrackingEnabled()) { - // If this isn't true then it would cause an infinite loop. The - // following will likely crash. - SB_DCHECK(false) << "Unexpected, memory tracking should be disabled."; - } - - std::stringstream ss; - - ss << kNewLine; - ss << "TimeNow " << params->TimeInMinutesString() - << " (minutes):" << kNewLine << kNewLine; - - ss << kSeperator << kNewLine; - MemoryStats memstats = GetProcessMemoryStats(); - - F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", ""); - ss << kSeperator << kNewLine; - F::PrintRow(&ss, "Total CPU Reserved", - NumberFormatWithCommas(memstats.total_cpu_memory), ""); - - F::PrintRow(&ss, "Total CPU Used", - NumberFormatWithCommas(memstats.used_cpu_memory), ""); - - F::PrintRow(&ss, "Total GPU Reserved", - NumberFormatWithCommas(memstats.total_gpu_memory), ""); - - F::PrintRow(&ss, "Total GPU Used", - NumberFormatWithCommas(memstats.used_gpu_memory), ""); - - ss << kSeperator << kNewLine << kNewLine; - - ss << kSeperator << kNewLine; - F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS"); - ss << kSeperator << kNewLine; - - for (MapIt it = output.begin(); it != output.end(); ++it) { - const AllocationGroup* group = it->second; - if (!group) { - continue; - } - - int32 num_group_allocs = -1; - int64 total_group_bytes = -1; - - group->GetAggregateStats(&num_group_allocs, &total_group_bytes); - SB_DCHECK(-1 != num_group_allocs); - SB_DCHECK(-1 != total_group_bytes); - - cvals_map_->Update(group->name(), static_cast<size_t>(total_group_bytes)); - - num_allocs += num_group_allocs; - total_bytes += total_group_bytes; - - F::PrintRow(&ss, it->first, NumberFormatWithCommas(total_group_bytes), - NumberFormatWithCommas(num_group_allocs)); - } - - cvals_map_->Update("Total", static_cast<size_t>(total_bytes)); - - ss << kNewLine; - - F::PrintRow(&ss, "Total (in groups above)", - NumberFormatWithCommas(total_bytes), - NumberFormatWithCommas(num_allocs)); - - ss << kSeperator << kNewLine; - ss << kNewLine << kNewLine; - - params->logger()->Output(ss.str().c_str()); - // Output once every 5 seconds. - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5)); - } -} - -PrintCSVTool::PrintCSVTool(int sampling_interval_ms, int sampling_time_ms) - : sample_interval_ms_(sampling_interval_ms), - sampling_time_ms_(sampling_time_ms) {} - -std::string PrintCSVTool::ToCsvString(const MapAllocationSamples& samples_in) { - typedef MapAllocationSamples Map; - typedef Map::const_iterator MapIt; - - size_t largest_sample_size = 0; - size_t smallest_sample_size = INT_MAX; - - // Sanitize samples_in and store as samples. - MapAllocationSamples samples; - for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) { - std::string name = it->first; - const AllocationSamples& value = it->second; - - if (value.allocated_bytes_.size() != value.number_allocations_.size()) { - SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__; - return "ERROR"; - } - - const size_t n = value.allocated_bytes_.size(); - if (n > largest_sample_size) { - largest_sample_size = n; - } - if (n < smallest_sample_size) { - smallest_sample_size = n; - } - - const bool duplicate_found = (samples.end() != samples.find(name)); - if (duplicate_found) { - SB_NOTREACHED() << "Error, duplicate found for entry: " << name - << kNewLine; - } - // Store value as a sanitized sample. - samples[name] = value; - } - - SB_DCHECK(largest_sample_size == smallest_sample_size); - - std::stringstream ss; - - // Begin output to CSV. - // Sometimes we need to skip the CPU memory entry. - const MapIt total_cpu_memory_it = samples.find(UntrackedMemoryKey()); - - // Preamble - ss << kNewLine << "//////////////////////////////////////////////"; - ss << kNewLine << "// CSV of bytes / allocation" << kNewLine; - // HEADER. - ss << "Name" << kDelimiter << kQuote << "Bytes/Alloc" << kQuote << kNewLine; - // DATA. - for (MapIt it = samples.begin(); it != samples.end(); ++it) { - if (total_cpu_memory_it == it) { - continue; - } - - const AllocationSamples& samples = it->second; - if (samples.allocated_bytes_.empty() || - samples.number_allocations_.empty()) { - SB_NOTREACHED() << "Should not be here"; - return "ERROR"; - } - const int64 n_allocs = samples.number_allocations_.back(); - const int64 n_bytes = samples.allocated_bytes_.back(); - int64 bytes_per_alloc = 0; - if (n_allocs > 0) { - bytes_per_alloc = n_bytes / n_allocs; - } - const std::string& name = it->first; - ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter - << bytes_per_alloc << kNewLine; - } - ss << kNewLine; - - // Preamble - ss << kNewLine << "//////////////////////////////////////////////" << kNewLine - << "// CSV of bytes allocated per region (MB's)." << kNewLine - << "// Units are in Megabytes. This is designed" << kNewLine - << "// to be used in a stacked graph." << kNewLine; - - // HEADER. - for (MapIt it = samples.begin(); it != samples.end(); ++it) { - if (total_cpu_memory_it == it) { - continue; - } - // Strip out any characters that could make parsing the csv difficult. - const std::string name = SanitizeCSVKey(it->first); - ss << kQuote << name << kQuote << kDelimiter; - } - // Save the total for last. - if (total_cpu_memory_it != samples.end()) { - const std::string& name = SanitizeCSVKey(total_cpu_memory_it->first); - ss << kQuote << name << kQuote << kDelimiter; - } - ss << kNewLine; - - // Print out the values of each of the samples. - for (size_t i = 0; i < smallest_sample_size; ++i) { - for (MapIt it = samples.begin(); it != samples.end(); ++it) { - if (total_cpu_memory_it == it) { - continue; - } - const int64 alloc_bytes = it->second.allocated_bytes_[i]; - // Convert to float megabytes with decimals of precision. - double n = alloc_bytes / (1000 * 10); - n = n / (100.); - ss << n << kDelimiter; - } - if (total_cpu_memory_it != samples.end()) { - const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i]; - // Convert to float megabytes with decimals of precision. - double n = alloc_bytes / (1000 * 10); - n = n / (100.); - ss << n << kDelimiter; - } - ss << kNewLine; - } - - ss << kNewLine; - // Preamble - ss << kNewLine << "//////////////////////////////////////////////"; - ss << kNewLine << "// CSV of number of allocations per region." << kNewLine; - - // HEADER - for (MapIt it = samples.begin(); it != samples.end(); ++it) { - if (total_cpu_memory_it == it) { - continue; - } - const std::string& name = SanitizeCSVKey(it->first); - ss << kQuote << name << kQuote << kDelimiter; - } - ss << kNewLine; - for (size_t i = 0; i < smallest_sample_size; ++i) { - for (MapIt it = samples.begin(); it != samples.end(); ++it) { - if (total_cpu_memory_it == it) { - continue; - } - const int64 n_allocs = it->second.number_allocations_[i]; - ss << n_allocs << kDelimiter; - } - ss << kNewLine; - } - std::string output = ss.str(); - return output; -} - -const char* PrintCSVTool::UntrackedMemoryKey() { return "Untracked Memory"; } - -void PrintCSVTool::Run(Params* params) { - params->logger()->Output("\nMemoryTrackerPrintCSVThread is sampling...\n"); - int sample_count = 0; - MapAllocationSamples map_samples; - - while (!TimeExpiredYet(*params) && !params->finished()) { - // Sample total memory used by the system. - MemoryStats mem_stats = GetProcessMemoryStats(); - int64 untracked_used_memory = - mem_stats.used_cpu_memory + mem_stats.used_gpu_memory; - - std::vector<const AllocationGroup*> vector_output; - params->memory_tracker()->GetAllocationGroups(&vector_output); - - // Sample all known memory scopes. - for (size_t i = 0; i < vector_output.size(); ++i) { - const AllocationGroup* group = vector_output[i]; - const std::string& name = group->name(); - - const bool first_creation = - map_samples.find(group->name()) == map_samples.end(); - - AllocationSamples* new_entry = &(map_samples[name]); - - // Didn't see it before so create new entry. - if (first_creation) { - // Make up for lost samples... - new_entry->allocated_bytes_.resize(sample_count, 0); - new_entry->number_allocations_.resize(sample_count, 0); - } - - int32 num_allocs = -1; - int64 allocation_bytes = -1; - group->GetAggregateStats(&num_allocs, &allocation_bytes); - - new_entry->allocated_bytes_.push_back(allocation_bytes); - new_entry->number_allocations_.push_back(num_allocs); - - untracked_used_memory -= allocation_bytes; - } - - // Now push in remaining total. - AllocationSamples* process_sample = &(map_samples[UntrackedMemoryKey()]); - if (untracked_used_memory < 0) { - // On some platforms, total GPU memory may not be correctly reported. - // However the allocations from the GPU memory may be reported. In this - // case untracked_used_memory will go negative. To protect the memory - // reporting the untracked_used_memory is set to 0 so that it doesn't - // cause an error in reporting. - untracked_used_memory = 0; - } - process_sample->allocated_bytes_.push_back(untracked_used_memory); - process_sample->number_allocations_.push_back(-1); - - ++sample_count; - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds(sample_interval_ms_)); - } - - std::stringstream ss; - ss.precision(2); - ss << "Time now: " << params->TimeInMinutesString() << ",\n"; - ss << ToCsvString(map_samples); - params->logger()->Output(ss.str().c_str()); - params->logger()->Flush(); - // Prevents the "thread exited code 0" from being interleaved into the - // output. This happens if flush is not implemented correctly in the system. - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); -} - -bool PrintCSVTool::TimeExpiredYet(const Params& params) { - base::TimeDelta dt = params.time_since_start(); - int64 dt_ms = dt.InMilliseconds(); - const bool expired_time = dt_ms > sampling_time_ms_; - return expired_time; -} - -MemorySizeBinner::MemorySizeBinner(const std::string& memory_scope_name) - : memory_scope_name_(memory_scope_name) {} - -const AllocationGroup* FindAllocationGroup(const std::string& name, - MemoryTracker* memory_tracker) { - std::vector<const AllocationGroup*> groups; - memory_tracker->GetAllocationGroups(&groups); - // Find by exact string match. - for (size_t i = 0; i < groups.size(); ++i) { - const AllocationGroup* group = groups[i]; - if (group->name().compare(name) == 0) { - return group; - } - } - return NULL; -} - -void MemorySizeBinner::Run(Params* params) { - const AllocationGroup* target_group = NULL; - - while (!params->finished()) { - if (target_group == NULL && !memory_scope_name_.empty()) { - target_group = - FindAllocationGroup(memory_scope_name_, params->memory_tracker()); - } - - std::stringstream ss; - ss.precision(2); - if (target_group || memory_scope_name_.empty()) { - AllocationSizeBinner visitor_binner = AllocationSizeBinner(target_group); - params->memory_tracker()->Accept(&visitor_binner); - - size_t min_size = 0; - size_t max_size = 0; - - visitor_binner.GetLargestSizeRange(&min_size, &max_size); - - FindTopSizes top_size_visitor = - FindTopSizes(min_size, max_size, target_group); - params->memory_tracker()->Accept(&top_size_visitor); - - ss << kNewLine; - ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):"; - ss << kNewLine; - if (!memory_scope_name_.empty()) { - ss << "Tracking Memory Scope \"" << memory_scope_name_ << "\", "; - } else { - ss << "Tracking whole program, "; - } - ss << "first row is allocation size range, second row is number of " - << kNewLine << "allocations in that range." << kNewLine; - ss << visitor_binner.ToCSVString(); - ss << kNewLine; - ss << "Largest allocation range: \"" << min_size << "..." << max_size - << "\"" << kNewLine; - ss << "Printing out top allocations from this range: " << kNewLine; - ss << top_size_visitor.ToString(5) << kNewLine; - } else { - ss << "No allocations for \"" << memory_scope_name_ << "\"."; - } - - params->logger()->Output(ss.str().c_str()); - params->logger()->Flush(); - - // Sleep until the next sample. - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); - } -} - size_t AllocationSizeBinner::GetBucketIndexForAllocationSize(size_t size) { for (int i = 0; i < 32; ++i) { size_t val = 0x1 << i;
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.h b/src/cobalt/browser/memory_tracker/tool/tool_impl.h index 5141d6a..9edef18 100644 --- a/src/cobalt/browser/memory_tracker/tool/tool_impl.h +++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
@@ -28,6 +28,7 @@ #include "base/threading/simple_thread.h" #include "base/time.h" #include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h" +#include "cobalt/browser/memory_tracker/tool/util.h" #include "nb/analytics/memory_tracker.h" #include "nb/analytics/memory_tracker_helpers.h" #include "nb/concurrent_map.h" @@ -72,89 +73,6 @@ virtual void Run(Params* params) = 0; }; -// Start() is called when this object is created, and Cancel() & Join() are -// called during destruction. -class PrintTool : public AbstractTool { - public: - PrintTool(); - ~PrintTool() OVERRIDE; - - // Overridden so that the thread can exit gracefully. - virtual void Run(Params* params) OVERRIDE; - virtual std::string tool_name() const OVERRIDE { - return "MemoryTrackerPrintThread"; - } - - private: - class CvalsMap; - scoped_ptr<CvalsMap> cvals_map_; -}; - -// Generates CSV values of the engine. -// There are three sections of data including: -// 1. average bytes / alloc -// 2. # Bytes allocated per memory scope. -// 3. # Allocations per memory scope. -// This data can be pasted directly into a Google spreadsheet and visualized. -// Note that this thread will implicitly call Start() is called during -// construction and Cancel() & Join() during destruction. -class PrintCSVTool : public AbstractTool { - public: - // This tool will only produce on CSV dump of the engine. This is useful - // for profiling startup memory consumption. - PrintCSVTool(int sampling_interval_ms, int sampling_time_ms); - - // Overridden so that the thread can exit gracefully. - virtual void Run(Params* params) OVERRIDE; - virtual std::string tool_name() const OVERRIDE { - return "MemoryTrackerPrintCSV"; - } - - private: - struct AllocationSamples { - std::vector<int32_t> number_allocations_; - std::vector<int64_t> allocated_bytes_; - }; - typedef std::map<std::string, AllocationSamples> MapAllocationSamples; - static std::string ToCsvString(const MapAllocationSamples& samples); - static const char* UntrackedMemoryKey(); - bool TimeExpiredYet(const Params& params); - - const int sample_interval_ms_; - const int sampling_time_ms_; -}; - -struct AllocationSamples { - std::vector<int32_t> number_allocations_; - std::vector<int64_t> allocated_bytes_; -}; -typedef std::map<std::string, AllocationSamples> MapAllocationSamples; -typedef std::vector<base::TimeDelta> TimeStamps; - -struct TimeSeries { - MapAllocationSamples samples_; - TimeStamps time_stamps_; -}; - -// This tool inspects a memory scope and reports on the memory usage. -// The output will be a CSV file printed to stdout representing -// the number of memory allocations for objects. The objects are binned -// according to the size of the memory allocation. Objects within the same -// power of two are binned together. For example 1024 will be binned with 1025. -class MemorySizeBinner : public AbstractTool { - public: - // memory_scope_name represents the memory scope that is to be investigated. - explicit MemorySizeBinner(const std::string& memory_scope_name); - - virtual void Run(Params* params) OVERRIDE; - virtual std::string tool_name() const OVERRIDE { - return "MemoryTrackerCompressedTimeSeries"; - } - - private: - std::string memory_scope_name_; -}; - // Bins the size according from all the encountered allocations. // If AllocationGroup* is non-null, then this is used as a filter such that // ONLY allocations belonging to that AllocationGroup are considered.
diff --git a/src/cobalt/browser/memory_tracker/tool/util.h b/src/cobalt/browser/memory_tracker/tool/util.h index 3d44f6a..aa34660 100644 --- a/src/cobalt/browser/memory_tracker/tool/util.h +++ b/src/cobalt/browser/memory_tracker/tool/util.h
@@ -15,8 +15,10 @@ #ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_ #define COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_ +#include <map> #include <sstream> #include <string> +#include <vector> #include "base/time.h" @@ -78,6 +80,18 @@ base::TimeDelta time_before_expiration_; }; +struct AllocationSamples { + std::vector<int32_t> number_allocations_; + std::vector<int64_t> allocated_bytes_; +}; +typedef std::map<std::string, AllocationSamples> MapAllocationSamples; +typedef std::vector<base::TimeDelta> TimeStamps; + +struct TimeSeries { + MapAllocationSamples samples_; + TimeStamps time_stamps_; +}; + // Generates a linear fit in the form of slope / y-intercept form. // Returns true if linear fit was calculated. False otherwise. Reasons for // returning false include passing in an empty range, such that
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc index 59d9850..5e2ffd0 100644 --- a/src/cobalt/browser/switches.cc +++ b/src/cobalt/browser/switches.cc
@@ -167,6 +167,12 @@ const char kSkiaTextureAtlasDimensions[] = "skia_atlas_texture_dimensions"; +// Specifies the maximum CPU usage of the cobalt. +const char kMaxCobaltCpuUsage[] = "max_cobalt_cpu_usage"; + +// Specifies the maximum GPU usage of the cobalt. +const char kMaxCobaltGpuUsage[] = "max_cobalt_gpu_usage"; + } // namespace switches } // namespace browser } // namespace cobalt
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h index 6b30b2b..9214784 100644 --- a/src/cobalt/browser/switches.h +++ b/src/cobalt/browser/switches.h
@@ -59,6 +59,9 @@ extern const char kJavaScriptGcThresholdInBytes[]; extern const char kSkiaTextureAtlasDimensions[]; +extern const char kMaxCobaltCpuUsage[]; +extern const char kMaxCobaltGpuUsage[]; + } // namespace switches } // namespace browser } // namespace cobalt
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc index 5087c2d..2b6aad6 100644 --- a/src/cobalt/browser/web_module.cc +++ b/src/cobalt/browser/web_module.cc
@@ -779,7 +779,9 @@ debug_overlay_->ClearInput(); #endif - // Finally mark that we have no resource provider. + // Finally purge the resource provider's caches and mark that we have no + // resource provider. + resource_provider_->PurgeCaches(); resource_provider_ = NULL; } @@ -840,8 +842,7 @@ : name("WebModule"), layout_trigger(layout::LayoutManager::kOnDocumentMutation), image_cache_capacity(32 * 1024 * 1024), - remote_typeface_cache_capacity( - COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES), + remote_typeface_cache_capacity(4 * 1024 * 1024), mesh_cache_capacity(COBALT_MESH_CACHE_SIZE_IN_BYTES), csp_enforcement_mode(dom::kCspEnforcementEnable), csp_insecure_allowed_token(0),
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index 86bfd59..9f62695 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -51583 \ No newline at end of file +52708 \ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi index 339b511..ddfd269 100644 --- a/src/cobalt/build/config/base.gypi +++ b/src/cobalt/build/config/base.gypi
@@ -33,9 +33,15 @@ # implement spherical video playback. 'enable_map_to_mesh%': 0, - # Enables embedding Cobalt as a shared library within another app. This - # requires a 'lib' starboard implementation for the corresponding platform. - 'cobalt_enable_lib%': 0, + # 'sb_enable_lib' is initially defined inside this inner 'variables' dict so + # that it can be accessed by 'cobalt_enable_lib' below here. + 'variables': { + # Enables embedding Cobalt as a shared library within another app. This + # requires a 'lib' starboard implementation for the corresponding platform. + 'sb_enable_lib%': 0, + }, + 'sb_enable_lib%': '<(sb_enable_lib)', + 'cobalt_enable_lib': '<(sb_enable_lib)', # Contains the current font package selection. This can be used to trade # font quality, coverage, and latency for different font package sizes. @@ -325,6 +331,18 @@ # from 8MB to 1MB. 'mozjs_garbage_collection_threshold_in_bytes%': 8 * 1024 * 1024, + # Max Cobalt CPU usage specifies that the cobalt program should + # keep it's size below the specified size. A value of -1 causes this + # value to be assumed from the starboard API function: + # SbSystemGetTotalCPUMemory(). + 'max_cobalt_cpu_usage%': -1, + + # Max Cobalt GPU usage specifies that the cobalt program should + # keep it's size below the specified size. A value of -1 causes this + # value to be assumed from the starboard API function: + # SbSystemGetTotalGPUMemory(). + 'max_cobalt_gpu_usage%': -1, + # Compiler configuration. # The following variables are used to specify compiler and linker
diff --git a/src/cobalt/doc/spherical_video.md b/src/cobalt/doc/spherical_video.md index 2a5adf4..f117617 100644 --- a/src/cobalt/doc/spherical_video.md +++ b/src/cobalt/doc/spherical_video.md
@@ -7,7 +7,7 @@ for spherical video in Cobalt requires a GLES rasterizer (i.e. it is not supported for the Starboard Blitter API), and Starboard platform support for the player -[decode-to-texture output mode](../starboard/doc/howto_decode_to_texture.md). +[decode-to-texture output mode](../../starboard/doc/howto_decode_to_texture.md). ## Enabling spherical video support @@ -37,4 +37,4 @@ ``` Finally, it is required that your platform provides -[decode-to-texture support](../starboard/doc/howto_decode_to_texture.md). +[decode-to-texture support](../../starboard/doc/howto_decode_to_texture.md).
diff --git a/src/cobalt/dom/comment_test.cc b/src/cobalt/dom/comment_test.cc index 1e9cf83..00de4e0 100644 --- a/src/cobalt/dom/comment_test.cc +++ b/src/cobalt/dom/comment_test.cc
@@ -38,9 +38,7 @@ scoped_refptr<Document> document_; }; -CommentTest::CommentTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") { +CommentTest::CommentTest() { EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks()); document_ = new Document(&html_element_context_); }
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc index 2f3ad0e..754ee9b 100644 --- a/src/cobalt/dom/document.cc +++ b/src/cobalt/dom/document.cc
@@ -43,6 +43,7 @@ #include "cobalt/dom/html_element_factory.h" #include "cobalt/dom/html_head_element.h" #include "cobalt/dom/html_html_element.h" +#include "cobalt/dom/html_script_element.h" #include "cobalt/dom/initial_computed_style.h" #include "cobalt/dom/keyframes_map_updater.h" #include "cobalt/dom/location.h" @@ -52,7 +53,6 @@ #include "cobalt/dom/ui_event.h" #include "cobalt/dom/window.h" #include "cobalt/script/global_environment.h" - #include "nb/memory_scope.h" namespace cobalt { @@ -197,7 +197,7 @@ scoped_refptr<Element> Document::CreateElementNS( const std::string& namespace_uri, const std::string& local_name) { // TODO: Implement namespaces, if we actually need this. - NOTIMPLEMENTED() << namespace_uri; + UNREFERENCED_PARAMETER(namespace_uri); return CreateElement(local_name); } @@ -700,6 +700,25 @@ ->DisableJit(); } +void Document::TraceMembers(script::Tracer* tracer) { + Node::TraceMembers(tracer); + + tracer->Trace(implementation_); + tracer->Trace(style_sheets_); + for (std::deque<HTMLScriptElement*>::iterator it = + scripts_to_be_executed_.begin(); + it != scripts_to_be_executed_.end(); ++it) { + tracer->Trace(static_cast<Wrappable*>(*it)); + } + for (cssom::CSSKeyframesRule::NameMap::iterator it = keyframes_map_.begin(); + it != keyframes_map_.end(); ++it) { + tracer->Trace(it->second); + } + tracer->Trace(location_); + tracer->Trace(user_agent_style_sheet_); + tracer->Trace(initial_computed_style_declaration_); +} + void Document::DispatchOnLoadEvent() { TRACE_EVENT0("cobalt::dom", "Document::DispatchOnLoadEvent()");
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h index f1e121b..4e63114 100644 --- a/src/cobalt/dom/document.h +++ b/src/cobalt/dom/document.h
@@ -331,6 +331,8 @@ // Disable just-in-time compilation of JavaScript code. void DisableJit(); + void TraceMembers(script::Tracer* tracer) OVERRIDE; + DEFINE_WRAPPABLE_TYPE(Document); protected:
diff --git a/src/cobalt/dom/document_type_test.cc b/src/cobalt/dom/document_type_test.cc index e2e7efe..2dabb5c 100644 --- a/src/cobalt/dom/document_type_test.cc +++ b/src/cobalt/dom/document_type_test.cc
@@ -23,10 +23,7 @@ class DocumentTypeTest : public ::testing::Test { protected: - DocumentTypeTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""), - document_(new Document(&html_element_context_)) {} + DocumentTypeTest() : document_(new Document(&html_element_context_)) {} ~DocumentTypeTest() OVERRIDE {} HTMLElementContext html_element_context_;
diff --git a/src/cobalt/dom/dom_implementation.cc b/src/cobalt/dom/dom_implementation.cc index e7c73c7..cda0129 100644 --- a/src/cobalt/dom/dom_implementation.cc +++ b/src/cobalt/dom/dom_implementation.cc
@@ -15,6 +15,7 @@ #include "cobalt/dom/dom_implementation.h" #include "cobalt/dom/dom_settings.h" +#include "cobalt/dom/element.h" #include "cobalt/dom/window.h" namespace cobalt { @@ -32,13 +33,38 @@ return CreateDocument(namespace_name, qualified_name, NULL); } +// Algorithm for CreateDocument: +// https://www.w3.org/TR/dom/#dom-domimplementation-createdocument scoped_refptr<XMLDocument> DOMImplementation::CreateDocument( base::optional<std::string> namespace_name, const std::string& qualified_name, scoped_refptr<DocumentType> doctype) { - UNREFERENCED_PARAMETER(namespace_name); - UNREFERENCED_PARAMETER(qualified_name); - DCHECK(!doctype); - return new XMLDocument(html_element_context_); + UNREFERENCED_PARAMETER(doctype); + + // 1. Let document be a new XMLDocument. + scoped_refptr<XMLDocument> document = new XMLDocument(html_element_context_); + + // 2. Let element be null. + scoped_refptr<Element> element; + + // 3. If qualifiedName is not the empty string, set element to the result of + // invoking the createElementNS() method with the arguments namespace and + // qualifiedName on document. Rethrow any exceptions. + if (!qualified_name.empty()) { + element = + document->CreateElementNS(namespace_name.value_or(""), qualified_name); + } + + // 4. Not needed by Cobalt. + + // 5. If element is not null, append element to document. + if (element) { + document->AppendChild(element); + } + + // 6. Not needed by Cobalt. + + // 7. Return document. + return document; } } // namespace dom
diff --git a/src/cobalt/dom/dom_implementation.h b/src/cobalt/dom/dom_implementation.h index 66a04f6..06d5f99 100644 --- a/src/cobalt/dom/dom_implementation.h +++ b/src/cobalt/dom/dom_implementation.h
@@ -33,7 +33,7 @@ // The DOMImplementation interface represent an object providing methods which // are not dependent on any particular document. Such an object is returned by // the Document.implementation property. -// https://www.w3.org/TR/2015/WD-dom-20150618/#interface-domimplementation +// https://www.w3.org/TR/dom/#domimplementation class DOMImplementation : public script::Wrappable { public: explicit DOMImplementation(script::EnvironmentSettings* settings);
diff --git a/src/cobalt/dom/dom_implementation_test.cc b/src/cobalt/dom/dom_implementation_test.cc index f161a30..54336f5 100644 --- a/src/cobalt/dom/dom_implementation_test.cc +++ b/src/cobalt/dom/dom_implementation_test.cc
@@ -16,6 +16,7 @@ #include "base/memory/ref_counted.h" #include "cobalt/dom/document.h" +#include "cobalt/dom/element.h" #include "cobalt/dom/html_element_context.h" #include "cobalt/dom/xml_document.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,15 +24,27 @@ namespace cobalt { namespace dom { -TEST(DOMImplementationTest, CreateDocumentShouldCreateXMLDocument) { - HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, ""); +TEST(DOMImplementationTest, CreateDocumentWithEmptyName) { + HTMLElementContext html_element_context; scoped_refptr<DOMImplementation> dom_implementation = new DOMImplementation(&html_element_context); scoped_refptr<Document> document = dom_implementation->CreateDocument(base::optional<std::string>(""), ""); + ASSERT_TRUE(document); EXPECT_TRUE(document->IsXMLDocument()); + EXPECT_FALSE(document->first_element_child()); +} + +TEST(DOMImplementationTest, CreateDocumentWithName) { + HTMLElementContext html_element_context; + scoped_refptr<DOMImplementation> dom_implementation = + new DOMImplementation(&html_element_context); + scoped_refptr<Document> document = dom_implementation->CreateDocument( + base::optional<std::string>(""), "ROOT"); + ASSERT_TRUE(document); + EXPECT_TRUE(document->IsXMLDocument()); + ASSERT_TRUE(document->first_element_child()); + EXPECT_EQ("ROOT", document->first_element_child()->tag_name()); } } // namespace dom
diff --git a/src/cobalt/dom/dom_string_map_test.cc b/src/cobalt/dom/dom_string_map_test.cc index cf8a9a5..545dd07 100644 --- a/src/cobalt/dom/dom_string_map_test.cc +++ b/src/cobalt/dom/dom_string_map_test.cc
@@ -42,9 +42,7 @@ }; DOMStringMapTest::DOMStringMapTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""), - document_(new Document(&html_element_context_)), + : document_(new Document(&html_element_context_)), element_(new Element(document_, base::Token("element"))), dom_string_map_(new DOMStringMap(element_)) {}
diff --git a/src/cobalt/dom/dom_token_list_test.cc b/src/cobalt/dom/dom_token_list_test.cc index a40f8be..c39a226 100644 --- a/src/cobalt/dom/dom_token_list_test.cc +++ b/src/cobalt/dom/dom_token_list_test.cc
@@ -36,9 +36,7 @@ scoped_refptr<Document> document_; }; -DOMTokenListTest::DOMTokenListTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") { +DOMTokenListTest::DOMTokenListTest() { EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks()); document_ = new Document(&html_element_context_); }
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc index 6a77ff0..b923be1 100644 --- a/src/cobalt/dom/event_target.cc +++ b/src/cobalt/dom/event_target.cc
@@ -149,6 +149,12 @@ return !event_listener_infos_.empty(); } +void EventTarget::TraceMembers(script::Tracer* tracer) { + UNREFERENCED_PARAMETER(tracer); + // TODO: EventListenerInfo references can be removed and logically live here + // instead. +} + void EventTarget::FireEventOnListeners(const scoped_refptr<Event>& event) { DCHECK(event->IsBeingDispatched()); DCHECK(event->target());
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h index 6ec9450..a31a414 100644 --- a/src/cobalt/dom/event_target.h +++ b/src/cobalt/dom/event_target.h
@@ -135,6 +135,8 @@ // Returns a string that represents the target for debug purpose. virtual std::string GetDebugName() { return ""; } + void TraceMembers(script::Tracer* tracer) OVERRIDE; + DEFINE_WRAPPABLE_TYPE(EventTarget); protected:
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc index 3cf96f0..b7aeed0 100644 --- a/src/cobalt/dom/html_element_context.cc +++ b/src/cobalt/dom/html_element_context.cc
@@ -19,6 +19,27 @@ namespace cobalt { namespace dom { +HTMLElementContext::HTMLElementContext() + : fetcher_factory_(NULL), + css_parser_(NULL), + dom_parser_(NULL), + can_play_type_handler_(NULL), + web_media_player_factory_(NULL), + script_runner_(NULL), + script_value_factory_(NULL), + media_source_registry_(NULL), + resource_provider_(NULL), + animated_image_tracker_(NULL), + image_cache_(NULL), + reduced_image_cache_capacity_manager_(NULL), + remote_typeface_cache_(NULL), + mesh_cache_(NULL), + dom_stat_tracker_(NULL), + sync_load_thread_("Synchronous Load"), + html_element_factory_(new HTMLElementFactory()) { + sync_load_thread_.Start(); +} + HTMLElementContext::HTMLElementContext( loader::FetcherFactory* fetcher_factory, cssom::CSSParser* css_parser, Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h index 4650c31..cae6ad8 100644 --- a/src/cobalt/dom/html_element_context.h +++ b/src/cobalt/dom/html_element_context.h
@@ -46,6 +46,7 @@ public: typedef UrlRegistry<MediaSource> MediaSourceRegistry; + HTMLElementContext(); HTMLElementContext( loader::FetcherFactory* fetcher_factory, cssom::CSSParser* css_parser, Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
diff --git a/src/cobalt/dom/named_node_map_test.cc b/src/cobalt/dom/named_node_map_test.cc index d99df3b..fe8a605 100644 --- a/src/cobalt/dom/named_node_map_test.cc +++ b/src/cobalt/dom/named_node_map_test.cc
@@ -38,9 +38,7 @@ scoped_refptr<Element> element_; }; -NamedNodeMapTest::NamedNodeMapTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") { +NamedNodeMapTest::NamedNodeMapTest() { EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks()); document_ = new Document(&html_element_context_); element_ = new Element(document_, base::Token("element"));
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc index fdc872e..b1f0569 100644 --- a/src/cobalt/dom/node.cc +++ b/src/cobalt/dom/node.cc
@@ -435,6 +435,22 @@ scoped_refptr<Text> Node::AsText() { return NULL; } +void Node::TraceMembers(script::Tracer* tracer) { + EventTarget::TraceMembers(tracer); + + tracer->Trace(node_document_); + tracer->Trace(parent_); + tracer->Trace(previous_sibling_); + tracer->Trace(last_child_); + tracer->Trace(first_child_); + tracer->Trace(next_sibling_); + for (RegisteredObserverList::RegisteredObserverVector::const_iterator it = + registered_observers_.registered_observers().begin(); + it != registered_observers_.registered_observers().end(); ++it) { + tracer->Trace(it->observer()); + } +} + Node::Node(Document* document) : node_document_(base::AsWeakPtr(document)), parent_(NULL),
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h index 695ce4d..e1fbecb 100644 --- a/src/cobalt/dom/node.h +++ b/src/cobalt/dom/node.h
@@ -233,6 +233,8 @@ registered_observers_.RemoveMutationObserver(observer); } + void TraceMembers(script::Tracer* tracer) OVERRIDE; + DEFINE_WRAPPABLE_TYPE(Node); protected:
diff --git a/src/cobalt/dom/node_dispatch_event_test.cc b/src/cobalt/dom/node_dispatch_event_test.cc index 5758126..997fafe 100644 --- a/src/cobalt/dom/node_dispatch_event_test.cc +++ b/src/cobalt/dom/node_dispatch_event_test.cc
@@ -56,9 +56,7 @@ scoped_ptr<MockEventListener> event_listener_bubbling_; }; -NodeDispatchEventTest::NodeDispatchEventTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") { +NodeDispatchEventTest::NodeDispatchEventTest() { EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks()); document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/node_test.cc b/src/cobalt/dom/node_test.cc index cd8e4f1..36b0330 100644 --- a/src/cobalt/dom/node_test.cc +++ b/src/cobalt/dom/node_test.cc
@@ -74,9 +74,7 @@ scoped_refptr<Document> document_; }; -NodeTest::NodeTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") { +NodeTest::NodeTest() { EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks()); document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/text_test.cc b/src/cobalt/dom/text_test.cc index 75ab826..3ec5550 100644 --- a/src/cobalt/dom/text_test.cc +++ b/src/cobalt/dom/text_test.cc
@@ -37,9 +37,7 @@ scoped_refptr<Document> document_; }; -TextTest::TextTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") { +TextTest::TextTest() { EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks()); document_ = new Document(&html_element_context_); }
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc index 665126d..a270c9d 100644 --- a/src/cobalt/dom/window.cc +++ b/src/cobalt/dom/window.cc
@@ -420,6 +420,20 @@ document_->set_synchronous_layout_callback(synchronous_layout_callback); } +void Window::TraceMembers(script::Tracer* tracer) { + tracer->Trace(performance_); + tracer->Trace(document_); + tracer->Trace(history_); + tracer->Trace(navigator_); + tracer->Trace(console_); + tracer->Trace(camera_3d_); + tracer->Trace(crypto_); + tracer->Trace(speech_synthesis_); + tracer->Trace(local_storage_); + tracer->Trace(session_storage_); + tracer->Trace(screen_); +} + Window::~Window() {} void Window::FireHashChangeEvent() {
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h index 36eddab..7a90c00 100644 --- a/src/cobalt/dom/window.h +++ b/src/cobalt/dom/window.h
@@ -278,6 +278,8 @@ void SetSynchronousLayoutCallback( const base::Closure& synchronous_layout_callback); + void TraceMembers(script::Tracer* tracer) OVERRIDE; + DEFINE_WRAPPABLE_TYPE(Window); private:
diff --git a/src/cobalt/dom/xml_document_test.cc b/src/cobalt/dom/xml_document_test.cc index d4294eb..c1c11a7 100644 --- a/src/cobalt/dom/xml_document_test.cc +++ b/src/cobalt/dom/xml_document_test.cc
@@ -23,9 +23,7 @@ namespace dom { TEST(XMLDocumentTest, IsXMLDocument) { - HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, ""); + HTMLElementContext html_element_context; scoped_refptr<Document> document = new XMLDocument(&html_element_context); EXPECT_TRUE(document->IsXMLDocument()); }
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc index b85ba7a..5b7e6bc 100644 --- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc +++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -293,7 +293,7 @@ DCHECK(severity >= kWarning && severity <= kFatal); xmlErrorPtr error = xmlGetLastError(); - if (error->code == XML_HTML_UNKNOWN_TAG) { + if (error && error->code == XML_HTML_UNKNOWN_TAG) { return; }
diff --git a/src/cobalt/dom_parser/xml_decoder_test.cc b/src/cobalt/dom_parser/xml_decoder_test.cc index 3fb2732..725a989 100644 --- a/src/cobalt/dom_parser/xml_decoder_test.cc +++ b/src/cobalt/dom_parser/xml_decoder_test.cc
@@ -48,9 +48,7 @@ }; XMLDecoderTest::XMLDecoderTest() - : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""), - document_(new dom::XMLDocument(&html_element_context_)), + : document_(new dom::XMLDocument(&html_element_context_)), source_location_(base::SourceLocation("[object XMLDecoderTest]", 1, 1)) {} TEST_F(XMLDecoderTest, ShouldNotAddImpliedTags) {
diff --git a/src/cobalt/media/filters/source_buffer_stream.h b/src/cobalt/media/filters/source_buffer_stream.h index 6fe9229..edeac1a 100644 --- a/src/cobalt/media/filters/source_buffer_stream.h +++ b/src/cobalt/media/filters/source_buffer_stream.h
@@ -151,7 +151,7 @@ class MEDIA_EXPORT SourceBufferStream : private SourceBufferStreamState { public: using SourceBufferStreamState::BufferQueue; - using SourceBufferStreamState::RangeList; + typedef SourceBufferStreamState::RangeList RangeList; // Status returned by GetNextBuffer(). // kSuccess: Indicates that the next buffer was returned.
diff --git a/src/cobalt/media/shell_video_data_allocator_common.cc b/src/cobalt/media/shell_video_data_allocator_common.cc index f1b4e6f..8cbb35c 100644 --- a/src/cobalt/media/shell_video_data_allocator_common.cc +++ b/src/cobalt/media/shell_video_data_allocator_common.cc
@@ -131,6 +131,13 @@ // issues. gfx::Size plane_size(param.visible_rect().size()); + // When plane_size.height() is only aligned to 2, the height of U/V plane + // will be odd, which is not supported on most platforms. Align height to 4 + // to work around this issue. + if (param.decoded_height() % 4 == 0 && plane_size.height() % 4 != 0) { + plane_size.set_height((plane_size.height() + 3) / 4 * 4); + } + intptr_t offset = 0; int pitch_in_bytes = param.y_pitch();
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h index 1d13b6c..0fba09f 100644 --- a/src/cobalt/render_tree/mock_resource_provider.h +++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -164,6 +164,8 @@ Mesh::DrawMode draw_mode) { return make_scoped_refptr(CreateMeshMock(vertices.get(), draw_mode)); } + + virtual void PurgeCaches() {} }; } // namespace render_tree
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h index 69e0f6c..be1c28e 100644 --- a/src/cobalt/render_tree/resource_provider.h +++ b/src/cobalt/render_tree/resource_provider.h
@@ -218,6 +218,9 @@ virtual scoped_refptr<Image> DrawOffscreenImage( const scoped_refptr<render_tree::Node>& root) = 0; + + // Purges any caches being used by the render_tree consumer. + virtual void PurgeCaches() = 0; }; } // namespace render_tree
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h index 5d33cd0..0f70147 100644 --- a/src/cobalt/render_tree/resource_provider_stub.h +++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -331,6 +331,8 @@ UNREFERENCED_PARAMETER(root); return scoped_refptr<Image>(NULL); } + + void PurgeCaches() OVERRIDE {} }; } // namespace render_tree
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc index 2315836..f72fafa 100644 --- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc +++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -213,6 +213,8 @@ new SinglePlaneImage(root, submit_offscreen_callback_, device_)); } +void ResourceProvider::PurgeCaches() { skia_resource_provider_->PurgeCaches(); } + } // namespace blitter } // namespace rasterizer } // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h index 3f01c64..bf67280 100644 --- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h +++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -113,6 +113,8 @@ scoped_refptr<render_tree::Image> DrawOffscreenImage( const scoped_refptr<render_tree::Node>& root) OVERRIDE; + void PurgeCaches() OVERRIDE; + private: SbBlitterDevice device_;
diff --git a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc index 43a1a0d..955ea45 100644 --- a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc +++ b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
@@ -25,8 +25,8 @@ return scoped_ptr<rasterizer::Rasterizer>( new rasterizer::lib::ExternalRasterizer( graphics_context, - options.skia_texture_atlas_dimensions.width(), - options.skia_texture_atlas_dimensions.height(), + options.skia_glyph_texture_atlas_dimensions.width(), + options.skia_glyph_texture_atlas_dimensions.height(), options.skia_cache_size_in_bytes, options.scratch_surface_cache_size_in_bytes, options.surface_cache_size_in_bytes));
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc index a892498..504faab 100644 --- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc +++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -380,6 +380,13 @@ self_message_loop_)); } +void HardwareResourceProvider::PurgeCaches() { + SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault()); + SkFontMgr_Cobalt* cobalt_font_manager = + base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager.get()); + cobalt_font_manager->PurgeCaches(); +} + } // namespace skia } // namespace rasterizer } // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h index 6532ff2..5fe7814 100644 --- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h +++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -136,6 +136,8 @@ scoped_refptr<render_tree::Image> DrawOffscreenImage( const scoped_refptr<render_tree::Node>& root) OVERRIDE; + void PurgeCaches() OVERRIDE; + private: backend::GraphicsContextEGL* cobalt_context_; GrContext* gr_context_;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc index 206c6e3..6160f4f 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
@@ -253,23 +253,25 @@ kOtherElementType, }; -// The FamilyData structure is passed around by the parser so that each handler -// can read these variables that are relevant to the current parsing. -struct FamilyData { - explicit FamilyData(SkTDArray<FontFamily*>* families_array) +// The ParserContext structure is passed around by the parser so that each +// handler can read these variables that are relevant to the current parsing. +struct ParserContext { + explicit ParserContext(SkTDArray<FontFamilyInfo*>* families_array) : families(families_array), current_font_info(NULL) {} // The array that each family is put into as it is parsed - SkTDArray<FontFamily*>* families; + SkTDArray<FontFamilyInfo*>* families; // The current family being created. FamilyData owns this object while it is // not NULL. - scoped_ptr<FontFamily> current_family; - // The current fontInfo being created. It is owned by FontFamily. + scoped_ptr<FontFamilyInfo> current_family; + // The current FontFileInfo being created. It is owned by FontFamilyInfo. FontFileInfo* current_font_info; + + // Contains all of the elements that are actively being parsed. std::stack<ElementType> element_stack; }; -void FamilyElementHandler(FontFamily* family, const char** attributes) { +void FamilyElementHandler(FontFamilyInfo* family, const char** attributes) { if (attributes == NULL) { return; } @@ -399,10 +401,10 @@ } } -FontFamily* FindFamily(FamilyData* family_data, const char* family_name) { +FontFamilyInfo* FindFamily(ParserContext* context, const char* family_name) { size_t name_len = strlen(family_name); - for (int i = 0; i < family_data->families->count(); i++) { - FontFamily* candidate = (*family_data->families)[i]; + for (int i = 0; i < context->families->count(); i++) { + FontFamilyInfo* candidate = (*context->families)[i]; for (int j = 0; j < candidate->names.count(); j++) { if (!strncmp(candidate->names[j].c_str(), family_name, name_len) && name_len == strlen(candidate->names[j].c_str())) { @@ -414,7 +416,7 @@ return NULL; } -void AliasElementHandler(FamilyData* family_data, const char** attributes) { +void AliasElementHandler(ParserContext* context, const char** attributes) { // An <alias> must have name and to attributes. // It is a variant name for a <family>. @@ -434,7 +436,7 @@ } // Assumes that the named family is already declared - FontFamily* target_family = FindFamily(family_data, to.c_str()); + FontFamilyInfo* target_family = FindFamily(context, to.c_str()); if (!target_family) { LOG(ERROR) << "---- Invalid alias target [name: " << alias_name.c_str() << ", to: " << to.c_str() << "]"; @@ -449,55 +451,56 @@ target_family->names.push_back().set(alias_name); } -void StartElement(void* data, const xmlChar* xml_tag, +void StartElement(void* context, const xmlChar* xml_tag, const xmlChar** xml_attribute_pairs) { - FamilyData* family_data = reinterpret_cast<FamilyData*>(data); + ParserContext* parser_context = reinterpret_cast<ParserContext*>(context); const char* tag = reinterpret_cast<const char*>(xml_tag); const char** attribute_pairs = reinterpret_cast<const char**>(xml_attribute_pairs); size_t tag_len = strlen(tag); if (tag_len == 6 && strncmp("family", tag, tag_len) == 0) { - family_data->element_stack.push(kFamilyElementType); - family_data->current_family = make_scoped_ptr(new FontFamily()); - FamilyElementHandler(family_data->current_family.get(), attribute_pairs); + parser_context->element_stack.push(kFamilyElementType); + parser_context->current_family = make_scoped_ptr(new FontFamilyInfo()); + FamilyElementHandler(parser_context->current_family.get(), attribute_pairs); } else if (tag_len == 4 && strncmp("font", tag, tag_len) == 0) { - family_data->element_stack.push(kFontElementType); - FontFileInfo* file = &family_data->current_family->fonts.push_back(); - family_data->current_font_info = file; + parser_context->element_stack.push(kFontElementType); + FontFileInfo* file = &parser_context->current_family->fonts.push_back(); + parser_context->current_font_info = file; FontElementHandler(file, attribute_pairs); } else if (tag_len == 5 && strncmp("alias", tag, tag_len) == 0) { - family_data->element_stack.push(kAliasElementType); - AliasElementHandler(family_data, attribute_pairs); + parser_context->element_stack.push(kAliasElementType); + AliasElementHandler(parser_context, attribute_pairs); } else { - family_data->element_stack.push(kOtherElementType); + parser_context->element_stack.push(kOtherElementType); } } -void EndElement(void* data, const xmlChar* xml_tag) { - FamilyData* family_data = reinterpret_cast<FamilyData*>(data); +void EndElement(void* context, const xmlChar* xml_tag) { + ParserContext* parser_context = reinterpret_cast<ParserContext*>(context); const char* tag = reinterpret_cast<const char*>(xml_tag); size_t tag_len = strlen(tag); if (tag_len == 6 && strncmp("family", tag, tag_len) == 0) { - if (family_data->current_family != NULL) { - *family_data->families->append() = family_data->current_family.release(); + if (parser_context->current_family != NULL) { + *parser_context->families->append() = + parser_context->current_family.release(); } else { LOG(ERROR) << "---- Encountered end family tag with no current family"; NOTREACHED(); } } - family_data->element_stack.pop(); + parser_context->element_stack.pop(); } -void Characters(void* data, const xmlChar* xml_characters, int len) { - FamilyData* family_data = reinterpret_cast<FamilyData*>(data); +void Characters(void* context, const xmlChar* xml_characters, int len) { + ParserContext* parser_context = reinterpret_cast<ParserContext*>(context); const char* characters = reinterpret_cast<const char*>(xml_characters); - if (family_data->element_stack.size() > 0 && - family_data->element_stack.top() == kFontElementType) { - family_data->current_font_info->file_name.set(characters, len); + if (parser_context->element_stack.size() > 0 && + parser_context->element_stack.top() == kFontElementType) { + parser_context->current_font_info->file_name.set(characters, len); } } @@ -529,7 +532,8 @@ // This function parses the given filename and stores the results in the given // families array. -void ParseConfigFile(const char* directory, SkTDArray<FontFamily*>* families) { +void ParseConfigFile(const char* directory, + SkTDArray<FontFamilyInfo*>* families) { SkString file_path = SkOSPath::Join(directory, kConfigFile); SkAutoTUnref<SkStream> file_stream(SkStream::NewFromFile(file_path.c_str())); @@ -545,8 +549,8 @@ return; } - FamilyData family_data(families); - xmlSAXUserParseMemory(&xml_sax_handler, &family_data, + ParserContext parser_context(families); + xmlSAXUserParseMemory(&xml_sax_handler, &parser_context, static_cast<const char*>(file_data->data()), static_cast<int>(file_data->size())); } @@ -558,7 +562,7 @@ // Loads data on font families from the configuration file. The resulting data // is returned in the given fontFamilies array. void GetFontFamilies(const char* directory, - SkTDArray<FontFamily*>* font_families) { + SkTDArray<FontFamilyInfo*>* font_families) { ParseConfigFile(directory, font_families); }
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h index d2cb6d9..40e00d4 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h
@@ -23,7 +23,7 @@ // Parses all system font configuration files and returns the results in an // array of FontFamily structures. void GetFontFamilies(const char* directory, - SkTDArray<FontFamily*>* font_families); + SkTDArray<FontFamilyInfo*>* font_families); } // namespace SkFontConfigParser
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc index 850a112..36d4b6f 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
@@ -18,6 +18,7 @@ #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h" #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h" #include "SkData.h" +#include "SkGraphics.h" #include "SkStream.h" #include "SkString.h" #include "SkTSearch.h" @@ -59,38 +60,43 @@ FindDefaultFamily(default_families); } +void SkFontMgr_Cobalt::PurgeCaches() { + SkGraphics::PurgeFontCache(); + local_typeface_stream_manager_.PurgeUnusedMemoryChunks(); +} + SkTypeface* SkFontMgr_Cobalt::MatchFaceName(const char face_name[]) { - if (!face_name) { + if (face_name == NULL) { return NULL; } SkAutoAsciiToLC face_name_to_lc(face_name); std::string face_name_string(face_name_to_lc.lc(), face_name_to_lc.length()); - // Lock the style sets mutex prior to accessing them. - SkAutoMutexAcquire scoped_mutex(style_sets_mutex_); + // Lock the family mutex prior to accessing them. + SkAutoMutexAcquire scoped_mutex(family_mutex_); // Prioritize looking up the postscript name first since some of our client // applications prefer this method to specify face names. for (int i = 0; i <= 1; ++i) { - NameToStyleSetMap& name_to_style_set_map = - i == 0 ? font_postscript_name_to_style_set_map_ - : full_font_name_to_style_set_map_; + NameToStyleSetMap& name_to_family_map = + i == 0 ? font_postscript_name_to_family_map_ + : full_font_name_to_family_map_; - NameToStyleSetMap::iterator style_set_iterator = - name_to_style_set_map.find(face_name_string); - if (style_set_iterator != name_to_style_set_map.end()) { - SkFontStyleSet_Cobalt* style_set = style_set_iterator->second; + NameToStyleSetMap::iterator family_map_iterator = + name_to_family_map.find(face_name_string); + if (family_map_iterator != name_to_family_map.end()) { + SkFontStyleSet_Cobalt* family = family_map_iterator->second; SkTypeface* typeface = - i == 0 ? style_set->MatchFontPostScriptName(face_name_string) - : style_set->MatchFullFontName(face_name_string); + i == 0 ? family->MatchFontPostScriptName(face_name_string) + : family->MatchFullFontName(face_name_string); if (typeface != NULL) { return typeface; } else { // If no typeface was successfully created then remove the entry from // the map. It won't provide a successful result in subsequent calls // either. - name_to_style_set_map.erase(style_set_iterator); + name_to_family_map.erase(family_map_iterator); } } } @@ -124,7 +130,7 @@ SkFontStyleSet_Cobalt* SkFontMgr_Cobalt::onMatchFamily( const char family_name[]) const { - if (!family_name) { + if (family_name == NULL) { return NULL; } @@ -143,12 +149,12 @@ const char family_name[], const SkFontStyle& style) const { SkTypeface* typeface = NULL; - if (family_name) { - SkAutoTUnref<SkFontStyleSet> style_set(matchFamily(family_name)); - typeface = style_set->matchStyle(style); + if (family_name != NULL) { + SkAutoTUnref<SkFontStyleSet> family(matchFamily(family_name)); + typeface = family->matchStyle(style); } - if (NULL == typeface) { + if (typeface == NULL) { typeface = default_family_->matchStyle(style); } @@ -157,13 +163,13 @@ SkTypeface* SkFontMgr_Cobalt::onMatchFaceStyle(const SkTypeface* family_member, const SkFontStyle& style) const { - // Lock the style sets mutex prior to calling private |SkFontStyleSet_Cobalt| + // Lock the family mutex prior to calling private SkFontStyleSet_Cobalt // functions that expect the mutex to already be locked. - SkAutoMutexAcquire scoped_mutex(style_sets_mutex_); + SkAutoMutexAcquire scoped_mutex(family_mutex_); - for (int i = 0; i < font_style_sets_.count(); ++i) { - if (font_style_sets_[i]->ContainsTypeface(family_member)) { - return font_style_sets_[i]->MatchStyleWithoutLocking(style); + for (int i = 0; i < families_.count(); ++i) { + if (families_[i]->ContainsTypeface(family_member)) { + return families_[i]->MatchStyleWithoutLocking(style); } } return NULL; @@ -177,11 +183,11 @@ // Remove const from the manager. SkFontMgr_Cobalt modifies its internals // within FindFamilyStyleCharacter(). - SkFontMgr_Cobalt* font_mgr = const_cast<SkFontMgr_Cobalt*>(this); + SkFontMgr_Cobalt* font_manager = const_cast<SkFontMgr_Cobalt*>(this); - // Lock the style sets mutex prior to calling |FindFamilyStyleCharacter|. It + // Lock the family mutex prior to calling FindFamilyStyleCharacter(). It // expects the mutex to already be locked. - SkAutoMutexAcquire scoped_mutex(style_sets_mutex_); + SkAutoMutexAcquire scoped_mutex(family_mutex_); // Search the fallback families for ones matching the requested language. // They are given priority over other fallback families in checking for @@ -189,7 +195,7 @@ for (int bcp47_index = bcp47_count; bcp47_index-- > 0;) { SkLanguage language(bcp47[bcp47_index]); while (!language.GetTag().isEmpty()) { - SkTypeface* matching_typeface = font_mgr->FindFamilyStyleCharacter( + SkTypeface* matching_typeface = font_manager->FindFamilyStyleCharacter( style, language.GetTag(), character); if (matching_typeface) { return matching_typeface; @@ -203,7 +209,7 @@ // requirement. This will select the first encountered family that contains // the character. SkTypeface* matching_typeface = - font_mgr->FindFamilyStyleCharacter(style, SkString(), character); + font_manager->FindFamilyStyleCharacter(style, SkString(), character); // If no family was found that supports the character, then just fall back // to the default family. @@ -253,56 +259,58 @@ void SkFontMgr_Cobalt::ParseConfigAndBuildFamilies( const char* font_config_directory, const char* font_files_directory, PriorityStyleSetArrayMap* priority_fallback_families) { - SkTDArray<FontFamily*> font_families; + SkTDArray<FontFamilyInfo*> config_font_families; { TRACE_EVENT0("cobalt::renderer", "SkFontConfigParser::GetFontFamilies()"); - SkFontConfigParser::GetFontFamilies(font_config_directory, &font_families); + SkFontConfigParser::GetFontFamilies(font_config_directory, + &config_font_families); } - BuildNameToFamilyMap(font_files_directory, &font_families, + BuildNameToFamilyMap(font_files_directory, &config_font_families, priority_fallback_families); - font_families.deleteAll(); + config_font_families.deleteAll(); } void SkFontMgr_Cobalt::BuildNameToFamilyMap( - const char* font_files_directory, SkTDArray<FontFamily*>* families, + const char* font_files_directory, + SkTDArray<FontFamilyInfo*>* config_font_families, PriorityStyleSetArrayMap* priority_fallback_families) { TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::BuildNameToFamilyMap()"); - for (int i = 0; i < families->count(); i++) { - FontFamily& family = *(*families)[i]; - bool named_family = family.names.count() > 0; + for (int i = 0; i < config_font_families->count(); i++) { + FontFamilyInfo& family_info = *(*config_font_families)[i]; + bool is_named_family = family_info.names.count() > 0; - if (!named_family) { + if (!is_named_family) { // Unnamed families should always be fallback families. - DCHECK(family.is_fallback_family); - SkString& fallback_name = family.names.push_back(); - fallback_name.printf("%.2x##fallback", font_style_sets_.count()); + DCHECK(family_info.is_fallback_family); + SkString& fallback_name = family_info.names.push_back(); + fallback_name.printf("%.2x##fallback", families_.count()); } - SkAutoTUnref<SkFontStyleSet_Cobalt> new_set( + SkAutoTUnref<SkFontStyleSet_Cobalt> new_family( SkNEW_ARGS(SkFontStyleSet_Cobalt, - (family, font_files_directory, - &local_typeface_stream_manager_, &style_sets_mutex_))); + (family_info, font_files_directory, + &local_typeface_stream_manager_, &family_mutex_))); - // Do not add the set if none of its fonts were available. This allows the - // config file to specify a superset of all fonts, and ones that are not - // included in the final package are stripped out. - if (new_set->styles_.count() == 0) { + // Do not add the family if none of its fonts were available. This allows + // the configuration files to specify a superset of all fonts, and ones that + // are not included in the final package are stripped out. + if (new_family->styles_.count() == 0) { continue; } - font_style_sets_.push_back().reset(SkRef(new_set.get())); + families_.push_back().reset(SkRef(new_family.get())); - if (named_family) { - for (int j = 0; j < family.names.count(); j++) { + if (is_named_family) { + for (int j = 0; j < family_info.names.count(); j++) { // Verify that the name was not previously added. - if (name_to_family_map_.find(family.names[j].c_str()) == + if (name_to_family_map_.find(family_info.names[j].c_str()) == name_to_family_map_.end()) { - family_names_.push_back(family.names[j]); + family_names_.push_back(family_info.names[j]); name_to_family_map_.insert( - std::make_pair(family.names[j].c_str(), new_set.get())); + std::make_pair(family_info.names[j].c_str(), new_family.get())); } else { - NOTREACHED() << "Duplicate Font name: \"" << family.names[j].c_str() - << "\""; + NOTREACHED() << "Duplicate Font name: \"" + << family_info.names[j].c_str() << "\""; } } } @@ -311,35 +319,35 @@ // that corresponds to its priority. This will be used to generate a // priority-ordered fallback families list once the family map is fully // built. - if (family.is_fallback_family) { - (*priority_fallback_families)[family.fallback_priority].push_back( - new_set.get()); + if (family_info.is_fallback_family) { + (*priority_fallback_families)[family_info.fallback_priority].push_back( + new_family.get()); } for (SkAutoTUnref<SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt>* - font_style_set_entry = new_set->styles_.begin(); - font_style_set_entry != new_set->styles_.end(); - ++font_style_set_entry) { + family_style_entry = new_family->styles_.begin(); + family_style_entry != new_family->styles_.end(); + ++family_style_entry) { // On the first pass through, process the full font name. // On the second pass through, process the font postscript name. for (int i = 0; i <= 1; ++i) { const std::string& font_face_name = - i == 0 ? (*font_style_set_entry)->full_font_name - : (*font_style_set_entry)->font_postscript_name; + i == 0 ? (*family_style_entry)->full_font_name + : (*family_style_entry)->font_postscript_name; // If there is no font face name for this style entry, then there's // nothing to add. Simply skip past it. if (font_face_name.empty()) { continue; } - NameToStyleSetMap& font_face_name_style_set_map = - i == 0 ? full_font_name_to_style_set_map_ - : font_postscript_name_to_style_set_map_; + NameToStyleSetMap& font_face_name_to_family_map = + i == 0 ? full_font_name_to_family_map_ + : font_postscript_name_to_family_map_; // Verify that the font face name was not already added. - if (font_face_name_style_set_map.find(font_face_name) == - font_face_name_style_set_map.end()) { - font_face_name_style_set_map[font_face_name] = new_set.get(); + if (font_face_name_to_family_map.find(font_face_name) == + font_face_name_to_family_map.end()) { + font_face_name_to_family_map[font_face_name] = new_family.get(); } else { const std::string font_face_name_type = i == 0 ? "Full Font" : "Postscript"; @@ -375,28 +383,28 @@ void SkFontMgr_Cobalt::FindDefaultFamily( const SkTArray<SkString, true>& default_families) { - CHECK(!font_style_sets_.empty()); + CHECK(!families_.empty()); for (size_t i = 0; i < default_families.count(); ++i) { - SkAutoTUnref<SkFontStyleSet_Cobalt> check_style_set( + SkAutoTUnref<SkFontStyleSet_Cobalt> check_family( onMatchFamily(default_families[i].c_str())); - if (NULL == check_style_set) { + if (check_family.get() == NULL) { continue; } SkAutoTUnref<SkTypeface> check_typeface( - check_style_set->MatchStyleWithoutLocking(SkFontStyle())); - if (NULL != check_typeface) { - default_family_ = check_style_set.get(); + check_family->MatchStyleWithoutLocking(SkFontStyle())); + if (check_typeface.get() != NULL) { + default_family_ = check_family.get(); break; } } - if (NULL == default_family_) { + if (default_family_ == NULL) { SkAutoTUnref<SkTypeface> check_typeface( - font_style_sets_[0]->MatchStyleWithoutLocking(SkFontStyle())); - if (NULL != check_typeface) { - default_family_ = font_style_sets_[0].get(); + families_[0]->MatchStyleWithoutLocking(SkFontStyle())); + if (check_typeface.get() != NULL) { + default_family_ = families_[0].get(); } }
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h index 5ea8eec..6ea9f51 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
@@ -29,18 +29,25 @@ #include "SkTArray.h" #include "SkTypeface.h" -// This class is essentially a collection of SkFontStyleSet_Cobalt, one -// SkFontStyleSet_Cobalt for each family. This class may be modified to load -// fonts from any source by changing the initialization. +// This class, which is thread-safe, is Cobalt's implementation of SkFontMgr. It +// is responsible for the creation of remote typefaces and for, given a set of +// constraints, providing Cobalt with its best matching local typeface. // -// In addition to containing a collection of SkFontStyleSet_Cobalt, the class -// also contains a mapping of names to families, allowing for multiple aliases -// to map to the same back-end family. +// When a remote typeface's raw data is downloaded from the web, it is provided +// to the font manager, which returns a typeface created from that data. // -// It also contains a list of fallback fonts, which are used when attempting -// to match a character, rather than a family name. If a language is provided -// then fallback fonts with that language are given priority. Otherwise, the -// fallback fonts are checked in order. +// For local typefaces, the font manager uses Cobalt-specific and +// system-specific configuration files to generate named font families and +// fallback font families. Cobalt uses named families when it needs a typeface +// for a family with a specific name. It utilizes fallback families when none +// of the listed named families supported a given character. In that case, +// the manager finds the best match among the fallback families that can provide +// a glyph for that character. +// +// For both named families and fallback families, after the manager locates the +// best matching family, the determination of the specific typeface to use is +// left to the family. The manager provides the family with the requested style +// and the family returns the typeface that best fits that style. class SkFontMgr_Cobalt : public SkFontMgr { public: typedef std::vector<SkFontStyleSet_Cobalt*> StyleSetArray; @@ -52,6 +59,9 @@ const char* system_font_files_directory, const SkTArray<SkString, true>& default_fonts); + // Purges all font caching in Skia and the local stream manager. + void PurgeCaches(); + // NOTE: This returns NULL if a match is not found. SkTypeface* MatchFaceName(const char face_name[]); @@ -74,8 +84,8 @@ virtual SkTypeface* onMatchFamilyStyle( const char family_name[], const SkFontStyle& style) const SK_OVERRIDE; -// NOTE: This always returns a non-NULL value. If no match can be found, then -// the best match among the default family is returned. + // NOTE: This always returns a non-NULL value. If no match can be found, then + // the best match among the default family is returned. virtual SkTypeface* onMatchFamilyStyleCharacter( const char family_name[], const SkFontStyle& style, const char bcp47[], SkUnichar character) const SK_OVERRIDE; @@ -111,7 +121,8 @@ const char* font_config_directory, const char* font_files_directory, PriorityStyleSetArrayMap* priority_fallback_families); void BuildNameToFamilyMap( - const char* font_files_directory, SkTDArray<FontFamily*>* families, + const char* font_files_directory, + SkTDArray<FontFamilyInfo*>* config_font_families, PriorityStyleSetArrayMap* priority_fallback_families); void GeneratePriorityOrderedFallbackFamilies( const PriorityStyleSetArrayMap& priority_fallback_families); @@ -131,14 +142,14 @@ SkFileMemoryChunkStreamManager local_typeface_stream_manager_; - SkTArray<SkAutoTUnref<SkFontStyleSet_Cobalt>, true> font_style_sets_; + SkTArray<SkAutoTUnref<SkFontStyleSet_Cobalt>, true> families_; SkTArray<SkString> family_names_; - // Map names to the back end so that all names for a given family refer to - // the same (non-replicated) set of typefaces. + // Map names to the back end so that all names for a given family refer to + // the same (non-replicated) set of typefaces. NameToStyleSetMap name_to_family_map_; - NameToStyleSetMap full_font_name_to_style_set_map_; - NameToStyleSetMap font_postscript_name_to_style_set_map_; + NameToStyleSetMap full_font_name_to_family_map_; + NameToStyleSetMap font_postscript_name_to_family_map_; // Fallback families that are used during character fallback. // All fallback families, regardless of language. @@ -148,12 +159,12 @@ ScopedVector<StyleSetArray> language_fallback_families_array_; NameToStyleSetArrayMap language_fallback_families_map_; - // The default family that is used when no specific match is found during a + // The default family that is used when no specific match is found during a // request. SkFontStyleSet_Cobalt* default_family_; - // Mutex shared by all style sets for accessing their modifiable data. - mutable SkMutex style_sets_mutex_; + // Mutex shared by all families for accessing their modifiable data. + mutable SkMutex family_mutex_; }; #endif // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc index 4774f00..2b5718b 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
@@ -87,27 +87,27 @@ } SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt( - const FontFamily& family, const char* base_path, + const FontFamilyInfo& family_info, const char* base_path, SkFileMemoryChunkStreamManager* const local_typeface_stream_manager, SkMutex* const manager_owned_mutex) : local_typeface_stream_manager_(local_typeface_stream_manager), manager_owned_mutex_(manager_owned_mutex), - is_fallback_family_(family.is_fallback_family), - language_(family.language), - page_ranges_(family.page_ranges), + is_fallback_family_(family_info.is_fallback_family), + language_(family_info.language), + page_ranges_(family_info.page_ranges), is_character_map_generated_(!is_fallback_family_) { TRACE_EVENT0("cobalt::renderer", "SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt()"); DCHECK(manager_owned_mutex_); - if (family.names.count() == 0) { + if (family_info.names.count() == 0) { return; } - family_name_ = family.names[0]; + family_name_ = family_info.names[0]; - for (int i = 0; i < family.fonts.count(); ++i) { - const FontFileInfo& font_file = family.fonts[i]; + for (int i = 0; i < family_info.fonts.count(); ++i) { + const FontFileInfo& font_file = family_info.fonts[i]; SkString file_path(SkOSPath::Join(base_path, font_file.file_name.c_str()));
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h index 3d1f7c7..7d6648a 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
@@ -25,20 +25,27 @@ #include "SkTArray.h" #include "SkTypeface.h" -// This class is used by SkFontMgr_Cobalt to store families of -// SkTypeface_Cobalt objects. +// This class, which is thread-safe, is Cobalt's implementation of +// SkFontStyleSet. It represents a collection of local typefaces that support +// the same set of characters but with different styles. When a specific style +// is requested, SkFontStyleSet returns the typeface that best matches that +// style. // -// Both the full character map of the style set and the typeface of each -// individual entry are lazily loaded the first time that they are needed. After -// this, they are retained in memory. +// SkFontStyleSet contains a map of the characters that it supports. It uses +// this map to quickly check for whether or not its typefaces can provide glyphs +// for specific characters during fallback. +// +// Both the character map of the style set and the typeface of each individual +// entry are lazily loaded the first time that they are needed. After this, they +// are retained in memory. class SkFontStyleSet_Cobalt : public SkFontStyleSet { public: struct SkFontStyleSetEntry_Cobalt : public SkRefCnt { - // NOTE: |SkFontStyleSetEntry_Cobalt| objects are not guaranteed to last for - // the lifetime of |SkFontMgr_Cobalt| and can be removed by their owning - // |SkFontStyleSet_Cobalt| if their typeface fails to load properly. As a + // NOTE: SkFontStyleSetEntry_Cobalt objects are not guaranteed to last for + // the lifetime of SkFontMgr_Cobalt and can be removed by their owning + // SkFontStyleSet_Cobalt if their typeface fails to load properly. As a // result, it is not safe to store their pointers outside of - // |SkFontStyleSet_Cobalt|. + // SkFontStyleSet_Cobalt. SkFontStyleSetEntry_Cobalt(const SkString& file_path, const int face_index, const SkFontStyle& style, const std::string& full_name, @@ -72,17 +79,17 @@ }; SkFontStyleSet_Cobalt( - const FontFamily& family, const char* base_path, + const FontFamilyInfo& family_info, const char* base_path, SkFileMemoryChunkStreamManager* const local_typeface_stream_manager, SkMutex* const manager_owned_mutex); // From SkFontStyleSet virtual int count() SK_OVERRIDE; - // NOTE: SkFontStyleSet_Cobalt does not support |getStyle|, as publicly + // NOTE: SkFontStyleSet_Cobalt does not support getStyle(), as publicly // accessing styles by index is unsafe. virtual void getStyle(int index, SkFontStyle* style, SkString* name) SK_OVERRIDE; - // NOTE: SkFontStyleSet_Cobalt does not support |createTypeface|, as + // NOTE: SkFontStyleSet_Cobalt does not support createTypeface(), as // publicly accessing styles by index is unsafe. virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h index 5aeb2c8..6b3b175 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
@@ -117,16 +117,22 @@ SkString postscript_name; }; -// A font family provides one or more names for a collection of fonts, each of -// which has a different style (normal, italic) or weight (thin, light, bold, -// etc). -// Cobalt distinguishes "fallback" fonts to support non-ASCII character sets. -// Page ranges are used to indicate the pages where a fallback font supports -// at least one character. This allows Cobalt to quickly determine fonts that -// cannot possible contain a character, without needing to load the font file -// and generate a full mapping of the font's characters. -struct FontFamily { - FontFamily() : is_fallback_family(true), fallback_priority(0) {} +// A font family is a collection of fonts, which support the same characters, +// each of which has a different style (normal, italic) or weight (thin, light, +// bold, etc). +// +// Families can have any number of names, each of which can be used to look up +// the family. +// +// Additionally, families can be fallback families, which are used to support +// character sets that are unavailable within the named fonts. +// +// Page ranges are used with fallback families to indicate 256 character pages +// where it contains at least one character. This allows Cobalt to quickly +// determine that a family cannot support a character, without needing to +// generate a full mapping of the family's characters. +struct FontFamilyInfo { + FontFamilyInfo() : is_fallback_family(true), fallback_priority(0) {} SkTArray<SkString> names; SkTArray<FontFileInfo> fonts;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc index 60d30dc..237cfb1 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc
@@ -23,12 +23,12 @@ #include "SkOSFile.h" SkFileMemoryChunkStreamManager::SkFileMemoryChunkStreamManager( - const std::string& name, int cache_size_in_bytes) - : available_chunk_count_(cache_size_in_bytes / + const std::string& name, int cache_capacity_in_bytes) + : available_chunk_count_(cache_capacity_in_bytes / SkFileMemoryChunk::kSizeInBytes), - cache_limit_in_bytes_(StringPrintf("Memory.%s.Capacity", name.c_str()), - cache_size_in_bytes, - "The byte capacity of the cache."), + cache_capacity_in_bytes_(StringPrintf("Memory.%s.Capacity", name.c_str()), + cache_capacity_in_bytes, + "The byte capacity of the cache."), cache_size_in_bytes_( StringPrintf("Memory.%s.Size", name.c_str()), 0, "Total number of bytes currently used by the cache.") {} @@ -56,6 +56,15 @@ return stream_provider; } +void SkFileMemoryChunkStreamManager::PurgeUnusedMemoryChunks() { + SkAutoMutexAcquire scoped_mutex(stream_provider_mutex_); + for (ScopedVector<SkFileMemoryChunkStreamProvider>::iterator iter = + stream_provider_array_.begin(); + iter != stream_provider_array_.end(); ++iter) { + (*iter)->PurgeUnusedMemoryChunks(); + } +} + bool SkFileMemoryChunkStreamManager::TryReserveMemoryChunk() { // First check to see if the count is already 0. If it is, then there's no // available memory chunk to try to reserve. Simply return failure.
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h index 7adb62f..77266cc 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h
@@ -67,13 +67,16 @@ class SkFileMemoryChunkStreamManager { public: SkFileMemoryChunkStreamManager(const std::string& name, - int cache_size_in_bytes); + int cache_capacity_in_bytes); // Returns the stream provider associated with the file path. If it does not // exist then it is created. SkFileMemoryChunkStreamProvider* GetStreamProvider( const std::string& file_path); + // Purges unused memory chunks from all existing stream providers. + void PurgeUnusedMemoryChunks(); + private: friend SkFileMemoryChunkStreamProvider; @@ -90,7 +93,7 @@ base::subtle::Atomic32 available_chunk_count_; const base::CVal<base::cval::SizeInBytes, base::CValPublic> - cache_limit_in_bytes_; + cache_capacity_in_bytes_; base::CVal<base::cval::SizeInBytes, base::CValPublic> cache_size_in_bytes_; DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStreamManager);
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc index 4bec9d0..4ad07b9 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
@@ -19,7 +19,7 @@ SkTypeface_Cobalt::SkTypeface_Cobalt(int face_index, Style style, bool is_fixed_pitch, - const SkString family_name) + const SkString& family_name) : INHERITED(style, SkTypefaceCache::NewFontID(), is_fixed_pitch), face_index_(face_index), family_name_(family_name), @@ -32,7 +32,7 @@ SkTypeface_CobaltStream::SkTypeface_CobaltStream(SkStreamAsset* stream, int face_index, Style style, bool is_fixed_pitch, - const SkString family_name) + const SkString& family_name) : INHERITED(face_index, style, is_fixed_pitch, family_name), stream_(SkRef(stream)) { LOG(INFO) << "Created SkTypeface_CobaltStream: " << family_name.c_str() << "(" @@ -56,7 +56,7 @@ SkTypeface_CobaltStreamProvider::SkTypeface_CobaltStreamProvider( SkFileMemoryChunkStreamProvider* stream_provider, int face_index, - Style style, bool is_fixed_pitch, const SkString family_name, + Style style, bool is_fixed_pitch, const SkString& family_name, bool disable_synthetic_bolding) : INHERITED(face_index, style, is_fixed_pitch, family_name), stream_provider_(stream_provider) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h index 4fcbda5..2ac66ab 100644 --- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h +++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
@@ -26,7 +26,7 @@ class SkTypeface_Cobalt : public SkTypeface_FreeType { public: SkTypeface_Cobalt(int face_index, Style style, bool is_fixed_pitch, - const SkString family_name); + const SkString& family_name); bool synthesizes_bold() const { return synthesizes_bold_; } @@ -44,7 +44,7 @@ class SkTypeface_CobaltStream : public SkTypeface_Cobalt { public: SkTypeface_CobaltStream(SkStreamAsset* stream, int face_index, Style style, - bool is_fixed_pitch, const SkString family_name); + bool is_fixed_pitch, const SkString& family_name); virtual void onGetFontDescriptor(SkFontDescriptor* descriptor, bool* serialize) const SK_OVERRIDE; @@ -61,7 +61,7 @@ public: SkTypeface_CobaltStreamProvider( SkFileMemoryChunkStreamProvider* stream_provider, int face_index, - Style style, bool is_fixed_pitch, const SkString family_name, + Style style, bool is_fixed_pitch, const SkString& family_name, bool disable_synthetic_bolding); virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc index e4db0c1..32d795e 100644 --- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc +++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -233,6 +233,13 @@ return scoped_refptr<render_tree::Image>(NULL); } +void SoftwareResourceProvider::PurgeCaches() { + SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault()); + SkFontMgr_Cobalt* cobalt_font_manager = + base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager.get()); + cobalt_font_manager->PurgeCaches(); +} + } // namespace skia } // namespace rasterizer } // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h index 4ed9bde..1f0d1f4 100644 --- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h +++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -121,6 +121,8 @@ scoped_refptr<render_tree::Image> DrawOffscreenImage( const scoped_refptr<render_tree::Node>& root) OVERRIDE; + void PurgeCaches() OVERRIDE; + private: TextShaper text_shaper_; };
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc index 624d591..2f5af46 100644 --- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc +++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -382,26 +382,38 @@ void MozjsGlobalEnvironment::BeginGarbageCollection() { TRACK_MEMORY_SCOPE("Javascript"); // It's possible that a GC could be triggered from within the - // BeginGarbageCollection callback. Only create the OpaqueRootState the first - // time we enter. + // BeginGarbageCollection callback. Only create the OpaqueRootState the + // first time we enter. Also, only verify that |visisted_wrappables_| is + // empty in this case. + // TODO: Opaque root logic is a special case of tracing wrappables, and + // should be removed. garbage_collection_count_++; - if (global_object_proxy_ && garbage_collection_count_ == 1) { - DCHECK(!opaque_root_state_); - JSAutoRequest auto_request(context_); - JSAutoCompartment auto_compartment(context_, global_object_proxy_); - // Get the current state of opaque root relationships. Keep this object - // alive for the duration of the GC phase to ensure that reachability - // between roots and reachable objects is maintained. - opaque_root_state_ = opaque_root_tracker_->GetCurrentOpaqueRootState(); + + if (garbage_collection_count_ == 1) { + if (global_object_proxy_) { + DCHECK(!opaque_root_state_); + JSAutoRequest auto_request(context_); + JSAutoCompartment auto_compartment(context_, global_object_proxy_); + // Get the current state of opaque root relationships. Keep this object + // alive for the duration of the GC phase to ensure that reachability + // between roots and reachable objects is maintained. + opaque_root_state_ = opaque_root_tracker_->GetCurrentOpaqueRootState(); + } + + DCHECK_EQ(visited_wrappables_.size(), 0); } } void MozjsGlobalEnvironment::EndGarbageCollection() { - // Reset opaque root reachability relationships. + // Reset opaque root reachability relationships. Also reset + // |visisted_wrappables_|. + // TODO: Opaque root logic is a special case of tracing wrappables, and + // should be removed. garbage_collection_count_--; DCHECK_GE(garbage_collection_count_, 0); if (garbage_collection_count_ == 0) { opaque_root_state_.reset(NULL); + visited_wrappables_.clear(); } }
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h index e23f1bb..c6cfb2a 100644 --- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h +++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -103,6 +103,10 @@ return opaque_root_tracker_.get(); } + base::hash_set<Wrappable*>* visited_wrappables() { + return &visited_wrappables_; + } + // Used for CallWith=EnvironmentSettings void SetEnvironmentSettings(EnvironmentSettings* environment_settings) { DCHECK(!environment_settings_); @@ -174,6 +178,8 @@ scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_; JS::Heap<JSObject*> global_object_proxy_; EnvironmentSettings* environment_settings_; + // TODO: Should be |std::unordered_set| once C++11 is enabled. + base::hash_set<Wrappable*> visited_wrappables_; // If non-NULL, the error message from the ReportErrorHandler will get // assigned to this instead of being printed.
diff --git a/src/cobalt/script/mozjs-45/wrapper_factory.cc b/src/cobalt/script/mozjs-45/wrapper_factory.cc index c4ed731..9395b73 100644 --- a/src/cobalt/script/mozjs-45/wrapper_factory.cc +++ b/src/cobalt/script/mozjs-45/wrapper_factory.cc
@@ -56,6 +56,13 @@ return wrapper_proxy; } +bool WrapperFactory::HasWrapperProxy( + const scoped_refptr<Wrappable>& wrappable) const { + return wrappable && + !!MozjsWrapperHandle::GetObjectProxy( + GetCachedWrapper(wrappable.get())); +} + bool WrapperFactory::IsWrapper(JS::HandleObject wrapper) const { return JS_GetPrivate(wrapper) != NULL; }
diff --git a/src/cobalt/script/mozjs-45/wrapper_factory.h b/src/cobalt/script/mozjs-45/wrapper_factory.h index ba83745..043b96c 100644 --- a/src/cobalt/script/mozjs-45/wrapper_factory.h +++ b/src/cobalt/script/mozjs-45/wrapper_factory.h
@@ -46,6 +46,12 @@ // Gets the Proxy for the Wrapper object for this Wrappable. It may create a // new Wrapper and Proxy. JSObject* GetWrapperProxy(const scoped_refptr<Wrappable>& wrappable) const; + + // Check whether the Proxy for the Wrapper object for this Wrappable exists + // or not. This will NOT create a new Wrapper and Proxy if they do not + // already exist. + bool HasWrapperProxy(const scoped_refptr<Wrappable>& wrappable) const; + // Returns true if this JSObject is a Wrapper object. bool IsWrapper(JS::HandleObject wrapper) const;
diff --git a/src/cobalt/script/mozjs-45/wrapper_private.cc b/src/cobalt/script/mozjs-45/wrapper_private.cc index ecb9fdd..44a0930 100644 --- a/src/cobalt/script/mozjs-45/wrapper_private.cc +++ b/src/cobalt/script/mozjs-45/wrapper_private.cc
@@ -14,6 +14,7 @@ #include "cobalt/script/mozjs-45/wrapper_private.h" +#include "base/hash_tables.h" #include "cobalt/script/mozjs-45/mozjs_global_environment.h" #include "cobalt/script/mozjs-45/proxy_handler.h" #include "cobalt/script/mozjs-45/referenced_object_map.h" @@ -26,6 +27,68 @@ namespace script { namespace mozjs { +void Tracer::Trace(Wrappable* wrappable) { + // Clearly, a null wrappable could not possibly reference any other + // wrappables. + if (!wrappable) { + return; + } + + // Unfortunately, |JSTracer| will only supply us with a |JSRuntime|, + // rather than a |JSContext|. Fortunately, Cobalt will only create one + // global environment per runtime, so we can still safely get back to our + // context, and thus our global environment. + JSContext* context = NULL; + JS_ContextIterator(js_tracer_->runtime(), &context); + DCHECK(context); + MozjsGlobalEnvironment* global_environment = + MozjsGlobalEnvironment::GetFromContext(context); + DCHECK(global_environment); + + // Clearly, if we have already visited this wrappable during the current + // tracing session, there is no need to visit it again. We rely on + // |JS_SetGCCallback| in the |MozjsEngine| to properly manage clearing + // |visited_wrappables_| in between GC sessions. + base::hash_set<Wrappable*>* visited_wrappables = + global_environment->visited_wrappables(); + DCHECK(visited_wrappables); + if (!visited_wrappables->insert(wrappable).second) { + return; + } + + // There are now two cases left to handle. Since we cannot create the + // wrapper while tracing due to internal SpiderMonkey restrictions, we will + // instead directly call |TraceMembers| here if the wrapper does not exist. + // In the case where the wrapper already does exist, we will pass the + // wrapper to |JS_CallObjectTracer|, and rely on SpiderMonkey to begin + // another |WrapperPrivate::Trace| on that wrapper. + WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); + if (!wrapper_factory->HasWrapperProxy(wrappable)) { + frontier_.push_back(wrappable); + } else { + JSObject* proxy_object = wrapper_factory->GetWrapperProxy(wrappable); + JSObject* target = js::GetProxyTargetObject(proxy_object); + WrapperPrivate* wrapper_private = + static_cast<WrapperPrivate*>(JS_GetPrivate(target)); + DCHECK(wrapper_private->context_ == context); + DCHECK(wrapper_private->wrapper_proxy_); + JS_CallObjectTracer(js_tracer_, &wrapper_private->wrapper_proxy_, + "WrapperPrivate::TraceWrappable"); + } + + DCHECK(JS_ContextIterator(js_tracer_->runtime(), &context) == NULL); +} + +void Tracer::TraceFrom(Wrappable* wrappable) { + DCHECK(frontier_.empty()); + frontier_.push_back(wrappable); + while (!frontier_.empty()) { + Wrappable* wrappable = frontier_.back(); + frontier_.pop_back(); + wrappable->TraceMembers(this); + } +} + Wrappable* WrapperPrivate::GetOpaqueRoot() const { if (!get_opaque_root_function_.is_null()) { return get_opaque_root_function_.Run(wrappable_); @@ -152,6 +215,8 @@ wrapper_private->wrappable_.get()); global_environment->referenced_objects()->TraceReferencedObjects(trace, key); + Tracer tracer(trace); + tracer.TraceFrom(wrapper_private->wrappable_); } }
diff --git a/src/cobalt/script/mozjs-45/wrapper_private.h b/src/cobalt/script/mozjs-45/wrapper_private.h index dc1a955..7e4b917 100644 --- a/src/cobalt/script/mozjs-45/wrapper_private.h +++ b/src/cobalt/script/mozjs-45/wrapper_private.h
@@ -28,6 +28,29 @@ namespace script { namespace mozjs { +// Our mozjs specific implementation of |script::Tracer|. Tracing sessions +// will be initiated from a |Wrapper| of SpiderMonkey's choice, and then it +// will be the |mozjs::Tracer|'s job to assist SpiderMonkey's garbage +// collector in traversing the graph of |Wrapper|s and |Wrappable|s. +// |Wrappable|s will inform us about what they can reach through their +// |TraceMembers| implementation, and then we will pass the reachable +// |Wrappable|'s |Wrapper| to SpiderMonkey GC if it exists, and otherwise +// continue traversing ourselves from |Wrappable| to (the unwrapped) +// |Wrappable|. +class Tracer : public ::cobalt::script::Tracer { + public: + explicit Tracer(JSTracer* js_tracer) : js_tracer_(js_tracer) {} + void Trace(Wrappable* wrappable) OVERRIDE; + + void TraceFrom(Wrappable* wrappable); + + private: + JSTracer* js_tracer_; + // Pending |Wrappable|s that we must traverse ourselves, since they did not + // have a |Wrapper|. + std::vector<Wrappable*> frontier_; +}; + // Contains private data associated with a JSObject representing a JS wrapper // for a Cobalt platform object. There should be a one-to-one mapping of such // JSObjects and WrapperPrivate instances, and the corresponding WrapperPrivate @@ -107,6 +130,8 @@ JS::Heap<JSObject*> wrapper_proxy_; GetOpaqueRootFunction get_opaque_root_function_; GetReachableWrappablesFunction get_reachable_wrappables_function_; + + friend Tracer; }; } // namespace mozjs
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc index a6bc89f..e306a50 100644 --- a/src/cobalt/script/mozjs/mozjs_global_environment.cc +++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -394,26 +394,38 @@ void MozjsGlobalEnvironment::BeginGarbageCollection() { TRACK_MEMORY_SCOPE("Javascript"); // It's possible that a GC could be triggered from within the - // BeginGarbageCollection callback. Only create the OpaqueRootState the first - // time we enter. + // BeginGarbageCollection callback. Only create the OpaqueRootState the + // first time we enter. Also, only verify that |visisted_wrappables_| is + // empty in this case. + // TODO: Opaque root logic is a special case of tracing wrappables, and + // should be removed. garbage_collection_count_++; - if (global_object_proxy_ && garbage_collection_count_ == 1) { - DCHECK(!opaque_root_state_); - JSAutoRequest auto_request(context_); - JSAutoCompartment auto_compartment(context_, global_object_proxy_); - // Get the current state of opaque root relationships. Keep this object - // alive for the duration of the GC phase to ensure that reachability - // between roots and reachable objects is maintained. - opaque_root_state_ = opaque_root_tracker_->GetCurrentOpaqueRootState(); + + if (garbage_collection_count_ == 1) { + if (global_object_proxy_) { + DCHECK(!opaque_root_state_); + JSAutoRequest auto_request(context_); + JSAutoCompartment auto_compartment(context_, global_object_proxy_); + // Get the current state of opaque root relationships. Keep this object + // alive for the duration of the GC phase to ensure that reachability + // between roots and reachable objects is maintained. + opaque_root_state_ = opaque_root_tracker_->GetCurrentOpaqueRootState(); + } + + DCHECK_EQ(visited_wrappables_.size(), 0); } } void MozjsGlobalEnvironment::EndGarbageCollection() { - // Reset opaque root reachability relationships. + // Reset opaque root reachability relationships. Also reset + // |visisted_wrappables_|. + // TODO: Opaque root logic is a special case of tracing wrappables, and + // should be removed. garbage_collection_count_--; DCHECK_GE(garbage_collection_count_, 0); if (garbage_collection_count_ == 0) { opaque_root_state_.reset(NULL); + visited_wrappables_.clear(); } }
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h index 628631a..6c79872 100644 --- a/src/cobalt/script/mozjs/mozjs_global_environment.h +++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -102,6 +102,10 @@ return opaque_root_tracker_.get(); } + base::hash_set<Wrappable*>* visited_wrappables() { + return &visited_wrappables_; + } + // Used for CallWith=EnvironmentSettings void SetEnvironmentSettings(EnvironmentSettings* environment_settings) { DCHECK(!environment_settings_); @@ -174,6 +178,8 @@ scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_; JS::Heap<JSObject*> global_object_proxy_; EnvironmentSettings* environment_settings_; + // TODO: Should be |std::unordered_set| once C++11 is enabled. + base::hash_set<Wrappable*> visited_wrappables_; // If non-NULL, the error message from the ReportErrorHandler will get // assigned to this instead of being printed.
diff --git a/src/cobalt/script/mozjs/wrapper_factory.cc b/src/cobalt/script/mozjs/wrapper_factory.cc index 968003c..c456283 100644 --- a/src/cobalt/script/mozjs/wrapper_factory.cc +++ b/src/cobalt/script/mozjs/wrapper_factory.cc
@@ -56,6 +56,13 @@ return wrapper_proxy; } +bool WrapperFactory::HasWrapperProxy( + const scoped_refptr<Wrappable>& wrappable) const { + return wrappable && + !!MozjsWrapperHandle::GetObjectProxy( + GetCachedWrapper(wrappable.get())); +} + bool WrapperFactory::IsWrapper(JS::HandleObject wrapper) const { return JS_GetPrivate(wrapper) != NULL; }
diff --git a/src/cobalt/script/mozjs/wrapper_factory.h b/src/cobalt/script/mozjs/wrapper_factory.h index 357132d..ffe4e3d 100644 --- a/src/cobalt/script/mozjs/wrapper_factory.h +++ b/src/cobalt/script/mozjs/wrapper_factory.h
@@ -46,6 +46,12 @@ // Gets the Proxy for the Wrapper object for this Wrappable. It may create a // new Wrapper and Proxy. JSObject* GetWrapperProxy(const scoped_refptr<Wrappable>& wrappable) const; + + // Check whether the Proxy for the Wrapper object for this Wrappable exists + // or not. This will NOT create a new Wrapper and Proxy if they do not + // already exist. + bool HasWrapperProxy(const scoped_refptr<Wrappable>& wrappable) const; + // Returns true if this JSObject is a Wrapper object. bool IsWrapper(JS::HandleObject wrapper) const;
diff --git a/src/cobalt/script/mozjs/wrapper_private.cc b/src/cobalt/script/mozjs/wrapper_private.cc index 0095269..7b8d983 100644 --- a/src/cobalt/script/mozjs/wrapper_private.cc +++ b/src/cobalt/script/mozjs/wrapper_private.cc
@@ -14,6 +14,7 @@ #include "cobalt/script/mozjs/wrapper_private.h" +#include "base/hash_tables.h" #include "cobalt/script/mozjs/mozjs_global_environment.h" #include "cobalt/script/mozjs/proxy_handler.h" #include "cobalt/script/mozjs/referenced_object_map.h" @@ -25,6 +26,68 @@ namespace script { namespace mozjs { +void Tracer::Trace(Wrappable* wrappable) { + // Clearly, a null wrappable could not possibly reference any other + // wrappables. + if (!wrappable) { + return; + } + + // Unfortunately, |JSTracer| will only supply us with a |JSRuntime|, + // rather than a |JSContext|. Fortunately, Cobalt will only create one + // global environment per runtime, so we can still safely get back to our + // context, and thus our global environment. + JSContext* context = NULL; + JS_ContextIterator(js_tracer_->runtime, &context); + DCHECK(context); + MozjsGlobalEnvironment* global_environment = + MozjsGlobalEnvironment::GetFromContext(context); + DCHECK(global_environment); + + // Clearly, if we have already visited this wrappable during the current + // tracing session, there is no need to visit it again. We rely on + // |JS_SetGCCallback| in the |MozjsEngine| to properly manage clearing + // |visited_wrappables_| in between GC sessions. + base::hash_set<Wrappable*>* visited_wrappables = + global_environment->visited_wrappables(); + DCHECK(visited_wrappables); + if (!visited_wrappables->insert(wrappable).second) { + return; + } + + // There are now two cases left to handle. Since we cannot create the + // wrapper while tracing due to internal SpiderMonkey restrictions, we + // will instead directly call |TraceMembers| here if the wrapper does not + // exist. In the case where the wrapper already does exist, we will pass + // the wrapper to |JS_CallHeapObjectTracer|, and rely on SpiderMonkey to + // begin another |WrapperPrivate::Trace| on that wrapper. + WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); + if (!wrapper_factory->HasWrapperProxy(wrappable)) { + frontier_.push_back(wrappable); + } else { + JSObject* proxy_object = wrapper_factory->GetWrapperProxy(wrappable); + JSObject* target = js::GetProxyTargetObject(proxy_object); + WrapperPrivate* wrapper_private = + static_cast<WrapperPrivate*>(JS_GetPrivate(target)); + DCHECK(wrapper_private->context_ == context); + DCHECK(wrapper_private->wrapper_proxy_); + JS_CallHeapObjectTracer(js_tracer_, &wrapper_private->wrapper_proxy_, + "WrapperPrivate::TraceWrappable"); + } + + DCHECK(JS_ContextIterator(js_tracer_->runtime, &context) == NULL); +} + +void Tracer::TraceFrom(Wrappable* wrappable) { + DCHECK(frontier_.empty()); + frontier_.push_back(wrappable); + while (!frontier_.empty()) { + Wrappable* wrappable = frontier_.back(); + frontier_.pop_back(); + wrappable->TraceMembers(this); + } +} + Wrappable* WrapperPrivate::GetOpaqueRoot() const { if (!get_opaque_root_function_.is_null()) { return get_opaque_root_function_.Run(wrappable_); @@ -148,6 +211,8 @@ wrapper_private->wrappable_.get()); global_environment->referenced_objects()->TraceReferencedObjects(trace, key); + Tracer tracer(trace); + tracer.TraceFrom(wrapper_private->wrappable_); } }
diff --git a/src/cobalt/script/mozjs/wrapper_private.h b/src/cobalt/script/mozjs/wrapper_private.h index 85a1ee4..1ac464f 100644 --- a/src/cobalt/script/mozjs/wrapper_private.h +++ b/src/cobalt/script/mozjs/wrapper_private.h
@@ -28,6 +28,29 @@ namespace script { namespace mozjs { +// Our mozjs specific implementation of |script::Tracer|. Tracing sessions +// will be initiated from a |Wrapper| of SpiderMonkey's choice, and then it +// will be the |mozjs::Tracer|'s job to assist SpiderMonkey's garbage +// collector in traversing the graph of |Wrapper|s and |Wrappable|s. +// |Wrappable|s will inform us about what they can reach through their +// |TraceMembers| implementation, and then we will pass the reachable +// |Wrappable|'s |Wrapper| to SpiderMonkey GC if it exists, and otherwise +// continue traversing ourselves from |Wrappable| to (the unwrapped) +// |Wrappable|. +class Tracer : public ::cobalt::script::Tracer { + public: + explicit Tracer(JSTracer* js_tracer) : js_tracer_(js_tracer) {} + void Trace(Wrappable* wrappable) OVERRIDE; + + void TraceFrom(Wrappable* wrappable); + + private: + JSTracer* js_tracer_; + // Pending |Wrappable|s that we must traverse ourselves, since they did not + // have a |Wrapper|. + std::vector<Wrappable*> frontier_; +}; + // Contains private data associated with a JSObject representing a JS wrapper // for a Cobalt platform object. There should be a one-to-one mapping of such // JSObjects and WrapperPrivate instances, and the corresponding WrapperPrivate @@ -107,6 +130,8 @@ JS::Heap<JSObject*> wrapper_proxy_; GetOpaqueRootFunction get_opaque_root_function_; GetReachableWrappablesFunction get_reachable_wrappables_function_; + + friend Tracer; }; } // namespace mozjs
diff --git a/src/cobalt/script/wrappable.h b/src/cobalt/script/wrappable.h index 7d7f32f..e803fe4 100644 --- a/src/cobalt/script/wrappable.h +++ b/src/cobalt/script/wrappable.h
@@ -24,6 +24,12 @@ namespace cobalt { namespace script { +class Wrappable; +class Tracer { + public: + virtual void Trace(Wrappable* wrappable) = 0; +}; + class Wrappable : public base::RefCounted<Wrappable> { public: // A handle to this Wrappable's corresponding Wrapper object. It may be @@ -65,6 +71,12 @@ // this (which will result in the Wrappable being destructed as well.) virtual bool ShouldKeepWrapperAlive() { return false; } + // Trace all native |Wrappable|s accessible by the |Wrappable|. Must be + // manually implemented by the |Wrappable|. + // TODO: Should be pure virtual after static analysis tool for |Wrappable|s + // is created. + virtual void TraceMembers(Tracer* /*tracer*/) {} + protected: virtual ~Wrappable() { }
diff --git a/src/cobalt/webdriver/stub_web_driver_module.cc b/src/cobalt/webdriver/stub_web_driver_module.cc new file mode 100644 index 0000000..0a63ec1 --- /dev/null +++ b/src/cobalt/webdriver/stub_web_driver_module.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. + +// A source file used to create a nearly empty library that is needed for +// dependency purposes. On Windows one cannot create a library that does not +// define any previously undefined public symbols. +// See related Linker Tools Warning LNK4221: +// https://msdn.microsoft.com/en-us/library/604bzebd.aspx + +namespace cobalt { +namespace webdriver { +class WebDriverModule {}; +} +} \ No newline at end of file
diff --git a/src/cobalt/webdriver/webdriver.gyp b/src/cobalt/webdriver/webdriver.gyp index 6fd1acf..e6b431b 100644 --- a/src/cobalt/webdriver/webdriver.gyp +++ b/src/cobalt/webdriver/webdriver.gyp
@@ -80,6 +80,11 @@ 'defines': [ 'ENABLE_WEBDRIVER', ], }, }], + ['enable_webdriver==0', { + 'sources': [ + 'stub_web_driver_module.cc' + ] + }], ], 'dependencies': [ '<(DEPTH)/base/base.gyp:base',
diff --git a/src/cobalt/webdriver_benchmarks/tests/README.md b/src/cobalt/webdriver_benchmarks/tests/README.md index 752f1a0..ca89b03 100644 --- a/src/cobalt/webdriver_benchmarks/tests/README.md +++ b/src/cobalt/webdriver_benchmarks/tests/README.md
@@ -102,7 +102,8 @@ ### Interesting benchmarks -Some particularly interesting benchmark results are: +#### Timing-related +Some particularly interesting timing-related benchmark results are: - `wbStartupDurBlankToBrowseUs*`: Measures the startup time, until all images finish loading. @@ -120,10 +121,34 @@ In each case above, the `*` symbol can be one of either `Mean`, `Pct25`, `Pct50`, `Pct75` or `Pct95`. For example, `wbStartupDurBlankToBrowseUsMean` or -`wbStartupDurBlankToBrowseUsPct95` are both valid measurements. The +`wbStartupDurBlankToBrowseUsPct95` are both valid measurements. The webdriver +benchmarks runs its tests many times in order to obtain multiple samples, so you +can drill into the data by exploring either the mean, or the various +percentiles. + +#### Object count-related +Some particularly interesting count-related benchmark results are: + + - `wbBrowseVerticalCntDomHtmlElements*`: Lists the number of HTML elements in + existence after the event. This includes HTML elements that are no longer in + the DOM but have not been garbage collected yet. + - `wbBrowseVerticalCntLayoutBoxes*`: Lists the number of layout boxes within + the layout tree after the event. + - `wbBrowseVerticalCntLayoutBoxesCreated*`: Lists the number of new layout + boxes that were created during the event. + - `wbBrowseHorizontalCntDomHtmlElements*`: Same as + `wbBrowseVerticalCntDomHtmlElements*` except for horizontal scroll events. + - `wbBrowseHorizontalCntLayoutBoxes*`: Same as + `wbBrowseVerticalCntLayoutBoxes*` except for horizontal scroll events. + - `wbBrowseHorizontalCntLayoutBoxesCreated*`: Same as + `wbBrowseVerticalCntLayoutBoxesCreated*` except for horizontal scroll events. + +In each case above, the `*` symbol can be one of either `Max`, `Median`, or +`Mean`. For example, `wbBrowseVerticalCntDomHtmlElementsMax` or +`wbBrowseVerticalCntDomHtmlElementsMedian` are both valid measurements. The webdriver benchmarks runs its tests many times in order to obtain multiple -samples, so you can drill into the data by exploring either the mean, or the -various percentiles. +samples, so you can drill into the data by exploring either the max, median, or +mean. ### Filtering results @@ -140,6 +165,10 @@ grep -o "wbBrowseVerticalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt grep -o "wbBrowseHorizontalDurTotalUs.*$" results.txt >> filtered_results.txt grep -o "wbBrowseHorizontalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt +grep -o "wbBrowseVerticalCntDomHtmlElements.*$" results.txt >> filtered_results.txt +grep -o "wbBrowseVerticalCntLayoutBoxes.*$" results.txt >> filtered_results.txt +grep -o "wbBrowseHorizontalCntDomHtmlElements.*$" results.txt >> filtered_results.txt +grep -o "wbBrowseHorizontalCntLayoutBoxes.*$" results.txt >> filtered_results.txt cat filtered_results.txt ```
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md index fbddb4a..d9629f6 100644 --- a/src/starboard/CHANGELOG.md +++ b/src/starboard/CHANGELOG.md
@@ -16,6 +16,7 @@ `SB_IS_PLAYER_COMPOSITED` now no longer need to be defined (and should not be defined) by platforms. Instead, these capabilities are detected at runtime via `SbPlayerOutputModeSupported()`. + In `starboard/player.h`, * The enum `SbPlayerOutputMode` is introduced. * `SbPlayerOutputModeSupported()` is introduced to let applications query @@ -28,6 +29,7 @@ * The function `SbPlayerGetCompositionHandle()` is removed. * The function `SbPlayerGetTextureId()` is replaced by the new `SbPlayerGetCurrentFrame()`, which returns a `SbDecodeTarget`. + In `starboard/decode_target.h`, * All get methods (`SbDecodeTargetGetPlane()` and `SbDecodeTargetGetFormat()`, `SbDecodeTargetIsOpaque()`) are now replaced with `SbDecodeTargetGetInfo()`. @@ -40,6 +42,7 @@ (`EGLDisplay`, `EGLContext`) replaced by `void*` types, so that `decode_target.h` can avoid #including EGL/GLES2 headers. * `SbDecodeTargetDestroy()` is renamed to `SbDecodeTargetRelease()`. + In `starboard/player.h`, `starboard/image.h` and `starboard/decode_target.h`, * Replace `SbDecodeTargetProvider` with `SbDecodeTargetGraphicsContextProvider`.
diff --git a/src/starboard/client_porting/pr_starboard/pr_starboard.cc b/src/starboard/client_porting/pr_starboard/pr_starboard.cc index 52a7ebc..89767be 100644 --- a/src/starboard/client_porting/pr_starboard/pr_starboard.cc +++ b/src/starboard/client_porting/pr_starboard/pr_starboard.cc
@@ -28,7 +28,9 @@ namespace { typedef starboard::Queue<PRThread*> SetupSignalQueue; -nb::ThreadLocalObject<PRThread> g_local_pr_thread; +typedef nb::ThreadLocalObject<PRThread> ThreadLocalPRThread; +SB_ONCE_INITIALIZE_FUNCTION(ThreadLocalPRThread, + g_local_pr_thread); // Utility function to convert a PRInterval to signed 64 bit integer // microseconds. @@ -61,8 +63,8 @@ delete context; - SB_DCHECK(g_local_pr_thread.GetIfExists() == NULL); - PRThread* pr_thread = g_local_pr_thread.GetOrCreate(SbThreadGetCurrent()); + SB_DCHECK(g_local_pr_thread()->GetIfExists() == NULL); + PRThread* pr_thread = g_local_pr_thread()->GetOrCreate(SbThreadGetCurrent()); SB_DCHECK(pr_thread); setup_signal_queue->Put(pr_thread); pr_entry_point(pr_context); @@ -119,7 +121,7 @@ } PRThread* PR_GetCurrentThread() { - return g_local_pr_thread.GetOrCreate(SbThreadGetCurrent()); + return g_local_pr_thread()->GetOrCreate(SbThreadGetCurrent()); } uint32_t PR_snprintf(char* out, uint32_t outlen, const char* fmt, ...) {
diff --git a/src/starboard/export.h b/src/starboard/export.h index b19c1c7..78d5892 100644 --- a/src/starboard/export.h +++ b/src/starboard/export.h
@@ -31,10 +31,13 @@ // SB_IMPORT: Specification for a symbol that is expected to be defined // externally to this module. -#if defined(COMPONENT_BUILD) +#if defined(COMPONENT_BUILD) || SB_IS(LIBRARY) // COMPONENT_BUILD is defined when generating shared libraries for each project, // rather than static libraries. This means we need to be careful about // EXPORT/IMPORT. +// SB_IS_LIBRARY is defined when building Starboard as a shared library to be +// linked into a client app. In this case, we want to explicitly define +// EXPORT/IMPORT so that Starboard's symbols are visible to such clients. #if defined(STARBOARD_IMPLEMENTATION) // STARBOARD_IMPLEMENTATION is defined when building the Starboard library // sources, and shouldn't be defined when building sources that are clients of @@ -47,7 +50,7 @@ #define SB_EXPORT_PRIVATE SB_IMPORT_PLATFORM #define SB_IMPORT SB_EXPORT_PLATFORM #endif -#else // defined(COMPONENT_BUILD) +#else // defined(COMPONENT_BUILD) || SB_IS(LIBRARY) #define SB_EXPORT #define SB_EXPORT_PRIVATE #define SB_IMPORT
diff --git a/src/starboard/nplb/file_helpers.cc b/src/starboard/nplb/file_helpers.cc index 327b0d5..f6a9f43 100644 --- a/src/starboard/nplb/file_helpers.cc +++ b/src/starboard/nplb/file_helpers.cc
@@ -62,7 +62,7 @@ } std::ostringstream filename_stream; - filename_stream << path << "/ScopedRandomFile.File_" + filename_stream << path << SB_FILE_SEP_CHAR << "ScopedRandomFile.File_" << SbSystemGetRandomUInt64(); return filename_stream.str(); }
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp index 92c4f2d..782c729 100644 --- a/src/starboard/nplb/nplb.gyp +++ b/src/starboard/nplb/nplb.gyp
@@ -172,6 +172,12 @@ 'socket_waiter_wait_test.cc', 'socket_waiter_wait_timed_test.cc', 'socket_waiter_wake_up_test.cc', + 'speech_recognizer_cancel_test.cc', + 'speech_recognizer_create_test.cc', + 'speech_recognizer_destroy_test.cc', + 'speech_recognizer_helper.h', + 'speech_recognizer_start_test.cc', + 'speech_recognizer_stop_test.cc', 'speech_synthesis_basic_test.cc', 'storage_close_record_test.cc', 'storage_delete_record_test.cc', @@ -250,6 +256,7 @@ '<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "nplb/*_test.cc")', ], 'dependencies': [ + '<@(cobalt_platform_dependencies)', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/starboard/starboard.gyp:starboard',
diff --git a/src/starboard/nplb/socket_get_local_interface_address_test.cc b/src/starboard/nplb/socket_get_local_interface_address_test.cc index 566aad3..a282f61 100644 --- a/src/starboard/nplb/socket_get_local_interface_address_test.cc +++ b/src/starboard/nplb/socket_get_local_interface_address_test.cc
@@ -20,13 +20,7 @@ namespace nplb { namespace { -class SbSocketGetLocalInterfaceAddressTest - : public ::testing::TestWithParam<SbSocketAddressType> { - public: - SbSocketAddressType GetAddressType() { return GetParam(); } -}; - -TEST_F(SbSocketGetLocalInterfaceAddressTest, SunnyDay) { +TEST(SbSocketGetLocalInterfaceAddressTest, SunnyDay) { SbSocketAddress address; // Initialize to something invalid. SbMemorySet(&address, 0xFE, sizeof(address)); @@ -40,7 +34,7 @@ EXPECT_FALSE(IsLocalhost(&address)); } -TEST_F(SbSocketGetLocalInterfaceAddressTest, RainyDayNull) { +TEST(SbSocketGetLocalInterfaceAddressTest, RainyDayNull) { #if SB_API_VERSION < 4 EXPECT_FALSE(SbSocketGetLocalInterfaceAddress(NULL)); #else @@ -48,17 +42,6 @@ #endif // SB_API_VERSION < 4 } -#if SB_HAS(IPV6) -INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes, - SbSocketGetLocalInterfaceAddressTest, - ::testing::Values(kSbSocketAddressTypeIpv4, - kSbSocketAddressTypeIpv6)); -#else -INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes, - SbSocketGetLocalInterfaceAddressTest, - ::testing::Values(kSbSocketAddressTypeIpv4)); -#endif - } // namespace } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_cancel_test.cc b/src/starboard/nplb/speech_recognizer_cancel_test.cc new file mode 100644 index 0000000..471275e --- /dev/null +++ b/src/starboard/nplb/speech_recognizer_cancel_test.cc
@@ -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. + +#include "starboard/nplb/speech_recognizer_helper.h" +#include "starboard/speech_recognizer.h" +#include "starboard/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { + +#if SB_HAS(SPEECH_RECOGNIZER) && \ + SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION + +TEST_F(SpeechRecognizerTest, CancelTestSunnyDay) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, true, 1}; + + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + SbSpeechRecognizerCancel(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, CancelIsCalledMultipleTimes) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, false, 1}; + + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + SbSpeechRecognizerCancel(recognizer); + SbSpeechRecognizerCancel(recognizer); + SbSpeechRecognizerCancel(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, CancelTestStartIsNotCalled) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechRecognizerCancel(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, CancelWithInvalidSpeechRecognizer) { + SbSpeechRecognizerCancel(kSbSpeechRecognizerInvalid); +} + +#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= + // SB_SPEECH_RECOGNIZER_API_VERSION + +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_create_test.cc b/src/starboard/nplb/speech_recognizer_create_test.cc new file mode 100644 index 0000000..ddbc93f --- /dev/null +++ b/src/starboard/nplb/speech_recognizer_create_test.cc
@@ -0,0 +1,35 @@ +// 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/nplb/speech_recognizer_helper.h" +#include "starboard/speech_recognizer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { + +#if SB_HAS(SPEECH_RECOGNIZER) && \ + SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION + +TEST_F(SpeechRecognizerTest, CreateTestSunnyDay) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechRecognizerDestroy(recognizer); +} + +#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= + // SB_SPEECH_RECOGNIZER_API_VERSION + +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_destroy_test.cc b/src/starboard/nplb/speech_recognizer_destroy_test.cc new file mode 100644 index 0000000..ffd14de --- /dev/null +++ b/src/starboard/nplb/speech_recognizer_destroy_test.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/nplb/speech_recognizer_helper.h" +#include "starboard/speech_recognizer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { + +#if SB_HAS(SPEECH_RECOGNIZER) && \ + SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION + +TEST_F(SpeechRecognizerTest, DestroyInvalidSpeechRecognizer) { + SbSpeechRecognizerDestroy(kSbSpeechRecognizerInvalid); +} + +TEST_F(SpeechRecognizerTest, DestroyRecognizerWithoutStopping) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, true, 1}; + SbSpeechRecognizerStart(recognizer, &configuration); + SbSpeechRecognizerDestroy(recognizer); +} + +#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= + // SB_SPEECH_RECOGNIZER_API_VERSION + +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_helper.h b/src/starboard/nplb/speech_recognizer_helper.h new file mode 100644 index 0000000..727a993 --- /dev/null +++ b/src/starboard/nplb/speech_recognizer_helper.h
@@ -0,0 +1,103 @@ +// 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_NPLB_SPEECH_RECOGNIZER_HELPER_H_ +#define STARBOARD_NPLB_SPEECH_RECOGNIZER_HELPER_H_ + +#include "starboard/common/ref_counted.h" +#include "starboard/common/semaphore.h" +#include "starboard/speech_recognizer.h" +#include "starboard/time.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { + +#if SB_HAS(SPEECH_RECOGNIZER) && \ + SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION + +class EventMock : public RefCounted<EventMock> { + public: + MOCK_METHOD0(OnEvent, void(void)); +}; + +class SpeechRecognizerTest : public ::testing::Test { + public: + SpeechRecognizerTest() + : handler_(), test_mock_(new ::testing::StrictMock<EventMock>()) { + handler_.on_speech_detected = OnSpeechDetected; + handler_.on_error = OnError; + handler_.on_results = OnResults; + handler_.context = this; + } + + static void OnSpeechDetected(void* context, bool detected) { + SpeechRecognizerTest* test = static_cast<SpeechRecognizerTest*>(context); + test->OnSignalEvent(); + } + static void OnError(void* context, SbSpeechRecognizerError error) { + SpeechRecognizerTest* test = static_cast<SpeechRecognizerTest*>(context); + test->OnSignalEvent(); + } + static void OnResults(void* context, + SbSpeechResult* results, + int results_size, + bool is_final) { + SpeechRecognizerTest* test = static_cast<SpeechRecognizerTest*>(context); + test->OnSignalEvent(); + } + + SbSpeechRecognizerHandler* handler() { return &handler_; } + + EventMock& test_mock() { return *test_mock_.get(); } + + void Wait() { + if (!event_semaphore_.TakeWait(kWaitTime)) { + SB_LOG(WARNING) << "Waiting for recognizer event to come, but timeout!"; + } + } + + void OnSignalEvent() { + test_mock_->OnEvent(); + event_semaphore_.Put(); + } + + protected: + // Per test teardown. + virtual void TearDown() { + // Wait for the speech recognizer server to tear down in order to start + // a new one for the next test. The speech recognizer server was running in + // another thread. + SbThreadSleep(kTearDownTime); + } + + private: + const SbTime kTearDownTime = 10 * kSbTimeMillisecond; + const SbTime kWaitTime = 600 * kSbTimeMillisecond; + + SbSpeechRecognizerHandler handler_; + + starboard::Semaphore event_semaphore_; + + const scoped_refptr<EventMock> test_mock_; +}; + +#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= + // SB_SPEECH_RECOGNIZER_API_VERSION + +} // namespace nplb +} // namespace starboard + +#endif // STARBOARD_NPLB_SPEECH_RECOGNIZER_HELPER_H_
diff --git a/src/starboard/nplb/speech_recognizer_start_test.cc b/src/starboard/nplb/speech_recognizer_start_test.cc new file mode 100644 index 0000000..65fb03d --- /dev/null +++ b/src/starboard/nplb/speech_recognizer_start_test.cc
@@ -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. + +#include "starboard/nplb/speech_recognizer_helper.h" +#include "starboard/speech_recognizer.h" +#include "starboard/thread.h" + +namespace starboard { +namespace nplb { + +#if SB_HAS(SPEECH_RECOGNIZER) && \ + SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION + +TEST_F(SpeechRecognizerTest, StartTestSunnyDay) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {false, false, 1}; + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + EXPECT_CALL(test_mock(), OnEvent()).Times(testing::AtLeast(1)); + Wait(); + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, StartRecognizerWithContinuousRecognition) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, false, 1}; + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + EXPECT_CALL(test_mock(), OnEvent()).Times(testing::AtLeast(1)); + Wait(); + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, StartRecognizerWithInterimResults) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {false, true, 1}; + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + EXPECT_CALL(test_mock(), OnEvent()).Times(testing::AtLeast(1)); + Wait(); + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, StartRecognizerWith10MaxAlternatives) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, true, 10}; + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + EXPECT_CALL(test_mock(), OnEvent()).Times(testing::AtLeast(1)); + Wait(); + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, StartIsCalledMultipleTimes) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, true, 1}; + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + EXPECT_CALL(test_mock(), OnEvent()).Times(testing::AtLeast(1)); + bool success = SbSpeechRecognizerStart(recognizer, &configuration); + EXPECT_FALSE(success); + success = SbSpeechRecognizerStart(recognizer, &configuration); + EXPECT_FALSE(success); + Wait(); + SbSpeechRecognizerDestroy(recognizer); +} + +TEST_F(SpeechRecognizerTest, StartWithInvalidSpeechRecognizer) { + SbSpeechConfiguration configuration = {true, true, 1}; + bool success = + SbSpeechRecognizerStart(kSbSpeechRecognizerInvalid, &configuration); + EXPECT_FALSE(success); +} + +#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= + // SB_SPEECH_RECOGNIZER_API_VERSION + +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_stop_test.cc b/src/starboard/nplb/speech_recognizer_stop_test.cc new file mode 100644 index 0000000..0663a50 --- /dev/null +++ b/src/starboard/nplb/speech_recognizer_stop_test.cc
@@ -0,0 +1,45 @@ +// 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/nplb/speech_recognizer_helper.h" +#include "starboard/speech_recognizer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { + +#if SB_HAS(SPEECH_RECOGNIZER) && \ + SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION + +TEST_F(SpeechRecognizerTest, StopIsCalledMultipleTimes) { + SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler()); + EXPECT_TRUE(SbSpeechRecognizerIsValid(recognizer)); + SbSpeechConfiguration configuration = {true, true, 1}; + if (!SbSpeechRecognizerStart(recognizer, &configuration)) { + SB_LOG(WARNING) << "SbSpeechRecognizerStart failed. Test skipped."; + SbSpeechRecognizerDestroy(recognizer); + return; + } + + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerStop(recognizer); + SbSpeechRecognizerDestroy(recognizer); +} + +#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= + // SB_SPEECH_RECOGNIZER_API_VERSION + +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/player.h b/src/starboard/player.h index de12f42..5335bd9 100644 --- a/src/starboard/player.h +++ b/src/starboard/player.h
@@ -179,7 +179,9 @@ SbPlayerState state, int ticket); -// Callback to free the given sample buffer data. +// Callback to free the given sample buffer data. When more than one buffer +// are sent in SbPlayerWriteSample(), the implementation only has to call this +// callback with |sample_buffer| points to the the first buffer. typedef void (*SbPlayerDeallocateSampleFunc)(SbPlayer player, void* context, const void* sample_buffer);
diff --git a/src/starboard/shared/linux/socket_get_interface_address.cc b/src/starboard/shared/linux/socket_get_interface_address.cc index 752d898..923e4c0 100644 --- a/src/starboard/shared/linux/socket_get_interface_address.cc +++ b/src/starboard/shared/linux/socket_get_interface_address.cc
@@ -26,7 +26,6 @@ #include <sys/socket.h> #include <sys/types.h> -#include "net/base/net_util.h" #include "starboard/byte_swap.h" #include "starboard/log.h" #include "starboard/memory.h" @@ -36,6 +35,9 @@ namespace { +// TODO: Move this constant to socket.h. +const int kIPv6AddressSize = 16; + bool IsAnyAddress(const SbSocketAddress& address) { switch (address.type) { case kSbSocketAddressTypeIpv4: @@ -44,7 +46,7 @@ #if SB_HAS(IPV6) case kSbSocketAddressTypeIpv6: { bool found_nonzero = false; - for (std::size_t i = 0; i != net::kIPv6AddressSize; ++i) { + for (std::size_t i = 0; i != kIPv6AddressSize; ++i) { found_nonzero |= (address.address[i] != 0); } return !found_nonzero;
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc index aa77349..e831818 100644 --- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc +++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc
@@ -21,8 +21,9 @@ #include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h" void SbSpeechRecognizerCancel(SbSpeechRecognizer recognizer) { - SB_DCHECK(SbSpeechRecognizerIsValid(recognizer)); - recognizer->Cancel(); + if (SbSpeechRecognizerIsValid(recognizer)) { + recognizer->Cancel(); + } } #endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc index b7b052b..3948cc7 100644 --- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc +++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc
@@ -20,7 +20,9 @@ #include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h" void SbSpeechRecognizerDestroy(SbSpeechRecognizer recognizer) { - SbSpeechRecognizerPrivate::DestroySpeechRecognizer(recognizer); + if (SbSpeechRecognizerIsValid(recognizer)) { + SbSpeechRecognizerPrivate::DestroySpeechRecognizer(recognizer); + } } #endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc index e8cadfd..717d229 100644 --- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc +++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc
@@ -21,8 +21,9 @@ #include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h" void SbSpeechRecognizerStop(SbSpeechRecognizer recognizer) { - SB_DCHECK(SbSpeechRecognizerIsValid(recognizer)); - recognizer->Stop(); + if (SbSpeechRecognizerIsValid(recognizer)) { + recognizer->Stop(); + } } #endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
diff --git a/src/starboard/starboard_base_target.gypi b/src/starboard/starboard_base_target.gypi index dd66b79..a4d07a7 100644 --- a/src/starboard/starboard_base_target.gypi +++ b/src/starboard/starboard_base_target.gypi
@@ -41,6 +41,11 @@ 'STARBOARD_ALLOWS_MEMORY_TRACKING', ], }], + ['sb_enable_lib==1', { + 'defines': [ + 'SB_IS_LIBRARY=1', + ], + }], ['starboard_path == ""', { 'defines': [ # There doesn't appear to be any way to use the C preprocessor to do
diff --git a/src/third_party/mozjs/js/public/Value.h b/src/third_party/mozjs/js/public/Value.h index f2345f4..3637495 100644 --- a/src/third_party/mozjs/js/public/Value.h +++ b/src/third_party/mozjs/js/public/Value.h
@@ -802,7 +802,13 @@ #endif /* JS_BITS_PER_WORD */ +#if defined(STARBOARD) && SB_IS(COMPILER_MSVC) +// Note that the Value class's data member is private +// if !defined(_MSC_VER) && !defined(__sparc) and is public otherwise. +#define JSVAL_TO_IMPL(v) v.data +#else static inline jsval_layout JSVAL_TO_IMPL(JS::Value v); +#endif static inline JS::Value IMPL_TO_JSVAL(jsval_layout l); namespace JS { @@ -1187,8 +1193,11 @@ JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4); JS_STATIC_ASSERT(sizeof(Value) == 8); } - +#if defined(STARBOARD) && SB_IS(COMPILER_MSVC) + // Do not declare JSVAL_TO_IMPL a friend. It is a macro. +#else friend jsval_layout (::JSVAL_TO_IMPL)(Value); +#endif friend Value (::IMPL_TO_JSVAL)(jsval_layout l); }; @@ -1620,11 +1629,15 @@ } // namespace js +#if defined(STARBOARD) && SB_IS(COMPILER_MSVC) +// Do not define JSVAL_TO_IMPL as a function. It is a macro. +#else inline jsval_layout JSVAL_TO_IMPL(JS::Value v) { return v.data; } +#endif inline JS::Value IMPL_TO_JSVAL(jsval_layout l)