Import Cobalt 2.11130 2016-09-17
diff --git a/src/base/test/run_all_unittests.cc b/src/base/test/run_all_unittests.cc
index 1aba309..7c69f3b 100644
--- a/src/base/test/run_all_unittests.cc
+++ b/src/base/test/run_all_unittests.cc
@@ -5,22 +5,11 @@
 #include "base/test/main_hook.h"
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 
-#if !defined(OS_STARBOARD)
-int main(int argc, char** argv) {
-  MainHook hook(main, argc, argv);
+int TestSuiteRun(int argc, char** argv) {
+  MainHook hook(NULL, argc, argv);
   return base::TestSuite(argc, argv).Run();
 }
-#else
-#include "starboard/event.h"
-#include "starboard/system.h"
 
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    MainHook hook(NULL, data->argument_count, data->argument_values);
-    SbSystemRequestStop(
-        base::TestSuite(data->argument_count, data->argument_values).Run());
-  }
-}
-#endif
+STARBOARD_WRAP_SIMPLE_MAIN(TestSuiteRun);
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 74d4c2d..8ab8b57 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -16,6 +16,7 @@
 
 #include "cobalt/audio/audio_device.h"
 
+#include "base/debug/trace_event.h"
 #include "base/memory/scoped_ptr.h"
 #if defined(OS_STARBOARD)
 #include "starboard/audio_sink.h"
@@ -192,6 +193,7 @@
                                            int* offset_in_frames,
                                            bool* is_playing,
                                            bool* is_eos_reached) {
+  TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::UpdateSourceStatus()");
   *is_playing = true;
   *is_eos_reached = false;
 
@@ -248,6 +250,7 @@
 }
 
 void AudioDevice::Impl::FillOutputAudioBus() {
+  TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::FillOutputAudioBus()");
   if (output_sample_type_ == kSbMediaAudioSampleTypeFloat32) {
     FillOutputAudioBusForType<float>();
   } else if (output_sample_type_ == kSbMediaAudioSampleTypeInt16) {
@@ -305,6 +308,8 @@
                  static_cast<size_t>(kRenderBufferSizeFrames),
                  ShellAudioBus::kFloat32, ShellAudioBus::kPlanar),
       render_callback_(callback) {
+  TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::Impl()");
+
   DCHECK_GT(number_of_channels, 0);
   DCHECK(media::ShellAudioStreamer::Instance()->GetConfig().interleaved())
       << "Planar audio is not supported.";
@@ -341,6 +346,8 @@
 
 bool AudioDevice::Impl::PullFrames(uint32* offset_in_frame,
                                    uint32* total_frames) {
+  TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::PullFrames()");
+
   // In case offset_in_frame or total_frames is NULL.
   uint32 dummy_offset_in_frame;
   uint32 dummy_total_frames;
@@ -401,6 +408,7 @@
 }
 
 void AudioDevice::Impl::FillOutputAudioBus() {
+  TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::FillOutputAudioBus()");
   // Determine the offset into the audio bus that represents the tail of
   // buffered data.
   uint64 channel_offset = buffered_frame_cursor_ % kFramesPerChannel;
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index 787f51a..c4bcc0c 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -18,6 +18,7 @@
 
 #include <stdint.h>
 
+#include <iomanip>
 #include <set>
 #include <sstream>
 #include <string>
@@ -29,6 +30,7 @@
 #include "base/memory/singleton.h"
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
+#include "base/time.h"
 
 // The CVal system allows you to mark certain variables to be part of the
 // CVal system and therefore analyzable and trackable by other systems.  All
@@ -93,6 +95,7 @@
 // An enumeration to allow CVals to track the type that they hold in a run-time
 // variable.
 enum CValType {
+  kSize,
   kU32,
   kU64,
   kS32,
@@ -100,8 +103,41 @@
   kFloat,
   kDouble,
   kString,
+  kTimeDelta,
 };
 
+// CVals are commonly used for values that are in units of bytes.  By making
+// a CVal of type SizeInBytes, this can be made explicit, and allows the CVal
+// system to use KB/MB suffixes instead of K/M.
+namespace cval {
+class SizeInBytes {
+ public:
+  SizeInBytes(uint64 size_in_bytes)  // NOLINT(runtime/explicit)
+      : size_in_bytes_(size_in_bytes) {}
+  SizeInBytes& operator=(uint64 rhs) {
+    size_in_bytes_ = rhs;
+    return *this;
+  }
+  SizeInBytes& operator+=(const SizeInBytes& rhs) {
+    size_in_bytes_ += rhs.size_in_bytes_;
+    return *this;
+  }
+  SizeInBytes& operator-=(const SizeInBytes& rhs) {
+    size_in_bytes_ -= rhs.size_in_bytes_;
+    return *this;
+  }
+  operator uint64() const { return value(); }
+
+  uint64 value() const { return size_in_bytes_; }
+
+ private:
+  uint64 size_in_bytes_;
+};
+inline std::ostream& operator<<(std::ostream& out, const SizeInBytes& size) {
+  return out << static_cast<uint64>(size);
+}
+}  // namespace cval
+
 namespace CValDetail {
 
 // Introduce a Traits class so that we can convert from C++ type to
@@ -113,6 +149,11 @@
   int UnsupportedCValType[0];
 };
 template <>
+struct Traits<cval::SizeInBytes> {
+  static const CValType kTypeVal = kSize;
+  static const bool kIsNumerical = true;
+};
+template <>
 struct Traits<uint32_t> {
   static const CValType kTypeVal = kU32;
   static const bool kIsNumerical = true;
@@ -147,6 +188,11 @@
   static const CValType kTypeVal = kString;
   static const bool kIsNumerical = false;
 };
+template <>
+struct Traits<base::TimeDelta> {
+  static const CValType kTypeVal = kTimeDelta;
+  static const bool kIsNumerical = true;
+};
 
 // Provide methods to convert from an arbitrary type to a string, useful for
 // systems that want to read the value of a CVal without caring about its type.
@@ -162,22 +208,60 @@
   return value;
 }
 
+template <>
+inline std::string ValToString<base::TimeDelta>(const base::TimeDelta& value) {
+  return ValToString(value.InMicroseconds());
+}
+
 // Helper function to implement the numerical branch of ValToPrettyString
 template <typename T>
-std::string NumericalValToPrettyString(const T& value) {
-  struct {
-    T threshold;
-    T divide_by;
-    const char* postfix;
-  } thresholds[] = {
-      {static_cast<T>(10 * 1024 * 1024), static_cast<T>(1024 * 1024), "MB"},
-      {static_cast<T>(10 * 1024), static_cast<T>(1024), "KB"},
+struct ThresholdListElement {
+  T threshold;
+  T divide_by;
+  const char* postfix;
+};
+template <>
+struct ThresholdListElement<cval::SizeInBytes> {
+  uint64 threshold;
+  uint64 divide_by;
+  const char* postfix;
+};
+template <typename T>
+struct ThresholdList {
+  ThresholdList(ThresholdListElement<T>* array, size_t size)
+      : array(array), size(size) {}
+  ThresholdListElement<T>* array;
+  size_t size;
+};
+
+template <typename T>
+ThresholdList<T> GetThresholdList() {
+  static ThresholdListElement<T> thresholds[] = {
+      {static_cast<T>(10 * 1000 * 1000), static_cast<T>(1000 * 1000), "M"},
+      {static_cast<T>(10 * 1000), static_cast<T>(1000), "K"},
   };
+  return ThresholdList<T>(thresholds, arraysize(thresholds));
+}
+
+template <>
+inline ThresholdList<cval::SizeInBytes> GetThresholdList<cval::SizeInBytes>() {
+  static ThresholdListElement<cval::SizeInBytes> thresholds[] = {
+      {10LL * 1024LL * 1024LL * 1024LL, 1024LL * 1024LL * 1024LL, "GB"},
+      {10LL * 1024LL * 1024LL, 1024LL * 1024LL, "MB"},
+      {10LL * 1024LL, 1024LL, "KB"},
+  };
+  return ThresholdList<cval::SizeInBytes>(thresholds, arraysize(thresholds));
+}
+
+template <typename T>
+std::string NumericalValToPrettyString(const T& value) {
+  ThresholdList<T> threshold_list = GetThresholdList<T>();
+  ThresholdListElement<T>* thresholds = threshold_list.array;
 
   T divided_value = value;
   const char* postfix = "";
 
-  for (size_t i = 0; i < sizeof(thresholds) / sizeof(thresholds[0]); ++i) {
+  for (size_t i = 0; i < threshold_list.size; ++i) {
     if (value >= thresholds[i].threshold) {
       divided_value = value / thresholds[i].divide_by;
       postfix = thresholds[i].postfix;
@@ -190,6 +274,43 @@
   return oss.str();
 }
 
+template <>
+inline std::string NumericalValToPrettyString<base::TimeDelta>(
+    const base::TimeDelta& value) {
+  const int64 kMicrosecond = 1LL;
+  const int64 kMillisecond = 1000LL * kMicrosecond;
+  const int64 kSecond = 1000LL * kMillisecond;
+  const int64 kMinute = 60LL * kSecond;
+  const int64 kHour = 60LL * kMinute;
+
+  int64 value_in_us = value.InMicroseconds();
+  bool negative = value_in_us < 0;
+  value_in_us *= negative ? -1 : 1;
+
+  std::ostringstream oss;
+  if (negative) {
+    oss << "-";
+  }
+  if (value_in_us > kHour) {
+    oss << value_in_us / kHour << ":" << std::setfill('0') << std::setw(2)
+        << (value_in_us % kHour) / kMinute << ":" << std::setfill('0')
+        << std::setw(2) << (value_in_us % kMinute) / kSecond << "h";
+  } else if (value_in_us > kMinute) {
+    oss << value_in_us / kMinute << ":" << std::setfill('0') << std::setw(2)
+        << (value_in_us % kMinute) / kSecond << "m";
+  } else if (value_in_us > kSecond * 10) {
+    oss << value_in_us / kSecond << "s";
+  } else if (value_in_us > kMillisecond * 2) {
+    oss << value_in_us / kMillisecond << "ms";
+  } else if (value_in_us > 0) {
+    oss << value_in_us << "us";
+  } else {
+    oss << value_in_us;
+  }
+
+  return oss.str();
+}
+
 // Helper function for the subsequent ValToPrettyString function.
 template <typename T, bool IsNumerical>
 class ValToPrettyStringHelper {};
@@ -413,10 +534,7 @@
     }
   }
 
-  operator T() const {
-    base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
-    return value_;
-  }
+  operator T() const { return value(); }
 
   const CValImpl<T>& operator=(const T& rhs) {
     bool value_changed;
@@ -479,6 +597,10 @@
     return *this;
   }
 
+  bool operator<(const T& rhs) const { return value() < rhs; }
+  bool operator>(const T& rhs) const { return value() > rhs; }
+  bool operator==(const T& rhs) const { return value() == rhs; }
+
   std::string GetValueAsString() const {
     // Can be called to get the value of a CVal without knowing the type first.
     base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
@@ -492,6 +614,11 @@
     return ValToPrettyString<T>(value_);
   }
 
+  T value() const {
+    base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+    return value_;
+  }
+
  private:
   void CommonConstructor() {
     registered_ = false;
@@ -560,6 +687,12 @@
     return *this;
   }
 
+  bool operator<(const T& rhs) const { return value_ < rhs; }
+  bool operator>(const T& rhs) const { return value_ > rhs; }
+  bool operator==(const T& rhs) const { return value_ == rhs; }
+
+  T value() const { return value_; }
+
  private:
   T value_;
 };
diff --git a/src/cobalt/base/c_val_time_interval_entry_stats.h b/src/cobalt/base/c_val_time_interval_entry_stats.h
index 11d1f94..4ea30ad 100644
--- a/src/cobalt/base/c_val_time_interval_entry_stats.h
+++ b/src/cobalt/base/c_val_time_interval_entry_stats.h
@@ -28,6 +28,44 @@
 
 namespace base {
 
+namespace detail {
+template <typename T>
+double ToDouble(T value) {
+  return static_cast<double>(value);
+}
+template <>
+inline double ToDouble<base::TimeDelta>(base::TimeDelta value) {
+  return static_cast<double>(value.InMicroseconds());
+}
+
+template <typename T>
+T FromDouble(double value) {
+  return static_cast<T>(value);
+}
+template <>
+inline base::TimeDelta FromDouble<base::TimeDelta>(double value) {
+  return base::TimeDelta::FromMicroseconds(static_cast<int64>(value));
+}
+
+template <typename T>
+T Max() {
+  return std::numeric_limits<T>::max();
+}
+template <>
+inline base::TimeDelta Max<base::TimeDelta>() {
+  return base::TimeDelta::Max();
+}
+
+template <typename T>
+T Min() {
+  return std::numeric_limits<T>::min();
+}
+template <>
+inline base::TimeDelta Min<base::TimeDelta>() {
+  return -base::TimeDelta::Max();
+}
+}  // namespace detail
+
 // This class tracks entries over a specified time period. When the time period
 // has elapsed, the average, minimum, maximum, and standard deviation of that
 // time period are recorded with CVals, and the tracking resets for the next
@@ -48,10 +86,10 @@
   const int64 time_interval_in_ms_;
 
   // CVals of the stats for the previously completed time interval.
-  base::CVal<double, Visibility> average_;
+  base::CVal<EntryType, Visibility> average_;
   base::CVal<EntryType, Visibility> minimum_;
   base::CVal<EntryType, Visibility> maximum_;
-  base::CVal<double, Visibility> standard_deviation_;
+  base::CVal<EntryType, Visibility> standard_deviation_;
 
   // Active time interval-related
   base::TimeTicks active_start_time_;
@@ -79,10 +117,13 @@
 CValTimeIntervalEntryStats<EntryType, Visibility>::CValTimeIntervalEntryStats(
     const std::string& name, int64 time_interval_in_ms)
     : time_interval_in_ms_(time_interval_in_ms),
-      average_(StringPrintf("%s.Avg", name.c_str()), 0, "Average time."),
-      minimum_(StringPrintf("%s.Min", name.c_str()), 0, "Minimum time."),
-      maximum_(StringPrintf("%s.Max", name.c_str()), 0, "Maximum time."),
-      standard_deviation_(StringPrintf("%s.Std", name.c_str()), 0,
+      average_(StringPrintf("%s.Avg", name.c_str()), EntryType(),
+               "Average time."),
+      minimum_(StringPrintf("%s.Min", name.c_str()), EntryType(),
+               "Minimum time."),
+      maximum_(StringPrintf("%s.Max", name.c_str()), EntryType(),
+               "Maximum time."),
+      standard_deviation_(StringPrintf("%s.Std", name.c_str()), EntryType(),
                           "Standard deviation of times."),
       active_estimated_mean_(0) {
   ResetActiveEntryStats();
@@ -103,7 +144,7 @@
   // the estimated mean is set to the passed in value.
   if (active_start_time_.is_null()) {
     active_start_time_ = now;
-    active_estimated_mean_ = static_cast<double>(value);
+    active_estimated_mean_ = detail::ToDouble(value);
     // Otherwise, check for the time interval having ended. If it has, then the
     // CVals are updated using the active stats, the active stats are reset, and
     // the timer restarted.
@@ -114,7 +155,8 @@
     DCHECK_GT(active_count_, 0);
 
     double active_shifted_mean = active_shifted_sum_ / active_count_;
-    average_ = active_estimated_mean_ + active_shifted_mean;
+    average_ = detail::FromDouble<EntryType>(active_estimated_mean_ +
+                                             active_shifted_mean);
     // The equation comes from the following:
     // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Computing_shifted_data
     double variance =
@@ -122,7 +164,7 @@
          ((active_shifted_sum_ * active_shifted_sum_) / active_count_)) /
         active_count_;
     variance = std::max(variance, 0.0);
-    standard_deviation_ = std::sqrt(variance);
+    standard_deviation_ = detail::FromDouble<EntryType>(std::sqrt(variance));
     minimum_ = active_minimum_;
     maximum_ = active_maximum_;
 
@@ -143,7 +185,7 @@
   ++active_count_;
 
   double shifted_value_as_double =
-      static_cast<double>(value - active_estimated_mean_);
+      detail::ToDouble(value) - active_estimated_mean_;
   active_shifted_sum_ += shifted_value_as_double;
   active_shifted_sum_squares_ +=
       shifted_value_as_double * shifted_value_as_double;
@@ -164,8 +206,8 @@
   active_shifted_sum_ = 0;
   active_shifted_sum_squares_ = 0;
 
-  active_minimum_ = std::numeric_limits<EntryType>::max();
-  active_maximum_ = std::numeric_limits<EntryType>::min();
+  active_minimum_ = detail::Max<EntryType>();
+  active_maximum_ = detail::Min<EntryType>();
 }
 
 }  // namespace base
diff --git a/src/cobalt/base/c_val_time_interval_timer.h b/src/cobalt/base/c_val_time_interval_timer.h
index 77d386d..8d24dd6 100644
--- a/src/cobalt/base/c_val_time_interval_timer.h
+++ b/src/cobalt/base/c_val_time_interval_timer.h
@@ -54,12 +54,12 @@
       return;
     }
 
-    entry_stats_.AddEntry((now - start_time_).InMicroseconds(), now);
+    entry_stats_.AddEntry(now - start_time_, now);
     start_time_ = base::TimeTicks();
   }
 
  private:
-  base::CValTimeIntervalEntryStats<int64, Visibility> entry_stats_;
+  base::CValTimeIntervalEntryStats<base::TimeDelta, Visibility> entry_stats_;
   base::TimeTicks start_time_;
 };
 
diff --git a/src/cobalt/base/stop_watch.cc b/src/cobalt/base/stop_watch.cc
index ae464e4..d7814fe 100644
--- a/src/cobalt/base/stop_watch.cc
+++ b/src/cobalt/base/stop_watch.cc
@@ -35,8 +35,7 @@
 
 void StopWatch::Stop() {
   if (IsCounting()) {
-    int64 time_elapsed =
-        (base::TimeTicks::Now() - start_time_).InMicroseconds();
+    base::TimeDelta time_elapsed = base::TimeTicks::Now() - start_time_;
     start_time_ = base::TimeTicks();
     owner_->OnStopWatchStopped(id_, time_elapsed);
   }
diff --git a/src/cobalt/base/stop_watch.h b/src/cobalt/base/stop_watch.h
index 7ea32a7..97fefd3 100644
--- a/src/cobalt/base/stop_watch.h
+++ b/src/cobalt/base/stop_watch.h
@@ -51,7 +51,7 @@
 
  private:
   virtual bool IsStopWatchEnabled(int id) const = 0;
-  virtual void OnStopWatchStopped(int id, int64 time_elapsed) = 0;
+  virtual void OnStopWatchStopped(int id, base::TimeDelta time_elapsed) = 0;
 
   friend class StopWatch;
 };
diff --git a/src/cobalt/base/wrap_main_starboard.h b/src/cobalt/base/wrap_main_starboard.h
index c42dc73..1a72bd7 100644
--- a/src/cobalt/base/wrap_main_starboard.h
+++ b/src/cobalt/base/wrap_main_starboard.h
@@ -23,31 +23,13 @@
 #include "base/message_loop.h"
 #include "cobalt/base/init_cobalt.h"
 #include "cobalt/base/wrap_main.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 #include "starboard/event.h"
 #include "starboard/system.h"
 
 namespace cobalt {
 namespace wrap_main {
 
-// Starboard implementation of the "Simple Main" use case.
-template <MainFunction main_function>
-void SimpleEventHandler(const SbEvent* event) {
-  static base::AtExitManager* g_at_exit = NULL;
-  switch (event->type) {
-    case kSbEventTypeStart: {
-      SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-      DCHECK(!g_at_exit);
-      g_at_exit = new base::AtExitManager();
-      InitCobalt(data->argument_count, data->argument_values);
-      SbSystemRequestStop(
-          main_function(data->argument_count, data->argument_values));
-      break;
-    }
-    default:
-      break;
-  }
-}
-
 // Starboard implementation of the "Base Main" use case.
 template <StartFunction start_function, EventFunction event_function,
           StopFunction stop_function>
@@ -93,15 +75,21 @@
   }
 }
 
+template <MainFunction main_function>
+int CobaltMainAddOns(int argc, char** argv) {
+  base::AtExitManager at_exit;
+  cobalt::InitCobalt(argc, argv);
+  return main_function(argc, argv);
+}
+
 }  // namespace wrap_main
 }  // namespace cobalt
 
 // Calls |main_function| at startup, creates an AtExitManager and calls
 // InitCobalt, and terminates once it is completed.
