Import Cobalt 6.14838

Change-Id: I49864fe26f7f6fca3777d185823aa31251e8ae57
diff --git a/src/base/base.gypi b/src/base/base.gypi
index fde1a96..79b9dd4 100644
--- a/src/base/base.gypi
+++ b/src/base/base.gypi
@@ -661,7 +661,7 @@
               'threading/thread_checker_impl.cc',
             ],
             'conditions': [
-              ['target_os!="linux" and target_arch!="android"', {
+              ['target_os!="linux" and target_os!="android"', {
                 'sources!': [
                   # Since wchar_t is 2-bytes wide, string16 == wstring here.
                   'string16.cc',
diff --git a/src/base/debug/trace_event_impl.cc b/src/base/debug/trace_event_impl.cc
index 5af61e5..ac641c3 100644
--- a/src/base/debug/trace_event_impl.cc
+++ b/src/base/debug/trace_event_impl.cc
@@ -114,6 +114,7 @@
 
 TraceEvent::TraceEvent(int thread_id,
                        TimeTicks timestamp,
+                       TimeTicks thread_timestamp,
                        char phase,
                        const unsigned char* category_enabled,
                        const char* name,
@@ -124,6 +125,7 @@
                        const unsigned long long* arg_values,
                        unsigned char flags)
     : timestamp_(timestamp),
+      thread_timestamp_(thread_timestamp),
       id_(id),
       category_enabled_(category_enabled),
       name_(name),
@@ -242,6 +244,7 @@
 
 void TraceEvent::AppendAsJSON(std::string* out) const {
   int64 time_int64 = timestamp_.ToInternalValue();
+  int64 thread_time_int64 = thread_timestamp_.ToInternalValue();
   int process_id = TraceLog::GetInstance()->process_id();
   // Category name checked at category creation time.
   DCHECK(!strchr(name_, '"'));
@@ -266,6 +269,11 @@
   }
   *out += "}";
 
+  // add thread timestamp only if it was available
+  if (TimeTicks::HasThreadNow()) {
+    StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64);
+  }
+
   // If id_ is set, print it out as a hex string so we don't loose any
   // bits (it might be a 64-bit pointer).
   if (flags_ & TRACE_EVENT_FLAG_HAS_ID)
@@ -667,6 +675,7 @@
 #endif
 
   TimeTicks now = TimeTicks::NowFromSystemTraceTime() - time_offset_;
+  TimeTicks thread_now = TimeTicks::ThreadNow();
   NotificationHelper notifier(this);
   {
     AutoLock lock(lock_);
@@ -710,7 +719,7 @@
 
     logged_events_.push_back(
         TraceEvent(thread_id,
-                   now, phase, category_enabled, name, id,
+                   now, thread_now, phase, category_enabled, name, id,
                    num_args, arg_names, arg_types, arg_values,
                    flags));
 
@@ -795,7 +804,7 @@
       trace_event_internal::SetTraceValue(it->second, &arg_type, &arg_value);
       logged_events_.push_back(
           TraceEvent(it->first,
-                     TimeTicks(), TRACE_EVENT_PHASE_METADATA,
+                     TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA,
                      &g_category_enabled[g_category_metadata],
                      "thread_name", trace_event_internal::kNoEventId,
                      num_args, &arg_name, &arg_type, &arg_value,
diff --git a/src/base/debug/trace_event_impl.h b/src/base/debug/trace_event_impl.h
index b8b4110..6d373c7 100644
--- a/src/base/debug/trace_event_impl.h
+++ b/src/base/debug/trace_event_impl.h
@@ -64,6 +64,7 @@
   TraceEvent();
   TraceEvent(int thread_id,
              TimeTicks timestamp,
+             TimeTicks thread_timestamp,
              char phase,
              const unsigned char* category_enabled,
              const char* name,
@@ -87,6 +88,7 @@
                                 std::string* out);
 
   TimeTicks timestamp() const { return timestamp_; }
+  TimeTicks thread_timestamp() const { return thread_timestamp_; }
 
   // Exposed for unittesting:
 
@@ -107,6 +109,7 @@
  private:
   // Note: these are ordered by size (largest first) for optimal packing.
   TimeTicks timestamp_;
+  TimeTicks thread_timestamp_;
   // id_ can be used to store phase-specific data.
   unsigned long long id_;
   TraceValue arg_values_[kTraceMaxNumArgs];
diff --git a/src/base/file_util_proxy_unittest.cc b/src/base/file_util_proxy_unittest.cc
index cfa5023..dcd5ad8 100644
--- a/src/base/file_util_proxy_unittest.cc
+++ b/src/base/file_util_proxy_unittest.cc
@@ -39,10 +39,18 @@
   }
 
   void DidFinish(PlatformFileError error) {
-    error_ = error;
+    if (error_ == PLATFORM_FILE_OK) {
+      error_ = error;
+    }
     MessageLoop::current()->Quit();
   }
 
+  void NeedsMoreWork(PlatformFileError error) {
+    if (error_ == PLATFORM_FILE_OK) {
+      error_ = error;
+    }
+  }
+
   void DidCreateOrOpen(PlatformFileError error,
                        PassPlatformFile file,
                        bool created) {
@@ -379,10 +387,8 @@
   PlatformFile file = GetTestPlatformFile(PLATFORM_FILE_OPEN |
       PLATFORM_FILE_WRITE);
   FileUtilProxy::Truncate(
-      file_task_runner(),
-      file,
-      7,
-      Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+      file_task_runner(), file, 7,
+      Bind(&FileUtilProxyTest::NeedsMoreWork, weak_factory_.GetWeakPtr()));
   FileUtilProxy::Flush(
       file_task_runner(),
       file,
@@ -418,10 +424,8 @@
   PlatformFile file = GetTestPlatformFile(PLATFORM_FILE_OPEN |
       PLATFORM_FILE_WRITE);
   FileUtilProxy::Truncate(
-      file_task_runner(),
-      file,
-      53,
-      Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+      file_task_runner(), file, 53,
+      Bind(&FileUtilProxyTest::NeedsMoreWork, weak_factory_.GetWeakPtr()));
   FileUtilProxy::Flush(
       file_task_runner(),
       file,
diff --git a/src/base/threading/thread_local.h b/src/base/threading/thread_local.h
index 762d77b..fb6dfb2 100644
--- a/src/base/threading/thread_local.h
+++ b/src/base/threading/thread_local.h
@@ -73,7 +73,7 @@
 
   static void AllocateSlot(SlotType& slot);
   static void FreeSlot(SlotType& slot);
-  static void* GetValueFromSlot(SlotType& slot);
+  static void* GetValueFromSlot(const SlotType& slot);
   static void SetValueInSlot(SlotType& slot, void* value);
 };
 
@@ -90,7 +90,7 @@
     internal::ThreadLocalPlatform::FreeSlot(slot_);
   }
 
-  Type* Get() {
+  Type* Get() const {
     return static_cast<Type*>(
         internal::ThreadLocalPlatform::GetValueFromSlot(slot_));
   }
@@ -113,7 +113,7 @@
   ThreadLocalBoolean() { }
   ~ThreadLocalBoolean() { }
 
-  bool Get() {
+  bool Get() const {
     return tlp_.Get() != NULL;
   }
 
diff --git a/src/base/threading/thread_local_posix.cc b/src/base/threading/thread_local_posix.cc
index 4951006..1b381c2 100644
--- a/src/base/threading/thread_local_posix.cc
+++ b/src/base/threading/thread_local_posix.cc
@@ -25,7 +25,7 @@
 }
 
 // static
-void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+void* ThreadLocalPlatform::GetValueFromSlot(const SlotType& slot) {
   return pthread_getspecific(slot);
 }
 
diff --git a/src/base/threading/thread_local_starboard.cc b/src/base/threading/thread_local_starboard.cc
index e3a96d6..1234425 100644
--- a/src/base/threading/thread_local_starboard.cc
+++ b/src/base/threading/thread_local_starboard.cc
@@ -33,7 +33,7 @@
 }
 
 // static
-void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+void* ThreadLocalPlatform::GetValueFromSlot(const SlotType& slot) {
   return SbThreadGetLocalValue(slot);
 }
 
diff --git a/src/base/threading/thread_local_win.cc b/src/base/threading/thread_local_win.cc
index 56d3a3a..7e82c90 100644
--- a/src/base/threading/thread_local_win.cc
+++ b/src/base/threading/thread_local_win.cc
@@ -26,7 +26,7 @@
 }
 
 // static
-void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+void* ThreadLocalPlatform::GetValueFromSlot(const SlotType& slot) {
   return TlsGetValue(slot);
 }
 
diff --git a/src/base/time.h b/src/base/time.h
index 38bc53a..9fde0e4 100644
--- a/src/base/time.h
+++ b/src/base/time.h
@@ -593,6 +593,15 @@
   // SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED.
   static TimeTicks HighResNow();
 
+  // Returns the amount of time the current thread has spent in the execution
+  // state (i.e. not pre-empted or waiting on some event). If this is not
+  // available, then HighResNow() will be returned.
+  static TimeTicks ThreadNow();
+
+  // Returns whether ThreadNow() is implemented to report thread time (as
+  // opposed to HighResNow()).
+  static bool HasThreadNow();
+
   // Returns the current system trace time or, if none is defined, the current
   // high-res time (i.e. HighResNow()). On systems where a global trace clock
   // is defined, timestamping TraceEvents's with this value guarantees
diff --git a/src/base/time_mac.cc b/src/base/time_mac.cc
index 883a35b..327f9e6 100644
--- a/src/base/time_mac.cc
+++ b/src/base/time_mac.cc
@@ -192,6 +192,16 @@
 }
 
 // static
+TimeTicks TimeTicks::ThreadNow() {
+  return HighResNow();
+}
+
+// static
+bool TimeTicks::HasThreadNow() {
+  return false;
+}
+
+// static
 TimeTicks TimeTicks::NowFromSystemTraceTime() {
   return HighResNow();
 }
diff --git a/src/base/time_posix.cc b/src/base/time_posix.cc
index 80b1c4e..d053ff2 100644
--- a/src/base/time_posix.cc
+++ b/src/base/time_posix.cc
@@ -267,6 +267,27 @@
   return Now();
 }
 
+// static
+TimeTicks TimeTicks::ThreadNow() {
+  uint64_t absolute_micro;
+
+  struct timespec ts;
+  if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) {
+    return HighResNow();
+  }
+
+  absolute_micro =
+      (static_cast<int64>(ts.tv_sec) * Time::kMicrosecondsPerSecond) +
+      (static_cast<int64>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond);
+
+  return TimeTicks(absolute_micro);
+}
+
+// static
+bool TimeTicks::HasThreadNow() {
+  return true;
+}
+
 #if defined(OS_CHROMEOS)
 // Force definition of the system trace clock; it is a chromeos-only api
 // at the moment and surfacing it in the right place requires mucking
diff --git a/src/base/time_starboard.cc b/src/base/time_starboard.cc
index 2e7785e..0327166 100644
--- a/src/base/time_starboard.cc
+++ b/src/base/time_starboard.cc
@@ -97,6 +97,24 @@
 }
 
 // static
+TimeTicks TimeTicks::ThreadNow() {
+#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW)
+  return TimeTicks(SbTimeGetMonotonicThreadNow());
+#else
+  return HighResNow();
+#endif
+}
+
+// static
+bool TimeTicks::HasThreadNow() {
+#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW)
+  return true;
+#else
+  return false;
+#endif
+}
+
+// static
 TimeTicks TimeTicks::NowFromSystemTraceTime() {
   return HighResNow();
 }
diff --git a/src/base/time_win.cc b/src/base/time_win.cc
index 6d8e432..294a564 100644
--- a/src/base/time_win.cc
+++ b/src/base/time_win.cc
@@ -460,6 +460,16 @@
 }
 
 // static
+TimeTicks TimeTicks::ThreadNow() {
+  return HighResNow();
+}
+
+// static
+bool TimeTicks::HasThreadNow() {
+  return false;
+}
+
+// static
 TimeTicks TimeTicks::NowFromSystemTraceTime() {
   return HighResNow();
 }
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc
index ae86fb4..2aa7a62 100644
--- a/src/cobalt/audio/async_audio_decoder.cc
+++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -31,8 +31,8 @@
 void Decode(
     const uint8* audio_data, size_t size,
     const AsyncAudioDecoder::DecodeFinishCallback& decode_finish_callback) {
-  scoped_ptr<AudioFileReader> reader(
-      AudioFileReader::TryCreate(audio_data, size));
+  scoped_ptr<AudioFileReader> reader(AudioFileReader::TryCreate(
+      audio_data, size, GetPreferredOutputSampleType()));
 
   if (reader) {
     decode_finish_callback.Run(reader->sample_rate(),
diff --git a/src/cobalt/audio/audio_file_reader.cc b/src/cobalt/audio/audio_file_reader.cc
index ed61830..a858789 100644
--- a/src/cobalt/audio/audio_file_reader.cc
+++ b/src/cobalt/audio/audio_file_reader.cc
@@ -23,9 +23,10 @@
 
 // static
 scoped_ptr<AudioFileReader> AudioFileReader::TryCreate(const uint8* data,
-                                                       size_t size) {
+                                                       size_t size,
+                                                       SampleType sample_type) {
   // Try to create other type of audio file reader.
-  return AudioFileReaderWAV::TryCreate(data, size).Pass();
+  return AudioFileReaderWAV::TryCreate(data, size, sample_type).Pass();
 }
 
 }  // namespace audio
diff --git a/src/cobalt/audio/audio_file_reader.h b/src/cobalt/audio/audio_file_reader.h
index e1eacca..2d35a25 100644
--- a/src/cobalt/audio/audio_file_reader.h
+++ b/src/cobalt/audio/audio_file_reader.h
@@ -28,7 +28,8 @@
  public:
   virtual ~AudioFileReader() {}
 
-  static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size);
+  static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size,
+                                               SampleType sample_type);
 
   // Returns the sample data stored as float sample in planar form.  Note that
   // this function transfers the ownership of the data to the caller so it can
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index fad6ef0..4edc13f 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -46,15 +46,15 @@
 }  // namespace
 
 // static
-scoped_ptr<AudioFileReader> AudioFileReaderWAV::TryCreate(const uint8* data,
-                                                          size_t size) {
+scoped_ptr<AudioFileReader> AudioFileReaderWAV::TryCreate(
+    const uint8* data, size_t size, SampleType sample_type) {
   // Need at least the |kWAVChunkSize| bytes for this to be a WAV.
   if (size < kWAVChunkSize) {
     return scoped_ptr<AudioFileReader>();
   }
 
   scoped_ptr<AudioFileReaderWAV> audio_file_reader_wav(
-      new AudioFileReaderWAV(data, size));
+      new AudioFileReaderWAV(data, size, sample_type));
 
   if (!audio_file_reader_wav->is_valid()) {
     return scoped_ptr<AudioFileReader>();
@@ -63,8 +63,12 @@
   return make_scoped_ptr<AudioFileReader>(audio_file_reader_wav.release());
 }
 
-AudioFileReaderWAV::AudioFileReaderWAV(const uint8* data, size_t size)
-    : sample_rate_(0.f), number_of_frames_(0), number_of_channels_(0) {
+AudioFileReaderWAV::AudioFileReaderWAV(const uint8* data, size_t size,
+                                       SampleType sample_type)
+    : sample_rate_(0.f),
+      number_of_frames_(0),
+      number_of_channels_(0),
+      sample_type_(sample_type) {
   DCHECK_GE(size, kWAVRIFFChunkHeaderSize);
 
   if (ParseRIFFHeader(data, size)) {
@@ -179,7 +183,6 @@
       static_cast<int32>(is_sample_in_float ? sizeof(float) : sizeof(int16));
   number_of_frames_ =
       static_cast<int32>(size / (bytes_per_src_sample * number_of_channels_));
-  sample_type_ = GetPreferredOutputSampleType();
   const int32 bytes_per_dest_sample =
       static_cast<int32>(GetSampleTypeSize(sample_type_));
   const bool is_dest_float = sample_type_ == kSampleTypeFloat32;
diff --git a/src/cobalt/audio/audio_file_reader_wav.h b/src/cobalt/audio/audio_file_reader_wav.h
index 06e4883..dc3699a 100644
--- a/src/cobalt/audio/audio_file_reader_wav.h
+++ b/src/cobalt/audio/audio_file_reader_wav.h
@@ -27,7 +27,8 @@
 //   http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
 class AudioFileReaderWAV : public AudioFileReader {
  public:
-  static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size);
+  static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size,
+                                               SampleType sample_type);
 
   scoped_array<uint8> sample_data() OVERRIDE { return sample_data_.Pass(); }
   float sample_rate() const OVERRIDE { return sample_rate_; }
@@ -36,7 +37,7 @@
   SampleType sample_type() const OVERRIDE { return sample_type_; }
 
  private:
-  AudioFileReaderWAV(const uint8* data, size_t size);
+  AudioFileReaderWAV(const uint8* data, size_t size, SampleType sample_type);
 
   bool ParseRIFFHeader(const uint8* data, size_t size);
   void ParseChunks(const uint8* data, size_t size);
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 2f8beaf..0911d57 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -276,7 +276,8 @@
 #if defined(ENABLE_FAKE_MICROPHONE)
   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kFakeMicrophone) ||
       CommandLine::ForCurrentProcess()->HasSwitch(switches::kInputFuzzer)) {
-    options.dom_settings_options.enable_fake_microphone = true;
+    options.dom_settings_options.microphone_options.enable_fake_microphone =
+        true;
   }
 #endif  // defined(ENABLE_FAKE_MICROPHONE)
 
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index a9747b9..52a802c 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -59,6 +59,7 @@
         '<(DEPTH)/cobalt/samples/samples.gyp:*',
         '<(DEPTH)/cobalt/script/script.gyp:*',
         '<(DEPTH)/cobalt/script/engine.gyp:all_engines',
+        '<(DEPTH)/cobalt/speech/sandbox/sandbox.gyp:*',
         '<(DEPTH)/cobalt/speech/speech.gyp:*',
         '<(DEPTH)/cobalt/storage/storage.gyp:*',
         '<(DEPTH)/cobalt/trace_event/trace_event.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 4868c74..260d24f 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-14197
\ No newline at end of file
+14838
\ No newline at end of file
diff --git a/src/cobalt/build/save_build_id.py b/src/cobalt/build/save_build_id.py
index 1f79d60..63ab937 100755
--- a/src/cobalt/build/save_build_id.py
+++ b/src/cobalt/build/save_build_id.py
@@ -14,13 +14,14 @@
 # limitations under the License.
 """Calculates the current Build ID and writes it to 'build.id'."""
 
+import argparse
 import logging
 import os
 import sys
+import textwrap
 
 import gyp_utils
 
-
 _SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
 _BUILD_ID_PATH = gyp_utils.BUILD_ID_PATH
 
@@ -32,12 +33,28 @@
 def main():
   logging.basicConfig(level=logging.WARNING, format='%(message)s')
 
+  parser = argparse.ArgumentParser(
+      formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+      description=textwrap.dedent(__doc__))
+
+  parser.add_argument(
+      '--delete',
+      '-d',
+      action='store_true',
+      default=False,
+      help='Delete build.id file.')
+
+  options = parser.parse_args()
+
   # Update the build id to the latest, even if one is already set.
   try:
     os.unlink(_BUILD_ID_PATH)
   except OSError:
     pass
 
+  if options.delete:
+    return 0
+
   build_id = gyp_utils.GetBuildNumber()
   if not build_id:
     logging.error('Unable to retrieve build id.')
diff --git a/src/cobalt/content/ssl/certs/0a775a30.0 b/src/cobalt/content/ssl/certs/0a775a30.0
new file mode 100644
index 0000000..7d540a4
--- /dev/null
+++ b/src/cobalt/content/ssl/certs/0a775a30.0
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout
+736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A
+DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
+DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk
+fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA
+njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd
+-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/1001acf7.0 b/src/cobalt/content/ssl/certs/1001acf7.0
new file mode 100644
index 0000000..5496703
--- /dev/null
+++ b/src/cobalt/content/ssl/certs/1001acf7.0
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH
+MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
+QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy
+MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl
+cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM
+f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX
+mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7
+zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P
+fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc
+vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4
+Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp
+zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO
+Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW
+k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+
+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF
+lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW
+Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1
+d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z
+XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR
+gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3
+d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv
+J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg
+DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM
++SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy
+F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9
+SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws
+E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl
+-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/626dceaf.0 b/src/cobalt/content/ssl/certs/626dceaf.0
new file mode 100644
index 0000000..984f1d1
--- /dev/null
+++ b/src/cobalt/content/ssl/certs/626dceaf.0
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH
+MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
+QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy
+MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl
+cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv
+CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg
+GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu
+XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd
+re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu
+PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1
+mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K
+8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj
+x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR
+nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0
+kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok
+twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp
+8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT
+vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT
+z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA
+pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb
+pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB
+R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R
+RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk
+0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC
+5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF
+izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn
+yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC
+-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/a3418fda.0 b/src/cobalt/content/ssl/certs/a3418fda.0
new file mode 100644
index 0000000..07372d3
--- /dev/null
+++ b/src/cobalt/content/ssl/certs/a3418fda.0
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu
+hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l
+xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
+DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0
+CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx
+sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w==
+-----END CERTIFICATE-----
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc
index 0db0ac9..e0bb25a 100644
--- a/src/cobalt/dom/dom_settings.cc
+++ b/src/cobalt/dom/dom_settings.cc
@@ -29,7 +29,7 @@
     script::JavaScriptEngine* engine,
     script::GlobalEnvironment* global_environment, const Options& options)
     : max_dom_element_depth_(max_dom_element_depth),
-      enable_fake_microphone_(options.enable_fake_microphone),
+      microphone_options_(options.microphone_options),
       fetcher_factory_(fetcher_factory),
       network_module_(network_module),
       window_(window),
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h
index 492d21b..e2099c7 100644
--- a/src/cobalt/dom/dom_settings.h
+++ b/src/cobalt/dom/dom_settings.h
@@ -25,6 +25,7 @@
 #include "cobalt/dom/window.h"
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/script/environment_settings.h"
+#include "cobalt/speech/microphone.h"
 
 namespace cobalt {
 
@@ -46,10 +47,7 @@
  public:
   // Hold optional settings for DOMSettings.
   struct Options {
-    Options()
-        : array_buffer_allocator(NULL),
-          array_buffer_cache(NULL),
-          enable_fake_microphone(false) {}
+    Options() : array_buffer_allocator(NULL), array_buffer_cache(NULL) {}
 
     // ArrayBuffer allocates its memory on the heap by default and ArrayBuffers
     // may occupy a lot of memory.  It is possible to provide an allocator via
@@ -60,8 +58,8 @@
     // amount of ArrayBuffer inside main memory.  So we have provide the
     // following cache to manage ArrayBuffer in main memory.
     ArrayBuffer::Cache* array_buffer_cache;
-    // Use fake microphone if this flag is set to true.
-    bool enable_fake_microphone;
+    // Microphone options.
+    speech::Microphone::Options microphone_options;
   };
 
   DOMSettings(const int max_dom_element_depth,
@@ -77,7 +75,9 @@
   ~DOMSettings() OVERRIDE;
 
   int max_dom_element_depth() { return max_dom_element_depth_; }
-  bool enable_fake_microphone() const { return enable_fake_microphone_; }
+  const speech::Microphone::Options& microphone_options() const {
+    return microphone_options_;
+  }
 
   void set_window(const scoped_refptr<Window>& window) { window_ = window; }
   scoped_refptr<Window> window() const { return window_; }
@@ -115,7 +115,7 @@
 
  private:
   const int max_dom_element_depth_;
-  const bool enable_fake_microphone_;
+  const speech::Microphone::Options microphone_options_;
   loader::FetcherFactory* fetcher_factory_;
   network::NetworkModule* network_module_;
   scoped_refptr<Window> window_;
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index b94ee93..e4b5178 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -505,7 +505,9 @@
     return;
   }
 
-  TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::Execute()");
+  TRACE_EVENT2("cobalt::dom", "HTMLScriptElement::Execute()", "file_path",
+               script_location.file_path, "line_number",
+               script_location.line_number);
   // Since error is already handled, it is guaranteed the load is successful.
 
   // 1. 2. 3. Not needed by Cobalt.
diff --git a/src/cobalt/dom/url.cc b/src/cobalt/dom/url.cc
index 911c619..e7b256e 100644
--- a/src/cobalt/dom/url.cc
+++ b/src/cobalt/dom/url.cc
@@ -97,11 +97,18 @@
                        const char** data, size_t* size) {
   DCHECK(data);
   DCHECK(size);
+
+  *size = 0;
+  *data = NULL;
+
   dom::Blob* blob = registry->Retrieve(url.spec()).get();
 
   if (blob) {
     *size = static_cast<size_t>(blob->size());
-    *data = reinterpret_cast<const char*>(blob->data());
+
+    if (*size > 0) {
+      *data = reinterpret_cast<const char*>(blob->data());
+    }
 
     return true;
   } else {
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 4a57476..4426498 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -472,10 +472,17 @@
   // 'visibility: visible'.
   //   https://www.w3.org/TR/CSS21/visufx.html#propdef-visibility
   if (computed_style()->visibility() == cssom::KeywordValue::GetVisible()) {
-    RenderAndAnimateBackgroundColor(
-        padding_rounded_corners, &border_node_builder, &animate_node_builder);
-    RenderAndAnimateBackgroundImage(
-        padding_rounded_corners, &border_node_builder, &animate_node_builder);
+    RenderAndAnimateBackgroundImageResult background_image_result =
+        RenderAndAnimateBackgroundImage(padding_rounded_corners);
+    // If the background image is opaque, then it will occlude the background
+    // color and so we do not need to render the background color.
+    if (!background_image_result.is_opaque) {
+      RenderAndAnimateBackgroundColor(
+          padding_rounded_corners, &border_node_builder, &animate_node_builder);
+    }
+    if (background_image_result.node) {
+      border_node_builder.AddChild(background_image_result.node);
+    }
     RenderAndAnimateBorder(border_radius_provider.rounded_corners(),
                            &border_node_builder, &animate_node_builder);
     RenderAndAnimateBoxShadow(border_radius_provider.rounded_corners(),
@@ -1066,11 +1073,16 @@
   }
 }
 
-void Box::RenderAndAnimateBackgroundImage(
-    const base::optional<RoundedCorners>& rounded_corners,
-    CompositionNode::Builder* border_node_builder,
-    AnimateNode::Builder* animate_node_builder) {
-  UNREFERENCED_PARAMETER(animate_node_builder);
+Box::RenderAndAnimateBackgroundImageResult Box::RenderAndAnimateBackgroundImage(
+    const base::optional<RoundedCorners>& rounded_corners) {
+  RenderAndAnimateBackgroundImageResult result;
+  // We track a single render tree node because most of the time there will only
+  // be one.  If there is more, we set |single_node| to NULL and instead
+  // populate |composition|.  The code here tries to avoid using CompositionNode
+  // if possible to avoid constructing an std::vector.
+  scoped_refptr<render_tree::Node> single_node = NULL;
+  base::optional<CompositionNode::Builder> composition;
+  result.is_opaque = false;
 
   math::RectF image_frame(
       math::PointF(border_left_width().toFloat(), border_top_width().toFloat()),
@@ -1105,9 +1117,34 @@
         background_node = new FilterNode(filter_node_builder);
       }
 
-      border_node_builder->AddChild(background_node);
+      // If any of the background image layers are opaque, we set that the
+      // background image is opaque.  This is used to avoid setting up the
+      // background color if the background image is just going to cover it
+      // anyway.
+      result.is_opaque |= background_node_provider.is_opaque();
+
+      // If this is not the first node to return, then our |single_node|
+      // shortcut won't work, copy that single node into |composition| before
+      // continuing.
+      if (single_node) {
+        composition.emplace();
+        composition->AddChild(single_node);
+        single_node = NULL;
+      }
+      if (!composition) {
+        single_node = background_node;
+      } else {
+        composition->AddChild(background_node);
+      }
     }
   }
+
+  if (single_node) {
+    result.node = single_node;
+  } else if (composition) {
+    result.node = new CompositionNode(composition->Pass());
+  }
+  return result;
 }
 
 scoped_refptr<render_tree::Node> Box::RenderAndAnimateOpacity(
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 676b04d..60e9ccb 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -603,10 +603,17 @@
       const base::optional<render_tree::RoundedCorners>& rounded_corners,
       render_tree::CompositionNode::Builder* border_node_builder,
       render_tree::animations::AnimateNode::Builder* animate_node_builder);
-  void RenderAndAnimateBackgroundImage(
-      const base::optional<render_tree::RoundedCorners>& rounded_corners,
-      render_tree::CompositionNode::Builder* border_node_builder,
-      render_tree::animations::AnimateNode::Builder* animate_node_builder);
+  struct RenderAndAnimateBackgroundImageResult {
+    // The node representing the background image (may be a CompositionNode if
+    // there are multiple layers).
+    scoped_refptr<render_tree::Node> node;
+    // Returns whether the background image opaquely fills the entire frame.
+    // If true, then we don't need to even consider rendering the background
+    // color, since it will be occluded by the image.
+    bool is_opaque;
+  };
+  RenderAndAnimateBackgroundImageResult RenderAndAnimateBackgroundImage(
+      const base::optional<render_tree::RoundedCorners>& rounded_corners);
   void RenderAndAnimateBoxShadow(
       const base::optional<render_tree::RoundedCorners>& rounded_corners,
       render_tree::CompositionNode::Builder* border_node_builder,
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index f252231..26cc44d 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -566,7 +566,8 @@
       background_size_(background_size),
       background_position_(background_position),
       background_repeat_(background_repeat),
-      used_style_provider_(used_style_provider) {}
+      used_style_provider_(used_style_provider),
+      is_opaque_(false) {}
 
 void UsedBackgroundNodeProvider::VisitAbsoluteURL(
     cssom::AbsoluteURLValue* url_value) {
@@ -595,10 +596,16 @@
             &used_background_size_provider, &used_background_position_provider,
             &used_background_repeat_provider, frame_, single_image_size);
 
+    math::RectF image_rect(image_transform_data.composition_node_translation,
+                           image_transform_data.image_node_size);
+
+    is_opaque_ = used_background_image->IsOpaque() &&
+                 image_rect.x() <= frame_.x() && image_rect.y() <= frame_.y() &&
+                 image_rect.right() >= frame_.right() &&
+                 image_rect.bottom() >= frame_.bottom();
+
     background_node_ = new render_tree::ImageNode(
-        used_background_image,
-        math::RectF(image_transform_data.composition_node_translation,
-                    image_transform_data.image_node_size),
+        used_background_image, image_rect,
         image_transform_data.image_node_transform_matrix);
   }
 }
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h
index ca32a1c..8c964a1 100644
--- a/src/cobalt/layout/used_style.h
+++ b/src/cobalt/layout/used_style.h
@@ -134,6 +134,8 @@
     return background_node_;
   }
 
+  bool is_opaque() const { return is_opaque_; }
+
  private:
   const math::RectF frame_;
   const scoped_refptr<cssom::PropertyValue> background_size_;
@@ -143,6 +145,8 @@
 
   scoped_refptr<render_tree::Node> background_node_;
 
+  bool is_opaque_;
+
   DISALLOW_COPY_AND_ASSIGN(UsedBackgroundNodeProvider);
 };
 
diff --git a/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color-expected.png b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color-expected.png
new file mode 100644
index 0000000..6418185
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color.html b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color.html
new file mode 100644
index 0000000..49defbc
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!--
+ | Tests that opaque images render correctly and exercises an optimization
+ | code path where a background color does not need to be rendered if an
+ | opaque image covers an element's frame instead.
+ -->
+<html>
+<head>
+  <style>
+    div {
+      width: 300px;
+      height: 80px;
+      background-color: #f88;
+      background-repeat: no-repeat;
+    }
+  </style>
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    var image = new Image();
+    var image_name = 'cobalt_opaque.jpg';
+
+    image.onload = function() {
+      var image_elements = document.getElementsByClassName('image');
+      for (var i = 0; i < image_elements.length; ++i) {
+        image_elements[i].style.backgroundImage =
+            'url(' + image_name + ')';
+      }
+
+      if (window.testRunner) {
+        window.testRunner.notifyDone();
+      }
+    }
+
+    image.src = image_name;
+
+</script>
+</head>
+<body>
+  <div class='image' style="background-repeat: repeat;"></div>
+  <div class='image' style="width: 100px; background-size: cover;"></div>
+  <div class='image' style="background-size: contain;"></div>
+  <!-- Matches the image's dimensions exactly. -->
+  <div class='image'
+       style="width: 87px; height: 100px; background-size: contain;"></div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-background/cobalt_opaque.jpg b/src/cobalt/layout_tests/testdata/css3-background/cobalt_opaque.jpg
new file mode 100644
index 0000000..a1dc59e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/cobalt_opaque.jpg
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
index e5e5ccb..45cffd4 100644
--- a/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
@@ -27,6 +27,7 @@
 14-2-1-background-with-image-repeat-color-position
 14-2-1-background-with-image-repeat-position
 14-2-1-background-with-image-repeat-position-color
+14-2-1-background-with-opaque-image-color
 14-2-1-background-with-position-color
 14-2-1-background-with-position-color-image
 14-2-1-background-with-position-color-image-repeat
diff --git a/src/cobalt/loader/blob_fetcher.h b/src/cobalt/loader/blob_fetcher.h
index aa8f991..9b40c3f 100644
--- a/src/cobalt/loader/blob_fetcher.h
+++ b/src/cobalt/loader/blob_fetcher.h
@@ -26,10 +26,17 @@
 // For fetching the 'blob:' scheme.
 class BlobFetcher : public Fetcher {
  public:
-  // Returns true if the blob is succesfully fetched, and only then writes
-  // the address of the buffer and its size to |data| and |size|. |data| can
-  // be NULL when size is 0. This callback avoids a dependency from the
-  // fetcher to the actual blob implementation.
+  // This callback avoids a dependency from the fetcher to the actual blob
+  // implementation.
+  // If the blob is succesfully fetched:
+  //   1. Writes the size of its buffer to |size|.
+  //   2. If |size| > 0, then it also writes the address of the non-empty buffer
+  //      to |data|, otherwise writes NULL to |data|.
+  //   3. Returns true.
+  // If the fetch fails:
+  //   1. Writes 0 to |size|
+  //   2. Writes NULL to |data|
+  //   3. Returns false.
   typedef base::Callback<bool(const GURL& url, const char** data, size_t* size)>
       ResolverCallback;
 
diff --git a/src/cobalt/loader/blob_fetcher_test.cc b/src/cobalt/loader/blob_fetcher_test.cc
index a1e1fd6..e2baea3 100644
--- a/src/cobalt/loader/blob_fetcher_test.cc
+++ b/src/cobalt/loader/blob_fetcher_test.cc
@@ -43,11 +43,18 @@
   DCHECK(data);
   DCHECK(size);
 
+  *size = 0;
+  *data = NULL;
+
   TestRegistry::const_iterator match = registry.find(url.spec());
   if (match != registry.end()) {
     const std::vector<char>& buffer = match->second;
     *size = buffer.size();
-    *data = &buffer[0];
+
+    if (!buffer.empty()) {
+      *data = &buffer[0];
+    }
+
     return true;
   } else {
     return false;
diff --git a/src/cobalt/loader/image/dummy_gif_image_decoder.h b/src/cobalt/loader/image/dummy_gif_image_decoder.h
index 5c3775c..3ea5b38 100644
--- a/src/cobalt/loader/image/dummy_gif_image_decoder.h
+++ b/src/cobalt/loader/image/dummy_gif_image_decoder.h
@@ -38,11 +38,6 @@
   // From ImageDataDecoder
   std::string GetTypeString() const OVERRIDE { return "DummyGIFImageDecoder"; }
 
-  // Returns true if the signature is valid for the particular image type.
-  static bool IsValidSignature(const uint8* header) {
-    return !memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6);
-  }
-
  private:
   // From ImageDataDecoder
   size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
diff --git a/src/cobalt/loader/image/image_data_decoder.h b/src/cobalt/loader/image/image_data_decoder.h
index d97c329..ac19988 100644
--- a/src/cobalt/loader/image/image_data_decoder.h
+++ b/src/cobalt/loader/image/image_data_decoder.h
@@ -22,6 +22,9 @@
 
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/resource_provider.h"
+#if defined(STARBOARD)
+#include "starboard/decode_target.h"
+#endif
 
 namespace cobalt {
 namespace loader {
@@ -44,6 +47,23 @@
     return image_data_.Pass();
   }
 
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+  // Starboard version 3 adds support for hardware accelerated image decoding.
+  // In order to make use of this feature, subclasses of ImageDataDecoder may
+  // override this method in order to return an SbDecodeTarget, rather than a
+  // render_tree::ImageData, which could potentially save a copy from CPU
+  // memory to GPU memory, depending on the render_tree implementation and
+  // hardware image decoding functionality available.  If
+  // |RetrieveSbDecodeTarget| returns any value other than the default
+  // |kSbDecodeInvalid|, ImageDecoder will interpret it as a preference
+  // towards SbDecodeTarget rather than render_tree::ImageData.
+  virtual SbDecodeTarget RetrieveSbDecodeTarget() {
+    return kSbDecodeTargetInvalid;
+  }
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   void DecodeChunk(const uint8* data, size_t size);
   // Return true if decoding succeeded.
   bool FinishWithSuccess();
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 5161dd3..6fcf6ec 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -20,6 +20,7 @@
 
 #include "base/debug/trace_event.h"
 #include "cobalt/loader/image/dummy_gif_image_decoder.h"
+#include "cobalt/loader/image/image_decoder_starboard.h"
 #if defined(__LB_PS3__)
 #include "cobalt/loader/image/jpeg_image_decoder_ps3.h"
 #else  // defined(__LB_PS3__)
@@ -30,6 +31,9 @@
 #include "cobalt/loader/image/webp_image_decoder.h"
 #include "net/base/mime_util.h"
 #include "net/http/http_status_code.h"
+#if defined(STARBOARD)
+#include "starboard/image.h"
+#endif
 
 namespace cobalt {
 namespace loader {
@@ -49,6 +53,41 @@
   result->append(message);
 }
 
+// The various types of images we support decoding.
+enum ImageType {
+  kImageTypeInvalid,
+  kImageTypeGIF,
+  kImageTypeJPEG,
+  kImageTypePNG,
+  kImageTypeWebP,
+};
+
+// Determine the ImageType of an image from its signature.
+ImageType DetermineImageType(const uint8* header) {
+  if (!memcmp(header, "\xFF\xD8\xFF", 3)) {
+    return kImageTypeJPEG;
+  } else if (!memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6)) {
+    return kImageTypeGIF;
+  } else if (!memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8)) {
+    return kImageTypePNG;
+  } else if (!memcmp(header, "RIFF", 4) && !memcmp(header + 8, "WEBPVP", 6)) {
+    return kImageTypeWebP;
+  } else {
+    return kImageTypeInvalid;
+  }
+}
+
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+// clang-format off
+SbDecodeTargetFormat kPreferredFormats[] = {
+    kSbDecodeTargetFormat1PlaneRGBA,
+    kSbDecodeTargetFormat1PlaneBGRA,
+};
+// clang-format on
+#endif
+#endif
+
 }  // namespace
 
 ImageDecoder::ImageDecoder(render_tree::ResourceProvider* resource_provider,
@@ -91,9 +130,8 @@
     CacheMessage(&failure_message_, "No content returned.");
   }
 
-  std::string mime_style;
-  bool success = headers->GetMimeType(&mime_style);
-  if (!success || !net::IsSupportedImageMimeType(mime_style)) {
+  bool success = headers->GetMimeType(&mime_type_);
+  if (!success || !net::IsSupportedImageMimeType(mime_type_)) {
     state_ = kNotApplicable;
     CacheMessage(&failure_message_, "Not an image mime type.");
   }
@@ -118,11 +156,22 @@
     case kDecoding:
       DCHECK(decoder_);
       if (decoder_->FinishWithSuccess()) {
-        scoped_ptr<render_tree::ImageData> image_data =
-            decoder_->RetrieveImageData();
-        success_callback_.Run(
-            image_data ? resource_provider_->CreateImage(image_data.Pass())
-                       : NULL);
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+        SbDecodeTarget target = decoder_->RetrieveSbDecodeTarget();
+        if (SbDecodeTargetIsValid(target)) {
+          success_callback_.Run(
+              resource_provider_->CreateImageFromSbDecodeTarget(target));
+        } else  // NOLINT
+#endif
+#endif
+        {
+          scoped_ptr<render_tree::ImageData> image_data =
+              decoder_->RetrieveImageData();
+          success_callback_.Run(
+              image_data ? resource_provider_->CreateImage(image_data.Pass())
+                         : NULL);
+        }
       } else {
         error_callback_.Run(decoder_->GetTypeString() +
                             " failed to decode image.");
@@ -224,26 +273,50 @@
     return false;
   }
 
+  const ImageType image_type = DetermineImageType(signature_cache_.data);
+
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+  const char* mime_type_c_string = mime_type_.c_str();
+
+  // Find out if any of our preferred formats are supported for this mime
+  // type.
+  SbDecodeTargetFormat format = kSbDecodeTargetFormatInvalid;
+  for (size_t i = 0; i < SB_ARRAY_SIZE(kPreferredFormats); ++i) {
+    if (SbImageIsDecodeSupported(mime_type_c_string, kPreferredFormats[i])) {
+      format = kPreferredFormats[i];
+      break;
+    }
+  }
+
+  if (SbDecodeTargetIsFormatValid(format) &&
+      resource_provider_->SupportsSbDecodeTarget()) {
+    decoder_ = make_scoped_ptr<ImageDataDecoder>(new ImageDecoderStarboard(
+        resource_provider_, mime_type_c_string, format));
+    return true;
+  }
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   // Call different types of decoders by matching the image signature.
   if (s_use_stub_image_decoder) {
     decoder_ = make_scoped_ptr<ImageDataDecoder>(
         new StubImageDecoder(resource_provider_));
+  } else if (image_type == kImageTypeJPEG) {
 #if defined(__LB_PS3__)
-  } else if (JPEGImageDecoderPS3::IsValidSignature(signature_cache_.data)) {
     decoder_ = make_scoped_ptr<ImageDataDecoder>(
         new JPEGImageDecoderPS3(resource_provider_));
 #else   // defined(__LB_PS3__)
-  } else if (JPEGImageDecoder::IsValidSignature(signature_cache_.data)) {
     decoder_ = make_scoped_ptr<ImageDataDecoder>(
         new JPEGImageDecoder(resource_provider_));
 #endif  // defined(__LB_PS3__)
-  } else if (PNGImageDecoder::IsValidSignature(signature_cache_.data)) {
+  } else if (image_type == kImageTypePNG) {
     decoder_ = make_scoped_ptr<ImageDataDecoder>(
         new PNGImageDecoder(resource_provider_));
-  } else if (WEBPImageDecoder::IsValidSignature(signature_cache_.data)) {
+  } else if (image_type == kImageTypeWebP) {
     decoder_ = make_scoped_ptr<ImageDataDecoder>(
         new WEBPImageDecoder(resource_provider_));
-  } else if (DummyGIFImageDecoder::IsValidSignature(signature_cache_.data)) {
+  } else if (image_type == kImageTypeGIF) {
     decoder_ = make_scoped_ptr<ImageDataDecoder>(
         new DummyGIFImageDecoder(resource_provider_));
   } else {
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h
index 610d27b..00902bc 100644
--- a/src/cobalt/loader/image/image_decoder.h
+++ b/src/cobalt/loader/image/image_decoder.h
@@ -89,6 +89,7 @@
   SignatureCache signature_cache_;
   State state_;
   std::string failure_message_;
+  std::string mime_type_;
 };
 
 }  // namespace image
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc
new file mode 100644
index 0000000..5a90442
--- /dev/null
+++ b/src/cobalt/loader/image/image_decoder_starboard.cc
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#if defined(STARBOARD)
+
+#include "cobalt/loader/image/image_decoder_starboard.h"
+
+#include <algorithm>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "starboard/decode_target.h"
+#include "starboard/image.h"
+
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+ImageDecoderStarboard::ImageDecoderStarboard(
+    render_tree::ResourceProvider* resource_provider, const char* mime_type,
+    SbDecodeTargetFormat format)
+    : ImageDataDecoder(resource_provider),
+      mime_type_(mime_type),
+      format_(format),
+      provider_(resource_provider->GetSbDecodeTargetProvider()),
+      target_(kSbDecodeTargetInvalid) {}
+
+ImageDecoderStarboard::~ImageDecoderStarboard() {}
+
+size_t ImageDecoderStarboard::DecodeChunkInternal(const uint8* data,
+                                                  size_t input_byte) {
+  buffer_.insert(buffer_.end(), data, data + input_byte);
+  return input_byte;
+}
+
+void ImageDecoderStarboard::FinishInternal() {
+  DCHECK(!buffer_.empty());
+  DCHECK(SbImageIsDecodeSupported(mime_type_, format_));
+  target_ =
+      SbImageDecode(provider_, &buffer_[0], static_cast<int>(buffer_.size()),
+                    mime_type_, format_);
+  if (SbDecodeTargetIsValid(target_)) {
+    set_state(kDone);
+  } else {
+    set_state(kError);
+  }
+}
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+#endif  // #if defined(STARBOARD)
diff --git a/src/cobalt/loader/image/image_decoder_starboard.h b/src/cobalt/loader/image/image_decoder_starboard.h
new file mode 100644
index 0000000..ac14d4c
--- /dev/null
+++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
+#define COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
+
+#if defined(STARBOARD)
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/loader/image/image_data_decoder.h"
+#include "starboard/decode_target.h"
+
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+class ImageDecoderStarboard : public ImageDataDecoder {
+ public:
+  explicit ImageDecoderStarboard(
+      render_tree::ResourceProvider* resource_provider, const char* mime_type,
+      SbDecodeTargetFormat format);
+  ~ImageDecoderStarboard() OVERRIDE;
+
+  // From ImageDataDecoder
+  std::string GetTypeString() const OVERRIDE { return "ImageDecoderStarboard"; }
+
+  SbDecodeTarget RetrieveSbDecodeTarget() OVERRIDE { return target_; }
+
+ private:
+  // From ImageDataDecoder
+  size_t DecodeChunkInternal(const uint8* data, size_t size) OVERRIDE;
+  void FinishInternal() OVERRIDE;
+
+  const char* mime_type_;
+  SbDecodeTargetFormat format_;
+  std::vector<uint8> buffer_;
+  SbDecodeTargetProvider* provider_;
+  SbDecodeTarget target_;
+};
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+#endif  // defined(STARBOARD)
+
+#endif  // COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.h b/src/cobalt/loader/image/jpeg_image_decoder.h
index 9d8c487..242fd0e 100644
--- a/src/cobalt/loader/image/jpeg_image_decoder.h
+++ b/src/cobalt/loader/image/jpeg_image_decoder.h
@@ -41,11 +41,6 @@
   // From ImageDataDecoder
   std::string GetTypeString() const OVERRIDE { return "JPEGImageDecoder"; }
 
-  // Returns true if the signature is valid for the particular image type.
-  static bool IsValidSignature(const uint8* header) {
-    return !memcmp(header, "\xFF\xD8\xFF", 3);
-  }
-
  private:
   // From ImageDataDecoder
   size_t DecodeChunkInternal(const uint8* data, size_t size) OVERRIDE;
diff --git a/src/cobalt/loader/image/png_image_decoder.h b/src/cobalt/loader/image/png_image_decoder.h
index 29a3887..d270087 100644
--- a/src/cobalt/loader/image/png_image_decoder.h
+++ b/src/cobalt/loader/image/png_image_decoder.h
@@ -36,11 +36,6 @@
   // From ImageDataDecoder
   std::string GetTypeString() const OVERRIDE { return "PNGImageDecoder"; }
 
-  // Returns true if the signature is valid for the particular image type.
-  static bool IsValidSignature(const uint8* header) {
-    return !memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8);
-  }
-
  private:
   // From ImageDataDecoder
   size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
diff --git a/src/cobalt/loader/image/webp_image_decoder.h b/src/cobalt/loader/image/webp_image_decoder.h
index da9ce59..187ca62 100644
--- a/src/cobalt/loader/image/webp_image_decoder.h
+++ b/src/cobalt/loader/image/webp_image_decoder.h
@@ -34,11 +34,6 @@
   // From ImageDataDecoder
   std::string GetTypeString() const OVERRIDE { return "WEBPImageDecoder"; }
 
-  // Returns true if the signature is valid for the particular image type.
-  static bool IsValidSignature(const uint8* header) {
-    return !memcmp(header, "RIFF", 4) && !memcmp(header + 8, "WEBPVP", 6);
-  }
-
  private:
   // From ImageDataDecoder
   size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index b80a2cb..f3460d8 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -42,6 +42,8 @@
         'image/image_data_decoder.h',
         'image/image_decoder.cc',
         'image/image_decoder.h',
+        'image/image_decoder_starboard.cc',
+        'image/image_decoder_starboard.h',
         'image/jpeg_image_decoder.cc',
         'image/jpeg_image_decoder.h',
         'image/png_image_decoder.cc',
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc
index 1ed1376..efb946e 100644
--- a/src/cobalt/loader/net_fetcher.cc
+++ b/src/cobalt/loader/net_fetcher.cc
@@ -81,32 +81,33 @@
                        const Options& options)
     : Fetcher(handler),
       security_callback_(security_callback),
-      ALLOW_THIS_IN_INITIALIZER_LIST(start_callback_(
-          base::Bind(&NetFetcher::Start, base::Unretained(this)))) {
+      ALLOW_THIS_IN_INITIALIZER_LIST(csp_reject_callback_(
+          base::Bind(&NetFetcher::ProcessCSPReject, base::Unretained(this)))) {
   url_fetcher_.reset(
       net::URLFetcher::Create(url, options.request_method, this));
   url_fetcher_->SetRequestContext(network_module->url_request_context_getter());
   url_fetcher_->DiscardResponse();
 
-  // Delay the actual start until this function is complete. Otherwise we might
-  // call handler's callbacks at an unexpected time- e.g. receiving OnError()
-  // while a loader is still being constructed.
-  MessageLoop::current()->PostTask(FROM_HERE, start_callback_.callback());
-}
-
-void NetFetcher::Start() {
-  DCHECK(thread_checker_.CalledOnValidThread());
   const GURL& original_url = url_fetcher_->GetOriginalURL();
   if (security_callback_.is_null() ||
       security_callback_.Run(original_url, false /* did not redirect */)) {
     url_fetcher_->Start();
   } else {
-    std::string msg(base::StringPrintf("URL %s rejected by security policy.",
-                                       original_url.spec().c_str()));
-    return HandleError(msg).InvalidateThis();
+    // Delay the callback until this function is complete. Otherwise we might
+    // call handler's callbacks at an unexpected time- e.g. receiving OnError()
+    // while a loader is still being constructed.
+    MessageLoop::current()->PostTask(FROM_HERE,
+                                     csp_reject_callback_.callback());
   }
 }
 
+void NetFetcher::ProcessCSPReject() {
+  const GURL& original_url = url_fetcher_->GetOriginalURL();
+  std::string msg(base::StringPrintf("URL %s rejected by security policy.",
+                                     original_url.spec().c_str()));
+  return HandleError(msg).InvalidateThis();
+}
+
 void NetFetcher::OnURLFetchResponseStarted(const net::URLFetcher* source) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (source->GetURL() != source->GetOriginalURL()) {
@@ -162,7 +163,7 @@
 
 NetFetcher::~NetFetcher() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  start_callback_.Cancel();
+  csp_reject_callback_.Cancel();
 }
 
 NetFetcher::ReturnWrapper NetFetcher::HandleError(const std::string& message) {
diff --git a/src/cobalt/loader/net_fetcher.h b/src/cobalt/loader/net_fetcher.h
index ce816b5..1d6bc5d 100644
--- a/src/cobalt/loader/net_fetcher.h
+++ b/src/cobalt/loader/net_fetcher.h
@@ -59,7 +59,7 @@
   net::URLFetcher* url_fetcher() const { return url_fetcher_.get(); }
 
  private:
-  void Start();
+  void ProcessCSPReject();
 
   // Empty struct to ensure the caller of |HandleError()| knows that |this|
   // may have been destroyed and handles it appropriately.
@@ -81,9 +81,9 @@
   base::ThreadChecker thread_checker_;
   scoped_ptr<net::URLFetcher> url_fetcher_;
   csp::SecurityCallback security_callback_;
-  // Ensure we can cancel any in-flight Start() task if we are destroyed
-  // after being constructed, but before Start() runs.
-  base::CancelableClosure start_callback_;
+  // Ensure we can cancel any in-flight ProcessCSPReject() task if we are
+  // destroyed after being constructed, but before ProcessCSPReject() runs.
+  base::CancelableClosure csp_reject_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(NetFetcher);
 };
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
index 219a980..f51cba3 100644
--- a/src/cobalt/render_tree/mock_resource_provider.h
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -83,6 +83,20 @@
   scoped_refptr<Image> CreateImage(scoped_ptr<ImageData> pixel_data) {
     return scoped_refptr<Image>(CreateImageMock(pixel_data.get()));
   }
+
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+  scoped_refptr<Image> CreateImageFromSbDecodeTarget(SbDecodeTarget target) {
+    UNREFERENCED_PARAMETER(target);
+    return NULL;
+  }
+
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
+
+  bool SupportsSbDecodeTarget() { return false; }
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
                                                     size_t alignment) {
     return scoped_ptr<RawImageMemory>(
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index c1bd3cb..4590121 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -27,6 +27,9 @@
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/typeface.h"
+#if defined(STARBOARD)
+#include "starboard/decode_target.h"
+#endif  // defined(STARBOARD)
 
 namespace cobalt {
 namespace render_tree {
@@ -72,6 +75,23 @@
   virtual scoped_refptr<Image> CreateImage(
       scoped_ptr<ImageData> pixel_data) = 0;
 
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+  // This function will consume an SbDecodeTarget object produced by
+  // SbDecodeTargetCreate(), wrap it in a render_tree::Image that can be used
+  // in a render tree, and return it to the caller.
+  virtual scoped_refptr<Image> CreateImageFromSbDecodeTarget(
+      SbDecodeTarget target) = 0;
+
+  // Return the associated SbDecodeTargetProvider with the ResourceProvider,
+  // if it exists.  Returns NULL if SbDecodeTarget is not supported.
+  virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() = 0;
+
+  // Whether SbDecodeTargetIsSupported or not.
+  virtual bool SupportsSbDecodeTarget() = 0;
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   // Returns a raw chunk of memory that can later be passed into a function like
   // CreateMultiPlaneImageFromRawMemory() in order to create a texture.
   // If possible, the memory returned will be GPU memory that can be directly
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index bf0a8bd..d40787f 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -187,6 +187,21 @@
     return make_scoped_refptr(new ImageStub(skia_source_data.Pass()));
   }
 
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+  scoped_refptr<Image> CreateImageFromSbDecodeTarget(
+      SbDecodeTarget decode_target) OVERRIDE {
+    NOTREACHED();
+    SbDecodeTargetDestroy(decode_target);
+    return NULL;
+  }
+
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+
+  bool SupportsSbDecodeTarget() OVERRIDE { return false; }
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
                                                     size_t alignment) OVERRIDE {
     return scoped_ptr<RawImageMemory>(
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc
index bd82e11..af46382 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -69,6 +69,16 @@
                render_tree::kAlphaFormatOpaque;
 }
 
+SinglePlaneImage::SinglePlaneImage(SbBlitterSurface surface, bool is_opaque)
+    : surface_(surface), is_opaque_(is_opaque) {
+  CHECK(SbBlitterIsSurfaceValid(surface_));
+  SbBlitterSurfaceInfo info;
+  if (!SbBlitterGetSurfaceInfo(surface_, &info)) {
+    NOTREACHED();
+  }
+  size_ = math::Size(info.width, info.height);
+}
+
 bool SinglePlaneImage::EnsureInitialized() { return false; }
 
 const SkBitmap& SinglePlaneImage::GetBitmap() const {
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.h b/src/cobalt/renderer/rasterizer/blitter/image.h
index 949e3a2..65229ab 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.h
+++ b/src/cobalt/renderer/rasterizer/blitter/image.h
@@ -71,6 +71,7 @@
 class SinglePlaneImage : public skia::SinglePlaneImage {
  public:
   explicit SinglePlaneImage(scoped_ptr<ImageData> image_data);
+  SinglePlaneImage(SbBlitterSurface surface, bool is_opaque);
 
   const math::Size& GetSize() const OVERRIDE { return size_; }
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index f518c52..b23aa15 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -68,6 +68,31 @@
   return make_scoped_refptr(new SinglePlaneImage(blitter_source_data.Pass()));
 }
 
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+scoped_refptr<render_tree::Image>
+ResourceProvider::CreateImageFromSbDecodeTarget(SbDecodeTarget decode_target) {
+  SbDecodeTargetFormat format = SbDecodeTargetGetFormat(decode_target);
+  if (format == kSbDecodeTargetFormat1PlaneRGBA) {
+    SbBlitterSurface surface =
+        SbDecodeTargetGetPlane(decode_target, kSbDecodeTargetPlaneRGBA);
+    DCHECK(SbBlitterIsSurfaceValid(surface));
+    bool is_opaque = SbDecodeTargetIsOpaque(decode_target);
+
+    // Now that we have the surface it contained, we are free to delete
+    // |decode_target|.
+    SbDecodeTargetDestroy(decode_target);
+    return make_scoped_refptr(new SinglePlaneImage(surface, is_opaque));
+  }
+
+  NOTREACHED()
+      << "Only format kSbDecodeTargetFormat1PlaneRGBA is currently supported.";
+  SbDecodeTargetDestroy(decode_target);
+  return NULL;
+}
+
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+
 scoped_ptr<render_tree::RawImageMemory>
 ResourceProvider::AllocateRawImageMemory(size_t size_in_bytes,
                                          size_t alignment) {
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index a7edd28..c0ae7ab 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -25,6 +25,7 @@
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "starboard/blitter.h"
+#include "starboard/decode_target.h"
 
 #if SB_HAS(BLITTER)
 
@@ -42,6 +43,15 @@
 
   void Finish() OVERRIDE {}
 
+#if SB_VERSION(3)
+  scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
+      SbDecodeTarget decode_target) OVERRIDE;
+
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+
+  bool SupportsSbDecodeTarget() OVERRIDE { return true; }
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+
   bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
   bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index aaadf4b..2fd11a4 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -463,7 +463,7 @@
   TestTree(new ImageNode(image));
 }
 
-TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatNone) {
+TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatOpaque) {
   scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat(
       GetResourceProvider(), output_surface_size(),
       render_tree::kAlphaFormatOpaque);
@@ -471,7 +471,7 @@
   TestTree(new ImageNode(image));
 }
 
-TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners) {
+TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners) {
   scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat(
       GetResourceProvider(), output_surface_size(),
       render_tree::kAlphaFormatOpaque);
@@ -481,6 +481,35 @@
       new ImageNode(image)));
 }
 
+TEST_F(PixelTest,
+       SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor) {
+  scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat(
+      GetResourceProvider(), output_surface_size(),
+      render_tree::kAlphaFormatOpaque);
+
+  CompositionNode::Builder builder;
+  builder.AddChild(new RectNode(
+      RectF(output_surface_size()),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(0.0, 1.0, 0.0, 1)))));
+  builder.AddChild(new FilterNode(
+      ViewportFilter(RectF(25, 25, 150, 150), RoundedCorners(75, 75)),
+      new ImageNode(image)));
+  TestTree(new CompositionNode(builder.Pass()));
+}
+
+TEST_F(PixelTest, RectWithRoundedCornersOnSolidColor) {
+  CompositionNode::Builder builder;
+  builder.AddChild(new RectNode(
+      RectF(output_surface_size()),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(0.0, 1.0, 0.0, 1)))));
+  builder.AddChild(new FilterNode(
+      ViewportFilter(RectF(25, 25, 150, 150), RoundedCorners(75, 75)),
+      new RectNode(RectF(output_surface_size()),
+                   scoped_ptr<Brush>(
+                       new SolidColorBrush(ColorRGBA(0.0, 0.0, 1.0, 1))))));
+  TestTree(new CompositionNode(builder.Pass()));
+}
+
 TEST_F(PixelTest, SingleRGBAImageLargerThanRenderTarget) {
   // Tests that rasterizing images that are larger than the render target
   // work as expected (e.g. they are cropped).
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 8d85fea..1d18280 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -51,6 +51,27 @@
   scoped_refptr<render_tree::Image> CreateImage(
       scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
 
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+  scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
+      SbDecodeTarget decode_target) OVERRIDE {
+    NOTREACHED()
+        << "CreateImageFromSbDecodeTarget is not supported on EGL yet.";
+    SbDecodeTargetDestroy(decode_target);
+    return NULL;
+  }
+
+  // Return the associated SbDecodeTargetProvider with the ResourceProvider,
+  // if it exists.  Returns NULL if SbDecodeTarget is not supported.
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+
+  // Whether SbDecodeTargetIsSupported or not.
+  bool SupportsSbDecodeTarget() OVERRIDE { return false; }
+
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
       size_t size_in_bytes, size_t alignment) 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 06ed04b..9e2116d 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -189,7 +189,7 @@
 }
 
 void ApplyViewportMask(
-    SkCanvas* canvas,
+    RenderTreeNodeVisitorDrawState* draw_state,
     const base::optional<render_tree::ViewportFilter>& filter) {
   if (!filter) {
     return;
@@ -197,11 +197,12 @@
 
   if (!filter->has_rounded_corners()) {
     SkRect filter_viewport(CobaltRectFToSkiaRect(filter->viewport()));
-    canvas->clipRect(filter_viewport);
+    draw_state->render_target->clipRect(filter_viewport);
   } else {
-    canvas->clipPath(
+    draw_state->render_target->clipPath(
         RoundedRectToSkiaPath(filter->viewport(), filter->rounded_corners()),
         SkRegion::kIntersect_Op, true /* doAntiAlias */);
+    draw_state->clip_is_rect = false;
   }
 }
 
@@ -287,7 +288,7 @@
   // the offscreen surface, so reset the scale for now.
   draw_state_.render_target->save();
   ApplyBlurFilterToPaint(&paint, filter_node.blur_filter);
-  ApplyViewportMask(draw_state_.render_target, filter_node.viewport_filter);
+  ApplyViewportMask(&draw_state_, filter_node.viewport_filter);
 
   draw_state_.render_target->setMatrix(CobaltMatrixToSkia(
       math::TranslateMatrix(coord_mapping.output_pre_translate) * total_matrix *
@@ -452,13 +453,17 @@
     RenderTreeNodeVisitorDrawState original_draw_state(draw_state_);
 
     draw_state_.render_target->save();
-    ApplyViewportMask(draw_state_.render_target,
-                      filter_node->data().viewport_filter);
+    // Remember the value of |clip_is_rect| because ApplyViewportMask may
+    // modify it.
+    bool clip_was_rect = draw_state_.clip_is_rect;
+    ApplyViewportMask(&draw_state_, filter_node->data().viewport_filter);
 
     if (filter_node->data().opacity_filter) {
       draw_state_.opacity *= filter_node->data().opacity_filter->opacity();
     }
     filter_node->data().source->Accept(this);
+
+    draw_state_.clip_is_rect = clip_was_rect;
     draw_state_.render_target->restore();
     draw_state_ = original_draw_state;
   } else {
@@ -511,7 +516,7 @@
 
   if (draw_state.opacity < 1.0f) {
     paint.setAlpha(draw_state.opacity * 255);
-  } else if (is_opaque) {
+  } else if (is_opaque && draw_state.clip_is_rect) {
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
   }
 
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h
index 01a5d62..d3cb0fe 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h
@@ -26,10 +26,14 @@
 
 struct RenderTreeNodeVisitorDrawState {
   explicit RenderTreeNodeVisitorDrawState(SkCanvas* render_target)
-      : render_target(render_target), opacity(1.0f) {}
+      : render_target(render_target), opacity(1.0f), clip_is_rect(true) {}
 
   SkCanvas* render_target;
   float opacity;
+
+  // True if the current clip is a rectangle or not.  If it is not, we need
+  // to enable blending when rendering clipped rectangles.
+  bool clip_is_rect;
 };
 
 }  // namespace skia
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index 6b979ce..9528e22 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -46,6 +46,21 @@
   scoped_refptr<render_tree::Image> CreateImage(
       scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
 
+#if defined(STARBOARD)
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+  scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
+      SbDecodeTarget decode_target) OVERRIDE {
+    NOTREACHED();
+    SbDecodeTargetDestroy(decode_target);
+    return NULL;
+  }
+
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+
+  bool SupportsSbDecodeTarget() OVERRIDE { return false; }
+#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // defined(STARBOARD)
+
   scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
       size_t size_in_bytes, size_t alignment) OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png b/src/cobalt/renderer/rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png
new file mode 100644
index 0000000..e72e8d1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png
new file mode 100644
index 0000000..b8638eb
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png
new file mode 100644
index 0000000..2076087
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png
new file mode 100644
index 0000000..6251d5f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png
Binary files differ
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp
index 6e856f5..7e833e4 100644
--- a/src/cobalt/script/mozjs/mozjs.gyp
+++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -26,6 +26,7 @@
         'mozjs_global_environment.cc',
         'mozjs_property_enumerator.cc',
         'mozjs_source_code.cc',
+        'mozjs_trace_logging.cc',
         'opaque_root_tracker.cc',
         'proxy_handler.cc',
         'referenced_object_map.cc',
diff --git a/src/cobalt/script/mozjs/mozjs_trace_logging.cc b/src/cobalt/script/mozjs/mozjs_trace_logging.cc
new file mode 100644
index 0000000..2e11ce8
--- /dev/null
+++ b/src/cobalt/script/mozjs/mozjs_trace_logging.cc
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+#include "third_party/mozjs/js/src/TraceLogging.h"
+
+#include "base/debug/trace_event.h"
+#include "base/memory/singleton.h"
+
+namespace js {
+
+namespace {
+
+// Container class to transform the 3rd-party-declared TraceLogging class
+// into our Singleton type.
+class TraceLoggingContainer {
+ public:
+  TraceLoggingContainer() {}
+
+  static TraceLoggingContainer* GetInstance() {
+    return Singleton<TraceLoggingContainer,
+      StaticMemorySingletonTraits<TraceLoggingContainer> >::get();
+  }
+
+  TraceLogging* GetLogger() {
+    return &logger_;
+  }
+
+ private:
+  TraceLogging logger_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceLoggingContainer);
+};
+
+const char kTraceLoggingCategory[] = "JavaScript";
+
+}  // namespace
+
+const char* const TraceLogging::type_name[] = {
+  "IonCompile",
+  "IonCompile",
+  "IonCannon",
+  "IonCannon",
+  "IonCannon",
+  "IonSideCannon",
+  "IonSideCannon",
+  "IonSideCannon",
+  "YarrJIT",
+  "YarrJIT",
+  "JMSafepoint",
+  "JMSafepoint",
+  "JMNormal",
+  "JMNormal",
+  "JMCompile",
+  "JMCompile",
+  "GarbageCollect",
+  "GarbageCollect",
+  "Interpreter",
+  "Interpreter",
+  "Info",
+};
+
+// None of the member variables will be needed. Instead, logging will be
+// funneled to the trace event system.
+TraceLogging::TraceLogging() {
+}
+
+TraceLogging::~TraceLogging() {
+}
+
+// static
+TraceLogging* TraceLogging::defaultLogger() {
+  return TraceLoggingContainer::GetInstance()->GetLogger();
+}
+
+// static
+void TraceLogging::releaseDefaultLogger() {
+  // Do nothing. Lifetime is managed by the TraceLoggingContainer singleton.
+}
+
+void TraceLogging::log(Type type, const char* filename, unsigned int line) {
+  // Unfortunately, we don't have access to the enum, so can't declare a
+  // "count"-type enum. Instead, INFO is assumed to be the last enumeration.
+  COMPILE_ASSERT(ARRAYSIZE_UNSAFE(type_name)-1 == Type::INFO, array_mismatch);
+
+  switch (type) {
+    // "Start" types
+    case ION_COMPILE_START:
+    case ION_CANNON_START:
+    case ION_SIDE_CANNON_START:
+    case YARR_JIT_START:
+    case JM_SAFEPOINT_START:
+    case JM_START:
+    case JM_COMPILE_START:
+    case GC_START:
+    case INTERPRETER_START:
+      if (filename != NULL) {
+        TRACE_EVENT_BEGIN2(kTraceLoggingCategory, type_name[type],
+                          "file", TRACE_STR_COPY(filename),
+                          "line", line);
+      } else {
+        TRACE_EVENT_BEGIN0(kTraceLoggingCategory, type_name[type]);
+      }
+      break;
+
+    // Ignored types
+    case INFO:
+      break;
+
+    // "Stop" types
+    default:
+      TRACE_EVENT_END0(kTraceLoggingCategory, type_name[type]);
+      break;
+  }
+}
+
+void TraceLogging::log(Type type, JSScript* script) {
+  log(type, script->filename(), script->lineno);
+}
+
+void TraceLogging::log(const char* log) {
+  UNREFERENCED_PARAMETER(log);
+}
+
+void TraceLogging::log(Type type) {
+  log(type, NULL, 0);
+}
+
+void TraceLogging::flush() {
+}
+
+// Helper functions declared in TraceLogging.h
+void TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script) {
+  logger->log(type, script);
+}
+
+void TraceLog(TraceLogging* logger, const char* log) {
+  logger->log(log);
+}
+
+void TraceLog(TraceLogging* logger, TraceLogging::Type type) {
+  logger->log(type);
+}
+
+}  // namespace js
diff --git a/src/cobalt/speech/endpointer/endpointer.cc b/src/cobalt/speech/endpointer/endpointer.cc
index 0b19a22..cabad1a 100644
--- a/src/cobalt/speech/endpointer/endpointer.cc
+++ b/src/cobalt/speech/endpointer/endpointer.cc
@@ -93,7 +93,6 @@
 EpStatus Endpointer::ProcessAudio(
     const ShellAudioBus& audio_bus, float* rms_out) {
   DCHECK_EQ(audio_bus.channels(), 1);
-  DCHECK_LE(kFrameSize, static_cast<int>(audio_bus.frames()));
 
   const size_t num_samples = audio_bus.frames();
   const int16_t* audio_data = NULL;
diff --git a/src/cobalt/speech/microphone.h b/src/cobalt/speech/microphone.h
index 3b9e6ae..4c0d87b 100644
--- a/src/cobalt/speech/microphone.h
+++ b/src/cobalt/speech/microphone.h
@@ -19,7 +19,9 @@
 
 #include <string>
 
+#include "base/file_path.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
 
 namespace cobalt {
 namespace speech {
@@ -27,6 +29,24 @@
 // An abstract class is used for interacting platform specific microphone.
 class Microphone {
  public:
+  struct Options {
+    Options()
+        : enable_fake_microphone(false),
+          external_audio_data(NULL),
+          audio_data_size(0) {}
+
+    // Use fake microphone if this flag is set to true.
+    bool enable_fake_microphone;
+    // The following members are only applicable to the fake microphone.
+    //
+    // External audio input data for microphone input.
+    const uint8* external_audio_data;
+    // Audio data size.
+    int audio_data_size;
+    // Input file path.
+    base::optional<FilePath> file_path;
+  };
+
   virtual ~Microphone() {}
 
   // Opens microphone port and starts recording audio.
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
index 7ee601c..0dad830 100644
--- a/src/cobalt/speech/microphone_fake.cc
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -23,6 +23,7 @@
 #include "base/file_util.h"
 #include "base/path_service.h"
 #include "base/rand_util.h"
+#include "cobalt/audio/audio_file_reader.h"
 #include "starboard/file.h"
 #include "starboard/memory.h"
 #include "starboard/time.h"
@@ -31,14 +32,14 @@
 namespace speech {
 
 namespace {
-const int kMaxBufferSize = 512 * 1024;
+const int kMaxBufferSize = 1024 * 1024;
 const int kMinMicrophoneReadInBytes = 1024;
 // The possiblity of microphone creation failed is 1/20.
 const int kCreationRange = 20;
 // The possiblity of microphone open failed is 1/20.
 const int kOpenRange = 20;
-// The possiblity of microphone read failed is 1/200.
-const int kReadRange = 200;
+// The possiblity of microphone read failed is 1/300.
+const int kReadRange = 300;
 // The possiblity of microphone close failed is 1/20.
 const int kCloseRange = 20;
 const int kFailureNumber = 5;
@@ -49,8 +50,9 @@
 
 }  // namespace
 
-MicrophoneFake::MicrophoneFake()
+MicrophoneFake::MicrophoneFake(const Options& options)
     : Microphone(),
+      read_data_from_file_(options.audio_data_size == 0),
       file_length_(-1),
       read_index_(0),
       is_valid_(!ShouldFail(kCreationRange)) {
@@ -59,46 +61,91 @@
     return;
   }
 
-  FilePath audio_files_path;
-  SB_CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &audio_files_path));
-  audio_files_path = audio_files_path.Append(FILE_PATH_LITERAL("cobalt"))
-                         .Append(FILE_PATH_LITERAL("speech"))
-                         .Append(FILE_PATH_LITERAL("testdata"));
+  if (read_data_from_file_) {
+    if (options.file_path) {
+      SB_DCHECK(!options.file_path->empty());
+      // External input file.
+      file_paths_.push_back(options.file_path.value());
+    } else {
+      FilePath audio_files_path;
+      SB_CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &audio_files_path));
+      audio_files_path = audio_files_path.Append(FILE_PATH_LITERAL("cobalt"))
+                             .Append(FILE_PATH_LITERAL("speech"))
+                             .Append(FILE_PATH_LITERAL("testdata"));
 
-  file_util::FileEnumerator file_enumerator(audio_files_path,
-                                            false /* Not recursive */,
-                                            file_util::FileEnumerator::FILES);
-  for (FilePath next = file_enumerator.Next(); !next.empty();
-       next = file_enumerator.Next()) {
-    file_paths_.push_back(next);
+      file_util::FileEnumerator file_enumerator(
+          audio_files_path, false /* Not recursive */,
+          file_util::FileEnumerator::FILES);
+      for (FilePath next = file_enumerator.Next(); !next.empty();
+           next = file_enumerator.Next()) {
+        file_paths_.push_back(next);
+      }
+    }
+  } else {
+    file_length_ = std::min(options.audio_data_size, kMaxBufferSize);
+    SB_DCHECK(file_length_ > 0);
+    file_buffer_.reset(new uint8[file_length_]);
+    SbMemoryCopy(file_buffer_.get(), options.external_audio_data, file_length_);
   }
 }
 
 bool MicrophoneFake::Open() {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   if (ShouldFail(kOpenRange)) {
     SB_DLOG(WARNING) << "Mocking microphone open failed.";
     return false;
   }
 
-  SB_DCHECK(file_paths_.size() != 0);
-  uint64 random_index = base::RandGenerator(file_paths_.size());
-  starboard::ScopedFile file(file_paths_[random_index].value().c_str(),
-                             kSbFileOpenOnly | kSbFileRead, NULL, NULL);
-  SB_DCHECK(file.IsValid());
-  int file_buffer_size =
-      std::min(static_cast<int>(file.GetSize()), kMaxBufferSize);
-  SB_DCHECK(file_buffer_size > 0);
-  file_buffer_.reset(new char[file_buffer_size]);
-  int read_bytes = file.ReadAll(file_buffer_.get(), file_buffer_size);
-  if (read_bytes < 0) {
-    return false;
-  }
+  if (read_data_from_file_) {
+    // Read from local files.
+    SB_DCHECK(file_paths_.size() != 0);
+    uint64 random_index = base::RandGenerator(file_paths_.size());
+    starboard::ScopedFile file(file_paths_[random_index].value().c_str(),
+                               kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+    SB_DCHECK(file.IsValid());
+    int file_buffer_size =
+        std::min(static_cast<int>(file.GetSize()), kMaxBufferSize);
+    SB_DCHECK(file_buffer_size > 0);
 
-  file_length_ = read_bytes;
+    scoped_array<char> audio_input(new char[file_buffer_size]);
+    int read_bytes = file.ReadAll(audio_input.get(), file_buffer_size);
+    if (read_bytes < 0) {
+      return false;
+    }
+
+    scoped_ptr<audio::AudioFileReader> reader(audio::AudioFileReader::TryCreate(
+        reinterpret_cast<const uint8*>(audio_input.get()), file_buffer_size,
+        audio::kSampleTypeInt16));
+    const float kSupportedSampleRate = 16000.0f;
+    const int kSupportedMonoChannel = 1;
+    if (!reader) {
+      // If it is not a WAV file, read audio data as raw audio.
+      file_buffer_.reset(new uint8[file_buffer_size]);
+      SbMemoryCopy(file_buffer_.get(), audio_input.get(), file_buffer_size);
+      file_length_ = file_buffer_size;
+    } else if (reader->sample_type() != ::media::ShellAudioBus::kInt16 ||
+               reader->sample_rate() != kSupportedSampleRate ||
+               reader->number_of_channels() != kSupportedMonoChannel) {
+      // If it is a WAV file but it doesn't meet the audio input criteria, treat
+      // it as an error.
+      return false;
+    } else {
+      // Read WAV PCM16 audio data.
+      int size =
+          static_cast<int>(reader->number_of_frames() *
+                           audio::GetSampleTypeSize(reader->sample_type()));
+      file_buffer_.reset(new uint8[size]);
+      SbMemoryCopy(file_buffer_.get(), reader->sample_data().get(), size);
+      file_length_ = size;
+    }
+  }
   return true;
 }
 
 int MicrophoneFake::Read(char* out_data, int data_size) {
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
   if (ShouldFail(kReadRange)) {
     SB_DLOG(WARNING) << "Mocking microphone read failed.";
     return -1;
@@ -115,8 +162,13 @@
 }
 
 bool MicrophoneFake::Close() {
-  file_buffer_.reset();
-  file_length_ = -1;
+  SB_DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (read_data_from_file_) {
+    file_buffer_.reset();
+    file_length_ = -1;
+  }
+
   read_index_ = 0;
 
   if (ShouldFail(kCloseRange)) {
diff --git a/src/cobalt/speech/microphone_fake.h b/src/cobalt/speech/microphone_fake.h
index dd17c1c..068eca3 100644
--- a/src/cobalt/speech/microphone_fake.h
+++ b/src/cobalt/speech/microphone_fake.h
@@ -24,8 +24,10 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/file_path.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "cobalt/speech/microphone.h"
 
 namespace cobalt {
@@ -35,7 +37,7 @@
 // audio.
 class MicrophoneFake : public Microphone {
  public:
-  MicrophoneFake();
+  explicit MicrophoneFake(const Options& options);
   ~MicrophoneFake() SB_OVERRIDE {}
 
   bool Open() SB_OVERRIDE;
@@ -45,8 +47,11 @@
   bool IsValid() SB_OVERRIDE { return is_valid_; }
 
  private:
+  base::ThreadChecker thread_checker_;
+
+  bool read_data_from_file_;
   std::vector<FilePath> file_paths_;
-  scoped_array<char> file_buffer_;
+  scoped_array<uint8> file_buffer_;
   int file_length_;
   int read_index_;
   bool is_valid_;
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index 7b491d9..5d8210d 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -38,21 +38,21 @@
                                      const DataReceivedCallback& data_received,
                                      const CompletionCallback& completion,
                                      const ErrorCallback& error,
-                                     bool enable_fake_microphone)
+                                     const Microphone::Options& options)
     : sample_rate_(sample_rate),
       data_received_callback_(data_received),
       completion_callback_(completion),
       error_callback_(error),
 #if defined(ENABLE_FAKE_MICROPHONE)
-      enable_fake_microphone_(enable_fake_microphone),
+      microphone_options_(options),
 #endif  // defined(ENABLE_FAKE_MICROPHONE)
       state_(kStopped),
       thread_("microphone_thread") {
   UNREFERENCED_PARAMETER(sample_rate_);
 #if defined(ENABLE_FAKE_MICROPHONE)
-  UNREFERENCED_PARAMETER(enable_fake_microphone_);
+  UNREFERENCED_PARAMETER(microphone_options_);
 #else
-  UNREFERENCED_PARAMETER(enable_fake_microphone);
+  UNREFERENCED_PARAMETER(options);
 #endif  // defined(ENABLE_FAKE_MICROPHONE)
   thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
 }
@@ -84,8 +84,8 @@
 
 #if defined(SB_USE_SB_MICROPHONE)
 #if defined(ENABLE_FAKE_MICROPHONE)
-  if (enable_fake_microphone_) {
-    microphone_.reset(new MicrophoneFake());
+  if (microphone_options_.enable_fake_microphone) {
+    microphone_.reset(new MicrophoneFake(microphone_options_));
   } else {
     microphone_.reset(
         new MicrophoneStarboard(sample_rate_, kBufferSizeInBytes));
@@ -185,6 +185,7 @@
 
   microphone_.reset();
   state_ = kStopped;
+  poll_mic_events_timer_ = base::nullopt;
 }
 
 }  // namespace speech
diff --git a/src/cobalt/speech/microphone_manager.h b/src/cobalt/speech/microphone_manager.h
index e91ca8c..c07c21a 100644
--- a/src/cobalt/speech/microphone_manager.h
+++ b/src/cobalt/speech/microphone_manager.h
@@ -41,7 +41,8 @@
 
   MicrophoneManager(int sample_rate, const DataReceivedCallback& data_received,
                     const CompletionCallback& completion,
-                    const ErrorCallback& error, bool enable_fake_microphone);
+                    const ErrorCallback& error,
+                    const Microphone::Options& options);
 
   ~MicrophoneManager();
 
@@ -71,7 +72,7 @@
 
   scoped_ptr<Microphone> microphone_;
 #if defined(ENABLE_FAKE_MICROPHONE)
-  bool enable_fake_microphone_;
+  Microphone::Options microphone_options_;
 #endif  // defined(ENABLE_FAKE_MICROPHONE)
 
   // Microphone state.
diff --git a/src/cobalt/speech/sandbox/audio_loader.cc b/src/cobalt/speech/sandbox/audio_loader.cc
new file mode 100644
index 0000000..1f83132
--- /dev/null
+++ b/src/cobalt/speech/sandbox/audio_loader.cc
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include "cobalt/speech/sandbox/audio_loader.h"
+
+#include <vector>
+
+namespace cobalt {
+namespace speech {
+namespace sandbox {
+
+namespace {
+class DummyDecoder : public loader::Decoder {
+ public:
+  typedef base::Callback<void(const uint8*, int)> DoneCallback;
+
+  explicit DummyDecoder(const DoneCallback& done_callback)
+      : done_callback_(done_callback) {}
+  ~DummyDecoder() OVERRIDE {}
+
+  // This function is used for binding callback for creating DummyDecoder.
+  static scoped_ptr<Decoder> Create(const DoneCallback& done_callback) {
+    return scoped_ptr<Decoder>(new DummyDecoder(done_callback));
+  }
+
+  // From Decoder.
+  void DecodeChunk(const char* data, size_t size) OVERRIDE {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    // Because we load data into memory, set a maximum buffer size.
+    const int kMaxBufferSize = 1024 * 1024;
+    if (buffer_.size() + size > kMaxBufferSize) {
+      return;
+    }
+    buffer_.insert(buffer_.end(), data, data + size);
+  }
+
+  void Finish() OVERRIDE {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    if (buffer_.size() == 0) {
+      // No data loaded.
+      done_callback_.Run(NULL, 0);
+      return;
+    }
+
+    done_callback_.Run(reinterpret_cast<uint8*>(&buffer_[0]), buffer_.size());
+  }
+  bool Suspend() OVERRIDE {
+    NOTIMPLEMENTED();
+    return false;
+  }
+  void Resume(render_tree::ResourceProvider* /*resource_provider*/) OVERRIDE {
+    NOTIMPLEMENTED();
+  }
+
+ private:
+  base::ThreadChecker thread_checker_;
+  std::vector<char> buffer_;
+  DoneCallback done_callback_;
+};
+}  // namespace
+
+AudioLoader::AudioLoader(const GURL& url,
+                         network::NetworkModule* network_module,
+                         const DoneCallback& callback)
+    : network_module_(network_module), done_callback_(callback) {
+  DCHECK(url.is_valid());
+  DCHECK(!callback.is_null());
+
+  fetcher_factory_.reset(new loader::FetcherFactory(network_module_));
+  loader_ = make_scoped_ptr(new loader::Loader(
+      base::Bind(&loader::FetcherFactory::CreateFetcher,
+                 base::Unretained(fetcher_factory_.get()), url),
+      scoped_ptr<loader::Decoder>(new DummyDecoder(
+          base::Bind(&AudioLoader::OnLoadingDone, base::Unretained(this)))),
+      base::Bind(&AudioLoader::OnLoadingError, base::Unretained(this))));
+}
+
+AudioLoader::~AudioLoader() {}
+
+void AudioLoader::OnLoadingDone(const uint8* data, int size) {
+  done_callback_.Run(data, size);
+}
+
+void AudioLoader::OnLoadingError(const std::string& error) {
+  DLOG(WARNING) << "OnLoadingError with error message: " << error;
+}
+
+}  // namespace sandbox
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/sandbox/audio_loader.h b/src/cobalt/speech/sandbox/audio_loader.h
new file mode 100644
index 0000000..921713e
--- /dev/null
+++ b/src/cobalt/speech/sandbox/audio_loader.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef COBALT_SPEECH_SANDBOX_AUDIO_LOADER_H_
+#define COBALT_SPEECH_SANDBOX_AUDIO_LOADER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader.h"
+#include "cobalt/network/network_module.h"
+
+namespace cobalt {
+namespace speech {
+namespace sandbox {
+
+class AudioLoader {
+ public:
+  typedef base::Callback<void(const uint8*, int)> DoneCallback;
+  AudioLoader(const GURL& url, network::NetworkModule* network_module,
+              const DoneCallback& callback);
+  ~AudioLoader();
+
+ private:
+  void OnLoadingDone(const uint8* data, int size);
+  void OnLoadingError(const std::string& error);
+
+  const DoneCallback done_callback_;
+  network::NetworkModule* network_module_;
+  scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+  scoped_ptr<loader::Loader> loader_;
+};
+
+}  // namespace sandbox
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SANDBOX_AUDIO_LOADER_H_
diff --git a/src/cobalt/speech/sandbox/sandbox.gyp b/src/cobalt/speech/sandbox/sandbox.gyp
new file mode 100644
index 0000000..82f36b4
--- /dev/null
+++ b/src/cobalt/speech/sandbox/sandbox.gyp
@@ -0,0 +1,54 @@
+# 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 is a sample sandbox application for experimenting with the Cobalt
+# Speech API.
+
+{
+  'targets': [
+    {
+      'target_name': 'speech_sandbox',
+      'type': '<(final_executable_type)',
+      'sources': [
+        'audio_loader.cc',
+        'audio_loader.h',
+        'speech_sandbox.cc',
+        'speech_sandbox.h',
+        'speech_sandbox_main.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/audio/audio.gyp:audio',
+        '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/loader/loader.gyp:loader',
+        '<(DEPTH)/cobalt/network/network.gyp:network',
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
+        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
+        '<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event',
+        '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+      ],
+    },
+
+    {
+      'target_name': 'speech_sandbox_deploy',
+      'type': 'none',
+      'dependencies': [
+        'speech_sandbox',
+      ],
+      'variables': {
+        'executable_name': 'speech_sandbox',
+      },
+      'includes': [ '../../../starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/cobalt/speech/sandbox/speech_sandbox.cc b/src/cobalt/speech/sandbox/speech_sandbox.cc
new file mode 100644
index 0000000..b0946b6
--- /dev/null
+++ b/src/cobalt/speech/sandbox/speech_sandbox.cc
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#include "cobalt/speech/sandbox/speech_sandbox.h"
+
+#include "base/path_service.h"
+#include "cobalt/script/environment_settings.h"
+
+namespace cobalt {
+namespace speech {
+namespace sandbox {
+
+namespace {
+// The maximum number of element depth in the DOM tree. Elements at a level
+// deeper than this could be discarded, and will not be rendered.
+const int kDOMMaxElementDepth = 32;
+}  // namespace
+
+SpeechSandbox::SpeechSandbox(const std::string& file_path_string,
+                             const FilePath& trace_log_path) {
+  trace_to_file_.reset(new trace_event::ScopedTraceToFile(trace_log_path));
+
+  network::NetworkModule::Options network_options;
+  network_options.require_https = false;
+  network_module_.reset(new network::NetworkModule(network_options));
+
+  GURL url(file_path_string);
+  if (url.is_valid()) {
+    audio_loader_.reset(new AudioLoader(
+        url, network_module_.get(),
+        base::Bind(&SpeechSandbox::OnLoadingDone, base::Unretained(this))));
+  } else {
+    FilePath file_path(file_path_string);
+    if (!file_path.IsAbsolute()) {
+      FilePath exe_path;
+      PathService::Get(base::FILE_EXE, &exe_path);
+      DCHECK(exe_path.IsAbsolute());
+      std::string exe_path_string(exe_path.value());
+      std::size_t found = exe_path_string.find_last_of("/\\");
+      DCHECK_NE(found, std::string::npos);
+      // Find the executable directory. Using exe_path.DirName() doesn't work
+      // on Windows based platforms due to the path is mixed with "/" and "\".
+      exe_path_string = exe_path_string.substr(0, found);
+      file_path = FilePath(exe_path_string).Append(file_path_string);
+      DCHECK(file_path.IsAbsolute());
+    }
+
+    dom::DOMSettings::Options dom_settings_options;
+    dom_settings_options.microphone_options.enable_fake_microphone = true;
+    dom_settings_options.microphone_options.file_path = file_path;
+
+    StartRecognition(dom_settings_options);
+  }
+}
+
+SpeechSandbox::~SpeechSandbox() {
+  if (speech_recognition_) {
+    speech_recognition_->Stop();
+  }
+}
+
+void SpeechSandbox::StartRecognition(
+    const dom::DOMSettings::Options& dom_settings_options) {
+  scoped_ptr<script::EnvironmentSettings> environment_settings(
+      new dom::DOMSettings(kDOMMaxElementDepth, NULL, network_module_.get(),
+                           NULL, NULL, NULL, NULL, NULL, NULL,
+                           dom_settings_options));
+  DCHECK(environment_settings);
+
+  speech_recognition_ = new SpeechRecognition(environment_settings.get());
+  speech_recognition_->set_continuous(true);
+  speech_recognition_->set_interim_results(true);
+  speech_recognition_->Start(NULL);
+}
+
+void SpeechSandbox::OnLoadingDone(const uint8* data, int size) {
+  dom::DOMSettings::Options dom_settings_options;
+  dom_settings_options.microphone_options.enable_fake_microphone = true;
+  dom_settings_options.microphone_options.external_audio_data = data;
+  dom_settings_options.microphone_options.audio_data_size = size;
+
+  StartRecognition(dom_settings_options);
+}
+
+}  // namespace sandbox
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/sandbox/speech_sandbox.h b/src/cobalt/speech/sandbox/speech_sandbox.h
new file mode 100644
index 0000000..15bbd60
--- /dev/null
+++ b/src/cobalt/speech/sandbox/speech_sandbox.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef COBALT_SPEECH_SANDBOX_SPEECH_SANDBOX_H_
+#define COBALT_SPEECH_SANDBOX_SPEECH_SANDBOX_H_
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/threading/thread.h"
+#include "cobalt/dom/dom_settings.h"
+#include "cobalt/speech/sandbox/audio_loader.h"
+#include "cobalt/speech/speech_recognition.h"
+#include "cobalt/trace_event/scoped_trace_to_file.h"
+
+namespace cobalt {
+namespace speech {
+namespace sandbox {
+
+// This class is for speech sandbox application to experiment with voice search.
+// It takes a wav audio as audio input for a fake microphone, and starts/stops
+// speech recognition.
+class SpeechSandbox {
+ public:
+  // The constructor takes a file path string for an audio input and a log path
+  // for tracing.
+  SpeechSandbox(const std::string& file_path_string,
+                const FilePath& trace_log_path);
+  ~SpeechSandbox();
+
+ private:
+  void StartRecognition(const dom::DOMSettings::Options& dom_settings_options);
+  void OnLoadingDone(const uint8* data, int size);
+
+  scoped_ptr<trace_event::ScopedTraceToFile> trace_to_file_;
+  scoped_ptr<network::NetworkModule> network_module_;
+  scoped_ptr<AudioLoader> audio_loader_;
+  scoped_refptr<SpeechRecognition> speech_recognition_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SpeechSandbox);
+};
+
+}  // namespace sandbox
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_SANDBOX_SPEECH_SANDBOX_H_
diff --git a/src/cobalt/speech/sandbox/speech_sandbox_main.cc b/src/cobalt/speech/sandbox/speech_sandbox_main.cc
new file mode 100644
index 0000000..2cbb874
--- /dev/null
+++ b/src/cobalt/speech/sandbox/speech_sandbox_main.cc
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "cobalt/base/wrap_main.h"
+#include "cobalt/speech/sandbox/speech_sandbox.h"
+#include "googleurl/src/gurl.h"
+
+namespace cobalt {
+namespace speech {
+namespace sandbox {
+
+SpeechSandbox* g_speech_sandbox = NULL;
+
+// The application takes an audio url or path, and a timeout in second.
+// The timeout is optional. If it is not set or set to 0, the application
+// doesn't shut down.
+void StartApplication(int argc, char** argv, const char* /*link*/,
+                      const base::Closure& quit_closure) {
+  if (argc != 3 && argc != 2) {
+    LOG(ERROR) << "Usage: " << argv[0]
+               << " <audio url|path> [timeout in seconds]";
+    return;
+  }
+
+  int timeout = 0;
+  if (argc == 3) {
+    base::StringToInt(argv[2], &timeout);
+  }
+
+  DCHECK(!g_speech_sandbox);
+  g_speech_sandbox = new SpeechSandbox(
+      std::string(argv[1]),
+      FilePath(FILE_PATH_LITERAL("speech_sandbox_trace.json")));
+
+  if (timeout != 0) {
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, quit_closure, base::TimeDelta::FromSeconds(timeout));
+  }
+}
+
+void StopApplication() {
+  DCHECK(g_speech_sandbox);
+  delete g_speech_sandbox;
+  g_speech_sandbox = NULL;
+}
+
+}  // namespace sandbox
+}  // namespace speech
+}  // namespace cobalt
+
+COBALT_WRAP_BASE_MAIN(cobalt::speech::sandbox::StartApplication,
+                      cobalt::speech::sandbox::StopApplication);
diff --git a/src/cobalt/speech/speech_recognition.cc b/src/cobalt/speech/speech_recognition.cc
index 8ea7c49..06deca0 100644
--- a/src/cobalt/speech/speech_recognition.cc
+++ b/src/cobalt/speech/speech_recognition.cc
@@ -28,12 +28,11 @@
 SpeechRecognition::SpeechRecognition(script::EnvironmentSettings* settings)
     : ALLOW_THIS_IN_INITIALIZER_LIST(
           manager_(base::polymorphic_downcast<dom::DOMSettings*>(settings)
-                       ->fetcher_factory()
                        ->network_module(),
                    base::Bind(&SpeechRecognition::OnEventAvailable,
                               base::Unretained(this)),
                    base::polymorphic_downcast<dom::DOMSettings*>(settings)
-                       ->enable_fake_microphone())),
+                       ->microphone_options())),
       config_("" /*lang*/, false /*continuous*/, false /*interim_results*/,
               1 /*max alternatives*/) {}
 
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index 114d374..0b4ffdf 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -19,6 +19,7 @@
 #include "base/bind.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/dom_exception.h"
+#include "cobalt/speech/microphone_manager.h"
 
 namespace cobalt {
 namespace speech {
@@ -30,7 +31,7 @@
 
 SpeechRecognitionManager::SpeechRecognitionManager(
     network::NetworkModule* network_module, const EventCallback& event_callback,
-    bool enable_fake_microphone)
+    const Microphone::Options& microphone_options)
     : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
       weak_this_(weak_ptr_factory_.GetWeakPtr()),
       main_message_loop_(base::MessageLoopProxy::current()),
@@ -39,14 +40,14 @@
           recognizer_(network_module,
                       base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
                                  base::Unretained(this)))),
-      ALLOW_THIS_IN_INITIALIZER_LIST(microphone_manager_(
+      ALLOW_THIS_IN_INITIALIZER_LIST(microphone_manager_(new MicrophoneManager(
           kSampleRate, base::Bind(&SpeechRecognitionManager::OnDataReceived,
                                   base::Unretained(this)),
           base::Bind(&SpeechRecognitionManager::OnDataCompletion,
                      base::Unretained(this)),
           base::Bind(&SpeechRecognitionManager::OnMicError,
                      base::Unretained(this)),
-          enable_fake_microphone)),
+          microphone_options))),
       endpointer_delegate_(kSampleRate),
       state_(kStopped) {}
 
@@ -65,7 +66,7 @@
   }
 
   recognizer_.Start(config, kSampleRate);
-  microphone_manager_.Open();
+  microphone_manager_->Open();
   endpointer_delegate_.Start();
   state_ = kStarted;
 }
@@ -80,7 +81,7 @@
   }
 
   endpointer_delegate_.Stop();
-  microphone_manager_.Close();
+  microphone_manager_->Close();
   recognizer_.Stop();
   state_ = kStopped;
   event_callback_.Run(new dom::Event(base::Tokens::soundend()));
@@ -96,7 +97,7 @@
   }
 
   endpointer_delegate_.Stop();
-  microphone_manager_.Close();
+  microphone_manager_->Close();
   recognizer_.Stop();
   state_ = kAborted;
   event_callback_.Run(new dom::Event(base::Tokens::soundend()));
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index 9367a10..6f0faa7 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -22,7 +22,7 @@
 #include "cobalt/network/network_module.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/speech/endpointer_delegate.h"
-#include "cobalt/speech/microphone_manager.h"
+#include "cobalt/speech/microphone.h"
 #include "cobalt/speech/speech_configuration.h"
 #include "cobalt/speech/speech_recognition_config.h"
 #include "cobalt/speech/speech_recognition_error.h"
@@ -33,6 +33,8 @@
 namespace cobalt {
 namespace speech {
 
+class MicrophoneManager;
+
 // Owned by SpeechRecognition to manage major speech recognition logic.
 // This class interacts with microphone, speech recognition service and audio
 // encoder. It provides the interface to start/stop microphone and
@@ -45,7 +47,7 @@
 
   SpeechRecognitionManager(network::NetworkModule* network_module,
                            const EventCallback& event_callback,
-                           bool enable_fake_microphone);
+                           const Microphone::Options& microphone_options);
   ~SpeechRecognitionManager();
 
   // Start/Stop speech recognizer and microphone. Multiple calls would be
@@ -80,7 +82,7 @@
   EventCallback event_callback_;
   SpeechRecognizer recognizer_;
 
-  MicrophoneManager microphone_manager_;
+  scoped_ptr<MicrophoneManager> microphone_manager_;
 
   // Delegate of endpointer which is used for detecting sound energy.
   EndPointerDelegate endpointer_delegate_;
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc
index d7ad5dd..27eb43a 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -32,7 +32,7 @@
 #include "net/url_request/url_fetcher.h"
 
 #if defined(SB_USE_SB_MICROPHONE)
-#include "starboard/microphone.h"
+#include "starboard/system.h"
 #endif  // defined(SB_USE_SB_MICROPHONE)
 
 namespace cobalt {
@@ -274,12 +274,18 @@
   up_url = AppendQueryParameter(up_url, "pair", pair);
   up_url = AppendQueryParameter(up_url, "output", "pb");
 
-  const char* speech_api_key = NULL;
-#if defined(SB_USE_SB_MICROPHONE)
-  speech_api_key = SbMicrophoneGetSpeechApiKey();
-#else
-  speech_api_key = "";
-#endif
+  const char* speech_api_key = "";
+#if defined(OS_STARBOARD)
+#if SB_VERSION(2)
+  const int kSpeechApiKeyLength = 100;
+  char buffer[kSpeechApiKeyLength] = {0};
+  bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer,
+                                    SB_ARRAY_SIZE_INT(buffer));
+  SB_DCHECK(result);
+  speech_api_key = result ? buffer : "";
+#endif  // SB_VERSION(2)
+#endif  // defined(OS_STARBOARD)
+
   up_url = AppendQueryParameter(up_url, "key", speech_api_key);
 
   // Language is required. If no language is specified, use the system language.
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index 94ad4ca..ab7b3eb 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -179,10 +179,10 @@
 WebDriverServer::WebDriverServer(int port,
                                  const HandleRequestCallback& callback)
     : handle_request_callback_(callback) {
-  LOG(INFO) << "Starting WebDriver server on port " << port;
   // Create http server
   factory_.reset(new net::TCPListenSocketFactory("0.0.0.0", port));
   server_ = new net::HttpServer(*factory_, this);
+  LOG(INFO) << "Starting WebDriver server on port " << port;
 }
 
 void WebDriverServer::OnHttpRequest(int connection_id,
diff --git a/src/cobalt/webdriver_benchmarks/README.md b/src/cobalt/webdriver_benchmarks/README.md
new file mode 100644
index 0000000..da77083
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/README.md
@@ -0,0 +1,3 @@
+Framework for webdriver-driven benchmarks.
+
+Please see tests/README.md
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
index cc7547c..cd03ee2 100755
--- a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
+++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -13,7 +13,6 @@
 import inspect
 import os
 import re
-import socket
 import sys
 import thread
 import threading
@@ -142,12 +141,13 @@
       # potentially from thread.interrupt_main(). We will treat as
       # a timeout regardless
       raise TimeoutException
+    return self
 
   def __exit__(self, exc_type, exc_value, traceback):
     # The unittest module terminates with a SystemExit
     # If this is a successful exit, then this is a successful run
-    success = exc_type is None or (exc_type is SystemExit
-                                   and not exc_value.code)
+    success = exc_type is None or (exc_type is SystemExit and
+                                   not exc_value.code)
     self.SetShouldExit(failed=not success)
     self.thread.join(COBALT_EXIT_TIMEOUT_SECONDS)
 
@@ -163,7 +163,7 @@
       return
 
     port = match.group(1)
-    sys.stderr.write("WebDriver port opened:" + port + "\n")
+    print("WebDriver port opened:" + port + "\n", file=self.log_file)
     self._StartWebdriver(port)
 
   def SetShouldExit(self, failed=False):
@@ -180,7 +180,7 @@
     url = "http://{}:{}/".format(self._GetIPAddress(), port)
     self.webdriver = self.selenium_webdriver_module.Remote(
         url, COBALT_WEBDRIVER_CAPABILITIES)
-    sys.stderr.write("Selenium Connected\n")
+    print("Selenium Connected\n", file=self.log_file)
     _webdriver = self.webdriver
     self.test_script_started.set()
 
@@ -189,7 +189,7 @@
     if not self.test_script_started.wait(STARTUP_TIMEOUT_SECONDS):
       self.SetShouldExit(failed=True)
       raise TimeoutException
-    sys.stderr.write("Cobalt started\n")
+    print("Cobalt started", file=self.log_file)
 
   def Run(self):
     """Thread run routine."""
@@ -205,12 +205,14 @@
         self.log_file = sys.stdout
 
       self.launcher.SetOutputFile(self.log_file)
-      sys.stderr.write("Running launcher \n")
+      print("Running launcher", file=self.log_file)
       self.launcher.Run()
-      sys.stderr.write("Cobalt terminated. failed: " + str(self.failed) + "\n")
+      print(
+          "Cobalt terminated. failed: " + str(self.failed), file=self.log_file)
       # This is watched for in webdriver_benchmark_test.py
       if not self.failed:
         sys.stdout.write("partial_layout_benchmark TEST COMPLETE\n")
+    # pylint: disable=broad-except
     except Exception as ex:
       print("Exception running Cobalt " + str(ex), file=sys.stderr)
     finally:
@@ -255,13 +257,11 @@
   if executable is None:
     executable = GetCobaltExecutablePath(platform, args.config)
 
-  devkit_name = args.devkit_name
-  if devkit_name is None:
-    devkit_name = socket.gethostname()
-
   try:
-    with CobaltRunner(platform, executable, devkit_name, args.log_file):
-      unittest.main()
+    with CobaltRunner(platform, executable, args.devkit_name,
+                      args.log_file) as runner:
+      unittest.main(testRunner=unittest.TextTestRunner(
+          verbosity=0, stream=runner.log_file))
     return 0
   except TimeoutException:
     print("Timeout waiting for Cobalt to start", file=sys.stderr)
diff --git a/src/cobalt/webdriver_benchmarks/test_tv_testcase.py b/src/cobalt/webdriver_benchmarks/test_tv_testcase.py
new file mode 100755
index 0000000..742fa66
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/test_tv_testcase.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python2
+# 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.
+# ==============================================================================
+"""Unit tests for tv_testcase.py."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+import unittest
+
+# This directory is a package
+sys.path.insert(0, os.path.abspath('.'))
+# pylint: disable=C6203,C6203
+# pylint: disable=g-import-not-at-top
+import tv_testcase
+
+
+class MedianPercentileTest(unittest.TestCase):
+
+  def test_empty_case(self):
+    self.assertEqual(tv_testcase._percentile([], 50), None)
+
+  def test_one_item(self):
+    self.assertEqual(tv_testcase._percentile([1], 50), 1)
+
+  def test_two_items(self):
+    self.assertAlmostEqual(tv_testcase._percentile([4, 1], 50), 2.5)
+
+  def test_three_items(self):
+    self.assertAlmostEqual(tv_testcase._percentile([4, -4, -2], 50), -2)
+
+  def test_four_items(self):
+    self.assertAlmostEqual(tv_testcase._percentile([4, 0, 1, -2], 50), 0.5)
+
+
+class PercentileTest(unittest.TestCase):
+
+  def test_one_item(self):
+    self.assertEqual(tv_testcase._percentile([1], 10), 1)
+    self.assertEqual(tv_testcase._percentile([2], 50), 2)
+    self.assertEqual(tv_testcase._percentile([3], 90), 3)
+
+  def test_two_items(self):
+    self.assertEqual(tv_testcase._percentile([2, 1], 10), 1.1)
+    self.assertEqual(tv_testcase._percentile([2, 3], 50), 2.5)
+    self.assertEqual(tv_testcase._percentile([3, 4], 90), 3.9)
+    self.assertEqual(tv_testcase._percentile([3, 4], 100), 4)
+
+  def test_five_items(self):
+    self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 10), 1.4)
+    self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 50), 3)
+    self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 90), 4.6)
+    self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 100), 5)
+
+
+class MergeDictTest(unittest.TestCase):
+
+  def test_empty_merge(self):
+    a = {'x': 4}
+    b = {}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': 4})
+
+  def test_merge_into_empty(self):
+    a = {}
+    b = {'x': 4}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': 4})
+
+  def test_merge_non_overlapping_item(self):
+    a = {'x': 4}
+    b = {'y': 5}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': 4, 'y': 5})
+
+  def test_overlapping_item_with_item(self):
+    a = {'x': 4}
+    b = {'x': 5}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': [4, 5]})
+
+  def test_overlapping_list_with_item(self):
+    a = {'x': [4]}
+    b = {'x': 5}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': [4, 5]})
+
+  def test_overlapping_list_with_list(self):
+    a = {'x': [4]}
+    b = {'x': [5]}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': [4, 5]})
+
+  def test_overlapping_item_with_list(self):
+    a = {'x': 4}
+    b = {'x': [5]}
+    tv_testcase._merge_dict(a, b)
+    self.assertEqual(a, {'x': [4, 5]})
+
+
+if __name__ == '__main__':
+  sys.exit(unittest.main())
diff --git a/src/cobalt/webdriver_benchmarks/tests/README.md b/src/cobalt/webdriver_benchmarks/tests/README.md
new file mode 100644
index 0000000..a8e30ad
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tests/README.md
@@ -0,0 +1,29 @@
+Cobalt Webdriver-driven Benchmarks
+---------------------
+
+This directory contains a set of webdriver-driven benchmarks
+for Cobalt.
+
+Each file should contain a set of tests in Python "unittest" format.
+
+All tests in all of the files in this directory will be run on the build system.
+Results can be recorded in the build results database.
+
+To run an individual test, simply execute a script directly (or run
+all of them via "all.py"). Platform configuration will be inferred from
+the environment if set. Otherwise, it must be specified via commandline
+parameters.
+
+To make a new test:
+
+ 1. If appropriate, create a new file borrowing the boilerplate from
+    an existing simple file, such as "shelf.py"
+
+ 2. If this file contains internal names or details, consider adding it
+    to the "EXCLUDE.FILES" list.
+
+ 3. Use the `record_result*` methods in the `tv_testcase.TvTestCase` base
+    class where appropriate.
+
+ 4. Results must be added to the build results database schema. See
+    the internal "README-Updating-Result-Schema.md" file
diff --git a/src/cobalt/webdriver_benchmarks/tests/guide.py b/src/cobalt/webdriver_benchmarks/tests/guide.py
index 324daef..5c0a011 100755
--- a/src/cobalt/webdriver_benchmarks/tests/guide.py
+++ b/src/cobalt/webdriver_benchmarks/tests/guide.py
@@ -38,7 +38,7 @@
       self.wait_for_layout_complete_after_focused_shelf()
       layout_times_us.append(self.get_keyup_layout_duration_us())
 
-    self.record_results("GuideTest.test_simple", layout_times_us)
+    self.record_result_percentile("guideTestLayout95thUs", layout_times_us, 95)
 
 
 if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/shelf.py b/src/cobalt/webdriver_benchmarks/tests/shelf.py
index ce058e0..c73255b 100755
--- a/src/cobalt/webdriver_benchmarks/tests/shelf.py
+++ b/src/cobalt/webdriver_benchmarks/tests/shelf.py
@@ -40,7 +40,8 @@
       self.wait_for_layout_complete_after_focused_shelf()
       layout_times_us.append(self.get_keyup_layout_duration_us())
 
-    self.record_results("ShelfTest.test_simple", layout_times_us)
+    self.record_result_percentile("webdriverBenchmarkShelfLayout95thUsec",
+                                  layout_times_us, 95)
 
 
 if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
index b1ede63..e180362 100755
--- a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
+++ b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
@@ -51,9 +51,10 @@
       startup_time_microseconds = t1 - t0
       metrics_array.append(startup_time_microseconds)
 
-    self.record_results('TimeToShelf.test_time_shelf_display', metrics_array)
-    self.record_results('TimeToShelf.blank_startup_time',
-                        blank_startup_time_microseconds)
+    self.record_result_median('timeToShelfTestTimeShelfDisplayMedianUs',
+                              metrics_array)
+    self.record_result('timeToShelfBlankStartupTimeUs',
+                       blank_startup_time_microseconds)
 
 
 if __name__ == '__main__':
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py
index cbb5903..57a7b77 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -5,6 +5,8 @@
 from __future__ import print_function
 
 import json
+import logging
+import math
 import os
 import sys
 import time
@@ -41,36 +43,69 @@
 LAYOUT_TIMEOUT_SECONDS = 5
 
 
-def _median(l):
-  """Returns median value of items in a list.
+def _percentile(results, percentile):
+  """Returns the percentile of an array.
 
-  Note: This function is setup for convieniance, and might not be performant
-  for large datasets.  Also, no care has been taken to deal with nans, or
-  infinities, so please expand the functionality and tests if that is desired.
-  The other option is to use median.
-
-  Running time: O(n^2)
-  Space complexity: O(n)
+  This method interpolates between two numbers if the percentile lands between
+  two data points.
 
   Args:
-    l: List containing sortable items.
+      results: Sortable results array.
+      percentile: A number ranging from 0-100.
+  Returns:
+      Appropriate value.
+  Raises:
+    RuntimeError: Raised on invalid args.
+  """
+  if not results:
+    return None
+  if percentile > 100 or percentile < 0:
+    raise RuntimeError("percentile must be 0-100")
+  sorted_results = sorted(results)
+
+  if percentile == 100:
+    return sorted_results[-1]
+  fractional, index = math.modf((len(sorted_results) - 1) * (percentile * 0.01))
+  index = int(index)
+
+  if len(sorted_results) == index + 1:
+    return sorted_results[index]
+
+  return sorted_results[index] * (1 - fractional
+                                 ) + sorted_results[index + 1] * fractional
+
+
+def _merge_dict(merge_into, merge_from):
+  """Merges the second dict into the first dict.
+
+  Merge into differs from update in that it will not override values.  If the
+  values already exist, the resulting value will be a list with a union of
+  existing and new items.
+
+  Args:
+    merge_into: An output dict to merge values into.
+    merge_from: An input dict to iterate over and insert values from.
 
   Returns:
-    Median of the items in a list.  None if |l| is empty.
+    None
   """
-  if not l:
-    return None
-  l_length = len(l)
-  if l_length == 0:
-    return l[0]
+  if not merge_from:
+    return
+  for k, v in merge_from.items():
+    try:
+      existing_value = merge_into[k]
+    except KeyError:
+      merge_into[k] = v
+      continue
 
-  l_sorted = sorted(l)
-  middle_index = l_length // 2
-  if len(l) % 2 == 1:
-    return l_sorted[middle_index]
-
-  # If there are even number of items, take the average of two middle values.
-  return 0.5 * (l_sorted[middle_index] + l_sorted[middle_index - 1])
+    if not isinstance(v, list):
+      v = [v]
+    if isinstance(existing_value, list):
+      existing_value.extend(v)
+    else:
+      new_value = [existing_value]
+      new_value.extend(v)
+      merge_into[k] = new_value
 
 
 class TvTestCase(unittest.TestCase):
@@ -83,6 +118,14 @@
   class LayoutTimeoutException(BaseException):
     """Exception thrown when layout did not complete in time."""
 
+  @classmethod
+  def setUpClass(cls):
+    print("Running " + cls.__name__)
+
+  @classmethod
+  def tearDownClass(cls):
+    print("Done " + cls.__name__)
+
   def get_webdriver(self):
     return partial_layout_benchmark.GetWebDriver()
 
@@ -129,22 +172,25 @@
     query_dict = BASE_PARAMS.copy()
     if query_params:
       query_dict.update(urlparse.parse_qsl(parsed_url[4]))
-      query_dict.update(query_params)
-    parsed_url[4] = urlencode(query_dict)
+      _merge_dict(query_dict, query_params)
+    parsed_url[4] = urlencode(query_dict, doseq=True)
     final_url = urlparse.urlunparse(parsed_url)
     self.get_webdriver().get(final_url)
 
-  def load_tv(self, label=None):
+  def load_tv(self, label=None, additional_query_params=None):
     """Loads the main TV page and waits for it to display.
 
     Args:
       label: A value for the label query parameter.
+      additional_query_params: A dict containing additional query parameters.
     Raises:
       Underlying WebDriver exceptions
     """
-    query_params = None
+    query_params = {}
     if label is not None:
       query_params = {"label": label}
+    if additional_query_params is not None:
+      query_params.update(additional_query_params)
     self.goto(TV_APP_PATH, query_params)
     # Note that the internal tests use "expect_transition" which is
     # a mechanism that sets a maximum timeout for a "@with_retries"
@@ -257,23 +303,45 @@
     self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
     self.wait_for_layout_complete()
 
-  def record_results(self, name, results):
-    """Records results of benchmark.
-
-    The duration of KeyUp events will be recorded.
+  def record_result(self, name, result):
+    """Records an individual scalar result of a benchmark.
 
     Args:
       name: name of test case
-      results: Test results. Must be JSON encodable
+      result: Test result. Must be JSON encodable scalar.
     """
-    if isinstance(results, list):
-      value_to_record = _median(results)
-    else:
-      value_to_record = results
+    value_to_record = result
 
     string_value_to_record = json.JSONEncoder().encode(value_to_record)
     print("tv_testcase RESULT: {} {}".format(name, string_value_to_record))
 
+  def record_result_median(self, name, results):
+    """Records the median of an array of results.
+
+    Args:
+      name: name of test case
+      results: Test results array. Must be array of JSON encodable scalar.
+    """
+    value_to_record = _percentile(results, 50)
+
+    string_value_to_record = json.JSONEncoder().encode(value_to_record)
+    print("tv_testcase RESULT: {} {}".format(name, string_value_to_record))
+
+  def record_result_percentile(self, name, results, percentile):
+    """Records the percentile of an array of results.
+
+    Args:
+      name: The (string) name of test case.
+      results: Test results array. Must be array of JSON encodable scalars.
+      percentile: A number ranging from 0-100.
+    Raises:
+      RuntimeError: Raised on invalid args.
+    """
+    value_to_record = _percentile(results, percentile)
+    string_value_to_record = json.JSONEncoder().encode(value_to_record)
+    print("tv_testcase RESULT: {} {}".format(name, string_value_to_record))
+
 
 def main():
+  logging.basicConfig(level=logging.DEBUG)
   partial_layout_benchmark.main()
diff --git a/src/media/audio/shell_audio_streamer_starboard.cc b/src/media/audio/shell_audio_streamer_starboard.cc
index ddcc139..22a143f 100644
--- a/src/media/audio/shell_audio_streamer_starboard.cc
+++ b/src/media/audio/shell_audio_streamer_starboard.cc
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#include "media/audio/null_audio_streamer.h"
-
-#include "base/logging.h"
+#include "media/audio/shell_audio_streamer.h"
 
 namespace media {
 
@@ -25,7 +23,7 @@
 void ShellAudioStreamer::Terminate() {}
 
 ShellAudioStreamer* ShellAudioStreamer::Instance() {
-  return NullAudioStreamer::GetInstance();
+  return NULL;
 }
 
 }  // namespace media
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index 2708468..b62db8a 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -290,11 +290,15 @@
   SbMediaVideoCodec video_codec =
       MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
 
-  player_ = SbPlayerCreate(window_, video_codec, audio_codec,
-                           SB_PLAYER_NO_DURATION, drm_system_, &audio_header,
-                           &StarboardPlayer::DeallocateSampleCB,
-                           &StarboardPlayer::DecoderStatusCB,
-                           &StarboardPlayer::PlayerStatusCB, this);
+  player_ = SbPlayerCreate(
+      window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
+      &audio_header, &StarboardPlayer::DeallocateSampleCB,
+      &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
+#if SB_VERSION(3)
+      ,
+      NULL  // provider
+#endif
+      );
   set_bounds_helper_->SetPlayer(this);
 }
 
diff --git a/src/nb/analytics/memory_tracker.cc b/src/nb/analytics/memory_tracker.cc
new file mode 100644
index 0000000..cf6c868
--- /dev/null
+++ b/src/nb/analytics/memory_tracker.cc
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#include "nb/analytics/memory_tracker.h"
+
+#include "nb/analytics/memory_tracker_impl.h"
+#include "starboard/once.h"
+
+namespace nb {
+namespace analytics {
+namespace {
+SB_ONCE_INITIALIZE_FUNCTION(MemoryTrackerImpl, GetMemoryTrackerImplSingleton);
+}  // namespace
+
+MemoryTracker* MemoryTracker::Get() {
+  return GetMemoryTrackerImplSingleton();
+}
+
+}  // namespace analytics
+}  // namespace nb
diff --git a/src/nb/analytics/memory_tracker.h b/src/nb/analytics/memory_tracker.h
new file mode 100644
index 0000000..27ef75f
--- /dev/null
+++ b/src/nb/analytics/memory_tracker.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#ifndef NB_MEMORY_TRACKER_H_
+#define NB_MEMORY_TRACKER_H_
+
+#include <vector>
+#include "starboard/configuration.h"
+#include "starboard/types.h"
+
+namespace nb {
+namespace analytics {
+
+class MemoryTracker;
+class AllocationVisitor;
+class AllocationGroup;
+class AllocationRecord;
+
+// Creates a MemoryTracker instance that implements the
+//  MemoryTracker. Once the instance is created it can begin tracking
+//  system allocations by calling InstallGlobalTrackingHooks().
+//  Deleting the MemoryTracker is forbidden.
+//
+// Example, Creation and Hooking:
+//   static MemoryTracker* s_global_tracker =
+//       GetOrCreateMemoryTracker();
+//   s_global_tracker->InstallGlobalTrackingHooks();  // now tracking memory.
+//
+// Data about the allocations are aggregated under AllocationGroups and it's
+//  recommended that GetAllocationGroups(...) is used to get simple allocation
+//  statistics.
+//
+// Deeper analytics are possible by creating an AllocationVisitor subclass and
+//  traversing through the internal allocations of the tracker. In this way all
+//  known information about allocation state of the program is made accessible.
+//  The visitor does not need to perform any locking as this is guaranteed by
+//  the MemoryTracker.
+//
+// Example (AllocationVisitor):
+//  MyAllocation visitor = ...;
+//  s_global_tracker->Accept(&visitor);
+//  visitor.PrintAllocations();
+//
+// Performance:
+//  1) Gold builds disallow memory tracking and therefore have zero-cost
+//     for this feature.
+//  2) All other builds that allow memory tracking have minimal cost as long
+//     as memory tracking has not been activated. This is facilitated by NOT
+//     using locks, at the expense of thread safety during teardown (hence the
+//     reason why you should NOT delete a memory tracker with hooks installed).
+//  3) When the memory tracking has been activated then there is a non-trivial
+//     performance cost in terms of CPU and memory for the feature.
+class MemoryTracker {
+ public:
+  // Gets the singleton instance of the default MemoryTracker. This
+  // is created the first time it is used.
+  static MemoryTracker* Get();
+
+  MemoryTracker() {}
+  virtual bool InstallGlobalTrackingHooks() = 0;
+
+  // It's recommended the MemoryTracker is never removed or deleted during the
+  // runtime.
+  virtual void RemoveGlobalTrackingHooks() = 0;
+
+  // Returns the total amount of bytes that are tracked.
+  virtual int64_t GetTotalAllocationBytes() = 0;
+  virtual int64_t GetTotalNumberOfAllocations() = 0;
+
+  // Allows probing of all memory allocations. The visitor does not need to
+  // perform any locking and can allocate memory during it's operation.
+  virtual void Accept(AllocationVisitor* visitor) = 0;
+
+  // Collects all memory groups that exist. The AllocationGroups lifetime
+  // exists for as long as the MemoryTracker instance is alive.
+  virtual void GetAllocationGroups(
+      std::vector<const AllocationGroup*>* output) = 0;
+
+  // Enabled/disables memory tracking in the current thread.
+  virtual void SetMemoryTrackingEnabled(bool on) = 0;
+  // Returns the memory tracking state in the current thread.
+  virtual bool IsMemoryTrackingEnabled() const = 0;
+
+  // Returns true if the memory was successfully tracked.
+  virtual bool AddMemoryTracking(const void* memory, size_t size) = 0;
+  // Returns a non-zero size if the memory was successfully removed.
+  virtual size_t RemoveMemoryTracking(const void* memory) = 0;
+  // Returns true if the memory has tracking. When true is returned then the
+  // supplied AllocRecord is written.
+  virtual bool GetMemoryTracking(const void* memory,
+                                 AllocationRecord* record) const = 0;
+
+ protected:
+  virtual ~MemoryTracker() {}
+
+  SB_DISALLOW_COPY_AND_ASSIGN(MemoryTracker);
+};
+
+// A visitor class which is useful for inspecting data.
+class AllocationVisitor {
+ public:
+  // Returns true to keep visiting, otherwise abort.
+  virtual bool Visit(const void* memory,
+                     const AllocationRecord& alloc_record) = 0;
+  virtual ~AllocationVisitor() {}
+};
+
+// Contains an allocation record for a pointer including it's size and what
+// AllocationGroup it was constructed under.
+struct AllocationRecord {
+  AllocationRecord() : size(0), allocation_group(NULL) {}
+  AllocationRecord(size_t sz, AllocationGroup* group)
+      : size(sz), allocation_group(group) {}
+
+  static AllocationRecord Empty() { return AllocationRecord(); }
+  bool IsEmpty() const { return !size && !allocation_group; }
+  size_t size;
+  AllocationGroup* allocation_group;
+};
+
+}  // namespace analytics
+}  // namespace nb
+
+#endif  // NB_MEMORY_TRACKER_H_
diff --git a/src/nb/analytics/memory_tracker_helpers.cc b/src/nb/analytics/memory_tracker_helpers.cc
new file mode 100644
index 0000000..2f4776a
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_helpers.cc
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ */
+
+#include "nb/analytics/memory_tracker_helpers.h"
+
+#include <stdint.h>
+#include <vector>
+
+#include "nb/hash.h"
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+
+namespace nb {
+namespace analytics {
+
+AllocationGroup::AllocationGroup(const std::string& name)
+    : name_(name), allocation_bytes_(0), num_allocations_(0) {}
+
+AllocationGroup::~AllocationGroup() {}
+
+void AllocationGroup::AddAllocation(int64_t num_bytes) {
+  if (num_bytes == 0)
+    return;
+  int num_alloc_diff = num_bytes > 0 ? 1 : -1;
+
+  allocation_bytes_.fetch_add(num_bytes);
+  num_allocations_.fetch_add(num_alloc_diff);
+}
+
+void AllocationGroup::GetAggregateStats(int32_t* num_allocs,
+                                        int64_t* allocation_bytes) const {
+  *num_allocs = num_allocations_.load();
+  *allocation_bytes = allocation_bytes_.load();
+}
+
+int64_t AllocationGroup::allocation_bytes() const {
+  return allocation_bytes_.load();
+}
+
+int32_t AllocationGroup::num_allocations() const {
+  return num_allocations_.load();
+}
+
+AtomicStringAllocationGroupMap::AtomicStringAllocationGroupMap() {
+  unaccounted_group_ = Ensure("Unaccounted");
+}
+
+AtomicStringAllocationGroupMap::~AtomicStringAllocationGroupMap() {
+  unaccounted_group_ = NULL;
+  while (!group_map_.empty()) {
+    Map::iterator it = group_map_.begin();
+    delete it->second;
+    group_map_.erase(it);
+  }
+}
+
+AllocationGroup* AtomicStringAllocationGroupMap::Ensure(
+    const std::string& name) {
+  starboard::ScopedLock lock(mutex_);
+  Map::const_iterator found_it = group_map_.find(name);
+  if (found_it != group_map_.end()) {
+    return found_it->second;
+  }
+  AllocationGroup* group = new AllocationGroup(name);
+  group_map_[name] = group;
+  return group;
+}
+
+AllocationGroup* AtomicStringAllocationGroupMap::GetDefaultUnaccounted() {
+  return unaccounted_group_;
+}
+
+bool AtomicStringAllocationGroupMap::Erase(const std::string& name) {
+  starboard::ScopedLock lock(mutex_);
+  Map::iterator found_it = group_map_.find(name);
+  if (found_it == group_map_.end()) {
+    // Didn't find it.
+    return false;
+  }
+  group_map_.erase(found_it);
+  return true;
+}
+
+void AtomicStringAllocationGroupMap::GetAll(
+    std::vector<const AllocationGroup*>* output) const {
+  starboard::ScopedLock lock(mutex_);
+  for (Map::const_iterator it = group_map_.begin(); it != group_map_.end();
+       ++it) {
+    output->push_back(it->second);
+  }
+}
+
+void AllocationGroupStack::Push(AllocationGroup* group) {
+  alloc_group_stack_.push_back(group);
+}
+
+void AllocationGroupStack::Pop() {
+  alloc_group_stack_.pop_back();
+}
+
+AllocationGroup* AllocationGroupStack::Peek() {
+  if (alloc_group_stack_.empty()) {
+    return NULL;
+  }
+  return alloc_group_stack_.back();
+}
+
+AtomicAllocationMap::AtomicAllocationMap() {}
+
+AtomicAllocationMap::~AtomicAllocationMap() {}
+
+bool AtomicAllocationMap::Add(const void* memory,
+                              const AllocationRecord& alloc_record) {
+  starboard::ScopedLock lock(mutex_);
+  const bool inserted =
+      pointer_map_.insert(std::make_pair(memory, alloc_record)).second;
+  return inserted;
+}
+
+bool AtomicAllocationMap::Get(const void* memory,
+                              AllocationRecord* alloc_record) const {
+  starboard::ScopedLock lock(mutex_);
+  PointerMap::const_iterator found_it = pointer_map_.find(memory);
+  if (found_it == pointer_map_.end()) {
+    if (alloc_record) {
+      *alloc_record = AllocationRecord::Empty();
+    }
+    return false;
+  }
+  if (alloc_record) {
+    *alloc_record = found_it->second;
+  }
+  return true;
+}
+
+bool AtomicAllocationMap::Remove(const void* memory,
+                                 AllocationRecord* alloc_record) {
+  starboard::ScopedLock lock(mutex_);
+  PointerMap::iterator found_it = pointer_map_.find(memory);
+
+  if (found_it == pointer_map_.end()) {
+    if (alloc_record) {
+      *alloc_record = AllocationRecord::Empty();
+    }
+    return false;
+  }
+  if (alloc_record) {
+    *alloc_record = found_it->second;
+  }
+  pointer_map_.erase(found_it);
+  return true;
+}
+
+bool AtomicAllocationMap::Accept(AllocationVisitor* visitor) const {
+  starboard::ScopedLock lock(mutex_);
+  for (PointerMap::const_iterator it = pointer_map_.begin();
+       it != pointer_map_.end(); ++it) {
+    const void* memory = it->first;
+    const AllocationRecord& alloc_rec = it->second;
+    if (!visitor->Visit(memory, alloc_rec)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+size_t AtomicAllocationMap::Size() const {
+  starboard::ScopedLock lock(mutex_);
+  return pointer_map_.size();
+}
+
+bool AtomicAllocationMap::Empty() const {
+  starboard::ScopedLock lock(mutex_);
+  return pointer_map_.empty();
+}
+
+void AtomicAllocationMap::Clear() {
+  starboard::ScopedLock lock(mutex_);
+  for (PointerMap::iterator it = pointer_map_.begin();
+       it != pointer_map_.end(); ++it) {
+    const AllocationRecord& rec = it->second;
+    AllocationGroup* group = rec.allocation_group;
+    group->AddAllocation(-rec.size);
+  }
+  return pointer_map_.clear();
+}
+
+ConcurrentAllocationMap::ConcurrentAllocationMap() : pointer_map_array_() {}
+
+ConcurrentAllocationMap::~ConcurrentAllocationMap() {
+  Clear();
+}
+
+bool ConcurrentAllocationMap::Add(const void* memory,
+                                  const AllocationRecord& alloc_record) {
+  AtomicAllocationMap& map = GetMapForPointer(memory);
+  return map.Add(memory, alloc_record);
+}
+
+bool ConcurrentAllocationMap::Get(const void* memory,
+                                  AllocationRecord* alloc_record) const {
+  const AtomicAllocationMap& map = GetMapForPointer(memory);
+  return map.Get(memory, alloc_record);
+}
+
+bool ConcurrentAllocationMap::Remove(const void* memory,
+                                     AllocationRecord* alloc_record) {
+  AtomicAllocationMap& map = GetMapForPointer(memory);
+  bool output = map.Remove(memory, alloc_record);
+  return output;
+}
+
+size_t ConcurrentAllocationMap::Size() const {
+  size_t size = 0;
+  for (int i = 0; i < kNumElements; ++i) {
+    const AtomicAllocationMap& map = pointer_map_array_[i];
+    size += map.Size();
+  }
+  return size;
+}
+
+bool ConcurrentAllocationMap::Empty() const {
+  return 0 == Size();
+}
+
+void ConcurrentAllocationMap::Clear() {
+  for (int i = 0; i < kNumElements; ++i) {
+    AtomicAllocationMap& map = pointer_map_array_[i];
+    map.Clear();
+  }
+}
+
+bool ConcurrentAllocationMap::Accept(AllocationVisitor* visitor) const {
+  for (int i = 0; i < kNumElements; ++i) {
+    const AtomicAllocationMap& map = pointer_map_array_[i];
+    if (!map.Accept(visitor)) {
+      return false;  // Early out.
+    }
+  }
+  return true;
+}
+
+size_t ConcurrentAllocationMap::hash_ptr(const void* ptr) {
+  uintptr_t val = reinterpret_cast<uintptr_t>(ptr);
+
+  return RuntimeHash32(reinterpret_cast<const char*>(&val), sizeof(val));
+}
+
+int ConcurrentAllocationMap::ToIndex(const void* ptr) const {
+  SB_DCHECK(0 != kNumElements);
+  uint32_t hash_val = hash_ptr(ptr);
+  int index = hash_val % kNumElements;
+  return index;
+}
+
+AtomicAllocationMap& ConcurrentAllocationMap::GetMapForPointer(
+    const void* ptr) {
+  return pointer_map_array_[ToIndex(ptr)];
+}
+
+const AtomicAllocationMap& ConcurrentAllocationMap::GetMapForPointer(
+    const void* ptr) const {
+  return pointer_map_array_[ToIndex(ptr)];
+}
+
+SimpleThread::SimpleThread(const std::string& name)
+    : thread_(kSbThreadInvalid), name_(name) {}
+
+SimpleThread::~SimpleThread() {}
+
+void SimpleThread::Start() {
+  SbThreadEntryPoint entry_point = ThreadEntryPoint;
+
+  thread_ = SbThreadCreate(0,                    // default stack_size.
+                           kSbThreadNoPriority,  // default priority.
+                           kSbThreadNoAffinity,  // default affinity.
+                           true,                 // joinable.
+                           name_.c_str(), entry_point, this);
+
+  // SbThreadCreate() above produced an invalid thread handle.
+  SB_DCHECK(thread_ != kSbThreadInvalid);
+  return;
+}
+
+void* SimpleThread::ThreadEntryPoint(void* context) {
+  SimpleThread* this_ptr = static_cast<SimpleThread*>(context);
+  this_ptr->Run();
+  return NULL;
+}
+
+void SimpleThread::DoJoin() {
+  if (!SbThreadJoin(thread_, NULL)) {
+    SB_DCHECK(false) << "Could not join thread.";
+  }
+}
+
+}  // namespace analytics
+}  // namespace nb
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
new file mode 100644
index 0000000..d07651c
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+
+#ifndef NB_MEMORY_TRACKER_HELPERS_H_
+#define NB_MEMORY_TRACKER_HELPERS_H_
+
+#include <map>
+#include <vector>
+
+#include "nb/analytics/memory_tracker.h"
+#include "nb/atomic.h"
+#include "starboard/mutex.h"
+#include "starboard/thread.h"
+#include "starboard/types.h"
+#include "starboard/log.h"
+
+namespace nb {
+namespace analytics {
+
+class AllocationGroup;
+struct AllocationRecord;
+
+template <typename Type>
+class ThreadLocalPointer {
+ public:
+  ThreadLocalPointer() {
+    slot_ = SbThreadCreateLocalKey(NULL);  // No destructor for pointer.
+    SB_DCHECK(kSbThreadLocalKeyInvalid != slot_);
+  }
+
+  ~ThreadLocalPointer() { SbThreadDestroyLocalKey(slot_); }
+
+  Type* Get() const {
+    void* ptr = SbThreadGetLocalValue(slot_);
+    Type* type_ptr = static_cast<Type*>(ptr);
+    return type_ptr;
+  }
+
+  void Set(Type* ptr) {
+    void* void_ptr = static_cast<void*>(ptr);
+    SbThreadSetLocalValue(slot_, void_ptr);
+  }
+
+ private:
+  SbThreadLocalKey slot_;
+  SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);
+};
+
+class ThreadLocalBoolean {
+ public:
+  ThreadLocalBoolean() : default_value_(false) {}
+  explicit ThreadLocalBoolean(bool default_value)
+      : default_value_(default_value) {}
+  ~ThreadLocalBoolean() {}
+
+  bool Get() const {
+    bool val = tlp_.Get() != NULL;
+    return val ^ default_value_;
+  }
+
+  void Set(bool val) {
+    val = val ^ default_value_;
+    tlp_.Set(val ? TruePointer() : FalsePointer());
+  }
+
+ private:
+  static void* TruePointer() { return reinterpret_cast<void*>(0x1); }
+  static void* FalsePointer() { return NULL; }
+  ThreadLocalPointer<void> tlp_;
+  const bool default_value_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean);
+};
+
+// An AllocationGroup is a collection of allocations that are logically lumped
+// together, such as "Javascript" or "Graphics".
+class AllocationGroup {
+ public:
+  AllocationGroup(const std::string& name);
+  ~AllocationGroup();
+  const std::string& name() const { return name_; }
+
+  void AddAllocation(int64_t num_bytes);
+  void GetAggregateStats(int32_t* num_allocs, int64_t* allocation_bytes) const;
+
+  int64_t allocation_bytes() const;
+  int32_t num_allocations() const;
+
+ private:
+  const std::string name_;
+  nb::atomic_int64_t allocation_bytes_;
+  nb::atomic_int32_t num_allocations_;
+};
+
+// A self locking data structure that maps strings -> AllocationGroups. This is
+// used to resolve MemoryGroup names (e.g. "Javascript") to an AllocationGroup
+// which can be used to group allocations together.
+class AtomicStringAllocationGroupMap {
+ public:
+  AtomicStringAllocationGroupMap();
+  ~AtomicStringAllocationGroupMap();
+
+  AllocationGroup* Ensure(const std::string& name);
+  AllocationGroup* GetDefaultUnaccounted();
+
+  bool Erase(const std::string& key);
+  void GetAll(std::vector<const AllocationGroup*>* output) const;
+
+ private:
+  typedef std::map<std::string, AllocationGroup*> Map;
+  Map group_map_;
+  AllocationGroup* unaccounted_group_;
+  mutable starboard::Mutex mutex_;
+};
+
+class AllocationGroupStack {
+ public:
+  AllocationGroupStack() { Push_DebugBreak(NULL); }
+  ~AllocationGroupStack() {}
+
+  void Push(AllocationGroup* group);
+  void Pop();
+  AllocationGroup* Peek();
+
+  void Push_DebugBreak(AllocationGroup* ag) { debug_stack_.push_back(ag); }
+  void Pop_DebugBreak() { debug_stack_.pop_back(); }
+  AllocationGroup* Peek_DebugBreak() {
+    if (debug_stack_.empty()) {
+      return NULL;
+    }
+    return debug_stack_.back();
+  }
+
+ private:
+  SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroupStack);
+  typedef std::vector<AllocationGroup*> AllocationGroupPtrVec;
+  AllocationGroupPtrVec alloc_group_stack_, debug_stack_;
+};
+
+// A per-pointer map of allocations to AllocRecords. This map is thread safe.
+class AtomicAllocationMap {
+ public:
+  AtomicAllocationMap();
+  ~AtomicAllocationMap();
+
+  // Returns true if Added. Otherwise false means that the pointer
+  // already existed.
+  bool Add(const void* memory, const AllocationRecord& alloc_record);
+
+  // Returns true if the memory exists in this set.
+  bool Get(const void* memory, AllocationRecord* alloc_record) const;
+
+  // Return true if the memory existed in this set. If true
+  // then output alloc_record is written with record that was found.
+  // otherwise the record is written as 0 bytes and null key.
+  bool Remove(const void* memory, AllocationRecord* alloc_record);
+
+  bool Accept(AllocationVisitor* visitor) const;
+
+  size_t Size() const;
+  bool Empty() const;
+  void Clear();
+
+ private:
+  SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap);
+  typedef std::map<const void*, AllocationRecord> PointerMap;
+
+  PointerMap pointer_map_;
+  mutable starboard::Mutex mutex_;
+};
+
+// A per-pointer map of allocations to AllocRecords. This is a hybrid data
+// structure consisting of a hashtable of maps. Each pointer that is
+// stored or retrieved is hashed to a random bucket. Each bucket has it's own
+// lock. This distributed pattern increases performance significantly by
+// reducing contention. The top-level hashtable is of constant size and does
+// not resize. Each bucket is implemented as it's own map of elements.
+class ConcurrentAllocationMap {
+ public:
+  static const int kNumElements = 511;
+  ConcurrentAllocationMap();
+  ~ConcurrentAllocationMap();
+
+  // Returns true if Added. Otherwise false means that the pointer
+  // already existed.
+  bool Add(const void* memory, const AllocationRecord& alloc_record);
+  // Returns true if the memory exists in this set.
+  bool Get(const void* memory, AllocationRecord* alloc_record) const;
+  // Return true if the memory existed in this set. If true
+  // then output alloc_record is written with record that was found.
+  // otherwise the record is written as 0 bytes and null key.
+  bool Remove(const void* memory, AllocationRecord* alloc_record);
+  size_t Size() const;
+  bool Empty() const;
+  void Clear();
+
+  // Provides access to all the allocations within in a thread safe manner.
+  bool Accept(AllocationVisitor* visitor) const;
+
+  AtomicAllocationMap& GetMapForPointer(const void* ptr);
+  const AtomicAllocationMap& GetMapForPointer(const void* ptr) const;
+
+ private:
+  SB_DISALLOW_COPY_AND_ASSIGN(ConcurrentAllocationMap);
+  // Takes a pointer and generates a hash.
+  static size_t hash_ptr(const void* ptr);
+
+  int ToIndex(const void* ptr) const;
+  AtomicAllocationMap pointer_map_array_[kNumElements];
+};
+
+class SimpleThread {
+ public:
+  explicit SimpleThread(const std::string& name);
+  virtual ~SimpleThread() = 0;
+
+  // Subclasses should override the Run method.
+  virtual void Run() = 0;
+
+  void Join() {
+    Cancel();
+    DoJoin();
+  }
+
+  // If Join() is intended to interrupt the Run() function then override
+  // Cancel() to send a signal.
+  // Example:
+  //   virtual void Cancel() { finished_ = true; }
+  //   virtual void Run() {
+  //     while (!finished_) { /* do work */ }
+  //   }
+  virtual void Cancel() {}
+
+  // Calls SbThreadCreate() and starts running code.
+  void Start();
+
+ private:
+  static void* ThreadEntryPoint(void* context);
+  void DoJoin();
+  void DoStart();
+
+  const std::string name_;
+  SbThread thread_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);
+};
+
+}  // namespace analytics
+}  // namespace nb
+
+#endif  // NB_MEMORY_TRACKER_HELPERS_H_
diff --git a/src/nb/analytics/memory_tracker_helpers_test.cc b/src/nb/analytics/memory_tracker_helpers_test.cc
new file mode 100644
index 0000000..ebef340
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_helpers_test.cc
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace nb {
+namespace analytics {
+namespace {
+
+///////////////////////////////////////////////////////////////////////////////
+TEST(AtomicStringAllocationGroupMap, Use) {
+  AtomicStringAllocationGroupMap map;
+  AllocationGroup* tag = map.Ensure("MemoryRegion");
+  EXPECT_TRUE(tag != NULL);
+  EXPECT_EQ(std::string("MemoryRegion"), tag->name());
+  AllocationGroup* tag2 = map.Ensure("MemoryRegion");
+  EXPECT_EQ(tag, tag2);
+}
+
+TEST(AtomicAllocationMap, AddHasRemove) {
+  AtomicAllocationMap atomic_pointer_map;
+  int32_t int_a = 0;
+  int32_t int_b = 1;
+
+  // Initially empty.
+  EXPECT_TRUE(atomic_pointer_map.Empty());
+
+  const AllocationRecord int32_alloc_record =
+      AllocationRecord(sizeof(int32_t), NULL);
+  AllocationRecord alloc_output;
+
+  EXPECT_TRUE(atomic_pointer_map.Add(&int_a, int32_alloc_record));
+  EXPECT_FALSE(atomic_pointer_map.Add(&int_a, int32_alloc_record));
+  EXPECT_TRUE(atomic_pointer_map.Get(&int_a, &alloc_output));
+  EXPECT_EQ(alloc_output.size, sizeof(int32_t));
+
+  EXPECT_FALSE(atomic_pointer_map.Get(&int_b, &alloc_output));
+  EXPECT_EQ(0, alloc_output.size);
+  // Adding pointer to int_a increases set to 1 element.
+  EXPECT_EQ(atomic_pointer_map.Size(), 1);
+  EXPECT_FALSE(atomic_pointer_map.Empty());
+
+  // Adds pointer to int_b.
+  EXPECT_TRUE(atomic_pointer_map.Add(&int_b, int32_alloc_record));
+  EXPECT_TRUE(atomic_pointer_map.Get(&int_b, &alloc_output));
+  EXPECT_EQ(sizeof(int32_t), alloc_output.size);
+  // Expect that the second pointer added will increase the number of elements.
+  EXPECT_EQ(atomic_pointer_map.Size(), 2);
+  EXPECT_FALSE(atomic_pointer_map.Empty());
+
+  // Now remove the elements and ensure that they no longer found and that
+  // the size of the table shrinks to empty.
+  EXPECT_TRUE(atomic_pointer_map.Remove(&int_a, &alloc_output));
+  EXPECT_EQ(sizeof(int32_t), alloc_output.size);
+  EXPECT_EQ(atomic_pointer_map.Size(), 1);
+  EXPECT_FALSE(atomic_pointer_map.Remove(&int_a, &alloc_output));
+  EXPECT_EQ(0, alloc_output.size);
+  EXPECT_TRUE(atomic_pointer_map.Remove(&int_b, &alloc_output));
+  EXPECT_EQ(atomic_pointer_map.Size(), 0);
+
+  EXPECT_TRUE(atomic_pointer_map.Empty());
+}
+
+TEST(ConcurrentAllocationMap, AddHasRemove) {
+  ConcurrentAllocationMap alloc_map;
+  int32_t int_a = 0;
+  int32_t int_b = 1;
+
+  // Initially empty.
+  EXPECT_TRUE(alloc_map.Empty());
+
+  const AllocationRecord int32_alloc_record =
+      AllocationRecord(sizeof(int32_t), NULL);
+  AllocationRecord alloc_output;
+
+  EXPECT_TRUE(alloc_map.Add(&int_a, int32_alloc_record));
+  EXPECT_FALSE(alloc_map.Add(&int_a, int32_alloc_record));
+  EXPECT_TRUE(alloc_map.Get(&int_a, &alloc_output));
+  EXPECT_EQ(alloc_output.size, sizeof(int32_t));
+
+  EXPECT_FALSE(alloc_map.Get(&int_b, &alloc_output));
+  EXPECT_EQ(0, alloc_output.size);
+  // Adding pointer to int_a increases set to 1 element.
+  EXPECT_EQ(alloc_map.Size(), 1);
+  EXPECT_FALSE(alloc_map.Empty());
+
+  // Adds pointer to int_b.
+  EXPECT_TRUE(alloc_map.Add(&int_b, int32_alloc_record));
+  EXPECT_TRUE(alloc_map.Get(&int_b, &alloc_output));
+  EXPECT_EQ(sizeof(int32_t), alloc_output.size);
+  // Expect that the second pointer added will increase the number of elements.
+  EXPECT_EQ(alloc_map.Size(), 2);
+  EXPECT_FALSE(alloc_map.Empty());
+
+  // Now remove the elements and ensure that they no longer found and that
+  // the size of the table shrinks to empty.
+  EXPECT_TRUE(alloc_map.Remove(&int_a, &alloc_output));
+  EXPECT_EQ(sizeof(int32_t), alloc_output.size);
+  EXPECT_EQ(alloc_map.Size(), 1);
+  EXPECT_FALSE(alloc_map.Remove(&int_a, &alloc_output));
+  EXPECT_EQ(0, alloc_output.size);
+  EXPECT_TRUE(alloc_map.Remove(&int_b, &alloc_output));
+  EXPECT_EQ(alloc_map.Size(), 0);
+
+  EXPECT_TRUE(alloc_map.Empty());
+}
+
+}  // namespace
+}  // namespace analytics
+}  // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.cc b/src/nb/analytics/memory_tracker_impl.cc
new file mode 100644
index 0000000..fecda24
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_impl.cc
@@ -0,0 +1,541 @@
+/*
+ * 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.
+ */
+
+#include "nb/analytics/memory_tracker_impl.h"
+
+#include <iomanip>
+#include <sstream>
+
+#include "nb/atomic.h"
+#include "starboard/atomic.h"
+
+namespace nb {
+namespace analytics {
+
+SbMemoryReporter* MemoryTrackerImpl::GetMemoryReporter() {
+  return &sb_memory_tracker_;
+}
+
+NbMemoryScopeReporter* MemoryTrackerImpl::GetMemoryScopeReporter() {
+  return &nb_memory_scope_reporter_;
+}
+
+int64_t MemoryTrackerImpl::GetTotalAllocationBytes() {
+  return total_bytes_allocated_.load();
+}
+
+AllocationGroup* MemoryTrackerImpl::GetAllocationGroup(const char* name) {
+  DisableMemoryTrackingInScope no_tracking(this);
+  AllocationGroup* alloc_group = alloc_group_map_.Ensure(name);
+  return alloc_group;
+}
+
+void MemoryTrackerImpl::PushAllocationGroupByName(const char* group_name) {
+  AllocationGroup* group = GetAllocationGroup(group_name);
+  PushAllocationGroup(group);
+}
+
+void MemoryTrackerImpl::PushAllocationGroup(AllocationGroup* alloc_group) {
+  if (alloc_group == NULL) {
+    alloc_group = alloc_group_map_.GetDefaultUnaccounted();
+  }
+  DisableMemoryTrackingInScope no_tracking(this);
+  allocation_group_stack_tls_.GetOrCreate()->Push(alloc_group);
+}
+
+AllocationGroup* MemoryTrackerImpl::PeekAllocationGroup() {
+  DisableMemoryTrackingInScope no_tracking(this);
+  AllocationGroup* out =
+      allocation_group_stack_tls_.GetOrCreate()->Peek();
+  if (out == NULL) {
+    out = alloc_group_map_.GetDefaultUnaccounted();
+  }
+  return out;
+}
+
+void MemoryTrackerImpl::PopAllocationGroup() {
+  DisableMemoryTrackingInScope no_tracking(this);
+  AllocationGroupStack* alloc_tls = allocation_group_stack_tls_.GetOrCreate();
+  alloc_tls->Pop();
+  AllocationGroup* group = alloc_tls->Peek();
+  // We don't allow null, so if this is encountered then push the
+  // "default unaccounted" alloc group.
+  if (group == NULL) {
+    alloc_tls->Push(alloc_group_map_.GetDefaultUnaccounted());
+  }
+}
+
+void MemoryTrackerImpl::GetAllocationGroups(
+    std::vector<const AllocationGroup*>* output) {
+  DisableMemoryTrackingInScope no_allocation_tracking(this);
+  output->reserve(100);
+  alloc_group_map_.GetAll(output);
+}
+
+void MemoryTrackerImpl::GetAllocationGroups(
+    std::map<std::string, const AllocationGroup*>* output) {
+  output->clear();
+  DisableMemoryTrackingInScope no_tracking(this);
+  std::vector<const AllocationGroup*> tmp;
+  GetAllocationGroups(&tmp);
+  for (size_t i = 0; i < tmp.size(); ++i) {
+    output->insert(std::make_pair(tmp[i]->name(), tmp[i]));
+  }
+}
+
+void MemoryTrackerImpl::Accept(AllocationVisitor* visitor) {
+  DisableMemoryTrackingInScope no_mem_tracking(this);
+  atomic_allocation_map_.Accept(visitor);
+}
+
+void MemoryTrackerImpl::Clear() {
+  // Prevent clearing of the tree from triggering a re-entrant
+  // memory deallocation.
+  atomic_allocation_map_.Clear();
+  total_bytes_allocated_.store(0);
+}
+
+void MemoryTrackerImpl::Debug_PushAllocationGroupBreakPointByName(
+    const char* group_name) {
+  DisableMemoryTrackingInScope no_tracking(this);
+  SB_DCHECK(group_name != NULL);
+  AllocationGroup* group = alloc_group_map_.Ensure(group_name);
+  Debug_PushAllocationGroupBreakPoint(group);
+}
+
+void MemoryTrackerImpl::Debug_PushAllocationGroupBreakPoint(
+    AllocationGroup* alloc_group) {
+  DisableMemoryTrackingInScope no_tracking(this);
+  allocation_group_stack_tls_.GetOrCreate()->Push_DebugBreak(alloc_group);
+}
+
+void MemoryTrackerImpl::Debug_PopAllocationGroupBreakPoint() {
+  DisableMemoryTrackingInScope no_tracking(this);
+  allocation_group_stack_tls_.GetOrCreate()->Pop_DebugBreak();
+}
+
+// Converts "2345.54" => "2,345.54".
+std::string InsertCommasIntoNumberString(const std::string& input) {
+  typedef std::vector<char> CharVector;
+  typedef CharVector::iterator CharIt;
+
+  CharVector chars(input.begin(), input.end());
+  std::reverse(chars.begin(), chars.end());
+
+  CharIt curr_it = chars.begin();
+  CharIt mid = std::find(chars.begin(), chars.end(), '.');
+  if (mid == chars.end()) {
+    mid = curr_it;
+  }
+
+  CharVector out(curr_it, mid);
+
+  int counter = 0;
+  for (CharIt it = mid; it != chars.end(); ++it) {
+    if (counter != 0 && (counter % 3 == 0)) {
+      out.push_back(',');
+    }
+    if (*it != '.') {
+      counter++;
+    }
+    out.push_back(*it);
+  }
+
+  std::reverse(out.begin(), out.end());
+  std::stringstream ss;
+  for (int i = 0; i < out.size(); ++i) {
+    ss << out[i];
+  }
+  return ss.str();
+}
+
+template <typename T>
+std::string NumberFormatWithCommas(T val) {
+  // Convert value to string.
+  std::stringstream ss;
+  ss << val;
+  std::string s = InsertCommasIntoNumberString(ss.str());
+  return s;
+}
+
+void MemoryTrackerImpl::OnMalloc(void* context,
+                                 const void* memory,
+                                 size_t size) {
+  MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context);
+  t->AddMemoryTracking(memory, size);
+}
+
+void MemoryTrackerImpl::OnDealloc(void* context, const void* memory) {
+  MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context);
+  t->RemoveMemoryTracking(memory);
+}
+
+void MemoryTrackerImpl::OnMapMem(void* context,
+                                 const void* memory,
+                                 size_t size) {
+  // We might do something more interesting with MapMemory calls later.
+  OnMalloc(context, memory, size);
+}
+
+void MemoryTrackerImpl::OnUnMapMem(void* context,
+                                   const void* memory,
+                                   size_t size) {
+  // We might do something more interesting with UnMapMemory calls later.
+  OnDealloc(context, memory);
+}
+
+void MemoryTrackerImpl::OnPushAllocationGroup(
+    void* context,
+    NbMemoryScopeInfo* memory_scope_info) {
+  MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context);
+  uintptr_t* cached_handle = &(memory_scope_info->cached_handle_);
+  const bool allows_caching = memory_scope_info->allows_caching_;
+  const char* group_name = memory_scope_info->memory_scope_name_;
+
+  AllocationGroup* group = NULL;
+  if (allows_caching && *cached_handle != 0) {
+    group = reinterpret_cast<AllocationGroup*>(cached_handle);
+  } else {
+    group = t->GetAllocationGroup(group_name);
+    if (allows_caching) {
+      // Flush all pending writes so that the the pointee is well formed
+      // by the time the pointer becomes visible to other threads.
+      SbAtomicMemoryBarrier();
+      *cached_handle = reinterpret_cast<uintptr_t>(group);
+    }
+  }
+
+  t->PushAllocationGroup(group);
+}
+
+void MemoryTrackerImpl::OnPopAllocationGroup(void* context) {
+  MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context);
+  t->PopAllocationGroup();
+}
+
+void MemoryTrackerImpl::Initialize(
+    SbMemoryReporter* sb_memory_reporter,
+    NbMemoryScopeReporter* memory_scope_reporter) {
+  SbMemoryReporter mem_reporter = {
+      MemoryTrackerImpl::OnMalloc, MemoryTrackerImpl::OnDealloc,
+
+      MemoryTrackerImpl::OnMapMem, MemoryTrackerImpl::OnUnMapMem,
+
+      this};
+
+  NbMemoryScopeReporter mem_scope_reporter = {
+      MemoryTrackerImpl::OnPushAllocationGroup,
+      MemoryTrackerImpl::OnPopAllocationGroup,
+
+      this,
+  };
+
+  *sb_memory_reporter = mem_reporter;
+  *memory_scope_reporter = mem_scope_reporter;
+}
+
+void MemoryTrackerImpl::SetThreadFilter(SbThreadId tid) {
+  thread_filter_id_ = tid;
+}
+
+bool MemoryTrackerImpl::IsCurrentThreadAllowedToReport() const {
+  if (thread_filter_id_ == kSbThreadInvalidId) {
+    return true;
+  }
+  return SbThreadGetId() == thread_filter_id_;
+}
+
+MemoryTrackerImpl::DisableDeletionInScope::DisableDeletionInScope(
+    MemoryTrackerImpl* owner)
+    : owner_(owner) {
+  prev_state_ = owner->MemoryDeletionEnabled();
+  owner_->SetMemoryDeletionEnabled(false);
+}
+
+MemoryTrackerImpl::DisableDeletionInScope::~DisableDeletionInScope() {
+  owner_->SetMemoryDeletionEnabled(prev_state_);
+}
+
+// TODO: Get rid of the nb::SimpleThread
+class MemoryTrackerPrintThread : public SimpleThread {
+ public:
+  MemoryTrackerPrintThread(MemoryTracker* owner)
+      : SimpleThread("MemoryTrackerPrintThread"),
+        finished_(false),
+        owner_(owner) {}
+
+  // Overridden so that the thread can exit gracefully.
+  virtual void Cancel() SB_OVERRIDE { finished_.store(true); }
+
+  virtual void Run() {
+    struct NoMemTracking {
+      NoMemTracking(MemoryTracker* owner) : owner_(owner) {
+        prev_val_ = owner_->IsMemoryTrackingEnabled();
+        owner_->SetMemoryTrackingEnabled(false);
+      }
+      ~NoMemTracking() { owner_->SetMemoryTrackingEnabled(prev_val_); }
+
+      bool prev_val_;
+      MemoryTracker* owner_;
+    };
+
+    while (!finished_.load()) {
+      NoMemTracking no_mem_tracking_in_this_scope(owner_);
+
+      // std::map<std::string, const AllocationGroup*> output;
+      // typedef std::map<std::string, const AllocationGroup*>::const_iterator
+      // MapIt;
+      std::vector<const AllocationGroup*> vector_output;
+      owner_->GetAllocationGroups(&vector_output);
+
+      typedef std::map<std::string, const AllocationGroup*> Map;
+      typedef Map::const_iterator MapIt;
+
+      Map output;
+      for (int i = 0; i < vector_output.size(); ++i) {
+        const AllocationGroup* group = vector_output[i];
+        output[group->name()] = group;
+      }
+
+      int32_t num_allocs = 0;
+      int64_t total_bytes = 0;
+
+      struct F {
+        static void PrintRow(std::stringstream& ss,
+                             const std::string& v1,
+                             const std::string& v2,
+                             const std::string& v3) {
+          ss.width(20);
+          ss << std::left << v1;
+          ss.width(13);
+          ss << std::right << v2 << "  ";
+          ss.width(7);
+          ss << std::right << v3 << "\n";
+        }
+      };
+
+      if (owner_->IsMemoryTrackingEnabled()) {
+        // If this isn't true then it would cause an infinite loop. The
+        // following will likely crash.
+        SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
+      }
+
+      std::stringstream ss;
+      for (MapIt it = output.begin(); it != output.end(); ++it) {
+        const AllocationGroup* group = it->second;
+        if (!group) {
+          continue;
+        }
+
+        int32_t num_group_allocs = -1;
+        int64_t total_group_bytes = -1;
+
+        group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
+        SB_DCHECK(-1 != num_group_allocs);
+        SB_DCHECK(-1 != total_group_bytes);
+        num_allocs += num_group_allocs;
+        total_bytes += total_group_bytes;
+
+        ss.width(20);
+        ss << std::left << it->first;
+        ss.width(13);
+        ss << std::right << NumberFormatWithCommas(total_group_bytes) << "  ";
+        ss.width(7);
+        ss << std::right << NumberFormatWithCommas(num_group_allocs) << "\n";
+      }
+      ss << "-------------------------------\n";
+
+      SB_LOG(INFO) << "\n"
+                   << "Total Bytes Allocated: "
+                   << NumberFormatWithCommas(total_bytes) << "\n"
+                   << "Total allocations: "
+                   << NumberFormatWithCommas(num_allocs) << "\n\n" << ss.str();
+
+      SbThreadSleep(250);
+    }
+  }
+
+ private:
+  atomic_bool finished_;
+  MemoryTracker* owner_;
+};
+
+void MemoryTrackerImpl::Debug_EnablePrintOutThread() {
+  if (debug_output_thread_) {
+    return;
+  }  // Already enabled.
+  debug_output_thread_.reset(new MemoryTrackerPrintThread(this));
+  debug_output_thread_->Start();
+}
+
+MemoryTrackerImpl::MemoryTrackerImpl()
+    : thread_filter_id_(kSbThreadInvalidId) {
+  total_bytes_allocated_.store(0);
+  global_hooks_installed_ = false;
+  Initialize(&sb_memory_tracker_, &nb_memory_scope_reporter_);
+  // Push the default region so that stats can be accounted for.
+  PushAllocationGroup(alloc_group_map_.GetDefaultUnaccounted());
+}
+
+MemoryTrackerImpl::~MemoryTrackerImpl() {
+  // If we are currently hooked into allocation tracking...
+  if (global_hooks_installed_) {
+    SbMemorySetReporter(NULL);
+    // For performance reasons no locking is used on the tracker.
+    // Therefore give enough time for other threads to exit this tracker
+    // before fully destroying this object.
+    SbThreadSleep(250 * kSbTimeMillisecond);  // 250 millisecond wait.
+  }
+  if (debug_output_thread_) {
+    debug_output_thread_->Join();
+    debug_output_thread_.reset();
+  }
+}
+
+bool MemoryTrackerImpl::AddMemoryTracking(const void* memory, size_t size) {
+  // Vars are stored to assist in debugging.
+  const bool thread_allowed_to_report = IsCurrentThreadAllowedToReport();
+  const bool valid_memory_request = (memory != NULL) && (size != 0);
+  const bool mem_track_enabled = IsMemoryTrackingEnabled();
+
+  const bool tracking_enabled =
+      mem_track_enabled && valid_memory_request && thread_allowed_to_report;
+
+  if (!tracking_enabled) {
+    return false;
+  }
+
+  // End all memory tracking in subsequent data structures.
+  DisableMemoryTrackingInScope no_memory_tracking(this);
+  AllocationGroupStack* alloc_stack =
+      allocation_group_stack_tls_.GetOrCreate();
+  AllocationGroup* group = alloc_stack->Peek();
+  if (!group) {
+    group = alloc_group_map_.GetDefaultUnaccounted();
+  }
+
+#ifndef NDEBUG
+  // This section of the code is designed to allow a developer to break
+  // execution whenever the debug allocation stack is in scope, so that the
+  // allocations can be stepped through.
+  // Example:
+  //   Debug_PushAllocationGroupBreakPointByName("Javascript");
+  //  ...now set a break point below at "static int i = 0"
+  if (group && (group == alloc_stack->Peek_DebugBreak()) &&
+      alloc_stack->Peek_DebugBreak()) {
+    static int i = 0;  // This static is here to allow an
+    ++i;               // easy breakpoint in the debugger
+  }
+#endif
+
+  AllocationRecord alloc_record(size, group);
+  bool added = atomic_allocation_map_.Add(memory, alloc_record);
+  if (added) {
+    AddAllocationBytes(size);
+    group->AddAllocation(size);
+  } else {
+    AllocationRecord unexpected_alloc;
+    atomic_allocation_map_.Get(memory, &unexpected_alloc);
+    AllocationGroup* prev_group = unexpected_alloc.allocation_group;
+
+    std::string prev_group_name;
+    if (prev_group) {
+      prev_group_name = unexpected_alloc.allocation_group->name();
+    } else {
+      prev_group_name = "none";
+    }
+
+    SB_DCHECK(added)
+        << "\nUnexpected condition, previous allocation was not removed:\n"
+        << "\tprevious alloc group: " << prev_group_name << "\n"
+        << "\tnew alloc group: " << group->name() << "\n"
+        << "\tprevious size: " << unexpected_alloc.size << "\n"
+        << "\tnew size: " << size << "\n";
+  }
+  return added;
+}
+
+size_t MemoryTrackerImpl::RemoveMemoryTracking(const void* memory) {
+  const bool do_remove = memory && MemoryDeletionEnabled();
+  if (!do_remove) {
+    return 0;
+  }
+
+  AllocationRecord alloc_record;
+  bool removed = false;
+
+  // Prevent a map::erase() from causing an endless stack overflow by
+  // disabling memory deletion for the very limited scope.
+  {
+    // Not a correct name. TODO - change.
+    DisableDeletionInScope no_memory_deletion(this);
+    removed = atomic_allocation_map_.Remove(memory, &alloc_record);
+  }
+
+  if (!removed) {
+    return 0;
+  } else {
+    const int64_t alloc_size = (static_cast<int64_t>(alloc_record.size));
+    AllocationGroup* group = alloc_record.allocation_group;
+    if (group) {
+      group->AddAllocation(-alloc_size);
+    }
+    AddAllocationBytes(-alloc_size);
+    return alloc_record.size;
+  }
+}
+
+bool MemoryTrackerImpl::GetMemoryTracking(const void* memory,
+                                          AllocationRecord* record) const {
+  const bool exists = atomic_allocation_map_.Get(memory, record);
+  return exists;
+}
+
+void MemoryTrackerImpl::SetMemoryTrackingEnabled(bool on) {
+  memory_tracking_disabled_tls_.Set(!on);
+}
+
+bool MemoryTrackerImpl::IsMemoryTrackingEnabled() const {
+  const bool enabled = !memory_tracking_disabled_tls_.Get();
+  return enabled;
+}
+
+void MemoryTrackerImpl::AddAllocationBytes(int64_t val) {
+  total_bytes_allocated_.fetch_add(val);
+}
+
+bool MemoryTrackerImpl::MemoryDeletionEnabled() const {
+  return !memory_deletion_enabled_tls_.Get();
+}
+
+void MemoryTrackerImpl::SetMemoryDeletionEnabled(bool on) {
+  memory_deletion_enabled_tls_.Set(!on);
+}
+
+MemoryTrackerImpl::DisableMemoryTrackingInScope::DisableMemoryTrackingInScope(
+    MemoryTrackerImpl* t)
+    : owner_(t) {
+  prev_value_ = owner_->IsMemoryTrackingEnabled();
+  owner_->SetMemoryTrackingEnabled(false);
+}
+
+MemoryTrackerImpl::DisableMemoryTrackingInScope::
+    ~DisableMemoryTrackingInScope() {
+  owner_->SetMemoryTrackingEnabled(prev_value_);
+}
+
+}  // namespace analytics
+}  // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h
new file mode 100644
index 0000000..7508a71
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_impl.h
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#ifndef NB_MEMORY_TRACKER_IMPL_H_
+#define NB_MEMORY_TRACKER_IMPL_H_
+
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/memory_scope.h"
+#include "nb/scoped_ptr.h"
+#include "nb/thread_local_object.h"
+#include "starboard/configuration.h"
+#include "starboard/memory_reporter.h"
+#include "starboard/memory.h"
+#include "starboard/mutex.h"
+
+namespace nb {
+namespace analytics {
+
+class MemoryTrackerImpl : public MemoryTracker {
+ public:
+  typedef ConcurrentAllocationMap AllocationMapType;
+
+  MemoryTrackerImpl();
+  virtual ~MemoryTrackerImpl();
+
+  // MemoryTracker adapter which is compatible with the SbMemoryReporter
+  // interface.
+  SbMemoryReporter* GetMemoryReporter();
+  NbMemoryScopeReporter* GetMemoryScopeReporter();
+
+  AllocationGroup* GetAllocationGroup(const char* name);
+  // Declares the start of a memory region. After this call, all
+  // memory regions will be tagged with this allocation group.
+  // Note that AllocationGroup is a tracking characteristic and
+  // does not imply any sort of special allocation pool.
+  void PushAllocationGroupByName(const char* group_name);
+  void PushAllocationGroup(AllocationGroup* alloc_group);
+  AllocationGroup* PeekAllocationGroup();
+  // Ends the current memory region and the previous memory region
+  // is restored.
+  void PopAllocationGroup();
+
+  // CONTROL
+  //
+  // Adds tracking to the supplied memory pointer. An AllocationRecord is
+  // generated for the supplied allocation which can be queried immediately
+  // with GetMemoryTracking(...).
+  bool InstallGlobalTrackingHooks() SB_OVERRIDE {
+    global_hooks_installed_ = true;
+    bool ok = SbMemorySetReporter(GetMemoryReporter());
+    ok |= NbSetMemoryScopeReporter(GetMemoryScopeReporter());
+    return ok;
+  }
+  void RemoveGlobalTrackingHooks() SB_OVERRIDE {
+    SbMemorySetReporter(NULL);
+    NbSetMemoryScopeReporter(NULL);
+  }
+
+  bool AddMemoryTracking(const void* memory, size_t size) SB_OVERRIDE;
+  size_t RemoveMemoryTracking(const void* memory) SB_OVERRIDE;
+  // Returns true if the allocation record was successfully found.
+  // If true then the output will be written to with the values.
+  // Otherwise the output is reset to the empty AllocationRecord.
+  bool GetMemoryTracking(const void* memory,
+                         AllocationRecord* record) const SB_OVERRIDE;
+  // Thread local function to get and set the memory tracking state. When set
+  // to disabled then memory allocations are not recorded. However memory
+  // deletions are still recorded.
+  void SetMemoryTrackingEnabled(bool on) SB_OVERRIDE;
+  bool IsMemoryTrackingEnabled() const SB_OVERRIDE;
+
+  // REPORTING
+  //
+  // Total allocation bytes that have been allocated by this
+  // MemoryTrackerImpl.
+  int64_t GetTotalAllocationBytes() SB_OVERRIDE;
+  // Retrieves a collection of all known allocation groups. Locking is done
+  // internally.
+  void GetAllocationGroups(std::vector<const AllocationGroup*>* output)
+      SB_OVERRIDE;
+  // Retrieves a collection of all known allocation groups. Locking is done
+  // internally. The output is a map of names to AllocationGroups.
+  void GetAllocationGroups(
+      std::map<std::string, const AllocationGroup*>* output);
+
+  // Provides access to the internal allocations in a thread safe way.
+  // Allocation tracking is disabled in the current thread for the duration
+  // of the visitation.
+  void Accept(AllocationVisitor* visitor) SB_OVERRIDE;
+
+  int64_t GetTotalNumberOfAllocations() SB_OVERRIDE {
+    return pointer_map()->Size();
+  }
+
+  // TESTING.
+  AllocationMapType* pointer_map() { return &atomic_allocation_map_; }
+  void Clear();
+
+  // This is useful for debugging. Allows the developer to set a breakpoint
+  // and see only allocations that are in the defined allocation group. This
+  // is only active in the current thread.
+  void Debug_PushAllocationGroupBreakPointByName(const char* group_name);
+  void Debug_PushAllocationGroupBreakPoint(AllocationGroup* alloc_group);
+  void Debug_PopAllocationGroupBreakPoint();
+
+  // This is useful for testing, setting this to a thread will allow ONLY
+  // those allocations from the set thread.
+  // Setting this to kSbThreadInvalidId (default) allows all threads to report
+  // allocations.
+  void SetThreadFilter(SbThreadId tid);
+  bool IsCurrentThreadAllowedToReport() const;
+  // Spawns a thread who's lifetime is coupled to that of this owning
+  // MemoryTrackerImpl. This thread will output the state of the memory
+  // periodically.
+  void Debug_EnablePrintOutThread();
+
+ private:
+  struct DisableMemoryTrackingInScope {
+    DisableMemoryTrackingInScope(MemoryTrackerImpl* t);
+    ~DisableMemoryTrackingInScope();
+    MemoryTrackerImpl* owner_;
+    bool prev_value_;
+  };
+
+  // Disables all memory deletion in the current scope. This is used in one
+  // location.
+  struct DisableDeletionInScope {
+    DisableDeletionInScope(MemoryTrackerImpl* owner);
+    ~DisableDeletionInScope();
+    MemoryTrackerImpl* owner_;
+    bool prev_state_;
+  };
+
+  // These are functions that are used specifically SbMemoryReporter.
+  static void OnMalloc(void* context, const void* memory, size_t size);
+  static void OnDealloc(void* context, const void* memory);
+  static void OnMapMem(void* context, const void* memory, size_t size);
+  static void OnUnMapMem(void* context, const void* memory, size_t size);
+  static void OnPushAllocationGroup(void* context,
+                                    NbMemoryScopeInfo* memory_scope_info);
+  static void OnPopAllocationGroup(void* context);
+
+  void Initialize(SbMemoryReporter* memory_reporter,
+                  NbMemoryScopeReporter* nb_memory_scope_reporter);
+  void AddAllocationBytes(int64_t val);
+  bool MemoryDeletionEnabled() const;
+
+  void SetMemoryDeletionEnabled(bool on);
+
+  SbMemoryReporter sb_memory_tracker_;
+  NbMemoryScopeReporter nb_memory_scope_reporter_;
+  SbThreadId thread_filter_id_;
+
+  AllocationMapType atomic_allocation_map_;
+  AtomicStringAllocationGroupMap alloc_group_map_;
+
+  atomic_int64_t total_bytes_allocated_;
+  scoped_ptr<SimpleThread> debug_output_thread_;
+
+  // THREAD LOCAL SECTION.
+  ThreadLocalBoolean memory_deletion_enabled_tls_;
+  ThreadLocalBoolean memory_tracking_disabled_tls_;
+  ThreadLocalObject<AllocationGroupStack> allocation_group_stack_tls_;
+  bool global_hooks_installed_;
+};
+
+}  // namespace analytics
+}  // namespace nb
+
+#endif  // NB_MEMORY_TRACKER_IMPL_H_
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc
new file mode 100644
index 0000000..8236447
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -0,0 +1,802 @@
+/*
+ * 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.
+ */
+
+#include "nb/analytics/memory_tracker_impl.h"
+#include "nb/memory_scope.h"
+#include "nb/scoped_ptr.h"
+#include "nb/test_thread.h"
+#include "starboard/system.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define STRESS_TEST_DURATION_SECONDS 1
+#define NUM_STRESS_TEST_THREADS 3
+
+namespace nb {
+namespace analytics {
+namespace {
+
+MemoryTrackerImpl* s_memory_tracker_ = NULL;
+
+struct NoMemTracking {
+  bool prev_val;
+  NoMemTracking() : prev_val(false) {
+    if (s_memory_tracker_) {
+      prev_val = s_memory_tracker_->IsMemoryTrackingEnabled();
+      s_memory_tracker_->SetMemoryTrackingEnabled(false);
+    }
+  }
+  ~NoMemTracking() {
+    if (s_memory_tracker_) {
+      s_memory_tracker_->SetMemoryTrackingEnabled(prev_val);
+    }
+  }
+};
+
+// EXPECT_XXX and ASSERT_XXX allocate memory, a big no-no when
+// for unit testing allocations. These overrides disable memory
+// tracking for the duration of the EXPECT and ASSERT operations.
+#define EXPECT_EQ_NO_TRACKING(A, B)                 \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    EXPECT_EQ(A, B);                                \
+  }
+
+#define EXPECT_TRUE_NO_TRACKING(A)                  \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    EXPECT_TRUE(A);                                 \
+  }
+
+#define EXPECT_FALSE_NO_TRACKING(A)                 \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    EXPECT_FALSE(A);                                \
+  }
+
+#define ASSERT_EQ_NO_TRACKING(A, B)                 \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    ASSERT_EQ(A, B);                                \
+  }
+
+#define ASSERT_TRUE_NO_TRACKING(A)                  \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    ASSERT_TRUE(A);                                 \
+  }
+
+// !! converts int -> bool.
+bool FlipCoin() {
+  return !!(SbSystemGetRandomUInt64() & 0x1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Stress testing the Allocation Tracker.
+class MemoryScopeThread : public nb::TestThread {
+ public:
+  typedef nb::TestThread Super;
+
+  explicit MemoryScopeThread(MemoryTrackerImpl* memory_tracker)
+      : memory_tracker_(memory_tracker) {
+    static int s_counter = 0;
+
+    std::stringstream ss;
+    ss << "MemoryScopeThread_" << s_counter++;
+    unique_name_ = ss.str();
+  }
+  virtual ~MemoryScopeThread() {}
+
+  // Overridden so that the thread can exit gracefully.
+  virtual void Join() {
+    finished_ = true;
+    Super::Join();
+  }
+  virtual void Run() {
+    while (!finished_) {
+      TRACK_MEMORY_SCOPE_DYNAMIC(unique_name_.c_str());
+      AllocationGroup* group = memory_tracker_->PeekAllocationGroup();
+
+      const int cmp_result = group->name().compare(unique_name_);
+      if (cmp_result != 0) {
+        GTEST_FAIL() << "unique name mismatch";
+        return;
+      }
+    }
+  }
+
+ private:
+  MemoryTrackerImpl* memory_tracker_;
+  bool finished_;
+  std::string unique_name_;
+  int do_delete_counter_;
+  int do_malloc_counter_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Stress testing the Allocation Tracker.
+class AllocationStressThread : public nb::TestThread {
+ public:
+  explicit AllocationStressThread(MemoryTrackerImpl* memory_tracker);
+  virtual ~AllocationStressThread();
+
+  // Overridden so that the thread can exit gracefully.
+  virtual void Join();
+  virtual void Run();
+
+ private:
+  typedef std::map<const void*, AllocationRecord> AllocMap;
+
+  void CheckPointers();
+  bool RemoveRandomAllocation(std::pair<const void*, AllocationRecord>* output);
+  bool DoDelete();
+  void DoMalloc();
+
+  MemoryTrackerImpl* memory_tracker_;
+  bool finished_;
+  std::map<const void*, AllocationRecord> allocated_pts_;
+  std::string unique_name_;
+  int do_delete_counter_;
+  int do_malloc_counter_;
+};
+
+class AddAllocationStressThread : public nb::TestThread {
+ public:
+  typedef std::map<const void*, AllocationRecord> AllocMap;
+
+  AddAllocationStressThread(MemoryTracker* memory_tracker,
+                            int num_elements_add,
+                            AllocMap* destination_map,
+                            starboard::Mutex* destination_map_mutex)
+      : memory_tracker_(memory_tracker),
+        num_elements_to_add_(num_elements_add),
+        destination_map_(destination_map),
+        destination_map_mutex_(destination_map_mutex) {}
+
+  virtual void Run() {
+    for (int i = 0; i < num_elements_to_add_; ++i) {
+      const int alloc_size = std::rand() % 100 + 8;
+      void* ptr = SbMemoryAllocate(alloc_size);
+
+      AllocationRecord record;
+      if (memory_tracker_->GetMemoryTracking(ptr, &record)) {
+        NoMemTracking no_mem_tracking;  // simplifies test.
+
+        starboard::ScopedLock lock(*destination_map_mutex_);
+        destination_map_->insert(std::make_pair(ptr, record));
+      } else {
+        ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not add pointer.";
+      }
+      if (FlipCoin()) {
+        SbThreadYield();  // Give other threads a chance to run.
+      }
+    }
+  }
+
+ private:
+  MemoryTracker* memory_tracker_;
+  AllocMap* destination_map_;
+  starboard::Mutex* destination_map_mutex_;
+  int num_elements_to_add_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Framework which initializes the MemoryTracker once and installs it
+// for the first test and the removes the MemoryTracker after the
+// the last test finishes.
+class MemoryTrackerImplTest : public ::testing::Test {
+ public:
+  typedef MemoryTrackerImpl::AllocationMapType AllocationMapType;
+  MemoryTrackerImplTest() {}
+
+  MemoryTrackerImpl* memory_tracker() { return s_memory_tracker_; }
+
+  bool GetAllocRecord(void* alloc_memory, AllocationRecord* output) {
+    return memory_tracker()->GetMemoryTracking(alloc_memory, output);
+  }
+
+  AllocationMapType* pointer_map() { return memory_tracker()->pointer_map(); }
+
+  size_t NumberOfAllocations() {
+    AllocationMapType* map = pointer_map();
+    return map->Size();
+  }
+
+  int64_t TotalAllocationBytes() {
+    return memory_tracker()->GetTotalAllocationBytes();
+  }
+
+  bool MemoryTrackerEnabled() const { return s_memory_tracker_enabled_; }
+
+ protected:
+  static void SetUpTestCase() {
+    if (!s_memory_tracker_) {
+      s_memory_tracker_ = new MemoryTrackerImpl;
+      s_memory_tracker_enabled_ =
+          s_memory_tracker_->InstallGlobalTrackingHooks();
+    }
+  }
+  static void TearDownTestCase() { SbMemorySetReporter(NULL); }
+
+  virtual void SetUp() {
+    memory_tracker()->Clear();
+    memory_tracker()->SetThreadFilter(SbThreadGetId());
+  }
+
+  virtual void TearDown() { memory_tracker()->Clear(); }
+  static bool s_memory_tracker_enabled_;
+};
+bool MemoryTrackerImplTest::s_memory_tracker_enabled_ = false;
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryTrackerImplTest
+TEST_F(MemoryTrackerImplTest, NoMemTracking) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  ASSERT_EQ_NO_TRACKING(0, NumberOfAllocations());
+  scoped_ptr<int> dummy(new int());
+  EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations());
+  {
+    // Now that memory allocation is disabled, no more allocations should
+    // be recorded.
+    NoMemTracking no_memory_tracking_in_this_scope;
+    int* dummy2 = new int();
+    EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations());
+    delete dummy2;
+    EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations());
+  }
+  scoped_ptr<int> dummy2(new int());
+  EXPECT_EQ_NO_TRACKING(2, NumberOfAllocations());
+  dummy.reset(NULL);
+  EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations());
+  dummy2.reset(NULL);
+  EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations());
+}
+
+TEST_F(MemoryTrackerImplTest, RemovePointerOnNoMemoryTracking) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+
+  int* int_ptr = new int();
+  {
+    NoMemTracking no_memory_tracking_in_this_scope;
+    delete int_ptr;
+  }
+  EXPECT_FALSE_NO_TRACKING(pointer_map()->Get(int_ptr, NULL));
+}
+
+TEST_F(MemoryTrackerImplTest, NewDeleteOverridenTest) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations());
+  int* int_a = new int(0);
+  EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations());
+  delete int_a;
+  EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations());
+}
+
+TEST_F(MemoryTrackerImplTest, TotalAllocationBytes) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  int32_t* int_a = new int32_t(0);
+  EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations());
+  EXPECT_EQ_NO_TRACKING(4, TotalAllocationBytes());
+  delete int_a;
+  EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations());
+}
+
+// Tests the expectation that a lot of allocations can be executed and that
+// internal data structures won't overflow.
+TEST_F(MemoryTrackerImplTest, NoStackOverflow) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  static const int kNumAllocations = 1000;
+  std::vector<int*> allocations;
+
+  // Also it turns out that this test is great for catching
+  // background threads pushing allocations through the allocator.
+  // This is supposed to be filtered, but if it's not then this test will
+  // fail.
+  // SbThreadYield() is used to give other threads a chance to enter into our
+  // allocator and catch a test failure.
+  SbThreadSleep(1);
+  ASSERT_EQ_NO_TRACKING(0, NumberOfAllocations());
+
+  for (int i = 0; i < kNumAllocations; ++i) {
+    SbThreadYield();
+    EXPECT_EQ_NO_TRACKING(i, NumberOfAllocations());
+    int* val = new int(0);
+    NoMemTracking no_tracking_in_scope;
+    allocations.push_back(val);
+  }
+
+  EXPECT_EQ_NO_TRACKING(kNumAllocations, NumberOfAllocations());
+
+  for (int i = 0; i < kNumAllocations; ++i) {
+    SbThreadYield();
+    EXPECT_EQ_NO_TRACKING(kNumAllocations - i, NumberOfAllocations());
+    delete allocations[i];
+  }
+
+  EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations());
+}
+
+// Tests the expectation that the macros will push/pop the memory scope.
+TEST_F(MemoryTrackerImplTest, MacrosPushPop) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  scoped_ptr<int> dummy;
+  {
+    TRACK_MEMORY_SCOPE("TestAllocations");
+    dummy.reset(new int());
+  }
+
+  scoped_ptr<int> dummy2(new int());
+
+  AllocationRecord alloc_rec;
+  pointer_map()->Get(dummy.get(), &alloc_rec);
+
+  ASSERT_TRUE_NO_TRACKING(alloc_rec.allocation_group);
+  EXPECT_EQ_NO_TRACKING(std::string("TestAllocations"),
+                        alloc_rec.allocation_group->name());
+
+  pointer_map()->Get(dummy2.get(), &alloc_rec);
+
+  ASSERT_TRUE_NO_TRACKING(alloc_rec.allocation_group);
+  EXPECT_EQ_NO_TRACKING(std::string("Unaccounted"),
+                        alloc_rec.allocation_group->name());
+}
+
+// Tests the expectation that if the cached flag on the NbMemoryScopeInfo is
+// set to false that the caching of the handle is not performed.
+TEST_F(MemoryTrackerImplTest, RespectsNonCachedHandle) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  const bool kCaching = false;
+  NbMemoryScopeInfo memory_scope = {
+      0,        "MyName",     __FILE__,
+      __LINE__, __FUNCTION__, false};  // false to disallow caching.
+
+  // Pushing the memory scope should trigger the caching operation to be
+  // attempted. However, because caching was explicitly disabled this handle
+  // should retain the value of 0.
+  NbPushMemoryScope(&memory_scope);
+  EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_, uintptr_t(0));
+
+  // ... and still assert that the group was created with the expected name.
+  AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName");
+  // Equality check.
+  EXPECT_EQ_NO_TRACKING(0, group->name().compare("MyName"));
+  NbPopMemoryScope();
+}
+
+// Tests the expectation that if the cached flag on the NbMemoryScopeInfo is
+// set to true that the caching will be applied for the cached_handle of the
+// memory scope.
+TEST_F(MemoryTrackerImplTest, PushAllocGroupCachedHandle) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  NbMemoryScopeInfo memory_scope = {
+      0,         // Cached handle.
+      "MyName",  // Memory scope name.
+      __FILE__, __LINE__, __FUNCTION__,
+      true  // Allows caching.
+  };
+
+  NbPushMemoryScope(&memory_scope);
+  EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ != uintptr_t(0));
+  AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName");
+
+  EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ ==
+                          reinterpret_cast<uintptr_t>(group));
+}
+
+// Tests the expectation that the macro TRACK_MEMORY_SCOPE will capture the
+// allocation in the MemoryTrackerImpl.
+TEST_F(MemoryTrackerImplTest, MacrosGroupAccounting) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  MemoryTrackerImpl* track_alloc = memory_tracker();  // Debugging.
+  track_alloc->Clear();
+
+  memory_tracker()->Clear();
+  const AllocationGroup* group_a =
+      memory_tracker()->GetAllocationGroup("MemoryTrackerTest-ScopeA");
+
+  const AllocationGroup* group_b =
+      memory_tracker()->GetAllocationGroup("MemoryTrackerTest-ScopeB");
+
+  ASSERT_TRUE_NO_TRACKING(group_a);
+  ASSERT_TRUE_NO_TRACKING(group_b);
+
+  int32_t num_allocations = -1;
+  int64_t allocation_bytes = -1;
+
+  // Expect that both groups have no allocations in them.
+  group_a->GetAggregateStats(&num_allocations, &allocation_bytes);
+  EXPECT_EQ_NO_TRACKING(0, num_allocations);
+  EXPECT_EQ_NO_TRACKING(0, allocation_bytes);
+
+  group_b->GetAggregateStats(&num_allocations, &allocation_bytes);
+  EXPECT_EQ_NO_TRACKING(0, num_allocations);
+  EXPECT_EQ_NO_TRACKING(0, allocation_bytes);
+
+  scoped_ptr<int> alloc_a, alloc_b, alloc_b2;
+  {
+    TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeA");
+    alloc_a.reset(new int());
+    {
+      TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeB");
+      alloc_b.reset(new int());
+      alloc_b2.reset(new int());
+
+      group_a->GetAggregateStats(&num_allocations, &allocation_bytes);
+      EXPECT_EQ_NO_TRACKING(1, num_allocations);
+      EXPECT_EQ_NO_TRACKING(4, allocation_bytes);
+      alloc_a.reset(NULL);
+      group_a->GetAggregateStats(&num_allocations, &allocation_bytes);
+      EXPECT_EQ_NO_TRACKING(0, num_allocations);
+      EXPECT_EQ_NO_TRACKING(0, allocation_bytes);
+
+      num_allocations = allocation_bytes = -1;
+      group_b->GetAggregateStats(&num_allocations, &allocation_bytes);
+      EXPECT_EQ_NO_TRACKING(2, num_allocations);
+      EXPECT_EQ_NO_TRACKING(8, allocation_bytes);
+
+      alloc_b2.reset(NULL);
+      group_b->GetAggregateStats(&num_allocations, &allocation_bytes);
+      EXPECT_EQ_NO_TRACKING(1, num_allocations);
+      EXPECT_EQ_NO_TRACKING(4, allocation_bytes);
+
+      alloc_b.reset(NULL);
+      group_b->GetAggregateStats(&num_allocations, &allocation_bytes);
+      EXPECT_EQ_NO_TRACKING(0, num_allocations);
+      EXPECT_EQ_NO_TRACKING(0, allocation_bytes);
+    }
+  }
+}
+
+// Tests the expectation that the visitor can access the allocations.
+TEST_F(MemoryTrackerImplTest, VisitorAccess) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  class SimpleVisitor : public AllocationVisitor {
+   public:
+    SimpleVisitor() : num_memory_allocs_(0) {}
+    virtual bool Visit(const void* memory,
+                       const AllocationRecord& alloc_record) {
+      num_memory_allocs_++;
+      return true;  // Keep traversing.
+    }
+
+    size_t num_memory_allocs_;
+  };
+
+  SimpleVisitor visitor;
+  scoped_ptr<int> int_ptr(new int);
+
+  // Should see the int_ptr allocation.
+  memory_tracker()->Accept(&visitor);
+  EXPECT_EQ_NO_TRACKING(1, visitor.num_memory_allocs_);
+  visitor.num_memory_allocs_ = 0;
+
+  int_ptr.reset(NULL);
+  // Now no allocations should be available.
+  memory_tracker()->Accept(&visitor);
+  EXPECT_EQ_NO_TRACKING(0, visitor.num_memory_allocs_);
+}
+
+// A stress test that rapidly adds allocations, but saves all deletions
+// for the main thread. This test will catch concurrency errors related
+// to reporting new allocations.
+TEST_F(MemoryTrackerImplTest, MultiThreadedStressAddTest) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  // Disable allocation filtering.
+  memory_tracker()->SetThreadFilter(kSbThreadInvalidId);
+
+  std::vector<nb::TestThread*> threads;
+
+  const int kNumObjectsToAdd = 10000 / NUM_STRESS_TEST_THREADS;
+  AddAllocationStressThread::AllocMap map;
+  starboard::Mutex map_mutex;
+
+  for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) {
+    nb::TestThread* thread = new AddAllocationStressThread(
+        memory_tracker(), kNumObjectsToAdd, &map, &map_mutex);
+
+    threads.push_back(thread);
+  }
+
+  for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) {
+    threads[i]->Start();
+  }
+
+  for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) {
+    threads[i]->Join();
+  }
+  for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) {
+    delete threads[i];
+  }
+
+  while (!map.empty()) {
+    const void* ptr = map.begin()->first;
+    map.erase(map.begin());
+
+    if (!memory_tracker()->GetMemoryTracking(ptr, NULL)) {
+      ADD_FAILURE_AT(__FILE__, __LINE__) << "No tracking?!";
+    }
+
+    SbMemoryDeallocate(const_cast<void*>(ptr));
+    if (memory_tracker()->GetMemoryTracking(ptr, NULL)) {
+      ADD_FAILURE_AT(__FILE__, __LINE__) << "Tracking?!";
+    }
+  }
+}
+
+// Tests the expectation that memory scopes are multi-threaded safe.
+TEST_F(MemoryTrackerImplTest, MultiThreadedMemoryScope) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  memory_tracker()->SetThreadFilter(kSbThreadInvalidId);
+  TRACK_MEMORY_SCOPE("MultiThreadedStressUseTest");
+
+  std::vector<MemoryScopeThread*> threads;
+
+  for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) {
+    threads.push_back(new MemoryScopeThread(memory_tracker()));
+  }
+
+  for (int i = 0; i < threads.size(); ++i) {
+    threads[i]->Start();
+  }
+
+  SbThreadSleep(STRESS_TEST_DURATION_SECONDS * 1000 * 1000);
+
+  for (int i = 0; i < threads.size(); ++i) {
+    threads[i]->Join();
+  }
+
+  for (int i = 0; i < threads.size(); ++i) {
+    delete threads[i];
+  }
+
+  threads.clear();
+}
+
+// Tests the expectation that new/delete can be done by different threads.
+TEST_F(MemoryTrackerImplTest, MultiThreadedStressUseTest) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+  // Disable allocation filtering.
+  memory_tracker()->SetThreadFilter(kSbThreadInvalidId);
+  TRACK_MEMORY_SCOPE("MultiThreadedStressUseTest");
+
+  std::vector<AllocationStressThread*> threads;
+
+  for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) {
+    threads.push_back(new AllocationStressThread(memory_tracker()));
+  }
+
+  for (int i = 0; i < threads.size(); ++i) {
+    threads[i]->Start();
+  }
+
+  SbThreadSleep(STRESS_TEST_DURATION_SECONDS * 1000 * 1000);
+
+  for (int i = 0; i < threads.size(); ++i) {
+    threads[i]->Join();
+  }
+
+  for (int i = 0; i < threads.size(); ++i) {
+    delete threads[i];
+  }
+
+  threads.clear();
+}
+
+//////////////////////////// Implementation ///////////////////////////////////
+/// Impl of AllocationStressThread
+AllocationStressThread::AllocationStressThread(MemoryTrackerImpl* tracker)
+    : memory_tracker_(tracker), finished_(false) {
+  static int counter = 0;
+  std::stringstream ss;
+  ss << "AllocStressThread-" << counter++;
+  unique_name_ = ss.str();
+}
+
+AllocationStressThread::~AllocationStressThread() {
+  if (!allocated_pts_.empty()) {
+    ADD_FAILURE_AT(__FILE__, __LINE__) << "allocated pointers still exist";
+  }
+}
+
+void AllocationStressThread::Join() {
+  finished_ = true;
+  nb::TestThread::Join();
+}
+
+void AllocationStressThread::CheckPointers() {
+  typedef AllocMap::iterator Iter;
+
+  for (Iter it = allocated_pts_.begin(); it != allocated_pts_.end(); ++it) {
+    const void* ptr = it->first;
+    const bool found = memory_tracker_->GetMemoryTracking(ptr, NULL);
+    if (!found) {
+      NoMemTracking no_tracking_in_scope;
+      ADD_FAILURE_AT(__FILE__, __LINE__) << "Not found";
+    }
+  }
+}
+
+void AllocationStressThread::Run() {
+  while (!finished_) {
+    const bool do_delete = FlipCoin();
+    if (FlipCoin()) {
+      DoDelete();
+    } else {
+      DoMalloc();
+    }
+    CheckPointers();
+
+    // Randomly give other threads the opportunity run.
+    if (FlipCoin()) {
+      SbThreadYield();
+    }
+  }
+
+  // Clear out all memory.
+  while (DoDelete()) {
+    ;
+  }
+}
+
+bool AllocationStressThread::RemoveRandomAllocation(
+    std::pair<const void*, AllocationRecord>* output) {
+  if (allocated_pts_.empty()) {
+    return false;
+  }
+
+  // Select a random pointer to delete.
+  int idx = std::rand() % allocated_pts_.size();
+  AllocMap::iterator iter = allocated_pts_.begin();
+  while (idx > 0) {
+    idx--;
+    iter++;
+  }
+  output->first = iter->first;
+  output->second = iter->second;
+  allocated_pts_.erase(iter);
+  return true;
+}
+
+bool AllocationStressThread::DoDelete() {
+  NoMemTracking no_memory_tracking_in_this_scope;
+  ++do_delete_counter_;
+
+  std::pair<const void*, AllocationRecord> alloc;
+  if (!RemoveRandomAllocation(&alloc)) {
+    return false;
+  }
+
+  const void* ptr = alloc.first;
+  const AllocationRecord expected_alloc_record = alloc.second;
+
+  TRACK_MEMORY_SCOPE_DYNAMIC(unique_name_.c_str());
+  AllocationGroup* current_group = memory_tracker_->PeekAllocationGroup();
+
+  // Expect that the name of the current allocation group name is the same as
+  // what we expect.
+  if (current_group->name() != unique_name_) {
+    NoMemTracking no_memory_tracking_in_this_scope;
+    ADD_FAILURE_AT(__FILE__, __LINE__) << " " << current_group->name()
+                                       << " != " << unique_name_;
+  }
+
+  MemoryTrackerImpl::AllocationMapType* internal_alloc_map =
+      memory_tracker_->pointer_map();
+
+  AllocationRecord existing_alloc_record;
+
+  const bool found_existing_record =
+      memory_tracker_->GetMemoryTracking(ptr, &existing_alloc_record);
+
+  if (!found_existing_record) {
+    ADD_FAILURE_AT(__FILE__, __LINE__)
+        << "expected to find existing record, but did not";
+  } else if (current_group != existing_alloc_record.allocation_group) {
+    ADD_FAILURE_AT(__FILE__, __LINE__)
+        << "group allocation mismatch: " << current_group->name()
+        << " != " << existing_alloc_record.allocation_group->name() << "\n";
+  }
+  SbMemoryDeallocate(const_cast<void*>(ptr));
+  return true;
+}
+
+void AllocationStressThread::DoMalloc() {
+  ++do_malloc_counter_;
+  if (allocated_pts_.size() > 10000) {
+    return;
+  }
+
+  TRACK_MEMORY_SCOPE_DYNAMIC(unique_name_.c_str());
+  AllocationGroup* current_group = memory_tracker_->PeekAllocationGroup();
+
+  // Sanity check, make sure that the current_group name is the same as
+  // our unique name.
+  if (current_group->name() != unique_name_) {
+    NoMemTracking no_tracking_in_scope;
+    ADD_FAILURE_AT(__FILE__, __LINE__) << " " << current_group->name()
+                                       << " != " << unique_name_;
+  }
+
+  if (!memory_tracker_->IsMemoryTrackingEnabled()) {
+    NoMemTracking no_tracking_in_scope;
+    ADD_FAILURE_AT(__FILE__, __LINE__)
+        << " memory tracking state was disabled.";
+  }
+
+  const int alloc_size = std::rand() % 100 + 8;
+
+  void* memory = SbMemoryAllocate(alloc_size);
+
+  AllocationRecord record;
+  bool found = memory_tracker_->GetMemoryTracking(memory, &record);
+  if (!found) {
+    NoMemTracking no_tracking_in_scope;
+    ADD_FAILURE_AT(__FILE__, __LINE__)
+        << "Violated expectation, malloc counter: " << do_malloc_counter_;
+  }
+  AllocMap::iterator found_it = allocated_pts_.find(memory);
+
+  if (found_it != allocated_pts_.end()) {
+    NoMemTracking no_tracking_in_scope;
+    ADD_FAILURE_AT(__FILE__, __LINE__)
+        << "This pointer should not be in the map.";
+  }
+
+  NoMemTracking no_tracking_in_scope;  // DEBUG!!
+  allocated_pts_[memory] = AllocationRecord(alloc_size, current_group);
+}
+
+}  // namespace
+}  // namespace analytics
+}  // namespace nb
diff --git a/src/nb/analytics/memory_tracker_test.cc b/src/nb/analytics/memory_tracker_test.cc
new file mode 100644
index 0000000..92cb8b3
--- /dev/null
+++ b/src/nb/analytics/memory_tracker_test.cc
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "nb/memory_scope.h"
+#include "nb/scoped_ptr.h"
+#include "starboard/memory.h"
+#include "starboard/memory_reporter.h"
+#include "starboard/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace nb {
+namespace analytics {
+namespace {
+
+MemoryTracker* s_memory_tracker_ = NULL;
+
+struct NoMemTracking {
+  bool prev_val;
+  NoMemTracking() : prev_val(false) {
+    if (s_memory_tracker_) {
+      prev_val = s_memory_tracker_->IsMemoryTrackingEnabled();
+      s_memory_tracker_->SetMemoryTrackingEnabled(false);
+    }
+  }
+  ~NoMemTracking() {
+    if (s_memory_tracker_) {
+      s_memory_tracker_->SetMemoryTrackingEnabled(prev_val);
+    }
+  }
+};
+
+// EXPECT_XXX and ASSERT_XXX allocate memory, a big no-no when
+// for unit testing allocations. These overrides disable memory
+// tracking for the duration of the EXPECT and ASSERT operations.
+#define EXPECT_EQ_NO_TRACKING(A, B)                 \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    EXPECT_EQ(A, B);                                \
+  }
+
+#define EXPECT_TRUE_NO_TRACKING(A)                  \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    EXPECT_TRUE(A);                                 \
+  }
+
+#define EXPECT_FALSE_NO_TRACKING(A)                 \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    EXPECT_FALSE(A);                                \
+  }
+
+#define ASSERT_TRUE_NO_TRACKING(A)                  \
+  {                                                 \
+    NoMemTracking no_memory_tracking_in_this_scope; \
+    ASSERT_TRUE(A);                                 \
+  }
+
+///////////////////////////////////////////////////////////////////////////////
+// Framework which initializes the MemoryTracker once and installs it
+// for the first test and the removes the MemoryTracker after the
+// the last test finishes.
+class MemoryTrackerTest : public ::testing::Test {
+ public:
+  MemoryTrackerTest() {}
+
+  MemoryTracker* memory_tracker() { return s_memory_tracker_; }
+
+  bool GetAllocRecord(void* alloc_memory, AllocationRecord* output) {
+    return memory_tracker()->GetMemoryTracking(alloc_memory, output);
+  }
+
+  int64_t TotalNumberOfAllocations() {
+    return memory_tracker()->GetTotalNumberOfAllocations();
+  }
+
+  int64_t TotalAllocationBytes() {
+    return memory_tracker()->GetTotalAllocationBytes();
+  }
+
+  bool MemoryTrackerEnabled() const { return s_memory_tracker_enabled_; }
+
+ protected:
+  static void SetUpTestCase() {
+    s_memory_tracker_ = MemoryTracker::Get();
+    s_memory_tracker_enabled_ = s_memory_tracker_->InstallGlobalTrackingHooks();
+  }
+  static void TearDownTestCase() {
+    s_memory_tracker_->RemoveGlobalTrackingHooks();
+  }
+  static bool s_memory_tracker_enabled_;
+};
+bool MemoryTrackerTest::s_memory_tracker_enabled_ = false;
+
+///////////////////////////////////////////////////////////////////////////////
+class FindAllocationVisitor : public AllocationVisitor {
+ public:
+  FindAllocationVisitor() : found_(false), memory_to_find_(NULL) {}
+
+  bool found() const { return found_; }
+  void set_found(bool val) { found_ = val; }
+  void set_memory_to_find(const void* memory) {
+    memory_to_find_ = memory;
+    found_ = false;
+  }
+
+  virtual bool Visit(const void* memory, const AllocationRecord& alloc_record) {
+    if (memory_to_find_ == memory) {
+      found_ = true;
+      return false;
+    }
+    return true;
+  }
+
+ private:
+  bool found_;
+  const void* memory_to_find_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_F(MemoryTrackerTest, MacrosScopedObject) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+
+  scoped_ptr<int> alloc_a, alloc_b;
+  {
+    TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeA");
+    alloc_a.reset(new int());
+    {
+      TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeB");
+      alloc_b.reset(new int());
+    }
+  }
+
+  // Now test that the allocations now exist in the memory tracker.
+  AllocationRecord alloc_record_a, alloc_record_b;
+  // Expect that the allocations exist and that the AllocRecords are written
+  // with the allocation information.
+  EXPECT_TRUE_NO_TRACKING(
+      memory_tracker()->GetMemoryTracking(alloc_a.get(), &alloc_record_a));
+  EXPECT_TRUE_NO_TRACKING(
+      memory_tracker()->GetMemoryTracking(alloc_b.get(), &alloc_record_b));
+
+  // Sanity test that the allocations are non-null.
+
+  const AllocationGroup* group_a = alloc_record_a.allocation_group;
+  const AllocationGroup* group_b = alloc_record_b.allocation_group;
+  ASSERT_TRUE_NO_TRACKING(group_a);
+  ASSERT_TRUE_NO_TRACKING(group_b);
+
+  EXPECT_EQ_NO_TRACKING(group_a->name(),
+                        std::string("MemoryTrackerTest-ScopeA"));
+  EXPECT_EQ_NO_TRACKING(group_b->name(),
+                        std::string("MemoryTrackerTest-ScopeB"));
+
+  // When the allocation is returned to the free store then it's expected that
+  // the memory tracker will indicate that the allocation no longer exists.
+  alloc_a.reset();
+  alloc_b.reset();
+
+  EXPECT_FALSE_NO_TRACKING(
+      memory_tracker()->GetMemoryTracking(alloc_a.get(), &alloc_record_a));
+  EXPECT_FALSE_NO_TRACKING(
+      memory_tracker()->GetMemoryTracking(alloc_b.get(), &alloc_record_b));
+
+  int32_t num_allocations = -1;
+  int64_t allocation_bytes = -1;
+
+  group_a->GetAggregateStats(&num_allocations, &allocation_bytes);
+  EXPECT_EQ_NO_TRACKING(0, num_allocations);
+  EXPECT_EQ_NO_TRACKING(0, allocation_bytes);
+
+  group_b->GetAggregateStats(&num_allocations, &allocation_bytes);
+  EXPECT_EQ_NO_TRACKING(0, num_allocations);
+  EXPECT_EQ_NO_TRACKING(0, allocation_bytes);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+TEST_F(MemoryTrackerTest, Visitor) {
+  // Memory tracker is not enabled for this build.
+  if (!MemoryTrackerEnabled()) {
+    return;
+  }
+
+  FindAllocationVisitor visitor;
+
+  scoped_ptr<int> alloc_a;
+  {
+    TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeA");
+
+    alloc_a.reset(new int());
+    visitor.set_memory_to_find(alloc_a.get());
+    memory_tracker()->Accept(&visitor);
+    EXPECT_TRUE_NO_TRACKING(visitor.found());
+
+    alloc_a.reset(NULL);
+    visitor.set_found(false);
+    memory_tracker()->Accept(&visitor);
+    EXPECT_FALSE_NO_TRACKING(visitor.found());
+  }
+}
+
+}  // namespace
+}  // namespace analytics
+}  // namespace nb
diff --git a/src/nb/hash.cc b/src/nb/hash.cc
new file mode 100644
index 0000000..b04c7d5
--- /dev/null
+++ b/src/nb/hash.cc
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "nb/hash.h"
+
+#include "starboard/types.h"
+
+namespace nb {
+
+namespace {
+// Injects the super fast hash into the nb namespace.
+#include "third_party/super_fast_hash/super_fast_hash.cc"
+}  // namespace
+
+uint32_t RuntimeHash32(const char* data, int length, uint32_t prev_hash) {
+  return SuperFastHash(data, length, prev_hash);
+}
+
+}  // namespace nb
diff --git a/src/nb/hash.h b/src/nb/hash.h
new file mode 100644
index 0000000..fe48e3d
--- /dev/null
+++ b/src/nb/hash.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef NB_HASH_H_
+#define NB_HASH_H_
+
+#include "starboard/types.h"
+
+namespace nb {
+
+enum { kDefaultSeed = 0x12345789 };
+
+// RuntimeHash32 is a 32 bit hash for data. The only guarantee is that this
+// hash is persistent for the lifetime of the program. This hash function
+// is not guaranteed to be consistent across platforms. The hash value should
+// never be saved to disk.
+// It's sufficient however, to use as an in-memory hashtable.
+uint32_t RuntimeHash32(const char* data,
+                       int length,
+                       uint32_t prev_hash = kDefaultSeed);
+
+}  // namespace nb
+
+#endif  // NB_HASH_H_
diff --git a/src/nb/memory_scope.cc b/src/nb/memory_scope.cc
new file mode 100644
index 0000000..f475a16
--- /dev/null
+++ b/src/nb/memory_scope.cc
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#include "nb/memory_scope.h"
+#include "starboard/log.h"
+#include "starboard/atomic.h"
+
+NbMemoryScopeReporter* s_memory_reporter_ = NULL;
+
+bool NbSetMemoryScopeReporter(NbMemoryScopeReporter* reporter) {
+  // Flush all the pending memory writes out to main memory so that
+  // other threads see a fully constructed reporter.
+  SbAtomicMemoryBarrier();
+  s_memory_reporter_ = reporter;
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  SbLogRaw("\nMemory Scope Reporting is disabled because this build does "
+           "not support it. Try a QA, devel or debug build.\n");
+  return false;
+#else
+  return true;
+#endif
+}
+
+void NbPushMemoryScope(NbMemoryScopeInfo* memory_scope_info) {
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return;
+#else
+  if (SB_LIKELY(!s_memory_reporter_)) {
+    return;
+  }
+  s_memory_reporter_->push_memory_scope_cb(
+    s_memory_reporter_->context,
+    memory_scope_info);
+#endif
+}
+
+void NbPopMemoryScope() {
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return;
+#else
+  if (SB_LIKELY(!s_memory_reporter_)) {
+    return;
+  }
+  s_memory_reporter_->pop_memory_scope_cb(s_memory_reporter_->context);
+#endif
+}
diff --git a/src/nb/memory_scope.h b/src/nb/memory_scope.h
new file mode 100644
index 0000000..9c9446b
--- /dev/null
+++ b/src/nb/memory_scope.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#ifndef NB_MEMORY_SCOPE_H_
+#define NB_MEMORY_SCOPE_H_
+
+#include "starboard/types.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Macros to define the memory scope objects. These are objects that are used
+// to annotate sections of the code base as belonging to a particular memory
+// scope. Note that this is an annotation and does not memory allocation.
+///////////////////////////////////////////////////////////////////////////////
+
+// Macro to track the memory scope inside a function or block of code. The
+// memory scope is in effect until the end of the code block.
+// Example:
+//   void Foo() {
+//     TRACK_MEMORY_SCOPE("FooMemoryScope");
+//     // pops the memory scope at the end.
+//   }
+
+#if !defined(__cplusplus)
+// Disallow macro use for non-C++ builds.
+#define TRACK_MEMORY_SCOPE(STR) error_forbidden_in_non_c_plus_plus_code
+#define TRACK_MEMORY_SCOPE_DYNAMIC(STR) error_forbidden_in_non_c_plus_plus_code
+#elif defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+#define TRACK_MEMORY_SCOPE(STR) TRACK_MEMORY_STATIC_CACHED(STR)
+#define TRACK_MEMORY_SCOPE_DYNAMIC(STR) TRACK_MEMORY_STATIC_NOT_CACHED(STR)
+#else
+// No-op when starboard does not allow memory tracking.
+#define TRACK_MEMORY_SCOPE(STR)
+#define TRACK_MEMORY_SCOPE_DYNAMIC(STR)
+#endif
+
+// Preprocessor needs double expansion in order to __FILE__, __LINE__ and
+// __FUNCTION__ properly.
+#define TRACK_MEMORY_STATIC_CACHED(STR) \
+  TRACK_MEMORY_STATIC_CACHED_IMPL_2(STR, __FILE__, __LINE__, __FUNCTION__)
+
+#define TRACK_MEMORY_STATIC_NOT_CACHED(STR) \
+  TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(STR, __FILE__, __LINE__, __FUNCTION__)
+
+// Only enable TRACK_MEMORY_STATIC_CACHED_IMPL_2 if starboard allows memory
+// tracking.
+#define TRACK_MEMORY_STATIC_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
+  static NbMemoryScopeInfo memory_scope_handle_##LineNum =                \
+      { 0, Str, FileStr, LineNum, FuncStr, true };                        \
+  NbPushMemoryScope(&memory_scope_handle_##LineNum);                      \
+  NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
+
+#define TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
+  NbMemoryScopeInfo memory_scope_handle_##LineNum = {                         \
+      0, Str, FileStr, LineNum, FuncStr, false};                              \
+  NbPushMemoryScope(&memory_scope_handle_##LineNum);                          \
+  NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NbMemoryScopeReporter;
+struct NbMemoryScopeInfo;
+
+// Sets the memory reporter. Returns true on success, false something
+// goes wrong.
+bool NbSetMemoryScopeReporter(NbMemoryScopeReporter* reporter);
+
+// Note that we pass by pointer because the memory scope contains a
+// variable allowing the result to be cached.
+void NbPushMemoryScope(NbMemoryScopeInfo* memory_scope);
+void NbPopMemoryScope();
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementation
+///////////////////////////////////////////////////////////////////////////////
+// Interface for handling memory scopes.
+typedef void (*NbReportPushMemoryScopeCallback)(void* context,
+                                                NbMemoryScopeInfo* info);
+typedef void (*NbReportPopMemoryScopeCallback)(void* context);
+
+struct NbMemoryScopeReporter {
+  // Callback to report pushing of memory scope.
+  NbReportPushMemoryScopeCallback push_memory_scope_cb;
+
+  // Callback to report poping of the memory scope.
+  NbReportPopMemoryScopeCallback pop_memory_scope_cb;
+
+  // Optional, is passed to the callbacks as first argument.
+  void* context;
+};
+
+// This MemoryScope must remain a POD data type so that it can be statically
+// initialized.
+struct NbMemoryScopeInfo {
+  // cached_handle_ allows a cached result of the the fields represented in
+  // this struct to be generated and the handle be placed into this field.
+  // See also allows_caching_.
+  uintptr_t cached_handle_;
+
+  // Represents the name of the memory scope. I.E. "Javascript" or "Gfx".
+  const char* memory_scope_name_;
+
+  // Represents the file name that this memory scope was created at.
+  const char* file_name_;
+
+  // Represents the line number that this memory scope was created at.
+  int line_number_;
+
+  // Represents the function name that this memory scope was created at.
+  const char* function_name_;
+
+  // When true, if cached_handle_ is 0 then an object may be created that
+  // represents the fields of this object. The handle that represents this
+  // cached object is then placed in cached_hanlde_.
+  const bool allows_caching_;
+};
+
+// NbPopMemoryScopeOnScopeEnd is only allowed for C++ builds.
+#ifdef __cplusplus
+// A helper that pops the memory scope at the end of the current code block.
+struct NbPopMemoryScopeOnScopeEnd {
+  ~NbPopMemoryScopeOnScopeEnd() { NbPopMemoryScope(); }
+};
+#endif
+
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // NB_MEMORY_SCOPE_H_
diff --git a/src/nb/memory_scope_test.cc b/src/nb/memory_scope_test.cc
new file mode 100644
index 0000000..e734c2f
--- /dev/null
+++ b/src/nb/memory_scope_test.cc
@@ -0,0 +1,257 @@
+/*

+ * 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.

+ */

+

+#include "nb/memory_scope.h"

+#include "starboard/mutex.h"

+#include "nb/thread_local_object.h"

+

+#include "testing/gtest/include/gtest/gtest.h"

+

+namespace nb {

+namespace {

+

+bool StarboardAllowsMemoryTracking() {

+#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING)

+  return true;

+#else

+  return false;

+#endif

+}

+

+// This is a memory scope reporter that is compatible

+// with the MemoryScopeReporter.

+class TestMemoryScopeReporter {

+ public:

+  typedef std::vector<NbMemoryScopeInfo*> MemoryScopeVector;

+

+  TestMemoryScopeReporter() {

+    memory_scope_reporter_ = CreateMemoryScopeReporter();

+  }

+

+  NbMemoryScopeReporter* memory_scope_reporter() {

+    return &memory_scope_reporter_;

+  }

+

+  MemoryScopeVector* stack_thread_local() { return stack_tlo_.GetOrCreate(); }

+

+  void OnPushMemoryScope(NbMemoryScopeInfo* memory_scope) {

+    stack_thread_local()->push_back(memory_scope);

+  }

+

+  void OnPopMemoryScope() {

+    MemoryScopeVector* stack = stack_thread_local();

+    if (!stack->empty()) {

+      stack->pop_back();

+    } else {

+      ADD_FAILURE_AT(__FILE__, __LINE__)

+          << " stack was empty and could not be popped.";

+    }

+  }

+

+ private:

+  static void OnPushMemoryScopeCallback(void* context,

+                                        NbMemoryScopeInfo* info) {

+    TestMemoryScopeReporter* t = static_cast<TestMemoryScopeReporter*>(context);

+    t->OnPushMemoryScope(info);

+  }

+

+  static void OnPopMemoryScopeCallback(void* context) {

+    TestMemoryScopeReporter* t = static_cast<TestMemoryScopeReporter*>(context);

+    t->OnPopMemoryScope();

+  }

+

+  NbMemoryScopeReporter CreateMemoryScopeReporter() {

+    NbMemoryScopeReporter reporter = {OnPushMemoryScopeCallback,

+                                      OnPopMemoryScopeCallback, this};

+    return reporter;

+  }

+

+  NbMemoryScopeReporter memory_scope_reporter_;

+  ThreadLocalObject<MemoryScopeVector> stack_tlo_;

+};

+

+// A test framework for testing the Pushing & popping memory scopes.

+// The key feature here is that reporter is setup on the first test

+// instance and torn down after the last test has run.

+class MemoryScopeReportingTest : public ::testing::Test {

+ public:

+  TestMemoryScopeReporter* test_memory_reporter() { return s_reporter_; }

+

+  bool reporting_enabled() const { return s_reporter_enabled_; }

+

+ protected:

+  static void SetUpTestCase() {

+    if (!s_reporter_) {

+      s_reporter_ = new TestMemoryScopeReporter;

+    }

+    s_reporter_enabled_ =

+        NbSetMemoryScopeReporter(s_reporter_->memory_scope_reporter());

+

+    EXPECT_EQ(StarboardAllowsMemoryTracking(), s_reporter_enabled_)

+        << "Expected the memory scope reporter to be enabled whenever "

+           "starboard memory tracking is allowed.";

+  }

+

+  static void TearDownTestCase() {

+    // The reporter itself is not deleted because other threads could

+    // be traversing through it's data structures. It's better just to leave

+    // the object alive for the purposes of this unit test and set the pointer

+    // to NULL.

+    // This is done in order to make the MemoryScopeReport object lock free.

+    // This increases performance and reduces complexity of design.

+    NbSetMemoryScopeReporter(NULL);

+  }

+

+  // Per test setup.

+  virtual void SetUp() {

+    test_memory_reporter()->stack_thread_local()->clear();

+  }

+

+  static TestMemoryScopeReporter* s_reporter_;

+  static bool s_reporter_enabled_;

+};

+TestMemoryScopeReporter* MemoryScopeReportingTest::s_reporter_ = NULL;

+bool MemoryScopeReportingTest::s_reporter_enabled_;

+

+///////////////////////////////////////////////////////////////////////////////

+// TESTS.

+// There are two sets of tests: POSITIVE and NEGATIVE.

+//  The positive tests are active when STARBOARD_ALLOWS_MEMORY_TRACKING is

+//  defined and test that memory tracking is enabled.

+//  NEGATIVE tests ensure that tracking is disabled when

+//  STARBOARD_ALLOWS_MEMORY_TRACKING is not defined.

+// When adding new tests:

+//  POSITIVE tests are named normally.

+//  NEGATIVE tests are named with "No" prefixed to the beginning.

+//  Example:

+//   TEST_F(MemoryScopeReportingTest, PushPop) <--- POSITIVE test.

+//   TEST_F(MemoryScopeReportingTest, NoPushPop) <- NEGATIVE test.

+//  All positive & negative tests are grouped together.

+///////////////////////////////////////////////////////////////////////////////

+

+#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING)

+// These are POSITIVE tests, which test the expectation that when the define

+// STARBOARD_ALLOWS_MEMORY_TRACKING is active that the memory scope reporter

+// will receive memory scope notifications.

+

+// Tests the assumption that the SbMemoryAllocate and SbMemoryDeallocate

+// will report memory allocations.

+TEST_F(MemoryScopeReportingTest, PushPop) {

+  ASSERT_TRUE(reporting_enabled());

+  const int line_number = __LINE__;

+  const char* file_name = __FILE__;

+  const char* function_name = __FUNCTION__;

+  NbMemoryScopeInfo info = {0,              // Cached value (null).

+                            "Javascript",   // Name of the memory scope.

+                            file_name,      // Filename that invoked this.

+                            line_number,    // Line number.

+                            function_name,  // Function name.

+                            true};          // true allows caching.

+

+  NbPushMemoryScope(&info);

+

+  ASSERT_FALSE(test_memory_reporter()->stack_thread_local()->empty());

+  NbMemoryScopeInfo* info_ptr =

+      test_memory_reporter()->stack_thread_local()->front();

+

+  EXPECT_EQ(&info, info_ptr);

+  EXPECT_STREQ(info.file_name_, file_name);

+  EXPECT_STREQ(info.function_name_, function_name);

+  EXPECT_EQ(info.line_number_, line_number);

+

+  NbPopMemoryScope();

+  EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty());

+}

+

+// Tests the expectation that the memory reporting macros will

+// push/pop memory regions and will also correctly bind to the

+// file, linenumber and also the function name.

+TEST_F(MemoryScopeReportingTest, Macros) {

+  ASSERT_TRUE(reporting_enabled());

+  // There should be no leftover stack objects.

+  EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty());

+  {

+    const int line_before = __LINE__;

+    TRACK_MEMORY_SCOPE("TestMemoryScope");

+    const int predicted_line = line_before + 1;

+

+    NbMemoryScopeInfo* info_ptr =

+        test_memory_reporter()->stack_thread_local()->front();

+

+    // TRACK_MEMORY_SCOPE is defined to allow caching.

+    EXPECT_EQ(true, info_ptr->allows_caching_);

+

+    // The cached_handle_ is not mutated by TestMemoryScopeReporter so

+    // therefore it should be the default value of 0.

+    EXPECT_EQ(0, info_ptr->cached_handle_);

+

+    EXPECT_STREQ("TestMemoryScope", info_ptr->memory_scope_name_);

+    EXPECT_STREQ(__FILE__, info_ptr->file_name_);

+    EXPECT_EQ(predicted_line, info_ptr->line_number_);

+    EXPECT_STREQ(__FUNCTION__, info_ptr->function_name_);

+  }

+  // Expect that the stack object is now empty again.

+  EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty());

+}

+

+#else  // !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)

+// These are NEGATIVE tests, which test the expectation that when the

+// STARBOARD_ALLOWS_MEMORY_TRACKING is undefined that the memory scope reprter

+// does not receive memory scope notifications.

+

+// Tests the expectation that push pop does not send notifications to the

+// reporter when disabled.

+TEST_F(MemoryScopeReportingTest, NoPushPop) {

+  ASSERT_FALSE(reporting_enabled());

+  const int line_number = __LINE__;

+  const char* file_name = __FILE__;

+  const char* function_name = __FUNCTION__;

+  NbMemoryScopeInfo info = {0,              // Cached value (null).

+                            "Javascript",   // Name of the memory scope.

+                            file_name,      // Filename that invoked this.

+                            line_number,    // Line number.

+                            function_name,  // Function name.

+                            true};          // true allows caching.

+

+  NbPushMemoryScope(&info);

+

+  ASSERT_FALSE(test_memory_reporter()->stack_thread_local()->empty());

+  NbMemoryScopeInfo* info_ptr =

+      test_memory_reporter()->stack_thread_local()->front();

+

+  EXPECT_EQ(&info, info_ptr);

+  EXPECT_STREQ(info.file_name_, file_name);

+  EXPECT_STREQ(info.function_name_, function_name);

+  EXPECT_EQ(info.line_number_, line_number);

+

+  NbPopMemoryScope();

+  EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty());

+}

+

+// Tests the expectation that the memory reporting macros are disabled when

+// memory tracking is not allowed.

+TEST_F(MemoryScopeReportingTest, NoMacros) {

+  ASSERT_FALSE(reporting_enabled());

+  // Test that the macros do nothing when memory reporting has been

+  // disabled.

+  TRACK_MEMORY_SCOPE("InternalMemoryRegion");

+  ASSERT_TRUE(test_memory_reporter()->stack_thread_local()->empty())

+      << "Memory reporting received notifications when it should be disabled.";

+}

+#endif

+

+}  // namespace.

+}  // namespace nb.

diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index f87d42a..855c800 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -24,11 +24,21 @@
         'allocator.h',
         'allocator_decorator.cc',
         'allocator_decorator.h',
+        'analytics/memory_tracker.cc',
+        'analytics/memory_tracker.h',
+        'analytics/memory_tracker_impl.cc',
+        'analytics/memory_tracker_impl.h',
+        'analytics/memory_tracker_helpers.cc',
+        'analytics/memory_tracker_helpers.h',
         'atomic.h',
         'fixed_no_free_allocator.cc',
         'fixed_no_free_allocator.h',
+        'hash.cc',
+        'hash.h',
         'memory_pool.cc',
         'memory_pool.h',
+        'memory_scope.cc',
+        'memory_scope.h',
         'move.h',
         'pointer_arithmetic.h',
         'rect.h',
@@ -62,8 +72,12 @@
       'target_name': 'nb_test',
       'type': '<(gtest_target_type)',
       'sources': [
+        'analytics/memory_tracker_helpers_test.cc',
+        'analytics/memory_tracker_impl_test.cc',
+        'analytics/memory_tracker_test.cc',
         'atomic_test.cc',
         'fixed_no_free_allocator_test.cc',
+        'memory_scope_test.cc',
         'reuse_allocator_test.cc',
         'run_all_unittests.cc',
         'test_thread.h',
diff --git a/src/net/base/file_stream_unittest.cc b/src/net/base/file_stream_unittest.cc
index 04b613d..2cdb252 100644
--- a/src/net/base/file_stream_unittest.cc
+++ b/src/net/base/file_stream_unittest.cc
@@ -437,6 +437,8 @@
     drainable->DidConsume(rv);
     total_bytes_written += rv;
   }
+  rv = stream.FlushSync();
+  EXPECT_EQ(OK, rv);
   ok = file_util::GetFileSize(temp_file_path(), &file_size);
   EXPECT_TRUE(ok);
   EXPECT_EQ(file_size, total_bytes_written);
@@ -533,6 +535,8 @@
     drainable->DidConsume(rv);
     total_bytes_written += rv;
   }
+  rv = stream.FlushSync();
+  EXPECT_EQ(OK, rv);
   ok = file_util::GetFileSize(temp_file_path(), &file_size);
   EXPECT_TRUE(ok);
   EXPECT_EQ(file_size, kTestDataSize * 2);
@@ -881,6 +885,8 @@
   EXPECT_LT(0, rv);
   EXPECT_EQ(kTestDataSize, total_bytes_written);
 
+  rv = stream->FlushSync();
+  EXPECT_EQ(OK, rv);
   stream.reset();
 
   ok = file_util::GetFileSize(temp_file_path(), &file_size);
diff --git a/src/net/quic/crypto/crypto_framer_test.cc b/src/net/quic/crypto/crypto_framer_test.cc
index eed175f..b959541 100644
--- a/src/net/quic/crypto/crypto_framer_test.cc
+++ b/src/net/quic/crypto/crypto_framer_test.cc
@@ -10,6 +10,7 @@
 #include "net/quic/crypto/crypto_framer.h"
 #include "net/quic/crypto/crypto_protocol.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/disabled_if_big_endian.h"
 
 using base::StringPiece;
 using std::map;
@@ -62,7 +63,7 @@
 
 }  // namespace test
 
-TEST(CryptoFramerTest, ConstructHandshakeMessage) {
+TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructHandshakeMessage)) {
   CryptoHandshakeMessage message;
   message.tag = 0xFFAA7733;
   message.tag_value_map[0x12345678] = "abcdef";
@@ -107,7 +108,8 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) {
+TEST(CryptoFramerTest,
+     DISABLED_IF_BIG_ENDIAN(ConstructHandshakeMessageWithTwoKeys)) {
   CryptoHandshakeMessage message;
   message.tag = 0xFFAA7733;
   message.tag_value_map[0x12345678] = "abcdef";
@@ -166,7 +168,7 @@
   EXPECT_TRUE(data.get() == NULL);
 }
 
-TEST(CryptoFramerTest, ProcessInput) {
+TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInput)) {
   test::TestCryptoVisitor visitor;
   CryptoFramer framer;
   framer.set_visitor(&visitor);
@@ -201,7 +203,7 @@
   EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]);
 }
 
-TEST(CryptoFramerTest, ProcessInputIncrementally) {
+TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInputIncrementally)) {
   test::TestCryptoVisitor visitor;
   CryptoFramer framer;
   framer.set_visitor(&visitor);
@@ -237,7 +239,7 @@
   EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]);
 }
 
-TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) {
+TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInputTagsOutOfOrder)) {
   test::TestCryptoVisitor visitor;
   CryptoFramer framer;
   framer.set_visitor(&visitor);
@@ -275,7 +277,7 @@
   EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error());
 }
 
-TEST(CryptoFramerTest, ProcessInputInvalidLength) {
+TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInputInvalidLength)) {
   test::TestCryptoVisitor visitor;
   CryptoFramer framer;
   framer.set_visitor(&visitor);
diff --git a/src/net/quic/crypto/null_decrypter_test.cc b/src/net/quic/crypto/null_decrypter_test.cc
index ad73cea..f76de31 100644
--- a/src/net/quic/crypto/null_decrypter_test.cc
+++ b/src/net/quic/crypto/null_decrypter_test.cc
@@ -4,13 +4,14 @@
 
 #include "net/quic/crypto/null_decrypter.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/disabled_if_big_endian.h"
 
 using base::StringPiece;
 
 namespace net {
 namespace test {
 
-TEST(NullDecrypterTest, Decrypt) {
+TEST(NullDecrypterTest, DISABLED_IF_BIG_ENDIAN(Decrypt)) {
   unsigned char expected[] = {
     // fnv hash
     0x47, 0x11, 0xea, 0x5f,
diff --git a/src/net/quic/crypto/null_encrypter_test.cc b/src/net/quic/crypto/null_encrypter_test.cc
index 9f2cec2..9f9f2c4 100644
--- a/src/net/quic/crypto/null_encrypter_test.cc
+++ b/src/net/quic/crypto/null_encrypter_test.cc
@@ -4,13 +4,14 @@
 
 #include "net/quic/crypto/null_encrypter.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/disabled_if_big_endian.h"
 
 using base::StringPiece;
 
 namespace net {
 namespace test {
 
-TEST(NullEncrypterTest, Encrypt) {
+TEST(NullEncrypterTest, DISABLED_IF_BIG_ENDIAN(Encrypt)) {
   unsigned char expected[] = {
     // fnv hash
     0x47, 0x11, 0xea, 0x5f,
diff --git a/src/net/quic/quic_connection_test.cc b/src/net/quic/quic_connection_test.cc
index 6e14df7..97c9399 100644
--- a/src/net/quic/quic_connection_test.cc
+++ b/src/net/quic/quic_connection_test.cc
@@ -13,6 +13,7 @@
 #include "net/quic/test_tools/mock_clock.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/quic/quic_utils.h"
+#include "net/test/disabled_if_big_endian.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -794,7 +795,8 @@
   EXPECT_TRUE(last_feedback() == NULL);
 }
 
-TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
+TEST_F(QuicConnectionTest,
+       DISABLED_IF_BIG_ENDIAN(WithQuicCongestionFeedbackFrame)) {
   QuicCongestionFeedbackFrame info;
   info.type = kFixRate;
   info.fix_rate.bitrate_in_bytes_per_second = 123;
diff --git a/src/net/quic/quic_framer_test.cc b/src/net/quic/quic_framer_test.cc
index 25c3f39..9ef87dc 100644
--- a/src/net/quic/quic_framer_test.cc
+++ b/src/net/quic/quic_framer_test.cc
@@ -16,6 +16,7 @@
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/disabled_if_big_endian.h"
 
 using base::hash_set;
 using base::StringPiece;
@@ -249,7 +250,7 @@
   EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
 }
 
-TEST_F(QuicFramerTest, LargePacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(LargePacket)) {
   unsigned char packet[kMaxPacketSize + 1] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -271,13 +272,14 @@
   EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, encrypted));
 
   ASSERT_TRUE(visitor_.header_.get());
+
   // Make sure we've parsed the packet header, so we can send an error.
   EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), visitor_.header_->guid);
   // Make sure the correct error is propogated.
   EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error());
 }
 
-TEST_F(QuicFramerTest, PacketHeader) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(PacketHeader)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -318,7 +320,7 @@
   }
 }
 
-TEST_F(QuicFramerTest, StreamFrame) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(StreamFrame)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -435,7 +437,7 @@
   EXPECT_EQ(0u, visitor_.ack_frames_.size());
 }
 
-TEST_F(QuicFramerTest, RevivedStreamFrame) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(RevivedStreamFrame)) {
   unsigned char payload[] = {
     // frame count
     0x01,
@@ -486,7 +488,7 @@
   EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data);
 }
 
-TEST_F(QuicFramerTest, StreamFrameInFecGroup) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(StreamFrameInFecGroup)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -539,7 +541,7 @@
   EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data);
 }
 
-TEST_F(QuicFramerTest, AckFrame) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(AckFrame)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -606,7 +608,7 @@
   }
 }
 
-TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(CongestionFeedbackFrameTCP)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -666,7 +668,8 @@
   }
 }
 
-TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) {
+TEST_F(QuicFramerTest,
+       DISABLED_IF_BIG_ENDIAN(CongestionFeedbackFrameInterArrival)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -775,7 +778,7 @@
   }
 }
 
-TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(CongestionFeedbackFrameFixRate)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -858,7 +861,7 @@
   EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error());
 }
 
-TEST_F(QuicFramerTest, RstStreamFrame) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(RstStreamFrame)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -923,7 +926,7 @@
   }
 }
 
-TEST_F(QuicFramerTest, ConnectionCloseFrame) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConnectionCloseFrame)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -1008,7 +1011,7 @@
   }
 }
 
-TEST_F(QuicFramerTest, FecPacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(FecPacket)) {
   unsigned char packet[] = {
     // guid
     0x10, 0x32, 0x54, 0x76,
@@ -1047,7 +1050,7 @@
   EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy);
 }
 
-TEST_F(QuicFramerTest, ConstructStreamFramePacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructStreamFramePacket)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1102,7 +1105,7 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_F(QuicFramerTest, ConstructAckFramePacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructAckFramePacket)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1154,7 +1157,8 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) {
+TEST_F(QuicFramerTest,
+       DISABLED_IF_BIG_ENDIAN(ConstructCongestionFeedbackFramePacketTCP)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1201,7 +1205,9 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) {
+TEST_F(QuicFramerTest,
+       DISABLED_IF_BIG_ENDIAN(
+           ConstructCongestionFeedbackFramePacketInterArrival)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1274,7 +1280,8 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) {
+TEST_F(QuicFramerTest,
+       DISABLED_IF_BIG_ENDIAN(ConstructCongestionFeedbackFramePacketFixRate)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1337,7 +1344,7 @@
   ASSERT_TRUE(data == NULL);
 }
 
-TEST_F(QuicFramerTest, ConstructRstFramePacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructRstFramePacket)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1393,7 +1400,7 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_F(QuicFramerTest, ConstructCloseFramePacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructCloseFramePacket)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
@@ -1460,7 +1467,7 @@
                                       AsChars(packet), arraysize(packet));
 }
 
-TEST_F(QuicFramerTest, ConstructFecPacket) {
+TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructFecPacket)) {
   QuicPacketHeader header;
   header.guid = GG_UINT64_C(0xFEDCBA9876543210);
   header.packet_sequence_number = (GG_UINT64_C(0x123456789ABC));
diff --git a/src/net/test/disabled_if_big_endian.h b/src/net/test/disabled_if_big_endian.h
new file mode 100644
index 0000000..dfc0eed
--- /dev/null
+++ b/src/net/test/disabled_if_big_endian.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NET_TEST_DISABLED_IF_BIG_ENDIAN_H_
+#define NET_TEST_DISABLED_IF_BIG_ENDIAN_H_
+
+#define DISABLED_IF_BIG_ENDIAN(Test) Test
+
+#if defined(OS_STARBOARD)
+#include "starboard/configuration.h"
+#if SB_IS(BIG_ENDIAN)
+#undef DISABLED_IF_BIG_ENDIAN
+#define DISABLED_IF_BIG_ENDIAN(Test) DISABLED_##Test
+#endif  // SB_IS(BIG_ENDIAN)
+#endif  // defined(OS_STARBOARD)
+
+#endif  // NET_TEST_DISABLED_IF_BIG_ENDIAN_H_
diff --git a/src/net/url_request/url_fetcher_core.cc b/src/net/url_request/url_fetcher_core.cc
index 2097903..ea36c74 100644
--- a/src/net/url_request/url_fetcher_core.cc
+++ b/src/net/url_request/url_fetcher_core.cc
@@ -5,6 +5,7 @@
 #include "net/url_request/url_fetcher_core.h"
 
 #include "base/bind.h"
+#include "base/debug/trace_event.h"
 #include "base/file_util_proxy.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
@@ -611,6 +612,10 @@
     if (!request_->status().is_success() || bytes_read <= 0)
       break;
 
+    if (current_response_bytes_ == 0) {
+      TRACE_EVENT_ASYNC_STEP0("net::url_request", "URLFetcher", this,
+                              "Fetch Content");
+    }
     current_response_bytes_ += bytes_read;
     InformDelegateDownloadDataIfNecessary(bytes_read);
 
@@ -710,6 +715,8 @@
 
 void URLFetcherCore::StartURLRequest() {
   DCHECK(network_task_runner_->BelongsToCurrentThread());
+  TRACE_EVENT_ASYNC_STEP0("net::url_request", "URLFetcher", this,
+                          "Waiting For Data");
 
   if (was_cancelled_) {
     // Since StartURLRequest() is posted as a *delayed* task, it may
@@ -801,6 +808,8 @@
 }
 
 void URLFetcherCore::StartURLRequestWhenAppropriate() {
+  TRACE_EVENT_ASYNC_BEGIN1("net::url_request", "URLFetcher", this, "url",
+                           original_url_.path());
   DCHECK(network_task_runner_->BelongsToCurrentThread());
 
   if (was_cancelled_)
@@ -837,6 +846,7 @@
   if (request_.get()) {
     request_->Cancel();
     ReleaseRequest();
+    TRACE_EVENT_ASYNC_END0("net::url_request", "URLFetcher", this);
   }
   // Release the reference to the request context. There could be multiple
   // references to URLFetcher::Core at this point so it may take a while to
@@ -852,6 +862,8 @@
 
 void URLFetcherCore::OnCompletedURLRequest(
     base::TimeDelta backoff_delay) {
+  TRACE_EVENT_ASYNC_END1("net::url_request", "URLFetcher", this, "url",
+                         original_url_.path());
   DCHECK(delegate_task_runner_->BelongsToCurrentThread());
 
   // Save the status and backoff_delay so that delegates can read it.
diff --git a/src/starboard/README.md b/src/starboard/README.md
index 3d35ee3..5e81034 100644
--- a/src/starboard/README.md
+++ b/src/starboard/README.md
@@ -160,7 +160,7 @@
 
 In order to use a new platform configuration in a build, you need to ensure that
 you have a `gyp_configuration.py`, `gyp_configuration.gypi`, and
-`starboard_platform.gypi` in their own directory for each binary variant, plus
+`starboard_platform.gyp` in their own directory for each binary variant, plus
 the header files `configuration_public.h`, `atomic_public.h`, and
 `thread_types_public.h`. `gyp_cobalt` will scan your directories for these
 files, and then calculate a port name based on the directories between
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index e47391f..243c4ea 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Module Overview: Starboard Atomic API
+//
 // Defines a set of atomic integer operations that can be used as lightweight
-// synchronization or as building blocks for heavier synchronization
-// primitives. Their use is very subtle and requires detailed understanding of
-// the behavior of supported architectures, so their direct use is not
-// recommended except when rigorously deemed absolutely necessary for
-// performance reasons.
+// synchronization or as building blocks for heavier synchronization primitives.
+// Their use is very subtle and requires detailed understanding of the behavior
+// of supported architectures, so their direct use is not recommended except
+// when rigorously deemed absolutely necessary for performance reasons.
 
 #ifndef STARBOARD_ATOMIC_H_
 #define STARBOARD_ATOMIC_H_
diff --git a/src/starboard/blitter.h b/src/starboard/blitter.h
index d183475..e807f63 100644
--- a/src/starboard/blitter.h
+++ b/src/starboard/blitter.h
@@ -12,14 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Starboard Blitter API.  The Blitter API provides support for issuing simple
-// blit-style draw commands to either an offscreen surface or to a Starboard
-// SbWindow object.  This API is designed to allow implementations make use
-// of GPU hardware acceleration, if it is available.  Draw commands exist for
-// solid-color rectangles and rasterization/blitting of rectangular images onto
+// Module Overview: Starboard Blitter API
+//
+// The Blitter API provides support for issuing simple blit-style draw
+// commands to either an offscreen surface or to a Starboard SbWindow object.
+// Blitter is jargon that means "BLock Transfer," which might be abbreviated
+// as BLT and, hence, the word "blit."
+//
+// This API is designed to allow implementations make use of GPU hardware
+// acceleration, if it is available.  Draw commands exist for solid-color
+// rectangles and rasterization/blitting of rectangular images onto
 // rectangular target patches.
-
-// Threading Concerns:
+//
+// #### Threading Concerns
+//
 // Note that in general the Blitter API is not thread safe, except for all
 // SbBlitterDevice-related functions.  All functions that are not required to
 // internally ensure any thread safety guarantees are prefaced with a comment
@@ -97,12 +103,16 @@
 #define kSbBlitterInvalidSurface ((SbBlitterSurface)NULL)
 
 // SbBlitterContext objects represent a stateful communications channel with
-// a device.  All state changes and draw calls will be made through a specific
-// SbBlitterContext object.  Every draw call made on a SbBlitterContext will
-// be submitted to the device with the SbBlitterContext's current state applied
-// to it.  Draw calls may be submitted to the device as they are made on the
-// SbBlitterContext, however they are not guaranteed to be submitted until
-// the SbBlitterContext object is flushed.
+// a device.  All state changes and draw calls are made through a specific
+// SbBlitterContext object.  Every draw call made on a SbBlitterContext is
+// submitted to the device with the SbBlitterContext's current state applied
+// to it.
+//
+// Draw calls may be submitted to the device as they are made on the
+// SbBlitterContext. However, they are not guaranteed to be submitted until
+// the SbBlitterContext object is flushed. That is, until you call
+// SbBlitterFlushContext, you are not guaranteed that any API calls you have
+// made have been received or acted on by the graphics device.
 typedef struct SbBlitterContextPrivate SbBlitterContextPrivate;
 typedef SbBlitterContextPrivate* SbBlitterContext;
 #define kSbBlitterInvalidContext ((SbBlitterContext)NULL)
@@ -283,66 +293,84 @@
   return context != kSbBlitterInvalidContext;
 }
 
-// Creates and returns a SbBlitterDevice object based on the Blitter API
-// implementation's decision of which device should be default.  The
-// SbBlitterDevice object represents a connection to a device (like a GPU).  On
-// many platforms there is always one single obvious choice for a device to use,
-// and that is the one that this function will return.  For example, if this is
-// called on a platform that has a single GPU, a device representing that GPU
-// should be returned by this call.  On a platform that has no GPU, a device
-// representing a software CPU implementation may be returned.  Only one
-// default device can exist within a process at a time.
-// This function is thread safe.
+// Creates and returns an SbBlitterDevice based on the Blitter API
+// implementation's decision of which device should be the default. The
+// returned SbBlitterDevice represents a connection to a device (like a GPU).
+//
+// On many platforms there is always one single obvious choice for a device
+// to use, and that is the one that this function will return. For example,
+// if this is called on a platform that has a single GPU, this call should
+// return an object that represents that GPU. On a platform that has no GPU,
+// an object representing a software CPU implementation may be returned.
+//
+// Only one default device can exist within a process at a time.
+// This function is thread-safe.
 // Returns kSbBlitterInvalidDevice on failure.
 SB_EXPORT SbBlitterDevice SbBlitterCreateDefaultDevice();
 
 // Destroys |device|, cleaning up all resources associated with it.
-// Returns whether the destruction succeeded.
-// This function is thread safe, though of course it should not be called if
-// |device| is still being accessed elsewhere.
+// This function is thread-safe, but it should not be called if |device| is
+// still being accessed elsewhere.
+//
+// The return value indicates whether the destruction succeeded.
+//
+// |device|: The SbBlitterDevice object to be destroyed.
 SB_EXPORT bool SbBlitterDestroyDevice(SbBlitterDevice device);
 
-// Creates and returns a SbBlitterSwapChain object that can be used to send
-// graphics to the display.  By calling this function, |device| will be linked
-// to |window|'s output and drawing to the returned swap chain will result in
-// |device| being used to render to |window|.  kSbBlitterInvalidSwapChain is
-// returned on failure.
+// Creates and returns an SbBlitterSwapChain that can then be used to send
+// graphics to the display. This function links |device| to |window|'s output,
+// and drawing to the returned swap chain will result in |device| being used
+// to render to |window|.
+//
 // This function must be called from the thread that called SbWindowCreate()
 // to create |window|.
+//
+// Returns kSbBlitterInvalidSwapChain on failure.
 SB_EXPORT SbBlitterSwapChain
 SbBlitterCreateSwapChainFromWindow(SbBlitterDevice device, SbWindow window);
 
 // Destroys |swap_chain|, cleaning up all resources associated with it.
-// This function must be called on the same thread that called
-// SbBlitterCreateSwapChainFromWindow().
-// This function is not thread safe.
-// Returns the destruction succeeded.
+// This function is not thread-safe and must be called on the same thread
+// that called SbBlitterCreateSwapChainFromWindow().
+//
+// The return value indicates whether the destruction succeeded.
+//
+// |swap_chain|: The SbBlitterSwapChain to be destroyed.
 SB_EXPORT bool SbBlitterDestroySwapChain(SbBlitterSwapChain swap_chain);
 
 // Returns the |SbBlitterRenderTarget| object that is owned by |swap_chain|.
 // The returned object can be used to provide a target to blitter draw calls
-// wishing to draw directly to the display buffer.
-// This function is not thread safe.
-// kSbBlitterInvalidRenderTarget is returned on failure.
+// that draw directly to the display buffer. This function is not thread-safe.
+//
+// Returns kSbBlitterInvalidRenderTarget on failure.
+//
+// |swap_chain|: The SbBlitterSwapChain for which the target object is being
+// retrieved.
 SB_EXPORT SbBlitterRenderTarget
 SbBlitterGetRenderTargetFromSwapChain(SbBlitterSwapChain swap_chain);
 
-// Returns whether |device| supports calls to SbBlitterCreatePixelData()
-// with |pixel_format|.
-// This function is thread safe.
+// Indicates whether |device| supports calls to SbBlitterCreatePixelData
+// with the specified |pixel_format|. This function is thread-safe.
+//
+// |device|: The device for which compatibility is being checked.
+// |pixel_format|: The SbBlitterPixelDataFormat for which compatibility is
+// being checked.
 SB_EXPORT bool SbBlitterIsPixelFormatSupportedByPixelData(
     SbBlitterDevice device,
     SbBlitterPixelDataFormat pixel_format);
 
-// Allocates a SbBlitterPixelData object through |device| with |width|, |height|
-// and |pixel_format|.  |pixel_format| must be supported by |device| (see
-// SbBlitterIsPixelFormatSupportedByPixelData()).  Calling this function will
-// result in the allocation of CPU-accessible (though perhaps
-// blitter-device-resident) memory to store pixel data of the requested
-// size/format. A SbBlitterPixelData object should either eventually be passed
-// into a call to SbBlitterCreateSurfaceFromPixelData(), or passed into a call
-// to SbBlitterDestroyPixelData().
-// This function is thread safe.
+// Allocates an SbBlitterPixelData object through |device| with |width|,
+// |height| and |pixel_format|. |pixel_format| must be supported by |device|
+// (see SbBlitterIsPixelFormatSupportedByPixelData()). This function is
+// thread-safe.
+//
+// Calling this function results in the allocation of CPU-accessible
+// (though perhaps blitter-device-resident) memory to store pixel data
+// of the requested size/format. An SbBlitterPixelData object should
+// eventually be passed either into a call to
+// SbBlitterCreateSurfaceFromPixelData() or into a call to
+// SbBlitterDestroyPixelData().
+//
 // Returns kSbBlitterInvalidPixelData upon failure.
 SB_EXPORT SbBlitterPixelData
 SbBlitterCreatePixelData(SbBlitterDevice device,
@@ -350,57 +378,74 @@
                          int height,
                          SbBlitterPixelDataFormat pixel_format);
 
-// Destroys the |pixel_data| object.  Note that
-// if SbBlitterCreateSurfaceFromPixelData() has been called on |pixel_data|
-// before, this function does not need to be and should not be called.
-// This function is thread safe.
-// Returns whether the destruction succeeded.
+// Destroys |pixel_data|. Note that this function does not need to be called
+// and should not be called if SbBlitterCreateSurfaceFromPixelData() has been
+// called on |pixel_data| before. This function is thread-safe.
+//
+// The return value indicates whether the destruction succeeded.
+//
+// |pixel_data|: The object to be destroyed.
 SB_EXPORT bool SbBlitterDestroyPixelData(SbBlitterPixelData pixel_data);
 
-// Getter method to return the pitch (in bytes) for |pixel_data|.  This
-// indicates the number of bytes per row of pixel data in the image.  -1 is
-// returned in case of an error.
-// This function is not thread safe.
+// Retrieves the pitch (in bytes) for |pixel_data|. This indicates the number of
+// bytes per row of pixel data in the image. This function is not thread-safe.
+//
+// Returns |-1| in the event of an error.
+//
+// |pixel_data|: The object for which you are retrieving the pitch.
 SB_EXPORT int SbBlitterGetPixelDataPitchInBytes(SbBlitterPixelData pixel_data);
 
-// Getter method to return a CPU-accessible pointer to the pixel data
-// represented by |pixel_data|.  This pixel data can be modified by the CPU
-// in order to initialize it on the CPU before calling
-// SbBlitterCreateSurfaceFromPixelData().  Note that the pointe returned here
-// is valid as long as |pixel_data| is valid, i.e. until either
+// Retrieves a CPU-accessible pointer to the pixel data represented by
+// |pixel_data|. This pixel data can be modified by the CPU to initialize it
+// on the CPU before calling SbBlitterCreateSurfaceFromPixelData().
+//
+// Note that the pointer returned here is valid as long as |pixel_data| is
+// valid, which means it is valid until either
 // SbBlitterCreateSurfaceFromPixelData() or SbBlitterDestroyPixelData() is
 // called.
-// This function is not thread safe.
-// If there is an error, NULL is returned.
+//
+// This function is not thread-safe.
+//
+// Returns |NULL| in the event of an error.
 SB_EXPORT void* SbBlitterGetPixelDataPointer(SbBlitterPixelData pixel_data);
 
-// Creates a SbBlitterSurface object on |device| (which must match the device
-// used to create the input SbBlitterPixelData object, |pixel_format|).
-// This function will destroy the input |pixel_data| object and so
-// |pixel_data| should not be accessed again after this function is called.
-// The returned surface cannot be used as a render target (e.g. calling
+// Creates an SbBlitterSurface object on |device|. Note that |device| must
+// match the device that was used to create the SbBlitterPixelData object
+// provided via the |pixel_data| parameter.
+//
+// This function also destroys the input |pixel_data| object. As a result,
+// |pixel_data| should not be accessed again after a call to this function.
+//
+// The returned object cannot be used as a render target (e.g. calling
 // SbBlitterGetRenderTargetFromSurface() on it will return
 // SbBlitterInvalidRenderTarget).
-// This function is thread safe with respect to |device|, however
-// |pixel_data| should not be modified on another thread while this function
-// is called.
-// kSbBlitterInvalidSurface is returned if there was an error.
+//
+// This function is thread-safe with respect to |device|, but |pixel_data|
+// should not be modified on another thread while this function is called.
+//
+// Returns kSbBlitterInvalidSurface in the event of an error.
 SB_EXPORT SbBlitterSurface
 SbBlitterCreateSurfaceFromPixelData(SbBlitterDevice device,
                                     SbBlitterPixelData pixel_data);
 
-// Returns whether the |device| supports calls to
-// SbBlitterCreateRenderTargetSurface() with |pixel_format|.
-// This function is thread safe.
+// Indicates whether the |device| supports calls to
+// SbBlitterCreateRenderTargetSurface() with |surface_format|.
+//
+// This function is thread-safe.
+//
+// |device|: The device being checked for compatibility.
+// |surface_format|: The surface format being checked for compatibility.
 SB_EXPORT bool SbBlitterIsSurfaceFormatSupportedByRenderTargetSurface(
     SbBlitterDevice device,
     SbBlitterSurfaceFormat surface_format);
 
 // Creates a new surface with undefined pixel data on |device| with the
-// specified |width|, |height| and |pixel_format|.  One can set the pixel data
+// specified |width|, |height| and |surface_format|. One can set the pixel data
 // on the resulting surface by getting its associated SbBlitterRenderTarget
-// object by calling SbBlitterGetRenderTargetFromSurface().
-// This function is thread safe.
+// object and then calling SbBlitterGetRenderTargetFromSurface().
+//
+// This function is thread-safe.
+//
 // Returns kSbBlitterInvalidSurface upon failure.
 SB_EXPORT SbBlitterSurface
 SbBlitterCreateRenderTargetSurface(SbBlitterDevice device,
@@ -408,195 +453,278 @@
                                    int height,
                                    SbBlitterSurfaceFormat surface_format);
 
-// Destroys |surface|, cleaning up all resources associated with it.
+// Destroys the |surface| object, cleaning up all resources associated with it.
 // This function is not thread safe.
-// Returns whether the destruction succeeded.
+//
+// The return value indicates whether the destruction succeeded.
+//
+// |surface|: The object to be destroyed.
 SB_EXPORT bool SbBlitterDestroySurface(SbBlitterSurface surface);
 
 // Returns the SbBlitterRenderTarget object owned by |surface|.  The returned
 // object can be used as a target for draw calls.
-// This function is not thread safe.
-// Returns kSbBlitterInvalidRenderTarget if |surface| is not able to provide a
-// render target, or on any other error.
+//
+// This function returns kSbBlitterInvalidRenderTarget if |surface| is not
+// able to provide a render target or on any other error.
+//
+// This function is not thread-safe.
 SB_EXPORT SbBlitterRenderTarget
 SbBlitterGetRenderTargetFromSurface(SbBlitterSurface surface);
 
-// Returns a SbBlitterSurfaceInfo structure describing immutable parameters of
-// |surface|, such as width, height and pixel format.  The results will be
-// set on the output parameter |surface_info| which cannot be NULL.
-// This function is not thread safe.
-// Returns whether the information was retrieved successfully.
+// Retrieves an SbBlitterSurfaceInfo structure, which describes immutable
+// parameters of the |surface|, such as its width, height and pixel format.
+// The results are set on the output parameter |surface_info|, which cannot
+// be NULL.
+//
+// The return value indicates whether the information was retrieved
+// successfully.
+//
+// This function is not thread-safe.
 SB_EXPORT bool SbBlitterGetSurfaceInfo(SbBlitterSurface surface,
                                        SbBlitterSurfaceInfo* surface_info);
 
-// Returns whether the combination of parameters (|surface|, |pixel_format|) are
-// valid for calls to SbBlitterDownloadSurfacePixels().
-// This function is not thread safe.
+// Indicates whether the combination of parameter values is valid for calls
+// to SbBlitterDownloadSurfacePixels().
+//
+// This function is not thread-safe.
+//
+// |surface|: The surface being checked.
+// |pixel_format|: The pixel format that would be used on the surface.
 SB_EXPORT bool SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels(
     SbBlitterSurface surface,
     SbBlitterPixelDataFormat pixel_format);
 
 // Downloads |surface| pixel data into CPU memory pointed to by
 // |out_pixel_data|, formatted according to the requested |pixel_format| and
-// the requested |pitch_in_bytes|.  Thus, |out_pixel_data| must point to a
-// region of memory with a size of surface_height * |pitch_in_bytes| *
-// SbBlitterBytesPerPixelForFormat(pixel_format) bytes.  The function
-// SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels() can be called first
-// to check that your requested |pixel_format| is valid for |surface|.  When
-// this function is called, it will first wait for all previously flushed
+// the requested |pitch_in_bytes|. Before calling this function, you can call
+// SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels() to confirm that
+// |pixel_format| is, in fact, valid for |surface|.
+//
+// When this function is called, it first waits for all previously flushed
 // graphics commands to be executed by the device before downloading the data.
 // Since this function waits for the pipeline to empty, it should be used
-// sparingly, such as within in debug or test environments.  The returned
-// alpha format will be premultiplied.
-// This function is not thread safe.
-// Returns whether the pixel data was downloaded successfully or not.
+// sparingly, such as within in debug or test environments.
+//
+// The return value indicates whether the pixel data was downloaded
+// successfully.
+//
+// The returned alpha format is premultiplied.
+//
+// This function is not thread-safe.
+//
+// |out_pixel_data|: A pointer to a region of memory with a size of
+//                   (surface_height * |pitch_in_bytes| *
+//                   SbBlitterBytesPerPixelForFormat(pixel_format) bytes.
 SB_EXPORT bool SbBlitterDownloadSurfacePixels(
     SbBlitterSurface surface,
     SbBlitterPixelDataFormat pixel_format,
     int pitch_in_bytes,
     void* out_pixel_data);
 
-// Flips |swap_chain|, making the buffer that was previously accessible to
-// draw commands via SbBlitterGetRenderTargetFromSwapChain() now visible on the
-// display, while another buffer in an initially undefined state is setup as the
-// draw command target.  Note that you do not need to call
+// Flips the |swap_chain| by making the buffer previously accessible to
+// draw commands via SbBlitterGetRenderTargetFromSwapChain() visible on the
+// display, while another buffer in an initially undefined state is set up
+// as the new draw command target. Note that you do not need to call
 // SbBlitterGetRenderTargetFromSwapChain() again after flipping, the swap
-// chain's render target will always refer to its current back buffer.  This
-// function will stall the calling thread until the next vertical refresh.
-// Note that to ensure consistency with the Starboard Player API when rendering
-// punch-out video, calls to SbPlayerSetBounds() will not take effect until
-// this method is called.
-// This function is not thread safe.
-// Returns whether the flip succeeded.
+// chain's render target always refers to its current back buffer.
+//
+// This function stalls the calling thread until the next vertical refresh.
+// In addition, to ensure consistency with the Starboard Player API when
+// rendering punch-out video, calls to SbPlayerSetBounds() do not take effect
+// until this method is called.
+//
+// The return value indicates whether the flip succeeded.
+//
+// This function is not thread-safe.
+//
+// |swap_chain|: The SbBlitterSwapChain to be flipped.
 SB_EXPORT bool SbBlitterFlipSwapChain(SbBlitterSwapChain swap_chain);
 
-// Returns the maximum number of contexts that |device| can support in parallel.
-// In many cases, devices support only a single context.  If
-// SbBlitterCreateContext() has been used to create the maximum number of
-// contexts, all subsequent calls to SbBlitterCreateContext() will fail.
-// This function is thread safe.
-// This function returns -1 upon failure.
+// Returns the maximum number of contexts that |device| can support in
+// parallel. Note that devices often support only a single context.
+//
+// This function is thread-safe.
+//
+// This function returns |-1| upon failure.
+//
+// |device|: The SbBlitterDevice for which the maximum number of contexts is
+// returned.
 SB_EXPORT int SbBlitterGetMaxContexts(SbBlitterDevice device);
 
-// Creates a SbBlitterContext object on the specified |device|.  The returned
-// context can be used to setup draw state and issue draw calls.  Note that
-// there is a limit on the number of contexts that can exist at the same time
-// (in many cases this is 1), and this can be queried by calling
-// SbBlitterGetMaxContexts().  SbBlitterContext objects keep track of draw
-// state between a series of draw calls.  Please refer to the documentation
-// around the definition of SbBlitterContext for more information about
-// contexts.
-// This function is thread safe.
-// This function returns kSbBlitterInvalidContext upon failure.
+// Creates an SbBlitterContext object on |device|. The returned context can be
+// used to set up draw state and issue draw calls.
+//
+// Note that there is a limit on the number of contexts that can exist
+// simultaneously, which can be queried by calling SbBlitterGetMaxContexts().
+// (The limit is often |1|.)
+//
+// SbBlitterContext objects keep track of draw state between a series of draw
+// calls. Please refer to the SbBlitterContext() definition for more
+// information about contexts.
+//
+// This function is thread-safe.
+//
+// This function returns kSbBlitterInvalidContext upon failure. Note that the
+// function fails if it has already been used to create the maximum number
+// of contexts.
+//
+// |device|: The SbBlitterDevice for which the SbBlitterContext object is
+// created.
 SB_EXPORT SbBlitterContext SbBlitterCreateContext(SbBlitterDevice device);
 
-// Destroys the specified |context| created by |device|, freeing all its
-// resources.
-// This function is not thread safe.
-// Returns whether the destruction succeeded.
+// Destroys the specified |context|, freeing all its resources. This function
+// is not thread-safe.
+//
+// The return value indicates whether the destruction succeeded.
+//
+// |context|: The object to be destroyed.
 SB_EXPORT bool SbBlitterDestroyContext(SbBlitterContext context);
 
-// Flushes all draw calls previously issued to |context|.  After this call,
-// all subsequent draw calls (on any context) are guaranteed to be processed
-// by the device after all previous draw calls issued on this |context|.
-// In many cases you will want to call this before calling
-// SbBlitterFlipSwapChain(), to ensure that all draw calls are submitted before
-// the flip occurs.
-// This function is not thread safe.
-// Returns whether the flush succeeded.
+// Flushes all draw calls previously issued to |context|. Calling this function
+// guarantees that the device processes all draw calls issued to this point on
+// this |context| before processing any subsequent draw calls on any context.
+//
+// Before calling SbBlitterFlipSwapChain(), it is often prudent to call this
+// function to ensure that all draw calls are submitted before the flip occurs.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the flush succeeded.
+//
+// |context|: The context for which draw calls are being flushed.
 SB_EXPORT bool SbBlitterFlushContext(SbBlitterContext context);
 
 // Sets up |render_target| as the render target that all subsequent draw calls
-// made on |context| will draw to.
-// This function is not thread safe.
-// Returns whether the render target was successfully set.
+// made on |context| will use.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the render target was set successfully.
+//
+// |context|: The object for which the render target is being set.
+// |render_target|: The target that the |context| should use for draw calls.
 SB_EXPORT bool SbBlitterSetRenderTarget(SbBlitterContext context,
                                         SbBlitterRenderTarget render_target);
 
-// Sets blending state on the specified |context|.  If |blending| is true, the
-// source alpha of subsequent draw calls will be used to blend with the
-// destination color.  In particular, Fc = Sc * Sa + Dc * (1 - Sa), where
-// Fc is the final color, Sc is the source color, Sa is the source alpha, and
-// Dc is the destination color.  If |blending| is false, the source color and
-// alpha will overwrite the destination color and alpha.  By default blending
+// Sets the blending state for the specified |context|. By default, blending
 // is disabled on a SbBlitterContext.
-// This function is not thread safe.
-// Returns whether the blending state was succcessfully set.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the blending state was set successfully.
+//
+// |context|: The context for which the blending state is being set.
+// |blending|: The blending state for the |context|.
+// If |blending| is |true|, the source alpha of subsequent draw calls
+// is used to blend with the destination color. In particular,
+// (|Fc = Sc * Sa + Dc * (1 - Sa)|), where:
+// - |Fc| is the final color.
+// - |Sc| is the source color.
+// - |Sa| is the source alpha.
+// - |Dc| is the destination color.
+// If |blending| is |false|, the source color and source alpha overwrite
+// the destination color and alpha.
 SB_EXPORT bool SbBlitterSetBlending(SbBlitterContext context, bool blending);
 
 // Sets the context's current color.  The current color's default value is
-// SbBlitterColorFromRGBA(255, 255, 255 255).  The current color affects the
-// fill rectangle's color in calls to SbBlitterFillRect(), and if
-// SbBlitterSetModulateBlitsWithColor() has been called to enable blit color
-// modulation, the source blit surface pixel color will also be modulated by
-// the color before being output.  The color is specified in unpremultiplied
-// alpha format.
-// This function is not thread safe.
-// Returns whether the color was successfully set.
+// |SbBlitterColorFromRGBA(255, 255, 255 255)|.
+//
+// The current color affects the fill rectangle's color in calls to
+// SbBlitterFillRect(). If SbBlitterSetModulateBlitsWithColor() has been called
+// to enable blit color modulation, the source blit surface pixel color is also
+// modulated by the color before being output.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the color was set successfully.
+//
+// |context|: The context for which the color is being set.
+// |color|: The context's new color, specified in unpremultiplied alpha format.
 SB_EXPORT bool SbBlitterSetColor(SbBlitterContext context,
                                  SbBlitterColor color);
 
 // Sets whether or not blit calls should have their source pixels modulated by
-// the current color (set via a call to SbBlitterSetColor()) before being
-// output.  This can be used to apply opacity to blit calls, as well as for
-// coloring alpha-only surfaces, and other effects.
-// This function is not thread safe.
-// Returns whether the state was successfully set.
+// the current color, which is set using SbBlitterSetColor(), before being
+// output. This function can apply opacity to blit calls, color alpha-only
+// surfaces, and apply other effects.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the state was set successfully.
+//
+// |modulate_blits_with_color|: Indicates whether to modulate source pixels
+// in blit calls.
 SB_EXPORT bool SbBlitterSetModulateBlitsWithColor(
     SbBlitterContext context,
     bool modulate_blits_with_color);
 
-// Sets the scissor rectangle.  The scissor rectangle dictates a rectangle of
-// visibility that affects all draw calls.  Only pixels within the scissor
-// rectangle will be rendered, all drawing outside of the scissor rectangle will
-// be clipped.  When SbBlitterSetRenderTarget() is called, the scissor rectangle
-// is automatically set to the extents of the specified render target.  If a
+// Sets the scissor rectangle, which dictates a visibility area that affects
+// all draw calls. Only pixels within the scissor rectangle are rendered, and
+// all drawing outside of that area is clipped.
+//
+// When SbBlitterSetRenderTarget() is called, that function automatically sets
+// the scissor rectangle to the size of the specified render target. If a
 // scissor rectangle is specified outside of the extents of the current render
-// target bounds, it will be intersected with the render target boudns.  It is
-// an error to call this function before a render target has been specified for
-// the context.
-// This function is not thread safe.
-// Returns whether the scissor was successfully set.
+// target bounds, it will be intersected with the render target bounds.
+//
+// This function is not thread-safe.
+//
+// Returns whether the scissor was successfully set. It returns an error if
+// it is called before a render target has been specified for the context.
 SB_EXPORT bool SbBlitterSetScissor(SbBlitterContext context,
                                    SbBlitterRect rect);
 
-// Issues a draw call on |context| that fills a rectangle |rect|.  The color
-// of the rectangle is specified by the last call to SbBlitterSetColor().
-// This function is not thread safe.
-// Returns whether the draw call succeeded.
+// Issues a draw call on |context| that fills the specified rectangle |rect|.
+// The rectangle's color is determined by the last call to SbBlitterSetColor().
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the draw call succeeded.
+//
+// |context|: The context on which the draw call will operate.
+// |rect|: The rectangle to be filled.
 SB_EXPORT bool SbBlitterFillRect(SbBlitterContext context, SbBlitterRect rect);
 
-// Issues a draw call on |context| that blits an area of |source_surface|
-// specified by a |src_rect| to |context|'s current render target at |dst_rect|.
-// The source rectangle must lie within the dimensions of |source_surface|.  The
-// |source_surface|'s alpha will be modulated by |opacity| before being drawn.
-// For |opacity|, a value of 0 implies complete invisibility and a value of
-// 255 implies complete opaqueness.
-// This function is not thread safe.
-// Returns whether the draw call succeeded.
+// Issues a draw call on |context| that blits the area of |source_surface|
+// specified by |src_rect| to |context|'s current render target at |dst_rect|.
+// The source rectangle must lie within the dimensions of |source_surface|.
+// Note that the |source_surface|'s alpha is modulated by |opacity| before
+// being drawn. For |opacity|, a value of 0 implies complete invisibility,
+// and a value of 255 implies complete opacity.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the draw call succeeded.
+//
+// |src_rect|: The area to be block transferred (blitted).
 SB_EXPORT bool SbBlitterBlitRectToRect(SbBlitterContext context,
                                        SbBlitterSurface source_surface,
                                        SbBlitterRect src_rect,
                                        SbBlitterRect dst_rect);
 
-// This function does the exact same as SbBlitterBlitRectToRect(), except
-// it permits values of |src_rect| outside of the dimensions of
-// |source_surface| and in these regions the pixel data from |source_surface|
-// will be wrapped.  Negative values for |src_rect.x| and |src_rect.y| are
-// allowed.  The output will all be stretched to fit inside of |dst_rect|.
-// This function is not thread safe.
-// Returns whether the draw call succeeded.
+// This function functions identically to SbBlitterBlitRectToRect(), except
+// it permits values of |src_rect| outside the dimensions of |source_surface|.
+// In those regions, the pixel data from |source_surface| will be wrapped.
+// Negative values for |src_rect.x| and |src_rect.y| are allowed.
+//
+// The output is all stretched to fit inside of |dst_rect|.
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the draw call succeeded.
 SB_EXPORT bool SbBlitterBlitRectToRectTiled(SbBlitterContext context,
                                             SbBlitterSurface source_surface,
                                             SbBlitterRect src_rect,
                                             SbBlitterRect dst_rect);
 
 // This function achieves the same effect as calling SbBlitterBlitRectToRect()
-// |num_rects| time with each of the |num_rects| values of |src_rects| and
-// |dst_rects|.  Using this function allows for greater efficiency than calling
-// SbBlitterBlitRectToRect() in a loop.
-// This function is not thread safe.
-// Returns whether the draw call succeeded.
+// |num_rects| times with each of the |num_rects| values of |src_rects| and
+// |dst_rects|. This function allows for greater efficiency than looped calls
+// to SbBlitterBlitRectToRect().
+//
+// This function is not thread-safe.
+//
+// The return value indicates whether the draw call succeeded.
 SB_EXPORT bool SbBlitterBlitRectsToRects(SbBlitterContext context,
                                          SbBlitterSurface source_surface,
                                          const SbBlitterRect* src_rects,
diff --git a/src/starboard/byte_swap.h b/src/starboard/byte_swap.h
index 7c2e968..9fbd1fa 100644
--- a/src/starboard/byte_swap.h
+++ b/src/starboard/byte_swap.h
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Functions for swapping byte order, used to deal with endianness when
-// performing I/O
+// Module Overview: Starboard Byte Swap module
+//
+// Specifies functions for swapping byte order. These functions are used to
+// deal with endianness when performing I/O.
 
 #ifndef STARBOARD_BYTE_SWAP_H_
 #define STARBOARD_BYTE_SWAP_H_
@@ -53,21 +55,27 @@
 // and then act on that determination somehow.
 
 // Unconditionally swaps the byte order in signed 16-bit |value|.
+// |value|: The value for which the byte order will be swapped.
 SB_EXPORT int16_t SbByteSwapS16(int16_t value);
 
 // Unconditionally swaps the byte order in unsigned 16-bit |value|.
+// |value|: The value for which the byte order will be swapped.
 SB_EXPORT uint16_t SbByteSwapU16(uint16_t value);
 
 // Unconditionally swaps the byte order in signed 32-bit |value|.
+// |value|: The value for which the byte order will be swapped.
 SB_EXPORT int32_t SbByteSwapS32(int32_t value);
 
 // Unconditionally swaps the byte order in unsigned 32-bit |value|.
+// |value|: The value for which the byte order will be swapped.
 SB_EXPORT uint32_t SbByteSwapU32(uint32_t value);
 
 // Unconditionally swaps the byte order in signed 64-bit |value|.
+// |value|: The value for which the byte order will be swapped.
 SB_EXPORT int64_t SbByteSwapS64(int64_t value);
 
 // Unconditionally swaps the byte order in unsigned 64-bit |value|.
+// |value|: The value for which the byte order will be swapped.
 SB_EXPORT uint64_t SbByteSwapU64(uint64_t value);
 
 #ifdef __cplusplus
diff --git a/src/starboard/character.h b/src/starboard/character.h
index f4d064f..f7758c7 100644
--- a/src/starboard/character.h
+++ b/src/starboard/character.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Functions for interacting with characters.
+// Module Overview: Starboard Character module
+//
+// Provides functions for interacting with characters.
 
 #ifndef STARBOARD_CHARACTER_H_
 #define STARBOARD_CHARACTER_H_
@@ -26,36 +28,50 @@
 #endif
 
 // Converts the given 8-bit character (as an int) to uppercase in the current
-// locale, returning the result, also an 8-bit character. If there is no
-// uppercase version of the character, or the character is already uppercase, it
-// will just return the character as-is.
-int SbCharacterToUpper(int c);
+// locale and returns an 8-bit character. If there is no uppercase version of
+// the character, or the character is already uppercase, the function just
+// returns the character as-is.
+//
+// |c|: The character to be converted.
+SB_EXPORT int SbCharacterToUpper(int c);
 
 // Converts the given 8-bit character (as an int) to lowercase in the current
-// locale, returning the result, also an 8-bit character. If there is no
-// lowercase version of the character, or the character is already lowercase, it
-// will just return the character as-is.
-int SbCharacterToLower(int c);
+// locale and returns an 8-bit character. If there is no lowercase version of
+// the character, or the character is already lowercase, the function just
+// returns the character as-is.
+//
+// |c|: The character to be converted.
+SB_EXPORT int SbCharacterToLower(int c);
 
-// Returns whether the given 8-bit character |c| (as an int) is a space in the
-// current locale.
-bool SbCharacterIsSpace(int c);
-
-// Returns whether the given 8-bit character |c| (as an int) is uppercase in the
-// current locale.
-bool SbCharacterIsUpper(int c);
-
-// Returns whether the given 8-bit character |c| (as an int) is a decimal digit
-// in the current locale.
-bool SbCharacterIsDigit(int c);
-
-// Returns whether the given 8-bit character |c| (as an int) is a hexidecimal
-// digit in the current locale.
-bool SbCharacterIsHexDigit(int c);
-
-// Returns whether the given 8-bit character |c| (as an int) is alphanumeric in
+// Indicates whether the given 8-bit character |c| (as an int) is a space in
 // the current locale.
-bool SbCharacterIsAlphanumeric(int c);
+//
+// |c|: The character to be evaluated.
+SB_EXPORT bool SbCharacterIsSpace(int c);
+
+// Indicates whether the given 8-bit character |c| (as an int) is uppercase
+// in the current locale.
+//
+// |c|: The character to be evaluated.
+SB_EXPORT bool SbCharacterIsUpper(int c);
+
+// Indicates whether the given 8-bit character |c| (as an int) is a
+// decimal digit in the current locale.
+//
+// |c|: The character to be evaluated.
+SB_EXPORT bool SbCharacterIsDigit(int c);
+
+// Indicates whether the given 8-bit character |c| (as an int) is a hexadecimal
+// in the current locale.
+//
+// |c|: The character to be evaluated.
+SB_EXPORT bool SbCharacterIsHexDigit(int c);
+
+// Indicates whether the given 8-bit character |c| (as an int) is alphanumeric
+// in the current locale.
+//
+// |c|: The character to be evaluated.
+SB_EXPORT bool SbCharacterIsAlphanumeric(int c);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/starboard/client_porting/eztime/eztime.h b/src/starboard/client_porting/eztime/eztime.h
index 8402006..8221c2f 100644
--- a/src/starboard/client_porting/eztime/eztime.h
+++ b/src/starboard/client_porting/eztime/eztime.h
@@ -15,7 +15,9 @@
 #ifndef STARBOARD_CLIENT_PORTING_EZTIME_EZTIME_H_
 #define STARBOARD_CLIENT_PORTING_EZTIME_EZTIME_H_
 
+#include "starboard/log.h"
 #include "starboard/time.h"
+#include "starboard/types.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -119,7 +121,12 @@
 // Converts SbTime to EzTimeValue.
 static SB_C_FORCE_INLINE EzTimeValue EzTimeValueFromSbTime(SbTime in_time) {
   EzTimeT sec = EzTimeTFromSbTime(in_time);
-  EzTimeValue value = {sec, in_time - EzTimeTToSbTime(sec)};
+  SbTime diff = in_time - EzTimeTToSbTime(sec);
+  SB_DCHECK(diff >= INT_MIN);
+  SB_DCHECK(diff <= INT_MAX);
+  EzTimeValue value = {sec, (int)diff};  // Some compilers do not support
+                                         // returning the initializer list
+                                         // directly.
   return value;
 }
 
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index e354d5e..c6a509b 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -28,8 +28,12 @@
         'decode_target_provider.cc',
         'memory.cc',
         'move.h',
+        'ref_counted.cc',
+        'ref_counted.h',
         'reset_and_return.h',
         'scoped_ptr.h',
+        'thread_collision_warner.cc',
+        'thread_collision_warner.h',
       ],
       'defines': [
         # This must be defined when building Starboard, and must not when
diff --git a/src/starboard/common/memory.cc b/src/starboard/common/memory.cc
index 6067214..4ed3362 100644
--- a/src/starboard/common/memory.cc
+++ b/src/starboard/common/memory.cc
@@ -12,32 +12,87 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/atomic.h"
+#include "starboard/log.h"
 #include "starboard/memory.h"
+#include "starboard/memory_reporter.h"
+#include "starboard/shared/starboard/memory_reporter_internal.h"
 
+namespace {
 inline void* SbMemoryAllocateImpl(size_t size);
 inline void* SbMemoryAllocateAlignedImpl(size_t alignment, size_t size);
 inline void* SbMemoryReallocateImpl(void* memory, size_t size);
+inline void SbReportAllocation(const void* memory, size_t size);
+inline void SbReportDeallocation(const void* memory);
+
+SbMemoryReporter* s_memory_reporter = NULL;
+
+bool LeakTraceEnabled();               // True when leak tracing enabled.
+bool StarboardAllowsMemoryTracking();  // True when build enabled.
+}  // namespace.
+
+bool SbMemorySetReporter(SbMemoryReporter* reporter) {
+  // TODO: We should run a runtime test here with a test memory
+  // reporter that determines whether global operator new/delete are properly
+  // overridden. This problem appeared with address sanitizer and given
+  // how tricky operator new/delete are in general (see a google search)
+  // it's reasonable to assume that the likely hood of this happening again
+  // is high. To allow the QA/developer to take corrective action, this
+  // condition needs to be detected at runtime with a simple error message so
+  // that corrective action (i.e. running a different build) can be applied.
+  //
+  // RunOperatorNewDeleteRuntimeTest();  // Implement me.
+
+  // Flush local memory to main so that other threads don't
+  // see a partially constructed reporter due to memory
+  // re-ordering.
+  SbAtomicMemoryBarrier();
+  s_memory_reporter = reporter;
+
+  // These are straight forward error messages. We use the build settings to
+  // predict whether the MemoryReporter is likely to fail.
+  if (!StarboardAllowsMemoryTracking()) {
+    SbLogRaw("\nMemory Reporting is disabled because this build does "
+             "not support it. Try a QA, devel or debug build.\n");
+    return false;
+  } else if (LeakTraceEnabled()) {
+    SbLogRaw("\nMemory Reporting might be disabled because leak trace "
+             "(from address sanitizer?) is active.\n");
+    return false;
+  }
+  return true;
+}
 
 void* SbMemoryAllocate(size_t size) {
   void* memory = SbMemoryAllocateImpl(size);
+  SbReportAllocation(memory, size);
   return memory;
 }
 
 void* SbMemoryAllocateAligned(size_t alignment, size_t size) {
   void* memory = SbMemoryAllocateAlignedImpl(alignment, size);
+  SbReportAllocation(memory, size);
   return memory;
 }
 
 void* SbMemoryReallocate(void* memory, size_t size) {
+  SbReportDeallocation(memory);
   void* new_memory = SbMemoryReallocateImpl(memory, size);
+  SbReportAllocation(new_memory, size);
   return new_memory;
 }
 
 void SbMemoryDeallocate(void* memory) {
+  // Report must happen first or else a race condition allows the memory to
+  // be freed and then reported as allocated, before the allocation is removed.
+  SbReportDeallocation(memory);
   SbMemoryFree(memory);
 }
 
 void SbMemoryDeallocateAligned(void* memory) {
+  // Report must happen first or else a race condition allows the memory to
+  // be freed and then reported as allocated, before the allocation is removed.
+  SbReportDeallocation(memory);
   SbMemoryFreeAligned(memory);
 }
 
@@ -63,6 +118,63 @@
   return address;
 }
 
+void SbMemoryReporterReportMappedMemory(const void* memory, size_t size) {
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return;
+#else
+  if (SB_LIKELY(!s_memory_reporter)) {
+    return;
+  }
+  s_memory_reporter->on_mapmem_cb(
+      s_memory_reporter->context,
+      memory,
+      size);
+#endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
+}
+
+void SbMemoryReporterReportUnmappedMemory(const void* memory, size_t size) {
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return;
+#else
+  if (SB_LIKELY(!s_memory_reporter)) {
+    return;
+  }
+  s_memory_reporter->on_unmapmem_cb(
+      s_memory_reporter->context,
+      memory,
+      size);
+#endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
+}
+
+namespace {  // anonymous namespace.
+
+inline void SbReportAllocation(const void* memory, size_t size) {
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return;
+#else
+  if (SB_LIKELY(!s_memory_reporter)) {
+    return;
+  }
+  s_memory_reporter->on_alloc_cb(
+      s_memory_reporter->context,
+      memory,
+      size);
+#endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
+}
+
+inline void SbReportDeallocation(const void* memory) {
+#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return;
+#else
+  if (SB_LIKELY(!s_memory_reporter)) {
+    return;
+  }
+  s_memory_reporter->on_dealloc_cb(
+      s_memory_reporter->context,
+      memory);
+#endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
+}
+
 inline void* SbMemoryAllocateImpl(size_t size) {
 #if SB_ABORT_ON_ALLOCATION_FAILURE
   return SbMemoryAllocateChecked(size);
@@ -86,3 +198,25 @@
   return SbMemoryReallocateUnchecked(memory, size);
 #endif
 }
+
+bool LeakTraceEnabled() {
+#if defined(HAS_LEAK_SANITIZER) && (0 == HAS_LEAK_SANITIZER)
+  // In this build the leak tracer is specifically disabled. This
+  // build condition is typical for some builds of address sanitizer.
+  return true;
+#elif defined(ADDRESS_SANITIZER)
+  // Leak tracer is not specifically disabled and address sanitizer is running.
+  return true;
+#else
+  return false;
+#endif
+}
+
+bool StarboardAllowsMemoryTracking() {
+#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+  return true;
+#else
+  return false;
+#endif
+}
+}  // namespace
diff --git a/src/starboard/common/ref_counted.cc b/src/starboard/common/ref_counted.cc
new file mode 100644
index 0000000..32f67d5
--- /dev/null
+++ b/src/starboard/common/ref_counted.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "starboard/common/ref_counted.h"
+
+#include "starboard/log.h"
+
+namespace starboard {
+
+namespace subtle {
+
+RefCountedBase::RefCountedBase()
+    : ref_count_(0)
+#ifndef NDEBUG
+      ,
+      in_dtor_(false)
+#endif
+{
+}
+
+RefCountedBase::~RefCountedBase() {
+#ifndef NDEBUG
+  SB_DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+#endif
+}
+
+void RefCountedBase::AddRef() const {
+// TODO(maruel): Add back once it doesn't assert 500 times/sec.
+// Current thread books the critical section "AddRelease" without release it.
+// DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+#ifndef NDEBUG
+  SB_DCHECK(!in_dtor_);
+#endif
+  ++ref_count_;
+}
+
+bool RefCountedBase::Release() const {
+// TODO(maruel): Add back once it doesn't assert 500 times/sec.
+// Current thread books the critical section "AddRelease" without release it.
+// DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+#ifndef NDEBUG
+  SB_DCHECK(!in_dtor_);
+#endif
+  if (--ref_count_ == 0) {
+#ifndef NDEBUG
+    in_dtor_ = true;
+#endif
+    return true;
+  }
+  return false;
+}
+
+bool RefCountedThreadSafeBase::HasOneRef() const {
+  return (SbAtomicAcquire_Load(
+              &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_) == 1);
+}
+
+RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) {
+#ifndef NDEBUG
+  in_dtor_ = false;
+#endif
+}
+
+RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
+#ifndef NDEBUG
+  SB_DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
+                         "calling Release()";
+#endif
+}
+
+void RefCountedThreadSafeBase::AddRef() const {
+#ifndef NDEBUG
+  SB_DCHECK(!in_dtor_);
+#endif
+  SbAtomicNoBarrier_Increment(&ref_count_, 1);
+}
+
+bool RefCountedThreadSafeBase::Release() const {
+#ifndef NDEBUG
+  SB_DCHECK(!in_dtor_);
+  SB_DCHECK(!(SbAtomicAcquire_Load(&ref_count_) == 0));
+#endif
+  if (SbAtomicBarrier_Increment(&ref_count_, -1) == 0) {
+#ifndef NDEBUG
+    in_dtor_ = true;
+#endif
+    return true;
+  }
+  return false;
+}
+
+}  // namespace subtle
+
+}  // namespace starboard
diff --git a/src/starboard/common/ref_counted.h b/src/starboard/common/ref_counted.h
new file mode 100644
index 0000000..0ab74db
--- /dev/null
+++ b/src/starboard/common/ref_counted.h
@@ -0,0 +1,288 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STARBOARD_COMMON_REF_COUNTED_H_
+#define STARBOARD_COMMON_REF_COUNTED_H_
+
+#include "starboard/atomic.h"
+#include "starboard/common/thread_collision_warner.h"
+#include "starboard/log.h"
+
+namespace starboard {
+
+namespace subtle {
+
+class RefCountedBase {
+ public:
+  bool HasOneRef() const { return ref_count_ == 1; }
+
+ protected:
+  RefCountedBase();
+  ~RefCountedBase();
+
+  void AddRef() const;
+
+  // Returns true if the object should self-delete.
+  bool Release() const;
+
+ private:
+  mutable int ref_count_;
+#ifndef NDEBUG
+  mutable bool in_dtor_;
+#endif
+
+  DFAKE_MUTEX(add_release_);
+};
+
+class RefCountedThreadSafeBase {
+ public:
+  bool HasOneRef() const;
+
+ protected:
+  RefCountedThreadSafeBase();
+  ~RefCountedThreadSafeBase();
+
+  void AddRef() const;
+
+  // Returns true if the object should self-delete.
+  bool Release() const;
+
+ private:
+  mutable SbAtomic32 ref_count_;
+#ifndef NDEBUG
+  mutable bool in_dtor_;
+#endif
+};
+
+}  // namespace subtle
+
+//
+// A base class for reference counted classes.  Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class.  To use this guy just extend your
+// class from it like so:
+//
+//   class MyFoo : public starboard::RefCounted<MyFoo> {
+//    ...
+//    private:
+//     friend class starboard::RefCounted<MyFoo>;
+//     ~MyFoo();
+//   };
+//
+// You should always make your destructor private, to avoid any code deleting
+// the object accidently while there are references to it.
+template <class T>
+class RefCounted : public subtle::RefCountedBase {
+ public:
+  RefCounted() {}
+
+  void AddRef() const { subtle::RefCountedBase::AddRef(); }
+
+  void Release() const {
+    if (subtle::RefCountedBase::Release()) {
+      delete static_cast<const T*>(this);
+    }
+  }
+
+ protected:
+  ~RefCounted() {}
+};
+
+// Forward declaration.
+template <class T, typename Traits>
+class RefCountedThreadSafe;
+
+// Default traits for RefCountedThreadSafe<T>.  Deletes the object when its ref
+// count reaches 0.  Overload to delete it on a different thread etc.
+template <typename T>
+struct DefaultRefCountedThreadSafeTraits {
+  static void Destruct(const T* x) {
+    // Delete through RefCountedThreadSafe to make child classes only need to be
+    // friend with RefCountedThreadSafe instead of this struct, which is an
+    // implementation detail.
+    RefCountedThreadSafe<T, DefaultRefCountedThreadSafeTraits>::DeleteInternal(
+        x);
+  }
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+//   class MyFoo : public starboard::RefCountedThreadSafe<MyFoo> {
+//    ...
+//   };
+//
+// If you're using the default trait, then you should add compile time
+// asserts that no one else is deleting your object.  i.e.
+//    private:
+//     friend class starboard::RefCountedThreadSafe<MyFoo>;
+//     ~MyFoo();
+template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> >
+class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
+ public:
+  RefCountedThreadSafe() {}
+
+  void AddRef() const { subtle::RefCountedThreadSafeBase::AddRef(); }
+
+  void Release() const {
+    if (subtle::RefCountedThreadSafeBase::Release()) {
+      Traits::Destruct(static_cast<const T*>(this));
+    }
+  }
+
+ protected:
+  ~RefCountedThreadSafe() {}
+
+ private:
+  friend struct DefaultRefCountedThreadSafeTraits<T>;
+  static void DeleteInternal(const T* x) { delete x; }
+};
+
+//
+// A thread-safe wrapper for some piece of data so we can place other
+// things in scoped_refptrs<>.
+//
+template <typename T>
+class RefCountedData : public starboard::RefCountedThreadSafe<starboard::RefCountedData<T> > {
+ public:
+  RefCountedData() : data() {}
+  RefCountedData(const T& in_value) : data(in_value) {}
+
+  T data;
+
+ private:
+  friend class starboard::RefCountedThreadSafe<starboard::RefCountedData<T> >;
+  ~RefCountedData() {}
+};
+
+//
+// A smart pointer class for reference counted objects.  Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference.  Sample usage:
+//
+//   class MyFoo : public RefCounted<MyFoo> {
+//    ...
+//   };
+//
+//   void some_function() {
+//     scoped_refptr<MyFoo> foo = new MyFoo();
+//     foo->Method(param);
+//     // |foo| is released when this function returns
+//   }
+//
+//   void some_other_function() {
+//     scoped_refptr<MyFoo> foo = new MyFoo();
+//     ...
+//     foo = NULL;  // explicitly releases |foo|
+//     ...
+//     if (foo)
+//       foo->Method(param);
+//   }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+//   {
+//     scoped_refptr<MyFoo> a = new MyFoo();
+//     scoped_refptr<MyFoo> b;
+//
+//     b.swap(a);
+//     // now, |b| references the MyFoo object, and |a| references NULL.
+//   }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+//   {
+//     scoped_refptr<MyFoo> a = new MyFoo();
+//     scoped_refptr<MyFoo> b;
+//
+//     b = a;
+//     // now, |a| and |b| each own a reference to the same MyFoo object.
+//   }
+//
+template <class T>
+class scoped_refptr {
+ public:
+  typedef T element_type;
+
+  scoped_refptr() : ptr_(NULL) {}
+
+  scoped_refptr(T* p) : ptr_(p) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  template <typename U>
+  scoped_refptr(const scoped_refptr<U>& r)
+      : ptr_(r.get()) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  ~scoped_refptr() {
+    if (ptr_)
+      ptr_->Release();
+  }
+
+  T* get() const { return ptr_; }
+  operator T*() const { return ptr_; }
+  T* operator->() const {
+    SB_DCHECK(ptr_ != NULL);
+    return ptr_;
+  }
+
+  T& operator*() const {
+    SB_DCHECK(ptr_ != NULL);
+    return *ptr_;
+  }
+
+  scoped_refptr<T>& operator=(T* p) {
+    // AddRef first so that self assignment should work
+    if (p)
+      p->AddRef();
+    T* old_ptr = ptr_;
+    ptr_ = p;
+    if (old_ptr)
+      old_ptr->Release();
+    return *this;
+  }
+
+  scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
+    return * this = r.ptr_;
+  }
+
+  template <typename U>
+  scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
+    return * this = r.get();
+  }
+
+  void swap(T** pp) {
+    T* p = ptr_;
+    ptr_ = *pp;
+    *pp = p;
+  }
+
+  void swap(scoped_refptr<T>& r) { swap(&r.ptr_); }
+
+ protected:
+  T* ptr_;
+};
+
+// Handy utility for creating a scoped_refptr<T> out of a T* explicitly without
+// having to retype all the template arguments
+template <typename T>
+scoped_refptr<T> make_scoped_refptr(T* t) {
+  return scoped_refptr<T>(t);
+}
+
+}  // namespace starboard
+
+#endif  // STARBOARD_COMMON_REF_COUNTED_H_
diff --git a/src/starboard/common/thread_collision_warner.cc b/src/starboard/common/thread_collision_warner.cc
new file mode 100644
index 0000000..5a9e853
--- /dev/null
+++ b/src/starboard/common/thread_collision_warner.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "starboard/common/thread_collision_warner.h"
+
+#include "starboard/atomic.h"
+#include "starboard/log.h"
+#include "starboard/thread.h"
+#include "starboard/types.h"
+
+namespace starboard {
+
+void DCheckAsserter::warn() {
+  SB_NOTREACHED() << "Thread Collision";
+}
+
+static SbAtomic32 CurrentThread() {
+  const SbThreadId current_thread_id = SbThreadGetId();
+  // We need to get the thread id into an atomic data type. This might be a
+  // truncating conversion, but any loss-of-information just increases the
+  // chance of a false negative, not a false positive.
+  const SbAtomic32 atomic_thread_id =
+      static_cast<SbAtomic32>(current_thread_id);
+  return atomic_thread_id;
+}
+
+void ThreadCollisionWarner::EnterSelf() {
+  // If the active thread is 0 then I'll write the current thread ID
+  // if two or more threads arrive here only one will succeed to
+  // write on valid_thread_id_ the current thread ID.
+  SbAtomic32 current_thread_id = CurrentThread();
+
+  int previous_value =
+      SbAtomicNoBarrier_CompareAndSwap(&valid_thread_id_, 0, current_thread_id);
+  if (previous_value != 0 && previous_value != current_thread_id) {
+    // gotcha! a thread is trying to use the same class and that is
+    // not current thread.
+    asserter_->warn();
+  }
+
+  SbAtomicNoBarrier_Increment(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Enter() {
+  SbAtomic32 current_thread_id = CurrentThread();
+
+  if (SbAtomicNoBarrier_CompareAndSwap(&valid_thread_id_, 0,
+                                       current_thread_id) != 0) {
+    // gotcha! another thread is trying to use the same class.
+    asserter_->warn();
+  }
+
+  SbAtomicNoBarrier_Increment(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Leave() {
+  if (SbAtomicBarrier_Increment(&counter_, -1) == 0) {
+    SbAtomicNoBarrier_Store(&valid_thread_id_, 0);
+  }
+}
+
+}  // namespace starboard
diff --git a/src/starboard/common/thread_collision_warner.h b/src/starboard/common/thread_collision_warner.h
new file mode 100644
index 0000000..4aefb21
--- /dev/null
+++ b/src/starboard/common/thread_collision_warner.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_
+#define STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_
+
+#include "starboard/atomic.h"
+
+// A helper class alongside macros to be used to verify assumptions about thread
+// safety of a class.
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+//          are synchronized somehow.
+//
+//          In this case the macro DFAKE_SCOPED_LOCK has to be
+//          used, it checks that if a thread is inside the push/pop then
+//          noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+//  public:
+//   ...
+//   void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... }
+//   int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... }
+//   ...
+//  private:
+//   DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+//          are synchronized somehow, it calls a method to "protect" from
+//          a "protected" method
+//
+//          In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK
+//          has to be used, it checks that if a thread is inside the push/pop
+//          then noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+//  public:
+//   void push(int) {
+//     DFAKE_SCOPED_LOCK(push_pop_);
+//     ...
+//   }
+//   int pop() {
+//     DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+//     bar();
+//     ...
+//   }
+//   void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... }
+//   ...
+//  private:
+//   DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation not usable even if clients are synchronized,
+//          so only one thread in the class life cycle can use the two members
+//          push/pop.
+//
+//          In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the
+//          specified
+//          critical section the first time a thread enters push or pop, from
+//          that time on only that thread is allowed to execute push or pop.
+//
+// class NonThreadSafeQueue {
+//  public:
+//   ...
+//   void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+//   int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+//   ...
+//  private:
+//   DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Class that has to be contructed/destroyed on same thread, it has
+//          a "shareable" method (with external synchronization) and a not
+//          shareable method (even with external synchronization).
+//
+//          In this case 3 Critical sections have to be defined
+//
+// class ExoticClass {
+//  public:
+//   ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+//   ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+//
+//   void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... }
+//   void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+//   ...
+//  private:
+//   DFAKE_MUTEX(ctor_dtor_);
+//   DFAKE_MUTEX(shareable_section_);
+// };
+
+#if !defined(NDEBUG)
+
+// Defines a class member that acts like a mutex. It is used only as a
+// verification tool.
+#define DFAKE_MUTEX(obj) mutable starboard::ThreadCollisionWarner obj
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope.
+#define DFAKE_SCOPED_LOCK(obj) \
+  starboard::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj)
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks.
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \
+  starboard::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj)
+// Asserts the code is always executed in the same thread.
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \
+  starboard::ThreadCollisionWarner::Check check_##obj(&obj)
+
+#else
+
+#define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj
+#define DFAKE_SCOPED_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0)
+
+#endif
+
+namespace starboard {
+
+// The class ThreadCollisionWarner uses an Asserter to notify the collision
+// AsserterBase is the interfaces and DCheckAsserter is the default asserter
+// used. During the unit tests is used another class that doesn't "DCHECK"
+// in case of collision (check thread_collision_warner_unittests.cc)
+struct AsserterBase {
+  virtual ~AsserterBase() {}
+  virtual void warn() = 0;
+};
+
+struct DCheckAsserter : public AsserterBase {
+  virtual ~DCheckAsserter() {}
+  virtual void warn();
+};
+
+class ThreadCollisionWarner {
+ public:
+  // The parameter asserter is there only for test purpose
+  ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
+      : valid_thread_id_(0), counter_(0), asserter_(asserter) {}
+
+  ~ThreadCollisionWarner() { delete asserter_; }
+
+  // This class is meant to be used through the macro
+  // DFAKE_SCOPED_LOCK_THREAD_LOCKED
+  // it doesn't leave the critical section, as opposed to ScopedCheck,
+  // because the critical section being pinned is allowed to be used only
+  // from one thread
+  class Check {
+   public:
+    explicit Check(ThreadCollisionWarner* warner) : warner_(warner) {
+      warner_->EnterSelf();
+    }
+
+    ~Check() {}
+
+   private:
+    ThreadCollisionWarner* warner_;
+  };
+
+  // This class is meant to be used through the macro
+  // DFAKE_SCOPED_LOCK
+  class ScopedCheck {
+   public:
+    explicit ScopedCheck(ThreadCollisionWarner* warner) : warner_(warner) {
+      warner_->Enter();
+    }
+
+    ~ScopedCheck() { warner_->Leave(); }
+
+   private:
+    ThreadCollisionWarner* warner_;
+  };
+
+  // This class is meant to be used through the macro
+  // DFAKE_SCOPED_RECURSIVE_LOCK
+  class ScopedRecursiveCheck {
+   public:
+    explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
+        : warner_(warner) {
+      warner_->EnterSelf();
+    }
+
+    ~ScopedRecursiveCheck() { warner_->Leave(); }
+
+   private:
+    ThreadCollisionWarner* warner_;
+  };
+
+ private:
+  // This method stores the current thread identifier and does a DCHECK
+  // if a another thread has already done it, it is safe if same thread
+  // calls this multiple time (recursion allowed).
+  void EnterSelf();
+
+  // Same as EnterSelf but recursion is not allowed.
+  void Enter();
+
+  // Removes the thread_id stored in order to allow other threads to
+  // call EnterSelf or Enter.
+  void Leave();
+
+  // This stores the thread id that is inside the critical section, if the
+  // value is 0 then no thread is inside.
+  volatile SbAtomic32 valid_thread_id_;
+
+  // Counter to trace how many time a critical section was "pinned"
+  // (when allowed) in order to unpin it when counter_ reaches 0.
+  volatile SbAtomic32 counter_;
+
+  // Here only for class unit tests purpose, during the test I need to not
+  // DCHECK but notify the collision with something else.
+  AsserterBase* asserter_;
+};
+
+}  // namespace starboard
+
+#endif  // STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_
diff --git a/src/starboard/condition_variable.h b/src/starboard/condition_variable.h
index 463f523..673b7d9 100644
--- a/src/starboard/condition_variable.h
+++ b/src/starboard/condition_variable.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Condition variables.
+// Module Overview: Starboard Condition Variable module
+//
+// Defines an interface for condition variables.
 
 #ifndef STARBOARD_CONDITION_VARIABLE_H_
 #define STARBOARD_CONDITION_VARIABLE_H_
@@ -51,18 +53,22 @@
 }
 
 // Creates a new condition variable to work with |opt_mutex|, which may be null,
-// placing the newly created condition variable in |out_condition|. Returns
-// whether the condition variable could be created.
+// placing the newly created condition variable in |out_condition|.
+//
+// The return value indicates whether the condition variable could be created.
+//
 // TODO: It looks like WTF does not have the mutex available when creating
 // the condition variable, and pthreads doesn't appear to require the mutex on
 // condvar creation, so we should just remove the parameter.
 SB_EXPORT bool SbConditionVariableCreate(SbConditionVariable* out_condition,
                                          SbMutex* opt_mutex);
 
-// Destroys a condition variable, returning whether the destruction was
-// successful. The condition variable specified by |condition| is
-// invalidated. Behavior is undefined if other threads are currently waiting
-// on this condition variable.
+// Destroys the specified SbConditionVariable. The return value indicates
+// whether the destruction was successful. The behavior is undefined if other
+// threads are currently waiting on this condition variable.
+//
+// |condition|: The SbConditionVariable to be destroyed. This invalidates the
+// condition variable.
 SB_EXPORT bool SbConditionVariableDestroy(SbConditionVariable* condition);
 
 // Waits for |condition|, releasing the held lock |mutex|, blocking
@@ -72,19 +78,30 @@
 SbConditionVariableWait(SbConditionVariable* condition, SbMutex* mutex);
 
 // Waits for |condition|, releasing the held lock |mutex|, blocking up to
-// |timeout_duration|, and returning the acquisition result. If
-// |timeout_duration| is less than or equal to zero, it will return as quickly
-// as possible with a kSbConditionVariableTimedOut result. Behavior is undefined
-// if |mutex| is not held.
+// |timeout_duration|, and returning the acquisition result. Behavior is
+// undefined if |mutex| is not held.
+//
+// |timeout_duration|: The maximum amount of time that function should wait
+// for |condition|. If the |timeout_duration| value is less than or equal to
+// zero, the function returns as quickly as possible with a
+// kSbConditionVariableTimedOut result.
 SB_EXPORT SbConditionVariableResult
 SbConditionVariableWaitTimed(SbConditionVariable* condition,
                              SbMutex* mutex,
                              SbTime timeout_duration);
 
-// Broadcasts to all current waiters of |condition| to stop waiting.
+// Broadcasts to all current waiters of |condition| to stop waiting. This
+// function wakes all of the threads waiting on |condition| while
+// SbConditionVariableSignal wakes a single thread.
+//
+// |condition|: The condition that should no longer be waited for.
 SB_EXPORT bool SbConditionVariableBroadcast(SbConditionVariable* condition);
 
-// Signals the next waiter of |condition| to stop waiting.
+// Signals the next waiter of |condition| to stop waiting. This function wakes
+// a single thread waiting on |condition| while SbConditionVariableBroadcast
+// wakes all threads waiting on it.
+//
+// |condition|: The condition that the waiter should stop waiting for.
 SB_EXPORT bool SbConditionVariableSignal(SbConditionVariable* condition);
 
 #ifdef __cplusplus
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 444cc9f..838b5e8 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -12,13 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// A description of the current platform in lurid detail such that common code
-// never needs to actually know what the current operating system and
-// architecture are. It is both very pragmatic and canonical in that if any
-// application code finds itself needing to make a platform decision, it should
-// always define a Starboard Configuration feature instead. This implies the
-// continued existence of very narrowly-defined configuration features, but it
-// retains porting control in Starboard.
+// Module Overview: Starboard Configuration module
+//
+// Provides a description of the current platform in lurid detail so that
+// common code never needs to actually know what the current operating system
+// and architecture are.
+//
+// It is both very pragmatic and canonical in that if any application code
+// finds itself needing to make a platform decision, it should always define
+// a Starboard Configuration feature instead. This implies the continued
+// existence of very narrowly-defined configuration features, but it retains
+// porting control in Starboard.
 
 #ifndef STARBOARD_CONFIGURATION_H_
 #define STARBOARD_CONFIGURATION_H_
@@ -365,6 +369,10 @@
 #error "Your platform must define SB_HAS_MICROPHONE in API versions 2 or later."
 #endif
 
+#if SB_VERSION(3) && !defined(SB_HAS_TIME_THREAD_NOW)
+#error "Your platform must define SB_HAS_TIME_THREAD_NOW in API 3 or later."
+#endif
+
 #if SB_HAS(PLAYER)
 #if !SB_IS(PLAYER_COMPOSITED) && !SB_IS(PLAYER_PUNCHED_OUT) && \
     !SB_IS(PLAYER_PRODUCING_TEXTURE)
diff --git a/src/starboard/creator/ci20directfb/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp
index b695c91..284be6b 100644
--- a/src/starboard/creator/ci20directfb/starboard_platform.gyp
+++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -209,6 +209,7 @@
         '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc',
         '<(DEPTH)/starboard/shared/posix/thread_sleep.cc',
         '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc',
+        '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc',
         '<(DEPTH)/starboard/shared/posix/time_get_now.cc',
         '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc',
         '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
diff --git a/src/starboard/creator/ci20directfb/system_get_property.cc b/src/starboard/creator/ci20directfb/system_get_property.cc
index db0f0e0..51ca55f 100644
--- a/src/starboard/creator/ci20directfb/system_get_property.cc
+++ b/src/starboard/creator/ci20directfb/system_get_property.cc
@@ -47,6 +47,7 @@
     case kSbSystemPropertyModelName:
     case kSbSystemPropertyModelYear:
     case kSbSystemPropertyNetworkOperatorName:
+    case kSbSystemPropertySpeechApiKey:
       return false;
 
     case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index bab02c6..34be5d0 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -173,6 +173,7 @@
         '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc',
         '<(DEPTH)/starboard/shared/posix/thread_sleep.cc',
         '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc',
+        '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc',
         '<(DEPTH)/starboard/shared/posix/time_get_now.cc',
         '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc',
         '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
diff --git a/src/starboard/creator/ci20x11/system_get_property.cc b/src/starboard/creator/ci20x11/system_get_property.cc
index 8a7a795..9d71165 100644
--- a/src/starboard/creator/ci20x11/system_get_property.cc
+++ b/src/starboard/creator/ci20x11/system_get_property.cc
@@ -47,6 +47,7 @@
     case kSbSystemPropertyModelName:
     case kSbSystemPropertyModelYear:
     case kSbSystemPropertyNetworkOperatorName:
+    case kSbSystemPropertySpeechApiKey:
       return false;
 
     case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h
index 8898d5b..bcf356a 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/creator/shared/configuration_public.h
@@ -88,7 +88,7 @@
 #define SB_HAS_CROSS_CORE_SCHEDULER 1
 
 // The API version implemented by this platform.
-#define SB_API_VERSION 1
+#define SB_API_VERSION 2
 
 // --- System Header Configuration -------------------------------------------
 
@@ -119,6 +119,12 @@
 // Whether the current platform provides the standard header float.h.
 #define SB_HAS_FLOAT_H 1
 
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
 // Type detection for wchar_t.
 #if defined(__WCHAR_MAX__) && \
     (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -439,6 +445,12 @@
 // The maximum number of users that can be signed in at the same time.
 #define SB_USER_MAX_SIGNED_IN 1
 
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
 // --- Platform Specific Audits ----------------------------------------------
 
 #if !defined(__GNUC__)
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index 6ea6951..557c511 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -49,7 +49,8 @@
 // Let's say there's an image decoder for .foo files:
 //
 //     bool SbImageDecodeFooSupportsFormat(SbDecodeTargetFormat format);
-//     bool SbImageDecodeFoo(void *data, int data_size, SbDecodeTarget target);
+//     SbDecodeTarget SbImageDecodeFoo(void* data, int data_size,
+//                                     SbDecodeTargetFormat format);
 //
 // First, the client should enumerate which SbDecodeTargetFormats are supported
 // by that decoder.
@@ -68,25 +69,13 @@
 //       }
 //     }
 //
-// Now that the client has a format, it can create a decode target that it will
-// use to decode the .foo file into. Let's assume format is
-// kSbDecodeTargetFormat1PlaneRGBA, and that we are on an EGL/GLES2 platform.
+// Now that the client has a format, it can create a decode target that it
+// will use to decode the .foo file into. Let's assume format is
+// kSbDecodeTargetFormat1PlaneRGBA, that we are on an EGL/GLES2 platform.
 // Also, we won't do any error checking, to keep things even simpler.
 //
-//     // Allocate a sized texture of the right format.
-//     GLuint texture_handle;
-//     glGenTextures(1, &texture_handle);
-//     glBindTexture(GL_TEXTURE_2D, texture_handle);
-//     glTexImage2D(GL_TEXTURE_2D, 0 /*level*/, GL_RGBA, width, height,
-//                  0 /*border*/, GL_RGBA8, GL_UNSIGNED_BYTE, NULL /*data*/);
-//     glBindTexture(GL_TEXTURE_2D, 0);
-//
-//     // Create an SbDecodeTarget wrapping the new texture handle.
-//     SbDecodeTarget target =
-//         SbDecodeTargetCreate(display, context, format, &texture_handle);
-//
-//     // Now pass the SbDecodeTarget into the decoder.
-//     SbImageDecodeFoo(encoded_foo_data, encoded_foo_data_size, target);
+//     SbDecodeTarget target = SbImageDecodeFoo(encoded_foo_data,
+//                                              encoded_foo_data_size, format);
 //
 //     // If the decode works, you can get the texture out and render it.
 //     GLuint texture =
@@ -100,14 +89,14 @@
 #include "starboard/export.h"
 #include "starboard/types.h"
 
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_VERSION(3)
 
 #if SB_HAS(BLITTER)
 #include "starboard/blitter.h"
-#else
+#elif SB_HAS(GLES2)  // SB_HAS(BLITTER)
 #include <EGL/egl.h>
-#include <GL/gl.h>
-#endif
+#include <GLES2/gl2.h>
+#endif  // SB_HAS(BLITTER)
 
 #ifdef __cplusplus
 extern "C" {
@@ -189,6 +178,9 @@
 
   // The function to release an acquired SbDecodeTarget back to the provider.
   SbDecodeTargetReleaseFunction release;
+
+  // |context| will be passed into every call to |acquire| and |release|.
+  void* context;
 } SbDecodeTargetProvider;
 
 // --- Constants -------------------------------------------------------------
@@ -203,6 +195,12 @@
   return handle != kSbDecodeTargetInvalid;
 }
 
+// Returns whether a given format is valid.
+static SB_C_INLINE bool SbDecodeTargetIsFormatValid(
+    SbDecodeTargetFormat format) {
+  return format != kSbDecodeTargetFormatInvalid;
+}
+
 #if SB_HAS(BLITTER)
 // Creates a new SbBlitter-compatible SbDecodeTarget from one or more |planes|
 // created from |display|.
@@ -218,7 +216,7 @@
 // Gets the surface that represents the given plane.
 SB_EXPORT SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
                                                   SbDecodeTargetPlane plane);
-#else   // SB_HAS(BLITTER)
+#elif SB_HAS(GLES2)  // SB_HAS(BLITTER)
 // Creates a new EGL/GLES2-compatible SbDecodeTarget from one or more |planes|
 // owned by |context|, created from |display|. Must be called from a thread
 // where |context| is current.
@@ -239,48 +237,71 @@
 // Gets the texture that represents the given plane.
 SB_EXPORT GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
                                         SbDecodeTargetPlane plane);
+
+#else  // SB_HAS(BLITTER)
+
+// Stub function for when graphics aren't enabled.  Always creates
+// kSbDecodeTargetInvalid.
+static SB_C_INLINE SbDecodeTarget
+SbDecodeTargetCreate(SbDecodeTargetFormat format) {
+  SB_UNREFERENCED_PARAMETER(format);
+  return kSbDecodeTargetInvalid;
+}
+
+// Stub function for when graphics aren't enabled.  There is no concept of a
+// plane, and |NULL| is always returned.
+static SB_C_INLINE void* SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
+                                                SbDecodeTargetPlane plane) {
+  SB_UNREFERENCED_PARAMETER(decode_target);
+  SB_UNREFERENCED_PARAMETER(plane);
+  return NULL;
+}
+
 #endif  // SB_HAS(BLITTER)
 
-// Destroys the given SbDecodeTarget and all associated surfaces.
+// Destroys the given SbDecodeTarget. Note that calling this function does NOT
+// destroy the associated surfaces with it, in order to accommodate the case
+// in which it is desirable for the lifetime of the surface to outlive the
+// SbDecodeTarget that contains it. If the surfaces should be destroyed along
+// with the SbDecodeTarget, then they should be extracted with
+// |SbDecodeTargetGetPlane| and destroyed manually by the client.
 SB_EXPORT void SbDecodeTargetDestroy(SbDecodeTarget decode_target);
 
 // Gets the format that |decode_target| was created with.
 SB_EXPORT SbDecodeTargetFormat
 SbDecodeTargetGetFormat(SbDecodeTarget decode_target);
 
-// Registers |provider| as the SbDecodeTargetProvider with the given |context|,
-// displacing any previous registered provider. The provider is expected to be
-// kept alive by the caller until unregistered, so this function is NOT passing
-// ownership in any way. |context| will be passed into every call to
-// SbDecodeTargetProvider::acquire or SbDecodeTargetProvider::release. May be
-// called from any thread.
-SB_EXPORT bool SbDecodeTargetRegisterProvider(SbDecodeTargetProvider* provider,
-                                              void* context);
+// Gets whether |decode_target| is opaque or not.  The underlying source of
+// this value is expected to be properly maintained by the Starboard
+// implementation.  So, for example, if an opaque only image type were decoded
+// into an SbDecodeTarget, then the implementation would configure things in
+// such a way that this function would return true.  By opaque, it is meant
+// that all alpha values are guaranteed to be 255, if |decode_target| is of a
+// format that has alpha values.  If |decode_target| is of a format that does
+// not have alpha values, then this function should return |true|.
+SB_EXPORT bool SbDecodeTargetIsOpaque(SbDecodeTarget decode_target);
 
-// Unregisters |provider| as the SbDecodeTargetProvider, with |context|, if it
-// is the current registered provider-context. This is checked by comparing the
-// pointer values of both |provider| and |context| to the currently registered
-// provider. May be called from any thread.
-SB_EXPORT void SbDecodeTargetUnregisterProvider(
-    SbDecodeTargetProvider* provider,
-    void* context);
-
-// Acquires a decode target from the registered SbDecodeTargetProvider,
-// returning NULL if none is registered. May be called from any thread.
-SB_EXPORT SbDecodeTarget
-SbDecodeTargetAcquireFromProvider(SbDecodeTargetFormat format,
+// Inline convenience function to acquire an SbDecodeTarget of type |format|,
+// |width|, and |height| from |provider|.
+static SB_C_INLINE SbDecodeTarget
+SbDecodeTargetAcquireFromProvider(SbDecodeTargetProvider* provider,
+                                  SbDecodeTargetFormat format,
                                   int width,
-                                  int height);
+                                  int height) {
+  return provider->acquire(provider->context, format, width, height);
+}
 
-// Releases |decode_target| back to the registered SbDecodeTargetProvider. If no
-// provider is registered, just calls SbDecodeTargetDestroy on |decode_target|.
-// May be called from any thread.
-SB_EXPORT void SbDecodeTargetReleaseToProvider(SbDecodeTarget decode_target);
+// Inline convenience function to release |decode_target| back to |provider|.
+static SB_C_INLINE void SbDecodeTargetReleaseToProvider(
+    SbDecodeTargetProvider* provider,
+    SbDecodeTarget decode_target) {
+  provider->release(provider->context, decode_target);
+}
 
 #ifdef __cplusplus
 }  // extern "C"
 #endif
 
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // SB_VERSION(3)
 
 #endif  // STARBOARD_DECODE_TARGET_H_
diff --git a/src/starboard/directory.h b/src/starboard/directory.h
index 728b875..0147306 100644
--- a/src/starboard/directory.h
+++ b/src/starboard/directory.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Directory listing API.
+// Module Overview: Starboard Directory module
+//
+// Provides directory listing functions.
 
 #ifndef STARBOARD_DIRECTORY_H_
 #define STARBOARD_DIRECTORY_H_
@@ -46,26 +48,43 @@
   return directory != kSbDirectoryInvalid;
 }
 
-// Opens the given existing directory for listing. Will return
-// kSbDirectoryInvalidHandle if not successful. If |out_error| is provided by
-// the caller, it will be set to the appropriate SbFileError code on failure.
+// Opens the given existing directory for listing. This function returns
+// kSbDirectoryInvalidHandle if it is not successful.
+//
+// If |out_error| is provided by the caller, it will be set to the appropriate
+// SbFileError code on failure.
+//
+// |out_error|: An output parameter that, in case of an error, is set to the
+// reason that the directory could not be opened.
 SB_EXPORT SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error);
 
-// Closes an open directory stream handle. Returns whether the close was
-// successful.
+// Closes an open directory stream handle. The return value indicates whether
+// the directory was closed successfully.
+//
+// |directory|: The directory stream handle to close.
 SB_EXPORT bool SbDirectoryClose(SbDirectory directory);
 
-// Populates |out_entry| with the next entry in that directory stream, and moves
-// the stream forward by one entry. Returns |true| if there was a next
-// directory, and |false| at the end of the directory stream.
+// Populates |out_entry| with the next entry in the specified directory stream,
+// and moves the stream forward by one entry.
+//
+// This function returns |true| if there was a next directory, and |false|
+// at the end of the directory stream.
+//
+// |directory|: The directory stream from which to retrieve the next directory.
+// |out_entry|: The variable to be populated with the next directory entry.
 SB_EXPORT bool SbDirectoryGetNext(SbDirectory directory,
                                   SbDirectoryEntry* out_entry);
 
-// Returns whether SbDirectoryOpen is allowed for the given |path|.
+// Indicates whether SbDirectoryOpen is allowed for the given |path|.
+//
+// |path|: The path to be checked.
 SB_EXPORT bool SbDirectoryCanOpen(const char* path);
 
 // Creates the directory |path|, assuming the parent directory already exists.
-// Returns whether the directory now exists (even if it existed before).
+// This function returns |true| if the directory now exists (even if it existed
+// before) and returns |false| if the directory does not exist.
+//
+// |path|: The path to be created.
 SB_EXPORT bool SbDirectoryCreate(const char* path);
 
 #ifdef __cplusplus
diff --git a/src/starboard/double.h b/src/starboard/double.h
index bde6acc..8a28bbc 100644
--- a/src/starboard/double.h
+++ b/src/starboard/double.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Double-precision floating point helper functions.
+// Module Overview: Starboard Double module
+//
+// Provides double-precision floating point helper functions.
 
 #ifndef STARBOARD_DOUBLE_H_
 #define STARBOARD_DOUBLE_H_
@@ -25,21 +27,32 @@
 #endif
 
 // Floors double-precision floating-point number |d| to the nearest integer.
+//
+// |d|: The number to be floored.
 SB_EXPORT double SbDoubleFloor(const double d);
 
 // Returns the absolute value of the given double-precision floating-point
-// number |d|, preserving NaN and Infinity.
+// number |d|, preserving |NaN| and infinity.
+//
+// |d|: The number to be adjusted.
 SB_EXPORT double SbDoubleAbsolute(const double d);
 
 // Returns |base| taken to the power of |exponent|.
+//
+// |base|: The number to be adjusted.
+// |exponent|: The power to which the |base| number should be raised.
 SB_EXPORT double SbDoubleExponent(const double base, const double exponent);
 
 // Determines whether double-precision floating-point number |d| represents a
-// fininte number.
+// finite number.
+//
+// |d|: The number to be evaluated.
 SB_EXPORT bool SbDoubleIsFinite(const double d);
 
-// Determines whether double-precision floating-point number |d| represents "Not
-// a Number."
+// Determines whether double-precision floating-point number |d| represents
+// "Not a Number."
+//
+// |d|: The number to be evaluated.
 SB_EXPORT bool SbDoubleIsNan(const double d);
 
 #ifdef __cplusplus
diff --git a/src/starboard/event.h b/src/starboard/event.h
index cec1884..44cdd79 100644
--- a/src/starboard/event.h
+++ b/src/starboard/event.h
@@ -12,7 +12,47 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// The event system that wraps the Starboard main loop and entry point.
+// Module Overview: Starboard Event module
+//
+// Defines the event system that wraps the Starboard main loop and entry point.
+//
+// ## The Starboard Application life cycle
+//
+// |          *
+// |          |                      _________________________
+// |        Start                   |                         |
+// |          |                     |                       Resume
+// |          V                     V                         |
+// |     [ STARTED ] --Pause--> [ PAUSED ] --Suspend--> [ SUSPENDED ]
+// |          ^                     |                         |
+// |          |                  Unpause                     Stop
+// |          |_____________________|                         |
+// |                                                          V
+// |                                                     [ STOPPED ]
+//
+// The first event that a Starboard application receives is Start
+// (kSbEventTypeStart). This puts the application in the |STARTED| state.
+// The application is in the foreground and can expect to do all of the normal
+// things it might want to do. Once in the |STARTED| state, it may receive a
+// |Pause| event, putting the application into the |PAUSED| state.
+//
+// In the |PAUSED| state, the application is still visible, but has lost
+// focus, or it is partially obscured by a modal dialog, or it is on its way
+// to being shut down. The application should pause activity in this state.
+// In this state, it can receive |Unpause| to be brought back to the foreground
+// state (|STARTED|), or |Suspend| to be pushed further in the background
+// to the |SUSPENDED| state.
+//
+// In the |SUSPENDED| state, the application is generally not visible. It
+// should immediately release all graphics and video resources, and shut down
+// all background activity (timers, rendering, etc). Additionally, the
+// application should flush storage to ensure that if the application is
+// killed, the storage will be up-to-date. The application may be killed at
+// this point, but will ideally receive a |Stop| event for a more graceful
+// shutdown.
+//
+// Note that the application is always expected to transition through |PAUSED|
+// to |SUSPENDED| before receiving |Stop| or being killed.
 
 #ifndef STARBOARD_EVENT_H_
 #define STARBOARD_EVENT_H_
@@ -26,43 +66,6 @@
 extern "C" {
 #endif
 
-// The Starboard Application life cycle
-// ------------------------------------
-//
-//           *
-//           |                      _________________________
-//         Start                   |                         |
-//           |                     |                       Resume
-//           V                     V                         |
-//      [ STARTED ] --Pause--> [ PAUSED ] --Suspend--> [ SUSPENDED ]
-//           ^                     |                         |
-//           |                  Unpause                     Stop
-//           |_____________________|                         |
-//                                                           V
-//                                                      [ STOPPED ]
-//
-// The first event that a Starboard application will receive is Start
-// (kSbEventTypeStart). This puts the application in the STARTED state; it is in
-// the foreground and can expect to do all the normal things it might want to
-// do. Once in the STARTED state, it may receive a Pause event, putting the
-// application into the PAUSED state.
-//
-// In the PAUSED state, the application is still visible, but has lost focus, or
-// it is partially obscured by a modal dialog, or it is on its way to being shut
-// down. The application should pause activity in this state. In this state, it
-// can receive Unpause to be brought back to the foreground state, STARTED, or
-// Suspend to be pushed further in the background to the SUSPENDED state.
-//
-// In the SUSPENDED state, the application is generally not visible. It should
-// immediately release all graphics and video resources, and shut down all
-// background activity (timers, rendering, etc). Additionally, the application
-// should flush storage to ensure that if the application is killed, the storage
-// will be up-to-date. The application may be killed at this point, but will
-// ideally receive a Stop event for a more graceful shutdown.
-//
-// Note that the application is always expected to transition through PAUSED to
-// SUSPENDED before receiving Stop or being killed.
-
 // An enumeration of all possible event types dispatched directly by the
 // system. Each event is accompanied by a void* data argument, and each event
 // must define the type of the value pointed to by that data argument, if any.
@@ -185,31 +188,36 @@
   return handle != kSbEventIdInvalid;
 }
 
-// Declaration of the entry point that Starboard applications MUST implement.
-// Any memory pointed at by |event| or the |data| field inside |event| is owned
-// by the system, and will be reclaimed after this function returns, so the
-// implementation must copy this data to extend its life.  This should also be
-// assumed of all fields within the data object, unless otherwise explicitly
-// specified. This function will only be called from the main Starboard thread.
-// There is no specification about what other work might happen on this thread,
-// so the application should generally do as little work as possible on this
-// thread, and just dispatch it over to another thread.
+// The entry point that Starboard applications MUST implement. Any memory
+// pointed at by |event| or the |data| field inside |event| is owned by the
+// system, and that memory is reclaimed after this function returns, so the
+// implementation must copy this data to extend its life. This behavior should
+// also be assumed of all fields within the |data| object, unless otherwise
+// explicitly specified.
+//
+// This function is only called from the main Starboard thread. There is no
+// specification about what other work might happen on this thread, so the
+// application should generally do as little work as possible on this thread,
+// and just dispatch it over to another thread.
 SB_IMPORT void SbEventHandle(const SbEvent* event);
 
-// Schedules an event |callback| into the main Starboard event loop, with
-// accompanying |context|. This function may be called from any thread, but
-// |callback| will always be called from the main Starboard thread, queued with
-// other pending events. |callback| will not be called any earlier than |delay|
-// microseconds from the time SbEventInject is called. Set |delay| to 0 to call
-// the event as soon as possible.
+// Schedules an event |callback| into the main Starboard event loop.
+// This function may be called from any thread, but |callback| is always
+// called from the main Starboard thread, queued with other pending events.
+//
+// |callback|: The callback function to be called.
+// |context|: The context that is passed to the |callback| function.
+// |delay|: The minimum number of microseconds to wait before calling the
+// |callback| function. Set |delay| to |0| to call the callback as soon as
+// possible.
 SB_EXPORT SbEventId SbEventSchedule(SbEventCallback callback,
                                     void* context,
                                     SbTime delay);
 
-// Cancels the injected |event_id|. Does nothing if the event already fired. Can
-// be safely called from any thread, but there is no guarantee that the event
-// will not run anyway unless it is called from the main Starboard event loop
-// thread.
+// Cancels the specified |event_id|. Note that this function is a no-op
+// if the event already fired. This function can be safely called from any
+// thread, but the only way to guarantee that the event does not run anyway
+// is to call it from the main Starboard event loop thread.
 SB_EXPORT void SbEventCancel(SbEventId event_id);
 
 #ifdef __cplusplus
diff --git a/src/starboard/export.h b/src/starboard/export.h
index af2303f..b19c1c7 100644
--- a/src/starboard/export.h
+++ b/src/starboard/export.h
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Macros to properly export or import symbols from shared libraries.
+// Module Overview: Starboard Export module
+//
+// Provides macros for properly exporting or importing symbols from shared
+// libraries.
 
 #ifndef STARBOARD_EXPORT_H_
 #define STARBOARD_EXPORT_H_
diff --git a/src/starboard/file.h b/src/starboard/file.h
index 3f15daf..0e92907 100644
--- a/src/starboard/file.h
+++ b/src/starboard/file.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// File system Input/Output
+// Module Overview: Starboard File module
+//
+// Defines file system input/output functions.
 
 #ifndef STARBOARD_FILE_H_
 #define STARBOARD_FILE_H_
@@ -32,17 +34,33 @@
 // A handle to an open file.
 typedef SbFilePrivate* SbFile;
 
-// kSbFile(Open|Create)(Only|Always|Truncated) are mutually exclusive. You
-// should specify exactly one of the five (possibly combining with other flags)
-// when opening or creating a file.
+// Flags that define how a file is used in the application. These flags should
+// be or'd together when passed to SbFileOpen to open or create a file.
+//
+// The following five flags are mutually exclusive. You must specify exactly one
+// of them:
+// - |kSbFileOpenAlways|
+// - |kSbFileOpenOnly|
+// - |kSbFileOpenTruncated|
+// - |kSbFileCreateAlways|
+// - |kSbFileCreateOnly|
+//
+// In addition, one or more of the following flags must be specified:
+// - |kSbFileRead|
+// - |kSbFileWrite|
+//
+// The |kSbFileAsync| flag is optional.
 typedef enum SbFileFlags {
   kSbFileOpenOnly = 1 << 0,       // Opens a file, only if it exists.
   kSbFileCreateOnly = 1 << 1,     // Creates a new file, only if it
                                   //   does not already exist.
-  kSbFileOpenAlways = 1 << 2,     // May create a new file.
-  kSbFileCreateAlways = 1 << 3,   // May overwrite an old file.
-  kSbFileOpenTruncated = 1 << 4,  // Opens a file and truncates it
-                                  //   to zero, only if it exists.
+  kSbFileOpenAlways = 1 << 2,     // Opens an existing file at the specified
+                                  // path or creates a new file at that path.
+  kSbFileCreateAlways = 1 << 3,   // Creates a new file at the specified path
+                                  // or overwrites an existing file at that
+                                  // path.
+  kSbFileOpenTruncated = 1 << 4,  // Opens a file and truncates it to zero,
+                                  // only if it exists.
   kSbFileRead = 1 << 5,
   kSbFileWrite = 1 << 6,
   kSbFileAsync = 1 << 7,  // May allow asynchronous I/O on some
@@ -112,81 +130,140 @@
 }
 
 // Opens the file at |path|, which must be absolute, creating it if specified by
-// |flags|. |out_created|, if provided, will be set to true if a new file was
-// created (or an old one truncated to zero length to simulate a new file, which
-// can happen with kSbFileCreateAlways), and false otherwise.  |out_error| can
-// be NULL. If creation failed, it will return kSbFileInvalid. The read/write
-// position is at the beginning of the file.
+// |flags|. The read/write position is at the beginning of the file.
 //
-// It only guarantees the correct behavior when |path| points to a file. If
-// |path| points to directory, the behavior is undefined.
+// Note that this function only guarantees the correct behavior when |path|
+// points to a file. The behavior is undefined if |path| points to a directory.
+//
+// |path|: The absolute path of the file to be opened.
+// |flags|: |SbFileFlags| that determine how the file is used in the
+//   application. Among other things, |flags| can indicate whether the
+//   application should create |path| if |path| does not already exist.
+// |out_created|: Starboard sets this value to |true| if a new file is created
+//   or if an old file is truncated to zero length to simulate a new file,
+//   which can happen if the |kSbFileCreateAlways| flag is set. Otherwise,
+//   Starboard sets this value to |false|.
+// |out_error|: If |path| cannot be created, this is set to |kSbFileInvalid|.
+//   Otherwise, it is |NULL|.
 SB_EXPORT SbFile SbFileOpen(const char* path,
                             int flags,
                             bool* out_created,
                             SbFileError* out_error);
 
-// Closes |file|. Returns whether the close was successful.
+// Closes |file|. The return value indicates whether the file was closed
+// successfully.
+//
+// |file|: The absolute path of the file to be closed.
 SB_EXPORT bool SbFileClose(SbFile file);
 
-// Changes current read/write position in |file| to |offset| relative to the
-// origin defined by |whence|. Returns the resultant current read/write position
-// in the file (relative to the start) or -1 in case of error. May not support
+// Changes the current read/write position in |file|. The return value
+// identifies the resultant current read/write position in the file (relative
+// to the start) or |-1| in case of an error. This function might not support
 // seeking past the end of the file on some platforms.
+//
+// |file|: The SbFile in which the read/write position will be changed.
+// |whence|: The starting read/write position. The position is modified relative
+//   to this value.
+// |offset|: The amount that the read/write position is changed, relative to
+//   |whence|.
 SB_EXPORT int64_t SbFileSeek(SbFile file, SbFileWhence whence, int64_t offset);
 
 // Reads |size| bytes (or until EOF is reached) from |file| into |data|,
-// starting at the file's current position. Returns the number of bytes read, or
-// -1 on error. Note that this function does NOT make a best effort to read all
-// data on all platforms, it just reads however many bytes are quickly
-// available. Can be run in a loop to make it a best-effort read.
+// starting at the file's current position.
+//
+// The return value specifies the number of bytes read or |-1| if there was
+// an error. Note that this function does NOT make a best effort to read all
+// data on all platforms; rather, it just reads however many bytes are quickly
+// available. However, this function can be run in a loop to make it a
+// best-effort read.
+//
+// |file|: The SbFile from which to read data.
+// |data|: The variable to which data is read.
+// |size|: The amount of data (in bytes) to read.
 SB_EXPORT int SbFileRead(SbFile file, char* data, int size);
 
 // Writes the given buffer into |file| at the file's current position,
-// overwritting any data that was previously there. Returns the number of bytes
-// written, or -1 on error. Note that this function does NOT make a best effort
-// to write all data, it writes however many bytes can be written quickly. It
-// should be run in a loop to ensure all data is written.
+// overwriting any data that was previously there.
+//
+// The return value identifies the number of bytes written, or |-1| on error.
+// Note that this function does NOT make a best effort to write all data;
+// rather, it writes however many bytes can be written quickly. Generally, this
+// means that it writes however many bytes as possible without blocking on IO.
+// Run this function in a loop to ensure that all data is written.
+//
+// |file|: The SbFile to which data will be written.
+// |data|: The data to be written.
+// |size|: The amount of data (in bytes) to write.
 SB_EXPORT int SbFileWrite(SbFile file, const char* data, int size);
 
-// Truncates the given |file| to the given |length|. If length is greater than
-// the current size of the file, the file is extended with zeros. Negative
-// |length| will do nothing and return false.
+// Truncates the given |file| to the given |length|. The return value indicates
+// whether the file was truncated successfully.
+//
+// |file|: The file to be truncated.
+// |length|: The expected length of the file after it is truncated. If |length|
+//   is greater than the current size of the file, then the file is extended
+//   with zeros. If |length| is negative, then the function is a no-op and
+//   returns |false|.
 SB_EXPORT bool SbFileTruncate(SbFile file, int64_t length);
 
-// Flushes the write buffer to |file|. Data written via SbFileWrite isn't
-// necessarily comitted right away until the file is flushed or closed.
+// Flushes the write buffer to |file|. Data written via SbFileWrite is not
+// necessarily committed until the SbFile is flushed or closed.
+//
+// |file|: The SbFile to which the write buffer is flushed.
 SB_EXPORT bool SbFileFlush(SbFile file);
 
-// Places some information about |file|, an open SbFile, in |out_info|. Returns
-// |true| if successful. If unsuccessful, |out_info| is untouched.
+// Retrieves information about |file|. The return value indicates whether the
+// file information was retrieved successfully.
+//
+// |file|: The SbFile for which information is retrieved.
+// |out_info|: The variable into which the retrieved data is placed. This
+//   variable is not touched if the operation is not successful.
 SB_EXPORT bool SbFileGetInfo(SbFile file, SbFileInfo* out_info);
 
-// Places information about the file or directory at |path|, which must be
-// absolute, into |out_info|. Returns |true| if successful. If unsuccessful,
-// |out_info| is untouched.
+// Retrieves information about the file at |path|. The return value indicates
+// whether the file information was retrieved successfully.
+//
+// |file|: The absolute path of the file for which information is retrieved.
+// |out_info|: The variable into which the retrieved data is placed. This
+//   variable is not touched if the operation is not successful.
 SB_EXPORT bool SbFileGetPathInfo(const char* path, SbFileInfo* out_info);
 
-// Deletes the regular file, symlink, or empty directory at |path|, which must
-// be absolute. Used mainly to clean up after unit tests. May fail on some
-// platforms if the file in question is being held open.
+// Deletes the regular file, symlink, or empty directory at |path|. This
+// function is used primarily to clean up after unit tests. On some platforms,
+// this function fails if the file in question is being held open.
+//
+// |path|: The absolute path fo the file, symlink, or directory to be deleted.
 SB_EXPORT bool SbFileDelete(const char* path);
 
-// Returns whether a file or directory exists at |path|, which must be absolute.
+// Indicates whether a file or directory exists at |path|.
+//
+// |path|: The absolute path of the file or directory being checked.
 SB_EXPORT bool SbFileExists(const char* path);
 
-// Returns whether SbFileOpen() with the given |flags| is allowed for |path|,
-// which must be absolute.
+// Indicates whether SbFileOpen() with the given |flags| is allowed for |path|.
+//
+// |path|: The absolute path to be checked.
+// |flags|: The flags that are being evaluated for the given |path|.
 SB_EXPORT bool SbFileCanOpen(const char* path, int flags);
 
-// Converts an ISO fopen() mode string into flags that can be equivalently
+// Converts an ISO |fopen()| mode string into flags that can be equivalently
 // passed into SbFileOpen().
+//
+// |mode|: The mode string to be converted into flags.
 SB_EXPORT int SbFileModeStringToFlags(const char* mode);
 
-// Reads the given number of bytes (or until EOF is reached). Returns the number
-// of bytes read, or -1 on error. Note that this function makes a best effort to
-// read all data on all platforms, so it is not intended for stream oriented
-// files but instead for cases when the normal expectation is that actually
-// |size| bytes are read unless there is an error.
+// Reads |size| bytes (or until EOF is reached) from |file| into |data|,
+// starting from the beginning of the file.
+//
+// The return value specifies the number of bytes read or |-1| if there was
+// an error. Note that, unlike |SbFileRead|, this function does make a best
+// effort to read all data on all platforms. As such, it is not intended for
+// stream-oriented files but instead for cases when the normal expectation is
+// that |size| bytes can be read unless there is an error.
+//
+// |file|: The SbFile from which to read data.
+// |data|: The variable to which data is read.
+// |size|: The amount of data (in bytes) to read.
 static inline int SbFileReadAll(SbFile file, char* data, int size) {
   if (!SbFileIsValid(file) || size < 0) {
     return -1;
@@ -204,9 +281,15 @@
   return bytes_read ? bytes_read : rv;
 }
 
-// Writes the given buffer into the file, overwritting any data that was
-// previously there. Returns the number of bytes written, or -1 on error. Note
-// that this function makes a best effort to write all data on all platforms.
+// Writes the given buffer into |file|, starting at the beginning of the file,
+// and overwriting any data that was previously there. Unlike SbFileWrite, this
+// function does make a best effort to write all data on all platforms.
+//
+// The return value identifies the number of bytes written, or |-1| on error.
+//
+// |file|: The file to which data will be written.
+// |data|: The data to be written.
+// |size|: The amount of data (in bytes) to write.
 static inline int SbFileWriteAll(SbFile file, const char* data, int size) {
   if (!SbFileIsValid(file) || size < 0) {
     return -1;
@@ -287,7 +370,7 @@
 
   int64_t GetSize() const {
     SbFileInfo file_info;
-    static bool success = GetInfo(&file_info);
+    bool success = GetInfo(&file_info);
     return (success ? file_info.size : -1);
   }
 
diff --git a/src/starboard/image.h b/src/starboard/image.h
new file mode 100644
index 0000000..5ff6e3f
--- /dev/null
+++ b/src/starboard/image.h
@@ -0,0 +1,102 @@
+// 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.
+
+// Module Overview: Starboard Image Decoding Module
+//
+// API for hardware accelerated image decoding. This module allows for the
+// client to feed in raw, encoded data to be decoded directly into an
+// SbDecodeTarget.  It also provides an interface for the client to query what
+// combinations of encoded image formats and SbDecodeTargetFormats are
+// supported or not.
+//
+// All functions in this module are safe to call from any thread at any point
+// in time.
+//
+// #### SbImageIsDecodeSupported and SbImageDecode Example
+//
+// Let's assume that we're on a Blitter platform.
+//
+//     SbDecodeTargetProvider* provider = GetProviderFromSomewhere();
+//     void* data = GetCompressedJPEGFromSomewhere();
+//     int data_size = GetCompressedJPEGSizeFromSomewhere();
+//     const char* mime_type = "image/jpeg";
+//     SbDecodeTargetFormat format = kSbDecodeTargetFormat1PlaneRGBA;
+//
+//     if (!SbImageIsDecodeSupported(mime_type, format)) {
+//       return;
+//     }
+//
+//     SbDecodeTarget result_target = SbDecodeImage(provider, data, data_size,
+//                                                  mime_type, format);
+//     SbBlitterSurface surface =
+//         SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA);
+//     // Do stuff with surface...
+//
+
+#ifndef STARBOARD_IMAGE_H_
+#define STARBOARD_IMAGE_H_
+
+#include "starboard/configuration.h"
+#include "starboard/decode_target.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#if SB_VERSION(3)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Whether the current platform supports hardware accelerated decoding an
+// image of mime type |mime_type| into SbDecodeTargetFormat |format|.  The
+// result of this function must not change over the course of the program,
+// which means that the results of this function may be cached indefinitely.
+SB_EXPORT bool SbImageIsDecodeSupported(const char* mime_type,
+                                        SbDecodeTargetFormat format);
+
+// Attempt to decode encoded |mime_type| image data |data| of size |data_size|
+// into an SbDecodeTarget of SbDecodeFormatType |format|, possibly using
+// SbDecodeTargetProvider |provider|, if it is non-null.  Thus, four following
+// scenarios regarding the provider may happen:
+//
+//   1. The provider is required by the |SbImageDecode| implementation and no
+//      provider is given.  The implementation should gracefully fail by
+//      immediately returning kSbDecodeTargetInvalid.
+//   2. The provider is required and is passed in.  The implementation will
+//      proceed forward, using the SbDecodeTarget from the provider.
+//   3. The provider is not required and is passed in.  The provider will NOT be
+//      called, and the implementation will proceed to decoding however it
+//      desires.
+//   4. The provider is not required and is not passed in.  The implementation
+//      will proceed forward.
+//
+// Thus, it is NOT safe for clients of this API to assume that the |provider|
+// it passes in will be called.  Finally, if the decode succeeds, a new
+// SbDecodeTarget will be allocated. If |mime_type| image decoding for the
+// requested format is not supported or the decode fails,
+// kSbDecodeTargetInvalid will be returned, with any intermediate allocations
+// being cleaned up in the implementation.
+SB_EXPORT SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider,
+                                       void* data,
+                                       int data_size,
+                                       const char* mime_type,
+                                       SbDecodeTargetFormat format);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // SB_VERSION(3)
+
+#endif  // STARBOARD_IMAGE_H_
diff --git a/src/starboard/input.h b/src/starboard/input.h
index fbce0f2..e8f98b2 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Definition of input events and associated data types.
+// Module Overview: Starboard Input module
+//
+// Defines input events and associated data types.
 
 #ifndef STARBOARD_INPUT_H_
 #define STARBOARD_INPUT_H_
@@ -27,57 +29,58 @@
 extern "C" {
 #endif
 
-// All possible input subsystem types.
+// Identifies possible input subsystem types. The types of events that each
+// device type produces correspond to |SbInputEventType| values.
 typedef enum SbInputDeviceType {
-  // Input from some gesture-detection mechanism. Examples include Kinect,
+  // Input from a gesture-detection mechanism. Examples include Kinect,
   // Wiimotes, LG Magic Remotes, etc...
   //
-  // Produces Move, Grab, Ungrab, Press and Unpress events.
+  // Produces |Move|, |Grab|, |Ungrab|, |Press| and |Unpress| events.
   kSbInputDeviceTypeGesture,
 
   // Input from a gamepad, following the layout provided in the W3C Web Gamepad
-  // API.
+  // API. [https://www.w3.org/TR/gamepad/]
   //
-  // Produces Move, Press and Unpress events.
+  // Produces |Move|, |Press| and |Unpress| events.
   kSbInputDeviceTypeGamepad,
 
-  // Keyboard input from a traditional keyboard, or game controller chatpad.
+  // Keyboard input from a traditional keyboard or game controller chatpad.
   //
-  // Produces Press and Unpress events.
+  // Produces |Press| and |Unpress| events.
   kSbInputDeviceTypeKeyboard,
 
   // Input from a microphone that would provide audio data to the caller, who
   // may then find some way to detect speech or other sounds within it. It may
   // have processed or filtered the audio in some way before it arrives.
   //
-  // Produces Audio events.
+  // Produces |Audio| events.
   kSbInputDeviceTypeMicrophone,
 
   // Input from a traditional mouse.
   //
-  // Produces Move, Press, and Unpress events.
+  // Produces |Move|, |Press|, and |Unpress| events.
   kSbInputDeviceTypeMouse,
 
-  // Input from a TV remote control-style device.
+  // Input from a TV remote-control-style device.
   //
-  // Produces Press and Unpress events.
+  // Produces |Press| and |Unpress| events.
   kSbInputDeviceTypeRemote,
 
   // Input from a speech command analyzer, which is some hardware or software
   // that, given a set of known phrases, activates when one of the registered
   // phrases is heard.
   //
-  // Produces Command events.
+  // Produces |Command| events.
   kSbInputDeviceTypeSpeechCommand,
 
   // Input from a single- or multi-touchscreen.
   //
-  // Produces Move, Press, and Unpress events.
+  // Produces |Move|, |Press|, and |Unpress| events.
   kSbInputDeviceTypeTouchScreen,
 
-  // Input from a touchpad that isn't masquerading as a mouse.
+  // Input from a touchpad that is not masquerading as a mouse.
   //
-  // Produces Move, Press, and Unpress events.
+  // Produces |Move|, |Press|, and |Unpress| events.
   kSbInputDeviceTypeTouchPad,
 } SbInputDeviceType;
 
@@ -90,28 +93,26 @@
   // like a speech recognizer.
   kSbInputEventTypeCommand,
 
-  // Grab activation. In some gesture systems a grab gesture is dfferent from an
-  // activate gesture. An Ungrab event will follow when the grab gesture is
-  // terminated.
+  // Grab activation. This event type is deprecated.
   kSbInputEventTypeGrab,
 
-  // Device Movement. In the case of Mouse, and perhaps Gesture, the movement
-  // tracks an absolute cursor position. In the case of TouchPad, only relative
-  // movements are provided.
+  // Device Movement. In the case of |Mouse|, and perhaps |Gesture|, the
+  // movement tracks an absolute cursor position. In the case of |TouchPad|,
+  // only relative movements are provided.
   kSbInputEventTypeMove,
 
-  // Key or button press activation. This could be a key on a keyboard, a button
-  // on a mouse or game controller, or a push from a touch screen or gesture. An
-  // Unpress event will be delivered once the key/button/finger is
-  // raised. Injecting repeat presses is up to the client.
+  // Key or button press activation. This could be a key on a keyboard, a
+  // button on a mouse or game controller, a push from a touch screen, or
+  // a gesture. An |Unpress| event is subsequently delivered when the
+  // |Press| event terminates, such as when the key/button/finger is raised.
+  // Injecting repeat presses is up to the client.
   kSbInputEventTypePress,
 
-  // Grab deactivation. An Ungrab event will be sent when a grab gesture is
-  // terminated.
+  // Grab deactivation. This event type is deprecated.
   kSbInputEventTypeUngrab,
 
-  // Key or button deactivation. The counterpart to the Press event, this event
-  // is sent when the key or button being pressed is released.
+  // Key or button deactivation. The counterpart to the |Press| event, this
+  // event is sent when the key or button being pressed is released.
   kSbInputEventTypeUnpress,
 } SbInputEventType;
 
@@ -121,42 +122,44 @@
   int y;
 } SbInputVector;
 
-// Event data for kSbEventTypeInput events.
+// Event data for |kSbEventTypeInput| events.
 typedef struct SbInputData {
-  // The window that this input was generated at.
+  // The window in which the input was generated.
   SbWindow window;
 
-  // The type of input event this represents.
+  // The type of input event that this represents.
   SbInputEventType type;
 
   // The type of device that generated this input event.
   SbInputDeviceType device_type;
 
-  // An identifier unique amongst all connected devices.
+  // An identifier that is unique among all connected devices.
   int device_id;
 
   // An identifier that indicates which keyboard key or mouse button was
   // involved in this event, if any. All known keys for all devices are mapped
-  // to a single ID space, defined by the enum SbKey in key.h.
+  // to a single ID space, defined by the |SbKey| enum in |key.h|.
   SbKey key;
 
   // The character that corresponds to the key. For an external keyboard, this
-  // character also depends on the language of keyboard type. Will be 0 if there
+  // character also depends on the keyboard language. The value is |0| if there
   // is no corresponding character.
   wchar_t character;
 
   // The location of the specified key, in cases where there are multiple
-  // instances of the button on the keyboard. The "shift" key, for example.
+  // instances of the button on the keyboard. For example, some keyboards
+  // have more than one "shift" key.
   SbKeyLocation key_location;
 
-  // Key modifiers (e.g. Ctrl, Chift) held down during this input event.
+  // Key modifiers (e.g. |Ctrl|, |Shift|) held down during this input event.
   unsigned int key_modifiers;
 
   // The (x, y) coordinates of the persistent cursor controlled by this
-  // device. Will be 0 if not applicable.
+  // device. The value is |0| if this data is not applicable.
   SbInputVector position;
 
-  // The relative motion vector of this input. Will be 0 if not applicable.
+  // The relative motion vector of this input. The value is |0| if this data
+  // is not applicable.
   SbInputVector delta;
 } SbInputData;
 
diff --git a/src/starboard/key.h b/src/starboard/key.h
index 05f05d9..b50608f 100644
--- a/src/starboard/key.h
+++ b/src/starboard/key.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// The canonical set of Starboard key codes.
+// Module Overview: Starboard Key module
+//
+// Defines the canonical set of Starboard key codes.
 
 #ifndef STARBOARD_KEY_H_
 #define STARBOARD_KEY_H_
@@ -21,17 +23,16 @@
 extern "C" {
 #endif
 
-// A standard set of key codes representing each possible input key across all
-// kinds of devices, we use the semi-standard Windows virtual key codes.
-//
-// Windows virtual key codes doc:
+// A standard set of key codes, ordered by value, that represent each possible
+// input key across all kinds of devices. Starboard uses the semi-standard
+// Windows virtual key codes documented at:
 //   https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
-//
-// The order here is by value.
 typedef enum SbKey {
   kSbKeyUnknown = 0,
   kSbKeyCancel = 0x03,
-  kSbKeyBack = 0x08,
+  kSbKeyBackspace = 0x08,
+  kSbKeyBack = kSbKeyBackspace,  // You probably want kSbKeyEscape for a
+                                 // semantic "back".
   kSbKeyTab = 0x09,
   kSbKeyBacktab = 0x0A,
   kSbKeyClear = 0x0C,
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index b374b00..afd4574 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -24,7 +24,7 @@
 #define STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_
 
 #ifndef SB_API_VERSION
-#define SB_API_VERSION 1
+#define SB_API_VERSION 2
 #endif
 
 // --- System Header Configuration -------------------------------------------
@@ -56,6 +56,12 @@
 // Whether the current platform provides the standard header float.h.
 #define SB_HAS_FLOAT_H 1
 
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
 // Type detection for wchar_t.
 #if defined(__WCHAR_MAX__) && \
     (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -375,6 +381,12 @@
 // The maximum number of users that can be signed in at the same time.
 #define SB_USER_MAX_SIGNED_IN 1
 
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
 // --- Platform Specific Audits ----------------------------------------------
 
 #if !defined(__GNUC__)
diff --git a/src/starboard/linux/shared/starboard_base_symbolize.gyp b/src/starboard/linux/shared/starboard_base_symbolize.gyp
new file mode 100644
index 0000000..5cd3440
--- /dev/null
+++ b/src/starboard/linux/shared/starboard_base_symbolize.gyp
@@ -0,0 +1,25 @@
+# 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.
+{
+  'targets': [
+    {
+      'target_name': 'starboard_base_symbolize',
+      'type': 'static_library',
+      'sources': [
+        '<(DEPTH)/base/third_party/symbolize/demangle.cc',
+        '<(DEPTH)/base/third_party/symbolize/symbolize.cc',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
new file mode 100644
index 0000000..1030459
--- /dev/null
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -0,0 +1,281 @@
+# 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.
+{
+  'variables': {
+    'starboard_platform_sources': [
+      '<(DEPTH)/starboard/linux/shared/atomic_public.h',
+      '<(DEPTH)/starboard/linux/shared/configuration_public.h',
+      '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
+      '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc',
+      '<(DEPTH)/starboard/linux/shared/system_get_path.cc',
+      '<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
+      '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
+      '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h',
+      '<(DEPTH)/starboard/shared/alsa/alsa_util.cc',
+      '<(DEPTH)/starboard/shared/alsa/alsa_util.h',
+      '<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc',
+      '<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc',
+      '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc',
+      '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
+      '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
+      '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
+      '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
+      '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc',
+      '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h',
+      '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc',
+      '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h',
+      '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h',
+      '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
+      '<(DEPTH)/starboard/shared/iso/character_is_digit.cc',
+      '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc',
+      '<(DEPTH)/starboard/shared/iso/character_is_space.cc',
+      '<(DEPTH)/starboard/shared/iso/character_is_upper.cc',
+      '<(DEPTH)/starboard/shared/iso/character_to_lower.cc',
+      '<(DEPTH)/starboard/shared/iso/character_to_upper.cc',
+      '<(DEPTH)/starboard/shared/iso/directory_close.cc',
+      '<(DEPTH)/starboard/shared/iso/directory_get_next.cc',
+      '<(DEPTH)/starboard/shared/iso/directory_open.cc',
+      '<(DEPTH)/starboard/shared/iso/double_absolute.cc',
+      '<(DEPTH)/starboard/shared/iso/double_exponent.cc',
+      '<(DEPTH)/starboard/shared/iso/double_floor.cc',
+      '<(DEPTH)/starboard/shared/iso/double_is_finite.cc',
+      '<(DEPTH)/starboard/shared/iso/double_is_nan.cc',
+      '<(DEPTH)/starboard/shared/iso/memory_compare.cc',
+      '<(DEPTH)/starboard/shared/iso/memory_copy.cc',
+      '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc',
+      '<(DEPTH)/starboard/shared/iso/memory_move.cc',
+      '<(DEPTH)/starboard/shared/iso/memory_set.cc',
+      '<(DEPTH)/starboard/shared/iso/string_compare.cc',
+      '<(DEPTH)/starboard/shared/iso/string_compare_all.cc',
+      '<(DEPTH)/starboard/shared/iso/string_find_character.cc',
+      '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc',
+      '<(DEPTH)/starboard/shared/iso/string_find_string.cc',
+      '<(DEPTH)/starboard/shared/iso/string_get_length.cc',
+      '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc',
+      '<(DEPTH)/starboard/shared/iso/string_parse_double.cc',
+      '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc',
+      '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc',
+      '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc',
+      '<(DEPTH)/starboard/shared/iso/string_scan.cc',
+      '<(DEPTH)/starboard/shared/iso/system_binary_search.cc',
+      '<(DEPTH)/starboard/shared/iso/system_sort.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc',
+      '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc',
+      '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
+      '<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
+      '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
+      '<(DEPTH)/starboard/shared/linux/page_internal.cc',
+      '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
+      '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
+      '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
+      '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
+      '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
+      '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
+      '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
+      '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
+      '<(DEPTH)/starboard/shared/linux/thread_get_name.cc',
+      '<(DEPTH)/starboard/shared/linux/thread_set_name.cc',
+      '<(DEPTH)/starboard/shared/nouser/user_get_current.cc',
+      '<(DEPTH)/starboard/shared/nouser/user_get_property.cc',
+      '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc',
+      '<(DEPTH)/starboard/shared/nouser/user_internal.cc',
+      '<(DEPTH)/starboard/shared/posix/directory_create.cc',
+      '<(DEPTH)/starboard/shared/posix/file_can_open.cc',
+      '<(DEPTH)/starboard/shared/posix/file_close.cc',
+      '<(DEPTH)/starboard/shared/posix/file_delete.cc',
+      '<(DEPTH)/starboard/shared/posix/file_exists.cc',
+      '<(DEPTH)/starboard/shared/posix/file_flush.cc',
+      '<(DEPTH)/starboard/shared/posix/file_get_info.cc',
+      '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc',
+      '<(DEPTH)/starboard/shared/posix/file_open.cc',
+      '<(DEPTH)/starboard/shared/posix/file_read.cc',
+      '<(DEPTH)/starboard/shared/posix/file_seek.cc',
+      '<(DEPTH)/starboard/shared/posix/file_truncate.cc',
+      '<(DEPTH)/starboard/shared/posix/file_write.cc',
+      '<(DEPTH)/starboard/shared/posix/log.cc',
+      '<(DEPTH)/starboard/shared/posix/log_flush.cc',
+      '<(DEPTH)/starboard/shared/posix/log_format.cc',
+      '<(DEPTH)/starboard/shared/posix/log_is_tty.cc',
+      '<(DEPTH)/starboard/shared/posix/log_raw.cc',
+      '<(DEPTH)/starboard/shared/posix/memory_flush.cc',
+      '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_accept.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_connect.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_create.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_destroy.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_internal.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_listen.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_resolve.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_send_to.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc',
+      '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc',
+      '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc',
+      '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc',
+      '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc',
+      '<(DEPTH)/starboard/shared/posix/string_format.cc',
+      '<(DEPTH)/starboard/shared/posix/string_format_wide.cc',
+      '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc',
+      '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc',
+      '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc',
+      '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc',
+      '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc',
+      '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc',
+      '<(DEPTH)/starboard/shared/posix/thread_sleep.cc',
+      '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc',
+      '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc',
+      '<(DEPTH)/starboard/shared/posix/time_get_now.cc',
+      '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc',
+      '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
+      '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc',
+      '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc',
+      '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc',
+      '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc',
+      '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc',
+      '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc',
+      '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc',
+      '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc',
+      '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc',
+      '<(DEPTH)/starboard/shared/pthread/mutex_create.cc',
+      '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc',
+      '<(DEPTH)/starboard/shared/pthread/mutex_release.cc',
+      '<(DEPTH)/starboard/shared/pthread/once.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_join.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_yield.cc',
+      '<(DEPTH)/starboard/shared/signal/crash_signals.h',
+      '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc',
+      '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
+      '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
+      '<(DEPTH)/starboard/shared/starboard/application.cc',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc',
+      '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h',
+      '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc',
+      '<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
+      '<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
+      '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc',
+      '<(DEPTH)/starboard/shared/starboard/log_message.cc',
+      '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
+      '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
+      '<(DEPTH)/starboard/shared/starboard/new.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
+      '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+      '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
+      '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
+      '<(DEPTH)/starboard/shared/starboard/string_copy.cc',
+      '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
+      '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+      '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+      '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
+      '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_close.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_create.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_destroy.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_get_available.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_is_sample_rate_supported.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_open.cc',
+      '<(DEPTH)/starboard/shared/stub/microphone_read.cc',
+      '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc',
+      '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
+      '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
+      '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
+      '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
+    ],
+    'starboard_platform_dependencies': [
+      '<(DEPTH)/starboard/common/common.gyp:common',
+      '<(DEPTH)/starboard/linux/shared/starboard_base_symbolize.gyp:starboard_base_symbolize',
+      '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
+      '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
+    ],
+  },
+}
diff --git a/src/starboard/linux/x64directfb/configuration_public.h b/src/starboard/linux/x64directfb/configuration_public.h
index c891232..6f91914 100644
--- a/src/starboard/linux/x64directfb/configuration_public.h
+++ b/src/starboard/linux/x64directfb/configuration_public.h
@@ -52,4 +52,8 @@
 #define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_BGRA
 #endif
 
+// The current platform has microphone supported.
+#undef SB_HAS_MICROPHONE
+#define SB_HAS_MICROPHONE 1
+
 #endif  // STARBOARD_LINUX_X64DIRECTFB_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gyp b/src/starboard/linux/x64directfb/starboard_platform.gyp
index 2419586..c45f24a 100644
--- a/src/starboard/linux/x64directfb/starboard_platform.gyp
+++ b/src/starboard/linux/x64directfb/starboard_platform.gyp
@@ -12,309 +12,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 {
+  'includes': [
+    'starboard_platform.gypi'
+  ],
   'targets': [
     {
-      'target_name': 'starboard_base_symbolize',
-      'type': 'static_library',
-      'sources': [
-        '<(DEPTH)/base/third_party/symbolize/demangle.cc',
-        '<(DEPTH)/base/third_party/symbolize/symbolize.cc',
-      ],
-    },
-    {
       'target_name': 'starboard_platform',
       'type': 'static_library',
-      'sources': [
-        '<(DEPTH)/starboard/linux/shared/atomic_public.h',
-        '<(DEPTH)/starboard/linux/shared/configuration_public.h',
-        '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
-        '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc',
-        '<(DEPTH)/starboard/linux/shared/system_get_path.cc',
-        '<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
-        '<(DEPTH)/starboard/linux/x64directfb/main.cc',
-        '<(DEPTH)/starboard/linux/x64directfb/system_get_property.cc',
-        '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
-        '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h',
-        '<(DEPTH)/starboard/shared/alsa/alsa_util.cc',
-        '<(DEPTH)/starboard/shared/alsa/alsa_util.h',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc',
-        '<(DEPTH)/starboard/shared/directfb/application_directfb.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_blit_rect_to_rect.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_create_context.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_create_default_device.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_create_pixel_data.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_create_render_target_surface.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_create_surface_from_pixel_data.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_create_swap_chain_from_window.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_context.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_device.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_pixel_data.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_surface.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_swap_chain.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_download_surface_pixels.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_fill_rect.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_flip_swap_chain.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_flush_context.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_get_max_contexts.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pitch_in_bytes.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pointer.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_surface.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_swap_chain.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_get_surface_info.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_internal.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_download_surface_pixels.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_pixel_data.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_is_surface_format_supported_by_render_target_surface.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_set_blending.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_set_color.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_set_modulate_blits_with_color.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_set_render_target.cc',
-        '<(DEPTH)/starboard/shared/directfb/blitter_set_scissor.cc',
-        '<(DEPTH)/starboard/shared/directfb/window_create.cc',
-        '<(DEPTH)/starboard/shared/directfb/window_destroy.cc',
-        '<(DEPTH)/starboard/shared/directfb/window_get_platform_handle.cc',
-        '<(DEPTH)/starboard/shared/directfb/window_get_size.cc',
-        '<(DEPTH)/starboard/shared/directfb/window_internal.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h',
-        '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h',
-        '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_digit.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_space.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_upper.cc',
-        '<(DEPTH)/starboard/shared/iso/character_to_lower.cc',
-        '<(DEPTH)/starboard/shared/iso/character_to_upper.cc',
-        '<(DEPTH)/starboard/shared/iso/directory_close.cc',
-        '<(DEPTH)/starboard/shared/iso/directory_get_next.cc',
-        '<(DEPTH)/starboard/shared/iso/directory_open.cc',
-        '<(DEPTH)/starboard/shared/iso/double_absolute.cc',
-        '<(DEPTH)/starboard/shared/iso/double_exponent.cc',
-        '<(DEPTH)/starboard/shared/iso/double_floor.cc',
-        '<(DEPTH)/starboard/shared/iso/double_is_finite.cc',
-        '<(DEPTH)/starboard/shared/iso/double_is_nan.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_compare.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_copy.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_move.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_set.cc',
-        '<(DEPTH)/starboard/shared/iso/string_compare.cc',
-        '<(DEPTH)/starboard/shared/iso/string_compare_all.cc',
-        '<(DEPTH)/starboard/shared/iso/string_find_character.cc',
-        '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc',
-        '<(DEPTH)/starboard/shared/iso/string_find_string.cc',
-        '<(DEPTH)/starboard/shared/iso/string_get_length.cc',
-        '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_double.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc',
-        '<(DEPTH)/starboard/shared/iso/string_scan.cc',
-        '<(DEPTH)/starboard/shared/iso/system_binary_search.cc',
-        '<(DEPTH)/starboard/shared/iso/system_sort.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc',
-        '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
-        '<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
-        '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
-        '<(DEPTH)/starboard/shared/linux/page_internal.cc',
-        '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
-        '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
-        '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
-        '<(DEPTH)/starboard/shared/linux/thread_get_name.cc',
-        '<(DEPTH)/starboard/shared/linux/thread_set_name.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_get_current.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_get_property.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_internal.cc',
-        '<(DEPTH)/starboard/shared/posix/directory_create.cc',
-        '<(DEPTH)/starboard/shared/posix/file_can_open.cc',
-        '<(DEPTH)/starboard/shared/posix/file_close.cc',
-        '<(DEPTH)/starboard/shared/posix/file_delete.cc',
-        '<(DEPTH)/starboard/shared/posix/file_exists.cc',
-        '<(DEPTH)/starboard/shared/posix/file_flush.cc',
-        '<(DEPTH)/starboard/shared/posix/file_get_info.cc',
-        '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc',
-        '<(DEPTH)/starboard/shared/posix/file_open.cc',
-        '<(DEPTH)/starboard/shared/posix/file_read.cc',
-        '<(DEPTH)/starboard/shared/posix/file_seek.cc',
-        '<(DEPTH)/starboard/shared/posix/file_truncate.cc',
-        '<(DEPTH)/starboard/shared/posix/file_write.cc',
-        '<(DEPTH)/starboard/shared/posix/log.cc',
-        '<(DEPTH)/starboard/shared/posix/log_flush.cc',
-        '<(DEPTH)/starboard/shared/posix/log_format.cc',
-        '<(DEPTH)/starboard/shared/posix/log_is_tty.cc',
-        '<(DEPTH)/starboard/shared/posix/log_raw.cc',
-        '<(DEPTH)/starboard/shared/posix/memory_flush.cc',
-        '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_accept.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_connect.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_create.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_destroy.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_internal.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_listen.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_resolve.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_send_to.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc',
-        '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc',
-        '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc',
-        '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc',
-        '<(DEPTH)/starboard/shared/posix/string_format.cc',
-        '<(DEPTH)/starboard/shared/posix/string_format_wide.cc',
-        '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc',
-        '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc',
-        '<(DEPTH)/starboard/shared/posix/thread_sleep.cc',
-        '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc',
-        '<(DEPTH)/starboard/shared/posix/time_get_now.cc',
-        '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc',
-        '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
-        '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_create.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_release.cc',
-        '<(DEPTH)/starboard/shared/pthread/once.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_join.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_yield.cc',
-        '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc',
-        '<(DEPTH)/starboard/shared/signal/crash_signals.h',
-        '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
-        '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
-        '<(DEPTH)/starboard/shared/starboard/application.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h',
-        '<(DEPTH)/starboard/shared/starboard/blitter_blit_rect_to_rect_tiled.cc',
-        '<(DEPTH)/starboard/shared/starboard/blitter_blit_rects_to_rects.cc',
-        '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc',
-        '<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
-        '<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_message.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
-        '<(DEPTH)/starboard/shared/starboard/new.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_copy.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-        '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
-        '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
-        '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
-        '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc',
-        '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
-        '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
-        '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
-        '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
-      ],
+      'sources': ['<@(starboard_platform_sources)'],
       'include_dirs': [
         '/usr/include/directfb',
       ],
@@ -324,10 +29,7 @@
         'STARBOARD_IMPLEMENTATION',
       ],
       'dependencies': [
-        '<(DEPTH)/starboard/common/common.gyp:common',
-        '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
-        '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
-        'starboard_base_symbolize',
+        '<@(starboard_platform_dependencies)',
       ],
     },
   ],
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gypi b/src/starboard/linux/x64directfb/starboard_platform.gypi
new file mode 100644
index 0000000..f5d40ad
--- /dev/null
+++ b/src/starboard/linux/x64directfb/starboard_platform.gypi
@@ -0,0 +1,62 @@
+# 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.
+{
+  'includes': ['../shared/starboard_platform.gypi'],
+
+  'variables': {
+    'starboard_platform_sources': [
+        '<(DEPTH)/starboard/linux/x64directfb/main.cc',
+        '<(DEPTH)/starboard/linux/x64directfb/system_get_property.cc',
+        '<(DEPTH)/starboard/shared/directfb/application_directfb.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_blit_rect_to_rect.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_create_context.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_create_default_device.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_create_pixel_data.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_create_render_target_surface.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_create_surface_from_pixel_data.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_create_swap_chain_from_window.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_context.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_device.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_pixel_data.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_surface.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_destroy_swap_chain.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_download_surface_pixels.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_fill_rect.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_flip_swap_chain.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_flush_context.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_get_max_contexts.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pitch_in_bytes.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pointer.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_surface.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_swap_chain.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_get_surface_info.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_internal.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_download_surface_pixels.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_pixel_data.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_is_surface_format_supported_by_render_target_surface.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_set_blending.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_set_color.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_set_modulate_blits_with_color.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_set_render_target.cc',
+        '<(DEPTH)/starboard/shared/directfb/blitter_set_scissor.cc',
+        '<(DEPTH)/starboard/shared/directfb/window_create.cc',
+        '<(DEPTH)/starboard/shared/directfb/window_destroy.cc',
+        '<(DEPTH)/starboard/shared/directfb/window_get_platform_handle.cc',
+        '<(DEPTH)/starboard/shared/directfb/window_get_size.cc',
+        '<(DEPTH)/starboard/shared/directfb/window_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/blitter_blit_rect_to_rect_tiled.cc',
+        '<(DEPTH)/starboard/shared/starboard/blitter_blit_rects_to_rects.cc',
+    ],
+  },
+}
diff --git a/src/starboard/linux/x64directfb/system_get_property.cc b/src/starboard/linux/x64directfb/system_get_property.cc
index b1529fe..1d20f7f 100644
--- a/src/starboard/linux/x64directfb/system_get_property.cc
+++ b/src/starboard/linux/x64directfb/system_get_property.cc
@@ -47,6 +47,7 @@
     case kSbSystemPropertyModelName:
     case kSbSystemPropertyModelYear:
     case kSbSystemPropertyNetworkOperatorName:
+    case kSbSystemPropertySpeechApiKey:
       return false;
 
     case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h
index 4aec879..8022c88 100644
--- a/src/starboard/linux/x64x11/configuration_public.h
+++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -109,9 +109,14 @@
 // textures. These textures typically originate from video decoders.
 #define SB_HAS_NV12_TEXTURE_SUPPORT 1
 
-#define SB_HAS_MICROPHONE 0
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
 
 // Include the Linux configuration that's common between all Desktop Linuxes.
 #include "starboard/linux/shared/configuration_public.h"
 
+// The current platform has microphone supported.
+#undef SB_HAS_MICROPHONE
+#define SB_HAS_MICROPHONE 1
+
 #endif  // STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.gypi b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
index 0cb12cf..0bd1f09 100644
--- a/src/starboard/linux/x64x11/future/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
@@ -14,6 +14,13 @@
 
 {
   'target_defaults': {
+    # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+    # nasty macros (|Status|, for example) that conflict with Chromium base.
+    # Since certain Cobalt headers now depend on EGL through SbDecodeTarget,
+    # we define this macro to configure EGL to not pull in these headers.
+    'defines': [
+      'MESA_EGL_NO_X11_HEADERS'
+    ],
     'default_configuration': 'linux-x64x11-future_debug',
     'configurations': {
       'linux-x64x11-future_debug': {
diff --git a/src/starboard/linux/x64x11/future/starboard_platform.gyp b/src/starboard/linux/x64x11/future/starboard_platform.gyp
index 9648c9d..31eaa25 100644
--- a/src/starboard/linux/x64x11/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/future/starboard_platform.gyp
@@ -12,16 +12,23 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 {
+  'includes': [
+    '../starboard_platform.gypi'
+  ],
   'targets': [
     {
       'target_name': 'starboard_platform',
       'product_name': 'starboard_platform_future',
       'type': 'static_library',
       'sources': [
+        '<@(starboard_platform_sources)',
         '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc',
         '<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc',
         '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
         '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_egl.cc',
+        '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
+        '<(DEPTH)/starboard/shared/stub/image_decode.cc',
+        '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
       ],
       'defines': [
         # This must be defined when building Starboard, and must not when
@@ -29,11 +36,7 @@
         'STARBOARD_IMPLEMENTATION',
       ],
       'dependencies': [
-        '<(DEPTH)/starboard/common/common.gyp:common',
-        '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp:starboard_platform',
-        '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp:starboard_base_symbolize',
-        '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
-        '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
+        '<@(starboard_platform_dependencies)',
       ],
     },
   ],
diff --git a/src/starboard/linux/x64x11/starboard_platform.gyp b/src/starboard/linux/x64x11/starboard_platform.gyp
index 228ceb0..56e5781 100644
--- a/src/starboard/linux/x64x11/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/starboard_platform.gyp
@@ -12,287 +12,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 {
+  'includes': [
+    'starboard_platform.gypi'
+  ],
   'targets': [
     {
-      'target_name': 'starboard_base_symbolize',
-      'type': 'static_library',
-      'sources': [
-        '<(DEPTH)/base/third_party/symbolize/demangle.cc',
-        '<(DEPTH)/base/third_party/symbolize/symbolize.cc',
-      ],
-    },
-    {
       'target_name': 'starboard_platform',
       'type': 'static_library',
-      'sources': [
-        '<(DEPTH)/starboard/linux/shared/atomic_public.h',
-        '<(DEPTH)/starboard/linux/shared/configuration_public.h',
-        '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
-        '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc',
-        '<(DEPTH)/starboard/linux/shared/system_get_path.cc',
-        '<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
-        '<(DEPTH)/starboard/linux/x64x11/main.cc',
-        '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
-        '<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
-        '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
-        '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h',
-        '<(DEPTH)/starboard/shared/alsa/alsa_util.cc',
-        '<(DEPTH)/starboard/shared/alsa/alsa_util.h',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc',
-        '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
-        '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc',
-        '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h',
-        '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h',
-        '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_digit.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_space.cc',
-        '<(DEPTH)/starboard/shared/iso/character_is_upper.cc',
-        '<(DEPTH)/starboard/shared/iso/character_to_lower.cc',
-        '<(DEPTH)/starboard/shared/iso/character_to_upper.cc',
-        '<(DEPTH)/starboard/shared/iso/directory_close.cc',
-        '<(DEPTH)/starboard/shared/iso/directory_get_next.cc',
-        '<(DEPTH)/starboard/shared/iso/directory_open.cc',
-        '<(DEPTH)/starboard/shared/iso/double_absolute.cc',
-        '<(DEPTH)/starboard/shared/iso/double_exponent.cc',
-        '<(DEPTH)/starboard/shared/iso/double_floor.cc',
-        '<(DEPTH)/starboard/shared/iso/double_is_finite.cc',
-        '<(DEPTH)/starboard/shared/iso/double_is_nan.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_compare.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_copy.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_move.cc',
-        '<(DEPTH)/starboard/shared/iso/memory_set.cc',
-        '<(DEPTH)/starboard/shared/iso/string_compare.cc',
-        '<(DEPTH)/starboard/shared/iso/string_compare_all.cc',
-        '<(DEPTH)/starboard/shared/iso/string_find_character.cc',
-        '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc',
-        '<(DEPTH)/starboard/shared/iso/string_find_string.cc',
-        '<(DEPTH)/starboard/shared/iso/string_get_length.cc',
-        '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_double.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc',
-        '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc',
-        '<(DEPTH)/starboard/shared/iso/string_scan.cc',
-        '<(DEPTH)/starboard/shared/iso/system_binary_search.cc',
-        '<(DEPTH)/starboard/shared/iso/system_sort.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc',
-        '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc',
-        '<(DEPTH)/starboard/shared/linux/byte_swap.cc',
-        '<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
-        '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
-        '<(DEPTH)/starboard/shared/linux/page_internal.cc',
-        '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc',
-        '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc',
-        '<(DEPTH)/starboard/shared/linux/system_symbolize.cc',
-        '<(DEPTH)/starboard/shared/linux/thread_get_id.cc',
-        '<(DEPTH)/starboard/shared/linux/thread_get_name.cc',
-        '<(DEPTH)/starboard/shared/linux/thread_set_name.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_get_current.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_get_property.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc',
-        '<(DEPTH)/starboard/shared/nouser/user_internal.cc',
-        '<(DEPTH)/starboard/shared/posix/directory_create.cc',
-        '<(DEPTH)/starboard/shared/posix/file_can_open.cc',
-        '<(DEPTH)/starboard/shared/posix/file_close.cc',
-        '<(DEPTH)/starboard/shared/posix/file_delete.cc',
-        '<(DEPTH)/starboard/shared/posix/file_exists.cc',
-        '<(DEPTH)/starboard/shared/posix/file_flush.cc',
-        '<(DEPTH)/starboard/shared/posix/file_get_info.cc',
-        '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc',
-        '<(DEPTH)/starboard/shared/posix/file_open.cc',
-        '<(DEPTH)/starboard/shared/posix/file_read.cc',
-        '<(DEPTH)/starboard/shared/posix/file_seek.cc',
-        '<(DEPTH)/starboard/shared/posix/file_truncate.cc',
-        '<(DEPTH)/starboard/shared/posix/file_write.cc',
-        '<(DEPTH)/starboard/shared/posix/log.cc',
-        '<(DEPTH)/starboard/shared/posix/log_flush.cc',
-        '<(DEPTH)/starboard/shared/posix/log_format.cc',
-        '<(DEPTH)/starboard/shared/posix/log_is_tty.cc',
-        '<(DEPTH)/starboard/shared/posix/log_raw.cc',
-        '<(DEPTH)/starboard/shared/posix/memory_flush.cc',
-        '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_accept.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_bind.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_connect.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_create.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_destroy.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_internal.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_listen.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_resolve.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_send_to.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc',
-        '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc',
-        '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc',
-        '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc',
-        '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc',
-        '<(DEPTH)/starboard/shared/posix/string_format.cc',
-        '<(DEPTH)/starboard/shared/posix/string_format_wide.cc',
-        '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc',
-        '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc',
-        '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc',
-        '<(DEPTH)/starboard/shared/posix/thread_sleep.cc',
-        '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc',
-        '<(DEPTH)/starboard/shared/posix/time_get_now.cc',
-        '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc',
-        '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
-        '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc',
-        '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_create.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc',
-        '<(DEPTH)/starboard/shared/pthread/mutex_release.cc',
-        '<(DEPTH)/starboard/shared/pthread/once.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_join.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc',
-        '<(DEPTH)/starboard/shared/pthread/thread_yield.cc',
-        '<(DEPTH)/starboard/shared/signal/crash_signals.h',
-        '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc',
-        '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
-        '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
-        '<(DEPTH)/starboard/shared/starboard/application.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc',
-        '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h',
-        '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc',
-        '<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
-        '<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_message.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
-        '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
-        '<(DEPTH)/starboard/shared/starboard/new.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
-        '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc',
-        '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h',
-        '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_copy.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
-        '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
-        '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-        '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
-        '<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
-        '<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
-        '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
-        '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc',
-        '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
-        '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
-        '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
-        '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
-        '<(DEPTH)/starboard/shared/x11/application_x11.cc',
-        '<(DEPTH)/starboard/shared/x11/window_create.cc',
-        '<(DEPTH)/starboard/shared/x11/window_destroy.cc',
-        '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
-        '<(DEPTH)/starboard/shared/x11/window_get_size.cc',
-        '<(DEPTH)/starboard/shared/x11/window_internal.cc',
-      ],
+      'sources': ['<@(starboard_platform_sources)'],
       'defines': [
         # This must be defined when building Starboard, and must not when
         # building Starboard client code.
         'STARBOARD_IMPLEMENTATION',
       ],
       'dependencies': [
-        '<(DEPTH)/starboard/common/common.gyp:common',
-        '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
-        '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
-        'starboard_base_symbolize',
+        '<@(starboard_platform_dependencies)',
       ],
     },
   ],
diff --git a/src/starboard/linux/x64x11/starboard_platform.gypi b/src/starboard/linux/x64x11/starboard_platform.gypi
new file mode 100644
index 0000000..05f1e12
--- /dev/null
+++ b/src/starboard/linux/x64x11/starboard_platform.gypi
@@ -0,0 +1,30 @@
+# 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.
+{
+  'includes': ['../shared/starboard_platform.gypi'],
+
+  'variables': {
+    'starboard_platform_sources': [
+      '<(DEPTH)/starboard/linux/x64x11/main.cc',
+      '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
+      '<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
+      '<(DEPTH)/starboard/shared/x11/application_x11.cc',
+      '<(DEPTH)/starboard/shared/x11/window_create.cc',
+      '<(DEPTH)/starboard/shared/x11/window_destroy.cc',
+      '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
+      '<(DEPTH)/starboard/shared/x11/window_get_size.cc',
+      '<(DEPTH)/starboard/shared/x11/window_internal.cc',
+    ],
+  },
+}
diff --git a/src/starboard/linux/x64x11/system_get_property.cc b/src/starboard/linux/x64x11/system_get_property.cc
index cffaa35..b72747d 100644
--- a/src/starboard/linux/x64x11/system_get_property.cc
+++ b/src/starboard/linux/x64x11/system_get_property.cc
@@ -47,6 +47,7 @@
     case kSbSystemPropertyModelName:
     case kSbSystemPropertyModelYear:
     case kSbSystemPropertyNetworkOperatorName:
+    case kSbSystemPropertySpeechApiKey:
       return false;
 
     case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/log.h b/src/starboard/log.h
index 3693925..8e86cd8 100644
--- a/src/starboard/log.h
+++ b/src/starboard/log.h
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Debug logging.
+// Module Overview: Starboard Logging module
+//
+// Defines debug logging functions.
 
 #ifndef STARBOARD_LOG_H_
 #define STARBOARD_LOG_H_
@@ -41,28 +43,38 @@
   kSbLogPriorityFatal,
 } SbLogPriority;
 
-// Writes |message| at |priority| to the debug output log for this platform.
-// Passing kSbLogPriorityFatal will not terminate the program, such policy must
-// be enforced at the application level. |priority| may, in fact, be completely
-// ignored on many platforms. No formatting is required to be done on the
-// message, not even newline termination. That said, platforms may wish to
-// adjust the message to be more suitable for their output method. This could be
-// wrapping the text, or stripping unprintable characters.
+// Writes |message| to the platform's debug output log.
+//
+// |priority|: The SbLogPriority at which the message should be logged. Note
+//   that passing |kSbLogPriorityFatal| does not terminate the program. Such a
+//   policy must be enforced at the application level. In fact, |priority| may
+//   be completely ignored on many platforms.
+// |message|: The message to be logged. No formatting is required to be done
+//   on the value, including newline termination. That said, platforms can
+//   adjust the message to be more suitable for their output method by
+//   wrapping the text, stripping unprintable characters, etc.
 SB_EXPORT void SbLog(SbLogPriority priority, const char* message);
 
 // A bare-bones log output method that is async-signal-safe, i.e. safe to call
-// from an asynchronous signal handler (e.g. a SIGSEGV handler). It should do no
-// additional formatting.
+// from an asynchronous signal handler (e.g. a |SIGSEGV| handler). It should not
+// do any additional formatting.
+//
+// |message|: The message to be logged.
 SB_EXPORT void SbLogRaw(const char* message);
 
 // Dumps the stack of the current thread to the log in an async-signal-safe
-// manner, i.e. safe to call from an asynchronous signal handler (e.g. a SIGSEGV
-// handler). Does not include SbLogRawDumpStack itself, and will additionally
-// skip |frames_to_skip| frames from the top of the stack.
+// manner, i.e. safe to call from an asynchronous signal handler (e.g. a
+// |SIGSEGV| handler). Does not include SbLogRawDumpStack itself.
+//
+// |frames_to_skip|: The number of frames to skip from the top of the stack
+//   when dumping the current thread to the log. This parameter lets you remove
+//   noise from helper functions that might end up on top of every stack dump
+//   so that the stack dump is just the relevant function stack where the
+//   problem occurred.
 SB_EXPORT void SbLogRawDumpStack(int frames_to_skip);
 
 // A formatted log output method that is async-signal-safe, i.e. safe to call
-// from an asynchronous signal handler (e.g. a SIGSEGV handler).
+// from an asynchronous signal handler (e.g. a |SIGSEGV| handler).
 SB_EXPORT void SbLogRawFormat(const char* format, va_list args)
     SB_PRINTF_FORMAT(1, 0);
 
@@ -76,12 +88,12 @@
   va_end(args);
 }
 
-// A log output method, that additionally performs a string format on the way
-// out.
+// A log output method that additionally performs a string format on the
+// data being logged.
 SB_EXPORT void SbLogFormat(const char* format, va_list args)
     SB_PRINTF_FORMAT(1, 0);
 
-// Inline wrapper of SbLogFormat to convert from ellipsis to va_args.
+// Inline wrapper of SbLogFormat that converts from ellipsis to va_args.
 static SB_C_INLINE void SbLogFormatF(const char* format, ...)
     SB_PRINTF_FORMAT(1, 2);
 void SbLogFormatF(const char* format, ...) {
@@ -91,10 +103,10 @@
   va_end(args);
 }
 
-// Flushes the log buffer, on some platforms.
+// Flushes the log buffer on some platforms.
 SB_EXPORT void SbLogFlush();
 
-// Returns whether the log output goes to a TTY or is being redirected.
+// Indicates whether the log output goes to a TTY or is being redirected.
 SB_EXPORT bool SbLogIsTty();
 
 #ifdef __cplusplus
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 89f2608..2c800ba 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Media definitions that are common between the Decoder and Player interfaces.
+// Module Overview: Starboard Media module
+//
+// Provides media definitions that are common between the Decoder and Player
+// interfaces.
 
 #ifndef STARBOARD_MEDIA_H_
 #define STARBOARD_MEDIA_H_
@@ -40,7 +43,7 @@
   kSbMediaTypeVideo,
 } SbMediaType;
 
-// Possibly supported types of video elementary streams.
+// Types of video elementary streams that could be supported.
 typedef enum SbMediaVideoCodec {
   kSbMediaVideoCodecNone,
 
@@ -54,7 +57,7 @@
   kSbMediaVideoCodecVp9,
 } SbMediaVideoCodec;
 
-// Possibly supported types of audio elementary streams.
+// Types of audio elementary streams that can be supported.
 typedef enum SbMediaAudioCodec {
   kSbMediaAudioCodecNone,
 
@@ -63,8 +66,9 @@
   kSbMediaAudioCodecVorbis,
 } SbMediaAudioCodec;
 
-// Types of media support which is a direct map as the result of canPlayType()
-// specified in the following link:
+// Indicates how confident the device is that it can play media resources
+// of the given type. The values are a direct map of the canPlayType() method
+// specified at the following link:
 // https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
 typedef enum SbMediaSupportType {
   // The media type cannot be played.
@@ -111,24 +115,27 @@
   kSbMediaAudioSampleTypeFloat32,
 } SbMediaAudioSampleType;
 
-// Possible audio frame storage types.  Interleaved means the samples of a
-// multi-channel audio stream are stored in one continuous buffer, samples at
-// the same timestamp are stored one after another.  Planar means the samples
-// of each channel are stored in their own continuous buffer. For example, for
-// stereo stream with channels L and R that contains samples with timestamp
-// 0, 1, ..., interleaved means the samples are stored in one buffer as
-// "L0 R0 L1 R1 L2 R2 ...".  Planar means the samples are stored in two buffers
-// "L0 L1 L2 ..." and "R0 R1 R2 ...".
+// Possible audio frame storage types.
 typedef enum SbMediaAudioFrameStorageType {
+  // The samples of a multi-channel audio stream are stored in one continuous
+  // buffer. Samples at the same timestamp are stored one after another. For
+  // example, for a stereo stream with channels L and R that contains samples
+  // with timestamps 0, 1, 2, etc., the samples are stored in one buffer as
+  // "L0 R0 L1 R1 L2 R2 ...".
   kSbMediaAudioFrameStorageTypeInterleaved,
+
+  // The samples of each channel are stored in their own continuous buffer.
+  // For example, for a stereo stream with channels L and R that contains
+  // samples with timestamps 0, 1, 2, etc., the samples are stored in two
+  // buffers "L0 L1 L2 ..." and "R0 R1 R2 ...".
   kSbMediaAudioFrameStorageTypePlanar,
 } SbMediaAudioFrameStorageType;
 
 // The set of information required by the decoder or player for each video
 // sample.
 typedef struct SbMediaVideoSampleInfo {
-  // Whether the associated sample is a key frame (I-frame). Video key frames
-  // must always start with SPS and PPS NAL units.
+  // Indicates whether the associated sample is a key frame (I-frame).
+  // Video key frames must always start with SPS and PPS NAL units.
   bool is_key_frame;
 
   // The frame width of this sample, in pixels. Also could be parsed from the
@@ -148,11 +155,11 @@
   // The platform-defined index of the associated audio output.
   int index;
 
-  // The type of audio connector. Will be the empty kSbMediaAudioConnectorNone
+  // The type of audio connector. Will be the empty |kSbMediaAudioConnectorNone|
   // if this device cannot provide this information.
   SbMediaAudioConnector connector;
 
-  // The expected latency of audio over this output, in microseconds, or 0 if
+  // The expected latency of audio over this output, in microseconds, or |0| if
   // this device cannot provide this information.
   SbTime latency;
 
@@ -160,8 +167,8 @@
   SbMediaAudioCodingType coding_type;
 
   // The number of audio channels currently supported by this device output, or
-  // 0 if this device cannot provide this information, in which case the caller
-  // can probably assume stereo output.
+  // |0| if this device cannot provide this information, in which case the
+  // caller can probably assume stereo output.
   int number_of_channels;
 } SbMediaAudioConfiguration;
 
@@ -170,18 +177,14 @@
 // decoder.
 //
 // The Sequence Header consists of a little-endian hexadecimal encoded
-// WAVEFORMATEX structure followed by an Audio-specific configuration field.
-//
-// Specification of WAVEFORMATEX structure:
+// |WAVEFORMATEX| structure followed by an Audio-specific configuration field.
+// The |WAVEFORMATEX| structure is specified at:
 // http://msdn.microsoft.com/en-us/library/dd390970(v=vs.85).aspx
-//
-// AudioSpecificConfig defined here in section 1.6.2.1:
-// http://read.pudn.com/downloads98/doc/comm/401153/14496/ISO_IEC_14496-3%20Part%203%20Audio/C036083E_SUB1.PDF
 typedef struct SbMediaAudioHeader {
   // The waveform-audio format type code.
   uint16_t format_tag;
 
-  // The number of audio channels in this format. 1 for mono, 2 for stereo.
+  // The number of audio channels in this format. |1| for mono, |2| for stereo.
   uint16_t number_of_channels;
 
   // The sampling rate.
@@ -193,13 +196,14 @@
   // Byte block alignment, e.g, 4.
   uint16_t block_alignment;
 
-  // The bit depth for the stream this represents, e.g. 8 or 16.
+  // The bit depth for the stream this represents, e.g. |8| or |16|.
   uint16_t bits_per_sample;
 
   // The size, in bytes, of the audio_specific_config.
   uint16_t audio_specific_config_size;
 
-  // The AudioSpecificConfig, as specified in ISO/IEC-14496-3.
+  // The AudioSpecificConfig, as specified in ISO/IEC-14496-3, section 1.6.2.1:
+  // http://read.pudn.com/downloads98/doc/comm/401153/14496/ISO_IEC_14496-3%20Part%203%20Audio/C036083E_SUB1.PDF
   int8_t audio_specific_config[8];
 } SbMediaAudioHeader;
 
@@ -209,73 +213,99 @@
 
 // --- Functions -------------------------------------------------------------
 
-// Returns whether decoding |video_codec|, |audio_codec|, and decrypting using
-// |key_system| is supported together by this platform.  If |video_codec| is
-// kSbMediaVideoCodecNone or |audio_codec| is kSbMediaAudioCodecNone, this
-// function should return true if |key_system| is supported on the platform to
-// decode any supported input formats.
+// Indicates whether this platform supports decoding |video_codec| and
+// |audio_codec| along with decrypting using |key_system|. If |video_codec| is
+// |kSbMediaVideoCodecNone| or if |audio_codec| is |kSbMediaAudioCodecNone|,
+// this function should return |true| as long as |key_system| is supported on
+// the platform to decode any supported input formats.
+//
+// |video_codec|: The |SbMediaVideoCodec| being checked for platform
+//   compatibility.
+// |audio_codec|: The |SbMediaAudioCodec| being checked for platform
+//   compatibility.
+// |key_system|: The key system being checked for platform compatibility.
 SB_EXPORT bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
                                   SbMediaAudioCodec audio_codec,
                                   const char* key_system);
 
-// Returns whether a given combination of |frame_width| x |frame_height| frames
-// at |bitrate| and |fps| is supported on this platform with |video_codec|. If
-// |video_codec| is not supported under any condition, this function will always
-// return false.
+// Indicates whether a given combination of
+// (|frame_width| x |frame_height|) frames at |bitrate| and |fps| is supported
+// on this platform with |video_codec|. If |video_codec| is not supported under
+// any condition, this function returns |false|.
 //
-// Any of the parameters may be set to 0 to mean that they shouldn't be
+// Setting any of the parameters to |0| indicates that they shouldn't be
 // considered.
+//
+// |video_codec|: The video codec used in the media content.
+// |frame_width|: The frame width of the media content.
+// |frame_height|: The frame height of the media content.
+// |bitrate|: The bitrate of the media content.
+// |fps|: The number of frames per second in the media content.
 SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
                                        int frame_width,
                                        int frame_height,
                                        int64_t bitrate,
                                        int fps);
 
-// Returns whether |audio_codec| is supported on this platform at |bitrate|. If
-// |audio_codec| is not supported under any condition, this function will always
-// return false.
+// Indicates whether this platform supports |audio_codec| at |bitrate|.
+// If |audio_codec| is not supported under any condition, this function
+// returns |false|.
+//
+// |audio_codec|: The media's audio codec (|SbMediaAudioCodec|).
+// |bitrate|: The media's bitrate.
 SB_EXPORT bool SbMediaIsAudioSupported(SbMediaVideoCodec audio_codec,
                                        int64_t bitrate);
 
-// Returns information on whether the playback of the specific media described
-// by |mime| and encrypted using |key_system| can be played.
-// |mime| contains the mime information of the media in the form of 'video/webm'
-// or 'video/mp4; codecs="avc1.42001E"'.  It may include arbitrary parameters
-// like "codecs", "channels", etc..
-// |key_system| should be a lower case in fhe form of
-// "com.example.somesystem" as suggested by
-// https://w3c.github.io/encrypted-media/#key-system
+// Returns information about whether the playback of the specific media
+// described by |mime| and encrypted using |key_system| can be played.
+//
+// Note that neither |mime| nor |key_system| can be NULL. This function returns
+// |kSbMediaSupportNotSupported| if either is NULL.
+//
+// |mime|: The mime information of the media in the form of |video/webm|
+//   or |video/mp4; codecs="avc1.42001E"|. It may include arbitrary parameters
+//   like "codecs", "channels", etc.
+// |key_system|: A lowercase value in fhe form of "com.example.somesystem"
+// as suggested by https://w3c.github.io/encrypted-media/#key-system
 // that can be matched exactly with known DRM key systems of the platform.
 // When |key_system| is an empty string, the return value is an indication for
 // non-encrypted media.
-// Note that both |mime| and |key_system| cannot be NULL.  This function returns
-// kSbMediaSupportNotSupported if any of them is NULL.
 SB_EXPORT SbMediaSupportType
 SbMediaCanPlayMimeAndKeySystem(const char* mime, const char* key_system);
 
-// Returns the number of audio outputs currently available on this device.  It
-// is expected that, even if the number of outputs or their audio configurations
-// can't be determined, the platform will at least return a single output that
-// supports at least stereo.
+// Returns the number of audio outputs currently available on this device.
+// Even if the number of outputs or their audio configurations can't be
+// determined, it is expected that the platform will at least return a single
+// output that supports at least stereo.
 SB_EXPORT int SbMediaGetAudioOutputCount();
 
-// Places the current physical audio configuration of audio output
-// |output_index| on this device into |out_configuration|, which must not be
-// NULL, or returns false if nothing could be determined on this platform, or if
-// |output_index| doesn't exist on this device.
+// Retrieves the current physical audio configuration of audio output
+// |output_index| on this device and places it in |out_configuration|,
+// which must not be NULL.
+//
+// This function returns |false| if nothing could be determined on this
+// platform or if |output_index| does not exist on this device.
+//
+// |out_configuration|: The variable that holds the audio configuration
+//   information.
 SB_EXPORT bool SbMediaGetAudioConfiguration(
     int output_index,
     SbMediaAudioConfiguration* out_configuration);
 
-// Returns whether output copy protection is currently enabled on all capable
-// outputs. If true, then non-protection-capable outputs are expected to be
+// Indicates whether output copy protection is currently enabled on all capable
+// outputs. If |true|, then non-protection-capable outputs are expected to be
 // blanked.
 SB_EXPORT bool SbMediaIsOutputProtected();
 
 // Enables or disables output copy protection on all capable outputs. If
 // enabled, then non-protection-capable outputs are expected to be blanked.
-// Returns whether the operation was successful. Returns a success even if the
-// call was redundant.
+//
+// The return value indicates whether the operation was successful, and the
+// function returns a success even if the call is redundant in that it doesn't
+// change the current value.
+//
+// |enabled|: Indicates whether output protection is enabled (|true|) or
+//   disabled.
 SB_EXPORT bool SbMediaSetOutputProtection(bool enabled);
 
 #ifdef __cplusplus
diff --git a/src/starboard/memory.h b/src/starboard/memory.h
index f48b44e..51102d6 100644
--- a/src/starboard/memory.h
+++ b/src/starboard/memory.h
@@ -11,13 +11,29 @@
 // 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.
+
+// Module Overview: Starboard Memory module
 //
-// Memory allocation, alignment, copying, and comparing.
+// Defines functions for memory allocation, alignment, copying, and comparing.
 //
-// DO NOT CALL SbMemoryAllocateUnchecked(), SbMemoryAllocateChecked()
-// SbMemoryReallocateUnchecked(), SbMemoryReallocateChecked(),
-// SbMemoryAllocateAlignedUnchecked(), SbMemoryAllocateAlignedChecked(),
-// SbMemoryFree(), SbMemoryFreeAligned(). They are internal.
+// #### Porters
+//
+// All of the "Unchecked" and "Free" functions must be implemented, but they
+// should not be called directly. The Starboard platform wraps them with extra
+// accounting under certain circumstances.
+//
+// #### Porters and Cobalt developers
+//
+// Nobody should call the "Checked", "Unchecked" or "Free" functions directly
+// because that evades Starboard's memory tracking. In both port implementations
+// and Cobalt code, you should always call SbMemoryAllocate and
+// SbMemoryDeallocate rather than SbMemoryAllocateUnchecked and SbMemoryFree.
+//
+// - The "checked" functions are SbMemoryAllocateChecked(),
+//   SbMemoryReallocateChecked(), and SbMemoryAllocateAlignedChecked().
+// - The "unchecked" functions are SbMemoryAllocateUnchecked(),
+//   SbMemoryReallocateUnchecked(), and SbMemoryAllocateAlignedUnchecked().
+// - The "free" functions are SbMemoryFree() and SbMemoryFreeAligned().
 
 #ifndef STARBOARD_MEMORY_H_
 #define STARBOARD_MEMORY_H_
@@ -64,46 +80,63 @@
   }
 }
 
-// Allocates a chunk of memory of at least |size| bytes, returning it. If unable
-// to allocate the memory, it returns NULL. If |size| is 0, it may return NULL
-// or it may return a unique pointer value that can be passed to
-// SbMemoryDeallocate. Meant to be a drop-in replacement for malloc.
+// Allocates and returns a chunk of memory of at least |size| bytes. This
+// function should be called from the Cobalt codebase. It is intended to
+// be a drop-in replacement for |malloc|.
 //
-// This memory function should be called from the Cobalt codebase.
+// Note that this function returns |NULL| if it is unable to allocate the
+// memory.
+//
+// |size|: The amount of memory to be allocated. If |size| is 0, the function
+//   may return |NULL| or it may return a unique pointer value that can be
+//   passed to SbMemoryDeallocate.
 SB_EXPORT void* SbMemoryAllocate(size_t size);
 
-// Attempts to resize |memory| to be at least |size| bytes, without touching the
-// contents. If it cannot perform the fast resize, it will allocate a new chunk
-// of memory, copy the contents over, and free the previous chunk, returning a
-// pointer to the new chunk. If it cannot perform the slow resize, it will
-// return NULL, leaving the given memory chunk unchanged. |memory| may be NULL,
-// in which case it behaves exactly like SbMemoryAllocateUnchecked.  If |size|
-// is 0, it may return NULL or it may return a unique pointer value that can be
-// passed to SbMemoryDeallocate. Meant to be a drop-in replacement for realloc.
+// Attempts to resize |memory| to be at least |size| bytes, without touching
+// the contents of memory.
+// - If the function cannot perform the fast resize, it allocates a new chunk
+//   of memory, copies the contents over, and frees the previous chunk,
+//   returning a pointer to the new chunk.
+// - If the function cannot perform the slow resize, it returns |NULL|,
+//   leaving the given memory chunk unchanged.
 //
-// This memory function should be called from the Cobalt codebase.
+// This function should be called from the Cobalt codebase. It is meant
+// to be a drop-in replacement for |realloc|.
+//
+// |memory|: The chunk of memory to be resized. |memory| may be NULL, in which
+//   case it behaves exactly like SbMemoryAllocateUnchecked.
+// |size|: The size to which |memory| will be resized. If |size| is |0|,
+//   the function may return |NULL| or it may return a unique pointer value
+//   that can be passed to SbMemoryDeallocate.
 SB_EXPORT void* SbMemoryReallocate(void* memory, size_t size);
 
-// Allocates a chunk of memory of at least |size| bytes, aligned to
-// |alignment|, returning it. |alignment| must be a power of two, otherwise
-// behavior is undefined. If unable to allocate the memory, it returns NULL.
-// If |size| is 0, it may return NULL or it may return a unique aligned pointer
-// value that can be passed to SbMemoryDeallocateAligned. Meant to be a drop-in
-// replacement for memalign.
+// Allocates and returns a chunk of memory of at least |size| bytes, aligned
+// to |alignment|. This function should be called from the Cobalt codebase.
+// It is meant to be a drop-in replacement for |memalign|.
 //
-// This memory function should be called from the Cobalt codebase.
+// The function returns |NULL| if it cannot allocate the memory. In addition,
+// the function's behavior is undefined if |alignment| is not a power of two.
+//
+// |alignment|: The way that data is arranged and accessed in memory. The value
+//   must be a power of two.
+// |size|: The size of the memory to be allocated. If |size| is |0|, the
+//   function may return |NULL| or it may return a unique aligned pointer value
+//   that can be passed to SbMemoryDeallocateAligned.
 SB_EXPORT void* SbMemoryAllocateAligned(size_t alignment, size_t size);
 
-// Frees a previously allocated chunk of memory. If |memory| is NULL, then it
-// does nothing.  Meant to be a drop-in replacement for free.
+// Frees a previously allocated chunk of memory. If |memory| is NULL, then the
+// operation is a no-op. This function should be called from the Cobalt
+// codebase. It is meant to be a drop-in replacement for |free|.
 //
-// This memory function should be called from the Cobalt codebase.
+// |memory|: The chunk of memory to be freed.
 SB_EXPORT void SbMemoryDeallocate(void* memory);
 
-// Frees a previously allocated chunk of aligned memory. If |memory| is NULL,
-// then it does nothing.  Meant to be a drop-in replacement for _aligned_free.
-//
-// This memory function should be called from the Cobalt codebase.
+// Frees a previously allocated chunk of aligned memory. This function should
+// be called from the Cobalt codebase. It is meant to be a drop-in replacement
+// for |_aligned_free|.
+
+// |memory|: The chunk of memory to be freed. If |memory| is NULL, then the
+//   function is a no-op.
 SB_EXPORT void SbMemoryDeallocateAligned(void* memory);
 
 /////////////////////////////////////////////////////////////////
@@ -148,19 +181,24 @@
 
 #if SB_HAS(MMAP)
 // Allocates |size_bytes| worth of physical memory pages and maps them into an
-// available virtual region. |flags| is the bitwise or of the protection flags
-// for the mapped memory as specified in SbMemoryMapFlags. On some platforms,
-// |name| appears in the debugger and can be up to 32 bytes. Returns
-// SB_MEMORY_MAP_FAILED on failure, as NULL is a valid return value.
+// available virtual region. This function returns |SB_MEMORY_MAP_FAILED| on
+// failure. |NULL| is a valid return value.
+//
+// |size_bytes|: The amount of physical memory pages to be allocated.
+// |flags|: The bitwise OR of the protection flags for the mapped memory
+//   as specified in |SbMemoryMapFlags|.
+// |name|: A value that appears in the debugger on some platforms. The value
+//   can be up to 32 bytes.
 SB_EXPORT void* SbMemoryMap(int64_t size_bytes, int flags, const char* name);
 
 // Unmap |size_bytes| of physical pages starting from |virtual_address|,
-// returning true on success. After this, [virtual_address, virtual_address +
-// size_bytes) will not be read/writable. SbMemoryUnmap() can unmap multiple
-// contiguous regions that were mapped with separate calls to
-// SbMemoryMap(). E.g. if one call to SbMemoryMap(0x1000) returns (void*)0xA000
-// and another call to SbMemoryMap(0x1000) returns (void*)0xB000,
-// SbMemoryUnmap(0xA000, 0x2000) should free both.
+// returning |true| on success. After this function completes,
+// [virtual_address, virtual_address + size_bytes) will not be read/writable.
+// This function can unmap multiple contiguous regions that were mapped with
+// separate calls to SbMemoryMap(). For example, if one call to
+// |SbMemoryMap(0x1000)| returns |(void*)0xA000|, and another call to
+// |SbMemoryMap(0x1000)| returns |(void*)0xB000|,
+// |SbMemoryUnmap(0xA000, 0x2000)| should free both regions.
 SB_EXPORT bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes);
 
 #if SB_CAN(MAP_EXECUTABLE_MEMORY)
@@ -172,51 +210,78 @@
 #endif
 #endif  // SB_HAS(MMAP)
 
-// Gets the stack bounds for the current thread, placing the highest addressable
-// byte + 1 in |out_high|, and the lowest addressable byte in |out_low|.
+// Gets the stack bounds for the current thread.
+//
+// |out_high|: The highest addressable byte + 1 for the current thread.
+// |out_low|: The lowest addressable byte for the current thread.
 SB_EXPORT void SbMemoryGetStackBounds(void** out_high, void** out_low);
 
 // Copies |count| sequential bytes from |source| to |destination|, without
-// support for the |source| and |destination| regions overlapping.  Behavior is
-// undefined if |destination| or |source| are NULL. Does nothing if |count| is
-// 0. Returns |destination|, for some reason. Meant to be a drop-in replacement
-// for memcpy.
+// support for the |source| and |destination| regions overlapping. This
+// function is meant to be a drop-in replacement for |memcpy|.
+//
+// The function's behavior is undefined if |destination| or |source| are NULL,
+// and the function is a no-op if |count| is 0. The return value is
+// |destination|.
+//
+// |destination|: The destination of the copied memory.
+// |source|: The source of the copied memory.
+// |count|: The number of sequential bytes to be copied.
 SB_EXPORT void* SbMemoryCopy(void* destination,
                              const void* source,
                              size_t count);
 
 // Copies |count| sequential bytes from |source| to |destination|, with support
-// for the |source| and |destination| regions overlapping.  Behavior is
-// undefined if |destination| or |source| are NULL. Does nothing if |count| is
-// 0. Returns |destination|, for some reason. Meant to be a drop-in replacement
-// for memmove.
+// for the |source| and |destination| regions overlapping. This function is
+// meant to be a drop-in replacement for |memmove|.
+//
+// The function's behavior is undefined if |destination| or |source| are NULL,
+// and the function is a no-op if |count| is 0. The return value is
+// |destination|.
+//
+// |destination|: The destination of the copied memory.
+// |source|: The source of the copied memory.
+// |count|: The number of sequential bytes to be copied.
 SB_EXPORT void* SbMemoryMove(void* destination,
                              const void* source,
                              size_t count);
 
 // Fills |count| sequential bytes starting at |destination|, with the unsigned
-// char coercion of |byte_value|. Behavior is undefined if |destination| is
-// NULL. Returns |destination|, for some reason. Does nothing if |count| is 0.
-// Meant to be a drop-in replacement for memset.
+// char coercion of |byte_value|. This function is meant to be a drop-in
+// replacement for |memset|.
+//
+// The function's behavior is undefined if |destination| is NULL, and the
+// function is a no-op if |count| is 0. The return value is |destination|.
+//
+// |destination|: The destination of the copied memory.
+// |count|: The number of sequential bytes to be set.
 SB_EXPORT void* SbMemorySet(void* destination, int byte_value, size_t count);
 
 // Compares the contents of the first |count| bytes of |buffer1| and |buffer2|.
-// returns -1 if |buffer1| is "less-than" |buffer2|, 0 if they are equal, or 1
-// if |buffer1| is "greater-than" |buffer2|.  Meant to be a drop-in replacement
-// for memcmp.
+// This function returns:
+// - |-1| if |buffer1| is "less-than" |buffer2|
+// - |0| if |buffer1| and |buffer2| are equal
+// - |1| if |buffer1| is "greater-than" |buffer2|.
+//
+// This function is meant to be a drop-in replacement for |memcmp|.
+//
+// |buffer1|: The first buffer to be compared.
+// |buffer2|: The second buffer to be compared.
+// |count|: The number of bytes to be compared.
 SB_EXPORT int SbMemoryCompare(const void* buffer1,
                               const void* buffer2,
                               size_t count);
 
-// Finds the lower 8-bits of |value| in the first |count| bytes of |buffer|,
-// returning a pointer to the first found occurrence, or NULL if not
-// found. Meant to be a drop-in replacement for memchr.
+// Finds the lower 8-bits of |value| in the first |count| bytes of |buffer|
+// and returns either a pointer to the first found occurrence or |NULL| if
+// the value is not found. This function is meant to be a drop-in replacement
+// for |memchr|.
 SB_EXPORT const void* SbMemoryFindByte(const void* buffer,
                                        int value,
                                        size_t count);
 
-// A wrapper that implements a drop-in replacement for calloc, since some
-// packages actually seem to use it.
+// A wrapper that implements a drop-in replacement for |calloc|, which is used
+// in some packages.
 static SB_C_INLINE void* SbMemoryCalloc(size_t count, size_t size) {
   size_t total = count * size;
   void* result = SbMemoryAllocate(total);
diff --git a/src/starboard/memory_reporter.h b/src/starboard/memory_reporter.h
new file mode 100644
index 0000000..1a17c38
--- /dev/null
+++ b/src/starboard/memory_reporter.h
@@ -0,0 +1,105 @@
+// 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.
+
+// Module Overview: Memory Reporting API.
+//
+// Provides an interface for memory reporting.
+
+#ifndef STARBOARD_MEMORY_REPORTER_H_
+#define STARBOARD_MEMORY_REPORTER_H_
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// These are callbacks used by SbMemoryReporter to report memory
+// allocation and deallocation.
+///////////////////////////////////////////////////////////////////////////////
+
+// A function to report a memory allocation from SbMemoryAllocate(). Note that
+// operator new calls SbMemoryAllocate which will delegate to this callback.
+typedef void (*SbMemoryReporterOnAlloc)(void* context, const void* memory,
+                                        size_t size);
+
+// A function to report a memory deallocation from SbMemoryDeallcoate(). Note
+// that operator delete calls SbMemoryDeallocate which will delegate to this
+// callback.
+typedef void (*SbMemoryReporterOnDealloc)(void* context, const void* memory);
+
+// A function to report a memory mapping from SbMemoryMap().
+typedef void (*SbMemoryReporterOnMapMemory)(void* context, const void* memory,
+                                            size_t size);
+
+// A function to report a memory unmapping from SbMemoryUnmap().
+typedef void (*SbMemoryReporterOnUnMapMemory)(void* context,
+                                              const void* memory,
+                                              size_t size);
+
+// SbMemoryReporter allows memory reporting via user-supplied functions.
+// The void* context is passed to every call back.
+// It's strongly recommended that C-Style struct initialization is used
+// so that the arguments can be typed check by the compiler.
+// For example,
+//  SbMemoryReporter mem_reporter = { MallocCallback, .... context };
+struct SbMemoryReporter {
+  // Callback to report allocations.
+  SbMemoryReporterOnAlloc on_alloc_cb;
+
+  // Callback to report deallocations.
+  SbMemoryReporterOnDealloc on_dealloc_cb;
+
+  // Callback to report memory map.
+  SbMemoryReporterOnMapMemory on_mapmem_cb;
+
+  // Callback to report memory unmap.
+  SbMemoryReporterOnUnMapMemory on_unmapmem_cb;
+
+  // Optional, is passed to callbacks as first argument.
+  void* context;
+};
+
+// Sets the MemoryReporter. Any previous memory reporter is unset. No lifetime
+// management is done internally on input pointer.
+//
+// Returns true if the memory reporter was set with no errors. If an error
+// was reported then check the log for why it failed.
+//
+// Note that other than a thread-barrier-write of the input pointer, there is
+// no thread safety guarantees with this function due to performance
+// considerations. It's recommended that this be called once during the
+// lifetime of the program, or not at all. Do not delete the supplied pointer,
+// ever.
+// Example (Good):
+//    SbMemoryReporter* mem_reporter = new ...;
+//    SbMemorySetReporter(&mem_reporter);
+//    ...
+//    SbMemorySetReporter(NULL);  // allow value to leak.
+// Example (Bad):
+//    SbMemoryReporter* mem_reporter = new ...;
+//    SbMemorySetReporter(&mem_reporter);
+//    ...
+//    SbMemorySetReporter(NULL);
+//    delete mem_reporter;        // May crash.
+SB_EXPORT bool SbMemorySetReporter(struct SbMemoryReporter* tracker);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // STARBOARD_MEMORY_REPORTER_H_
diff --git a/src/starboard/microphone.h b/src/starboard/microphone.h
index b742f71..52464c3 100644
--- a/src/starboard/microphone.h
+++ b/src/starboard/microphone.h
@@ -12,24 +12,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Microphone creation, control, audio data fetching and destruction.
-// Multiple calls to |SbMicrophoneOpen| and |SbMicrophoneClose| are allowed, and
-// the implementation also should take care of same calls of open and close in a
-// row on a microphone.
-// This API is not threadsafe and must be called from a single thread.
+// Module Overview: Starboard Microphone module
+//
+// Defines functions for microphone creation, control, audio data fetching,
+// and destruction. This module supports multiple calls to |SbMicrophoneOpen|
+// and |SbMicrophoneClose|, and the implementation should handle multiple calls
+// to one of those functions on the same microphone. For example, your
+// implementation should handle cases where |SbMicrophoneOpen| is called twice
+// on the same microphone without a call to |SbMicrophoneClose| in between.
+//
+// This API is not thread-safe and must be called from a single thread.
 //
 // How to use this API:
-// 1) |SbMicrophoneGetAvailableInfos| to get a list of available microphone
+// 1. Call |SbMicrophoneGetAvailableInfos| to get a list of available microphone
 //    information.
-// 2) Choose one to create microphone |SbMicrophoneCreate| with enough buffer
-//    size and sample rate. The sample rate can be verified by
-//    |SbMicrophoneIsSampleRateSupported|.
-// 3) Open the microphone port and start recording audio data by
-//    |SbMicrophoneOpen|.
-// 4) Periodically read out the data from microphone by |SbMicrophoneRead|.
-// 5) Close the microphone port and stop recording audio data by
-//    |SbMicrophoneClose|.
-// 6) Destroy the microphone |SbMicrophoneDestroy|.
+// 2. Create a supported microphone, using |SbMicrophoneCreate|, with enough
+//    buffer size and sample rate. Use |SbMicrophoneIsSampleRateSupported| to
+//    verify the sample rate.
+// 3. Use |SbMicrophoneOpen| to open the microphone port and start recording
+//    audio data.
+// 4. Periodically read out the data from microphone with |SbMicrophoneRead|.
+// 5. Call |SbMicrophoneClose| to close the microphone port and stop recording
+//    audio data.
+// 6. Destroy the microphone with |SbMicrophoneDestroy|.
 
 #ifndef STARBOARD_MICROPHONE_H_
 #define STARBOARD_MICROPHONE_H_
@@ -46,10 +51,10 @@
 
 // All possible microphone types.
 typedef enum SbMicrophoneType {
-  // Build-in microphone in camera.
+  // Built-in microphone in camera.
   kSbMicrophoneCamera,
 
-  // Microphone in the headset which can be wire or wireless USB headset.
+  // Microphone in the headset that can be a wired or wireless USB headset.
   kSbMicrophoneUSBHeadset,
 
   // Microphone in the VR headset.
@@ -58,19 +63,19 @@
   // Microphone in the analog headset.
   kSBMicrophoneAnalogHeadset,
 
-  // Unknown microphone type. Microphone other than those listed or could be
-  // either of those listed.
+  // Unknown microphone type. The microphone could be different than the other
+  // enum descriptions or could fall under one of those descriptions.
   kSbMicrophoneUnknown,
 } SbMicrophoneType;
 
-// An opaque handle to an implementation-private structure representing a
-// microphone id.
+// An opaque handle to an implementation-private structure that represents a
+// microphone ID.
 typedef struct SbMicrophoneIdPrivate* SbMicrophoneId;
 
-// Well-defined value for an invalid microphone id handle.
+// Well-defined value for an invalid microphone ID handle.
 #define kSbMicrophoneIdInvalid ((SbMicrophoneId)NULL)
 
-// Returns whether the given microphone id is valid.
+// Indicates whether the given microphone ID is valid.
 static SB_C_INLINE bool SbMicrophoneIdIsValid(SbMicrophoneId id) {
   return id != kSbMicrophoneIdInvalid;
 }
@@ -83,99 +88,108 @@
   // Microphone type.
   SbMicrophoneType type;
 
-  // Microphone max supported sampling rate.
+  // The microphone's maximum supported sampling rate.
   int max_sample_rate_hz;
 
   // The minimum read size required for each read from microphone.
   int min_read_size;
 } SbMicrophoneInfo;
 
-// An opaque handle to an implementation-private structure representing a
+// An opaque handle to an implementation-private structure that represents a
 // microphone.
 typedef struct SbMicrophonePrivate* SbMicrophone;
 
 // Well-defined value for an invalid microphone handle.
 #define kSbMicrophoneInvalid ((SbMicrophone)NULL)
 
-// Returns whether the given microphone is valid.
+// Indicates whether the given microphone is valid.
 static SB_C_INLINE bool SbMicrophoneIsValid(SbMicrophone microphone) {
   return microphone != kSbMicrophoneInvalid;
 }
 
-// Gets all currently-available microphone information and the results are
-// stored in |out_info_array|. |info_array_size| is the size of
-// |out_info_array|.
-// Returns the number of the available microphones. A negative return
-// value indicates that an internal error has occurred. If the number of
-// available microphones is larger than |info_array_size|, |out_info_array| will
-// be filled up with as many available microphones as possible and the actual
-// number of available microphones will be returned.
+// Retrieves all currently available microphone information and stores it in
+// |out_info_array|. The return value is the number of the available
+// microphones. If the number of available microphones is larger than
+// |info_array_size|, then |out_info_array| is filled up with as many available
+// microphones as possible and the actual number of available microphones is
+// returned. A negative return value indicates that an internal error occurred.
+//
+// |out_info_array|: All currently available information about the microphone
+//   is placed into this output parameter.
+// |info_array_size|: The size of |out_info_array|.
 SB_EXPORT int SbMicrophoneGetAvailable(SbMicrophoneInfo* out_info_array,
                                        int info_array_size);
 
-// Returns true if the sample rate is supported by the microphone.
+// Indicates whether the microphone supports the sample rate.
 SB_EXPORT bool SbMicrophoneIsSampleRateSupported(SbMicrophoneId id,
                                                  int sample_rate_in_hz);
 
-// Creates a microphone with |id|, audio sample rate in HZ, and the size of
-// the cached audio buffer.
+// Creates a microphone with the specified ID, audio sample rate, and cached
+// audio buffer size. Starboard only requires support for creating one
+// microphone at a time, and implementations may return an error if a second
+// microphone is created before the first is destroyed.
 //
-// If you try to create a microphone that has already been initialized or
-// the sample rate is unavailable or the buffer size is invalid, it should
-// return |kSbMicrophoneInvalid|. |buffer_size_bytes| is the size of the buffer
-// where signed 16-bit integer audio data is temporarily cached to during the
-// capturing. The audio data will be removed from the audio buffer if it has
-// been read. New audio data can be read from this buffer in smaller chunks than
-// this size. |buffer_size_bytes| must be set to a value greater than zero and
-// the ideal size is 2^n. We only require support for creating one microphone at
-// a time, and that implementations may return an error if a second microphone
-// is created before destroying the first.
+// The function returns the newly created SbMicrophone object. However, if you
+// try to create a microphone that has already been initialized, if the sample
+// rate is unavailable, or if the buffer size is invalid, the function should
+// return |kSbMicrophoneInvalid|.
+//
+// |id|: The ID that will be assigned to the newly created SbMicrophone.
+// |sample_rate_in_hz|: The new microphone's audio sample rate in Hz.
+// |buffer_size_bytes|: The size of the buffer where signed 16-bit integer
+//   audio data is temporarily cached to during the capturing. The audio data
+//   is removed from the audio buffer if it has been read, and new audio data
+//   can be read from this buffer in smaller chunks than this size. This
+//   parameter must be set to a value greater than zero and the ideal size is
+//   |2^n|.
 SB_EXPORT SbMicrophone SbMicrophoneCreate(SbMicrophoneId id,
                                           int sample_rate_in_hz,
                                           int buffer_size_bytes);
 
 // Opens the microphone port and starts recording audio on |microphone|.
 //
-// Once started, the client will have to periodically call |SbMicrophoneRead| to
+// Once started, the client needs to periodically call |SbMicrophoneRead| to
 // receive the audio data. If the microphone has already been started, this call
-// will clear the unread buffer. The return value indicates if the microphone is
-// open.
+// clears the unread buffer. The return value indicates whether the microphone
+// is open.
+// |microphone|: The microphone that will be opened and will start recording
+// audio.
 SB_EXPORT bool SbMicrophoneOpen(SbMicrophone microphone);
 
-// Closes the microphone port and stops recording audio on |microphone|.
-//
-// Clear the unread buffer if it is not empty. If the microphone has already
-// been stopped, this call would be ignored. The return value indicates if the
+// Closes the microphone port, stops recording audio on |microphone|, and
+// clears the unread buffer if it is not empty. If the microphone has already
+// been stopped, this call is ignored. The return value indicates whether the
 // microphone is closed.
+//
+// |microphone|: The microphone to close.
 SB_EXPORT bool SbMicrophoneClose(SbMicrophone microphone);
 
-// Gets the recorded audio data from the microphone.
+// Retrieves the recorded audio data from the microphone and writes that data
+// to |out_audio_data|.
 //
-// |out_audio_data| is where the recorded audio data is written to.
-// |audio_data_size| is the number of requested bytes. The return value is zero
-// or the positive number of bytes that were read. Neither the return value nor
-// |audio_data_size| exceeds the buffer size. Negative return value indicates
-// an error. This function should be called frequently, otherwise microphone
-// only buffers |buffer_size| bytes which is configured in |SbMicrophoneCreate|
-// and the new audio data will be thrown out. No audio data will be read from a
-// stopped microphone. If |audio_data_size| is smaller than |min_read_size| of
-// |SbMicrophoneInfo|, the extra audio data which is already read from device
-// will be discarded.
+// The return value is zero or the positive number of bytes that were read.
+// Neither the return value nor |audio_data_size| exceeds the buffer size.
+// A negative return value indicates that an error occurred.
+//
+// This function should be called frequently. Otherwise, the microphone only
+// buffers |buffer_size| bytes as configured in |SbMicrophoneCreate| and the
+// new audio data is thrown out. No audio data is read from a stopped
+// microphone.
+//
+// |microphone|: The microphone from which to retrieve recorded audio data.
+// |out_audio_data|: The buffer to which the retrieved data will be written.
+// |audio_data_size|: The number of requested bytes. If |audio_data_size| is
+//   smaller than |min_read_size| of |SbMicrophoneInfo|, the extra audio data
+//   that has already been read from the device is discarded.
 SB_EXPORT int SbMicrophoneRead(SbMicrophone microphone,
                                void* out_audio_data,
                                int audio_data_size);
 
-// Destroys a microphone. If the microphone is in started state, it will be
-// stopped first and then be destroyed. Any data that has been recorded and not
-// read will be thrown away.
+// Destroys a microphone. If the microphone is in started state, it is first
+// stopped and then destroyed. Any data that has been recorded and not read
+// is thrown away.
 SB_EXPORT void SbMicrophoneDestroy(SbMicrophone microphone);
 
-// Returns the Google Speech API key. The platform manufacturer is responsible
-// for registering a Google Speech API key for their products. In the API
-// Console (http://developers.google.com/console), you are able to enable the
-// Speech APIs and generate a Speech API key.
-SB_EXPORT const char* SbMicrophoneGetSpeechApiKey();
-
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/starboard/mutex.h b/src/starboard/mutex.h
index d06c752..953fa0d 100644
--- a/src/starboard/mutex.h
+++ b/src/starboard/mutex.h
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// A mutually exclusive lock that can be used to coordinate with other threads.
+// Module Overview: Starboard Mutex module
+//
+// Defines a mutually exclusive lock that can be used to coordinate with other
+// threads.
 
 #ifndef STARBOARD_MUTEX_H_
 #define STARBOARD_MUTEX_H_
@@ -37,31 +40,46 @@
   kSbMutexDestroyed,
 } SbMutexResult;
 
-// Returns whether the given result is a success.
+// Indicates whether the given result is a success. A value of |true| indicates
+// that the mutex was acquired.
+//
+// |result|: The result being checked.
 static SB_C_FORCE_INLINE bool SbMutexIsSuccess(SbMutexResult result) {
   return result == kSbMutexAcquired;
 }
 
-// Creates a new mutex, placing the handle to the newly created mutex in
-// |out_mutex|. Returns whether able to create a new mutex.
+// Creates a new mutex. The return value indicates whether the function
+// was able to create a new mutex.
+//
+// |out_mutex|: The handle to the newly created mutex.
 SB_EXPORT bool SbMutexCreate(SbMutex* out_mutex);
 
-// Destroys a mutex, returning whether the destruction was successful. The mutex
-// specified by |mutex| is invalidated.
+// Destroys a mutex. The return value indicates whether the destruction was
+// successful.
+//
+// |mutex|: The mutex to be invalidated.
 SB_EXPORT bool SbMutexDestroy(SbMutex* mutex);
 
-// Acquires |mutex|, blocking indefinitely, returning the acquisition result.
-// SbMutexes are not reentrant, so a recursive acquisition will block forever.
+// Acquires |mutex|, blocking indefinitely. The return value identifies
+// the acquisition result. SbMutexes are not reentrant, so a recursive
+// acquisition blocks forever.
+//
+// |mutex|: The mutex to be acquired.
 SB_EXPORT SbMutexResult SbMutexAcquire(SbMutex* mutex);
 
-// Acquires |mutex|, without blocking, returning the acquisition result.
-// SbMutexes are not reentrant, so a recursive acquisition will always fail.
+// Acquires |mutex|, without blocking. The return value identifies
+// the acquisition result. SbMutexes are not reentrant, so a recursive
+// acquisition always fails.
+//
+// |mutex|: The mutex to be acquired.
 SB_EXPORT SbMutexResult SbMutexAcquireTry(SbMutex* mutex);
 
-// Releases |mutex| held by the current thread, returning whether the release
-// was successful. Releases should always be successful if the mutex is held by
-// the current thread.
-SB_EXPORT bool SbMutexRelease(SbMutex* handle);
+// Releases |mutex| held by the current thread. The return value indicates
+// whether the release was successful. Releases should always be successful
+// if |mutex| is held by the current thread.
+//
+// |mutex|: The mutex to be released.
+SB_EXPORT bool SbMutexRelease(SbMutex* mutex);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/starboard/nplb/decode_target_create_test.cc b/src/starboard/nplb/decode_target_create_test.cc
index 9358c68..e8f3ea4 100644
--- a/src/starboard/nplb/decode_target_create_test.cc
+++ b/src/starboard/nplb/decode_target_create_test.cc
@@ -53,7 +53,7 @@
   SbDecodeTargetDestroy(target);
   EXPECT_TRUE(SbBlitterDestroySurface(surface));
 }
-#else  // SB_HAS(BLITTER)
+#elif SB_HAS(GLES2)  // SB_HAS(BLITTER)
 // clang-format off
 EGLint const kAttributeList[] = {
   EGL_RED_SIZE, 8,
@@ -66,7 +66,7 @@
   EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
   EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
   EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-  EGL_NONE
+  EGL_NONE,
 };
 // clang-format on
 
@@ -160,17 +160,31 @@
   glGenTextures(1, &texture_handle);
   glBindTexture(GL_TEXTURE_2D, texture_handle);
   glTexImage2D(GL_TEXTURE_2D, 0 /*level*/, GL_RGBA, 256, 256, 0 /*border*/,
-               GL_RGBA8, GL_UNSIGNED_BYTE, NULL /*data*/);
+               GL_RGBA, GL_UNSIGNED_BYTE, NULL /*data*/);
   glBindTexture(GL_TEXTURE_2D, 0);
 
   SbDecodeTarget target = SbDecodeTargetCreate(
       display_, context_, kSbDecodeTargetFormat1PlaneRGBA, &texture_handle);
-  GLuint plane = SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA);
-  EXPECT_EQ(texture_handle, plane);
-  SbDecodeTargetDestroy(target);
+  if (SbDecodeTargetIsValid(target)) {
+    GLuint plane = SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA);
+    EXPECT_EQ(texture_handle, plane);
+    SbDecodeTargetDestroy(target);
+  }
   glDeleteTextures(1, &texture_handle);
 }
 
+#else  // SB_HAS(BLITTER)
+
+TEST(SbDecodeTargetTest, SunnyDayCreate) {
+  // When graphics are not enabled, we expect to always create a
+  // kSbDecodeTargetInvalid, and get NULL back for planes.
+  EXPECT_EQ(SbDecodeTargetCreate(kSbDecodeTargetFormat1PlaneRGBA),
+            kSbDecodeTargetInvalid);
+  EXPECT_EQ(
+      SbDecodeTargetGetPlane(kSbDecodeTargetInvalid, kSbDecodeTargetPlaneRGBA),
+      NULL);
+}
+
 #endif  // SB_HAS(BLITTER)
 
 }  // namespace
diff --git a/src/starboard/nplb/decode_target_provider_test.cc b/src/starboard/nplb/decode_target_provider_test.cc
index a5fece5..cd53e40 100644
--- a/src/starboard/nplb/decode_target_provider_test.cc
+++ b/src/starboard/nplb/decode_target_provider_test.cc
@@ -43,130 +43,110 @@
   int called_release;
 };
 
-template <typename T>
-SbDecodeTargetProvider MakeProvider() {
-  return {&T::Acquire, &T::Release};
-}
-
-void AcquireFalse() {
-  SbDecodeTarget target = SbDecodeTargetAcquireFromProvider(
-      kSbDecodeTargetFormat1PlaneRGBA, 16, 16);
-  bool valid = SbDecodeTargetIsValid(target);
-  EXPECT_FALSE(valid);
-  if (valid) {
-    SbDecodeTargetDestroy(target);
+void AcquireFalse(SbDecodeTargetProvider* provider) {
+  if (provider) {
+    SbDecodeTarget target = SbDecodeTargetAcquireFromProvider(
+        provider, kSbDecodeTargetFormat1PlaneRGBA, 16, 16);
+    bool valid = SbDecodeTargetIsValid(target);
+    EXPECT_FALSE(valid);
+    if (valid) {
+      SbDecodeTargetDestroy(target);
+    }
   }
 }
 
-void ReleaseInvalid() {
-  SbDecodeTargetReleaseToProvider(kSbDecodeTargetInvalid);
-}
-
-TEST(SbDecodeTargetProviderTest, SunnyDayRegisterUnregister) {
-  ProviderContext context = {};
-  SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
-  ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context));
-  SbDecodeTargetUnregisterProvider(&provider, &context);
+void ReleaseInvalid(SbDecodeTargetProvider* provider) {
+  if (provider) {
+    SbDecodeTargetReleaseToProvider(provider, kSbDecodeTargetInvalid);
+  }
 }
 
 TEST(SbDecodeTargetProviderTest, SunnyDayCallsThroughToProvider) {
   ProviderContext context = {};
-  SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
-  ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context));
+  SbDecodeTargetProvider provider = {&ProviderContext::Acquire,
+                                     &ProviderContext::Release,
+                                     reinterpret_cast<void*>(&context)};
+
   EXPECT_EQ(0, context.called_acquire);
   EXPECT_EQ(0, context.called_release);
 
-  AcquireFalse();
+  AcquireFalse(&provider);
   EXPECT_EQ(1, context.called_acquire);
-  AcquireFalse();
+  AcquireFalse(&provider);
   EXPECT_EQ(2, context.called_acquire);
 
-  ReleaseInvalid();
+  ReleaseInvalid(&provider);
   EXPECT_EQ(1, context.called_release);
-  ReleaseInvalid();
+  ReleaseInvalid(&provider);
   EXPECT_EQ(2, context.called_release);
-
-  SbDecodeTargetUnregisterProvider(&provider, &context);
 }
 
-TEST(SbDecodeTargetProviderTest, SunnyDayRegisterOver) {
+TEST(SbDecodeTargetProviderTest, SunnyDayMultipleProviders) {
   ProviderContext context = {};
-  SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
-  ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context));
+  SbDecodeTargetProvider provider = {&ProviderContext::Acquire,
+                                     &ProviderContext::Release,
+                                     reinterpret_cast<void*>(&context)};
 
   ProviderContext context2 = {};
-  SbDecodeTargetProvider provider2 = MakeProvider<ProviderContext>();
-  ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider2, &context2));
+  SbDecodeTargetProvider provider2 = {&ProviderContext::Acquire,
+                                      &ProviderContext::Release,
+                                      reinterpret_cast<void*>(&context2)};
 
-  AcquireFalse();
+  AcquireFalse(&provider2);
   EXPECT_EQ(1, context2.called_acquire);
   EXPECT_EQ(0, context.called_acquire);
-  AcquireFalse();
+  AcquireFalse(&provider2);
   EXPECT_EQ(2, context2.called_acquire);
   EXPECT_EQ(0, context.called_acquire);
 
-  ReleaseInvalid();
+  ReleaseInvalid(&provider2);
   EXPECT_EQ(1, context2.called_release);
   EXPECT_EQ(0, context.called_release);
-  ReleaseInvalid();
+  ReleaseInvalid(&provider2);
   EXPECT_EQ(2, context2.called_release);
   EXPECT_EQ(0, context.called_release);
-
-  SbDecodeTargetUnregisterProvider(&provider2, &context2);
 }
 
 TEST(SbDecodeTargetProviderTest, RainyDayAcquireNoProvider) {
-  AcquireFalse();
+  AcquireFalse(NULL);
 }
 
 TEST(SbDecodeTargetProviderTest, RainyDayReleaseNoProvider) {
-  ReleaseInvalid();
+  ReleaseInvalid(NULL);
 }
 
-TEST(SbDecodeTargetProviderTest, RainyDayUnregisterUnregistered) {
+TEST(SbDecodeTargetProviderTest, RainyDayMultipleProviders) {
   ProviderContext context = {};
-  SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
-  SbDecodeTargetUnregisterProvider(&provider, &context);
+  SbDecodeTargetProvider provider = {&ProviderContext::Acquire,
+                                     &ProviderContext::Release,
+                                     reinterpret_cast<void*>(&context)};
 
   ProviderContext context2 = {};
-  SbDecodeTargetProvider provider2 = MakeProvider<ProviderContext>();
-  ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider2, &context2));
-  SbDecodeTargetUnregisterProvider(&provider, &context);
-  SbDecodeTargetUnregisterProvider(&provider, &context2);
-  SbDecodeTargetUnregisterProvider(&provider2, &context);
+  SbDecodeTargetProvider provider2 = {&ProviderContext::Acquire,
+                                      &ProviderContext::Release,
+                                      reinterpret_cast<void*>(&context2)};
 
-  AcquireFalse();
+  AcquireFalse(&provider2);
   EXPECT_EQ(1, context2.called_acquire);
-  AcquireFalse();
+  AcquireFalse(&provider2);
   EXPECT_EQ(2, context2.called_acquire);
 
-  ReleaseInvalid();
+  ReleaseInvalid(&provider2);
   EXPECT_EQ(1, context2.called_release);
-  ReleaseInvalid();
+  ReleaseInvalid(&provider2);
   EXPECT_EQ(2, context2.called_release);
 
-  SbDecodeTargetUnregisterProvider(&provider2, &context2);
-
-  AcquireFalse();
+  AcquireFalse(&provider);
   EXPECT_EQ(2, context2.called_acquire);
-  AcquireFalse();
+  AcquireFalse(&provider);
   EXPECT_EQ(2, context2.called_acquire);
 
-  ReleaseInvalid();
+  ReleaseInvalid(&provider);
   EXPECT_EQ(2, context2.called_release);
-  ReleaseInvalid();
+  ReleaseInvalid(&provider);
   EXPECT_EQ(2, context2.called_release);
 }
 
-TEST(SbDecodeTargetProviderTest, RainyDayUnregisterNull) {
-  ProviderContext context = {};
-  SbDecodeTargetUnregisterProvider(NULL, &context);
-}
-
-TEST(SbDecodeTargetProviderTest, RainyDayUnregisterNullNull) {
-  SbDecodeTargetUnregisterProvider(NULL, NULL);
-}
-
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/include_all.c b/src/starboard/nplb/include_all.c
index 5ac5aa1..e55a271 100644
--- a/src/starboard/nplb/include_all.c
+++ b/src/starboard/nplb/include_all.c
@@ -39,6 +39,7 @@
 #include "starboard/player.h"
 #include "starboard/socket.h"
 #include "starboard/socket_waiter.h"
+#include "starboard/speech_synthesis.h"
 #include "starboard/storage.h"
 #include "starboard/string.h"
 #include "starboard/system.h"
diff --git a/src/starboard/nplb/memory_reporter_test.cc b/src/starboard/nplb/memory_reporter_test.cc
new file mode 100644
index 0000000..f49e339
--- /dev/null
+++ b/src/starboard/nplb/memory_reporter_test.cc
@@ -0,0 +1,496 @@
+// 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.
+
+#include "starboard/configuration.h"
+#include "starboard/memory.h"
+#include "starboard/memory_reporter.h"
+#include "starboard/mutex.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+// This thread local boolean is used to filter allocations and
+// 1) Prevent allocations from other threads.
+// 2) Selectively disable allocations so that unintended allocs from
+//    gTest (ASSERT_XXX) don't cause the current test to fail.
+void InitMemoryTrackingState_ThreadLocal();
+bool GetMemoryTrackingEnabled_ThreadLocal();
+void SetMemoryTrackingEnabled_ThreadLocal(bool value);
+
+// Scoped object that temporary turns off MemoryTracking. This is needed
+// to avoid allocations from gTest being inserted into the memory tracker.
+struct NoMemTracking {
+  bool prev_val;
+  NoMemTracking() {
+    prev_val = GetMemoryTrackingEnabled_ThreadLocal();
+    SetMemoryTrackingEnabled_ThreadLocal(false);
+  }
+  ~NoMemTracking() {
+    SetMemoryTrackingEnabled_ThreadLocal(prev_val);
+  }
+};
+
+// EXPECT_XXX and ASSERT_XXX allocate memory, a big no-no when
+// for unit testing allocations. These overrides disable memory
+// tracking for the duration of the EXPECT and ASSERT operations.
+#define EXPECT_EQ_NO_TRACKING(A, B)                \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  EXPECT_EQ(A, B);                                 \
+}
+
+#define EXPECT_NE_NO_TRACKING(A, B)                \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  EXPECT_NE(A, B);                                 \
+}
+
+#define EXPECT_TRUE_NO_TRACKING(A)                 \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  EXPECT_TRUE(A);                                  \
+}
+
+#define EXPECT_FALSE_NO_TRACKING(A)                \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  EXPECT_FALSE(A);                                 \
+}
+
+#define ASSERT_EQ_NO_TRACKING(A, B)                \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  ASSERT_EQ(A, B);                                 \
+}
+
+#define ASSERT_NE_NO_TRACKING(A, B)                \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  ASSERT_NE(A, B);                                 \
+}
+
+#define ASSERT_TRUE_NO_TRACKING(A)                 \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  ASSERT_TRUE(A);                                  \
+}
+
+#define ASSERT_FALSE_NO_TRACKING(A)                \
+{ NoMemTracking no_memory_tracking_in_this_scope;  \
+  ASSERT_FALSE(A);                                 \
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// A memory reporter that is used to watch allocations from the system.
+class TestMemReporter {
+ public:
+  TestMemReporter() { Construct(); }
+
+  SbMemoryReporter* memory_reporter() {
+    return &memory_reporter_;
+  }
+
+  // Removes this object from listening to allocations.
+  void RemoveGlobalHooks() {
+    SbMemorySetReporter(NULL);
+    // Sleep to allow other threads time to pass through.
+    SbThreadSleep(250 * kSbTimeMillisecond);
+  }
+
+  // Total number allocations outstanding.
+  int number_allocs() const { return number_allocs_; }
+
+  void Clear() {
+    starboard::ScopedLock lock(mutex_);
+    number_allocs_ = 0;
+    last_allocation_ = NULL;
+    last_deallocation_ = NULL;
+    last_mem_map_ = NULL;
+    last_mem_unmap_ = NULL;
+  }
+
+  const void* last_allocation() const {
+    return last_allocation_;
+  }
+  const void* last_deallocation() const {
+    return last_deallocation_;
+  }
+  const void* last_mem_map() const {
+    return last_mem_map_;
+  }
+  const void* last_mem_unmap() const {
+    return last_mem_unmap_;
+  }
+
+ private:
+  // Boilerplate to delegate static function callbacks to the class instance.
+  static void OnMalloc(void* context, const void* memory, size_t size) {
+    TestMemReporter* t = static_cast<TestMemReporter*>(context);
+    t->ReportAlloc(memory, size);
+  }
+
+  static void OnMapMem(void* context, const void* memory, size_t size) {
+    TestMemReporter* t = static_cast<TestMemReporter*>(context);
+    t->ReportMapMem(memory, size);
+  }
+
+  static void OnDealloc(void* context, const void* memory) {
+    TestMemReporter* t = static_cast<TestMemReporter*>(context);
+    t->ReportDealloc(memory);
+  }
+
+  static void SbUnMapMem(void* context, const void* memory, size_t size) {
+    TestMemReporter* t = static_cast<TestMemReporter*>(context);
+    t->ReportUnMapMemory(memory, size);
+  }
+
+  static SbMemoryReporter CreateSbMemoryReporter(TestMemReporter* context) {
+    SbMemoryReporter cb = { OnMalloc, OnDealloc,
+                            OnMapMem, SbUnMapMem,
+                            context };
+    return cb;
+  }
+
+  void ReportAlloc(const void* memory, size_t /*size*/) {
+    if (!GetMemoryTrackingEnabled_ThreadLocal()) {
+      return;
+    }
+    starboard::ScopedLock lock(mutex_);
+    last_allocation_ = memory;
+    number_allocs_++;
+  }
+
+  void ReportMapMem(const void* memory, size_t size) {
+    if (!GetMemoryTrackingEnabled_ThreadLocal()) {
+      return;
+    }
+    starboard::ScopedLock lock(mutex_);
+    last_mem_map_ = memory;
+    number_allocs_++;
+  }
+
+  void ReportDealloc(const void* memory) {
+    if (!GetMemoryTrackingEnabled_ThreadLocal()) {
+      return;
+    }
+    starboard::ScopedLock lock(mutex_);
+    last_deallocation_ = memory;
+    number_allocs_--;
+  }
+
+  void ReportUnMapMemory(const void* memory, size_t size) {
+    if (!GetMemoryTrackingEnabled_ThreadLocal()) {
+      return;
+    }
+    starboard::ScopedLock lock(mutex_);
+    last_mem_unmap_ = memory;
+    number_allocs_--;
+  }
+
+  void Construct() {
+    Clear();
+    memory_reporter_ = CreateSbMemoryReporter(this);
+  }
+
+  SbMemoryReporter memory_reporter_;
+  starboard::Mutex mutex_;
+  const void* last_allocation_;
+  const void* last_deallocation_;
+  const void* last_mem_map_;
+  const void* last_mem_unmap_;
+  int number_allocs_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Needed by all tests that require a memory tracker to be installed. On the
+// first test the test memory tracker is installed and then after the last test
+// the memory tracker is removed.
+class MemoryReportingTest : public ::testing::Test {
+ public:
+  TestMemReporter* mem_reporter() { return s_test_alloc_tracker_; }
+  bool MemoryReportingEnabled() const {
+    return s_memory_reporter_error_enabled_;
+  }
+
+ protected:
+  // Global setup - runs before the first test in the series.
+  static void SetUpTestCase() {
+    InitMemoryTrackingState_ThreadLocal();
+    // global init test code should run here.
+    if (s_test_alloc_tracker_ == NULL) {
+      s_test_alloc_tracker_ = new TestMemReporter;
+    }
+    s_memory_reporter_error_enabled_ =
+        SbMemorySetReporter(s_test_alloc_tracker_->memory_reporter());
+  }
+
+  // Global Teardown after last test has run it's course.
+  static void TearDownTestCase() {
+    s_test_alloc_tracker_->RemoveGlobalHooks();
+  }
+
+  // Per test setup.
+  virtual void SetUp() {
+    mem_reporter()->Clear();
+    // Allows current thread to capture memory allocations. If this wasn't
+    // done then background threads spawned by a framework could notify this
+    // class about allocations and fail the test.
+    SetMemoryTrackingEnabled_ThreadLocal(true);
+  }
+
+  // Per test teardown.
+  virtual void TearDown() {
+    SetMemoryTrackingEnabled_ThreadLocal(false);
+    if (mem_reporter()->number_allocs() != 0) {
+      ADD_FAILURE_AT(__FILE__, __LINE__) << "Memory Leak detected.";
+    }
+    mem_reporter()->Clear();
+  }
+  static TestMemReporter* s_test_alloc_tracker_;
+  static bool s_memory_reporter_error_enabled_;
+};
+TestMemReporter* MemoryReportingTest::s_test_alloc_tracker_ = NULL;
+bool MemoryReportingTest::s_memory_reporter_error_enabled_ = false;
+
+///////////////////////////////////////////////////////////////////////////////
+// TESTS.
+// There are two sets of tests: POSITIVE and NEGATIVE.
+//  The positive tests are active when STARBOARD_ALLOWS_MEMORY_TRACKING is
+//  defined and test that memory tracking is enabled.
+//  NEGATIVE tests ensure that tracking is disabled when
+//  STARBOARD_ALLOWS_MEMORY_TRACKING is not defined.
+// When adding new tests:
+//  POSITIVE tests are named normally.
+//  NEGATIVE tets are named with "No" prefixed to the beginning.
+//  Example:
+//   TEST_F(MemoryReportingTest, CapturesAllocDealloc) <--- POSITIVE test.
+//   TEST_F(MemoryReportingTest, NoCapturesAllocDealloc) <- NEGATIVE test.
+//  All positive & negative tests are grouped together.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef STARBOARD_ALLOWS_MEMORY_TRACKING
+// These are POSITIVE tests, which test the expectation that when the define
+// STARBOARD_ALLOWS_MEMORY_TRACKING is active that the memory tracker will
+// receive memory allocations notifications.
+//
+// Tests the assumption that the SbMemoryAllocate and SbMemoryDeallocate
+// will report memory allocations.
+TEST_F(MemoryReportingTest, CapturesAllocDealloc) {
+  if (!MemoryReportingEnabled()) {
+    GTEST_SUCCEED() << "Memory reporting is disabled.";
+    return;
+  }
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  void* memory = SbMemoryAllocate(4);
+  EXPECT_EQ_NO_TRACKING(1, mem_reporter()->number_allocs());
+
+  EXPECT_EQ_NO_TRACKING(memory, mem_reporter()->last_allocation());
+
+  SbMemoryDeallocate(memory);
+  EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0);
+
+  // Should equal the last allocation.
+  EXPECT_EQ_NO_TRACKING(memory, mem_reporter()->last_deallocation());
+}
+
+// Tests the assumption that SbMemoryReallocate() will report a
+// deallocation and an allocation to the memory tracker.
+TEST_F(MemoryReportingTest, CapturesRealloc) {
+  if (!MemoryReportingEnabled()) {
+    GTEST_SUCCEED() << "Memory reporting is disabled.";
+    return;
+  }
+  void* prev_memory = SbMemoryAllocate(4);
+  void* new_memory = SbMemoryReallocate(prev_memory, 8);
+
+  EXPECT_EQ_NO_TRACKING(new_memory, mem_reporter()->last_allocation());
+  EXPECT_EQ_NO_TRACKING(prev_memory, mem_reporter()->last_deallocation());
+
+  EXPECT_EQ_NO_TRACKING(1, mem_reporter()->number_allocs());
+
+  SbMemoryDeallocate(new_memory);
+  EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0);
+}
+
+// Tests the assumption that the SbMemoryMap and SbMemoryUnmap
+// will report memory allocations.
+TEST_F(MemoryReportingTest, CapturesMemMapUnmap) {
+  if (!MemoryReportingEnabled()) {
+    GTEST_SUCCEED() << "Memory reporting is disabled.";
+    return;
+  }
+  const int64_t kMemSize = 4096;
+  const int kFlags = 0;
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  void* mem_chunk = SbMemoryMap(kMemSize, kFlags, "TestMemMap");
+  EXPECT_EQ_NO_TRACKING(1, mem_reporter()->number_allocs());
+
+  // Now unmap the memory and confirm that this memory was reported as free.
+  EXPECT_EQ_NO_TRACKING(mem_chunk, mem_reporter()->last_mem_map());
+  SbMemoryUnmap(mem_chunk, kMemSize);
+  EXPECT_EQ_NO_TRACKING(mem_chunk, mem_reporter()->last_mem_unmap());
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+}
+
+// Tests the assumption that the operator/delete will report
+// memory allocations.
+TEST_F(MemoryReportingTest, CapturesOperatorNewDelete) {
+  if (!MemoryReportingEnabled()) {
+    GTEST_SUCCEED() << "Memory reporting is disabled.";
+    return;
+  }
+  EXPECT_TRUE_NO_TRACKING(mem_reporter()->number_allocs() == 0);
+  int* my_int = new int();
+  EXPECT_TRUE_NO_TRACKING(mem_reporter()->number_allocs() == 1);
+
+  bool is_last_allocation =
+      my_int == mem_reporter()->last_allocation();
+
+  EXPECT_TRUE_NO_TRACKING(is_last_allocation);
+
+  delete my_int;
+  EXPECT_TRUE_NO_TRACKING(mem_reporter()->number_allocs() == 0);
+
+  // Expect last deallocation to be the expected pointer.
+  EXPECT_EQ_NO_TRACKING(my_int, mem_reporter()->last_deallocation());
+}
+
+#else  // !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+// These are NEGATIVE tests, which test the expectation that when the
+// STARBOARD_ALLOWS_MEMORY_TRACKING is undefined that the memory tracker does
+// not receive memory allocations notifications.
+
+TEST_F(MemoryReportingTest, NoCapturesAllocDealloc) {
+  EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled());
+
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  void* memory = SbMemoryAllocate(4);
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_allocation());
+
+  SbMemoryDeallocate(memory);
+  EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0);
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_deallocation());
+}
+
+TEST_F(MemoryReportingTest, NoCapturesRealloc) {
+  EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled());
+  void* prev_memory = SbMemoryAllocate(4);
+  void* new_memory = SbMemoryReallocate(prev_memory, 8);
+
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_allocation());
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_deallocation());
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+
+  SbMemoryDeallocate(new_memory);
+  EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0);
+}
+
+TEST_F(MemoryReportingTest, NoCapturesMemMapUnmap) {
+  EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled());
+  const int64_t kMemSize = 4096;
+  const int kFlags = 0;
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  void* mem_chunk = SbMemoryMap(kMemSize, kFlags, "TestMemMap");
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+
+  // Now unmap the memory and confirm that this memory was reported as free.
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_mem_map());
+  SbMemoryUnmap(mem_chunk, kMemSize);
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_mem_unmap());
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+}
+
+TEST_F(MemoryReportingTest, NoCapturesOperatorNewDelete) {
+  EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled());
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  int* my_int = new int();
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_allocation());
+
+  delete my_int;
+  EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs());
+  EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_deallocation());
+}
+
+#endif  // !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
+
+/////////////////////////////// Implementation ////////////////////////////////
+
+// Simple ThreadLocalBool class which allows a default value to be
+// true or false.
+class ThreadLocalBool {
+ public:
+  ThreadLocalBool() {
+    // NULL is the destructor.
+    slot_ = SbThreadCreateLocalKey(NULL);
+  }
+
+  ~ThreadLocalBool() {
+    SbThreadDestroyLocalKey(slot_);
+  }
+
+  void SetEnabled(bool value) {
+    SetEnabledThreadLocal(value);
+  }
+
+  bool Enabled() const {
+    return GetEnabledThreadLocal();
+  }
+
+ private:
+  void SetEnabledThreadLocal(bool value) {
+    void* bool_as_pointer;
+    if (value) {
+      bool_as_pointer = reinterpret_cast<void*>(0x1);
+    } else {
+      bool_as_pointer = NULL;
+    }
+    SbThreadSetLocalValue(slot_, bool_as_pointer);
+  }
+
+  bool GetEnabledThreadLocal() const {
+    void* ptr = SbThreadGetLocalValue(slot_);
+    return ptr != NULL;
+  }
+
+  mutable SbThreadLocalKey slot_;
+  bool default_value_;
+};
+
+void InitMemoryTrackingState_ThreadLocal() {
+  GetMemoryTrackingEnabled_ThreadLocal();
+}
+
+static ThreadLocalBool* GetMemoryTrackingState() {
+  static ThreadLocalBool* thread_local_bool = new ThreadLocalBool();
+  return thread_local_bool;
+}
+
+bool GetMemoryTrackingEnabled_ThreadLocal() {
+  return GetMemoryTrackingState()->Enabled();
+}
+
+void SetMemoryTrackingEnabled_ThreadLocal(bool value) {
+  GetMemoryTrackingState()->SetEnabled(value);
+}
+
+// No use for these macros anymore.
+#undef EXPECT_EQ_NO_TRACKING
+#undef EXPECT_TRUE_NO_TRACKING
+#undef EXPECT_FALSE_NO_TRACKING
+#undef ASSERT_EQ_NO_TRACKING
+#undef ASSERT_TRUE_NO_TRACKING
+#undef ASSERT_FALSE_NO_TRACKING
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index cf31d23..dc39b90 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -122,12 +122,12 @@
         'memory_map_test.cc',
         'memory_move_test.cc',
         'memory_reallocate_test.cc',
+        'memory_reporter_test.cc',
         'memory_set_test.cc',
         'microphone_close_test.cc',
         'microphone_create_test.cc',
         'microphone_destroy_test.cc',
         'microphone_get_available_test.cc',
-        'microphone_get_speech_api_key_test.cc',
         'microphone_is_sample_rate_supported_test.cc',
         'microphone_open_test.cc',
         'microphone_read_test.cc',
@@ -163,6 +163,7 @@
         'socket_waiter_wait_test.cc',
         'socket_waiter_wait_timed_test.cc',
         'socket_waiter_wake_up_test.cc',
+        'speech_synthesis_basic_test.cc',
         'storage_close_record_test.cc',
         'storage_delete_record_test.cc',
         'storage_get_record_size_test.cc',
diff --git a/src/starboard/nplb/once_test.cc b/src/starboard/nplb/once_test.cc
index b40817a..a504fc2 100644
--- a/src/starboard/nplb/once_test.cc
+++ b/src/starboard/nplb/once_test.cc
@@ -22,6 +22,7 @@
 

 namespace starboard {

 namespace nplb {

+namespace {

 

 int s_global_value;

 

@@ -149,5 +150,14 @@
   EXPECT_EQ(0, s_global_value);

 }

 

-}  // namespace nplb

-}  // namespace starboard

+SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton);

+TEST(SbOnceTest, InitializeOnceMacroFunction) {

+  int* int_singelton = GetIntSingleton();

+  ASSERT_TRUE(int_singelton);

+  EXPECT_EQ(0, *int_singelton)

+      << "Singleton Macro does not default initialize.";

+}

+

+}  // namespace.

+}  // namespace nplb.

+}  // namespace starboard.

diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index d81c51c..dffa92c 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -44,7 +44,12 @@
   SbPlayer player =

       SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,

                      SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header,

-                     NULL, NULL, NULL, NULL);

+                     NULL, NULL, NULL, NULL

+#if SB_VERSION(3)

+                     ,

+                     NULL

+#endif

+                     );  // NOLINT

   EXPECT_TRUE(SbPlayerIsValid(player));

   SbPlayerDestroy(player);

   SbWindowDestroy(window);

diff --git a/src/starboard/nplb/speech_synthesis_basic_test.cc b/src/starboard/nplb/speech_synthesis_basic_test.cc
new file mode 100644
index 0000000..4e01d35
--- /dev/null
+++ b/src/starboard/nplb/speech_synthesis_basic_test.cc
@@ -0,0 +1,28 @@
+// 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.
+
+#include "starboard/speech_synthesis.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+
+TEST(SbSpeechSynthesisBasicTest, Basic) {
+  // An implementation must at least support US English
+  EXPECT_TRUE(SbSpeechSynthesisSetLanguage("en-US"));
+  SbSpeechSynthesisSpeak("Hello");
+  SbSpeechSynthesisCancel();
+}
+
+#endif  // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+
diff --git a/src/starboard/nplb/system_get_property_test.cc b/src/starboard/nplb/system_get_property_test.cc
index de94895..ef4078f 100644
--- a/src/starboard/nplb/system_get_property_test.cc
+++ b/src/starboard/nplb/system_get_property_test.cc
@@ -70,6 +70,9 @@
   BasicTest(kSbSystemPropertyChipsetModelNumber, false, true, __LINE__);
   BasicTest(kSbSystemPropertyFirmwareVersion, false, true, __LINE__);
   BasicTest(kSbSystemPropertyNetworkOperatorName, false, true, __LINE__);
+#if SB_VERSION(2)
+  BasicTest(kSbSystemPropertySpeechApiKey, false, true, __LINE__);
+#endif  // SB_VERSION(2)
 
   if (IsCEDevice(SbSystemGetDeviceType())) {
     BasicTest(kSbSystemPropertyBrandName, true, true, __LINE__);
diff --git a/src/starboard/once.h b/src/starboard/once.h
index 03039c9..8eba6cb 100644
--- a/src/starboard/once.h
+++ b/src/starboard/once.h
@@ -1,43 +1,75 @@
-// Copyright 2015 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.

-

-// Onces represent initializations that should only ever happen once per

-// process, in a thread safe way.

-

-#ifndef STARBOARD_ONCE_H_

-#define STARBOARD_ONCE_H_

-

-#include "starboard/export.h"

-#include "starboard/thread_types.h"

-#include "starboard/types.h"

-

-#ifdef __cplusplus

-extern "C" {

-#endif

-

-// Function pointer type for methods that can be called va the SbOnce() system.

-typedef void (*SbOnceInitRoutine)(void);

-

-// If SbOnce() was never called before on with |once_control|, this function

-// will run |init_routine| in a thread-safe way and then returns true.  If

-// SbOnce() was called before, the function returns (true) immediately.

-// If |once_control| or |init_routine| are invalid, the function returns false.

-SB_EXPORT bool SbOnce(SbOnceControl* once_control,

-                      SbOnceInitRoutine init_routine);

-

-#ifdef __cplusplus

-}

-#endif

-

-#endif  // STARBOARD_ONCE_H_

+// Copyright 2015 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.
+
+// Module Overview: Starboard Once module
+//
+// Onces represent initializations that should only ever happen once per
+// process, in a thread-safe way.
+
+#ifndef STARBOARD_ONCE_H_
+#define STARBOARD_ONCE_H_
+
+#include "starboard/export.h"
+#include "starboard/thread_types.h"
+#include "starboard/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Function pointer type for methods that can be called via the SbOnce() system.
+typedef void (*SbOnceInitRoutine)(void);
+
+// Thread-safely runs |init_routine| only once.
+// - If this |once_control| has not run a function yet, this function runs
+//   |init_routine| in a thread-safe way and then returns |true|.
+// - If SbOnce() was called with |once_control| before, the function returns
+//   |true| immediately.
+// - If |once_control| or |init_routine| is invalid, the function returns
+//   |false|.
+SB_EXPORT bool SbOnce(SbOnceControl* once_control,
+                      SbOnceInitRoutine init_routine);
+
+#ifdef __cplusplus
+// Defines a function that will initialize the indicated type once and return
+// it. This initialization is thread safe if the type being created is side
+// effect free.
+//
+// These macros CAN ONLY BE USED IN A CC file, never in a header file.
+//
+// Example (in cc file):
+//   SB_ONCE_INITIALIZE_FUNCTION(MyClass, GetOrCreateMyClass)
+//   ..
+//   MyClass* instance = GetOrCreateMyClass();
+//   MyClass* instance2 = GetOrCreateMyClass();
+//   DCHECK_EQ(instance, instance2);
+#define SB_ONCE_INITIALIZE_FUNCTION(Type, FunctionName)    \
+Type* FunctionName() {                                     \
+  static SbOnceControl s_once_flag = SB_ONCE_INITIALIZER;  \
+  static Type* s_singleton = NULL;                         \
+  struct Local {                                           \
+    static void Init() {                                   \
+      s_singleton = new Type();                            \
+    }                                                      \
+  };                                                       \
+  SbOnce(&s_once_flag, Local::Init);                       \
+  return s_singleton;                                      \
+}
+#endif  // __cplusplus
+
+#ifdef __cplusplus