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)