-#define COBALT_WRAP_SIMPLE_MAIN(main_function)                     \
-  void SbEventHandle(const SbEvent* event) {                       \
-    ::cobalt::wrap_main::SimpleEventHandler<main_function>(event); \
-  }
+#define COBALT_WRAP_SIMPLE_MAIN(main_function)                          \
+  STARBOARD_WRAP_SIMPLE_MAIN(                                           \
+      ::cobalt::wrap_main::CobaltMainAddOns<main_function>);
 
 // Like COBALT_WRAP_BASE_MAIN, but supports an event_function to forward
 // non-application events to.
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
index 16e4b25..d2cfb40 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
@@ -430,9 +430,11 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(context, new_object, prototype, NULL,
                              proxy_handler.Pointer()));
-  WrapperPrivate::GetOpaqueRootFunction get_root =
-      base::Bind(&GetOpaqueRootFromWrappable);
-  WrapperPrivate::AddPrivateData(context, proxy, wrappable, get_root);
+  WrapperPrivate::GetOpaqueRootFunction get_root;
+  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
+  get_root = base::Bind(&GetOpaqueRootFromWrappable);
+  WrapperPrivate::AddPrivateData(
+      context, proxy, wrappable, get_root, get_reachable_wrappables);
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsGetOpaqueRootInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsGetOpaqueRootInterface.cc
index fbb57b3..6d71151 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsGetOpaqueRootInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsGetOpaqueRootInterface.cc
@@ -91,6 +91,17 @@
   return impl->get_opaque_root_function_name();
 }
 
+void GetReachableWrappables(const scoped_refptr<Wrappable>& wrappable,
+    WrapperPrivate::WrappableVector* reachable) {
+  DCHECK(reachable);
+  GetOpaqueRootInterface* impl =
+      base::polymorphic_downcast<GetOpaqueRootInterface*>(wrappable.get());
+  Wrappable* reachable_0 = impl->add_opaque_root_function_name();
+  if (reachable_0) {
+    reachable->push_back(reachable_0);
+  }
+}
+
 class MozjsGetOpaqueRootInterfaceHandler : public ProxyHandler {
  public:
   MozjsGetOpaqueRootInterfaceHandler()
@@ -326,9 +337,12 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(context, new_object, prototype, NULL,
                              proxy_handler.Pointer()));
-  WrapperPrivate::GetOpaqueRootFunction get_root =
-      base::Bind(&GetOpaqueRootFromWrappable);
-  WrapperPrivate::AddPrivateData(context, proxy, wrappable, get_root);
+  WrapperPrivate::GetOpaqueRootFunction get_root;
+  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
+  get_root = base::Bind(&GetOpaqueRootFromWrappable);
+  get_reachable_wrappables = base::Bind(&GetReachableWrappables);
+  WrapperPrivate::AddPrivateData(
+      context, proxy, wrappable, get_root, get_reachable_wrappables);
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template
index 19eddeb..eb4e553 100644
--- a/src/cobalt/bindings/mozjs/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -107,6 +107,21 @@
 }
 
 {% endif %}
+{% if add_opaque_roots %}
+void GetReachableWrappables(const scoped_refptr<Wrappable>& wrappable,
+    WrapperPrivate::WrappableVector* reachable) {
+  DCHECK(reachable);
+  {{impl_class}}* impl =
+      base::polymorphic_downcast<{{impl_class}}*>(wrappable.get());
+{% for reachable_object in add_opaque_roots %}
+  Wrappable* reachable_{{loop.index0}} = impl->{{reachable_object}}();
+  if (reachable_{{loop.index0}}) {
+    reachable->push_back(reachable_{{loop.index0}});
+  }
+{% endfor %}
+}
+
+{% endif %}
 {% if named_property_getter %}
 bool IsSupportedNamedProperty(JSContext* context, JS::HandleObject object,
                               const std::string& property_name) {
@@ -833,12 +848,20 @@
   JS::RootedObject proxy(context,
       ProxyHandler::NewProxy(context, new_object, prototype, NULL,
                              proxy_handler.Pointer()));
+{% if add_opaque_roots or get_opaque_root %}
+  WrapperPrivate::GetOpaqueRootFunction get_root;
+  WrapperPrivate::GetReachableWrappablesFunction get_reachable_wrappables;
 {% if get_opaque_root %}
-  WrapperPrivate::GetOpaqueRootFunction get_root =
-      base::Bind(&GetOpaqueRootFromWrappable);
+  get_root = base::Bind(&GetOpaqueRootFromWrappable);
 {% endif %}
-  WrapperPrivate::AddPrivateData(context, proxy, wrappable{{
-      ", get_root" if get_opaque_root }});
+{% if add_opaque_roots %}
+  get_reachable_wrappables = base::Bind(&GetReachableWrappables);
+{% endif %}
+  WrapperPrivate::AddPrivateData(
+      context, proxy, wrappable, get_root, get_reachable_wrappables);
+{% else %}
+  WrapperPrivate::AddPrivateData(context, proxy, wrappable);
+{% endif %}
   return proxy;
 }
 
diff --git a/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
deleted file mode 100644
index 1f9a3fc..0000000
--- a/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
+++ /dev/null
@@ -1,45 +0,0 @@
-AnonymousIndexedGetterInterface.idl
-AnonymousNamedGetterInterface.idl
-AnonymousNamedIndexedGetterInterface.idl
-ArbitraryInterface.idl
-BaseInterface.idl
-BooleanTypeTestInterface.idl
-CallbackFunctionInterface.idl
-CallbackInterfaceInterface.idl
-ConditionalInterface.idl
-ConstantsInterface.idl
-ConstructorInterface.idl
-ConstructorWithArgumentsInterface.idl
-DerivedGetterSetterInterface.idl
-DerivedInterface.idl
-DisabledInterface.idl
-DOMStringTestInterface.idl
-EnumerationInterface.idl
-ExceptionObjectInterface.idl
-ExceptionsInterface.idl
-ExtendedIDLAttributesInterface.idl
-GarbageCollectionTestInterface.idl
-GetOpaqueRootInterface.idl
-GlobalInterfaceParent.idl
-IndexedGetterInterface.idl
-InterfaceWithUnsupportedProperties.idl
-NamedConstructorInterface.idl
-NamedGetterInterface.idl
-NamedIndexedGetterInterface.idl
-NestedPutForwardsInterface.idl
-NoConstructorInterface.idl
-NoInterfaceObjectInterface.idl
-NullableTypesTestInterface.idl
-NumericTypesTestInterface.idl
-ObjectTypeBindingsInterface.idl
-OperationsTestInterface.idl
-PutForwardsInterface.idl
-SingleOperationInterface.idl
-StringifierAnonymousOperationInterface.idl
-StringifierAttributeInterface.idl
-StringifierOperationInterface.idl
-StaticPropertiesInterface.idl
-TargetInterface.idl
-UnionTypesInterface.idl
-Window.idl
-UnsupportedInterface.idl
diff --git a/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
deleted file mode 100644
index c1c7594..0000000
--- a/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
+++ /dev/null
@@ -1,44 +0,0 @@
-AnonymousIndexedGetterInterface.idl
-AnonymousNamedGetterInterface.idl
-AnonymousNamedIndexedGetterInterface.idl
-ArbitraryInterface.idl
-BaseInterface.idl
-BooleanTypeTestInterface.idl
-CallbackFunctionInterface.idl
-CallbackInterfaceInterface.idl
-ConditionalInterface.idl
-ConstantsInterface.idl
-ConstructorInterface.idl
-ConstructorWithArgumentsInterface.idl
-DerivedGetterSetterInterface.idl
-DerivedInterface.idl
-DisabledInterface.idl
-DOMStringTestInterface.idl
-EnumerationInterface.idl
-ExceptionObjectInterface.idl
-ExceptionsInterface.idl
-ExtendedIDLAttributesInterface.idl
-GarbageCollectionTestInterface.idl
-GetOpaqueRootInterface.idl
-GlobalInterfaceParent.idl
-IndexedGetterInterface.idl
-InterfaceWithUnsupportedProperties.idl
-NamedConstructorInterface.idl
-NamedGetterInterface.idl
-NamedIndexedGetterInterface.idl
-NestedPutForwardsInterface.idl
-NoConstructorInterface.idl
-NoInterfaceObjectInterface.idl
-NullableTypesTestInterface.idl
-NumericTypesTestInterface.idl
-ObjectTypeBindingsInterface.idl
-OperationsTestInterface.idl
-PutForwardsInterface.idl
-SingleOperationInterface.idl
-StringifierAnonymousOperationInterface.idl
-StringifierAttributeInterface.idl
-StringifierOperationInterface.idl
-StaticPropertiesInterface.idl
-TargetInterface.idl
-UnionTypesInterface.idl
-Window.idl
diff --git a/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
deleted file mode 100644
index ff5e547..0000000
--- a/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
+++ /dev/null
@@ -1,48 +0,0 @@
-AnonymousIndexedGetterInterface.idl
-AnonymousNamedGetterInterface.idl
-AnonymousNamedIndexedGetterInterface.idl
-ArbitraryInterface.idl
-BaseInterface.idl
-BooleanTypeTestInterface.idl
-CallbackFunctionInterface.idl
-CallbackInterfaceInterface.idl
-ConditionalInterface.idl
-ConstantsInterface.idl
-ConstructorInterface.idl
-ConstructorWithArgumentsInterface.idl
-DerivedGetterSetterInterface.idl
-DerivedInterface.idl
-DisabledInterface.idl
-DOMStringTestInterface.idl
-EnumerationInterface.idl
-ExceptionObjectInterface.idl
-ExceptionsInterface.idl
-ExtendedIDLAttributesInterface.idl
-GarbageCollectionTestInterface.idl
-GetOpaqueRootInterface.idl
-GlobalInterfaceParent.idl
-IndexedGetterInterface.idl
-InterfaceWithUnsupportedProperties.idl
-NamedConstructorInterface.idl
-NamedGetterInterface.idl
-NamedIndexedGetterInterface.idl
-NestedPutForwardsInterface.idl
-NoConstructorInterface.idl
-NoInterfaceObjectInterface.idl
-NullableTypesTestInterface.idl
-NumericTypesTestInterface.idl
-ObjectTypeBindingsInterface.idl
-OperationsTestInterface.idl
-PutForwardsInterface.idl
-SingleOperationInterface.idl
-StringifierAnonymousOperationInterface.idl
-StringifierAttributeInterface.idl
-StringifierOperationInterface.idl
-StaticPropertiesInterface.idl
-TargetInterface.idl
-UnionTypesInterface.idl
-Window.idl
-ImplementedInterface.idl
-PartialInterface.idl
-InterfaceWithUnsupportedProperties_partial.idl
-UnsupportedInterface.idl
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 0006b33..ac19432 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -557,16 +557,24 @@
 }
 
 Application::CValStats::CValStats()
-    : free_memory("Memory.CPU.Free", 0,
-                  "Total free application memory remaining."),
-      used_memory("Memory.CPU.Used", 0,
-                  "Total memory allocated via the app's allocators."),
+    : free_cpu_memory("Memory.CPU.Free", 0,
+                      "Total free application CPU memory remaining."),
+      used_cpu_memory("Memory.CPU.Used", 0,
+                      "Total CPU memory allocated via the app's allocators."),
 #if !defined(__LB_SHELL__FOR_RELEASE__)
       exe_memory("Memory.CPU.Exe", 0,
                  "Total memory occupied by the size of the executable."),
 #endif
-      app_lifetime_in_ms("Cobalt.Lifetime", 0,
-                         "Application lifetime in milliseconds.") {
+      app_lifetime("Cobalt.Lifetime", base::TimeDelta(),
+                   "Application lifetime.") {
+#if defined(OS_STARBOARD)
+  if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
+    free_gpu_memory.emplace("Memory.GPU.Free", 0,
+                            "Total free application GPU memory remaining.");
+    used_gpu_memory.emplace("Memory.GPU.Used", 0,
+                            "Total GPU memory allocated by the application.");
+  }
+#endif  // defined(OS_STARBOARD)
 }
 
 void Application::RegisterUserLogs() {
@@ -624,8 +632,9 @@
     lb_memory_get_info(&memory_info);
 
     available_memory_ = memory_info.free_memory;
-    c_val_stats_.free_memory = static_cast<size_t>(memory_info.free_memory);
-    c_val_stats_.used_memory =
+    c_val_stats_.free_cpu_memory =
+        static_cast<size_t>(memory_info.free_memory);
+    c_val_stats_.used_cpu_memory =
         static_cast<size_t>(memory_info.application_memory);
     c_val_stats_.exe_memory = static_cast<size_t>(memory_info.executable_size);
   }
@@ -634,19 +643,25 @@
   // unallocated memory as the available memory.
   if (!memory_stats_updated) {
     available_memory_ = lb_get_unallocated_memory();
-    c_val_stats_.free_memory = static_cast<size_t>(available_memory_);
-    c_val_stats_.used_memory =
+    c_val_stats_.free_cpu_memory = static_cast<size_t>(available_memory_);
+    c_val_stats_.used_cpu_memory =
         lb_get_total_system_memory() - lb_get_unallocated_memory();
   }
 #elif defined(OS_STARBOARD)
-  int64_t used_memory = SbSystemGetUsedCPUMemory();
-  available_memory_ = SbSystemGetTotalCPUMemory() - used_memory;
-  c_val_stats_.free_memory = available_memory_;
-  c_val_stats_.used_memory = used_memory;
+  int64_t used_cpu_memory = SbSystemGetUsedCPUMemory();
+  available_memory_ = SbSystemGetTotalCPUMemory() - used_cpu_memory;
+  c_val_stats_.free_cpu_memory = available_memory_;
+  c_val_stats_.used_cpu_memory = used_cpu_memory;
+
+  if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
+    int64_t used_gpu_memory = SbSystemGetUsedGPUMemory();
+    *c_val_stats_.free_gpu_memory =
+        SbSystemGetTotalGPUMemory() - used_gpu_memory;
+    *c_val_stats_.used_gpu_memory = used_gpu_memory;
+  }
 #endif
 
