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)