-  lifetime_in_ms_ = (base::TimeTicks::Now() - start_time_).InMilliseconds();
-  c_val_stats_.app_lifetime_in_ms = lifetime_in_ms_;
+  c_val_stats_.app_lifetime = base::TimeTicks::Now() - start_time_;
 }
 
 }  // namespace browser
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 870e87f..3b5189a 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -127,13 +127,22 @@
   struct CValStats {
     CValStats();
 
-    base::CVal<size_t, base::CValPublic> free_memory;
-    base::CVal<size_t, base::CValPublic> used_memory;
+    base::CVal<base::cval::SizeInBytes, base::CValPublic> free_cpu_memory;
+    base::CVal<base::cval::SizeInBytes, base::CValPublic> used_cpu_memory;
+
+    // GPU memory stats are not always available, so we put them behind
+    // base::optional so that we can enable them at runtime depending on system
+    // capabilities.
+    base::optional<base::CVal<base::cval::SizeInBytes, base::CValPublic> >
+        free_gpu_memory;
+    base::optional<base::CVal<base::cval::SizeInBytes, base::CValPublic> >
+        used_gpu_memory;
+
 #if !defined(__LB_SHELL__FOR_RELEASE__)
-    base::CVal<size_t, base::CValPublic> exe_memory;
+    base::CVal<base::cval::SizeInBytes, base::CValPublic> exe_memory;
 #endif
 
-    base::CVal<int64_t, base::CValPublic> app_lifetime_in_ms;
+    base::CVal<base::TimeDelta, base::CValPublic> app_lifetime;
   };
 
   void RegisterUserLogs();
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 36effe7..88b34e9 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -49,6 +49,7 @@
       'defines': [
         'COBALT_IMAGE_CACHE_SIZE_IN_BYTES=<(image_cache_size_in_bytes)',
         'COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES=<(remote_typeface_cache_size_in_bytes)',
+        'COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO=<(image_cache_capacity_multiplier_when_playing_video)',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/account/account.gyp:account',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 55e33b9..cd3c605 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -249,6 +249,8 @@
       array_buffer_allocator_.get();
   options.dom_settings_options.array_buffer_cache = array_buffer_cache_.get();
 #endif  // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
+  options.image_cache_capacity_multiplier_when_playing_video =
+      COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO;
   web_module_.reset(new WebModule(
       url,
       base::Bind(&BrowserModule::OnRenderTreeProduced, base::Unretained(this)),
diff --git a/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp b/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp
deleted file mode 100644
index f212dcf..0000000
--- a/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp
+++ /dev/null
@@ -1,143 +0,0 @@
-../audio/AudioBuffer.idl
-../audio/AudioBufferSourceNode.idl
-../audio/AudioContext.idl
-../audio/AudioDestinationNode.idl
-../audio/AudioNode.idl
-../cssom/CSSConditionRule.idl
-../cssom/CSSGroupingRule.idl
-../cssom/CSSFontFaceRule.idl
-../cssom/CSSMediaRule.idl
-../cssom/CSSKeyframeRule.idl
-../cssom/CSSKeyframesRule.idl
-../cssom/CSSRule.idl
-../cssom/CSSRuleList.idl
-../cssom/CSSStyleDeclaration.idl
-../cssom/CSSStyleRule.idl
-../cssom/CSSStyleSheet.idl
-../cssom/MediaList.idl
-../cssom/StyleSheet.idl
-../cssom/StyleSheetList.idl
-../debug/DebugHub.idl
-../debug/Debugger.idl
-../debug/DebuggerEventTarget.idl
-../debug/DebugScriptRunner.idl
-../dom/AnimationEvent.idl
-../dom/ArrayBuffer.idl
-../dom/ArrayBufferView.idl
-../dom/Attr.idl
-../dom/Blob.idl
-../dom/CDATASection.idl
-../dom/CharacterData.idl
-../dom/Comment.idl
-../dom/Console.idl
-../dom/Crypto.idl
-../dom/DataView.idl
-../dom/Document.idl
-../dom/DocumentTimeline.idl
-../dom/DocumentType.idl
-../dom/DOMException.idl
-../dom/DOMImplementation.idl
-../dom/DOMParser.idl
-../dom/DOMRect.idl
-../dom/DOMRectList.idl
-../dom/DOMRectReadOnly.idl
-../dom/DOMStringMap.idl
-../dom/DOMTokenList.idl
-../dom/Element.idl
-../dom/Event.idl
-../dom/EventListener.idl
-../dom/EventTarget.idl
-../dom/Float32Array.idl
-../dom/Float64Array.idl
-../dom/FocusEvent.idl
-../dom/History.idl
-../dom/HTMLAnchorElement.idl
-../dom/HTMLBodyElement.idl
-../dom/HTMLBRElement.idl
-../dom/HTMLCollection.idl
-../dom/HTMLDivElement.idl
-../dom/HTMLElement.idl
-../dom/HTMLHeadElement.idl
-../dom/HTMLHeadingElement.idl
-../dom/HTMLHtmlElement.idl
-../dom/HTMLImageElement.idl
-../dom/HTMLLinkElement.idl
-../dom/HTMLMediaElement.idl
-../dom/HTMLMetaElement.idl
-../dom/HTMLParagraphElement.idl
-../dom/HTMLScriptElement.idl
-../dom/HTMLSpanElement.idl
-../dom/HTMLStyleElement.idl
-../dom/HTMLTitleElement.idl
-../dom/HTMLUnknownElement.idl
-../dom/HTMLVideoElement.idl
-../dom/KeyboardEvent.idl
-../dom/Location.idl
-../dom/MediaError.idl
-../dom/MediaKeyCompleteEvent.idl
-../dom/MediaKeyError.idl
-../dom/MediaKeyErrorEvent.idl
-../dom/MediaKeyMessageEvent.idl
-../dom/MediaKeyNeededEvent.idl
-../dom/MediaQueryList.idl
-../dom/MediaSource.idl
-../dom/MimeTypeArray.idl
-../dom/NamedNodeMap.idl
-../dom/Navigator.idl
-../dom/Node.idl
-../dom/NodeList.idl
-../dom/Performance.idl
-../dom/PerformanceTiming.idl
-../dom/PluginArray.idl
-../dom/ProgressEvent.idl
-../dom/Screen.idl
-../dom/SecurityPolicyViolationEvent.idl
-../dom/SourceBuffer.idl
-../dom/SourceBufferList.idl
-../dom/Storage.idl
-../dom/StorageEvent.idl
-../dom/TestRunner.idl
-../dom/Text.idl
-../dom/TimeRanges.idl
-../dom/TransitionEvent.idl
-../dom/UIEvent.idl
-../dom/Uint16Array.idl
-../dom/Uint32Array.idl
-../dom/Uint8Array.idl
-../dom/URL.idl
-../dom/VideoPlaybackQuality.idl
-../dom/Window.idl
-../dom/XMLDocument.idl
-../dom/XMLSerializer.idl
-../h5vcc/dial/DialHttpRequest.idl
-../h5vcc/dial/DialHttpResponse.idl
-../h5vcc/dial/DialServer.idl
-../h5vcc/H5vcc.idl
-../h5vcc/H5vccAccountInfo.idl
-../h5vcc/H5vccAccountManager.idl
-../h5vcc/H5vccAudioConfig.idl
-../h5vcc/H5vccAudioConfigArray.idl
-../h5vcc/H5vccCVal.idl
-../h5vcc/H5vccCValKeyList.idl
-../h5vcc/H5vccRuntime.idl
-../h5vcc/H5vccRuntimeEventTarget.idl
-../h5vcc/H5vccSettings.idl
-../h5vcc/H5vccStorage.idl
-../h5vcc/H5vccSystem.idl
-../speech/SpeechRecognition.idl
-../speech/SpeechRecognitionAlternative.idl
-../speech/SpeechRecognitionError.idl
-../speech/SpeechRecognitionEvent.idl
-../speech/SpeechRecognitionResult.idl
-../speech/SpeechRecognitionResultList.idl
-../web_animations/Animatable.idl
-../web_animations/Animation.idl
-../web_animations/AnimationEffectReadOnly.idl
-../web_animations/AnimationEffectTimingReadOnly.idl
-../web_animations/AnimationTimeline.idl
-../web_animations/Keyframe.idl
-../web_animations/KeyframeEffectReadOnly.idl
-../webdriver/ScriptExecutor.idl
-../xhr/XMLHttpRequest.idl
-../xhr/XMLHttpRequestEventTarget.idl
-../xhr/XMLHttpRequestUpload.idl
diff --git a/src/cobalt/browser/global_objects_idl_files_list.tmp b/src/cobalt/browser/global_objects_idl_files_list.tmp
deleted file mode 100644
index f212dcf..0000000
--- a/src/cobalt/browser/global_objects_idl_files_list.tmp
+++ /dev/null
@@ -1,143 +0,0 @@
-../audio/AudioBuffer.idl
-../audio/AudioBufferSourceNode.idl
-../audio/AudioContext.idl
-../audio/AudioDestinationNode.idl
-../audio/AudioNode.idl
-../cssom/CSSConditionRule.idl
-../cssom/CSSGroupingRule.idl
-../cssom/CSSFontFaceRule.idl
-../cssom/CSSMediaRule.idl
-../cssom/CSSKeyframeRule.idl
-../cssom/CSSKeyframesRule.idl
-../cssom/CSSRule.idl
-../cssom/CSSRuleList.idl
-../cssom/CSSStyleDeclaration.idl
-../cssom/CSSStyleRule.idl
-../cssom/CSSStyleSheet.idl
-../cssom/MediaList.idl
-../cssom/StyleSheet.idl
-../cssom/StyleSheetList.idl
-../debug/DebugHub.idl
-../debug/Debugger.idl
-../debug/DebuggerEventTarget.idl
-../debug/DebugScriptRunner.idl
-../dom/AnimationEvent.idl
-../dom/ArrayBuffer.idl
-../dom/ArrayBufferView.idl
-../dom/Attr.idl
-../dom/Blob.idl
-../dom/CDATASection.idl
-../dom/CharacterData.idl
-../dom/Comment.idl
-../dom/Console.idl
-../dom/Crypto.idl
-../dom/DataView.idl
-../dom/Document.idl
-../dom/DocumentTimeline.idl
-../dom/DocumentType.idl
-../dom/DOMException.idl
-../dom/DOMImplementation.idl
-../dom/DOMParser.idl
-../dom/DOMRect.idl
-../dom/DOMRectList.idl
-../dom/DOMRectReadOnly.idl
-../dom/DOMStringMap.idl
-../dom/DOMTokenList.idl
-../dom/Element.idl
-../dom/Event.idl
-../dom/EventListener.idl
-../dom/EventTarget.idl
-../dom/Float32Array.idl
-../dom/Float64Array.idl
-../dom/FocusEvent.idl
-../dom/History.idl
-../dom/HTMLAnchorElement.idl
-../dom/HTMLBodyElement.idl
-../dom/HTMLBRElement.idl
-../dom/HTMLCollection.idl
-../dom/HTMLDivElement.idl
-../dom/HTMLElement.idl
-../dom/HTMLHeadElement.idl
-../dom/HTMLHeadingElement.idl
-../dom/HTMLHtmlElement.idl
-../dom/HTMLImageElement.idl
-../dom/HTMLLinkElement.idl
-../dom/HTMLMediaElement.idl
-../dom/HTMLMetaElement.idl
-../dom/HTMLParagraphElement.idl
-../dom/HTMLScriptElement.idl
-../dom/HTMLSpanElement.idl
-../dom/HTMLStyleElement.idl
-../dom/HTMLTitleElement.idl
-../dom/HTMLUnknownElement.idl
-../dom/HTMLVideoElement.idl
-../dom/KeyboardEvent.idl
-../dom/Location.idl
-../dom/MediaError.idl
-../dom/MediaKeyCompleteEvent.idl
-../dom/MediaKeyError.idl
-../dom/MediaKeyErrorEvent.idl
-../dom/MediaKeyMessageEvent.idl
-../dom/MediaKeyNeededEvent.idl
-../dom/MediaQueryList.idl
-../dom/MediaSource.idl
-../dom/MimeTypeArray.idl
-../dom/NamedNodeMap.idl
-../dom/Navigator.idl
-../dom/Node.idl
-../dom/NodeList.idl
-../dom/Performance.idl
-../dom/PerformanceTiming.idl
-../dom/PluginArray.idl
-../dom/ProgressEvent.idl
-../dom/Screen.idl
-../dom/SecurityPolicyViolationEvent.idl
-../dom/SourceBuffer.idl
-../dom/SourceBufferList.idl
-../dom/Storage.idl
-../dom/StorageEvent.idl
-../dom/TestRunner.idl
-../dom/Text.idl
-../dom/TimeRanges.idl
-../dom/TransitionEvent.idl
-../dom/UIEvent.idl
-../dom/Uint16Array.idl
-../dom/Uint32Array.idl
-../dom/Uint8Array.idl
-../dom/URL.idl
-../dom/VideoPlaybackQuality.idl
-../dom/Window.idl
-../dom/XMLDocument.idl
-../dom/XMLSerializer.idl
-../h5vcc/dial/DialHttpRequest.idl
-../h5vcc/dial/DialHttpResponse.idl
-../h5vcc/dial/DialServer.idl
-../h5vcc/H5vcc.idl
-../h5vcc/H5vccAccountInfo.idl
-../h5vcc/H5vccAccountManager.idl
-../h5vcc/H5vccAudioConfig.idl
-../h5vcc/H5vccAudioConfigArray.idl
-../h5vcc/H5vccCVal.idl
-../h5vcc/H5vccCValKeyList.idl
-../h5vcc/H5vccRuntime.idl
-../h5vcc/H5vccRuntimeEventTarget.idl
-../h5vcc/H5vccSettings.idl
-../h5vcc/H5vccStorage.idl
-../h5vcc/H5vccSystem.idl
-../speech/SpeechRecognition.idl
-../speech/SpeechRecognitionAlternative.idl
-../speech/SpeechRecognitionError.idl
-../speech/SpeechRecognitionEvent.idl
-../speech/SpeechRecognitionResult.idl
-../speech/SpeechRecognitionResultList.idl
-../web_animations/Animatable.idl
-../web_animations/Animation.idl
-../web_animations/AnimationEffectReadOnly.idl
-../web_animations/AnimationEffectTimingReadOnly.idl
-../web_animations/AnimationTimeline.idl
-../web_animations/Keyframe.idl
-../web_animations/KeyframeEffectReadOnly.idl
-../webdriver/ScriptExecutor.idl
-../xhr/XMLHttpRequest.idl
-../xhr/XMLHttpRequestEventTarget.idl
-../xhr/XMLHttpRequestUpload.idl
diff --git a/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp
deleted file mode 100644
index e4de45c..0000000
--- a/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp
+++ /dev/null
@@ -1,170 +0,0 @@
-../audio/AudioBuffer.idl
-../audio/AudioBufferSourceNode.idl
-../audio/AudioContext.idl
-../audio/AudioDestinationNode.idl
-../audio/AudioNode.idl
-../cssom/CSSConditionRule.idl
-../cssom/CSSGroupingRule.idl
-../cssom/CSSFontFaceRule.idl
-../cssom/CSSMediaRule.idl
-../cssom/CSSKeyframeRule.idl
-../cssom/CSSKeyframesRule.idl
-../cssom/CSSRule.idl
-../cssom/CSSRuleList.idl
-../cssom/CSSStyleDeclaration.idl
-../cssom/CSSStyleRule.idl
-../cssom/CSSStyleSheet.idl
-../cssom/MediaList.idl
-../cssom/StyleSheet.idl
-../cssom/StyleSheetList.idl
-../debug/DebugHub.idl
-../debug/Debugger.idl
-../debug/DebuggerEventTarget.idl
-../debug/DebugScriptRunner.idl
-../dom/AnimationEvent.idl
-../dom/ArrayBuffer.idl
-../dom/ArrayBufferView.idl
-../dom/Attr.idl
-../dom/Blob.idl
-../dom/CDATASection.idl
-../dom/CharacterData.idl
-../dom/Comment.idl
-../dom/Console.idl
-../dom/Crypto.idl
-../dom/DataView.idl
-../dom/Document.idl
-../dom/DocumentTimeline.idl
-../dom/DocumentType.idl
-../dom/DOMException.idl
-../dom/DOMImplementation.idl
-../dom/DOMParser.idl
-../dom/DOMRect.idl
-../dom/DOMRectList.idl
-../dom/DOMRectReadOnly.idl
-../dom/DOMStringMap.idl
-../dom/DOMTokenList.idl
-../dom/Element.idl
-../dom/Event.idl
-../dom/EventListener.idl
-../dom/EventTarget.idl
-../dom/Float32Array.idl
-../dom/Float64Array.idl
-../dom/FocusEvent.idl
-../dom/History.idl
-../dom/HTMLAnchorElement.idl
-../dom/HTMLBodyElement.idl
-../dom/HTMLBRElement.idl
-../dom/HTMLCollection.idl
-../dom/HTMLDivElement.idl
-../dom/HTMLElement.idl
-../dom/HTMLHeadElement.idl
-../dom/HTMLHeadingElement.idl
-../dom/HTMLHtmlElement.idl
-../dom/HTMLImageElement.idl
-../dom/HTMLLinkElement.idl
-../dom/HTMLMediaElement.idl
-../dom/HTMLMetaElement.idl
-../dom/HTMLParagraphElement.idl
-../dom/HTMLScriptElement.idl
-../dom/HTMLSpanElement.idl
-../dom/HTMLStyleElement.idl
-../dom/HTMLTitleElement.idl
-../dom/HTMLUnknownElement.idl
-../dom/HTMLVideoElement.idl
-../dom/KeyboardEvent.idl
-../dom/Location.idl
-../dom/MediaError.idl
-../dom/MediaKeyCompleteEvent.idl
-../dom/MediaKeyError.idl
-../dom/MediaKeyErrorEvent.idl
-../dom/MediaKeyMessageEvent.idl
-../dom/MediaKeyNeededEvent.idl
-../dom/MediaQueryList.idl
-../dom/MediaSource.idl
-../dom/MimeTypeArray.idl
-../dom/NamedNodeMap.idl
-../dom/Navigator.idl
-../dom/Node.idl
-../dom/NodeList.idl
-../dom/Performance.idl
-../dom/PerformanceTiming.idl
-../dom/PluginArray.idl
-../dom/ProgressEvent.idl
-../dom/Screen.idl
-../dom/SecurityPolicyViolationEvent.idl
-../dom/SourceBuffer.idl
-../dom/SourceBufferList.idl
-../dom/Storage.idl
-../dom/StorageEvent.idl
-../dom/TestRunner.idl
-../dom/Text.idl
-../dom/TimeRanges.idl
-../dom/TransitionEvent.idl
-../dom/UIEvent.idl
-../dom/Uint16Array.idl
-../dom/Uint32Array.idl
-../dom/Uint8Array.idl
-../dom/URL.idl
-../dom/VideoPlaybackQuality.idl
-../dom/Window.idl
-../dom/XMLDocument.idl
-../dom/XMLSerializer.idl
-../h5vcc/dial/DialHttpRequest.idl
-../h5vcc/dial/DialHttpResponse.idl
-../h5vcc/dial/DialServer.idl
-../h5vcc/H5vcc.idl
-../h5vcc/H5vccAccountInfo.idl
-../h5vcc/H5vccAccountManager.idl
-../h5vcc/H5vccAudioConfig.idl
-../h5vcc/H5vccAudioConfigArray.idl
-../h5vcc/H5vccCVal.idl
-../h5vcc/H5vccCValKeyList.idl
-../h5vcc/H5vccRuntime.idl
-../h5vcc/H5vccRuntimeEventTarget.idl
-../h5vcc/H5vccSettings.idl
-../h5vcc/H5vccStorage.idl
-../h5vcc/H5vccSystem.idl
-../speech/SpeechRecognition.idl
-../speech/SpeechRecognitionAlternative.idl
-../speech/SpeechRecognitionError.idl
-../speech/SpeechRecognitionEvent.idl
-../speech/SpeechRecognitionResult.idl
-../speech/SpeechRecognitionResultList.idl
-../web_animations/Animatable.idl
-../web_animations/Animation.idl
-../web_animations/AnimationEffectReadOnly.idl
-../web_animations/AnimationEffectTimingReadOnly.idl
-../web_animations/AnimationTimeline.idl
-../web_animations/Keyframe.idl
-../web_animations/KeyframeEffectReadOnly.idl
-../webdriver/ScriptExecutor.idl
-../xhr/XMLHttpRequest.idl
-../xhr/XMLHttpRequestEventTarget.idl
-../xhr/XMLHttpRequestUpload.idl
-../cssom/LinkStyle.idl
-../dom/Document_CSSOM.idl
-../dom/Document_HTML5.idl
-../dom/Document_WebAnimationsAPI.idl
-../dom/Element_CSSOMView.idl
-../dom/Element_DOMParsingAndSerialization.idl
-../dom/ElementCSSInlineStyle.idl
-../dom/GlobalCrypto.idl
-../dom/GlobalEventHandlers.idl
-../dom/HTMLElement_CSSOMView.idl
-../dom/NavigatorID.idl
-../dom/NavigatorLanguage.idl
-../dom/NavigatorPlugins.idl
-../dom/NavigatorStorageUtils.idl
-../dom/NonDocumentTypeChildNode.idl
-../dom/NonElementParentNode.idl
-../dom/ParentNode.idl
-../dom/Performance_HighResolutionTime.idl
-../dom/URLUtils.idl
-../dom/Window_AnimationTiming.idl
-../dom/Window_CSSOM.idl
-../dom/Window_CSSOMView.idl
-../dom/Window_Performance.idl
-../dom/WindowEventHandlers.idl
-../dom/WindowLocalStorage.idl
-../dom/WindowSessionStorage.idl
-../dom/WindowTimers.idl
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 372db0d..b80a9bd 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -154,6 +154,12 @@
   // ImageCache that is used to manage image cache logic.
   scoped_ptr<loader::image::ImageCache> image_cache_;
 
+  // The reduced cache capacity manager can be used to force a reduced image
+  // cache over periods of time where memory is known to be restricted, such
+  // as when a video is playing.
+  scoped_ptr<loader::image::ReducedCacheCapacityManager>
+      reduced_image_cache_capacity_manager_;
+
   // RemoteTypefaceCache that is used to manage loading and caching typefaces
   // from URLs.
   scoped_ptr<loader::font::RemoteTypefaceCache> remote_typeface_cache_;
@@ -263,6 +269,11 @@
       data.resource_provider, fetcher_factory_.get());
   DCHECK(image_cache_);
 
+  reduced_image_cache_capacity_manager_.reset(
+      new loader::image::ReducedCacheCapacityManager(
+          image_cache_.get(),
+          data.options.image_cache_capacity_multiplier_when_playing_video));
+
   DCHECK_LE(0, data.options.remote_typeface_cache_capacity);
   remote_typeface_cache_ = loader::font::CreateRemoteTypefaceCache(
       base::StringPrintf("Memory.%s.RemoteTypefaceCache", name_.c_str()),
@@ -297,7 +308,8 @@
   window_ = new dom::Window(
       data.window_dimensions.width(), data.window_dimensions.height(),
       css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
-      data.resource_provider, image_cache_.get(), remote_typeface_cache_.get(),
+      data.resource_provider, image_cache_.get(),
+      reduced_image_cache_capacity_manager_.get(), remote_typeface_cache_.get(),
       local_storage_database_.get(), data.media_module, data.media_module,
       execution_state_.get(), script_runner_.get(),
       media_source_registry_.get(),
@@ -538,7 +550,8 @@
           COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES),
       csp_enforcement_mode(dom::kCspEnforcementEnable),
       csp_insecure_allowed_token(0),
-      track_event_stats(false) {}
+      track_event_stats(false),
+      image_cache_capacity_multiplier_when_playing_video(1.0f) {}
 
 WebModule::WebModule(
     const GURL& initial_url,
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 854579a..a4742cb 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -134,6 +134,12 @@
 
     // Whether or not the web module's stat tracker should track event stats.
     bool track_event_stats;
+
+    // If set to something other than 1.0f, when a video starts to play, the
+    // image cache will be flushed and temporarily multiplied by this value (
+    // must be less than or equal to 1.0f) until the video ends.  This can
+    // help for platforms that are low on image memory while playing a video.
+    float image_cache_capacity_multiplier_when_playing_video;
   };
 
   typedef layout::LayoutManager::LayoutResults LayoutResults;
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc
index 313711a..10bdf94 100644
--- a/src/cobalt/browser/web_module_stat_tracker.cc
+++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -43,7 +43,7 @@
     stop_watches_.push_back(
         base::StopWatch(i, base::StopWatch::kAutoStartOff, this));
   }
-  stop_watch_durations_.resize(kNumStopWatchTypes, 0);
+  stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta());
 }
 
 WebModuleStatTracker::~WebModuleStatTracker() { EndCurrentEvent(false); }
@@ -104,38 +104,46 @@
       count_layout_boxes_destroyed(
           StringPrintf("Event.Count.%s.Layout.Box.Destroyed", name.c_str()), 0,
           "Number of boxes destroyed."),
-      duration_total(StringPrintf("Event.Duration.%s", name.c_str()), 0,
+      duration_total(StringPrintf("Event.Duration.%s", name.c_str()),
+                     base::TimeDelta(),
                      "Total duration of the event (in microseconds). This is "
                      "the time elapsed from the event injection until the "
                      "render tree is produced."),
       duration_dom_inject_event(
-          StringPrintf("Event.Duration.%s.DOM.InjectEvent", name.c_str()), 0,
+          StringPrintf("Event.Duration.%s.DOM.InjectEvent", name.c_str()),
+          base::TimeDelta(),
           "Injection duration, which includes JS, for event (in "
           "microseconds). This does not include subsequent DOM and Layout "
           "processing."),
       duration_dom_update_computed_style(
           StringPrintf("Event.Duration.%s.DOM.UpdateComputedStyle",
                        name.c_str()),
-          0, "UpdateComputedStyle duration for event (in microseconds)."),
+          base::TimeDelta(),
+          "UpdateComputedStyle duration for event (in microseconds)."),
       duration_layout_box_tree(
-          StringPrintf("Event.Duration.%s.Layout.BoxTree", name.c_str()), 0,
+          StringPrintf("Event.Duration.%s.Layout.BoxTree", name.c_str()),
+          base::TimeDelta(),
           "Layout box tree duration for event (in microseconds)."),
       duration_layout_box_generation(
           StringPrintf("Event.Duration.%s.Layout.BoxTree.BoxGeneration",
                        name.c_str()),
-          0, "BoxGeneration duration for event (in microseconds)."),
+          base::TimeDelta(),
+          "BoxGeneration duration for event (in microseconds)."),
       duration_layout_update_used_sizes(
           StringPrintf("Event.Duration.%s.Layout.BoxTree.UpdateUsedSizes",
                        name.c_str()),
-          0, "UpdateUsedSizes duration for event (in microseconds)."),
+          base::TimeDelta(),
+          "UpdateUsedSizes duration for event (in microseconds)."),
       duration_layout_render_and_animate(
           StringPrintf("Event.Duration.%s.Layout.RenderAndAnimate",
                        name.c_str()),
-          0, "RenderAndAnimate duration for event (in microseconds).") {}
+          base::TimeDelta(),
+          "RenderAndAnimate duration for event (in microseconds).") {}
 
 bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; }
 
-void WebModuleStatTracker::OnStopWatchStopped(int id, int64 time_elapsed) {
+void WebModuleStatTracker::OnStopWatchStopped(int id,
+                                              base::TimeDelta time_elapsed) {
   stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed;
 }
 
@@ -144,7 +152,7 @@
     return;
   }
 
-  stop_watch_durations_[kStopWatchTypeEvent] = 0;
+  stop_watch_durations_[kStopWatchTypeEvent] = base::TimeDelta();
   stop_watches_[kStopWatchTypeEvent].Stop();
   dom_stat_tracker_->DisableStopWatches();
   layout_stat_tracker_->DisableStopWatches();
@@ -166,8 +174,9 @@
       layout_stat_tracker_->boxes_destroyed_count();
 
   // Update event durations
-  int64 event_injection_duration = dom_stat_tracker_->GetStopWatchTypeDuration(
-      dom::DomStatTracker::kStopWatchTypeInjectEvent);
+  base::TimeDelta event_injection_duration =
+      dom_stat_tracker_->GetStopWatchTypeDuration(
+          dom::DomStatTracker::kStopWatchTypeInjectEvent);
   // If a render tree was produced, then the total duration is the duration from
   // when the event started until now. Otherwise, the injection duration is
   // used. This is because some events do not trigger a new layout. In these
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h
index b10554c..8e9dbd3 100644
--- a/src/cobalt/browser/web_module_stat_tracker.h
+++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -79,18 +79,22 @@
     base::CVal<int, base::CValPublic> count_layout_boxes_destroyed;
 
     // Duration-related
-    base::CVal<int64, base::CValPublic> duration_total;
-    base::CVal<int64, base::CValPublic> duration_dom_inject_event;
-    base::CVal<int64, base::CValPublic> duration_dom_update_computed_style;
-    base::CVal<int64, base::CValPublic> duration_layout_box_tree;
-    base::CVal<int64, base::CValPublic> duration_layout_box_generation;
-    base::CVal<int64, base::CValPublic> duration_layout_update_used_sizes;
-    base::CVal<int64, base::CValPublic> duration_layout_render_and_animate;
+    base::CVal<base::TimeDelta, base::CValPublic> duration_total;
+    base::CVal<base::TimeDelta, base::CValPublic> duration_dom_inject_event;
+    base::CVal<base::TimeDelta, base::CValPublic>
+        duration_dom_update_computed_style;
+    base::CVal<base::TimeDelta, base::CValPublic> duration_layout_box_tree;
+    base::CVal<base::TimeDelta, base::CValPublic>
+        duration_layout_box_generation;
+    base::CVal<base::TimeDelta, base::CValPublic>
+        duration_layout_update_used_sizes;
+    base::CVal<base::TimeDelta, base::CValPublic>
+        duration_layout_render_and_animate;
   };
 
   // From base::StopWatchOwner
   bool IsStopWatchEnabled(int id) const OVERRIDE;
-  void OnStopWatchStopped(int id, int64 time_elapsed) OVERRIDE;
+  void OnStopWatchStopped(int id, base::TimeDelta time_elapsed) OVERRIDE;
 
   // End the current event if one is active. This triggers an update of all
   // |EventStats| for the event.
@@ -110,7 +114,7 @@
 
   // Stop watch-related
   std::vector<base::StopWatch> stop_watches_;
-  std::vector<int64> stop_watch_durations_;
+  std::vector<base::TimeDelta> stop_watch_durations_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index f179e93..0e34392 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-10999
\ No newline at end of file
+11130
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index cfbf8bf..3e07aab 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -153,6 +153,15 @@
     # typefaces downloaded from a web page.
     'remote_typeface_cache_size_in_bytes%': 5 * 1024 * 1024,
 
+    # Modifying this value to be non-1.0f will result in the image cache
+    # capacity being cleared and then temporarily reduced for the duration that
+    # a video is playing.  This can be useful for some platforms if they are
+    # particularly constrained for (GPU) memory during video playback.  When
+    # playing a video, the image cache is reduced to:
+    # image_cache_size_in_bytes *
+    #     image_cache_capacity_multiplier_when_playing_video.
+    'image_cache_capacity_multiplier_when_playing_video%': '1.0f',
+
     # Compiler configuration.
 
     # The following variables are used to specify compiler and linker
diff --git a/src/cobalt/content/fonts/MinimalRoboto.ttf b/src/cobalt/content/fonts/MinimalRoboto.ttf
new file mode 100644
index 0000000..abfa5e2
--- /dev/null
+++ b/src/cobalt/content/fonts/MinimalRoboto.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/README.md b/src/cobalt/content/fonts/README.md
new file mode 100644
index 0000000..acee96c
--- /dev/null
+++ b/src/cobalt/content/fonts/README.md
@@ -0,0 +1,29 @@
+# Generating a minimal font for devices with small space requirements
+
+[GlyphIGo](https://github.com/pettarin/glyphIgo) was used to generate a subset
+of the Roboto font
+
+`cobalt/content/fonts` contains a script called `create_minimized_roboto.sh`
+that can help recreate minimized font if needed
+
+Steps:
+
+1.  `cd src/cobalt/content/fonts`
+1.  `./create_minimized_roboto.sh`
+1.  Download `fontforge` using apt.  `sudo apt install fontforge`
+1.  In `fontforge`, navigate the menu: `Encoding`->`Reencode`->`Glyph Order`.
+Scroll to the top, find the first glyph.  By spec, this glyph is called
+`.notdef`, and is used when this font is the default font and there glyph for a
+character we're looking for is missing in the file.  Often this will be blank
+after the last step, which can be undesired.
+1.  Copy `.notdef` glyph from a different font.
+    1.  Open a different font.
+    1.  Find the `.notdef` glyph.
+    1.  Select the glyph without opening it.
+    1.  Navigate the menu: `Edit`->`Copy` from the font you want to copy from.
+    1.  Switch focus to the minimized font.
+    1.  Select `.notdef` glyph.
+    1.  Navigate the menu: `Edit`->`Paste`.
+1.  Export the font using the menu: `File`->`Generate Fonts...`, make sure that
+the file name is correct.
+1.  Fix any errors if found, or if you can.
diff --git a/src/cobalt/content/fonts/create_minimized_roboto.sh b/src/cobalt/content/fonts/create_minimized_roboto.sh
new file mode 100755
index 0000000..02ef746
--- /dev/null
+++ b/src/cobalt/content/fonts/create_minimized_roboto.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script can be used as a convenience to launch an app under a Xephyr
+# X server.  It will first launch Xephyr, and then launch the executable given
+# on the command line such that it targets and will appear within the Xephyr
+# window.  It shuts down Xephyr after the main executable finishes.
+set -e
+
+SCRIPT_FILE="$(readlink -f "${BASH_SOURCE[0]}")"
+SCRIPT_DIR="$(dirname "${script_file}")"
+SCRIPT_NAME="$(basename "${script_file}")"
+
+GLPHYIGO_SRC=https://raw.githubusercontent.com/pettarin/glyphIgo/bb91c5f3746bcb17395acb6ffd436c982c41bd8a/src/glyphIgo.py
+
+function log() {
+  echo "${SCRIPT_NAME}: $@"
+}
+
+function deleteDirectory() {
+  if [[ -z "$1" ]]; then
+    log "deleteDirectory with no argument"
+    exit 1
+  fi
+
+  # Only delete target if it is an existing directory.
+  if [[ -d "$1" ]]; then
+    log "Deleting directory: $1"
+    rm -rf "$1"
+  fi
+}
+
+function deleteTempTrap() {
+  # If something interrupted the script, it might have printed a partial line.
+  echo
+  deleteDirectory "${TEMP_DIR}"
+}
+
+TEMP_DIR="$(mktemp -dt "${SCRIPT_NAME}.XXXXXXXXXX")"
+trap deleteTempTrap EXIT
+
+TEMP_GLYPHIGO="$TEMP_DIR/glyphIgo.py"
+
+# download the file
+curl -sSL $GLPHYIGO_SRC -o $TEMP_GLYPHIGO
+
+python $TEMP_GLYPHIGO subset --plain \
+  "$SCRIPT_DIR/minimized_roboto_subset_chars.txt" \
+  -f "$SCRIPT_DIR/Roboto-Regular.ttf" \
+  -o "$SCRIPT_DIR/minimized_roboto_needs_tofu.ttf"
+
+# Clear delete trap now that we have succeeded.
+trap - EXIT
+
+# Delete our temp directory on graceful exit.
+deleteDirectory "${TEMP_DIR}"
+
diff --git a/src/cobalt/content/fonts/fonts.xml b/src/cobalt/content/fonts/fonts.xml
index 9b1d31f..3e1ec53 100644
--- a/src/cobalt/content/fonts/fonts.xml
+++ b/src/cobalt/content/fonts/fonts.xml
@@ -27,6 +27,11 @@
     <alias name="roboto" to="sans-serif" />
     <alias name="tahoma" to="sans-serif" />
     <alias name="verdana" to="sans-serif" />
+    <!-- Ideally, this font should only be used if there are no other fonts in
+         our final image. -->
+    <family name="Minimal Roboto">
+        <font weight="400" style="normal">MinimalRoboto.ttf</font>
+    </family>
     <family name="serif">
         <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
         <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
@@ -281,4 +286,4 @@
     <family fallback="true" pages="0,24,32,36-37,48,254">
         <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
     </family>
-</familyset>
\ No newline at end of file
+</familyset>
diff --git a/src/cobalt/content/fonts/minimized_roboto_subset_chars.txt b/src/cobalt/content/fonts/minimized_roboto_subset_chars.txt
new file mode 100644
index 0000000..75498f5
--- /dev/null
+++ b/src/cobalt/content/fonts/minimized_roboto_subset_chars.txt
@@ -0,0 +1 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 (),.-/!
\ No newline at end of file
diff --git a/src/cobalt/cssom/complex_selector.cc b/src/cobalt/cssom/complex_selector.cc
index 5eb9047..b164c77 100644
--- a/src/cobalt/cssom/complex_selector.cc
+++ b/src/cobalt/cssom/complex_selector.cc
@@ -16,6 +16,7 @@
 
 #include "cobalt/cssom/complex_selector.h"
 
+#include "base/logging.h"
 #include "cobalt/cssom/combinator.h"
 #include "cobalt/cssom/compound_selector.h"
 #include "cobalt/cssom/selector_visitor.h"
@@ -23,6 +24,8 @@
 namespace cobalt {
 namespace cssom {
 
+const int ComplexSelector::kCombinatorLimit = 32;
+
 void ComplexSelector::Accept(SelectorVisitor* visitor) {
   visitor->VisitComplexSelector(this);
 }
@@ -39,6 +42,18 @@
     scoped_ptr<Combinator> combinator,
     scoped_ptr<CompoundSelector> compound_selector) {
   DCHECK(first_selector_);
+  DCHECK(last_selector_);
+
+  if (combinator_count_ >= kCombinatorLimit) {
+    if (!combinator_limit_exceeded_) {
+      LOG(WARNING)
+          << "Maximum number of calls to AppendCombinatorAndSelector exceeded."
+             "  Ignoring additional selectors.";
+      combinator_limit_exceeded_ = true;
+    }
+    return;
+  }
+
   specificity_.AddFrom(compound_selector->GetSpecificity());
 
   combinator->set_left_selector(last_selector_);
@@ -48,6 +63,8 @@
   last_selector_->set_right_combinator(combinator.Pass());
 
   last_selector_ = last_selector_->right_combinator()->right_selector();
+
+  combinator_count_++;
 }
 
 }  // namespace cssom
diff --git a/src/cobalt/cssom/complex_selector.h b/src/cobalt/cssom/complex_selector.h
index d8d2cae..e20ef8a 100644
--- a/src/cobalt/cssom/complex_selector.h
+++ b/src/cobalt/cssom/complex_selector.h
@@ -35,7 +35,12 @@
 //   https://www.w3.org/TR/selectors4/#complex
 class ComplexSelector : public Selector {
  public:
-  ComplexSelector() : last_selector_(NULL) {}
+  static const int kCombinatorLimit;
+
+  ComplexSelector()
+      : last_selector_(NULL),
+        combinator_count_(0),
+        combinator_limit_exceeded_(false) {}
   ~ComplexSelector() OVERRIDE {}
 
   // From Selector.
@@ -48,6 +53,10 @@
   CompoundSelector* first_selector() { return first_selector_.get(); }
   CompoundSelector* last_selector() { return last_selector_; }
 
+  int combinator_count() {
+    return combinator_count_;
+  }
+
   // For a chain of compound selectors separated by combinators, AppendSelector
   // should be first called with the left most compound selector, then
   // AppendCombinatorAndSelector should be called with each (combinator,
@@ -63,6 +72,9 @@
   scoped_ptr<CompoundSelector> first_selector_;
   Specificity specificity_;
 
+  int combinator_count_;
+  bool combinator_limit_exceeded_;
+
   DISALLOW_COPY_AND_ASSIGN(ComplexSelector);
 };
 
diff --git a/src/cobalt/cssom/selector_test.cc b/src/cobalt/cssom/selector_test.cc
index 090d9d7..c120db8 100644
--- a/src/cobalt/cssom/selector_test.cc
+++ b/src/cobalt/cssom/selector_test.cc
@@ -115,5 +115,42 @@
   EXPECT_EQ(Specificity(1, 2, 3), complex_selector->GetSpecificity());
 }
 
+TEST(SelectorTest, ComplexSelectorAppendCallLimit) {
+  {
+    scoped_ptr<ComplexSelector> complex_selector(new ComplexSelector());
+    complex_selector->AppendSelector(
+        make_scoped_ptr<CompoundSelector>(new CompoundSelector()));
+
+    for (int i = 0; i < ComplexSelector::kCombinatorLimit;
+         i++) {
+      scoped_ptr<CompoundSelector> compound_selector(new CompoundSelector());
+      scoped_ptr<ChildCombinator> child_combinator(new ChildCombinator());
+      complex_selector->AppendCombinatorAndSelector(
+          child_combinator.PassAs<Combinator>(), compound_selector.Pass());
+    }
+
+    EXPECT_EQ(complex_selector->combinator_count(),
+              ComplexSelector::kCombinatorLimit);
+  }
+
+  {
+    scoped_ptr<ComplexSelector> complex_selector(new ComplexSelector());
+    complex_selector->AppendSelector(
+        make_scoped_ptr<CompoundSelector>(new CompoundSelector()));
+
+    for (int i = 0;
+         i < 2 * ComplexSelector::kCombinatorLimit + 1;
+         i++) {
+      scoped_ptr<CompoundSelector> compound_selector(new CompoundSelector());
+      scoped_ptr<ChildCombinator> child_combinator(new ChildCombinator());
+      complex_selector->AppendCombinatorAndSelector(
+          child_combinator.PassAs<Combinator>(), compound_selector.Pass());
+    }
+
+    EXPECT_EQ(complex_selector->combinator_count(),
+              ComplexSelector::kCombinatorLimit);
+  }
+}
+
 }  // namespace cssom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/comment_test.cc b/src/cobalt/dom/comment_test.cc
index 2b8773d..6de330b 100644
--- a/src/cobalt/dom/comment_test.cc
+++ b/src/cobalt/dom/comment_test.cc
@@ -42,7 +42,7 @@
 
 CommentTest::CommentTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
 }
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 39e118d..ee0f442 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -66,8 +66,8 @@
     : css_parser_(css_parser::Parser::Create()),
       dom_stat_tracker_(new DomStatTracker("DocumentTest")),
       html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
-                            "") {
+                            NULL, NULL, NULL, NULL, NULL,
+                            dom_stat_tracker_.get(), "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 }
 
diff --git a/src/cobalt/dom/document_type_test.cc b/src/cobalt/dom/document_type_test.cc
index 1fc4b6a..90f51c3 100644
--- a/src/cobalt/dom/document_type_test.cc
+++ b/src/cobalt/dom/document_type_test.cc
@@ -27,7 +27,7 @@
  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() OVERRIDE {}
 
diff --git a/src/cobalt/dom/dom_implementation_test.cc b/src/cobalt/dom/dom_implementation_test.cc
index 6226a89..a9687aa 100644
--- a/src/cobalt/dom/dom_implementation_test.cc
+++ b/src/cobalt/dom/dom_implementation_test.cc
@@ -27,7 +27,8 @@
 
 TEST(DOMImplementationTest, CreateDocumentShouldCreateXMLDocument) {
   HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL,
-                                          NULL, NULL, NULL, NULL, NULL, "");
+                                          NULL, NULL, NULL, NULL, NULL, NULL,
+                                          "");
   scoped_refptr<DOMImplementation> dom_implementation =
       new DOMImplementation(&html_element_context);
   scoped_refptr<Document> document =
diff --git a/src/cobalt/dom/dom_parser_test.cc b/src/cobalt/dom/dom_parser_test.cc
index d3dd079..0e5cde6 100644
--- a/src/cobalt/dom/dom_parser_test.cc
+++ b/src/cobalt/dom/dom_parser_test.cc
@@ -49,6 +49,7 @@
           NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
           &stub_script_runner_, NULL /* media_source_registry */,
           NULL /* resource_provider */, NULL /* image_cache */,
+          NULL /* reduced_image_cache_capacity_manager */,
           NULL /* remote_typeface_cache */, NULL /* dom_stat_tracker */,
           "" /* language */),
       dom_parser_(new DOMParser(&html_element_context_)) {}
diff --git a/src/cobalt/dom/dom_stat_tracker.cc b/src/cobalt/dom/dom_stat_tracker.cc
index d5ca09f..003645c 100644
--- a/src/cobalt/dom/dom_stat_tracker.cc
+++ b/src/cobalt/dom/dom_stat_tracker.cc
@@ -30,7 +30,7 @@
       update_matching_rules_count_(0),
       update_computed_style_count_(0),
       are_stop_watches_enabled_(false) {
-  stop_watch_durations_.resize(kNumStopWatchTypes, 0);
+  stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta());
 }
 
 DomStatTracker::~DomStatTracker() {
@@ -52,7 +52,7 @@
   update_computed_style_count_ = 0;
 
   for (size_t i = 0; i < kNumStopWatchTypes; ++i) {
-    stop_watch_durations_[i] = 0;
+    stop_watch_durations_[i] = base::TimeDelta();
   }
 }
 
@@ -70,7 +70,8 @@
 
 void DomStatTracker::DisableStopWatches() { are_stop_watches_enabled_ = false; }
 
-int64 DomStatTracker::GetStopWatchTypeDuration(StopWatchType type) const {
+base::TimeDelta DomStatTracker::GetStopWatchTypeDuration(
+    StopWatchType type) const {
   return stop_watch_durations_[type];
 }
 
@@ -78,7 +79,7 @@
   return are_stop_watches_enabled_;
 }
 
-void DomStatTracker::OnStopWatchStopped(int id, int64 time_elapsed) {
+void DomStatTracker::OnStopWatchStopped(int id, base::TimeDelta time_elapsed) {
   stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed;
 }
 
diff --git a/src/cobalt/dom/dom_stat_tracker.h b/src/cobalt/dom/dom_stat_tracker.h
index e3accc0..e130da8 100644
--- a/src/cobalt/dom/dom_stat_tracker.h
+++ b/src/cobalt/dom/dom_stat_tracker.h
@@ -65,12 +65,12 @@
   void EnableStopWatches();
   void DisableStopWatches();
 
-  int64 GetStopWatchTypeDuration(StopWatchType type) const;
+  base::TimeDelta GetStopWatchTypeDuration(StopWatchType type) const;
 
  private:
   // From base::StopWatchOwner
   bool IsStopWatchEnabled(int id) const OVERRIDE;
-  void OnStopWatchStopped(int id, int64 time_elapsed) OVERRIDE;
+  void OnStopWatchStopped(int id, base::TimeDelta time_elapsed) OVERRIDE;
 
   // CVals. They are updated when the periodic counts are flushed.
   base::CVal<int, base::CValPublic> total_html_elements_;
@@ -85,7 +85,7 @@
   // Stop watch-related. The durations are cleared after the CVals are updated
   // in |FlushPeriodicTracking|.
   bool are_stop_watches_enabled_;
-  std::vector<int64> stop_watch_durations_;
+  std::vector<base::TimeDelta> stop_watch_durations_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/dom_string_map_test.cc b/src/cobalt/dom/dom_string_map_test.cc
index 1825b19..d9c5521 100644
--- a/src/cobalt/dom/dom_string_map_test.cc
+++ b/src/cobalt/dom/dom_string_map_test.cc
@@ -45,7 +45,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_)),
       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 49c58dd..0ef5e3e 100644
--- a/src/cobalt/dom/dom_token_list_test.cc
+++ b/src/cobalt/dom/dom_token_list_test.cc
@@ -40,7 +40,7 @@
 
 DOMTokenListTest::DOMTokenListTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
 }
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc
index 30acb76..c8b5ea9 100644
--- a/src/cobalt/dom/element_test.cc
+++ b/src/cobalt/dom/element_test.cc
@@ -61,7 +61,7 @@
       dom_parser_(new dom_parser::Parser()),
       dom_stat_tracker_(new DomStatTracker("ElementTest")),
       html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
-                            NULL, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                             dom_stat_tracker_.get(), "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/global_stats.cc b/src/cobalt/dom/global_stats.cc
index cfedc69..acc3a78 100644
--- a/src/cobalt/dom/global_stats.cc
+++ b/src/cobalt/dom/global_stats.cc
@@ -96,7 +96,7 @@
 void GlobalStats::IncreaseXHRMemoryUsage(size_t delta) { xhr_memory += delta; }
 
 void GlobalStats::DecreaseXHRMemoryUsage(size_t delta) {
-  DCHECK_GE(xhr_memory, delta);
+  DCHECK_GE(xhr_memory.value(), delta);
   xhr_memory -= delta;
 }
 
diff --git a/src/cobalt/dom/global_stats.h b/src/cobalt/dom/global_stats.h
index 4d29934..bb35c28 100644
--- a/src/cobalt/dom/global_stats.h
+++ b/src/cobalt/dom/global_stats.h
@@ -79,7 +79,7 @@
   base::CVal<int, base::CValPublic> num_nodes;
   base::CVal<int> num_node_lists;
   base::CVal<int> num_xhrs;
-  base::CVal<size_t> xhr_memory;
+  base::CVal<base::cval::SizeInBytes> xhr_memory;
 
   friend struct DefaultSingletonTraits<GlobalStats>;
   DISALLOW_COPY_AND_ASSIGN(GlobalStats);
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index 25d74f7..eee30eb 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -29,6 +29,8 @@
     MediaSource::Registry* media_source_registry,
     render_tree::ResourceProvider* resource_provider,
     loader::image::ImageCache* image_cache,
+    loader::image::ReducedCacheCapacityManager*
+        reduced_image_cache_capacity_manager,
     loader::font::RemoteTypefaceCache* remote_typeface_cache,
     DomStatTracker* dom_stat_tracker, const std::string& language)
     : fetcher_factory_(fetcher_factory),
@@ -40,6 +42,8 @@
       media_source_registry_(media_source_registry),
       resource_provider_(resource_provider),
       image_cache_(image_cache),
+      reduced_image_cache_capacity_manager_(
+          reduced_image_cache_capacity_manager),
       remote_typeface_cache_(remote_typeface_cache),
       dom_stat_tracker_(dom_stat_tracker),
       language_(language),
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index 642a4a0..8cdc86b 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -50,6 +50,8 @@
                      MediaSource::Registry* media_source_registry,
                      render_tree::ResourceProvider* resource_provider,
                      loader::image::ImageCache* image_cache,
+                     loader::image::ReducedCacheCapacityManager*
+                         reduced_image_cache_capacity_manager,
                      loader::font::RemoteTypefaceCache* remote_typeface_cache,
                      DomStatTracker* dom_stat_tracker,
                      const std::string& language);
@@ -93,6 +95,11 @@
     return html_element_factory_.get();
   }
 
+  loader::image::ReducedCacheCapacityManager*
+  reduced_image_cache_capacity_manager() {
+    return reduced_image_cache_capacity_manager_;
+  }
+
  private:
   loader::FetcherFactory* const fetcher_factory_;
   cssom::CSSParser* const css_parser_;
@@ -103,6 +110,8 @@
   MediaSource::Registry* const media_source_registry_;
   render_tree::ResourceProvider* resource_provider_;
   loader::image::ImageCache* const image_cache_;
+  loader::image::ReducedCacheCapacityManager* const
+      reduced_image_cache_capacity_manager_;
   loader::font::RemoteTypefaceCache* const remote_typeface_cache_;
   DomStatTracker* const dom_stat_tracker_;
   const std::string language_;
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index 94b421f..2f0c00c 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -58,8 +58,10 @@
             NULL /* can_play_type_handler */,
             NULL /* web_media_player_factory */, &stub_script_runner_,
             NULL /* media_source_registry */, NULL /* resource_provider */,
-            NULL /* image_cache */, NULL /* remote_typeface_cache */,
-            dom_stat_tracker_.get(), "" /* language */),
+            NULL /* image_cache */,
+            NULL /* reduced_image_cache_capacity_manager */,
+            NULL /* remote_typeface_cache */, dom_stat_tracker_.get(),
+            "" /* language */),
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementFactoryTest() OVERRIDE {}
 
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index 4561437..e445dcb 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -110,7 +110,8 @@
   HTMLElementTest()
       : dom_stat_tracker_(new DomStatTracker("HTMLElementTest")),
         html_element_context_(NULL, &css_parser_, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, dom_stat_tracker_.get(), ""),
+                              NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
+                              ""),
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementTest() OVERRIDE {}
 
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 3d7a596..55d1295 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -489,6 +489,29 @@
 }
 
 void HTMLMediaElement::CreateMediaPlayer() {
+  if (src().empty()) {
+    reduced_image_cache_capacity_request_ = base::nullopt;
+  } else if (html_element_context()
+                 ->reduced_image_cache_capacity_manager()
+                 ->reduced_capacity_percentage() != 1.0f) {
+    // Platforms with constrained GPU memory typically are placed in their most
+    // fragile position when a video is playing.  Thus, we request a lower image
+    // cache size and empty the cache before we start playing a video, in
+    // an effort to make room in memory for video decoding.  Reducing the
+    // image cache during this time should also be fine as the UI is not usually
+    // in the foreground during this time.
+
+    // Set a new reduced cache capacity.
+    reduced_image_cache_capacity_request_.emplace(
+        html_element_context()->reduced_image_cache_capacity_manager());
+    // Empty all non-referenced images from the image cache to make room for
+    // the video decoder.
+    html_element_context()->image_cache()->Purge();
+    // Ensure that all resource destructions are flushed and the memory is
+    // reclaimed.
+    html_element_context()->resource_provider()->Finish();
+  }
+
   player_.reset();
   player_ =
       html_element_context()->web_media_player_factory()->CreateWebMediaPlayer(
@@ -711,6 +734,8 @@
   pending_load_ = false;
   load_state_ = kWaitingForSource;
 
+  reduced_image_cache_capacity_request_ = base::nullopt;
+
   if (node_document()) {
     node_document()->OnDOMMutation();
   }
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index 300874f..7bd360e 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -30,6 +30,7 @@
 #include "cobalt/dom/media_source.h"
 #include "cobalt/dom/time_ranges.h"
 #include "cobalt/dom/uint8_array.h"
+#include "cobalt/loader/image/image_cache.h"
 #include "cobalt/script/exception_state.h"
 #include "googleurl/src/gurl.h"
 #include "media/player/web_media_player.h"
@@ -272,6 +273,10 @@
 
   scoped_refptr<MediaError> error_;
 
+  // Helper object to reduce the image capacity while a video is playing.
+  base::optional<loader::image::ReducedCacheCapacityManager::Request>
+      reduced_image_cache_capacity_request_;
+
   DISALLOW_COPY_AND_ASSIGN(HTMLMediaElement);
 };
 
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index 0b6d3a9..fcfdf35 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -52,6 +52,7 @@
       is_ready_(false),
       load_option_(0),
       inline_script_location_(GetSourceLocationName(), 1, 1),
+      is_sync_load_successful_(false),
       prevent_garbage_collection_count_(0) {
   DCHECK(document->html_element_context()->script_runner());
 }
@@ -172,7 +173,8 @@
   if (HasAttribute("src") && src() == "") {
     LOG(WARNING) << "src attribute of script element is empty.";
 
-    PostToDispatchEvent(FROM_HERE, base::Tokens::error());
+    PreventGarbageCollectionAndPostToDispatchEvent(FROM_HERE,
+                                                   base::Tokens::error());
     return;
   }
 
@@ -184,7 +186,8 @@
   if (!url_.is_valid()) {
     LOG(WARNING) << src() << " cannot be resolved based on " << base_url << ".";
 
-    PostToDispatchEvent(FROM_HERE, base::Tokens::error());
+    PreventGarbageCollectionAndPostToDispatchEvent(FROM_HERE,
+                                                   base::Tokens::error());
     return;
   }
 
@@ -336,7 +339,8 @@
                                     text)) {
         ExecuteInternal();
       } else {
-        PostToDispatchEvent(FROM_HERE, base::Tokens::error());
+        PreventGarbageCollectionAndPostToDispatchEvent(FROM_HERE,
+                                                       base::Tokens::error());
       }
     } break;
     default: { NOTREACHED(); }
@@ -451,11 +455,8 @@
   DCHECK(load_option_ == 4 || load_option_ == 5);
   TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::OnLoadingError()");
 
-  // Allow garbage collection on the current object. No script will be executed
-  // on it.
-  AllowGarbageCollection();
-
   if (!document_) {
+    AllowGarbageCollection();
     return;
   }
 
@@ -463,7 +464,9 @@
 
   // Executing the script block must just consist of firing a simple event
   // named error at the element.
+  DCHECK_GT(prevent_garbage_collection_count_, 0);
   DispatchEvent(new Event(base::Tokens::error()));
+  AllowGarbageCollection();
 
   switch (load_option_) {
     case 4: {
@@ -513,14 +516,27 @@
   // named load at the script element.
   // TODO: Remove the firing of readystatechange once we support Promise.
   if (is_external) {
+    DCHECK_GT(prevent_garbage_collection_count_, 0);
     DispatchEvent(new Event(base::Tokens::load()));
     DispatchEvent(new Event(base::Tokens::readystatechange()));
   } else {
-    PostToDispatchEvent(FROM_HERE, base::Tokens::load());
-    PostToDispatchEvent(FROM_HERE, base::Tokens::readystatechange());
+    PreventGarbageCollectionAndPostToDispatchEvent(FROM_HERE,
+                                                   base::Tokens::load());
+    PreventGarbageCollectionAndPostToDispatchEvent(
+        FROM_HERE, base::Tokens::readystatechange());
   }
 }
 
+void HTMLScriptElement::PreventGarbageCollectionAndPostToDispatchEvent(
+    const tracked_objects::Location& location, const base::Token& token) {
+  // Ensure that this HTMLScriptElement is not garbage collected until the event
+  // has been processed.
+  PreventGarbageCollection();
+  PostToDispatchEventAndRunCallback(
+      location, token,
+      base::Bind(&HTMLScriptElement::AllowGarbageCollection, this));
+}
+
 void HTMLScriptElement::PreventGarbageCollection() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_GE(prevent_garbage_collection_count_, 0);
diff --git a/src/cobalt/dom/html_script_element.h b/src/cobalt/dom/html_script_element.h
index 9335314..17ab6d4 100644
--- a/src/cobalt/dom/html_script_element.h
+++ b/src/cobalt/dom/html_script_element.h
@@ -102,6 +102,8 @@
   void Execute(const std::string& content,
                const base::SourceLocation& script_location, bool is_external);
 
+  void PreventGarbageCollectionAndPostToDispatchEvent(
+      const tracked_objects::Location& location, const base::Token& token);
   void PreventGarbageCollection();
   void AllowGarbageCollection();
 
diff --git a/src/cobalt/dom/node_dispatch_event_test.cc b/src/cobalt/dom/node_dispatch_event_test.cc
index d3368ef..2051305 100644
--- a/src/cobalt/dom/node_dispatch_event_test.cc
+++ b/src/cobalt/dom/node_dispatch_event_test.cc
@@ -59,7 +59,7 @@
 
 NodeDispatchEventTest::NodeDispatchEventTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 
   document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/node_list_live_test.cc b/src/cobalt/dom/node_list_live_test.cc
index b8e6702..e3f757f 100644
--- a/src/cobalt/dom/node_list_live_test.cc
+++ b/src/cobalt/dom/node_list_live_test.cc
@@ -30,7 +30,7 @@
   NodeListLiveTest()
       : dom_stat_tracker_("NodeListLiveTest"),
         html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, &dom_stat_tracker_, ""),
+                              NULL, NULL, NULL, &dom_stat_tracker_, ""),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListLiveTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_list_test.cc b/src/cobalt/dom/node_list_test.cc
index a93339a..c2e7b9a 100644
--- a/src/cobalt/dom/node_list_test.cc
+++ b/src/cobalt/dom/node_list_test.cc
@@ -30,7 +30,7 @@
   NodeListTest()
       : dom_stat_tracker_(new DomStatTracker("NodeListTest")),
         html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, dom_stat_tracker_.get(), ""),
+                              NULL, NULL, NULL, dom_stat_tracker_.get(), ""),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_test.cc b/src/cobalt/dom/node_test.cc
index 57153ae..a4abe5e 100644
--- a/src/cobalt/dom/node_test.cc
+++ b/src/cobalt/dom/node_test.cc
@@ -78,7 +78,7 @@
 
 NodeTest::NodeTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 
   document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index 2d1b118..7482605 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -45,7 +45,7 @@
         dom_parser_(new dom_parser::Parser()),
         dom_stat_tracker_(new DomStatTracker("RuleMatchingTest")),
         html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               dom_stat_tracker_.get(), ""),
         document_(new Document(&html_element_context_)),
         root_(document_->CreateElement("html")->AsHTMLElement()) {
diff --git a/src/cobalt/dom/serializer_test.cc b/src/cobalt/dom/serializer_test.cc
index ec16354..dedcbe1 100644
--- a/src/cobalt/dom/serializer_test.cc
+++ b/src/cobalt/dom/serializer_test.cc
@@ -47,7 +47,7 @@
     : dom_parser_(new dom_parser::Parser()),
       dom_stat_tracker_(new DomStatTracker("SerializerTest")),
       html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, dom_stat_tracker_.get(), ""),
+                            NULL, NULL, NULL, dom_stat_tracker_.get(), ""),
       document_(new Document(&html_element_context_)),
       root_(new Element(document_, base::Token("root"))),
       source_location_(base::SourceLocation("[object SerializerTest]", 1, 1)) {}
diff --git a/src/cobalt/dom/text_test.cc b/src/cobalt/dom/text_test.cc
index 536b42f..5b504b8 100644
--- a/src/cobalt/dom/text_test.cc
+++ b/src/cobalt/dom/text_test.cc
@@ -41,7 +41,7 @@
 
 TextTest::TextTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, "") {
   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 295c8f9..8a57e10 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -64,6 +64,8 @@
                Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
                render_tree::ResourceProvider* resource_provider,
                loader::image::ImageCache* image_cache,
+               loader::image::ReducedCacheCapacityManager*
+                   reduced_image_cache_capacity_manager,
                loader::font::RemoteTypefaceCache* remote_typeface_cache,
                LocalStorageDatabase* local_storage_database,
                media::CanPlayTypeHandler* can_play_type_handler,
@@ -86,8 +88,8 @@
       html_element_context_(new HTMLElementContext(
           fetcher_factory, css_parser, dom_parser, can_play_type_handler,
           web_media_player_factory, script_runner, media_source_registry,
-          resource_provider, image_cache, remote_typeface_cache,
-          dom_stat_tracker, language)),
+          resource_provider, image_cache, reduced_image_cache_capacity_manager,
+          remote_typeface_cache, dom_stat_tracker, language)),
       performance_(new Performance(new base::SystemMonotonicClock())),
       ALLOW_THIS_IN_INITIALIZER_LIST(document_(new Document(
           html_element_context_.get(),
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 2bb6c07..6b2e776 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -89,6 +89,8 @@
          Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
          render_tree::ResourceProvider* resource_provider,
          loader::image::ImageCache* image_cache,
+         loader::image::ReducedCacheCapacityManager*
+             reduced_image_cache_capacity_manager,
          loader::font::RemoteTypefaceCache* remote_typeface_cache,
          LocalStorageDatabase* local_storage_database,
          media::CanPlayTypeHandler* can_play_type_handler,
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index 2a5db90..43650c0 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -53,9 +53,10 @@
         url_("about:blank"),
         window_(new Window(
             1920, 1080, css_parser_.get(), dom_parser_.get(),
-            fetcher_factory_.get(), NULL, NULL, NULL, &local_storage_database_,
-            stub_media_module_.get(), stub_media_module_.get(), NULL, NULL,
-            NULL, NULL, url_, "", "en-US", base::Callback<void(const GURL &)>(),
+            fetcher_factory_.get(), NULL, NULL, NULL, NULL,
+            &local_storage_database_, stub_media_module_.get(),
+            stub_media_module_.get(), NULL, NULL, NULL, NULL, url_, "", "en-US",
+            base::Callback<void(const GURL &)>(),
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
diff --git a/src/cobalt/dom/xml_document_test.cc b/src/cobalt/dom/xml_document_test.cc
index 34d8e11..4603520 100644
--- a/src/cobalt/dom/xml_document_test.cc
+++ b/src/cobalt/dom/xml_document_test.cc
@@ -26,7 +26,8 @@
 
 TEST(XMLDocumentTest, IsXMLDocument) {
   HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL,
-                                          NULL, NULL, NULL, NULL, NULL, "");
+                                          NULL, NULL, NULL, NULL, NULL, NULL,
+                                          "");
   scoped_refptr<Document> document = new XMLDocument(&html_element_context);
   EXPECT_TRUE(document->IsXMLDocument());
 }
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index e00a271..ab5f6a1 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -69,7 +69,7 @@
       html_element_context_(&fetcher_factory_, &stub_css_parser_,
                             dom_parser_.get(), NULL /* can_play_type_handler */,
                             NULL /* web_media_player_factory */,
-                            &stub_script_runner_, NULL, NULL, NULL, NULL,
+                            &stub_script_runner_, NULL, NULL, NULL, NULL, NULL,
                             dom_stat_tracker_.get(), ""),
       document_(
           new dom::Document(&html_element_context_, dom::Document::Options())),
diff --git a/src/cobalt/dom_parser/xml_decoder_test.cc b/src/cobalt/dom_parser/xml_decoder_test.cc
index a7540c0..e7336eb 100644
--- a/src/cobalt/dom_parser/xml_decoder_test.cc
+++ b/src/cobalt/dom_parser/xml_decoder_test.cc
@@ -51,7 +51,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_)),
       source_location_(base::SourceLocation("[object XMLDecoderTest]", 1, 1)) {}
 
diff --git a/src/cobalt/layout/layout_stat_tracker.cc b/src/cobalt/layout/layout_stat_tracker.cc
index 53dc4d4..1ef39bf 100644
--- a/src/cobalt/layout/layout_stat_tracker.cc
+++ b/src/cobalt/layout/layout_stat_tracker.cc
@@ -27,7 +27,7 @@
       boxes_created_count_(0),
       boxes_destroyed_count_(0),
       are_stop_watches_enabled_(false) {
-  stop_watch_durations_.resize(kNumStopWatchTypes, 0);
+  stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta());
 }
 
 LayoutStatTracker::~LayoutStatTracker() {
@@ -46,7 +46,7 @@
   boxes_destroyed_count_ = 0;
 
   for (size_t i = 0; i < kNumStopWatchTypes; ++i) {
-    stop_watch_durations_[i] = 0;
+    stop_watch_durations_[i] = base::TimeDelta();
   }
 }
 
@@ -62,7 +62,8 @@
   are_stop_watches_enabled_ = false;
 }
 
-int64 LayoutStatTracker::GetStopWatchTypeDuration(StopWatchType type) const {
+base::TimeDelta LayoutStatTracker::GetStopWatchTypeDuration(
+    StopWatchType type) const {
   return stop_watch_durations_[type];
 }
 
@@ -70,7 +71,8 @@
   return are_stop_watches_enabled_;
 }
 
-void LayoutStatTracker::OnStopWatchStopped(int id, int64 time_elapsed) {
+void LayoutStatTracker::OnStopWatchStopped(int id,
+                                           base::TimeDelta time_elapsed) {
   stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed;
 }
 
diff --git a/src/cobalt/layout/layout_stat_tracker.h b/src/cobalt/layout/layout_stat_tracker.h
index 87269b3..2f89a9b 100644
--- a/src/cobalt/layout/layout_stat_tracker.h
+++ b/src/cobalt/layout/layout_stat_tracker.h
@@ -56,12 +56,12 @@
   void EnableStopWatches();
   void DisableStopWatches();
 
-  int64 GetStopWatchTypeDuration(StopWatchType type) const;
+  base::TimeDelta GetStopWatchTypeDuration(StopWatchType type) const;
 
  private:
   // From base::StopWatchOwner
   bool IsStopWatchEnabled(int id) const OVERRIDE;
-  void OnStopWatchStopped(int id, int64 time_elapsed) OVERRIDE;
+  void OnStopWatchStopped(int id, base::TimeDelta time_elapsed) OVERRIDE;
 
   // CVals. They are updated when the periodic counts are flushed.
   base::CVal<int, base::CValPublic> total_boxes_;
@@ -74,7 +74,7 @@
   // Stop watch-related. The durations are cleared after the CVals are updated
   // in |FlushPeriodicTracking|.
   bool are_stop_watches_enabled_;
-  std::vector<int64> stop_watch_durations_;
+  std::vector<base::TimeDelta> stop_watch_durations_;
 };
 
 }  // namespace layout
diff --git a/src/cobalt/loader/image/image_cache.h b/src/cobalt/loader/image/image_cache.h
index 032582c..78a64a1 100644
--- a/src/cobalt/loader/image/image_cache.h
+++ b/src/cobalt/loader/image/image_cache.h
@@ -79,6 +79,67 @@
       fetcher_factory));
 }
 
+// The ReducedCacheCapacityManager is a helper class that manages state which
+// makes it easy for clients to place the image cache in a reduced memory state,
+// at times when GPU memory is at a premium, such as when playing a video.
+// Clients should create ReducedCacheCapacityManager::Request objects to
+// indicate that they would like the image cache to enter a reduced capacity
+// state, internally, the manager keeps a reference count of how many Request
+// objects exist and enables the reduced capacity state if there is more than
+// one of them.
+class ReducedCacheCapacityManager {
+ public:
+  class Request {
+   public:
+    explicit Request(ReducedCacheCapacityManager* manager) : manager_(manager) {
+      manager_->IncrementRequestRefCount();
+    }
+    ~Request() { manager_->DecrementRequestRefCount(); }
+
+   private:
+    ReducedCacheCapacityManager* manager_;
+  };
+
+  ReducedCacheCapacityManager(ImageCache* cache,
+                              float reduced_capacity_percentage)
+      : cache_(cache),
+        request_ref_count_(0),
+        reduced_capacity_percentage_(reduced_capacity_percentage),
+        original_capacity_(cache_->capacity()),
+        reduced_capacity_(static_cast<uint32>(reduced_capacity_percentage_ *
+                                              original_capacity_)) {
+    DCHECK_GE(1.0f, reduced_capacity_percentage);
+  }
+
+  float reduced_capacity_percentage() const {
+    return reduced_capacity_percentage_;
+  }
+
+ private:
+  void IncrementRequestRefCount() {
+    if (request_ref_count_ == 0) {
+      cache_->SetCapacity(reduced_capacity_);
+    }
+    ++request_ref_count_;
+  }
+
+  void DecrementRequestRefCount() {
+    DCHECK_LT(0, request_ref_count_);
+    --request_ref_count_;
+    if (request_ref_count_ == 0) {
+      cache_->SetCapacity(original_capacity_);
+    }
+  }
+
+  ImageCache* cache_;
+  int request_ref_count_;
+  float reduced_capacity_percentage_;
+  const uint32 original_capacity_;
+  const uint32 reduced_capacity_;
+
+  friend class Request;
+};
+
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index 0d58b8c..f522f5b 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -409,6 +409,11 @@
     return security_callback_;
   }
 
+  uint32 capacity() const { return cache_capacity_; }
+  void SetCapacity(uint32 capacity);
+
+  void Purge();
+
  private:
   friend class CachedResource<CacheType>;
 
@@ -428,12 +433,15 @@
   // cache is over its memory limit.
   void NotifyResourceDestroyed(CachedResourceType* cached_resource);
 
-  void ReclaimMemory();
+  // Releases unreferenced cache objects until our total cache memory usage is
+  // less than or equal to |bytes_to_reclaim_down_to|, or until there are no
+  // more unreferenced cache objects to release.
+  void ReclaimMemory(uint32 bytes_to_reclaim_down_to);
 
   // The name of this resource cache object, useful while debugging.
   const std::string name_;
 
-  const uint32 cache_capacity_;
+  uint32 cache_capacity_;
 
   scoped_ptr<DecoderProviderType> decoder_provider_;
   loader::FetcherFactory* const fetcher_factory_;
@@ -450,8 +458,8 @@
 
   base::ThreadChecker resource_cache_thread_checker_;
 
-  base::CVal<uint32, base::CValPublic> size_in_bytes_;
-  base::CVal<uint32, base::CValPublic> capacity_in_bytes_;
+  base::CVal<base::cval::SizeInBytes, base::CValPublic> size_in_bytes_;
+  base::CVal<base::cval::SizeInBytes, base::CValPublic> capacity_in_bytes_;
 
   DISALLOW_COPY_AND_ASSIGN(ResourceCache);
 };
@@ -517,6 +525,20 @@
 }
 
 template <typename CacheType>
+void ResourceCache<CacheType>::SetCapacity(uint32 capacity) {
+  DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+  cache_capacity_ = capacity;
+  capacity_in_bytes_ = capacity;
+  ReclaimMemory(cache_capacity_);
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::Purge() {
+  DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+  ReclaimMemory(0);
+}
+
+template <typename CacheType>
 void ResourceCache<CacheType>::NotifyResourceSuccessfullyLoaded(
     CachedResourceType* cached_resource) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
@@ -525,7 +547,7 @@
     size_in_bytes_ +=
         CacheType::GetEstimatedSizeInBytes(cached_resource->TryGetResource());
     if (size_in_bytes_ > cache_capacity_) {
-      ReclaimMemory();
+      ReclaimMemory(cache_capacity_);
     }
   }
 }
@@ -547,15 +569,15 @@
     unreference_cached_resource_map_.insert(
         std::make_pair(url, cached_resource->TryGetResource()));
     // Try to reclaim some memory.
-    ReclaimMemory();
+    ReclaimMemory(cache_capacity_);
   }
 }
 
 template <typename CacheType>
-void ResourceCache<CacheType>::ReclaimMemory() {
+void ResourceCache<CacheType>::ReclaimMemory(uint32 bytes_to_reclaim_down_to) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
 
-  while (size_in_bytes_ > cache_capacity_ &&
+  while (size_in_bytes_.value() > bytes_to_reclaim_down_to &&
          !unreference_cached_resource_map_.empty()) {
     // The first element is the earliest-inserted element.
     scoped_refptr<ResourceType> resource =
@@ -574,7 +596,7 @@
   // have to increase the size of |cache_capacity_| if the system memory is
   // large enough or evict resources from the cache even though they are still
   // in use.
-  DLOG_IF(WARNING, size_in_bytes_ > cache_capacity_)
+  DLOG_IF(WARNING, size_in_bytes_.value() > cache_capacity_)
       << "cached size: " << size_in_bytes_
       << ", cache capacity: " << cache_capacity_;
 }
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index 9b6aa08..72c106a 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -49,6 +49,12 @@
 
   virtual ~ResourceProvider() {}
 
+  // Blocks until it can be guaranteed that all resource-related operations have
+  // completed.  This might be important if we would like to ensure that memory
+  // allocations or deallocations have occurred before proceeding with a memory
+  // intensive operation.
+  virtual void Finish() = 0;
+
   // Returns true if AllocateImageData() supports the given |pixel_format|.
   virtual bool PixelFormatSupported(PixelFormat pixel_format) = 0;
 
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index e45c349..1430f5f 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -162,6 +162,8 @@
  public:
   ~ResourceProviderStub() OVERRIDE {}
 
+  void Finish() OVERRIDE {}
+
   bool PixelFormatSupported(PixelFormat pixel_format) OVERRIDE {
     UNREFERENCED_PARAMETER(pixel_format);
     return true;
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index cbce361..078a683 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -40,6 +40,8 @@
       render_tree::ResourceProvider* skia_resource_provider);
   ~ResourceProvider() OVERRIDE {}
 
+  void Finish() OVERRIDE {}
+
   bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
   bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/common/surface_cache.cc b/src/cobalt/renderer/rasterizer/common/surface_cache.cc
index 1464d11..5ce019f 100644
--- a/src/cobalt/renderer/rasterizer/common/surface_cache.cc
+++ b/src/cobalt/renderer/rasterizer/common/surface_cache.cc
@@ -38,7 +38,7 @@
 
 // Since caching elements is expensive, we limit the number of new entries added
 // to the cache each frame.
-const size_t kMaxElementsToCachePerFrame = 3;
+const size_t kMaxElementsToCachePerFrame = 1;
 
 // If a node has been seen for kConsecutiveFramesForPreference consecutive
 // frames, it is preferred for being cached over nodes that have been seen less
@@ -56,40 +56,23 @@
 const float kApplySurfaceTimeMultipleCacheCriteria = 1.2f;
 }  // namespace
 
-void SurfaceCache::Block::InitBlock() {
+void SurfaceCache::Block::InitBlock(render_tree::Node* node) {
   start_time_ = base::TimeTicks::Now();
 
+  key_ = NodeMapKey(node,
+                    cache_->delegate_->GetRenderSize(node->GetBounds().size()));
+
   // Keep track of the stack of blocks currently being rendered.
   parent_ = cache_->current_block_;
   cache_->current_block_ = this;
 
-  math::RectF node_bounds = node_->GetBounds();
-
-  NodeMap::iterator seen_iter = cache_->seen_.find(node_);
+  NodeMap::iterator seen_iter = cache_->seen_.find(key_);
   node_data_ = (seen_iter == cache_->seen_.end() ? NULL : &seen_iter->second);
 
   if (node_data_) {
     // In order for the cache to function, it needs to know which nodes are
     // seen frequently and which are not.  Setting this flag is the main signal.
     node_data_->visited_this_frame = true;
-
-    // Check that the actual size that we are rendering to now is compatible
-    // with the size when this node was originally inserted into the cache.  If
-    // the sizes are incompatible, remove the old version from the cache so that
-    // it can be replaced by this new version.
-    // The new size is compatible with the old size if the new size is smaller
-    // than the old size, or the new size is smaller than the local size of
-    // the node being rendered.
-    math::Size render_size =
-        cache_->delegate_->GetRenderSize(node_bounds.size());
-    if ((render_size.width() > node_bounds.size().width() &&
-         render_size.width() > node_data_->render_size.width()) ||
-        (render_size.height() > node_bounds.size().height() &&
-         render_size.height() > node_data_->render_size.height())) {
-      // Sizes are incompatible, remove the old cached version of this node.
-      cache_->RemoveFromSeen(seen_iter);
-      node_data_ = NULL;
-    }
   }
 
   if (node_data_ && node_data_->cached()) {
@@ -123,14 +106,9 @@
       cache_->recording_ = true;
       cache_->PurgeUntilSpaceAvailable(node_data_->size_in_bytes());
 
-      // Since we are now caching the surface, make sure the recorded render
-      // size is up to date as this will reflect the size of the cached surface
-      // allocated for this node.
-      node_data_->render_size =
-          cache_->delegate_->GetRenderSize(node_bounds.size());
       // Signal to the delegate that we would like to record this block for
       // caching.
-      cache_->delegate_->StartRecording(node_bounds);
+      cache_->delegate_->StartRecording(key_.node->GetBounds());
     } else {
       // The node is not cached, and it should not become cached, so just do
       // nothing besides record how long it takes via |start_time_|, set above.
@@ -161,6 +139,11 @@
       IncreaseAncestorBlockDurations(node_data_->duration - duration);
     } break;
     case kStateNotRecording: {
+      if (key_.render_size.GetArea() == 0) {
+        // Do not consider degenerate render tree nodes for the cache.
+        break;
+      }
+
       // We're done now, get metrics from the delegate and determine whether or
       // not we should cache the result for next time.
       base::TimeDelta duration = base::TimeTicks::Now() - start_time_;
@@ -169,15 +152,12 @@
       // so.
       if (!node_data_) {
         std::pair<NodeMap::iterator, bool> insert_results =
-            cache_->seen_.insert(std::make_pair(node_, NodeData(node_)));
+            cache_->seen_.insert(std::make_pair(key_, NodeData(key_)));
         DCHECK(insert_results.second);
         node_data_ = &insert_results.first->second;
       }
 
       // Update the recorded entry with new timing information.
-      DCHECK_LT(0.0f, node_->GetBounds().size().GetArea());
-      node_data_->render_size =
-          cache_->delegate_->GetRenderSize(node_->GetBounds().size());
       node_data_->duration = duration;
     } break;
     case kStateInvalid: {
@@ -565,7 +545,7 @@
     NodeData* node_data_to_purge = to_purge_.back();
     to_purge_.pop_back();
 
-    if (seen_.find(node_data_to_purge->node) != seen_.end()) {
+    if (seen_.find(node_data_to_purge->key()) != seen_.end()) {
       FreeCachedSurface(node_data_to_purge);
     }
   }
diff --git a/src/cobalt/renderer/rasterizer/common/surface_cache.h b/src/cobalt/renderer/rasterizer/common/surface_cache.h
index c1a53a3..deb93de 100644
--- a/src/cobalt/renderer/rasterizer/common/surface_cache.h
+++ b/src/cobalt/renderer/rasterizer/common/surface_cache.h
@@ -104,16 +104,36 @@
   };
 
  private:
+  // The key to our cache's map of which items it has seen before.  We use
+  // the address of a render node along with its on-screen render size to
+  // determine whether there is a cache match or not.
+  struct NodeMapKey {
+    NodeMapKey() {}
+    NodeMapKey(render_tree::Node* node, const math::Size& render_size)
+        : node(node), render_size(render_size) {}
+
+    bool operator<(const NodeMapKey& rhs) const {
+      return (node == rhs.node
+                  ? (render_size.width() == rhs.render_size.width()
+                         ? render_size.height() < rhs.render_size.height()
+                         : render_size.width() < rhs.render_size.width())
+                  : node < rhs.node);
+    }
+
+    render_tree::Node* node;
+    math::Size render_size;
+  };
   // NodeData contains a set of data stored for every node that we have seen
   // and helps determine whether that node should be cached or not, or if it
   // is already cached.
   struct NodeData {
-    explicit NodeData(render_tree::Node* node)
-        : surface(NULL),
+    explicit NodeData(const NodeMapKey& key)
+        : render_size(key.render_size),
+          surface(NULL),
           visited_this_frame(true),
           consecutive_frames_visited(0),
           is_cache_candidate(false),
-          node(node) {}
+          node(key.node) {}
 
     // The size in bytes occupied by a cached surface representing this node.
     int size_in_bytes() { return render_size.GetArea() * 4; }
@@ -121,6 +141,9 @@
     // Returns whether or not this node is cached or not.
     bool cached() const { return surface != NULL; }
 
+    // Creates a key from the NodeData.
+    NodeMapKey key() const { return NodeMapKey(node.get(), render_size); }
+
     // Tracks how long it last took to render this node.
     base::TimeDelta duration;
 
@@ -145,7 +168,7 @@
     // A reference to the actual node.
     scoped_refptr<render_tree::Node> node;
   };
-  typedef base::hash_map<render_tree::Node*, NodeData> NodeMap;
+  typedef std::map<NodeMapKey, NodeData> NodeMap;
 
  public:
   // The main public interface to SurfaceCache is via SurfaceCache::Block.
@@ -159,7 +182,6 @@
       // We like this to be inlined so that this call makes very little
       // performance impact if |surface_cache| is null.
       cache_ = surface_cache;
-      node_ = node;
       if (!cache_) {
         // If there is no cache, we have nothing to do besides indicate that we
         // are not recording and the surface for this node is not already
@@ -167,7 +189,7 @@
         state_ = kStateNotRecording;
         return;
       } else {
-        InitBlock();
+        InitBlock(node);
       }
     }
     ~Block() {
@@ -190,7 +212,7 @@
       kStateNotRecording,
       kStateInvalid
     };
-    void InitBlock();
+    void InitBlock(render_tree::Node* node);
     void ShutdownBlock();
 
     // If any blocks on the stack are performing any timing, this method
@@ -202,7 +224,7 @@
     State state_;
 
     SurfaceCache* cache_;
-    render_tree::Node* node_;
+    NodeMapKey key_;
 
     base::TimeTicks start_time_;
     NodeData* node_data_;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 51f3f6d..20ddfd5 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -16,6 +16,8 @@
 
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
 
+#include <algorithm>
+
 #include "base/debug/trace_event.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 #include "cobalt/renderer/rasterizer/common/surface_cache.h"
@@ -161,11 +163,14 @@
       new HardwareResourceProvider(graphics_context_, gr_context_));
   graphics_context_->ReleaseCurrentContext();
 
+  int max_surface_size = std::max(gr_context_->getMaxRenderTargetSize(),
+                                  gr_context_->getMaxTextureSize());
+  DLOG(INFO) << "Max renderer surface size: " << max_surface_size;
+
   if (surface_cache_size_in_bytes > 0) {
     surface_cache_delegate_.emplace(
         create_sk_surface_function,
-        math::Size(gr_context_->getMaxTextureSize(),
-                   gr_context_->getMaxTextureSize()));
+        math::Size(max_surface_size, max_surface_size));
 
     surface_cache_.emplace(&surface_cache_delegate_.value(),
                            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 2cb69c9..d79a244 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -17,6 +17,7 @@
 #include "cobalt/renderer/rasterizer/skia/hardware_resource_provider.h"
 
 #include "base/debug/trace_event.h"
+#include "base/synchronization/waitable_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/renderer/backend/egl/graphics_system.h"
 #include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
@@ -47,6 +48,18 @@
       font_manager_(SkFontMgr::RefDefault()),
       self_message_loop_(MessageLoop::current()) {}
 
+void HardwareResourceProvider::Finish() {
+  // Wait for any resource-related to complete (by waiting for all tasks to
+  // complete).
+  if (MessageLoop::current() != self_message_loop_) {
+    base::WaitableEvent completion(true, false);
+    self_message_loop_->PostTask(FROM_HERE,
+                                 base::Bind(&base::WaitableEvent::Signal,
+                                            base::Unretained(&completion)));
+    completion.Wait();
+  }
+}
+
 bool HardwareResourceProvider::PixelFormatSupported(
     render_tree::PixelFormat pixel_format) {
   return pixel_format == render_tree::kPixelFormatRGBA8;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 5e01f73..54a439d 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -39,6 +39,8 @@
   HardwareResourceProvider(backend::GraphicsContextEGL* cobalt_context,
                            GrContext* gr_context);
 
+  void Finish() OVERRIDE;
+
   bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
   bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
index 8a34aff..b0b493f 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -670,6 +670,10 @@
 
   draw_state_.render_target->restore();
 
+  if (surface_cache_delegate_) {
+    surface_cache_delegate_->UpdateCanvasScale();
+  }
+
 #if ENABLE_FLUSH_AFTER_EVERY_NODE
   draw_state_.render_target->flush();
 #endif
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 b0c4956..013d0dc 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
@@ -267,11 +267,6 @@
   }
 }
 
-void FontFileNameHandler(void* data, const char* s, int len) {
-  FamilyData* family_data = reinterpret_cast<FamilyData*>(data);
-  family_data->current_font_info->file_name.set(s, len);
-}
-
 void FontElementHandler(FontFileInfo* file, const char** attributes) {
   // A <font> should have weight (integer) and style (normal, italic)attributes.
   // The element should contain a filename.
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 478d9c6..21b6b4c 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
@@ -519,12 +519,12 @@
                      DefaultSingletonTraits<SkFontMgrCVals> >::get();
   }
 
-  const base::CVal<int32_t, base::CValPublic>&
+  const base::CVal<base::cval::SizeInBytes, base::CValPublic>&
   system_typeface_open_stream_cache_limit_in_bytes() {
     return system_typeface_open_stream_cache_limit_in_bytes_;
   }
 
-  base::CVal<int32_t, base::CValPublic>&
+  base::CVal<base::cval::SizeInBytes, base::CValPublic>&
   system_typeface_open_stream_cache_size_in_bytes() {
     return system_typeface_open_stream_cache_size_in_bytes_;
   }
@@ -540,9 +540,9 @@
             "Memory.SystemTypeface.Size", 0,
             "Total number of bytes currently used by the cache.") {}
 
-  const base::CVal<int32_t, base::CValPublic>
+  const base::CVal<base::cval::SizeInBytes, base::CValPublic>
       system_typeface_open_stream_cache_limit_in_bytes_;
-  mutable base::CVal<int32_t, base::CValPublic>
+  mutable base::CVal<base::cval::SizeInBytes, base::CValPublic>
       system_typeface_open_stream_cache_size_in_bytes_;
 
   friend struct DefaultSingletonTraits<SkFontMgrCVals>;
@@ -922,9 +922,10 @@
     // The cache size is not over the memory limit. No open streams are released
     // while the cache is under its limit. Simply break out.
     if (SkFontMgrCVals::GetInstance()
-            ->system_typeface_open_stream_cache_size_in_bytes() <
-        SkFontMgrCVals::GetInstance()
-            ->system_typeface_open_stream_cache_limit_in_bytes()) {
+            ->system_typeface_open_stream_cache_size_in_bytes()
+            .value() < SkFontMgrCVals::GetInstance()
+                           ->system_typeface_open_stream_cache_limit_in_bytes()
+                           .value()) {
       break;
     }
 
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index fd4419e..7be9105 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -34,6 +34,8 @@
  public:
   SoftwareResourceProvider();
 
+  void Finish() OVERRIDE{};
+
   bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
   bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc b/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc
index 598de2c..354a899 100644
--- a/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc
+++ b/src/cobalt/renderer/rasterizer/skia/surface_cache_delegate.cc
@@ -16,6 +16,7 @@
 
 #include "cobalt/renderer/rasterizer/skia/surface_cache_delegate.h"
 
+#include <algorithm>
 #include <string>
 
 #include "base/debug/trace_event.h"
@@ -143,12 +144,24 @@
     return;
   }
 
+#if !defined(NDEBUG)
+  // Ensure that the surface we ultimately render to is not larger than what
+  // we are reporting with GetRenderSize().
+  math::Size render_size = GetRenderSize(local_bounds.size());
+  DCHECK_LE(coord_mapping.output_bounds.width(), render_size.width());
+  DCHECK_LE(coord_mapping.output_bounds.height(), render_size.height());
+#endif
+
   // Now create a SkSurface and set it up as the new canvas for our client to
   // target.
   SkSurface* surface = create_sk_surface_function_.Run(
       math::Size(coord_mapping.output_bounds.width(),
                  coord_mapping.output_bounds.height()));
-  CHECK(surface);
+  if (!surface) {
+    LOG(WARNING) << "Could not create a " << coord_mapping.output_bounds.width()
+                 << "x" << coord_mapping.output_bounds.height() << ".";
+    NOTREACHED();
+  }
 
   recording_data_.emplace(*draw_state_, coord_mapping, surface);
 
@@ -189,8 +202,16 @@
 }
 
 math::Size SurfaceCacheDelegate::GetRenderSize(const math::SizeF& local_size) {
-  math::Size size(static_cast<int>(local_size.width() * scale_.x() + 0.5f),
-                  static_cast<int>(local_size.height() * scale_.y() + 0.5f));
+  // All surfaces scaled down would get rendered at their unscaled size, while
+  // surfaces scaled up will be rendered at their scaled size.  This is so that
+  // we can keep in the cache items that have animating scale, as long as the
+  // scale is less than 1 (which is common).  The cost is that we may use more
+  // memory than necessary for these down-scaled surfaces.
+  math::Vector2dF scale(std::max(1.0f, scale_.x()), std::max(1.0f, scale_.y()));
+
+  // Add 2.0f to account for any rounding out that may occur.
+  math::Size size(static_cast<int>(local_size.width() * scale.x() + 2.0f),
+                  static_cast<int>(local_size.height() * scale.y() + 2.0f));
   return size;
 }
 
diff --git a/src/cobalt/renderer/submission_queue.cc b/src/cobalt/renderer/submission_queue.cc
index 594861a..88debd1 100644
--- a/src/cobalt/renderer/submission_queue.cc
+++ b/src/cobalt/renderer/submission_queue.cc
@@ -31,8 +31,8 @@
     : max_queue_size_(max_queue_size),
       dispose_function_(dispose_function),
       to_submission_time_in_ms_(time_to_converge),
-      to_submission_time_in_ms_cval_(
-          "Renderer.ToSubmissionTimeInMS", 0,
+      to_submission_time_cval_(
+          "Renderer.ToSubmissionTime", base::TimeDelta(),
           "The current difference in milliseconds between the layout's clock "
           "and the renderer's clock.  The absolute value does not mean much, "
           "but how it changes as the user navigates can show how the "
@@ -176,8 +176,8 @@
 
   // Update our CVal tracking the current (smoothed) to_submission_time value
   // and the one tracking submission queue size.
-  to_submission_time_in_ms_cval_ =
-      to_submission_time_in_ms_.GetValueAtTime(time);
+  to_submission_time_cval_ = base::TimeDelta::FromMilliseconds(
+      to_submission_time_in_ms_.GetValueAtTime(time));
   queue_size_ = submission_queue_.size();
 }
 
diff --git a/src/cobalt/renderer/submission_queue.h b/src/cobalt/renderer/submission_queue.h
index 4b6ada6..987a57c 100644
--- a/src/cobalt/renderer/submission_queue.h
+++ b/src/cobalt/renderer/submission_queue.h
@@ -164,7 +164,7 @@
   // increasing.
   base::optional<base::TimeTicks> last_now_;
 
-  base::CVal<float> to_submission_time_in_ms_cval_;
+  base::CVal<base::TimeDelta> to_submission_time_cval_;
   base::CVal<size_t> queue_size_;
 };
 
diff --git a/src/cobalt/script/javascriptcore/jsc_engine.cc b/src/cobalt/script/javascriptcore/jsc_engine.cc
index 2555ddf..08cfcbb 100644
--- a/src/cobalt/script/javascriptcore/jsc_engine.cc
+++ b/src/cobalt/script/javascriptcore/jsc_engine.cc
@@ -63,13 +63,13 @@
 
   void Update() {
     base::AutoLock auto_lock(lock_);
-    if (js_engine_count_ > 0) {
+    if (js_engine_count_.value() > 0) {
       js_memory_ = OSAllocator::getCurrentBytesAllocated();
     }
   }
 
   base::Lock lock_;
-  base::CVal<size_t, base::CValPublic> js_memory_;
+  base::CVal<base::cval::SizeInBytes, base::CValPublic> js_memory_;
   base::CVal<size_t> js_engine_count_;
   scoped_ptr<base::PollerWithThread> poller_;
 };
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index 84a8d91..bf2648c 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -122,6 +122,7 @@
 
 MozjsGlobalEnvironment::MozjsGlobalEnvironment(JSRuntime* runtime)
     : context_(NULL),
+      garbage_collection_count_(0),
       cached_interface_data_deleter_(&cached_interface_data_),
       context_destructor_(&context_),
       environment_settings_(NULL),
@@ -311,19 +312,28 @@
 }
 
 void MozjsGlobalEnvironment::BeginGarbageCollection() {
-  DCHECK(!opaque_root_state_);
-  JSAutoRequest auto_request(context_);
-  JSAutoCompartment auto_comparment(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();
+  // It's possible that a GC could be triggered from within the
+  // BeginGarbageCollection callback. Only create the OpaqueRootState the first
+  // time we enter.
+  garbage_collection_count_++;
+  if (global_object_proxy_ && garbage_collection_count_ == 1) {
+    DCHECK(!opaque_root_state_);
+    JSAutoRequest auto_request(context_);
+    JSAutoCompartment auto_comparment(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();
+  }
 }
 
 void MozjsGlobalEnvironment::EndGarbageCollection() {
-  DCHECK(opaque_root_state_);
   // Reset opaque root reachability relationships.
-  opaque_root_state_.reset(NULL);
+  garbage_collection_count_--;
+  DCHECK_GE(garbage_collection_count_, 0);
+  if (garbage_collection_count_ == 0) {
+    opaque_root_state_.reset(NULL);
+  }
 }
 
 MozjsGlobalEnvironment* MozjsGlobalEnvironment::GetFromContext(
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h
index eb0d2eb..a774cb2 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -153,6 +153,7 @@
 
   base::ThreadChecker thread_checker_;
   JSContext* context_;
+  int garbage_collection_count_;
   WeakHeapObjectManager weak_object_manager_;
   CachedWrapperMultiMap kept_alive_objects_;
   scoped_ptr<ReferencedObjectMap> referenced_objects_;
diff --git a/src/cobalt/script/mozjs/opaque_root_tracker.cc b/src/cobalt/script/mozjs/opaque_root_tracker.cc
index 22bfc40..1ae8d32 100644
--- a/src/cobalt/script/mozjs/opaque_root_tracker.cc
+++ b/src/cobalt/script/mozjs/opaque_root_tracker.cc
@@ -89,29 +89,52 @@
   // Get the current opaque root for all objects that are being tracked.
   for (WrapperPrivateSet::iterator it = all_objects_.begin();
        it != all_objects_.end(); ++it) {
-    WrapperPrivate* reachable_object_wrapper_private = *it;
-    Wrappable* opaque_root = reachable_object_wrapper_private->GetOpaqueRoot();
-    if (opaque_root) {
-      WrapperPrivate* opaque_root_private = WrapperPrivate::GetFromWrappable(
-          opaque_root, context_, wrapper_factory_);
-      // Always mark the root as reachable from the non-root object.
-      state->TrackReachability(reachable_object_wrapper_private,
-                               opaque_root_private);
-
-      // Only mark the non-root object as reachable if we need to keep the
-      // wrapper alive for some reason. In general it's okay for a wrapper to
-      // get GC'd because the Cobalt object will still be kept alive, and a new
-      // JS object can be created if needed again.
-      if (reachable_object_wrapper_private
-              ->ShouldKeepWrapperAliveIfReachable()) {
-        state->TrackReachability(opaque_root_private,
-                                 reachable_object_wrapper_private);
-      }
-    }
+    WrapperPrivate* wrapper_private = *it;
+    TrackReachabilityToOpaqueRoot(state.get(), wrapper_private);
+    TrackReachableWrappables(state.get(), wrapper_private);
   }
   return state.PassAs<OpaqueRootState>();
 }
 
+void OpaqueRootTracker::TrackReachabilityToOpaqueRoot(
+    OpaqueRootState* state, WrapperPrivate* wrapper_private) {
+  OpaqueRootStateImpl* state_impl =
+      base::polymorphic_downcast<OpaqueRootStateImpl*>(state);
+  // If this wrappable has an opaque root, track reachability between this
+  // wrappable and its root.
+  Wrappable* opaque_root = wrapper_private->GetOpaqueRoot();
+  if (opaque_root) {
+    WrapperPrivate* opaque_root_private = WrapperPrivate::GetFromWrappable(
+        opaque_root, context_, wrapper_factory_);
+    // Always mark the root as reachable from the non-root object.
+    state_impl->TrackReachability(wrapper_private, opaque_root_private);
+
+    // Only mark the non-root object as reachable if we need to keep the
+    // wrapper alive for some reason. In general it's okay for a wrapper to
+    // get GC'd because the Cobalt object will still be kept alive, and a new
+    // JS object can be created if needed again.
+    if (wrapper_private->ShouldKeepWrapperAliveIfReachable()) {
+      state_impl->TrackReachability(opaque_root_private, wrapper_private);
+    }
+  }
+}
+
+void OpaqueRootTracker::TrackReachableWrappables(
+    OpaqueRootState* state, WrapperPrivate* wrapper_private) {
+  OpaqueRootStateImpl* state_impl =
+      base::polymorphic_downcast<OpaqueRootStateImpl*>(state);
+  // Track any wrappables that are explicitly marked as reachable from
+  // this wrappable.
+  typedef std::vector<Wrappable*> WrappableVector;
+  WrappableVector reachable_objects;
+  wrapper_private->GetReachableWrappables(&reachable_objects);
+  for (size_t i = 0; i < reachable_objects.size(); ++i) {
+    WrapperPrivate* reachable_object_private = WrapperPrivate::GetFromWrappable(
+        reachable_objects[i], context_, wrapper_factory_);
+    state_impl->TrackReachability(wrapper_private, reachable_object_private);
+  }
+}
+
 }  // namespace mozjs
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/mozjs/opaque_root_tracker.h b/src/cobalt/script/mozjs/opaque_root_tracker.h
index bd9ef2a..10517a6 100644
--- a/src/cobalt/script/mozjs/opaque_root_tracker.h
+++ b/src/cobalt/script/mozjs/opaque_root_tracker.h
@@ -73,6 +73,10 @@
   scoped_ptr<OpaqueRootState> GetCurrentOpaqueRootState();
 
  private:
+  void TrackReachabilityToOpaqueRoot(OpaqueRootState* state,
+                                     WrapperPrivate* wrapper_private);
+  void TrackReachableWrappables(OpaqueRootState* state,
+                                WrapperPrivate* wrapper_private);
   typedef base::hash_set<WrapperPrivate*> WrapperPrivateSet;
 
   JSContext* context_;
diff --git a/src/cobalt/script/mozjs/wrapper_private.cc b/src/cobalt/script/mozjs/wrapper_private.cc
index ad2113a..7dc1410 100644
--- a/src/cobalt/script/mozjs/wrapper_private.cc
+++ b/src/cobalt/script/mozjs/wrapper_private.cc
@@ -33,6 +33,13 @@
   return NULL;
 }
 
+void WrapperPrivate::GetReachableWrappables(
+    std::vector<Wrappable*>* reachable) {
+  if (!get_reachable_wrappables_function_.is_null()) {
+    return get_reachable_wrappables_function_.Run(wrappable_, reachable);
+  }
+}
+
 bool WrapperPrivate::ShouldKeepWrapperAliveIfReachable() {
   ProxyHandler* proxy_handler = base::polymorphic_downcast<ProxyHandler*>(
       js::GetProxyHandler(wrapper_proxy_));
@@ -45,10 +52,12 @@
 void WrapperPrivate::AddPrivateData(
     JSContext* context, JS::HandleObject wrapper_proxy,
     const scoped_refptr<Wrappable>& wrappable,
-    const GetOpaqueRootFunction& get_opaque_root_function) {
+    const GetOpaqueRootFunction& get_opaque_root_function,
+    const GetReachableWrappablesFunction& get_reachable_wrappables_function) {
   DCHECK(js::IsProxy(wrapper_proxy));
   WrapperPrivate* private_data = new WrapperPrivate(
-      context, wrappable, wrapper_proxy, get_opaque_root_function);
+      context, wrappable, wrapper_proxy, get_opaque_root_function,
+      get_reachable_wrappables_function);
   JS::RootedObject target_object(context,
                                  js::GetProxyTargetObject(wrapper_proxy));
   JS_SetPrivate(target_object, private_data);
@@ -135,13 +144,16 @@
 WrapperPrivate::WrapperPrivate(
     JSContext* context, const scoped_refptr<Wrappable>& wrappable,
     JS::HandleObject wrapper_proxy,
-    const GetOpaqueRootFunction& get_opaque_root_function)
+    const GetOpaqueRootFunction& get_opaque_root_function,
+    const GetReachableWrappablesFunction& get_reachable_wrappables_function)
     : context_(context),
       wrappable_(wrappable),
       wrapper_proxy_(wrapper_proxy),
-      get_opaque_root_function_(get_opaque_root_function) {
+      get_opaque_root_function_(get_opaque_root_function),
+      get_reachable_wrappables_function_(get_reachable_wrappables_function) {
   DCHECK(js::IsProxy(wrapper_proxy));
-  if (!get_opaque_root_function_.is_null()) {
+  if (!get_opaque_root_function_.is_null() ||
+      !get_reachable_wrappables_function_.is_null()) {
     MozjsGlobalEnvironment* global_environment =
         MozjsGlobalEnvironment::GetFromContext(context_);
     global_environment->opaque_root_tracker()->AddObjectWithOpaqueRoot(this);
@@ -149,7 +161,8 @@
 }
 
 WrapperPrivate::~WrapperPrivate() {
-  if (!get_opaque_root_function_.is_null()) {
+  if (!get_opaque_root_function_.is_null() ||
+      !get_reachable_wrappables_function_.is_null()) {
     MozjsGlobalEnvironment* global_environment =
         MozjsGlobalEnvironment::GetFromContext(context_);
     global_environment->opaque_root_tracker()->RemoveObjectWithOpaqueRoot(this);
diff --git a/src/cobalt/script/mozjs/wrapper_private.h b/src/cobalt/script/mozjs/wrapper_private.h
index 5bce933..d7ebfb1 100644
--- a/src/cobalt/script/mozjs/wrapper_private.h
+++ b/src/cobalt/script/mozjs/wrapper_private.h
@@ -16,6 +16,8 @@
 #ifndef COBALT_SCRIPT_MOZJS_WRAPPER_PRIVATE_H_
 #define COBALT_SCRIPT_MOZJS_WRAPPER_PRIVATE_H_
 
+#include <vector>
+
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
@@ -34,8 +36,11 @@
 // must be destroyed when its JSObject is garbage collected.
 class WrapperPrivate : public base::SupportsWeakPtr<WrapperPrivate> {
  public:
+  typedef std::vector<Wrappable*> WrappableVector;
   typedef base::Callback<Wrappable*(const scoped_refptr<Wrappable>&)>
       GetOpaqueRootFunction;
+  typedef base::Callback<void(const scoped_refptr<Wrappable>&,
+                              WrappableVector*)> GetReachableWrappablesFunction;
 
   template <typename T>
   scoped_refptr<T> wrappable() const {
@@ -45,6 +50,7 @@
   JSObject* js_object_proxy() const { return wrapper_proxy_; }
 
   Wrappable* GetOpaqueRoot() const;
+  void GetReachableWrappables(std::vector<Wrappable*>* reachable);
 
   // Return true if the GC should avoid collecting this wrapper. Note that if
   // the wrapper is unreachable, it may still be collected.
@@ -54,11 +60,13 @@
   static void AddPrivateData(
       JSContext* context, JS::HandleObject wrapper_proxy,
       const scoped_refptr<Wrappable>& wrappable,
-      const GetOpaqueRootFunction& get_opaque_root_function);
+      const GetOpaqueRootFunction& get_opaque_root_function,
+      const GetReachableWrappablesFunction& get_reachable_wrappables_function);
 
   static void AddPrivateData(JSContext* context, JS::HandleObject wrapper_proxy,
                              const scoped_refptr<Wrappable>& wrappable) {
-    AddPrivateData(context, wrapper_proxy, wrappable, GetOpaqueRootFunction());
+    AddPrivateData(context, wrapper_proxy, wrappable, GetOpaqueRootFunction(),
+                   GetReachableWrappablesFunction());
   }
 
   // Get the WrapperPrivate associated with the given Wrappable. A new JSObject
@@ -86,15 +94,18 @@
   static void Trace(JSTracer* trace, JSObject* object);
 
  private:
-  WrapperPrivate(JSContext* context, const scoped_refptr<Wrappable>& wrappable,
-                 JS::HandleObject wrapper_proxy,
-                 const GetOpaqueRootFunction& get_opaque_root_function);
+  WrapperPrivate(
+      JSContext* context, const scoped_refptr<Wrappable>& wrappable,
+      JS::HandleObject wrapper_proxy,
+      const GetOpaqueRootFunction& get_opaque_root_function,
+      const GetReachableWrappablesFunction& get_reachable_wrappables_function);
   ~WrapperPrivate();
 
   JSContext* context_;
   scoped_refptr<Wrappable> wrappable_;
   JS::Heap<JSObject*> wrapper_proxy_;
   GetOpaqueRootFunction get_opaque_root_function_;
+  GetReachableWrappablesFunction get_reachable_wrappables_function_;
 };
 
 }  // namespace mozjs
diff --git a/src/cobalt/webdriver/get_element_text_test.cc b/src/cobalt/webdriver/get_element_text_test.cc
index fd9881b..b9a87e9 100644
--- a/src/cobalt/webdriver/get_element_text_test.cc
+++ b/src/cobalt/webdriver/get_element_text_test.cc
@@ -43,8 +43,8 @@
       : css_parser_(css_parser::Parser::Create()),
         dom_stat_tracker_(new dom::DomStatTracker("GetElementTextTest")),
         html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
-                              "") {}
+                              NULL, NULL, NULL, NULL, NULL,
+                              dom_stat_tracker_.get(), "") {}
 
   void SetUp() OVERRIDE {
     dom::Document::Options options;
diff --git a/src/cobalt/webdriver/is_displayed_test.cc b/src/cobalt/webdriver/is_displayed_test.cc
index 8953ec5..37d366a 100644
--- a/src/cobalt/webdriver/is_displayed_test.cc
+++ b/src/cobalt/webdriver/is_displayed_test.cc
@@ -61,8 +61,9 @@
             NULL /* can_play_type_handler  */,
             NULL /* web_media_player_factory */, &script_runner_,
             NULL /* media_source_registry */, resource_provider_stub_.get(),
-            image_cache_.get(), NULL /* remote_font_cache */,
-            dom_stat_tracker_.get(), "" /* language */) {}
+            image_cache_.get(), NULL /* reduced_image_cache_capacity_manager */,
+            NULL /* remote_font_cache */, dom_stat_tracker_.get(),
+            "" /* language */) {}
 
   void SetUp() OVERRIDE {
     // Load the document in a nested message loop.
diff --git a/src/crypto/run_all_unittests.cc b/src/crypto/run_all_unittests.cc
index 4571f08..585601f 100644
--- a/src/crypto/run_all_unittests.cc
+++ b/src/crypto/run_all_unittests.cc
@@ -5,30 +5,17 @@
 #include "base/test/main_hook.h"
 #include "base/test/test_suite.h"
 #include "crypto/nss_util.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 
-#if !defined(OS_STARBOARD)
-int main(int argc, char** argv) {
-  MainHook hook(main, argc, argv);
-
+int TestSuiteRun(int argc, char** argv) {
+  MainHook hook(NULL, argc, argv);
 #if defined(USE_NSS)
   // This is most likely not needed, but it basically replaces a similar call
   // that was performed on test_support_base.
   // TODO(rvargas) Bug 79359: remove this.
   crypto::EnsureNSSInit();
 #endif  // defined(USE_NSS)
-
   return base::TestSuite(argc, argv).Run();
 }
-#else
-#include "starboard/event.h"
-#include "starboard/system.h"
 
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    MainHook hook(NULL, data->argument_count, data->argument_values);
-    SbSystemRequestStop(
-        base::TestSuite(data->argument_count, data->argument_values).Run());
-  }
-}
-#endif
+STARBOARD_WRAP_SIMPLE_MAIN(TestSuiteRun);
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc
index 4130b45..ead7b85 100644
--- a/src/media/base/sbplayer_pipeline.cc
+++ b/src/media/base/sbplayer_pipeline.cc
@@ -299,7 +299,7 @@
   bool video_read_in_progress_;
   TimeDelta duration_;
 
-  PipelineStatistics statistics_;
+  mutable PipelineStatistics statistics_;
 
   SbPlayer player_;
 
@@ -372,7 +372,9 @@
 
   if (SbPlayerIsValid(player_)) {
     set_bounds_caller_->SetPlayer(kSbPlayerInvalid);
+    DLOG(INFO) << "Destroying SbPlayer.";
     SbPlayerDestroy(player_);
+    DLOG(INFO) << "SbPlayer destroyed.";
     player_ = kSbPlayerInvalid;
   }
   // When Stop() is in progress, we no longer need to call |error_cb_|.
@@ -461,6 +463,8 @@
   }
   SbPlayerInfo info;
   SbPlayerGetInfo(player_, &info);
+  statistics_.video_frames_decoded = info.total_video_frames;
+  statistics_.video_frames_dropped = info.dropped_video_frames;
   return SbMediaTimeToTimeDelta(info.current_media_pts);
 }
 
diff --git a/src/media/player/web_media_player_impl.cc b/src/media/player/web_media_player_impl.cc
index 42a2f87..1defe0f 100644
--- a/src/media/player/web_media_player_impl.cc
+++ b/src/media/player/web_media_player_impl.cc
@@ -1161,8 +1161,10 @@
   // Make sure to kill the pipeline so there's no more media threads running.
   // Note: stopping the pipeline might block for a long time.
   base::WaitableEvent waiter(false, false);
+  DLOG(INFO) << "Trying to stop media pipeline.";
   pipeline_->Stop(
       base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
+  DLOG(INFO) << "Media pipeline stopped.";
   waiter.Wait();
 
   message_loop_factory_.reset();
diff --git a/src/nb/reuse_allocator_benchmark.cc b/src/nb/reuse_allocator_benchmark.cc
index 42e5c45..4117d5c 100644
--- a/src/nb/reuse_allocator_benchmark.cc
+++ b/src/nb/reuse_allocator_benchmark.cc
@@ -23,6 +23,7 @@
 #include "nb/allocator_decorator.h"
 #include "nb/fixed_no_free_allocator.h"
 #include "nb/reuse_allocator.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 #include "starboard/event.h"
 #include "starboard/file.h"
 #include "starboard/log.h"
@@ -238,10 +239,4 @@
 
 }  // namespace
 
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    SbSystemRequestStop(
-        BenchmarkMain(data->argument_count, data->argument_values));
-  }
-}
+STARBOARD_WRAP_SIMPLE_MAIN(BenchmarkMain);
diff --git a/src/nb/run_all_unittests.cc b/src/nb/run_all_unittests.cc
index de25a95..f100b11 100644
--- a/src/nb/run_all_unittests.cc
+++ b/src/nb/run_all_unittests.cc
@@ -12,15 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/event.h"
-#include "starboard/system.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    ::testing::InitGoogleTest(&data->argument_count, data->argument_values);
-    int result = RUN_ALL_TESTS();
-    SbSystemRequestStop(result);
-  }
+namespace {
+int InitAndRunAllTests(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
 }
+}  // namespace
+
+STARBOARD_WRAP_SIMPLE_MAIN(InitAndRunAllTests);
diff --git a/src/net/base/run_all_unittests.cc b/src/net/base/run_all_unittests.cc
index 2fc6fca..c08963c 100644
--- a/src/net/base/run_all_unittests.cc
+++ b/src/net/base/run_all_unittests.cc
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "crypto/nss_util.h"
 #include "net/base/net_test_suite.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 
 #if !__LB_ENABLE_NATIVE_HTTP_STACK__
 #include "net/socket/client_socket_pool_base.h"
@@ -27,8 +28,7 @@
 #endif
 
 int test_main(int argc, char** argv) {
-  MainHook hook(test_main, argc, argv);
-
+  MainHook hook(NULL, argc, argv);
 #if !defined(OS_STARBOARD)
   scoped_ptr<base::ObjectWatchMultiplexer> watcher(
       new base::ObjectWatchMultiplexer());
@@ -61,17 +61,4 @@
   return test_suite.Run();
 }
 
-#if !defined(OS_STARBOARD)
-int main(int argc, char** argv) {
-  return test_main(argc, argv);
-}
-#else
-#include "starboard/event.h"
-#include "starboard/system.h"
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    SbSystemRequestStop(test_main(data->argument_count, data->argument_values));
-  }
-}
-#endif
+STARBOARD_WRAP_SIMPLE_MAIN(test_main);
diff --git a/src/sql/run_all_unittests.cc b/src/sql/run_all_unittests.cc
index f63d4d5..ca6109b 100644
--- a/src/sql/run_all_unittests.cc
+++ b/src/sql/run_all_unittests.cc
@@ -5,28 +5,14 @@
 #include "base/test/main_hook.h"
 #include "base/test/test_suite.h"
 #include "sql/test_vfs.h"
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 
-#if !defined(OS_STARBOARD)
-int main(int argc, char** argv) {
-  MainHook hook(main, argc, argv);
+int TestSuiteRun(int argc, char** argv) {
+  MainHook hook(NULL, argc, argv);
   sql::RegisterTestVfs();
-  int result = base::TestSuite(argc, argv).Run();
+  int error_level = base::TestSuite(argc, argv).Run();
   sql::UnregisterTestVfs();
-  return result;
+  return error_level;
 }
-#else
-#include "starboard/event.h"
-#include "starboard/system.h"
 
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    MainHook hook(NULL, data->argument_count, data->argument_values);
-    sql::RegisterTestVfs();
-    int error_level =
-        base::TestSuite(data->argument_count, data->argument_values).Run();
-    sql::UnregisterTestVfs();
-    SbSystemRequestStop(error_level);
-  }
-}
-#endif
+STARBOARD_WRAP_SIMPLE_MAIN(TestSuiteRun);
diff --git a/src/starboard/client_porting/wrap_main/wrap_main.h b/src/starboard/client_porting/wrap_main/wrap_main.h
new file mode 100644
index 0000000..e4879d3
--- /dev/null
+++ b/src/starboard/client_porting/wrap_main/wrap_main.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This header defines two macros, COBALT_WRAP_SIMPLE_MAIN() and
+// COBALT_WRAP_BASE_MAIN(). Simple main is for programs that are
+// run-and-terminate, like unit tests. Base main is for programs that need a
+// main message loop, and will terminate when the quit_closure is called.
+
+#ifndef STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
+#define STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
+
+#if defined(STARBOARD)
+#include "starboard/event.h"
+#include "starboard/system.h"
+
+namespace starboard {
+namespace client_porting {
+namespace wrap_main {
+// A main-style function.
+typedef int (*MainFunction)(int argc, char** argv);
+
+template <MainFunction main_function>
+void SimpleEventHandler(const SbEvent* event) {
+  switch (event->type) {
+    case kSbEventTypeStart: {
+      SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
+      SbSystemRequestStop(
+          main_function(data->argument_count, data->argument_values));
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+}  // namespace wrap_main
+}  // namespace client_porting
+}  // namespace starboard
+
+#define STARBOARD_WRAP_SIMPLE_MAIN(main_function)                \
+  void SbEventHandle(const SbEvent* event) {                     \
+    ::starboard::client_porting::wrap_main::SimpleEventHandler<  \
+        main_function>(event);                                   \
+  }
+
+#else
+#define STARBOARD_WRAP_SIMPLE_MAIN(main_function) \
+  int main(int argc, char** argv) {               \
+    return main_function(argc, argv);             \
+  }
+
+#endif  // STARBOARD
+#endif  // STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
diff --git a/src/starboard/nplb/main.cc b/src/starboard/nplb/main.cc
index 170a452..69f61b6 100644
--- a/src/starboard/nplb/main.cc
+++ b/src/starboard/nplb/main.cc
@@ -12,15 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/client_porting/wrap_main/wrap_main.h"
 #include "starboard/event.h"
 #include "starboard/system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-void SbEventHandle(const SbEvent* event) {
-  if (event->type == kSbEventTypeStart) {
-    SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-    ::testing::InitGoogleTest(&data->argument_count, data->argument_values);
-    int result = RUN_ALL_TESTS();
-    SbSystemRequestStop(result);
-  }
+namespace {
+int InitAndRunAllTests(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
 }
+}  // namespace
+
+STARBOARD_WRAP_SIMPLE_MAIN(InitAndRunAllTests);
diff --git a/src/starboard/player.h b/src/starboard/player.h
index c897cc7..bc19c20 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -99,6 +99,18 @@
 
   // The current player volume in [0, 1].
   double volume;
+
+  // The number of video frames sent to the player since the creation of the
+  // player.
+  int total_video_frames;
+
+  // The number of video frames decoded but not displayed since the creation of
+  // the player.
+  int dropped_video_frames;
+
+  // The number of video frames that failed to be decoded since the creation of
+  // the player.
+  int corrupted_video_frames;
 } SbPlayerInfo;
 
 // An opaque handle to an implementation-private structure representing a
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index e14da96..2c7c9f6 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -49,6 +49,7 @@
       frame_height_(0),
       is_paused_(true),
       volume_(1.0),
+      total_video_frames_(0),
       worker_(this,
               window,
               video_codec,
@@ -80,6 +81,9 @@
     SbMediaTime sample_pts,
     const SbMediaVideoSampleInfo* video_sample_info,
     const SbDrmSampleInfo* sample_drm_info) {
+  if (sample_type == kSbMediaTypeVideo) {
+    ++total_video_frames_;
+  }
   InputBuffer* input_buffer = new InputBuffer(
       sample_deallocate_func_, this, context_, sample_buffer,
       sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);
@@ -115,6 +119,9 @@
   out_player_info->frame_height = frame_height_;
   out_player_info->is_paused = is_paused_;
   out_player_info->volume = volume_;
+  out_player_info->total_video_frames = total_video_frames_;
+  out_player_info->dropped_video_frames = 0;
+  out_player_info->corrupted_video_frames = 0;
 }
 
 void SbPlayerPrivate::SetPause(bool pause) {
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index 58b350d..7ecc299 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -69,6 +69,7 @@
   int frame_height_;
   bool is_paused_;
   double volume_;
+  int total_video_frames_;
 
   starboard::shared::starboard::player::PlayerWorker worker_;
 };