Import Cobalt 10.49488
diff --git a/src/base/base_paths_starboard.cc b/src/base/base_paths_starboard.cc
index 15e7b20..6021b36 100644
--- a/src/base/base_paths_starboard.cc
+++ b/src/base/base_paths_starboard.cc
@@ -22,7 +22,7 @@
 // This is where we can control the path for placement of a lot of file
 // resources for cobalt.
 bool PathProviderStarboard(int key, FilePath *result) {
-  char path[SB_FILE_MAX_PATH];
+  char path[SB_FILE_MAX_PATH] = {0};
   switch (key) {
     case base::FILE_EXE: {
       bool success = SbSystemGetPath(kSbSystemPathExecutableFile, path,
diff --git a/src/base/debug/stack_trace_starboard.cc b/src/base/debug/stack_trace_starboard.cc
index 163105c..2569e4d 100644
--- a/src/base/debug/stack_trace_starboard.cc
+++ b/src/base/debug/stack_trace_starboard.cc
@@ -14,6 +14,7 @@
 
 #include "base/debug/stack_trace.h"
 
+#include <algorithm>
 #include <ostream>
 
 #include "base/basictypes.h"
diff --git a/src/base/file_path.cc b/src/base/file_path.cc
index acada54..4715417 100644
--- a/src/base/file_path.cc
+++ b/src/base/file_path.cc
@@ -1214,7 +1214,7 @@
 // TODO(rolandsteiner) check if this is sufficient/correct.
 int FilePath::CompareIgnoreCase(const StringType& string1,
                                 const StringType& string2) {
-  int comparison = strcasecmp(string1.c_str(), string2.c_str());
+  int comparison = base::strcasecmp(string1.c_str(), string2.c_str());
   if (comparison < 0)
     return -1;
   if (comparison > 0)
diff --git a/src/base/memory/aligned_memory.cc b/src/base/memory/aligned_memory.cc
index 6b0ed98..cad5f50 100644
--- a/src/base/memory/aligned_memory.cc
+++ b/src/base/memory/aligned_memory.cc
@@ -5,6 +5,7 @@
 #include "base/memory/aligned_memory.h"
 
 #include "base/logging.h"
+#include "starboard/memory.h"
 
 #if defined(OS_ANDROID) || defined(OS_NACL) || defined(__LB_SHELL__)
 #include <malloc.h>
diff --git a/src/base/message_loop.cc b/src/base/message_loop.cc
index 83d2184..1d07390 100644
--- a/src/base/message_loop.cc
+++ b/src/base/message_loop.cc
@@ -501,6 +501,20 @@
 
   FOR_EACH_OBSERVER(TaskObserver, task_observers_,
                     WillProcessTask(pending_task.time_posted));
+
+#ifdef STARBOARD_ALLOWS_MEMORY_TRACKING
+  // To assist in tracking memory for async jobs the information on where
+  // the async job came from is used.
+  const char* file_name = pending_task.posted_from.file_name();
+  const int line_num = pending_task.posted_from.line_number();
+  const char* function_name = pending_task.posted_from.function_name();
+
+  // The impl versions of these macros are not compiled out. Therefore the
+  // entire statement is wrapped up in an #ifdef.
+  TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2("MessageLoop", file_name, line_num,
+                                        function_name);
+#endif
+
   pending_task.task.Run();
   FOR_EACH_OBSERVER(TaskObserver, task_observers_,
                     DidProcessTask(pending_task.time_posted));
@@ -676,7 +690,7 @@
 }
 
 bool MessageLoop::DoWork() {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   if (!nestable_tasks_allowed_) {
     // Task can't be executed right now.
     return false;
diff --git a/src/base/message_pump_default.cc b/src/base/message_pump_default.cc
index ba6416e..812ea08 100644
--- a/src/base/message_pump_default.cc
+++ b/src/base/message_pump_default.cc
@@ -20,7 +20,7 @@
 }
 
 void MessagePumpDefault::Run(Delegate* delegate) {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   DCHECK(keep_running_) << "Quit must have been called outside of Run!";
 
   for (;;) {
diff --git a/src/base/message_pump_io_starboard.cc b/src/base/message_pump_io_starboard.cc
index e08ddb8..2ef146d 100644
--- a/src/base/message_pump_io_starboard.cc
+++ b/src/base/message_pump_io_starboard.cc
@@ -165,7 +165,7 @@
 
 // Reentrant!
 void MessagePumpIOStarboard::Run(Delegate* delegate) {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   DCHECK(keep_running_) << "Quit must have been called outside of Run!";
   AutoReset<bool> auto_reset_in_run(&in_run_, true);
 
diff --git a/src/base/message_pump_ui_starboard.cc b/src/base/message_pump_ui_starboard.cc
index a99be50..b3c696f 100644
--- a/src/base/message_pump_ui_starboard.cc
+++ b/src/base/message_pump_ui_starboard.cc
@@ -105,7 +105,7 @@
 
 void MessagePumpUIStarboard::ScheduleDelayedWork(
     const base::TimeTicks& delayed_work_time) {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   base::TimeDelta delay;
   if (!delayed_work_time.is_null()) {
     delay = delayed_work_time - base::TimeTicks::Now();
diff --git a/src/base/process_util.h b/src/base/process_util.h
index 38fbe1f..8e21ec5 100644
--- a/src/base/process_util.h
+++ b/src/base/process_util.h
@@ -237,6 +237,16 @@
 
 // Options for launching a subprocess that are passed to LaunchProcess().
 // The default constructor constructs the object with default options.
+#if defined(OS_STARBOARD)
+struct LaunchOptions {
+  LaunchOptions()
+      : wait(false),
+        fds_to_remap(NULL),
+        maximize_rlimits(NULL),
+        new_process_group(false) {
+    environ = NULL;
+  }
+#else
 struct LaunchOptions {
   LaunchOptions() : wait(false),
 #if defined(OS_WIN)
@@ -257,6 +267,7 @@
 #endif  // defined(OS_MACOSX)
 #endif  // !defined(OS_WIN)
       {}
+#endif  // defined(OS_STARBOARD)
 
   // If true, wait for the process to complete.
   bool wait;
diff --git a/src/base/sys_byteorder.h b/src/base/sys_byteorder.h
index 5548347..b458974 100644
--- a/src/base/sys_byteorder.h
+++ b/src/base/sys_byteorder.h
@@ -14,7 +14,9 @@
 #include "base/basictypes.h"
 #include "build/build_config.h"
 
-#if defined(OS_WIN)
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/poem/inet_poem.h"
+#elif defined(OS_WIN)
 #include <winsock2.h>
 #else
 #include <arpa/inet.h>
diff --git a/src/base/test/test_suite.cc b/src/base/test/test_suite.cc
index 6e0444d..40d4071 100644
--- a/src/base/test/test_suite.cc
+++ b/src/base/test/test_suite.cc
@@ -32,6 +32,10 @@
 #endif  // OS_IOS
 #endif  // OS_MACOSX
 
+#if defined(OS_STARBOARD)
+#include "starboard/system.h"
+#endif
+
 #if defined(OS_ANDROID) || defined(__LB_ANDROID__)
 #include "base/test/test_support_android.h"
 #endif
@@ -99,6 +103,11 @@
 #if defined(OS_WIN)
   testing::GTEST_FLAG(catch_exceptions) = false;
 #endif
+
+#if defined(OS_STARBOARD)
+  testing::GTEST_FLAG(break_on_failure) = SbSystemIsDebuggerAttached();
+#endif
+
   base::EnableTerminationOnHeapCorruption();
   initialized_command_line_ = CommandLine::Init(argc, argv);
   testing::InitGoogleTest(&argc, argv);
diff --git a/src/base/timer.cc b/src/base/timer.cc
index 7c9f26c..db670f1 100644
--- a/src/base/timer.cc
+++ b/src/base/timer.cc
@@ -32,7 +32,7 @@
   }
 
   void Run() {
-    TRACK_MEMORY_SCOPE("TaskProcessor");
+    TRACK_MEMORY_SCOPE("MessageLoop");
     // timer_ is NULL if we were abandoned.
     if (!timer_)
       return;
@@ -144,7 +144,7 @@
 // PostNewScheduledTask() to ensure that the default behavior of Timer is
 // exactly the same as before.
 void Timer::PostNewScheduledTask(TimeDelta delay) {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   DCHECK(scheduled_task_ == NULL);
   is_running_ = true;
   scheduled_task_ = new BaseTimerTaskInternal(this);
@@ -160,7 +160,7 @@
 
 Timer::NewScheduledTaskInfo Timer::SetupNewScheduledTask(
     TimeDelta expected_delay) {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   DCHECK(scheduled_task_ == NULL);
   DCHECK(is_task_run_before_scheduling_next_);
   DCHECK(thread_id_);
@@ -180,7 +180,7 @@
 
 void Timer::PostNewScheduledTask(NewScheduledTaskInfo task_info,
                                  TimeDelta delay) {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   // Some task runners expect a non-zero delay for PostDelayedTask.
   if (delay.ToInternalValue() == 0) {
     ThreadTaskRunnerHandle::Get()->PostTask(task_info.posted_from,
@@ -201,7 +201,7 @@
 }
 
 void Timer::RunScheduledTask() {
-  TRACK_MEMORY_SCOPE("TaskProcessor");
+  TRACK_MEMORY_SCOPE("MessageLoop");
   // Task may have been disabled.
   if (!is_running_)
     return;
diff --git a/src/build/common.gypi b/src/build/common.gypi
index a68007b..0164104 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -965,7 +965,7 @@
     'sas_dll_exists': '<!(python <(DEPTH)/build/dir_exists.py <(sas_dll_path))',
     'wix_exists': '<!(python <(DEPTH)/build/dir_exists.py <(wix_path))',
 
-    'windows_sdk_default_path': '<(DEPTH)/third_party/platformsdk_win8/files',
+    'windows_sdk_path%': 'C:/Program Files (x86)/Windows Kits/10',
     'directx_sdk_default_path': '<(DEPTH)/third_party/directxsdk/files',
 
     # Whether we are using the rlz library or not.  Platforms like Android send
@@ -973,11 +973,6 @@
     'enable_rlz%': 0,
 
     'conditions': [
-      ['OS=="win" and "<!(python <(DEPTH)/build/dir_exists.py <(windows_sdk_default_path))"=="True"', {
-        'windows_sdk_path%': '<(windows_sdk_default_path)',
-      }, {
-        'windows_sdk_path%': 'C:/Program Files (x86)/Windows Kits/8.0',
-      }],
       ['OS=="win" and "<!(python <(DEPTH)/build/dir_exists.py <(directx_sdk_default_path))"=="True"', {
         'directx_sdk_path%': '<(directx_sdk_default_path)',
       }, {
@@ -2007,7 +2002,7 @@
             ],
           }],
           # TODO(darin): Unfortunately, some third_party code depends on base.
-          [ '(OS=="win" or target_arch=="xb1") and component=="shared_library"', {
+          [ 'target_os=="win" and component=="shared_library"', {
             'msvs_disabled_warnings': [
               4251,  # class 'std::xx' needs to have dll-interface.
             ],
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper.cc b/src/cobalt/accessibility/internal/text_alternative_helper.cc
index c69cabd..8c803da 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper.cc
+++ b/src/cobalt/accessibility/internal/text_alternative_helper.cc
@@ -247,7 +247,8 @@
   // https://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
   // The only element type that supports the "alt" attribute that Cobalt
   // implements is the <img> element.
-  if (element->tag_name() == dom::HTMLImageElement::kTagName) {
+  if (element->AsHTMLElement() &&
+      element->AsHTMLElement()->AsHTMLImageElement()) {
     base::optional<std::string> alt_attribute =
         element->GetAttribute(base::Tokens::alt().c_str());
     if (alt_attribute) {
diff --git a/src/cobalt/accessibility/screen_reader.cc b/src/cobalt/accessibility/screen_reader.cc
index 13a554e..54a8d8c 100644
--- a/src/cobalt/accessibility/screen_reader.cc
+++ b/src/cobalt/accessibility/screen_reader.cc
@@ -34,7 +34,7 @@
 
 ScreenReader::ScreenReader(dom::Document* document, TTSEngine* tts_engine,
                            dom::MutationObserverTaskManager* task_manager)
-    : document_(document), tts_engine_(tts_engine) {
+    : enabled_(true), document_(document), tts_engine_(tts_engine) {
   document_->AddObserver(this);
   live_region_observer_ = new dom::MutationObserver(
       base::Bind(&ScreenReader::MutationObserverCallback,
@@ -47,6 +47,10 @@
   document_->RemoveObserver(this);
 }
 
+void ScreenReader::set_enabled(bool value) {
+  enabled_ = value;
+}
+
 void ScreenReader::OnLoad() {
   dom::MutationObserverInit init;
   init.set_subtree(true);
@@ -60,7 +64,9 @@
 }
 
 void ScreenReader::OnFocusChanged() {
-  // TODO: Only utter text if text-to-speech is enabled.
+  if (!enabled_) {
+    return;
+  }
   scoped_refptr<dom::Element> element = document_->active_element();
   if (element) {
     std::string text_alternative = ComputeTextAlternative(element);
@@ -74,6 +80,9 @@
     const MutationRecordSequence& sequence,
     const scoped_refptr<dom::MutationObserver>& observer) {
   // TODO: Only check live regions if text-to-speech is enabled.
+  if (!enabled_) {
+    return;
+  }
   DCHECK_EQ(observer, live_region_observer_);
   for (size_t i = 0; i < sequence.size(); ++i) {
     scoped_refptr<dom::MutationRecord> record = sequence.at(i);
diff --git a/src/cobalt/accessibility/screen_reader.h b/src/cobalt/accessibility/screen_reader.h
index 7f740ad..846d50f 100644
--- a/src/cobalt/accessibility/screen_reader.h
+++ b/src/cobalt/accessibility/screen_reader.h
@@ -34,6 +34,9 @@
   ScreenReader(dom::Document* document, TTSEngine* tts_engine,
                dom::MutationObserverTaskManager* task_manager);
 
+  void set_enabled(bool enabled);
+  bool enabled() const { return enabled_; }
+
  protected:
   ~ScreenReader();
   // dom::DocumentObserver overrides.
@@ -49,6 +52,7 @@
       const MutationRecordSequence& sequence,
       const scoped_refptr<dom::MutationObserver>& observer);
 
+  bool enabled_;
   dom::Document* document_;
   TTSEngine* tts_engine_;
   scoped_refptr<dom::MutationObserver> live_region_observer_;
diff --git a/src/cobalt/accessibility/screen_reader_tests.cc b/src/cobalt/accessibility/screen_reader_tests.cc
index 12ffb8d..64a0893 100644
--- a/src/cobalt/accessibility/screen_reader_tests.cc
+++ b/src/cobalt/accessibility/screen_reader_tests.cc
@@ -21,6 +21,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/path_service.h"
+#include "cobalt/accessibility/screen_reader.h"
 #include "cobalt/accessibility/text_alternative.h"
 #include "cobalt/accessibility/tts_engine.h"
 #include "cobalt/browser/web_module.h"
@@ -36,15 +37,20 @@
 namespace {
 struct TestInfo {
   TestInfo(const std::string& html_file_name,
-           const std::string& expected_result)
-      : html_file_name(html_file_name), expected_result(expected_result) {}
+           const std::string& expected_result,
+           bool screen_reader_enabled)
+      : html_file_name(html_file_name),
+        expected_result(expected_result),
+        screen_reader_enabled(screen_reader_enabled) {}
   std::string html_file_name;
   std::string expected_result;
+  bool screen_reader_enabled;
 };
 
 // Enumerate the *.html files in the accessibility_test directory and build
 // a list of input .html files and expected results.
-std::vector<TestInfo> EnumerateTests(const std::string& subdir) {
+std::vector<TestInfo> EnumerateTests(bool screen_reader_enabled,
+                                     const std::string& subdir) {
   std::vector<TestInfo> infos;
   FilePath root_directory;
   PathService::Get(base::DIR_SOURCE_ROOT, &root_directory);
@@ -65,7 +71,9 @@
         continue;
       }
       TrimWhitespaceASCII(results, TRIM_ALL, &results);
-      infos.push_back(TestInfo(html_file.BaseName().value(), results));
+      infos.push_back(TestInfo(html_file.BaseName().value(),
+                               results,
+                               screen_reader_enabled));
     }
   }
   return infos;
@@ -89,13 +97,29 @@
     DLOG(ERROR) << error;
     Quit();
   }
-  void Quit() { quit_event_.Signal(); }
+  void Quit() {
+    quit_event_.Signal();
+    screen_reader_.reset();
+  }
+
+  scoped_refptr<script::Wrappable> CreateWindowAttribute(
+      const scoped_refptr<dom::Window>& window,
+      dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+    screen_reader_.reset(new accessibility::ScreenReader(
+        window->document(), &tts_engine_, mutation_observer_task_manager));
+    screen_reader_->set_enabled(GetParam().screen_reader_enabled);
+    EXPECT_EQ(GetParam().screen_reader_enabled, screen_reader_->enabled());
+
+    return NULL;
+  }
+
   static void OnRenderTreeProducedStub(
       const browser::WebModule::LayoutResults&) {}
 
  protected:
   MockTTSEngine tts_engine_;
   base::WaitableEvent quit_event_;
+  scoped_ptr<accessibility::ScreenReader> screen_reader_;
 };
 }  // namespace
 
@@ -122,12 +146,13 @@
 
   // Use test runner mode to allow the content itself to dictate when it is
   // ready for layout should be performed.  See cobalt/dom/test_runner.h.
-  browser::WebModule::Options web_module_options(kDefaultViewportSize);
-  web_module_options.tts_engine = &tts_engine_;
+  browser::WebModule::Options web_module_options;
+  web_module_options.injected_window_attributes["test"] = base::Bind(
+      &LiveRegionMutationTest::CreateWindowAttribute, base::Unretained(this));
 
   // Set expected result from mutation.
   std::string expected_speech = GetParam().expected_result;
-  if (expected_speech.empty()) {
+  if (expected_speech.empty() || !GetParam().screen_reader_enabled) {
     EXPECT_CALL(tts_engine_, Speak(_)).Times(0);
   } else {
     EXPECT_CALL(tts_engine_, Speak(expected_speech));
@@ -139,6 +164,7 @@
       url, base::Bind(&LiveRegionMutationTest::OnRenderTreeProducedStub),
       base::Bind(&LiveRegionMutationTest::OnError, base::Unretained(this)),
       base::Bind(&LiveRegionMutationTest::Quit, base::Unretained(this)),
+      base::Closure(), /* window_minimize_callback */
       NULL /* media_module */, &network_module, kDefaultViewportSize,
       &resource_provider, NULL /* system_window */, kRefreshRate,
       web_module_options);
@@ -149,9 +175,14 @@
 
 INSTANTIATE_TEST_CASE_P(
     TextAlternativeTest, TextAlternativeTest,
-    ::testing::ValuesIn(EnumerateTests("text_alternative")));
+    ::testing::ValuesIn(EnumerateTests(true, "text_alternative")));
 
-INSTANTIATE_TEST_CASE_P(LiveRegionMutationTest, LiveRegionMutationTest,
-                        ::testing::ValuesIn(EnumerateTests("live_region")));
+INSTANTIATE_TEST_CASE_P(
+    LiveRegionMutationTestEnabled, LiveRegionMutationTest,
+    ::testing::ValuesIn(EnumerateTests(true, "live_region")));
+
+INSTANTIATE_TEST_CASE_P(
+    LiveRegionMutationTestDisabled, LiveRegionMutationTest,
+    ::testing::ValuesIn(EnumerateTests(false, "live_region")));
 }  // namespace accessibility
 }  // namespace cobalt
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index 23be191..e55e00b 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -47,9 +47,16 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
       ],
+      'export_dependent_settings': [
+        # Additionally, ensure that the include directories for generated
+        # headers are put on the include directories for targets that depend
+        # on this one.
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+      ]
     },
 
     {
@@ -63,6 +70,10 @@
         '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
+
+        # TODO: Remove the dependency below, it works around the fact that
+        #       ScriptValueFactory has non-virtual method CreatePromise().
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
       ],
     },
 
diff --git a/src/cobalt/audio/audio_node.cc b/src/cobalt/audio/audio_node.cc
index 961b862..1f561ee 100644
--- a/src/cobalt/audio/audio_node.cc
+++ b/src/cobalt/audio/audio_node.cc
@@ -24,8 +24,8 @@
     : audio_context_(context),
       audio_lock_(context->audio_lock()),
       channel_count_(2),
-      channel_count_mode_(AudioNode::kMax),
-      channel_interpretation_(AudioNode::kSpeakers) {}
+      channel_count_mode_(kAudioNodeChannelCountModeMax),
+      channel_interpretation_(kAudioNodeChannelInterpretationSpeakers) {}
 
 AudioNode::~AudioNode() {
   AudioLock::AutoLock lock(audio_lock());
@@ -56,14 +56,14 @@
 }
 
 void AudioNode::set_channel_count_mode(
-    const ChannelCountMode& channel_count_mode) {
+    const AudioNodeChannelCountMode& channel_count_mode) {
   AudioLock::AutoLock lock(audio_lock());
 
   channel_count_mode_ = channel_count_mode;
 }
 
 void AudioNode::set_channel_interpretation(
-    const ChannelInterpretation& channel_interpretation) {
+    const AudioNodeChannelInterpretation& channel_interpretation) {
   AudioLock::AutoLock lock(audio_lock());
 
   channel_interpretation_ = channel_interpretation;
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 9b2d3ed..b956d30 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -19,6 +19,8 @@
 #include <vector>
 
 #include "cobalt/audio/audio_helpers.h"
+#include "cobalt/audio/audio_node_channel_count_mode.h"
+#include "cobalt/audio/audio_node_channel_interpretation.h"
 #include "cobalt/audio/audio_node_input.h"
 #include "cobalt/audio/audio_node_output.h"
 #include "cobalt/dom/dom_exception.h"
@@ -55,17 +57,6 @@
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
  public:
-  enum ChannelCountMode {
-    kMax,
-    kClampedMax,
-    kExplicit,
-  };
-
-  enum ChannelInterpretation {
-    kSpeakers,
-    kDiscrete,
-  };
-
   explicit AudioNode(AudioContext* context);
 
   // Web API: AudioNode
@@ -97,19 +88,20 @@
   // Determines how channels will be counted when up-mixing and down-mixing
   // connections to any inputs to the node. This attributes has no effect for
   // nodes with no inputs.
-  const ChannelCountMode& channel_count_mode() const {
+  const AudioNodeChannelCountMode& channel_count_mode() const {
     return channel_count_mode_;
   }
-  void set_channel_count_mode(const ChannelCountMode& channel_count_mode);
+  void set_channel_count_mode(
+      const AudioNodeChannelCountMode& channel_count_mode);
 
   // Determines how individual channels will be treated when up-mixing and
   // down-mixing connections to any inputs to the node. This attribute has no
   // effect for nodes with no inputs.
-  const ChannelInterpretation& channel_interpretation() const {
+  const AudioNodeChannelInterpretation& channel_interpretation() const {
     return channel_interpretation_;
   }
   void set_channel_interpretation(
-      const ChannelInterpretation& channel_interpretation);
+      const AudioNodeChannelInterpretation& channel_interpretation);
 
   // Connects the AudioNode to another AudioNode.
   void Connect(const scoped_refptr<AudioNode>& destination, uint32 output,
@@ -156,8 +148,8 @@
 
   // Channel up-mixing and down-mixing rules for all inputs.
   uint32 channel_count_;
-  ChannelCountMode channel_count_mode_;
-  ChannelInterpretation channel_interpretation_;
+  AudioNodeChannelCountMode channel_count_mode_;
+  AudioNodeChannelInterpretation channel_interpretation_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioNode);
 };
diff --git a/src/cobalt/audio/audio_node.idl b/src/cobalt/audio/audio_node.idl
index afbb730..ce58946 100644
--- a/src/cobalt/audio/audio_node.idl
+++ b/src/cobalt/audio/audio_node.idl
@@ -14,17 +14,6 @@
 
 // https://www.w3.org/TR/webaudio/#AudioNode-section
 
-enum ChannelCountMode {
-  "max",
-  "clamped-max",
-  "explicit"
-};
-
-enum ChannelInterpretation {
-  "speakers",
-  "discrete"
-};
-
 interface AudioNode : EventTarget {
   [RaisesException] void connect(AudioNode destination,
                                  optional unsigned long output = 0,
@@ -37,6 +26,6 @@
 
   // Channel up-mixing and down-mixing rules for all inputs.
   [RaisesException=Setter] attribute unsigned long channelCount;
-  attribute ChannelCountMode channelCountMode;
-  attribute ChannelInterpretation channelInterpretation;
+  attribute AudioNodeChannelCountMode channelCountMode;
+  attribute AudioNodeChannelInterpretation channelInterpretation;
 };
diff --git a/src/cobalt/base/math.cc b/src/cobalt/audio/audio_node_channel_count_mode.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/audio/audio_node_channel_count_mode.idl
index d335495..bb5d8c8 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/audio/audio_node_channel_count_mode.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/webaudio/#AudioNode-section
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum AudioNodeChannelCountMode {
+  "max",
+  "clamped-max",
+  "explicit"
+};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/audio/audio_node_channel_interpretation.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/audio/audio_node_channel_interpretation.idl
index d335495..52e61f4 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/audio/audio_node_channel_interpretation.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/webaudio/#AudioNode-section
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum AudioNodeChannelInterpretation {
+  "speakers",
+  "discrete"
+};
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index 14bfe33..2e76dcf 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -32,10 +32,11 @@
 
 void MixAudioBufferBasedOnInterpretation(
     const float* speaker, const float* discrete,
-    const AudioNode::ChannelInterpretation& interpretation,
-    ShellAudioBus* source, ShellAudioBus* output_audio_data) {
+    const AudioNodeChannelInterpretation& interpretation, ShellAudioBus* source,
+    ShellAudioBus* output_audio_data) {
   const float* kMatrix =
-      interpretation == AudioNode::kSpeakers ? speaker : discrete;
+      interpretation == kAudioNodeChannelInterpretationSpeakers ? speaker
+                                                                : discrete;
   size_t array_size = source->channels() * output_audio_data->channels();
   std::vector<float> matrix(kMatrix, kMatrix + array_size);
   output_audio_data->Mix(*source, matrix);
@@ -49,12 +50,12 @@
 // "discrete".
 // Up down mix equations for mono, stereo, quad, 5.1:
 //   https://www.w3.org/TR/webaudio/#ChannelLayouts
-void MixAudioBuffer(const AudioNode::ChannelInterpretation& interpretation,
+void MixAudioBuffer(const AudioNodeChannelInterpretation& interpretation,
                     ShellAudioBus* source, ShellAudioBus* output_audio_data) {
   DCHECK_GT(source->channels(), 0u);
   DCHECK_GT(output_audio_data->channels(), 0u);
-  DCHECK(interpretation == AudioNode::kSpeakers ||
-         interpretation == AudioNode::kDiscrete)
+  DCHECK(interpretation == kAudioNodeChannelInterpretationSpeakers ||
+         interpretation == kAudioNodeChannelInterpretationDiscrete)
       << interpretation;
 
   if (output_audio_data->channels() == source->channels()) {
@@ -235,7 +236,7 @@
   // TODO: Consider computing computedNumberOfChannels and do up-mix or
   // down-mix base on computedNumberOfChannels. The current implementation
   // is based on the fact that the channelCountMode is max.
-  if (owner_node_->channel_count_mode() != AudioNode::kMax) {
+  if (owner_node_->channel_count_mode() != kAudioNodeChannelCountModeMax) {
     DLOG(ERROR) << "Unsupported channel count mode: "
                 << owner_node_->channel_count_mode();
     return;
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index 50a8bac..8a69ac3 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -59,7 +59,7 @@
 void FillAudioBusFromOneSource(
     size_t num_of_src_channel, size_t num_of_frames,
     scoped_array<uint8> src_data,
-    const AudioNode::ChannelInterpretation& interpretation,
+    const AudioNodeChannelInterpretation& interpretation,
     ShellAudioBus* audio_bus, bool* silence) {
   scoped_refptr<AudioBufferSourceNode> source(new AudioBufferSourceNode(NULL));
   scoped_refptr<AudioBuffer> buffer(
@@ -81,7 +81,8 @@
   size_t num_of_src_channel = 2;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[50];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -122,7 +123,8 @@
   size_t num_of_src_channel = 2;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[50];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -163,7 +165,8 @@
   size_t num_of_src_channel = 1;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[25];
   for (size_t i = 0; i < num_of_frames; ++i) {
@@ -198,7 +201,8 @@
   size_t num_of_src_channel = 1;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[25];
   for (size_t i = 0; i < num_of_frames; ++i) {
@@ -233,7 +237,8 @@
   size_t num_of_src_channel = 4;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[100];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -274,7 +279,8 @@
   size_t num_of_src_channel = 4;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[100];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -315,7 +321,8 @@
   size_t num_of_src_channel = 6;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 10;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[60];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -356,7 +363,8 @@
   size_t num_of_src_channel = 6;
   size_t num_of_dest_channel = 2;
   size_t num_of_frames = 10;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[60];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -397,7 +405,8 @@
   size_t num_of_src_channel = 2;
   size_t num_of_dest_channel = 1;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[50];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -434,7 +443,8 @@
   size_t num_of_src_channel = 2;
   size_t num_of_dest_channel = 1;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[50];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -471,7 +481,8 @@
   size_t num_of_src_channel = 4;
   size_t num_of_dest_channel = 1;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[100];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -508,7 +519,8 @@
   size_t num_of_src_channel = 4;
   size_t num_of_dest_channel = 1;
   size_t num_of_frames = 25;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[100];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -545,7 +557,8 @@
   size_t num_of_src_channel = 6;
   size_t num_of_dest_channel = 1;
   size_t num_of_frames = 10;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   float src_data_in_float[60];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -582,7 +595,8 @@
   size_t num_of_src_channel = 6;
   size_t num_of_dest_channel = 1;
   size_t num_of_frames = 10;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationDiscrete;
 
   float src_data_in_float[60];
   for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -618,7 +632,8 @@
 TEST(AudioNodeInputOutputTest, MultipleInputNodesLayoutTest) {
   size_t num_of_src_channel = 2;
   size_t num_of_dest_channel = 2;
-  AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+  AudioNodeChannelInterpretation interpretation =
+      kAudioNodeChannelInterpretationSpeakers;
 
   size_t num_of_frames_1 = 25;
   float src_data_in_float_1[50];
diff --git a/src/cobalt/base/accessibility_settings_changed_event.h b/src/cobalt/base/accessibility_settings_changed_event.h
new file mode 100644
index 0000000..c0380b1
--- /dev/null
+++ b/src/cobalt/base/accessibility_settings_changed_event.h
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_ACCESSIBILITY_SETTINGS_CHANGED_EVENT_H_
+#define COBALT_BASE_ACCESSIBILITY_SETTINGS_CHANGED_EVENT_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/string_util.h"
+#include "cobalt/base/event.h"
+
+namespace base {
+
+class AccessibilitySettingsChangedEvent : public Event {
+ public:
+  AccessibilitySettingsChangedEvent() {}
+
+  BASE_EVENT_SUBCLASS(AccessibilitySettingsChangedEvent);
+};
+
+}  // namespace base
+
+#endif  // COBALT_BASE_ACCESSIBILITY_SETTINGS_CHANGED_EVENT_H_
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index d453855..629d727 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -21,6 +21,7 @@
       'product_name': 'cobalt_base',
       'type': 'static_library',
       'sources': [
+        'accessibility_changed_event.h',
         'address_sanitizer.h',
         'clock.h',
         'cobalt_paths.h',
@@ -47,7 +48,6 @@
         'localized_strings.h',
         'log_message_handler.cc',
         'log_message_handler.h',
-        'math.cc',
         'math.h',
         'message_queue.h',
         'path_provider.cc',
@@ -55,6 +55,7 @@
         'poller.h',
         'polymorphic_downcast.h',
         'polymorphic_equatable.h',
+        'ref_counted_lock.h',
         'source_location.h',
         'stop_watch.cc',
         'stop_watch.h',
diff --git a/src/cobalt/base/c_val.cc b/src/cobalt/base/c_val.cc
index cc41ab8..f0c9a84 100644
--- a/src/cobalt/base/c_val.cc
+++ b/src/cobalt/base/c_val.cc
@@ -22,7 +22,7 @@
                    StaticMemorySingletonTraits<CValManager> >::get();
 }
 
-CValManager::CValManager() {
+CValManager::CValManager() : value_lock_refptr_(new RefCountedThreadSafeLock) {
   // Allocate these dynamically since CValManager may live across DLL boundaries
   // and so this allows its size to be more consistent (and avoids compiler
   // warnings on some platforms).
@@ -33,6 +33,15 @@
 }
 
 CValManager::~CValManager() {
+  // Lock the value lock prior to notifying the surviving CVals that the manager
+  // has been destroyed. This ensures that they will not attempt to make calls
+  // into the manager during destruction.
+  base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+  for (NameVarMap::iterator iter = registered_vars_->begin();
+       iter != registered_vars_->end(); ++iter) {
+    iter->second->OnManagerDestroyed();
+  }
+
 #if defined(ENABLE_DEBUG_C_VAL)
   delete on_changed_hook_set_;
 #endif  // ENABLE_DEBUG_C_VAL
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index 829e3d3..48227bf 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -30,6 +30,7 @@
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
 #include "base/time.h"
+#include "cobalt/base/ref_counted_lock.h"
 
 // The CVal system allows you to mark certain variables to be part of the
 // CVal system and therefore analyzable and trackable by other systems.  All
@@ -139,7 +140,6 @@
 }  // namespace cval
 
 namespace CValDetail {
-
 // Introduce a Traits class so that we can convert from C++ type to
 // CValType.
 template <typename T>
@@ -524,8 +524,10 @@
   // Lock that protects against CVals being registered/deregistered.
   base::Lock cvals_lock_;
 
-  // Lock that synchronizes |value_| reads/writes.
-  base::Lock values_lock_;
+  // Lock that synchronizes |value_| reads/writes. It exists as a refptr to
+  // ensure that CVals that survive longer than the CValManager can continue
+  // to use it after the CValManager has been destroyed.
+  scoped_refptr<base::RefCountedThreadSafeLock> value_lock_refptr_;
 
   // The actual value registry, mapping CVal name to actual CVal object.
   typedef base::hash_map<std::string, const CValDetail::CValBase*> NameVarMap;
@@ -559,9 +561,13 @@
   virtual std::string GetValueAsPrettyString() const = 0;
 
  private:
+  virtual void OnManagerDestroyed() const {}
+
   std::string name_;
   std::string description_;
   CValType type_;
+
+  friend CValManager;
 };
 
 // This is a wrapper class that marks that we wish to track a value through
@@ -580,6 +586,7 @@
     CommonConstructor();
   }
   virtual ~CValImpl() {
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
     if (registered_) {
       CValManager::GetInstance()->UnregisterCVal(this);
     }
@@ -588,27 +595,20 @@
   operator T() const { return value(); }
 
   const CValImpl<T>& operator=(const T& rhs) {
-    bool value_changed;
-    {
-      base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
-      value_changed = value_ != rhs;
-      if (value_changed) {
-        value_ = rhs;
-      }
-    }
-#if defined(ENABLE_DEBUG_C_VAL)
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+    bool value_changed = value_ != rhs;
     if (value_changed) {
+      value_ = rhs;
+#if defined(ENABLE_DEBUG_C_VAL)
       OnValueChanged();
-    }
 #endif  // ENABLE_DEBUG_C_VAL
+    }
     return *this;
   }
 
   const CValImpl<T>& operator+=(const T& rhs) {
-    {
-      base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
-      value_ += rhs;
-    }
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+    value_ += rhs;
 #if defined(ENABLE_DEBUG_C_VAL)
     OnValueChanged();
 #endif  // ENABLE_DEBUG_C_VAL
@@ -616,10 +616,8 @@
   }
 
   const CValImpl<T>& operator-=(const T& rhs) {
-    {
-      base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
-      value_ -= rhs;
-    }
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+    value_ -= rhs;
 #if defined(ENABLE_DEBUG_C_VAL)
     OnValueChanged();
 #endif  // ENABLE_DEBUG_C_VAL
@@ -627,10 +625,8 @@
   }
 
   const CValImpl<T>& operator++() {
-    {
-      base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
-      ++value_;
-    }
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+    ++value_;
 #if defined(ENABLE_DEBUG_C_VAL)
     OnValueChanged();
 #endif  // ENABLE_DEBUG_C_VAL
@@ -638,10 +634,8 @@
   }
 
   const CValImpl<T>& operator--() {
-    {
-      base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
-      --value_;
-    }
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+    --value_;
 #if defined(ENABLE_DEBUG_C_VAL)
     OnValueChanged();
 #endif  // ENABLE_DEBUG_C_VAL
@@ -654,19 +648,19 @@
 
   std::string GetValueAsString() const {
     // Can be called to get the value of a CVal without knowing the type first.
-    base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
     return ValToString<T>(value_);
   }
 
   std::string GetValueAsPrettyString() const {
     // Similar to GetValueAsString(), but it will also format the string to
     // do things like make very large numbers more readable.
-    base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
     return ValToPrettyString<T>(value_);
   }
 
   T value() const {
-    base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+    base::AutoLock auto_lock(value_lock_refptr_->GetLock());
     return value_;
   }
 
@@ -678,21 +672,32 @@
 
   void RegisterWithManager() {
     if (!registered_) {
-      CValManager::GetInstance()->RegisterCVal(this);
+      CValManager* manager = CValManager::GetInstance();
+      manager->RegisterCVal(this);
+      value_lock_refptr_ = manager->value_lock_refptr_;
       registered_ = true;
     }
   }
 
+  void OnManagerDestroyed() const {
+    // The value lock is locked by the manager prior to it calling
+    // OnManagerDestroyed  on its surviving CVals.
+    registered_ = false;
+  }
+
 #if defined(ENABLE_DEBUG_C_VAL)
   void OnValueChanged() {
     // Push the value changed event to all listeners.
-    CValManager::GetInstance()->PushValueChangedEvent(
-        this, CValSpecificValue<T>(value_));
+    if (registered_) {
+      CValManager::GetInstance()->PushValueChangedEvent(
+          this, CValSpecificValue<T>(value_));
+    }
   }
 #endif  // ENABLE_DEBUG_C_VAL
 
   T value_;
   mutable bool registered_;
+  mutable scoped_refptr<RefCountedThreadSafeLock> value_lock_refptr_;
 };
 
 #if !defined(ENABLE_DEBUG_C_VAL)
diff --git a/src/cobalt/base/math.h b/src/cobalt/base/math.h
index 7eb1f53..4723bfb 100644
--- a/src/cobalt/base/math.h
+++ b/src/cobalt/base/math.h
@@ -17,11 +17,4 @@
 
 #include <cmath>
 
-#include "build/build_config.h"
-
-// The MSVC compiler doesn't have roundf().
-#if defined(COMPILER_MSVC)
-float roundf(float x);
-#endif  // defined(COMPILER_MSVC)
-
 #endif  // COBALT_BASE_MATH_H_
diff --git a/src/cobalt/base/ref_counted_lock.h b/src/cobalt/base/ref_counted_lock.h
new file mode 100644
index 0000000..ddc77a6
--- /dev/null
+++ b/src/cobalt/base/ref_counted_lock.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_REF_COUNTED_LOCK_H_
+#define COBALT_BASE_REF_COUNTED_LOCK_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+//
+// A non-thread-safe ref-counted wrapper around a lock.
+//
+class RefCountedLock : public base::RefCounted<RefCountedLock> {
+ public:
+  base::Lock& GetLock() { return lock_; }
+
+ private:
+  base::Lock lock_;
+};
+
+//
+// A thread-safe ref-counted wrapper around a lock.
+//
+class RefCountedThreadSafeLock
+    : public base::RefCountedThreadSafe<RefCountedThreadSafeLock> {
+ public:
+  base::Lock& GetLock() { return lock_; }
+
+ private:
+  base::Lock lock_;
+};
+
+}  // namespace base
+
+#endif  // COBALT_BASE_REF_COUNTED_LOCK_H_
diff --git a/src/cobalt/bindings/IDLExtendedAttributes.txt b/src/cobalt/bindings/IDLExtendedAttributes.txt
index e444e91..8b1c115 100644
--- a/src/cobalt/bindings/IDLExtendedAttributes.txt
+++ b/src/cobalt/bindings/IDLExtendedAttributes.txt
@@ -127,3 +127,6 @@
 NewObject
 SameObject
 
+# Allows to specify an alternate method name for an attribute implementation.
+# Can be used to avoid using reserved keywords for method names.
+ImplementedAs=*
diff --git a/src/cobalt/bindings/bindings.gypi b/src/cobalt/bindings/bindings.gypi
index 9ba1577..29c4d39 100644
--- a/src/cobalt/bindings/bindings.gypi
+++ b/src/cobalt/bindings/bindings.gypi
@@ -20,7 +20,8 @@
 # Input variables:
 #   source_idl_files: All IDL files for which a bindings wrapper should be
 #       created.
-#   dictionary_idl_files: All IDL files for IDL dictionaries.
+#   generated_header_idl_files: IDL files for IDL dictionaries and IDL enums. A
+#       header will be generated for these that can be #included in
 #   dependency_idl_files: IDL files that are dependencies of other IDLs, but no
 #       bindings wrapper will be created. For example, partial interfaces and
 #       the right-hand-side of implements statements.
@@ -71,6 +72,7 @@
     'bindings_include_dirs': ['<@(engine_include_dirs)'],
     'bindings_templates_dir': '<(engine_templates_dir)',
     'idl_compiler_script': '<(engine_idl_compiler)',
+    'conversion_header_generator_script': '<(engine_conversion_header_generator_script)',
 
     # Templates that are shared by the code generation for multiple engines.
     'shared_template_files': [
@@ -88,11 +90,10 @@
     # directory of each IDL.
     'source_idl_files': [],
 
-    # A class definition that matches the dictionary definition and is
-    # suitable to be included and used in Cobalt code will be generated, as well
-    # as a conversion function between an instance of this class and a JS
-    # object.
-    'dictionary_idl_files': [],
+    # For each IDL file in this list, a header file that can be #included in
+    # Cobalt will be generated, as well as a .cc file that implements the
+    # conversion to/from a JS value.
+    'generated_header_idl_files': [],
 
     # Partial interfaces and the right-side of "implements"
     # Code will not get generated for these interfaces; they are used to add
@@ -140,6 +141,7 @@
       '<(DEPTH)/cobalt/bindings/expression_generator.py',
       '<(DEPTH)/cobalt/bindings/contexts.py',
       '<(DEPTH)/cobalt/bindings/idl_compiler_cobalt.py',
+      '<(DEPTH)/cobalt/bindings/generate_conversion_header.py',
       '<(DEPTH)/cobalt/bindings/name_conversion.py',
       '<(DEPTH)/cobalt/bindings/overload_context.py',
       '<@(code_generator_template_files)',
@@ -180,6 +182,15 @@
          ' --base_directory <(DEPTH)'
          ' <@(source_idl_files))'],
 
+    # .cc files that implement conversion functions to/from a JS value for
+    # generated types (enums and dictionaries).
+    'generated_type_conversions':
+        ['<!@pymod_do_main(cobalt.build.path_conversion -s'
+         ' --output_directory <(generated_source_output_dir)'
+         ' --output_extension cc --output_prefix <(prefix)_'
+         ' --base_directory <(DEPTH)'
+         ' <@(generated_header_idl_files))'],
+
     # Generated IDL file that will define all the constructors that should be
     # on the Window object
     'global_constructors_generated_idl_file':
@@ -189,6 +200,11 @@
     # there is a header for each IDL.
     'global_constructors_generated_header_file':
         '<(generated_idls_output_dir)/<(window_component)/window_constructors.h',
+
+    # Generated header file that contains forward declarations for converting
+    # to/from JS value and generated types.
+    'generated_type_conversion_header_file':
+        '<(generated_source_output_dir)/<(prefix)_gen_type_conversion.h',
   },
 
   'targets': [
@@ -204,7 +220,8 @@
       'dependencies': [
         'cached_jinja_templates',
         'cached_lex_yacc_tables',
-        'generated_dictionaries',
+        'generated_types',
+        'generated_type_conversion',
         'global_constructors_idls',
         'interfaces_info_overall',
         '<@(bindings_dependencies)',
@@ -245,6 +262,9 @@
           # not in this list is encountered, it will cause an error in the
           # pipeline.
           '<(extended_attributes_file)',
+
+          # Forward declarations of conversion functions for generated types.
+          '<(generated_type_conversion_header_file)',
         ],
         'outputs': [
           '<!@pymod_do_main(cobalt.build.path_conversion -s -p <(prefix)_ '
@@ -278,6 +298,7 @@
         ],
         'sources': [
           '<@(generated_sources)',
+          '<@(generated_type_conversions)',
         ],
         'defines': [ '<@(bindings_defines)'],
       }
@@ -286,7 +307,7 @@
     {
       # Based on the generated_bindings target above. Main difference is that
       # this produces two .h files, and takes the dictionary idl files as input.
-      'target_name': 'generated_dictionaries',
+      'target_name': 'generated_types',
       'type': 'none',
       'hard_dependency': 1,
       'dependencies': [
@@ -296,7 +317,7 @@
         'interfaces_info_overall',
       ],
       'sources': [
-        '<@(dictionary_idl_files)',
+        '<@(generated_header_idl_files)',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
@@ -338,7 +359,7 @@
           '<!@pymod_do_main(cobalt.build.path_conversion -s '
               '-e h -d <(generated_source_output_dir) -b <(DEPTH) <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT))',
           '<!@pymod_do_main(cobalt.build.path_conversion -s -p <(prefix)_ '
-              '-e h -d <(generated_source_output_dir) -b <(DEPTH) <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT))'
+              '-e cc -d <(generated_source_output_dir) -b <(DEPTH) <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT))'
         ],
         'action': [
           'python',
@@ -358,13 +379,83 @@
         'message': 'Generating dictionary from <(RULE_INPUT_PATH)',
       }],
     },
+    {
+      # Create a target that depends on this target to compile the generated
+      # source files. There should be only one such target.
+      # Based on blink/Source/bindings/core/v8/generated.gyp:bindings_core_v8_generated_individual
+      'target_name': 'generated_type_conversion',
+      'type': 'none',
+      # The 'binding' rule generates .h files, so mark as hard_dependency, per:
+      # https://code.google.com/p/gyp/wiki/InputFormatReference#Linking_Dependencies
+      'hard_dependency': 1,
+      'dependencies': [
+        'cached_jinja_templates',
+        'cached_lex_yacc_tables',
+        'global_constructors_idls',
+        'interfaces_info_overall',
+        '<@(bindings_dependencies)',
+      ],
+      'export_dependent_settings': [
+        '<@(bindings_dependencies)',
+      ],
+      'sources': [
+        '<@(source_idl_files)',
+        '<@(generated_header_idl_files)'
+      ],
+      'actions': [{
+        'action_name': 'generate_type_conversion_header',
+        'inputs': [
+          # Script source files, etc.
+          '<@(bindings_extra_inputs)',
+
+          # This file is global, so if it changes all files are rebuilt.
+          # However, this file will only change when dependency structure
+          # changes, so shouldn't change too much.
+          # See blink/Source/bindings/core/v8/generated.gyp for more info
+          '<(interfaces_info_combined_pickle)',
+
+          # Similarly, all files are rebuilt if a partial interface or
+          # right side of 'implements' changes.
+          # See blink/Source/bindings/core/v8/generated.gyp for more info
+          '<@(dependency_idl_files)',
+
+          # Also add as a dependency the set of unsupported IDL files.
+          '<@(unsupported_interface_idl_files)',
+
+          # The generated constructors IDL is also a partial interface, and
+          # we need to rebuild if it is modified.
+          '<(global_constructors_generated_idl_file)',
+
+          # The whitelist of what extended attributes we support. If an attribute
+          # not in this list is encountered, it will cause an error in the
+          # pipeline.
+          '<(extended_attributes_file)',
+        ],
+        'outputs': [
+          '<(generated_type_conversion_header_file)',
+        ],
+        'action': [
+          'python',
+          '<(conversion_header_generator_script)',
+          '--cache-dir',
+          '<(bindings_scripts_output_dir)',
+          '--output-dir',
+          '<(generated_source_output_dir)',
+          '--interfaces-info',
+          '<(interfaces_info_combined_pickle)',
+          '--component-info',
+          '<(component_info_pickle)',
+        ],
+        'message': 'Generating generated type conversion header',
+      }],
+    },
 
     {
       # Based on blink/Source/bindings/core/generated.gyp:core_global_objects
       'target_name': 'global_objects',
       'variables': {
         'idl_files': [
-          '<@(source_idl_files)', '<@(dictionary_idl_files)'
+          '<@(source_idl_files)', '<@(generated_header_idl_files)'
         ],
         'output_file': '<(bindings_scripts_output_dir)/GlobalObjects.pickle',
       },
@@ -405,7 +496,8 @@
       ],
       'variables': {
         'static_idl_files': [
-          '<@(source_idl_files)', '<@(dictionary_idl_files)',
+          '<@(source_idl_files)',
+          '<@(generated_header_idl_files)',
           '<@(dependency_idl_files)',
           '<@(unsupported_interface_idl_files)'],
         'generated_idl_files': ['<(global_constructors_generated_idl_file)'],
diff --git a/src/cobalt/bindings/code_generator_cobalt.py b/src/cobalt/bindings/code_generator_cobalt.py
index e2e6abd..f3a7892 100644
--- a/src/cobalt/bindings/code_generator_cobalt.py
+++ b/src/cobalt/bindings/code_generator_cobalt.py
@@ -135,6 +135,7 @@
       idl_type = idl_type.resolve_typedefs(info_provider.typedefs)
       if isinstance(idl_type, IdlTypedef):
         idl_type = idl_type.idl_type
+
       if idl_type.is_interface_type:
         yield get_interface_name(idl_type)
       if idl_type.is_dictionary:
@@ -143,6 +144,8 @@
         for interface_name in get_interface_type_names_from_idl_types(
             info_provider, idl_type.member_types):
           yield interface_name
+      elif idl_type.is_enum:
+        yield idl_type.name
       elif isinstance(idl_type, IdlSequenceType):
         for interface_name in get_interface_type_names_from_idl_types(
             info_provider, [idl_type.element_type]):
@@ -193,8 +196,7 @@
     self.jinja_env = initialize_jinja_env(cache_dir,
                                           os.path.abspath(templates_dir))
     self.path_builder = path_generator.PathBuilder(
-        self.generated_file_prefix, self.info_provider.interfaces_info,
-        cobalt_dir, output_dir)
+        self.generated_file_prefix, self.info_provider, cobalt_dir, output_dir)
 
   @abc.abstractproperty
   def generated_file_prefix(self):
@@ -220,6 +222,9 @@
       return self.generate_dictionary_code(
           definitions, definition_name,
           definitions.dictionaries[definition_name])
+    if definition_name in definitions.enumerations:
+      return self.generate_enum_code(definitions, definition_name,
+                                     definitions.enumerations[definition_name])
     raise ValueError('%s is not in IDL definitions' % definition_name)
 
   def generate_interface_code(self, definitions, interface_name, interface):
@@ -245,28 +250,103 @@
 
   def generate_dictionary_code(self, definitions, dictionary_name, dictionary):
     header_template_filename = 'dictionary.h.template'
-    conversion_template_filename = 'dictionary-conversion.h.template'
-    template_context = self.build_dictionary_context(dictionary, definitions)
+    conversion_template_filename = 'dictionary-conversion.cc.template'
+    implementation_context = self.build_dictionary_context(
+        dictionary, definitions, False)
+    conversion_context = self.build_dictionary_context(dictionary, definitions,
+                                                       True)
 
     header_text = self.render_template(header_template_filename,
-                                       template_context)
+                                       implementation_context)
     conversion_text = self.render_template(conversion_template_filename,
-                                           template_context)
+                                           conversion_context)
 
     header_path = self.path_builder.DictionaryHeaderFullPath(dictionary_name)
-    conversion_header_path = (
-        self.path_builder.DictionaryConversionHeaderFullPath(dictionary_name))
-    return ((header_path, header_text), (conversion_header_path,
+    conversion_impl_path = (
+        self.path_builder.DictionaryConversionImplementationPath(
+            dictionary_name))
+    return ((header_path, header_text), (conversion_impl_path,
                                          conversion_text),)
 
+  def generate_enum_code(self, definitions, enumeration_name, enumeration):
+    header_template_filename = 'enumeration.h.template'
+    conversion_template_filename = 'enumeration-conversion.cc.template'
+    context_builder = ContextBuilder(self.info_provider)
+    context = context_builder.enumeration_context(enumeration)
+
+    context['components'] = self.path_builder.NamespaceComponents(
+        enumeration_name)
+    context['namespace'] = self.path_builder.Namespace(enumeration_name)
+    context['fully_qualified_name'] = self.path_builder.FullClassName(
+        enumeration_name)
+    context['enum_include'] = self.path_builder.EnumHeaderIncludePath(
+        enumeration_name)
+
+    header_text = self.render_template(header_template_filename, context)
+    conversion_text = self.render_template(conversion_template_filename,
+                                           context)
+
+    header_path = self.path_builder.EnumHeaderFullPath(enumeration_name)
+    conversion_impl_path = (
+        self.path_builder.EnumConversionImplementationFullPath(enumeration_name)
+    )
+    return ((header_path, header_text), (conversion_impl_path,
+                                         conversion_text),)
+
+  def generate_conversion_code(self):
+    enumerations = list(self.info_provider.enumerations.keys())
+    dictionaries = list(self.info_provider.interfaces_info['dictionaries'])
+    includes = [
+        self.path_builder.DictionaryHeaderIncludePath(dictionary)
+        for dictionary in dictionaries
+    ]
+    includes.extend([
+        self.path_builder.EnumHeaderIncludePath(enum) for enum in enumerations
+    ])
+
+    context = {
+        'dictionaries': self.referenced_class_contexts(dictionaries, False),
+        'enumerations': self.referenced_class_contexts(enumerations, False),
+        'includes': includes,
+    }
+
+    header_template_filename = 'generated-types.h.template'
+    header_text = self.render_template(header_template_filename, context)
+
+    return self.path_builder.generated_conversion_header_path, header_text
+
+  def referenced_dictionary_context(self, dictionary_name, dictionary_info):
+    namespace = '::'.join(
+        self.path_builder.NamespaceComponents(dictionary_name))
+
+    return {
+        'fully_qualified_name':
+            '%s::%s' % (namespace, dictionary_name),
+        'include':
+            self.path_builder.DictionaryHeaderIncludePath(dictionary_name),
+        'conditional':
+            dictionary_info['conditional'],
+        'is_callback_interface':
+            False,
+    }
+
+  def referenced_enum_context(self, enum_name):
+    namespace = '::'.join(self.path_builder.NamespaceComponents(enum_name))
+    return {
+        'fully_qualified_name': '%s::%s' % (namespace, enum_name),
+        'include': self.path_builder.EnumHeaderIncludePath(enum_name),
+        'conditional': None,
+        'is_callback_interface': False,
+    }
+
   def referenced_class_contexts(self,
                                 interface_names,
-                                implementation_only=False):
+                                include_bindings_class=True):
     """Returns a list of jinja contexts describing referenced C++ classes.
 
     Args:
       interface_names: A list of interfaces.
-      implementation_only: Only include implemetation headers.
+      include_bindings_class: Include headers and classes uses only in bindings.
     Returns:
       list() of jinja contexts (python dicts) with information about C++ classes
       related to the interfaces in |interface_names|. dict has the following
@@ -279,26 +359,20 @@
     referenced_classes = []
     # Iterate over it as a set to uniquify the list.
     for interface_name in set(interface_names):
-      interface_info = self.info_provider.interfaces_info[interface_name]
-      is_dictionary = interface_info['is_dictionary']
-      namespace = '::'.join(
-          self.path_builder.NamespaceComponents(interface_name))
-      conditional = interface_info['conditional']
-      is_callback_interface = interface_name in IdlType.callback_interfaces
+      if interface_name in self.info_provider.enumerations:
+        # If this is an enumeration, get the enum context and continue.
+        referenced_classes.append(self.referenced_enum_context(interface_name))
+        continue
 
-      # Information about the Cobalt implementation class.
-      if is_dictionary:
-        referenced_classes.append({
-            'fully_qualified_name':
-                '%s::%s' % (namespace, interface_name),
-            'include':
-                self.path_builder.BindingsHeaderIncludePath(interface_name),
-            'conditional':
-                conditional,
-            'is_callback_interface':
-                is_callback_interface,
-        })
+      interface_info = self.info_provider.interfaces_info[interface_name]
+      if interface_info['is_dictionary']:
+        referenced_classes.append(
+            self.referenced_dictionary_context(interface_name, interface_info))
       else:
+        namespace = '::'.join(
+            self.path_builder.NamespaceComponents(interface_name))
+        conditional = interface_info['conditional']
+        is_callback_interface = interface_name in IdlType.callback_interfaces
         referenced_classes.append({
             'fully_qualified_name':
                 '%s::%s' % (namespace, interface_name),
@@ -309,7 +383,7 @@
             'is_callback_interface':
                 is_callback_interface,
         })
-        if not implementation_only:
+        if include_bindings_class:
           referenced_classes.append({
               'fully_qualified_name':
                   '%s::%s' % (namespace,
@@ -337,10 +411,12 @@
             normalize_slashes(os.path.relpath(module_path_pyname, cobalt_dir)),
         'template_path':
             normalize_slashes(os.path.relpath(template.filename, cobalt_dir)),
+        'generated_conversion_include':
+            self.path_builder.generated_conversion_include_path,
     }
     return context
 
-  def build_dictionary_context(self, dictionary, definitions):
+  def build_dictionary_context(self, dictionary, definitions, for_conversion):
     context_builder = ContextBuilder(self.info_provider)
     context = {
         'class_name':
@@ -356,7 +432,8 @@
         get_interface_type_names_from_typed_objects(self.info_provider,
                                                     dictionary.members))
     referenced_class_contexts = self.referenced_class_contexts(
-        referenced_interface_names, True)
+        referenced_interface_names, for_conversion)
+
     context['includes'] = sorted((interface['include']
                                   for interface in referenced_class_contexts))
     context['forward_declarations'] = sorted(
@@ -457,6 +534,7 @@
     all_interfaces = []
     for interface_name in referenced_interface_names:
       if (interface_name not in IdlType.callback_interfaces and
+          interface_name not in IdlType.enums and
           not interfaces_info[interface_name]['unsupported'] and
           not interfaces_info[interface_name]['is_dictionary']):
         all_interfaces.append({
diff --git a/src/cobalt/bindings/contexts.py b/src/cobalt/bindings/contexts.py
index eb14d72..c67a214 100644
--- a/src/cobalt/bindings/contexts.py
+++ b/src/cobalt/bindings/contexts.py
@@ -51,6 +51,8 @@
   """Map IDL literal to the corresponding cobalt value."""
   if idl_literal.is_null and not idl_type.is_interface_type:
     return 'base::nullopt'
+  if idl_type.is_enum:
+    return convert_to_cobalt_enumeration_value(idl_type, idl_literal.value)
   return str(idl_literal)
 
 
@@ -232,6 +234,8 @@
       cobalt_type = 'scoped_refptr<%s>' % get_interface_name(idl_type)
     elif idl_type.is_union_type:
       cobalt_type = self.idl_union_type_to_cobalt(idl_type)
+    elif idl_type.is_enum:
+      cobalt_type = idl_type.name
     elif is_sequence_type(idl_type):
       cobalt_type = self.idl_sequence_type_to_cobalt(idl_type)
     elif idl_type.name == 'void':
@@ -257,8 +261,6 @@
     idl_type = self.resolve_typedef(typed_object.idl_type)
     if idl_type.is_callback_function:
       cobalt_type = interface.name + '::' + get_interface_name(idl_type)
-    elif idl_type.is_enum:
-      cobalt_type = '%s::%s' % (interface.name, get_interface_name(idl_type))
     else:
       cobalt_type = self.idl_type_to_cobalt_type(idl_type)
     if getattr(typed_object, 'is_variadic', False):
@@ -423,13 +425,15 @@
 
   def attribute_context(self, interface, attribute, definitions):
     """Create template values for attribute bindings."""
+    cobalt_name = attribute.extended_attributes.get(
+        'ImplementedAs', convert_to_cobalt_name(attribute.name))
     context = {
         'idl_name':
             attribute.name,
         'getter_function_name':
-            convert_to_cobalt_name(attribute.name),
+            cobalt_name,
         'setter_function_name':
-            'set_' + convert_to_cobalt_name(attribute.name),
+            'set_' + cobalt_name,
         'type':
             self.typed_object_to_cobalt_type(interface, attribute),
         'is_static':
@@ -478,10 +482,10 @@
   def enumeration_context(self, enumeration):
     """Create template values for IDL enumeration type bindings."""
     return {
-        'name':
+        'enumeration_name':
             enumeration.name,
-        'value_pairs': [(convert_to_cobalt_enumeration_value(value), value,)
-                        for value in enumeration.values],
+        'value_pairs': [(convert_to_cobalt_enumeration_value(
+            enumeration.name, value), value,) for value in enumeration.values],
     }
 
   def constant_context(self, constant):
diff --git a/src/cobalt/bindings/generate_conversion_header.py b/src/cobalt/bindings/generate_conversion_header.py
new file mode 100644
index 0000000..f79e3bd
--- /dev/null
+++ b/src/cobalt/bindings/generate_conversion_header.py
@@ -0,0 +1,64 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a header for conversion of generated types.
+
+Generate a header with forward declarations for conversion functions to and from
+JavaScript values.
+"""
+
+from optparse import OptionParser
+import os
+import pickle
+
+from utilities import ComponentInfoProviderCobalt
+from utilities import write_file
+
+
+def create_component_info_provider(interfaces_info_path, component_info_path):
+  with open(os.path.join(interfaces_info_path)) as interface_info_file:
+    interfaces_info = pickle.load(interface_info_file)
+  with open(os.path.join(component_info_path)) as component_info_file:
+    component_info = pickle.load(component_info_file)
+  return ComponentInfoProviderCobalt(interfaces_info, component_info)
+
+
+def parse_options():
+  """Parse options needed for generating the header."""
+  parser = OptionParser()
+  parser.add_option(
+      '--cache-directory', help='cache directory, defaults to output directory')
+  parser.add_option('--component-info')
+  parser.add_option('--interfaces-info')
+  parser.add_option('--output-directory')
+  options, args = parser.parse_args()
+
+  assert not args
+  return options
+
+
+def generate_header(code_generator_class):
+  """Generate a header with forward declarations for type conversions."""
+  options = parse_options()
+
+  # Create a CodeGeneratorCobalt, which will generate the file.
+  cobalt_info_provider = create_component_info_provider(options.interfaces_info,
+                                                        options.component_info)
+  generator = code_generator_class(
+      info_provider=cobalt_info_provider,
+      cache_dir=options.cache_directory,
+      output_dir=options.output_directory)
+
+  # Generate the file and write it out to the specified path.
+  header_path, header_text = generator.generate_conversion_code()
+  write_file(header_text, header_path)
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
new file mode 100644
index 0000000..b5178c9
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/dictionary.h.template
+
+#ifndef DictionaryWithDictionaryMember_h
+#define DictionaryWithDictionaryMember_h
+
+#include <string>
+
+#include "cobalt/script/sequence.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class DictionaryWithDictionaryMember {
+ public:
+  DictionaryWithDictionaryMember() {
+    has_nested_dictionary_ = false;
+    nested_dictionary_ = TestDictionary();
+  }
+
+  bool has_nested_dictionary() const {
+    return has_nested_dictionary_;
+  }
+  TestDictionary nested_dictionary() const {
+    DCHECK(has_nested_dictionary_);
+    return nested_dictionary_;
+  }
+  void set_nested_dictionary(TestDictionary value) {
+    has_nested_dictionary_ = true;
+    nested_dictionary_ = value;
+  }
+
+ private:
+  bool has_nested_dictionary_;
+  TestDictionary nested_dictionary_;
+};
+
+// This ostream override is necessary for MOCK_METHODs commonly used
+// in idl test code
+inline std::ostream& operator<<(
+    std::ostream& stream, const cobalt::bindings::testing::DictionaryWithDictionaryMember& in) {
+  UNREFERENCED_PARAMETER(in);
+  stream << "[DictionaryWithDictionaryMember]";
+  return stream;
+}
+
+}  // namespace cobalt
+}  // namespace bindings
+}  // namespace testing
+
+#endif  // DictionaryWithDictionaryMember_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
index 2eec092..806ac70 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
index 2db7ce9..a0d23a1 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
index c0d2e3a..717863b 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
index c10480e..9740597 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
index 840e07d..ea95c8f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
index 4f6d720..e47a9ed 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
index d4323fd..59c48e9 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index c8af6c7..f52a434 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/mozjs_single_operation_interface.h"
 #include "cobalt/bindings/testing/single_operation_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
index f6adfc1..90e79e7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
index 3167249..589bf2c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
index 35fb74f..cbe05b6 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
index f33738c..754e7d5 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
index f590af2..6ba4eeb 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/mozjs_named_indexed_getter_interface.h"
 #include "cobalt/bindings/testing/named_indexed_getter_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
index 60a6efb..cc0512e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/base_interface.h"
 #include "cobalt/bindings/testing/mozjs_base_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 945e7da..d9c7928 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -24,7 +24,10 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
-#include "cobalt/bindings/testing/mozjs_test_dictionary.h"
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+#include "mozjs_gen_type_conversion.h"
 
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
@@ -50,6 +53,7 @@
 namespace {
 using cobalt::bindings::testing::DictionaryInterface;
 using cobalt::bindings::testing::MozjsDictionaryInterface;
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
 using cobalt::bindings::testing::TestDictionary;
 using cobalt::script::CallbackInterfaceTraits;
 using cobalt::script::GlobalEnvironment;
@@ -338,6 +342,71 @@
   return !exception_state.is_exception_set();
 }
 
+JSBool fcn_testOperation(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsDictionaryInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<DictionaryInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  DictionaryInterface* impl =
+      wrapper_private->wrappable<DictionaryInterface>().get();
+  const size_t kMinArguments = 1;
+  if (args.length() < kMinArguments) {
+    exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+    return false;
+  }
+  // Non-optional arguments
+  TypeTraits<DictionaryWithDictionaryMember >::ConversionType dict;
+
+  DCHECK_LT(0, args.length());
+  JS::RootedValue non_optional_value0(
+      context, args[0]);
+  FromJSValue(context,
+              non_optional_value0,
+              kNoConversionFlags,
+              &exception_state, &dict);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+
+  impl->TestOperation(dict);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
 
 const JSPropertySpec prototype_properties[] = {
   {  // Read/Write property
@@ -357,6 +426,13 @@
       JSPROP_ENUMERATE,
       NULL,
   },
+  {
+      "testOperation",
+      JSOP_WRAPPER(&fcn_testOperation),
+      1,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
   JS_FS_END
 };
 
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
new file mode 100644
index 0000000..50e5fde
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/dictionary-conversion.cc.template
+
+#include "mozjs_gen_type_conversion.h"
+
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+    JSContext* context,
+    const DictionaryWithDictionaryMember& in_dictionary,
+    JS::MutableHandleValue out_value) {
+  // Create a new object that will hold the dictionary values.
+  JS::RootedObject dictionary_object(
+      context, JS_NewObject(context, NULL, NULL, NULL));
+  const int kPropertyAttributes = JSPROP_ENUMERATE;
+  if (in_dictionary.has_nested_dictionary()) {
+    JS::RootedValue member_value(context);
+    ToJSValue(context, in_dictionary.nested_dictionary(), &member_value);
+    if (!JS_DefineProperty(context, dictionary_object,
+                           "nestedDictionary",
+                           member_value, NULL, NULL, kPropertyAttributes)) {
+      // Some internal error occurred.
+      NOTREACHED();
+      return;
+    }
+  }
+  out_value.set(OBJECT_TO_JSVAL(dictionary_object));
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 DictionaryWithDictionaryMember* out_dictionary) {
+  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  // https://heycam.github.io/webidl/#es-dictionary
+
+  if (value.isUndefined() || value.isNull()) {
+    // The default constructor will assign appropriate values to dictionary
+    // members with default values and leave the others unset.
+    *out_dictionary = DictionaryWithDictionaryMember();
+    return;
+  }
+  if (!value.isObject()) {
+    // 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+  JS::RootedObject dictionary_object(context, JSVAL_TO_OBJECT(value));
+  JS::RootedValue nested_dictionary(context);
+  if (!JS_GetProperty(context, dictionary_object,
+                      "nestedDictionary",
+                      nested_dictionary.address())) {
+    exception_state->SetSimpleException(kSimpleError);
+    return;
+  }
+  if (!nested_dictionary.isUndefined()) {
+    TestDictionary converted_value;
+    FromJSValue(context,
+                nested_dictionary,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
+    if (context->isExceptionPending()) {
+      return;
+    }
+    out_dictionary->set_nested_dictionary(converted_value);
+  }
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
index 203d729..5b3a925 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
index 7134234..68fa836 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
index 93a42bc..e66d97c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
@@ -24,6 +24,9 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
 
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
@@ -49,6 +52,7 @@
 namespace {
 using cobalt::bindings::testing::EnumerationInterface;
 using cobalt::bindings::testing::MozjsEnumerationInterface;
+using cobalt::bindings::testing::TestEnum;
 using cobalt::script::CallbackInterfaceTraits;
 using cobalt::script::GlobalEnvironment;
 using cobalt::script::OpaqueHandle;
@@ -79,15 +83,6 @@
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::mozjs::kNoConversionFlags;
-// Declare and define these in the same namespace that the other overloads
-// were brought into with the using declaration.
-void ToJSValue(
-    JSContext* context,
-    EnumerationInterface::TestEnum in_enum,
-    JS::MutableHandleValue out_value);
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags, ExceptionState* exception_state,
-                 EnumerationInterface::TestEnum* out_enum);
 }  // namespace
 
 namespace cobalt {
@@ -270,7 +265,7 @@
       WrapperPrivate::GetFromObject(context, object);
   EnumerationInterface* impl =
       wrapper_private->wrappable<EnumerationInterface>().get();
-  TypeTraits<EnumerationInterface::TestEnum >::ConversionType value;
+  TypeTraits<TestEnum >::ConversionType value;
   FromJSValue(context, vp, kNoConversionFlags, &exception_state,
               &value);
   if (exception_state.is_exception_set()) {
@@ -282,6 +277,69 @@
   return !exception_state.is_exception_set();
 }
 
+JSBool fcn_optionalEnumWithDefault(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, object.address())) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsEnumerationInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<EnumerationInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  EnumerationInterface* impl =
+      wrapper_private->wrappable<EnumerationInterface>().get();
+  // Optional arguments with default values
+  TypeTraits<TestEnum >::ConversionType value =
+      kTestEnumBeta;
+  size_t num_set_arguments = 1;
+  if (args.length() > 0) {
+    JS::RootedValue optional_value0(
+        context, args[0]);
+    FromJSValue(context,
+                optional_value0,
+                kNoConversionFlags,
+                &exception_state,
+                &value);
+    if (exception_state.is_exception_set()) {
+      return false;
+    }
+  }
+
+  impl->OptionalEnumWithDefault(value);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
 
 const JSPropertySpec prototype_properties[] = {
   {  // Read/Write property
@@ -294,6 +352,13 @@
 };
 
 const JSFunctionSpec prototype_functions[] = {
+  {
+      "optionalEnumWithDefault",
+      JSOP_WRAPPER(&fcn_optionalEnumWithDefault),
+      0,
+      JSPROP_ENUMERATE,
+      NULL,
+  },
   JS_FS_END
 };
 
@@ -487,58 +552,3 @@
 }  // namespace testing
 }  // namespace bindings
 }  // namespace cobalt
-
-namespace {
-
-inline void ToJSValue(
-    JSContext* context,
-    EnumerationInterface::TestEnum in_enum,
-    JS::MutableHandleValue out_value) {
-
-  switch (in_enum) {
-    case EnumerationInterface::kAlpha:
-      ToJSValue(context, std::string("alpha"), out_value);
-      return;
-    case EnumerationInterface::kBeta:
-      ToJSValue(context, std::string("beta"), out_value);
-      return;
-    case EnumerationInterface::kGamma:
-      ToJSValue(context, std::string("gamma"), out_value);
-      return;
-    default:
-      NOTREACHED();
-      out_value.set(JS::UndefinedValue());
-  }
-}
-
-
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags, ExceptionState* exception_state,
-                 EnumerationInterface::TestEnum* out_enum) {
-  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
-  // JSValue -> IDL enum algorithm described here:
-  // http://heycam.github.io/webidl/#es-enumeration
-  // 1. Let S be the result of calling ToString(V).
-  JS::RootedString rooted_string(context, JS_ValueToString(context, value));
-
-  JSBool match = JS_FALSE;
-// 3. Return the enumeration value of type E that is equal to S.
-if (JS_StringEqualsAscii(
-      context, rooted_string, "alpha", &match)
-      && match) {
-    *out_enum = EnumerationInterface::kAlpha;
-  } else if (JS_StringEqualsAscii(
-      context, rooted_string, "beta", &match)
-      && match) {
-    *out_enum = EnumerationInterface::kBeta;
-  } else if (JS_StringEqualsAscii(
-      context, rooted_string, "gamma", &match)
-      && match) {
-    *out_enum = EnumerationInterface::kGamma;
-  } else {
-    // 2. If S is not one of E's enumeration values, then throw a TypeError.
-    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
-    return;
-  }
-}
-}  // namespace
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
index 4132357..9b4ff43 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
index 186f21e..9ec5d55 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
index c609226..36681ea 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
@@ -189,6 +191,88 @@
   return interface_data;
 }
 
+JSBool get_default(
+    JSContext* context, JS::HandleObject object, JS::HandleId id,
+    JS::MutableHandleValue vp) {
+  const JSClass* proto_class =
+      MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  ExtendedIDLAttributesInterface* impl =
+      wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->attribute_default(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    vp.set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+JSBool set_default(
+    JSContext* context, JS::HandleObject object, JS::HandleId id,
+    JSBool strict, JS::MutableHandleValue vp) {
+  const JSClass* proto_class =
+      MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  ExtendedIDLAttributesInterface* impl =
+      wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+  TypeTraits<bool >::ConversionType value;
+  FromJSValue(context, vp, kNoConversionFlags, &exception_state,
+              &value);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+
+  impl->set_attribute_default(value);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
 JSBool fcn_callWithSettings(
     JSContext* context, uint32_t argc, JS::Value *vp) {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -305,6 +389,12 @@
 
 
 const JSPropertySpec prototype_properties[] = {
+  {  // Read/Write property
+      "default", 0,
+      JSPROP_SHARED | JSPROP_ENUMERATE,
+      JSOP_WRAPPER(&get_default),
+      JSOP_WRAPPER(&set_default),
+  },
   JS_PS_END
 };
 
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index 4bf4b5c..e496560 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/garbage_collection_test_interface.h"
 #include "cobalt/bindings/testing/mozjs_garbage_collection_test_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
index 3698732..0ef8902 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
index 5ec4a72..5e4454a 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
index 50b9278..d9a2ece 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index 7d61bf7..cfe8949 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
index 2c6fe76..2f323de 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
index 3aac1fe..052f2186 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
index e2e0a85..12df01c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
index b7ba06f..7b5593e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
index 6b1689e..6ba2f03 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/mozjs_put_forwards_interface.h"
 #include "cobalt/bindings/testing/put_forwards_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
index 7c5ff9a..a3ca932 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
index bc382f7..23a7a5d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
index 3796736..b52697e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
index 00b8359..728b36e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index adc219c..8e2d83d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -31,6 +31,8 @@
 #include "cobalt/bindings/testing/mozjs_base_interface.h"
 #include "cobalt/bindings/testing/mozjs_derived_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
index d2c44c0..e30f3c5 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
index e37f1f9..61d37cb 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
index 20c4381..0e4090d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index 1efad36..69655e2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
index 9a0f19f..f4ede71 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
@@ -22,6 +22,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "cobalt/script/logging_exception_state.h"
 #include "cobalt/script/mozjs/conversion_helpers.h"
 #include "cobalt/script/mozjs/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
index e20ac1c..47a2a55 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
index c475463..345a16f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
index 8f8c7ba..4afb9bc 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
index 0901b3e..bba0eb7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
index 4d48366..50d9053 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
similarity index 93%
rename from src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
rename to src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index fd4f532..50bd3c2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -17,14 +17,20 @@
 // clang-format off
 
 // This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
-// Auto-generated from template: bindings/mozjs/templates/dictionary-conversion.h.template
+// Auto-generated from template: bindings/mozjs/templates/dictionary-conversion.cc.template
 
-#ifndef TestDictionary_conversion_h
-#define TestDictionary_conversion_h
+#include "mozjs_gen_type_conversion.h"
 
 #include "cobalt/bindings/testing/test_dictionary.h"
 
 #include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+#include "cobalt/bindings/testing/arbitrary_interface.h"
+#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+
+using cobalt::bindings::testing::TestDictionary;
+using cobalt::bindings::testing::ArbitraryInterface;
+using cobalt::bindings::testing::MozjsArbitraryInterface;
 
 namespace cobalt {
 namespace script {
@@ -32,28 +38,6 @@
 
 void ToJSValue(
     JSContext* context,
-    const cobalt::bindings::testing::TestDictionary& in_dictionary,
-    JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags,
-                 cobalt::script::ExceptionState* exception_state,
-                 cobalt::bindings::testing::TestDictionary* out_dictionary);
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
-
-#include "cobalt/script/mozjs/conversion_helpers.h"
-
-using cobalt::bindings::testing::TestDictionary;
-using cobalt::bindings::testing::ArbitraryInterface;
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-inline void ToJSValue(
-    JSContext* context,
     const TestDictionary& in_dictionary,
     JS::MutableHandleValue out_value) {
   // Create a new object that will hold the dictionary values.
@@ -151,7 +135,7 @@
   out_value.set(OBJECT_TO_JSVAL(dictionary_object));
 }
 
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
                  int conversion_flags, ExceptionState* exception_state,
                  TestDictionary* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -327,4 +311,3 @@
 }  // namespace script
 }  // namespace cobalt
 
-#endif  // TestDictionary_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_enum.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_enum.cc
new file mode 100644
index 0000000..445ddbc
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_enum.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/enumeration-conversion.cc.template
+
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+using cobalt::bindings::testing::TestEnum;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+    JSContext* context,
+    TestEnum in_enum,
+    JS::MutableHandleValue out_value) {
+  switch (in_enum) {
+    case cobalt::bindings::testing::kTestEnumAlpha:
+      ToJSValue(context, std::string("alpha"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumBeta:
+      ToJSValue(context, std::string("beta"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumGamma:
+      ToJSValue(context, std::string("gamma"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumEnumWithDashes:
+      ToJSValue(context, std::string("enum-with-dashes"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumEnumWithSpaces:
+      ToJSValue(context, std::string("enum with spaces"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumTerribleEnum:
+      ToJSValue(context, std::string("terrible----enum"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumThisIsATerribleEnum:
+      ToJSValue(context, std::string("this is a terrible @#$%#$% enum"), out_value);
+      return;
+    default:
+      NOTREACHED();
+      out_value.set(JS::UndefinedValue());
+  }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 TestEnum* out_enum) {
+  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  // JSValue -> IDL enum algorithm described here:
+  // http://heycam.github.io/webidl/#es-enumeration
+  // 1. Let S be the result of calling ToString(V).
+  JS::RootedString rooted_string(context, JS_ValueToString(context, value));
+
+  JSBool match = JS_FALSE;
+// 3. Return the enumeration value of type E that is equal to S.
+if (JS_StringEqualsAscii(
+      context, rooted_string, "alpha", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumAlpha;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "beta", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumBeta;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "gamma", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumGamma;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "enum-with-dashes", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumEnumWithDashes;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "enum with spaces", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumEnumWithSpaces;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "terrible----enum", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumTerribleEnum;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "this is a terrible @#$%#$% enum", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumThisIsATerribleEnum;
+  } else {
+    // 2. If S is not one of E's enumeration values, then throw a TypeError.
+    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+    return;
+  }
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 8f4cb24..2eb8e32 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -29,6 +29,8 @@
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_base_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
index 07d98af..883ae30 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -123,6 +123,8 @@
 #include "cobalt/bindings/testing/union_types_interface.h"
 #include "cobalt/bindings/testing/window.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
index 4dc5055..4c36160 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
@@ -27,6 +27,8 @@
 #include "cobalt/script/sequence.h"
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 
+using cobalt::bindings::testing::ArbitraryInterface;
+
 namespace cobalt {
 namespace bindings {
 namespace testing {
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_enum.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_enum.h
new file mode 100644
index 0000000..5e4bb69
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_enum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/enumeration.h.template
+
+#ifndef TestEnum_h
+#define TestEnum_h
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+enum TestEnum {
+  kTestEnumAlpha,
+  kTestEnumBeta,
+  kTestEnumGamma,
+  kTestEnumEnumWithDashes,
+  kTestEnumEnumWithSpaces,
+  kTestEnumTerribleEnum,
+  kTestEnumThisIsATerribleEnum,
+};
+
+}  // namespace cobalt
+}  // namespace bindings
+}  // namespace testing
+
+#endif  // TestEnum_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
new file mode 100644
index 0000000..b5178c9
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/dictionary.h.template
+
+#ifndef DictionaryWithDictionaryMember_h
+#define DictionaryWithDictionaryMember_h
+
+#include <string>
+
+#include "cobalt/script/sequence.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class DictionaryWithDictionaryMember {
+ public:
+  DictionaryWithDictionaryMember() {
+    has_nested_dictionary_ = false;
+    nested_dictionary_ = TestDictionary();
+  }
+
+  bool has_nested_dictionary() const {
+    return has_nested_dictionary_;
+  }
+  TestDictionary nested_dictionary() const {
+    DCHECK(has_nested_dictionary_);
+    return nested_dictionary_;
+  }
+  void set_nested_dictionary(TestDictionary value) {
+    has_nested_dictionary_ = true;
+    nested_dictionary_ = value;
+  }
+
+ private:
+  bool has_nested_dictionary_;
+  TestDictionary nested_dictionary_;
+};
+
+// This ostream override is necessary for MOCK_METHODs commonly used
+// in idl test code
+inline std::ostream& operator<<(
+    std::ostream& stream, const cobalt::bindings::testing::DictionaryWithDictionaryMember& in) {
+  UNREFERENCED_PARAMETER(in);
+  stream << "[DictionaryWithDictionaryMember]";
+  return stream;
+}
+
+}  // namespace cobalt
+}  // namespace bindings
+}  // namespace testing
+
+#endif  // DictionaryWithDictionaryMember_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
index 46880fb..b06defe 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
index f83e1d7..680c83a 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
index 654870d..52b9f5d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
index fdfc37e..2051e93 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc
index 8f3c8f9..278db4a 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
index 4b00338..60e62a8 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
index 9dc43d9..287ca7e 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index 560292c..c8d4f23 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/mozjs_single_operation_interface.h"
 #include "cobalt/bindings/testing/single_operation_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
index e741632..8aa44f7 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -73,6 +77,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
index 3a47240..9fc7f7f 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
index a08bfff..12948a7 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
index 5efabd2..d7e4244 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
index 8ff047c..d346835 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/mozjs_named_indexed_getter_interface.h"
 #include "cobalt/bindings/testing/named_indexed_getter_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
index 6503829..8971ca7 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/base_interface.h"
 #include "cobalt/bindings/testing/mozjs_base_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 653b32b..d2278fb 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -24,7 +24,10 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
-#include "cobalt/bindings/testing/mozjs_test_dictionary.h"
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+#include "mozjs_gen_type_conversion.h"
 
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
@@ -36,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -48,6 +53,7 @@
 namespace {
 using cobalt::bindings::testing::DictionaryInterface;
 using cobalt::bindings::testing::MozjsDictionaryInterface;
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
 using cobalt::bindings::testing::TestDictionary;
 using cobalt::script::CallbackInterfaceTraits;
 using cobalt::script::GlobalEnvironment;
@@ -73,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -329,6 +336,71 @@
   return !exception_state.is_exception_set();
 }
 
+bool fcn_testOperation(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsDictionaryInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<DictionaryInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  DictionaryInterface* impl =
+      wrapper_private->wrappable<DictionaryInterface>().get();
+  const size_t kMinArguments = 1;
+  if (args.length() < kMinArguments) {
+    exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+    return false;
+  }
+  // Non-optional arguments
+  TypeTraits<DictionaryWithDictionaryMember >::ConversionType dict;
+
+  DCHECK_LT(0, args.length());
+  JS::RootedValue non_optional_value0(
+      context, args[0]);
+  FromJSValue(context,
+              non_optional_value0,
+              kNoConversionFlags,
+              &exception_state, &dict);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+
+  impl->TestOperation(dict);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
 
 
 const JSPropertySpec prototype_properties[] = {
@@ -345,6 +417,9 @@
   JS_FNSPEC(
       "dictionaryOperation", fcn_dictionaryOperation, NULL,
       1, JSPROP_ENUMERATE, NULL),
+  JS_FNSPEC(
+      "testOperation", fcn_testOperation, NULL,
+      1, JSPROP_ENUMERATE, NULL),
   JS_FS_END
 };
 
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
new file mode 100644
index 0000000..5d193d4
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/dictionary-conversion.cc.template
+
+#include "mozjs_gen_type_conversion.h"
+
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+    JSContext* context,
+    const DictionaryWithDictionaryMember& in_dictionary,
+    JS::MutableHandleValue out_value) {
+  // Create a new object that will hold the dictionary values.
+  JS::RootedObject dictionary_object(
+      context, JS_NewObject(context, nullptr));
+  const int kPropertyAttributes = JSPROP_ENUMERATE;
+  if (in_dictionary.has_nested_dictionary()) {
+    JS::RootedValue member_value(context);
+    ToJSValue(context, in_dictionary.nested_dictionary(), &member_value);
+    if (!JS_DefineProperty(context, dictionary_object,
+                           "nestedDictionary",
+                           member_value, kPropertyAttributes, nullptr, nullptr)) {
+      // Some internal error occurred.
+      NOTREACHED();
+      return;
+    }
+  }
+  out_value.setObject(*dictionary_object);
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 DictionaryWithDictionaryMember* out_dictionary) {
+  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  // https://heycam.github.io/webidl/#es-dictionary
+
+  if (value.isUndefined() || value.isNull()) {
+    // The default constructor will assign appropriate values to dictionary
+    // members with default values and leave the others unset.
+    *out_dictionary = DictionaryWithDictionaryMember();
+    return;
+  }
+  if (!value.isObject()) {
+    // 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
+    exception_state->SetSimpleException(kNotObjectType);
+    return;
+  }
+  JS::RootedObject dictionary_object(context, &value.toObject());
+  JS::RootedValue nested_dictionary(context);
+  if (!JS_GetProperty(context, dictionary_object,
+                      "nestedDictionary",
+                      &nested_dictionary)) {
+    exception_state->SetSimpleException(kSimpleError);
+    return;
+  }
+  if (!nested_dictionary.isUndefined()) {
+    TestDictionary converted_value;
+    FromJSValue(context,
+                nested_dictionary,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
+    if (context->isExceptionPending()) {
+      return;
+    }
+    out_dictionary->set_nested_dictionary(converted_value);
+  }
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
index eaf3ec7..59f0f29 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -73,6 +77,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
index 22e6f43..dcece70 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
index af0579c..e5b771e 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
@@ -24,6 +24,9 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
 
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
@@ -35,6 +38,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -47,6 +52,7 @@
 namespace {
 using cobalt::bindings::testing::EnumerationInterface;
 using cobalt::bindings::testing::MozjsEnumerationInterface;
+using cobalt::bindings::testing::TestEnum;
 using cobalt::script::CallbackInterfaceTraits;
 using cobalt::script::GlobalEnvironment;
 using cobalt::script::OpaqueHandle;
@@ -71,20 +77,12 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::mozjs::kNoConversionFlags;
-// Declare and define these in the same namespace that the other overloads
-// were brought into with the using declaration.
-void ToJSValue(
-    JSContext* context,
-    EnumerationInterface::TestEnum in_enum,
-    JS::MutableHandleValue out_value);
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags, ExceptionState* exception_state,
-                 EnumerationInterface::TestEnum* out_enum);
 }  // namespace
 
 namespace cobalt {
@@ -256,7 +254,7 @@
       WrapperPrivate::GetFromObject(context, object);
   EnumerationInterface* impl =
       wrapper_private->wrappable<EnumerationInterface>().get();
-  TypeTraits<EnumerationInterface::TestEnum >::ConversionType value;
+  TypeTraits<TestEnum >::ConversionType value;
   if (args.length() != 1) {
     NOTREACHED();
     return false;
@@ -272,6 +270,69 @@
   return !exception_state.is_exception_set();
 }
 
+bool fcn_optionalEnumWithDefault(
+    JSContext* context, uint32_t argc, JS::Value *vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  // Compute the 'this' value.
+  JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+  // 'this' should be an object.
+  JS::RootedObject object(context);
+  if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+    NOTREACHED();
+    return false;
+  }
+  if (!JS_ValueToObject(context, this_value, &object)) {
+    NOTREACHED();
+    return false;
+  }
+  const JSClass* proto_class =
+      MozjsEnumerationInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<EnumerationInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  EnumerationInterface* impl =
+      wrapper_private->wrappable<EnumerationInterface>().get();
+  // Optional arguments with default values
+  TypeTraits<TestEnum >::ConversionType value =
+      kTestEnumBeta;
+  size_t num_set_arguments = 1;
+  if (args.length() > 0) {
+    JS::RootedValue optional_value0(
+        context, args[0]);
+    FromJSValue(context,
+                optional_value0,
+                kNoConversionFlags,
+                &exception_state,
+                &value);
+    if (exception_state.is_exception_set()) {
+      return false;
+    }
+  }
+
+  impl->OptionalEnumWithDefault(value);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
 
 
 const JSPropertySpec prototype_properties[] = {
@@ -285,6 +346,9 @@
 };
 
 const JSFunctionSpec prototype_functions[] = {
+  JS_FNSPEC(
+      "optionalEnumWithDefault", fcn_optionalEnumWithDefault, NULL,
+      0, JSPROP_ENUMERATE, NULL),
   JS_FS_END
 };
 
@@ -479,57 +543,3 @@
 }  // namespace testing
 }  // namespace bindings
 }  // namespace cobalt
-
-namespace {
-
-inline void ToJSValue(
-    JSContext* context,
-    EnumerationInterface::TestEnum in_enum,
-    JS::MutableHandleValue out_value) {
-
-  switch (in_enum) {
-    case EnumerationInterface::kAlpha:
-      ToJSValue(context, std::string("alpha"), out_value);
-      return;
-    case EnumerationInterface::kBeta:
-      ToJSValue(context, std::string("beta"), out_value);
-      return;
-    case EnumerationInterface::kGamma:
-      ToJSValue(context, std::string("gamma"), out_value);
-      return;
-    default:
-      NOTREACHED();
-      out_value.set(JS::UndefinedValue());
-  }
-}
-
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags, ExceptionState* exception_state,
-                 EnumerationInterface::TestEnum* out_enum) {
-  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
-  // JSValue -> IDL enum algorithm described here:
-  // http://heycam.github.io/webidl/#es-enumeration
-  // 1. Let S be the result of calling ToString(V).
-  JS::RootedString rooted_string(context, JS::ToString(context, value));
-
-  bool match = false;
-// 3. Return the enumeration value of type E that is equal to S.
-if (JS_StringEqualsAscii(
-      context, rooted_string, "alpha", &match)
-      && match) {
-    *out_enum = EnumerationInterface::kAlpha;
-  } else if (JS_StringEqualsAscii(
-      context, rooted_string, "beta", &match)
-      && match) {
-    *out_enum = EnumerationInterface::kBeta;
-  } else if (JS_StringEqualsAscii(
-      context, rooted_string, "gamma", &match)
-      && match) {
-    *out_enum = EnumerationInterface::kGamma;
-  } else {
-    // 2. If S is not one of E's enumeration values, then throw a TypeError.
-    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
-    return;
-  }
-}
-}  // namespace
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
index 9d9e036..b24c533 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -72,6 +76,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
index a960a35..cd6f023 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
index a7b6263..82699e1 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -172,6 +177,96 @@
     NULL,
 };
 
+bool get_default(
+    JSContext* context, unsigned argc, JS::Value* vp) {
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  JS::RootedObject object(context, &args.thisv().toObject());
+  const JSClass* proto_class =
+      MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  ExtendedIDLAttributesInterface* impl =
+      wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(context,
+              impl->attribute_default(),
+              &result_value);
+  }
+  if (!exception_state.is_exception_set()) {
+    args.rval().set(result_value);
+  }
+  return !exception_state.is_exception_set();
+}
+
+bool set_default(
+    JSContext* context, unsigned argc, JS::Value* vp) {
+
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  JS::RootedObject object(context, &args.thisv().toObject());
+
+  const JSClass* proto_class =
+      MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+  if (proto_class == JS_GetClass(object)) {
+    // Simply returns true if the object is this class's prototype object.
+    // There is no need to return any value due to the object is not a platform
+    // object. The execution reaches here when Object.getOwnPropertyDescriptor
+    // gets called on native object prototypes.
+    return true;
+  }
+
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+  if (!wrapper_factory->DoesObjectImplementInterface(
+        object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+    MozjsExceptionState exception(context);
+    exception.SetSimpleException(script::kDoesNotImplementInterface);
+    return false;
+  }
+  MozjsExceptionState exception_state(context);
+  JS::RootedValue result_value(context);
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromObject(context, object);
+  ExtendedIDLAttributesInterface* impl =
+      wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+  TypeTraits<bool >::ConversionType value;
+  if (args.length() != 1) {
+    NOTREACHED();
+    return false;
+  }
+  FromJSValue(context, args[0], kNoConversionFlags, &exception_state,
+              &value);
+  if (exception_state.is_exception_set()) {
+    return false;
+  }
+
+  impl->set_attribute_default(value);
+  result_value.set(JS::UndefinedHandleValue);
+  return !exception_state.is_exception_set();
+}
+
 bool fcn_callWithSettings(
     JSContext* context, uint32_t argc, JS::Value *vp) {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -289,6 +384,12 @@
 
 
 const JSPropertySpec prototype_properties[] = {
+  {  // Read/Write property
+    "default",
+    JSPROP_SHARED | JSPROP_ENUMERATE,
+    { { &get_default, NULL } },
+    { { &set_default, NULL } },
+  },
   JS_PS_END
 };
 
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index 39c62da..0c62a2d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/garbage_collection_test_interface.h"
 #include "cobalt/bindings/testing/mozjs_garbage_collection_test_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
index a91cfc5..f2001fc 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
index b7d243f..cd39673 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
index 63cde6c..77f6e6f 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index 7f7880a..85578ca 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
index f1057fa..bf9a3d0 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
index 68e85ca..a719c77 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
index e15cfad..0367daa 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
index a4ac41e..5dea1d5 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
index d07df50..108fc17 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/mozjs_put_forwards_interface.h"
 #include "cobalt/bindings/testing/put_forwards_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
index b38b987..35d8aae 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
index 03e32c0..d7bae5a 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
index 5f832ef..7b18b10 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
index 9809983..48dd07b 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index fe85cd3..11db14d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -31,6 +31,8 @@
 #include "cobalt/bindings/testing/mozjs_base_interface.h"
 #include "cobalt/bindings/testing/mozjs_derived_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -41,6 +43,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -83,6 +87,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
index ddab4a5..78ee810 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
index b918e34..bc07a75 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
index ac1e14e..b139037 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index ebd378a..4473e30 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
index 017c4fe..3b0c942 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
@@ -22,6 +22,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "cobalt/script/logging_exception_state.h"
 #include "cobalt/script/mozjs-45/conversion_helpers.h"
 #include "cobalt/script/mozjs-45/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
index 5717417..d03a770 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -27,6 +27,8 @@
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
index f82e3b1..0699d67 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
index b2f806d..bd3097d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
index 7c67a24..da67d25 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
index ff6a0b7..f0da3bc 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -25,6 +25,8 @@
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
similarity index 84%
rename from src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
rename to src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index 197f039..98fc778 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -17,14 +17,20 @@
 // clang-format off
 
 // This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
-// Auto-generated from template: bindings/mozjs45/templates/dictionary-conversion.h.template
+// Auto-generated from template: bindings/mozjs45/templates/dictionary-conversion.cc.template
 
-#ifndef TestDictionary_conversion_h
-#define TestDictionary_conversion_h
+#include "mozjs_gen_type_conversion.h"
 
 #include "cobalt/bindings/testing/test_dictionary.h"
 
 #include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "cobalt/bindings/testing/arbitrary_interface.h"
+#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+
+using cobalt::bindings::testing::TestDictionary;
+using cobalt::bindings::testing::ArbitraryInterface;
+using cobalt::bindings::testing::MozjsArbitraryInterface;
 
 namespace cobalt {
 namespace script {
@@ -32,29 +38,6 @@
 
 void ToJSValue(
     JSContext* context,
-    const cobalt::bindings::testing::TestDictionary& in_dictionary,
-    JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags,
-                 cobalt::script::ExceptionState* exception_state,
-                 cobalt::bindings::testing::TestDictionary* out_dictionary);
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
-
-#include "cobalt/script/mozjs-45/conversion_helpers.h"
-#include "third_party/mozjs-45/js/src/jscntxt.h"
-
-using cobalt::bindings::testing::TestDictionary;
-using cobalt::bindings::testing::ArbitraryInterface;
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-inline void ToJSValue(
-    JSContext* context,
     const TestDictionary& in_dictionary,
     JS::MutableHandleValue out_value) {
   // Create a new object that will hold the dictionary values.
@@ -152,7 +135,7 @@
   out_value.setObject(*dictionary_object);
 }
 
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
                  int conversion_flags, ExceptionState* exception_state,
                  TestDictionary* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -179,7 +162,11 @@
   }
   if (!boolean_member.isUndefined()) {
     bool converted_value;
-    FromJSValue(context, boolean_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                boolean_member,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -194,7 +181,11 @@
   }
   if (!short_clamp_member.isUndefined()) {
     int16_t converted_value;
-    FromJSValue(context, short_clamp_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                short_clamp_member,
+                (kConversionFlagClamped),
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -209,7 +200,11 @@
   }
   if (!long_member.isUndefined()) {
     int32_t converted_value;
-    FromJSValue(context, long_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                long_member,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -224,7 +219,11 @@
   }
   if (!double_member.isUndefined()) {
     double converted_value;
-    FromJSValue(context, double_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                double_member,
+                (kConversionFlagRestricted),
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -239,7 +238,11 @@
   }
   if (!string_member.isUndefined()) {
     std::string converted_value;
-    FromJSValue(context, string_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                string_member,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -254,7 +257,11 @@
   }
   if (!interface_member.isUndefined()) {
     scoped_refptr<ArbitraryInterface> converted_value;
-    FromJSValue(context, interface_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                interface_member,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -269,7 +276,11 @@
   }
   if (!member_with_default.isUndefined()) {
     int32_t converted_value;
-    FromJSValue(context, member_with_default, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                member_with_default,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -284,7 +295,11 @@
   }
   if (!non_default_member.isUndefined()) {
     int32_t converted_value;
-    FromJSValue(context, non_default_member, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                non_default_member,
+                kNoConversionFlags,
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -292,8 +307,7 @@
   }
 }
 
-}
-}
-}
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
 
-#endif  // TestDictionary_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc
new file mode 100644
index 0000000..7aa9e08
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/enumeration-conversion.cc.template
+
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+using cobalt::bindings::testing::TestEnum;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+    JSContext* context,
+    TestEnum in_enum,
+    JS::MutableHandleValue out_value) {
+  switch (in_enum) {
+    case cobalt::bindings::testing::kTestEnumAlpha:
+      ToJSValue(context, std::string("alpha"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumBeta:
+      ToJSValue(context, std::string("beta"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumGamma:
+      ToJSValue(context, std::string("gamma"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumEnumWithDashes:
+      ToJSValue(context, std::string("enum-with-dashes"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumEnumWithSpaces:
+      ToJSValue(context, std::string("enum with spaces"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumTerribleEnum:
+      ToJSValue(context, std::string("terrible----enum"), out_value);
+      return;
+    case cobalt::bindings::testing::kTestEnumThisIsATerribleEnum:
+      ToJSValue(context, std::string("this is a terrible @#$%#$% enum"), out_value);
+      return;
+    default:
+      NOTREACHED();
+      out_value.set(JS::UndefinedValue());
+  }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 TestEnum* out_enum) {
+  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  // JSValue -> IDL enum algorithm described here:
+  // http://heycam.github.io/webidl/#es-enumeration
+  // 1. Let S be the result of calling ToString(V).
+  JS::RootedString rooted_string(context, JS::ToString(context, value));
+
+  bool match = false;
+// 3. Return the enumeration value of type E that is equal to S.
+if (JS_StringEqualsAscii(
+      context, rooted_string, "alpha", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumAlpha;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "beta", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumBeta;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "gamma", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumGamma;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "enum-with-dashes", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumEnumWithDashes;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "enum with spaces", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumEnumWithSpaces;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "terrible----enum", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumTerribleEnum;
+  } else if (JS_StringEqualsAscii(
+      context, rooted_string, "this is a terrible @#$%#$% enum", &match)
+      && match) {
+    *out_enum = cobalt::bindings::testing::kTestEnumThisIsATerribleEnum;
+  } else {
+    // 2. If S is not one of E's enumeration values, then throw a TypeError.
+    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+    return;
+  }
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 1934f7f..9d1fe7b 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -29,6 +29,8 @@
 #include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
 #include "cobalt/bindings/testing/mozjs_base_interface.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -39,6 +41,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -79,6 +83,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
index a16151c..c26f450 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -123,6 +123,8 @@
 #include "cobalt/bindings/testing/union_types_interface.h"
 #include "cobalt/bindings/testing/window.h"
 
+#include "mozjs_gen_type_conversion.h"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -133,6 +135,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -275,6 +279,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -1055,6 +1060,7 @@
   MozjsWindow::CreateProxy(
       context, global_interface);
   mozjs_global_environment->SetEnvironmentSettings(environment_settings);
+  mozjs_global_environment->EvaluateAutomatics();
 
   WrapperFactory* wrapper_factory =
       mozjs_global_environment->wrapper_factory();
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
index 4dc5055..4c36160 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
@@ -27,6 +27,8 @@
 #include "cobalt/script/sequence.h"
 #include "cobalt/bindings/testing/arbitrary_interface.h"
 
+using cobalt::bindings::testing::ArbitraryInterface;
+
 namespace cobalt {
 namespace bindings {
 namespace testing {
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h
new file mode 100644
index 0000000..5e4bb69
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/enumeration.h.template
+
+#ifndef TestEnum_h
+#define TestEnum_h
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+enum TestEnum {
+  kTestEnumAlpha,
+  kTestEnumBeta,
+  kTestEnumGamma,
+  kTestEnumEnumWithDashes,
+  kTestEnumEnumWithSpaces,
+  kTestEnumTerribleEnum,
+  kTestEnumThisIsATerribleEnum,
+};
+
+}  // namespace cobalt
+}  // namespace bindings
+}  // namespace testing
+
+#endif  // TestEnum_h
diff --git a/src/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py b/src/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py
new file mode 100644
index 0000000..b43e30c
--- /dev/null
+++ b/src/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py
@@ -0,0 +1,24 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a conversion header for SpiderMonkey."""
+
+import sys
+
+import bootstrap_path  # pylint: disable=g-bad-import-order,unused-import
+
+from cobalt.bindings.generate_conversion_header import generate_header
+from cobalt.bindings.mozjs.code_generator_mozjs import CodeGeneratorMozjs
+
+if __name__ == '__main__':
+  sys.exit(generate_header(CodeGeneratorMozjs))
diff --git a/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template b/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
index 0d2272c..f3031f9 100644
--- a/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
@@ -17,6 +17,8 @@
 
 {% block includes %}
 {{ super() }}
+#include "{{generated_conversion_include}}"
+
 #include "cobalt/script/logging_exception_state.h"
 #include "cobalt/script/mozjs/conversion_helpers.h"
 #include "cobalt/script/mozjs/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template b/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
similarity index 85%
rename from src/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template
rename to src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
index 9872892..cc878eb 100644
--- a/src/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template
+++ b/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
@@ -34,37 +34,18 @@
 // This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
 // Auto-generated from template: {{template_path}}
 
-#ifndef {{class_name}}_conversion_h
-#define {{class_name}}_conversion_h
-
 {% if conditional %}
 #if defined({{conditional}})
 
 {% endif %}
 {% block includes %}
+#include "{{generated_conversion_include}}"
+
 #include "{{header_file}}"
 
 #include "cobalt/script/exception_state.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-void ToJSValue(
-    JSContext* context,
-    const {{components|join('::')}}::{{class_name}}& in_dictionary,
-    JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags,
-                 cobalt::script::ExceptionState* exception_state,
-                 {{components|join('::')}}::{{class_name}}* out_dictionary);
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
-
-#include "cobalt/script/mozjs/conversion_helpers.h"
-{% for include in header_includes %}
+#include "third_party/mozjs/js/src/jsapi.h"
+{% for include in includes %}
 #include "{{include}}"
 {% endfor %}
 {% endblock includes %}
@@ -84,7 +65,7 @@
 namespace script {
 namespace mozjs {
 
-inline void ToJSValue(
+void ToJSValue(
     JSContext* context,
     const {{class_name}}& in_dictionary,
     JS::MutableHandleValue out_value) {
@@ -108,7 +89,7 @@
   out_value.set(OBJECT_TO_JSVAL(dictionary_object));
 }
 
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
                  int conversion_flags, ExceptionState* exception_state,
                  {{class_name}}* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -155,6 +136,4 @@
 
 {% if conditional %}
 #endif  // defined({{conditional}})
-
 {% endif %}
-#endif  // {{class_name}}_h
diff --git a/src/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template
new file mode 100644
index 0000000..3e4df11
--- /dev/null
+++ b/src/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template
@@ -0,0 +1,90 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ #     http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} 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.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#include "{{enum_include}}"
+
+#include "{{generated_conversion_include}}"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+using {{fully_qualified_name}};
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+    JSContext* context,
+    {{enumeration_name}} in_enum,
+    JS::MutableHandleValue out_value) {
+  switch (in_enum) {
+{% for value, idl_value in value_pairs %}
+    case {{namespace}}::{{value}}:
+      ToJSValue(context, std::string("{{idl_value}}"), out_value);
+      return;
+{% endfor %}
+    default:
+      NOTREACHED();
+      out_value.set(JS::UndefinedValue());
+  }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 {{enumeration_name}}* out_enum) {
+  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  // JSValue -> IDL enum algorithm described here:
+  // http://heycam.github.io/webidl/#es-enumeration
+  // 1. Let S be the result of calling ToString(V).
+  JS::RootedString rooted_string(context, JS_ValueToString(context, value));
+
+  JSBool match = JS_FALSE;
+// 3. Return the enumeration value of type E that is equal to S.
+{% for value, idl_value in value_pairs %}
+  {{-" else " if not loop.first}}if (JS_StringEqualsAscii(
+      context, rooted_string, "{{idl_value}}", &match)
+      && match) {
+    *out_enum = {{namespace}}::{{value}};
+  }{% endfor %} else {
+    // 2. If S is not one of E's enumeration values, then throw a TypeError.
+    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+    return;
+  }
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/mozjs/templates/generated-types.h.template b/src/cobalt/bindings/mozjs/templates/generated-types.h.template
new file mode 100644
index 0000000..d34d409
--- /dev/null
+++ b/src/cobalt/bindings/mozjs/templates/generated-types.h.template
@@ -0,0 +1,92 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ #     http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} 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.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#ifndef GENERATED_TYPE_CONVERSION_H_
+#define GENERATED_TYPE_CONVERSION_H_
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+// #includes for generated types.
+{% for path in includes %}
+#include "{{path}}"
+{% endfor %}
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+{% for dictionary in dictionaries %}
+// {{dictionary.fully_qualified_name}} -> JSValue
+void ToJSValue(
+    JSContext* context,
+    const {{dictionary.fully_qualified_name}}& in_value,
+    JS::MutableHandleValue out_value);
+
+// JSValue -> {{dictionary.fully_qualified_name}}
+void FromJSValue(
+    JSContext* context,
+    JS::HandleValue value,
+    int conversion_flags,
+    ExceptionState* exception_state,
+    {{dictionary.fully_qualified_name}}* out_value);
+
+{% endfor %}
+{% for enum in enumerations %}
+// {{enum.fully_qualified_name}} -> JSValue
+void ToJSValue(
+    JSContext* context,
+    {{enum.fully_qualified_name}} in_value,
+    JS::MutableHandleValue out_value);
+
+// JSValue -> {{enum.fully_qualified_name}}
+void FromJSValue(
+    JSContext* context,
+    JS::HandleValue value,
+    int conversion_flags,
+    ExceptionState* exception_state,
+    {{enum.fully_qualified_name}}* out_value);
+
+{% endfor %}
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+// #include this here so the conversion functions for generated types are
+// visible refer to template types, such as sequence<T>.
+#include "cobalt/script/mozjs/conversion_helpers.h"
+
+#endif  // GENERATED_TYPE_CONVERSION_H_
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template
index 92af4f7..12ad4c0 100644
--- a/src/cobalt/bindings/mozjs/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -26,6 +26,8 @@
 {% extends "interface-base.cc.template" %}
 {% block includes %}
 {{ super() }}
+#include "{{generated_conversion_include}}"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs/callback_function_conversion.h"
@@ -75,21 +77,6 @@
 using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::mozjs::kNoConversionFlags;
 {% endblock using_directives %}
-{% block enumeration_declarations %}
-{% if enumerations|length %}
-// Declare and define these in the same namespace that the other overloads
-// were brought into with the using declaration.
-{% for enumeration in enumerations %}
-void ToJSValue(
-    JSContext* context,
-    {{impl_class}}::{{enumeration.name}} in_enum,
-    JS::MutableHandleValue out_value);
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags, ExceptionState* exception_state,
-                 {{impl_class}}::{{enumeration.name}}* out_enum);
-{% endfor %}
-{% endif %}
-{% endblock enumeration_declarations %}
 {% block top_level_unnamed_namespace %}
 {% if is_global_interface %}
 JSObject* DummyFunctor(
@@ -975,49 +962,3 @@
 {% endfor %}
 
 {% endblock create_global_object_impl %}
-
-{%  block enumeration_definitions %}
-{% for enumeration in enumerations %}
-
-inline void ToJSValue(
-    JSContext* context,
-    {{impl_class}}::{{enumeration.name}} in_enum,
-    JS::MutableHandleValue out_value) {
-
-  switch (in_enum) {
-{% for value, idl_value in enumeration.value_pairs %}
-    case {{impl_class}}::{{value}}:
-      ToJSValue(context, std::string("{{idl_value}}"), out_value);
-      return;
-{% endfor %}
-    default:
-      NOTREACHED();
-      out_value.set(JS::UndefinedValue());
-  }
-}
-
-
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags, ExceptionState* exception_state,
-                 {{impl_class}}::{{enumeration.name}}* out_enum) {
-  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
-  // JSValue -> IDL enum algorithm described here:
-  // http://heycam.github.io/webidl/#es-enumeration
-  // 1. Let S be the result of calling ToString(V).
-  JS::RootedString rooted_string(context, JS_ValueToString(context, value));
-
-  JSBool match = JS_FALSE;
-// 3. Return the enumeration value of type E that is equal to S.
-{% for value, idl_value in enumeration.value_pairs %}
-  {{-" else " if not loop.first}}if (JS_StringEqualsAscii(
-      context, rooted_string, "{{idl_value}}", &match)
-      && match) {
-    *out_enum = {{impl_class}}::{{value}};
-  }{% endfor %} else {
-    // 2. If S is not one of E's enumeration values, then throw a TypeError.
-    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
-    return;
-  }
-}
-{% endfor %}
-{% endblock enumeration_definitions %}
diff --git a/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py b/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
new file mode 100644
index 0000000..d149eee
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
@@ -0,0 +1,24 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a conversion header for SpiderMonkey."""
+
+import sys
+
+import bootstrap_path  # pylint: disable=g-bad-import-order,unused-import
+
+from cobalt.bindings.generate_conversion_header import generate_header
+from cobalt.bindings.mozjs45.code_generator_mozjs45 import CodeGeneratorMozjs45
+
+if __name__ == '__main__':
+  sys.exit(generate_header(CodeGeneratorMozjs45))
diff --git a/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template b/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
index 3087785..04f6ccd 100644
--- a/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
@@ -17,6 +17,8 @@
 
 {% block includes %}
 {{ super() }}
+#include "{{generated_conversion_include}}"
+
 #include "cobalt/script/logging_exception_state.h"
 #include "cobalt/script/mozjs-45/conversion_helpers.h"
 #include "cobalt/script/mozjs-45/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
similarity index 84%
rename from src/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template
rename to src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
index 5b8b907..f276cbc 100644
--- a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template
+++ b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
@@ -34,38 +34,18 @@
 // This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
 // Auto-generated from template: {{template_path}}
 
-#ifndef {{class_name}}_conversion_h
-#define {{class_name}}_conversion_h
-
 {% if conditional %}
 #if defined({{conditional}})
 
 {% endif %}
 {% block includes %}
+#include "{{generated_conversion_include}}"
+
 #include "{{header_file}}"
 
 #include "cobalt/script/exception_state.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-void ToJSValue(
-    JSContext* context,
-    const {{components|join('::')}}::{{class_name}}& in_dictionary,
-    JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
-                 int conversion_flags,
-                 cobalt::script::ExceptionState* exception_state,
-                 {{components|join('::')}}::{{class_name}}* out_dictionary);
-}  // namespace mozjs
-}  // namespace script
-}  // namespace cobalt
-
-#include "cobalt/script/mozjs-45/conversion_helpers.h"
-#include "third_party/mozjs-45/js/src/jscntxt.h"
-{% for include in header_includes %}
+#include "third_party/mozjs-45/js/src/jsapi.h"
+{% for include in includes %}
 #include "{{include}}"
 {% endfor %}
 {% endblock includes %}
@@ -85,7 +65,7 @@
 namespace script {
 namespace mozjs {
 
-inline void ToJSValue(
+void ToJSValue(
     JSContext* context,
     const {{class_name}}& in_dictionary,
     JS::MutableHandleValue out_value) {
@@ -109,7 +89,7 @@
   out_value.setObject(*dictionary_object);
 }
 
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
                  int conversion_flags, ExceptionState* exception_state,
                  {{class_name}}* out_dictionary) {
   DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -137,7 +117,11 @@
   }
   if (!{{member.name}}.isUndefined()) {
     {{member.type}} converted_value;
-    FromJSValue(context, {{member.name}}, 0, exception_state, &converted_value);
+    FromJSValue(context,
+                {{member.name}},
+                {{member.conversion_flags}},
+                exception_state,
+                &converted_value);
     if (context->isExceptionPending()) {
       return;
     }
@@ -146,12 +130,10 @@
 {% endfor %}
 }
 
-}
-}
-}
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
 
 {% if conditional %}
 #endif  // defined({{conditional}})
-
 {% endif %}
-#endif  // {{class_name}}_h
diff --git a/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template
new file mode 100644
index 0000000..0c708c5
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template
@@ -0,0 +1,90 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ #     http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} 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.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#include "{{enum_include}}"
+
+#include "{{generated_conversion_include}}"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+using {{fully_qualified_name}};
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+    JSContext* context,
+    {{enumeration_name}} in_enum,
+    JS::MutableHandleValue out_value) {
+  switch (in_enum) {
+{% for value, idl_value in value_pairs %}
+    case {{namespace}}::{{value}}:
+      ToJSValue(context, std::string("{{idl_value}}"), out_value);
+      return;
+{% endfor %}
+    default:
+      NOTREACHED();
+      out_value.set(JS::UndefinedValue());
+  }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 {{enumeration_name}}* out_enum) {
+  DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+  // JSValue -> IDL enum algorithm described here:
+  // http://heycam.github.io/webidl/#es-enumeration
+  // 1. Let S be the result of calling ToString(V).
+  JS::RootedString rooted_string(context, JS::ToString(context, value));
+
+  bool match = false;
+// 3. Return the enumeration value of type E that is equal to S.
+{% for value, idl_value in value_pairs %}
+  {{-" else " if not loop.first}}if (JS_StringEqualsAscii(
+      context, rooted_string, "{{idl_value}}", &match)
+      && match) {
+    *out_enum = {{namespace}}::{{value}};
+  }{% endfor %} else {
+    // 2. If S is not one of E's enumeration values, then throw a TypeError.
+    exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+    return;
+  }
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/bindings/mozjs45/templates/generated-types.h.template b/src/cobalt/bindings/mozjs45/templates/generated-types.h.template
new file mode 100644
index 0000000..96d2650
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/templates/generated-types.h.template
@@ -0,0 +1,92 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ #     http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} 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.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#ifndef GENERATED_TYPE_CONVERSION_H_
+#define GENERATED_TYPE_CONVERSION_H_
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+// #includes for generated types.
+{% for path in includes %}
+#include "{{path}}"
+{% endfor %}
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+{% for dictionary in dictionaries %}
+// {{dictionary.fully_qualified_name}} -> JSValue
+void ToJSValue(
+    JSContext* context,
+    const {{dictionary.fully_qualified_name}}& in_value,
+    JS::MutableHandleValue out_value);
+
+// JSValue -> {{dictionary.fully_qualified_name}}
+void FromJSValue(
+    JSContext* context,
+    JS::HandleValue value,
+    int conversion_flags,
+    ExceptionState* exception_state,
+    {{dictionary.fully_qualified_name}}* out_value);
+
+{% endfor %}
+{% for enum in enumerations %}
+// {{enum.fully_qualified_name}} -> JSValue
+void ToJSValue(
+    JSContext* context,
+    {{enum.fully_qualified_name}} in_value,
+    JS::MutableHandleValue out_value);
+
+// JSValue -> {{enum.fully_qualified_name}}
+void FromJSValue(
+    JSContext* context,
+    JS::HandleValue value,
+    int conversion_flags,
+    ExceptionState* exception_state,
+    {{enum.fully_qualified_name}}* out_value);
+
+{% endfor %}
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+// #include this here so the conversion functions for generated types are
+// visible refer to template types, such as sequence<T>.
+#include "cobalt/script/mozjs-45/conversion_helpers.h"
+
+#endif  // GENERATED_TYPE_CONVERSION_H_
diff --git a/src/cobalt/bindings/mozjs45/templates/interface.cc.template b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
index 4e2839b..6296ec9 100644
--- a/src/cobalt/bindings/mozjs45/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
@@ -26,6 +26,8 @@
 {% extends "interface-base.cc.template" %}
 {% block includes %}
 {{ super() }}
+#include "{{generated_conversion_include}}"
+
 #include "base/lazy_instance.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -36,6 +38,8 @@
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -66,6 +70,7 @@
 using cobalt::script::mozjs::TypeTraits;
 using cobalt::script::mozjs::WrapperFactory;
 using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
 using cobalt::script::mozjs::kConversionFlagNullable;
 using cobalt::script::mozjs::kConversionFlagRestricted;
 using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -949,6 +954,7 @@
   {{binding_class}}::CreateProxy(
       context, global_interface);
   mozjs_global_environment->SetEnvironmentSettings(environment_settings);
+  mozjs_global_environment->EvaluateAutomatics();
 
   WrapperFactory* wrapper_factory =
       mozjs_global_environment->wrapper_factory();
diff --git a/src/cobalt/bindings/name_conversion.py b/src/cobalt/bindings/name_conversion.py
index 2a0c885..917b760 100644
--- a/src/cobalt/bindings/name_conversion.py
+++ b/src/cobalt/bindings/name_conversion.py
@@ -55,6 +55,7 @@
 
 
 def convert_to_cobalt_name(class_name):
+  """Convert an IDL attribute name to Cobalt naming convention."""
   replacement = []
 
   for token in special_token_re.split(class_name):
@@ -78,10 +79,10 @@
                         for token in constant_name.split('_')))
 
 
-def convert_to_cobalt_enumeration_value(enum_value):
-  return 'k' + ''.join(
+def convert_to_cobalt_enumeration_value(enum_type, enum_value):
+  return 'k%s%s' % (enum_type, ''.join(
       (token.capitalize()
-       for token in enumeration_value_word_delimeter_re.split(enum_value)))
+       for token in enumeration_value_word_delimeter_re.split(enum_value))))
 
 
 def get_interface_name(idl_type):
diff --git a/src/cobalt/bindings/path_generator.py b/src/cobalt/bindings/path_generator.py
index 6f3a089..46206cb 100644
--- a/src/cobalt/bindings/path_generator.py
+++ b/src/cobalt/bindings/path_generator.py
@@ -28,32 +28,53 @@
 class PathBuilder(object):
   """Provides helper functions for getting paths related to an interface."""
 
-  def __init__(self, engine_prefix, interfaces_info, interfaces_root,
+  def __init__(self, engine_prefix, info_provider, interfaces_root,
                generated_root_directory):
     self.interfaces_root = _NormalizeSlashes(interfaces_root)
     self.generated_root = _NormalizeSlashes(generated_root_directory)
     self.engine_prefix = engine_prefix
-    self.interfaces_info = interfaces_info
+    self.info_provider = info_provider
+    self.interfaces_info = info_provider.interfaces_info
+
+  @property
+  def generated_conversion_header_path(self):
+    return os.path.join(self.generated_root,
+                        '%s_gen_type_conversion.h' % self.engine_prefix)
+
+  @property
+  def generated_conversion_include_path(self):
+    return os.path.relpath(self.generated_conversion_header_path,
+                           self.generated_root)
 
   def NamespaceComponents(self, interface_name):
     """Get the interface's namespace as a list of namespace components."""
     # Get the IDL filename relative to the cobalt directory, and split the
     # directory to get the list of namespace components.
-    interface_info = self.interfaces_info[interface_name]
-    idl_path = interface_info['full_path']
+    if interface_name in self.interfaces_info:
+      interface_info = self.interfaces_info[interface_name]
+      idl_path = interface_info['full_path']
+    elif interface_name in self.info_provider.enumerations:
+      enum_info = self.info_provider.enumerations[interface_name]
+      idl_path = enum_info['full_path']
+    else:
+      raise KeyError('Unknown interface name %s', interface_name)
 
     idl_path = os.path.relpath(idl_path, self.interfaces_root)
     components = os.path.dirname(idl_path).split(os.sep)
     return [os.path.basename(self.interfaces_root)] + components
 
+  def Namespace(self, interface_name):
+    """Get the interface's namespace."""
+    return '::'.join(self.NamespaceComponents(interface_name))
+
   def BindingsClass(self, interface_name):
     """Get the name of the generated bindings class."""
     return self.engine_prefix.capitalize() + interface_name
 
   def FullBindingsClassName(self, interface_name):
     """Get the fully qualified name of the generated bindings class."""
-    components = self.NamespaceComponents(interface_name)
-    return '::'.join(components + [self.BindingsClass(interface_name)])
+    return '%s::%s' % (self.Namespace(interface_name),
+                       self.BindingsClass(interface_name))
 
   def FullClassName(self, interface_name):
     """Get the fully qualified name of the implementation class."""
@@ -109,12 +130,7 @@
         output_extension='h',
         base_directory=os.path.dirname(self.interfaces_root))
 
-  def DictionaryConversionHeaderIncludePath(self, dictionary_name):
-    """Get the #include path to the dictionary's conversion header."""
-    path = self.BindingsHeaderFullPath(dictionary_name)
-    return os.path.relpath(path, self.generated_root)
-
-  def DictionaryConversionHeaderFullPath(self, dictionary_name):
+  def DictionaryConversionImplementationPath(self, dictionary_name):
     """Get the full path to the dictionary's conversion header."""
     interface_info = self.interfaces_info[dictionary_name]
     return ConvertPath(
@@ -122,5 +138,31 @@
         forward_slashes=True,
         output_directory=self.generated_root,
         output_prefix='%s_' % self.engine_prefix,
+        output_extension='cc',
+        base_directory=os.path.dirname(self.interfaces_root))
+
+  def EnumHeaderIncludePath(self, enum_name):
+    """Get the #include path to the dictionary's header."""
+    path = self.EnumHeaderFullPath(enum_name)
+    return os.path.relpath(path, self.generated_root)
+
+  def EnumHeaderFullPath(self, enum_name):
+    """Get the full path to the dictionary's generated implementation header."""
+    interface_info = self.info_provider.enumerations[enum_name]
+    return ConvertPath(
+        interface_info['full_path'],
+        forward_slashes=True,
+        output_directory=self.generated_root,
         output_extension='h',
         base_directory=os.path.dirname(self.interfaces_root))
+
+  def EnumConversionImplementationFullPath(self, enum_name):
+    """Get the full path to the dictionary's conversion header."""
+    interface_info = self.info_provider.enumerations[enum_name]
+    return ConvertPath(
+        interface_info['full_path'],
+        forward_slashes=True,
+        output_directory=self.generated_root,
+        output_prefix='%s_' % self.engine_prefix,
+        output_extension='cc',
+        base_directory=os.path.dirname(self.interfaces_root))
diff --git a/src/cobalt/bindings/templates/dictionary.h.template b/src/cobalt/bindings/templates/dictionary.h.template
index 01eca9e..2999e8b 100644
--- a/src/cobalt/bindings/templates/dictionary.h.template
+++ b/src/cobalt/bindings/templates/dictionary.h.template
@@ -48,6 +48,16 @@
 #include "{{include}}"
 {% endfor %}
 
+{% for used_class in forward_declarations %}
+{% if used_class.conditional %}
+#if defined({{used_class.conditional}})
+{% endif %}
+using {{used_class.fully_qualified_name}};
+{% if used_class.conditional %}
+#endif  // defined({{used_class.conditional}})
+{% endif %}
+{% endfor %}
+
 {% for component in components %}
 namespace {{component}} {
 {% endfor %}
diff --git a/src/cobalt/bindings/templates/enumeration.h.template b/src/cobalt/bindings/templates/enumeration.h.template
new file mode 100644
index 0000000..d161069
--- /dev/null
+++ b/src/cobalt/bindings/templates/enumeration.h.template
@@ -0,0 +1,54 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ #     http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} 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.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#ifndef {{enumeration_name}}_h
+#define {{enumeration_name}}_h
+
+{% for component in components %}
+namespace {{component}} {
+{% endfor %}
+
+enum {{enumeration_name}} {
+{% for value, idl_value in value_pairs %}
+  {{value}},
+{% endfor %}
+};
+
+{% for component in components %}
+}  // namespace {{component}}
+{% endfor %}
+
+#endif  // {{enumeration_name}}_h
diff --git a/src/cobalt/bindings/templates/interface-base.cc.template b/src/cobalt/bindings/templates/interface-base.cc.template
index dfe5675..cf3ccdf 100644
--- a/src/cobalt/bindings/templates/interface-base.cc.template
+++ b/src/cobalt/bindings/templates/interface-base.cc.template
@@ -70,10 +70,6 @@
 using cobalt::script::ValueHandle;
 using cobalt::script::Wrappable;
 {% endblock using_directives %}
-{% if enumerations|length %}
-{% block enumeration_declarations %}
-{% endblock enumeration_declarations %}
-{% endif %}
 {% block top_level_unnamed_namespace %}
 {% endblock top_level_unnamed_namespace %}
 }  // namespace
@@ -104,13 +100,6 @@
 }  // namespace script
 }  // namespace cobalt
 {% endif %}
-{% if enumerations|length %}
-
-namespace {
-{% block enumeration_definitions %}
-{% endblock enumeration_definitions %}
-}  // namespace
-{% endif %}
 {% if conditional %}
 #endif  // defined({{conditional}})
 {% endif %}
diff --git a/src/cobalt/bindings/testing/bindings_test_base.h b/src/cobalt/bindings/testing/bindings_test_base.h
index f6e42a2..768dfb3 100644
--- a/src/cobalt/bindings/testing/bindings_test_base.h
+++ b/src/cobalt/bindings/testing/bindings_test_base.h
@@ -47,8 +47,7 @@
   BindingsTestBase()
       : environment_settings_(new script::EnvironmentSettings),
         engine_(script::JavaScriptEngine::CreateEngine()),
-        global_environment_(engine_->CreateGlobalEnvironment(
-            script::JavaScriptEngine::Options())),
+        global_environment_(engine_->CreateGlobalEnvironment()),
         window_(new Window()) {
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
@@ -57,8 +56,7 @@
   explicit BindingsTestBase(const scoped_refptr<Window> window)
       : environment_settings_(new script::EnvironmentSettings),
         engine_(script::JavaScriptEngine::CreateEngine()),
-        global_environment_(engine_->CreateGlobalEnvironment(
-            script::JavaScriptEngine::Options())),
+        global_environment_(engine_->CreateGlobalEnvironment()),
         window_(window) {
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
diff --git a/src/cobalt/bindings/testing/dictionary_interface.h b/src/cobalt/bindings/testing/dictionary_interface.h
index 4ec4793..4d8debf 100644
--- a/src/cobalt/bindings/testing/dictionary_interface.h
+++ b/src/cobalt/bindings/testing/dictionary_interface.h
@@ -19,6 +19,7 @@
 
 #include <string>
 
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
 #include "cobalt/bindings/testing/test_dictionary.h"
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
@@ -37,6 +38,8 @@
   MOCK_METHOD1(set_dictionary_sequence,
                void(script::Sequence<TestDictionary> test_dictionary));
 
+  void TestOperation(DictionaryWithDictionaryMember /* dict */) {}
+
   DEFINE_WRAPPABLE_TYPE(DictionaryInterface);
 };
 
diff --git a/src/cobalt/bindings/testing/dictionary_interface.idl b/src/cobalt/bindings/testing/dictionary_interface.idl
index ddb725c..80f4f21 100644
--- a/src/cobalt/bindings/testing/dictionary_interface.idl
+++ b/src/cobalt/bindings/testing/dictionary_interface.idl
@@ -17,4 +17,7 @@
 interface DictionaryInterface {
   void dictionaryOperation(TestDictionary dictionary);
   attribute sequence<TestDictionary> dictionarySequence;
+
+  // Dictionary with dictionary member.
+  void testOperation(DictionaryWithDictionaryMember dict);
 };
diff --git a/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl b/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl
new file mode 100644
index 0000000..60f967a
--- /dev/null
+++ b/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+dictionary DictionaryWithDictionaryMember {
+  TestDictionary nestedDictionary;
+};
diff --git a/src/cobalt/bindings/testing/enumeration_bindings_test.cc b/src/cobalt/bindings/testing/enumeration_bindings_test.cc
index 9ed5c79..c991c20 100644
--- a/src/cobalt/bindings/testing/enumeration_bindings_test.cc
+++ b/src/cobalt/bindings/testing/enumeration_bindings_test.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/bindings/testing/bindings_test_base.h"
 #include "cobalt/bindings/testing/enumeration_interface.h"
+#include "cobalt/bindings/testing/test_enum.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -31,13 +32,13 @@
 TEST_F(EnumerationBindingsTest, SetEnumeration) {
   InSequence dummy;
 
-  EXPECT_CALL(test_mock(), set_enum_property(EnumerationInterface::kAlpha));
+  EXPECT_CALL(test_mock(), set_enum_property(kTestEnumAlpha));
   EXPECT_TRUE(EvaluateScript("test.enumProperty = \"alpha\";", NULL));
 
-  EXPECT_CALL(test_mock(), set_enum_property(EnumerationInterface::kBeta));
+  EXPECT_CALL(test_mock(), set_enum_property(kTestEnumBeta));
   EXPECT_TRUE(EvaluateScript("test.enumProperty = \"beta\";", NULL));
 
-  EXPECT_CALL(test_mock(), set_enum_property(EnumerationInterface::kGamma));
+  EXPECT_CALL(test_mock(), set_enum_property(kTestEnumGamma));
   EXPECT_TRUE(EvaluateScript("test.enumProperty = \"gamma\";", NULL));
 }
 
@@ -45,18 +46,15 @@
   InSequence dummy;
   std::string result;
 
-  EXPECT_CALL(test_mock(), enum_property())
-      .WillOnce(Return(EnumerationInterface::kAlpha));
+  EXPECT_CALL(test_mock(), enum_property()).WillOnce(Return(kTestEnumAlpha));
   EXPECT_TRUE(EvaluateScript("test.enumProperty == \"alpha\";", &result));
   EXPECT_STREQ("true", result.c_str());
 
-  EXPECT_CALL(test_mock(), enum_property())
-      .WillOnce(Return(EnumerationInterface::kBeta));
+  EXPECT_CALL(test_mock(), enum_property()).WillOnce(Return(kTestEnumBeta));
   EXPECT_TRUE(EvaluateScript("test.enumProperty == \"beta\";", &result));
   EXPECT_STREQ("true", result.c_str());
 
-  EXPECT_CALL(test_mock(), enum_property())
-      .WillOnce(Return(EnumerationInterface::kGamma));
+  EXPECT_CALL(test_mock(), enum_property()).WillOnce(Return(kTestEnumGamma));
   EXPECT_TRUE(EvaluateScript("test.enumProperty == \"gamma\";", &result));
   EXPECT_STREQ("true", result.c_str());
 }
diff --git a/src/cobalt/bindings/testing/enumeration_interface.h b/src/cobalt/bindings/testing/enumeration_interface.h
index e6c0d16..58938ff 100644
--- a/src/cobalt/bindings/testing/enumeration_interface.h
+++ b/src/cobalt/bindings/testing/enumeration_interface.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_BINDINGS_TESTING_ENUMERATION_INTERFACE_H_
 #define COBALT_BINDINGS_TESTING_ENUMERATION_INTERFACE_H_
 
+#include "cobalt/bindings/testing/test_enum.h"
 #include "cobalt/script/wrappable.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -24,10 +25,11 @@
 
 class EnumerationInterface : public script::Wrappable {
  public:
-  enum TestEnum { kAlpha, kBeta, kGamma };
   MOCK_METHOD0(enum_property, TestEnum());
   MOCK_METHOD1(set_enum_property, void(TestEnum));
 
+  void OptionalEnumWithDefault(TestEnum /* value */) {}
+
   DEFINE_WRAPPABLE_TYPE(EnumerationInterface);
 };
 
diff --git a/src/cobalt/bindings/testing/enumeration_interface.idl b/src/cobalt/bindings/testing/enumeration_interface.idl
index b56de55..3116abb 100644
--- a/src/cobalt/bindings/testing/enumeration_interface.idl
+++ b/src/cobalt/bindings/testing/enumeration_interface.idl
@@ -12,13 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-enum TestEnum {
-  "alpha",
-  "beta",
-  "gamma"
-};
-
 [Constructor]
 interface EnumerationInterface {
   attribute TestEnum enumProperty;
+
+  void optionalEnumWithDefault(optional TestEnum value = "beta");
 };
diff --git a/src/cobalt/bindings/testing/exceptions_bindings_test.cc b/src/cobalt/bindings/testing/exceptions_bindings_test.cc
index 505ed9a..43c221a 100644
--- a/src/cobalt/bindings/testing/exceptions_bindings_test.cc
+++ b/src/cobalt/bindings/testing/exceptions_bindings_test.cc
@@ -59,16 +59,6 @@
   scoped_refptr<script::ScriptException> exception_object_;
 };
 
-std::string GetExceptionMessageString(script::MessageType message_type,
-                                      int dummy, ...) {
-  va_list arguments;
-  va_start(arguments, dummy);
-  std::string error_string = base::StringPrintV(
-      GetExceptionMessageFormat(message_type), arguments);
-  va_end(arguments);
-  return error_string;
-}
-
 }  // namespace
 
 TEST_F(ExceptionsBindingsTest, ThrowExceptionFromConstructor) {
@@ -147,16 +137,6 @@
   EXPECT_STREQ("the message", result.c_str());
 }
 
-TEST_F(ExceptionsBindingsTest, GetExceptionMessageStringTest) {
-  std::string error_message =
-      GetExceptionMessageString(script::kWrongByteLengthMultiple, 0, 8);
-  EXPECT_STREQ("Byte length should be a multiple of 8.", error_message.c_str());
-  error_message =
-      GetExceptionMessageString(script::kWrongByteOffsetMultiple, 0, 16);
-  EXPECT_STREQ("Byte offset should be a multiple of 16.",
-               error_message.c_str());
-}
-
 }  // namespace testing
 }  // namespace bindings
 }  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/extended_attributes_test.cc b/src/cobalt/bindings/testing/extended_attributes_test.cc
index 09ca65d..88d8b78 100644
--- a/src/cobalt/bindings/testing/extended_attributes_test.cc
+++ b/src/cobalt/bindings/testing/extended_attributes_test.cc
@@ -43,6 +43,35 @@
                              NULL));
 }
 
+TEST_F(ExtendedAttributesTest, ImplementedAsAttributeGetter) {
+  EXPECT_CALL(test_mock(), attribute_default()).WillOnce(Return(true));
+  EXPECT_TRUE(EvaluateScript("var result = test.default;", NULL));
+
+  std::string result;
+  EXPECT_TRUE(EvaluateScript("typeof result;", &result));
+  EXPECT_STREQ("boolean", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("result;", &result));
+  EXPECT_STREQ("true", result.c_str());
+
+  EXPECT_CALL(test_mock(), attribute_default()).WillOnce(Return(false));
+  EXPECT_TRUE(EvaluateScript("var result = test.default;", NULL));
+
+  EXPECT_TRUE(EvaluateScript("typeof result;", &result));
+  EXPECT_STREQ("boolean", result.c_str());
+
+  EXPECT_TRUE(EvaluateScript("result;", &result));
+  EXPECT_STREQ("false", result.c_str());
+}
+
+TEST_F(ExtendedAttributesTest, ImplementedAsAttributeSetter) {
+  EXPECT_CALL(test_mock(), set_attribute_default(true));
+  EXPECT_TRUE(EvaluateScript(StringPrintf("test.default = true;"), NULL));
+
+  EXPECT_CALL(test_mock(), set_attribute_default(false));
+  EXPECT_TRUE(EvaluateScript(StringPrintf("test.default = false;"), NULL));
+}
+
 }  // namespace testing
 }  // namespace bindings
 }  // namespace cobalt
diff --git a/src/cobalt/bindings/testing/extended_idl_attributes_interface.h b/src/cobalt/bindings/testing/extended_idl_attributes_interface.h
index 1b8c901..897d80d 100644
--- a/src/cobalt/bindings/testing/extended_idl_attributes_interface.h
+++ b/src/cobalt/bindings/testing/extended_idl_attributes_interface.h
@@ -27,6 +27,8 @@
  public:
   MOCK_METHOD1(CallWithSettings, void(script::EnvironmentSettings*));
   MOCK_METHOD1(ClampArgument, void(uint16_t));
+  MOCK_METHOD0(attribute_default, bool());
+  MOCK_METHOD1(set_attribute_default, void(bool));
 
   DEFINE_WRAPPABLE_TYPE(ExtendedIDLAttributesInterface);
 };
diff --git a/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl b/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl
index 9535a26..fe4b8d2 100644
--- a/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl
+++ b/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl
@@ -15,4 +15,5 @@
 interface ExtendedIDLAttributesInterface {
   [CallWith=EnvironmentSettings] void callWithSettings();
   void clampArgument([Clamp] unsigned short arg);
+  [ImplementedAs=attribute_default] attribute boolean default;
 };
diff --git a/src/cobalt/base/math.cc b/src/cobalt/bindings/testing/test_enum.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/bindings/testing/test_enum.idl
index d335495..d674515 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/bindings/testing/test_enum.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum TestEnum {
+  "alpha",
+  "beta",
+  "gamma",
+  "enum-with-dashes",
+  "enum with spaces",
+  "terrible----enum",
+  "this is a terrible @#$%#$% enum"
+};
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp
index d005089..c32311a 100644
--- a/src/cobalt/bindings/testing/testing.gyp
+++ b/src/cobalt/bindings/testing/testing.gyp
@@ -75,8 +75,10 @@
         'window.idl',
     ],
 
-    'dictionary_idl_files': [
+    'generated_header_idl_files': [
+        'dictionary_with_dictionary_member.idl',
         'test_dictionary.idl',
+        'test_enum.idl'
     ],
 
     # Partial interfaces and the right-side of "implements"
@@ -127,13 +129,13 @@
       ],
       'defines': [ '<@(bindings_defines)'],
       'dependencies': [
-        'generated_dictionaries',
+        'generated_types',
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
       ],
       'export_dependent_settings': [
-        'generated_dictionaries',
+        'generated_types',
       ],
     },
     {
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 1aa2b3b..77ecccd 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/browser/application.h"
 
+#include <algorithm>
 #include <string>
 #include <vector>
 
@@ -32,8 +33,8 @@
 #include "cobalt/base/language.h"
 #include "cobalt/base/localized_strings.h"
 #include "cobalt/base/user_log.h"
-#include "cobalt/browser/memory_settings/memory_settings.h"
-#include "cobalt/browser/memory_tracker/memory_tracker_tool.h"
+#include "cobalt/browser/memory_settings/auto_mem.h"
+#include "cobalt/browser/memory_tracker/tool.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/loader/image/image_decoder.h"
 #include "cobalt/math/size.h"
@@ -51,6 +52,7 @@
 #include "lbshell/src/lb_memory_pages.h"
 #endif  // defined(__LB_SHELL__)
 #if defined(OS_STARBOARD)
+#include "nb/lexical_cast.h"
 #include "starboard/configuration.h"
 #include "starboard/log.h"
 #endif  // defined(OS_STARBOARD)
@@ -195,6 +197,30 @@
 }
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
+// Represents a parsed int.
+struct ParsedIntValue {
+ public:
+  ParsedIntValue() : value_(0), error_(false) {}
+  ParsedIntValue(const ParsedIntValue& other)
+      : value_(other.value_), error_(other.error_) {}
+  int value_;
+  bool error_;  // true if there was a parse error.
+};
+// Parses a string like "1234x5678" to vector of parsed int values.
+std::vector<ParsedIntValue> ParseDimensions(const std::string& value_str) {
+  std::vector<ParsedIntValue> output;
+
+  std::vector<std::string> lengths;
+  base::SplitString(value_str, 'x', &lengths);
+
+  for (size_t i = 0; i < lengths.size(); ++i) {
+    ParsedIntValue parsed_value;
+    parsed_value.error_ = !base::StringToInt(lengths[i], &parsed_value.value_);
+    output.push_back(parsed_value);
+  }
+  return output;
+}
+
 std::string GetMinLogLevelString() {
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -241,19 +267,33 @@
                           &options->surface_cache_size_in_bytes);
   SetIntegerIfSwitchIsSet(browser::switches::kScratchSurfaceCacheSizeInBytes,
                           &options->scratch_surface_cache_size_in_bytes);
-  SetIntegerIfSwitchIsSet(browser::switches::kSkiaCacheSizeInBytes,
-                          &options->skia_cache_size_in_bytes);
-  SetIntegerIfSwitchIsSet(browser::switches::kSoftwareSurfaceCacheSizeInBytes,
-                          &options->software_surface_cache_size_in_bytes);
 }
 
 void ApplyCommandLineSettingsToWebModuleOptions(WebModule::Options* options) {
-  SetIntegerIfSwitchIsSet(browser::switches::kImageCacheSizeInBytes,
-                          &options->image_cache_capacity);
   SetIntegerIfSwitchIsSet(browser::switches::kRemoteTypefaceCacheSizeInBytes,
                           &options->remote_typeface_cache_capacity);
 }
 
+template <typename T>
+base::optional<T> ParseSetting(const CommandLine* command_line,
+                               const char* switch_name) {
+  base::optional<T> output;
+  if (!command_line->HasSwitch(switch_name)) {
+    return output;
+  }
+  std::string switch_value = command_line->GetSwitchValueNative(switch_name);
+
+  bool parse_ok = false;
+  T value = nb::lexical_cast<T>(switch_value.c_str(), &parse_ok);
+
+  if (parse_ok) {
+    output = static_cast<T>(value);
+  } else {
+    LOG(ERROR) << "Invalid value for command line setting: " << switch_name;
+  }
+  return output;
+}
+
 // Restrict navigation to a couple of whitelisted URLs by default.
 const char kYouTubeTvLocationPolicy[] =
     "h5vcc-location-src "
@@ -311,6 +351,29 @@
 int Application::network_connect_count_ = 0;
 int Application::network_disconnect_count_ = 0;
 
+void ApplyAutoMemSettings(const memory_settings::AutoMem& auto_mem,
+                          BrowserModule::Options* options) {
+  std::stringstream ss;
+  ss << "\n\n" << auto_mem.ToPrettyPrintString() << "\n\n";
+  SB_LOG(INFO) << ss.str();
+
+  options->web_module_options.image_cache_capacity =
+      static_cast<int>(auto_mem.image_cache_size_in_bytes()->value());
+
+  options->renderer_module_options.skia_cache_size_in_bytes =
+      static_cast<int>(auto_mem.skia_cache_size_in_bytes()->value());
+
+  options->renderer_module_options.skia_texture_atlas_dimensions =
+      auto_mem.skia_atlas_texture_dimensions()->value();
+
+  options->web_module_options.javascript_options.gc_threshold_bytes =
+      static_cast<size_t>(auto_mem.javascript_gc_threshold_in_bytes()->value());
+
+  options->renderer_module_options.software_surface_cache_size_in_bytes =
+      static_cast<int>(
+          auto_mem.software_surface_cache_size_in_bytes()->value());
+}
+
 Application::Application(const base::Closure& quit_closure)
     : message_loop_(MessageLoop::current()),
       quit_closure_(quit_closure),
@@ -357,8 +420,7 @@
   CommandLine* command_line = CommandLine::ForCurrentProcess();
   math::Size window_size = InitSystemWindow(command_line);
 
-  WebModule::Options web_options(window_size);
-
+  WebModule::Options web_options;
   // Create the main components of our browser.
   BrowserModule::Options options(web_options);
   options.web_module_options.name = "MainWebModule";
@@ -373,6 +435,10 @@
     options.web_module_options.javascript_options.disable_jit = true;
   }
 
+  memory_settings::AutoMem auto_mem(window_size, *command_line,
+                                    memory_settings::GetDefaultBuildSettings());
+  ApplyAutoMemSettings(auto_mem, &options);
+
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   if (command_line->HasSwitch(browser::switches::kNullSavegame)) {
     options.storage_manager_options.savegame_options.factory =
@@ -691,23 +757,31 @@
   if (command_line->HasSwitch(browser::switches::kViewport)) {
     const std::string switchValue =
         command_line->GetSwitchValueASCII(browser::switches::kViewport);
-    std::vector<std::string> lengths;
-    base::SplitString(switchValue, 'x', &lengths);
-    if (lengths.size() >= 1) {
-      int width = -1;
-      if (base::StringToInt(lengths[0], &width) && width >= 1) {
-        int height = -1;
-        if (lengths.size() < 2) {
+
+    std::vector<ParsedIntValue> parsed_ints = ParseDimensions(switchValue);
+
+    if (parsed_ints.size() >= 1) {
+      const ParsedIntValue parsed_width = parsed_ints[0];
+      if (!parsed_width.error_) {
+        const ParsedIntValue* parsed_height_ptr = NULL;
+        if (parsed_ints.size() >= 2) {
+          parsed_height_ptr = &parsed_ints[1];
+        }
+
+        if (!parsed_height_ptr) {
           // Allow shorthand specification of the viewport by only giving the
           // width. This calculates the height at 4:3 aspect ratio for smaller
           // viewport widths, and 16:9 for viewports 1280 pixels wide or larger.
-          if (width >= 1280) {
-            viewport_size.emplace(width, 9 * width / 16);
+          if (parsed_width.value_ >= 1280) {
+            viewport_size.emplace(parsed_width.value_,
+                                  9 * parsed_width.value_ / 16);
           } else {
-            viewport_size.emplace(width, 3 * width / 4);
+            viewport_size.emplace(parsed_width.value_,
+                                  3 * parsed_width.value_ / 4);
           }
-        } else if (base::StringToInt(lengths[1], &height) && height >= 1) {
-          viewport_size.emplace(width, height);
+        } else if (!parsed_height_ptr->error_) {
+          viewport_size.emplace(parsed_width.value_,
+                                parsed_height_ptr->value_);
         } else {
           DLOG(ERROR) << "Invalid value specified for viewport height: "
                       << switchValue << ". Using default viewport size.";
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 2c10118..6c1ac47 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -22,7 +22,7 @@
 #include "cobalt/account/account_manager.h"
 #include "cobalt/base/event_dispatcher.h"
 #include "cobalt/browser/browser_module.h"
-#include "cobalt/browser/memory_tracker/memory_tracker_tool.h"
+#include "cobalt/browser/memory_tracker/tool.h"
 #include "cobalt/system_window/system_window.h"
 
 #if defined(ENABLE_WEBDRIVER)
@@ -172,7 +172,7 @@
   base::Timer stats_update_timer_;
   base::Timer lite_stats_update_timer_;
 
-  scoped_ptr<memory_tracker::MemoryTrackerTool> memory_tracker_tool_;
+  scoped_ptr<memory_tracker::Tool> memory_tracker_tool_;
 };
 
 // Factory method for creating an application.  It should be implemented
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index a3d141a..db75521 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -29,14 +29,34 @@
         'debug_console.h',
         'h5vcc_url_handler.cc',
         'h5vcc_url_handler.h',
+        'memory_settings/auto_mem.cc',
+        'memory_settings/auto_mem.h',
+        'memory_settings/build_settings.cc',
+        'memory_settings/build_settings.h',
+        'memory_settings/calculations.cc',
+        'memory_settings/calculations.h',
         'memory_settings/memory_settings.cc',
         'memory_settings/memory_settings.h',
-        'memory_tracker/buffered_file_writer.cc',
-        'memory_tracker/buffered_file_writer.h',
-        'memory_tracker/memory_tracker_tool.cc',
-        'memory_tracker/memory_tracker_tool.h',
-        'memory_tracker/memory_tracker_tool_impl.cc',
-        'memory_tracker/memory_tracker_tool_impl.h',
+        'memory_settings/pretty_print.cc',
+        'memory_settings/pretty_print.h',
+        'memory_tracker/tool.cc',
+        'memory_tracker/tool.h',
+        'memory_tracker/tool/buffered_file_writer.cc',
+        'memory_tracker/tool/buffered_file_writer.h',
+        'memory_tracker/tool/compressed_time_series_tool.cc',
+        'memory_tracker/tool/compressed_time_series_tool.h',
+        'memory_tracker/tool/leak_finder_tool.cc',
+        'memory_tracker/tool/leak_finder_tool.h',
+        'memory_tracker/tool/log_writer_tool.cc',
+        'memory_tracker/tool/log_writer_tool.h',
+        'memory_tracker/tool/params.cc',
+        'memory_tracker/tool/params.h',
+        'memory_tracker/tool/tool_impl.cc',
+        'memory_tracker/tool/tool_impl.h',
+        'memory_tracker/tool/tool_thread.cc',
+        'memory_tracker/tool/tool_thread.h',
+        'memory_tracker/tool/util.cc',
+        'memory_tracker/tool/util.h',
         'render_tree_combiner.cc',
         'render_tree_combiner.h',
         'resource_provider_array_buffer_allocator.cc',
@@ -57,9 +77,14 @@
         'web_module_stat_tracker.h',
       ],
       'defines': [
+        'COBALT_SKIA_CACHE_SIZE_IN_BYTES=<(skia_cache_size_in_bytes)',
+        'COBALT_SKIA_GLYPH_ATLAS_WIDTH=<(skia_glyph_atlas_width)',
+        'COBALT_SKIA_GLYPH_ATLAS_HEIGHT=<(skia_glyph_atlas_height)',
         'COBALT_IMAGE_CACHE_SIZE_IN_BYTES=<(image_cache_size_in_bytes)',
-        'COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES=<(remote_typeface_cache_size_in_bytes)',
+        'COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES=<(remote_font_cache_size_in_bytes)',
         'COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO=<(image_cache_capacity_multiplier_when_playing_video)',
+        'COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES=<(software_surface_cache_size_in_bytes)',
+        'COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES=<(mozjs_garbage_collection_threshold_in_bytes)',
       ],
       'dependencies': [
         '<@(cobalt_platform_dependencies)',
@@ -171,11 +196,12 @@
       'type': '<(gtest_target_type)',
       'sources': [
         'storage_upgrade_handler_test.cc',
+        'memory_settings/auto_mem_test.cc',
+        'memory_settings/calculations_test.cc',
+        'memory_settings/pretty_print_test.cc',
         'memory_settings/memory_settings_test.cc',
-        'memory_tracker/memory_tracker_tool_test.cc',
-      ],
-      'defines': [
-        'COBALT_IMAGE_CACHE_SIZE_IN_BYTES=<(image_cache_size_in_bytes)',
+        'memory_tracker/tool/tool_impl_test.cc',
+        'memory_tracker/tool/util_test.cc',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index 7572715..aa35c9a 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -117,7 +117,7 @@
         #'../dom/media_key_message_event_init.idl',
         '../dom/media_key_needed_event.idl',
         #'../dom/media_key_session.idl',
-        '../dom/media_key_status_map.idl',
+        #'../dom/media_key_status_map.idl',
         #'../dom/media_key_system_access.idl',
         #'../dom/media_key_system_configuration.idl',
         #'../dom/media_key_system_media_capability.idl',
@@ -211,6 +211,7 @@
         '../webdriver/script_executor_params.idl',
         '../webdriver/script_executor_result.idl',
 
+        '../websocket/close_event.idl',
         '../websocket/web_socket.idl',
 
         '../xhr/xml_http_request.idl',
@@ -218,10 +219,30 @@
         '../xhr/xml_http_request_upload.idl',
     ],
 
-    'dictionary_idl_files': [
+
+    # IDL files that will end up generating a .h that will be #included in
+    # Cobalt directly. IDL files for dictionaries and enums.
+    'generated_header_idl_files': [
+        '../audio/audio_node_channel_count_mode.idl',
+        '../audio/audio_node_channel_interpretation.idl',
+        '../dom/blob_property_bag.idl',
+        '../dom/dom_parser_supported_type.idl',
+        '../dom/media_key_status.idl',
+        '../dom/media_keys_requirement.idl',
+        '../dom/media_source_end_of_stream_error.idl',
+        '../dom/media_source_ready_state.idl',
         '../dom/mutation_observer_init.idl',
+        '../dom/source_buffer_append_mode.idl',
+        '../dom/track_default_type.idl',
         '../media_session/media_image.idl',
         '../media_session/media_metadata_init.idl',
+        '../media_session/media_session_action.idl',
+        '../media_session/media_session_playback_state.idl',
+        '../speech/speech_synthesis_error_code.idl',
+        '../speech/speech_recognition_error_code.idl',
+        '../websocket/close_event_init.idl',
+        '../web_animations/animation_fill_mode.idl',
+        '../web_animations/animation_playback_direction.idl',
     ],
 
     # Partial interfaces and the right-side of "implements". Also includes
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 32c9309..fb03d85 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -35,6 +35,8 @@
 #include "cobalt/browser/switches.h"
 #include "cobalt/dom/csp_delegate_factory.h"
 #include "cobalt/dom/keycode.h"
+#include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/window.h"
 #include "cobalt/h5vcc/h5vcc.h"
 #include "cobalt/input/input_device_manager_fuzzer.h"
 #include "nb/memory_scope.h"
@@ -189,8 +191,11 @@
 #endif
 
 scoped_refptr<script::Wrappable> CreateH5VCC(
-    const h5vcc::H5vcc::Settings& settings) {
-  return scoped_refptr<script::Wrappable>(new h5vcc::H5vcc(settings));
+    const h5vcc::H5vcc::Settings& settings,
+    const scoped_refptr<dom::Window>& window,
+    dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+  return scoped_refptr<script::Wrappable>(
+      new h5vcc::H5vcc(settings, window, mutation_observer_task_manager));
 }
 
 }  // namespace
@@ -358,7 +363,8 @@
   DCHECK(web_module_);
   web_module_->ExecuteJavascript(
       "location.reload();",
-      base::SourceLocation("[object BrowserModule]", 1, 1));
+      base::SourceLocation("[object BrowserModule]", 1, 1),
+      NULL /* output: succeeded */);
 }
 
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
@@ -433,6 +439,7 @@
                       base::Unretained(this)),
       base::Bind(&BrowserModule::OnError, base::Unretained(this)),
       base::Bind(&BrowserModule::OnWindowClose, base::Unretained(this)),
+      base::Bind(&BrowserModule::OnWindowMinimize, base::Unretained(this)),
       media_module_.get(), &network_module_, viewport_size,
       renderer_module_.pipeline()->GetResourceProvider(), system_window_,
       kLayoutMaxRefreshFrequencyInHz, options));
@@ -525,7 +532,21 @@
 #if defined(OS_STARBOARD)
   SbSystemRequestStop(0);
 #else
-  LOG(WARNING) << "window.close is not supported on this platform.";
+  LOG(WARNING) << "window.close() is not supported on this platform.";
+#endif
+}
+
+void BrowserModule::OnWindowMinimize() {
+#if defined(ENABLE_DEBUG_CONSOLE)
+  if (input_device_manager_fuzzer_) {
+    return;
+  }
+#endif
+
+#if defined(OS_STARBOARD) && SB_API_VERSION >= 4
+  SbSystemRequestSuspend();
+#else
+  LOG(WARNING) << "window.minimize() is not supported on this platform.";
 #endif
 }
 
@@ -857,7 +878,13 @@
 
   renderer_module_.Resume();
 
-  media_module_->Resume();
+  // Note that at this point, it is probable that this resource provider is
+  // different than the one that was managed in the associated call to
+  // Suspend().
+  render_tree::ResourceProvider* resource_provider =
+      renderer_module_.pipeline()->GetResourceProvider();
+
+  media_module_->Resume(resource_provider);
 
 #if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
   // Resume() is not supported on platforms that allocates ArrayBuffer on GPU
@@ -865,12 +892,6 @@
   NOTREACHED();
 #endif  // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
 
-  // Note that at this point, it is probable that this resource provider is
-  // different than the one that was managed in the associated call to
-  // Suspend().
-  render_tree::ResourceProvider* resource_provider =
-      renderer_module_.pipeline()->GetResourceProvider();
-
 #if defined(ENABLE_DEBUG_CONSOLE)
   if (debug_console_) {
     debug_console_->Resume(resource_provider);
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 7acc494..fcb24ad 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -184,9 +184,12 @@
   // Destroys the splash screen, if currently displayed.
   void DestroySplashScreen();
 
-  // Called when web module has received window.close.
+  // Called when web module has received window.close().
   void OnWindowClose();
 
+  // Called when web module has received window.minimize().
+  void OnWindowMinimize();
+
 #if defined(ENABLE_DEBUG_CONSOLE)
   // Toggles the input fuzzer on/off.  Ignores the parameter.
   void OnFuzzerToggle(const std::string&);
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index f5bcfe9..6950cdd 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -20,10 +20,12 @@
     {
       'target_name': 'cobalt',
       'type': '<(final_executable_type)',
-      'sources': [
-        'main.cc',
-      ],
       'conditions': [
+        ['cobalt_enable_lib == 1', {
+            'sources': ['lib/main.cc',],
+        }, {
+            'sources': ['main.cc',],
+        }],
         ['OS=="lb_shell"', {
           'dependencies': [
             '<(DEPTH)/cobalt/browser/<(actual_target_arch)/platform_browser.gyp:platform_browser',
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index acb019d..fea12f6 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -15,7 +15,6 @@
 #if defined(ENABLE_DEBUG_CONSOLE)
 
 #include "cobalt/browser/debug_console.h"
-
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/file_util.h"
@@ -24,6 +23,8 @@
 #include "cobalt/base/source_location.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/dom/csp_delegate_factory.h"
+#include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/window.h"
 
 namespace cobalt {
 namespace browser {
@@ -147,7 +148,11 @@
 // A function to create a DebugHub object, to be injected into WebModule.
 scoped_refptr<script::Wrappable> CreateDebugHub(
     const debug::DebugHub::GetHudModeCallback& get_hud_mode_function,
-    const debug::Debugger::GetDebugServerCallback& get_debug_server_callback) {
+    const debug::Debugger::GetDebugServerCallback& get_debug_server_callback,
+    const scoped_refptr<dom::Window>& window,
+    dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+  UNREFERENCED_PARAMETER(window);
+  UNREFERENCED_PARAMETER(mutation_observer_task_manager);
   return new debug::DebugHub(get_hud_mode_function, get_debug_server_callback);
 }
 
@@ -163,9 +168,11 @@
     const script::JavaScriptEngine::Options& js_options) {
   mode_ = GetInitialMode();
 
-  WebModule::Options web_module_options(window_dimensions);
+  WebModule::Options web_module_options;
   web_module_options.javascript_options = js_options;
   web_module_options.name = "DebugConsoleWebModule";
+  // The debug console does not load any image assets.
+  web_module_options.image_cache_capacity = 0;
   // Disable CSP for the Debugger's WebModule. This will also allow eval() in
   // javascript.
   web_module_options.csp_enforcement_mode = dom::kCspEnforcementDisable;
@@ -183,6 +190,7 @@
       GURL(kInitialDebugConsoleUrl), render_tree_produced_callback,
       base::Bind(&DebugConsole::OnError, base::Unretained(this)),
       base::Closure(), /* window_close_callback */
+      base::Closure(), /* window_minimize_callback */
       media_module, network_module, window_dimensions, resource_provider,
       media_module->system_window(), layout_refresh_rate, web_module_options));
 }
diff --git a/src/cobalt/browser/lib/README.md b/src/cobalt/browser/lib/README.md
new file mode 100644
index 0000000..55af499
--- /dev/null
+++ b/src/cobalt/browser/lib/README.md
@@ -0,0 +1,9 @@
+This directory provides an API for clients that build Cobalt into a lib using
+the 'cobalt_enable_lib' flag.
+
+It is highly experimental at this point and is expected to change significantly
+across releases.
+
+As a general convention, functions whose symbols are exported are defined in an
+'exported' subdirectory and functions that are required to be implemented by
+clients are defined in a 'imported' subdirectory.
diff --git a/src/cobalt/browser/lib/imported/main.h b/src/cobalt/browser/lib/imported/main.h
new file mode 100644
index 0000000..c3abcb8
--- /dev/null
+++ b/src/cobalt/browser/lib/imported/main.h
@@ -0,0 +1,45 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// All imported functions defined below MUST be implemented by client
+// applications.
+
+#ifndef COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
+#define COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Structure representing an input event and its data. This provides a subset
+// of SbEvent so that clients don't have to copy the Starboard headers into
+// their codebase.
+typedef struct CbLibKeyInputEvent {
+  unsigned char keycode;
+  bool pressed;
+} CbLibKeyInputEvent;
+
+// Invoked after Cobalt has been initialized.
+void CbLibOnCobaltInitialized();
+
+// Invoked when Cobalt is receiving an event from Starboard.
+// Returns true if the event should pass through to Cobalt; returns false if
+// the event was consumed.
+bool CbLibHandleEvent(const CbLibKeyInputEvent& event);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
diff --git a/src/cobalt/browser/lib/main.cc b/src/cobalt/browser/lib/main.cc
new file mode 100644
index 0000000..51cd1e4
--- /dev/null
+++ b/src/cobalt/browser/lib/main.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "cobalt/base/wrap_main.h"
+#include "cobalt/browser/application.h"
+#include "cobalt/browser/lib/imported/main.h"
+#include "cobalt/browser/starboard/event_handler.h"
+#include "starboard/event.h"
+#include "starboard/input.h"
+
+namespace {
+
+bool SbEventToCbLibKeyInputEvent(
+    const SbEvent* starboard_event,
+    CbLibKeyInputEvent* out_key_input_event) {
+  if (starboard_event == NULL ||
+      starboard_event->type != SbEventType::kSbEventTypeInput) {
+    return false;
+  }
+  const SbInputData* input_data =
+      static_cast<SbInputData*>(starboard_event->data);
+  if (input_data->device_type !=
+      SbInputDeviceType::kSbInputDeviceTypeKeyboard) {
+    return false;
+  }
+  out_key_input_event->pressed =
+      input_data->type == SbInputEventType::kSbInputEventTypePress;
+  out_key_input_event->keycode =
+      static_cast<unsigned char>(input_data->character);
+  return true;
+}
+
+cobalt::browser::Application* g_application = NULL;
+
+void StartApplication(int /*argc*/, char** /*argv*/, const char* /*link*/,
+                      const base::Closure& quit_closure) {
+  DCHECK(!g_application);
+  LOG(INFO) << "Starting application!";
+  g_application = cobalt::browser::CreateApplication(quit_closure).release();
+  DCHECK(g_application);
+  CbLibOnCobaltInitialized();
+}
+
+void StopApplication() {
+  DCHECK(g_application);
+  LOG(INFO) << "Stopping application!";
+  delete g_application;
+  g_application = NULL;
+}
+
+void HandleEvent(const SbEvent* starboard_event) {
+  CbLibKeyInputEvent key;
+  if (!SbEventToCbLibKeyInputEvent(starboard_event, &key) ||
+      CbLibHandleEvent(key)) {
+    cobalt::browser::EventHandler::HandleEvent(starboard_event);
+  }
+}
+
+}  // namespace
+
+COBALT_WRAP_EVENT_MAIN(StartApplication,
+                       HandleEvent,
+                       StopApplication);
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
new file mode 100644
index 0000000..1c28846
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/auto_mem.h"
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/stringprintf.h"
+
+#include "cobalt/browser/memory_settings/build_settings.h"
+#include "cobalt/browser/memory_settings/calculations.h"
+#include "cobalt/browser/memory_settings/pretty_print.h"
+#include "cobalt/browser/switches.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+
+// Creates the specified memory setting type and binds it to (1) command line
+// or else (2) build setting or else (3) an auto_set value.
+template <typename MemorySettingType, typename ValueType>
+scoped_ptr<MemorySettingType> CreateMemorySetting(
+    const char* setting_name, const CommandLine& cmd_line,
+    const base::optional<ValueType>& build_setting,
+    const ValueType& autoset_value) {
+  scoped_ptr<MemorySettingType> output(new MemorySettingType(setting_name));
+
+  // The value is set according to importance:
+  // 1) Command line switches are the most important, so set those if they
+  //    exist.
+  if (cmd_line.HasSwitch(setting_name)) {
+    std::string value = cmd_line.GetSwitchValueNative(setting_name);
+    if (output->TryParseValue(MemorySetting::kCmdLine, value)) {
+      return output.Pass();
+    }
+  }
+
+  // 2) Is there a build setting? Then set to build_setting.
+  if (build_setting) {
+    output->set_value(MemorySetting::kBuildSetting, *build_setting);
+  } else {
+    // 3) Otherwise bind to the autoset_value.
+    output->set_value(MemorySetting::kAutoSet, autoset_value);
+  }
+  return output.Pass();
+}
+
+void EnsureValuePositive(IntSetting* setting) {
+  if (setting->value() < 0) {
+    setting->set_value(setting->source_type(), 0);
+  }
+}
+
+void EnsureValuePositive(DimensionSetting* setting) {
+  const math::Size value = setting->value();
+  if (value.width() < 0 || value.height() < 0) {
+    setting->set_value(setting->source_type(), math::Size(0, 0));
+  }
+}
+
+}  // namespace
+
+AutoMem::AutoMem(const math::Size& ui_resolution,
+                 const CommandLine& command_line,
+                 const BuildSettings& build_settings) {
+  // Set the ImageCache
+  image_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
+      switches::kImageCacheSizeInBytes, command_line,
+      build_settings.cobalt_image_cache_size_in_bytes,
+      CalculateImageCacheSize(ui_resolution));
+  EnsureValuePositive(image_cache_size_in_bytes_.get());
+
+  // Set javascript gc threshold
+  javascript_gc_threshold_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
+      switches::kJavaScriptGcThresholdInBytes, command_line,
+      build_settings.javascript_garbage_collection_threshold_in_bytes,
+      kDefaultJsGarbageCollectionThresholdSize);
+  EnsureValuePositive(javascript_gc_threshold_in_bytes_.get());
+
+  // Set skia_atlas_texture_dimensions
+  skia_atlas_texture_dimensions_ =
+      CreateMemorySetting<DimensionSetting, math::Size>(
+          switches::kSkiaTextureAtlasDimensions, command_line,
+          build_settings.skia_texture_atlas_dimensions,
+          CalculateSkiaGlyphAtlasTextureSize(ui_resolution));
+  // Not available for non-blitter platforms.
+  if (build_settings.has_blitter) {
+    skia_atlas_texture_dimensions_->set_value(MemorySetting::kNotApplicable,
+                                              math::Size(0, 0));
+  }
+  EnsureValuePositive(skia_atlas_texture_dimensions_.get());
+
+  // Set skia_cache_size_in_bytes
+  skia_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
+      switches::kSkiaCacheSizeInBytes, command_line,
+      build_settings.skia_cache_size_in_bytes,
+      CalculateSkiaCacheSize(ui_resolution));
+  // Not available for blitter platforms.
+  if (build_settings.has_blitter) {
+    skia_cache_size_in_bytes_->set_value(MemorySetting::kNotApplicable, 0);
+  }
+  EnsureValuePositive(skia_cache_size_in_bytes_.get());
+
+  // Set software_surface_cache_size_in_bytes
+  software_surface_cache_size_in_bytes_ =
+      CreateMemorySetting<IntSetting, int64_t>(
+          switches::kSoftwareSurfaceCacheSizeInBytes, command_line,
+          build_settings.software_surface_cache_size_in_bytes,
+          CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
+  // Blitter only feature.
+  if (!build_settings.has_blitter) {
+    software_surface_cache_size_in_bytes_->set_value(
+        MemorySetting::kNotApplicable, 0);
+  }
+  EnsureValuePositive(software_surface_cache_size_in_bytes_.get());
+}
+
+AutoMem::~AutoMem() {}
+
+const IntSetting* AutoMem::image_cache_size_in_bytes() const {
+  return image_cache_size_in_bytes_.get();
+}
+
+const IntSetting* AutoMem::javascript_gc_threshold_in_bytes() const {
+  return javascript_gc_threshold_in_bytes_.get();
+}
+
+const DimensionSetting* AutoMem::skia_atlas_texture_dimensions() const {
+  return skia_atlas_texture_dimensions_.get();
+}
+
+const IntSetting* AutoMem::skia_cache_size_in_bytes() const {
+  return skia_cache_size_in_bytes_.get();
+}
+
+const IntSetting* AutoMem::software_surface_cache_size_in_bytes() const {
+  return software_surface_cache_size_in_bytes_.get();
+}
+
+std::vector<const MemorySetting*> AutoMem::AllMemorySetttings() const {
+  std::vector<const MemorySetting*> all_settings;
+  // Keep these in alphabetical order.
+  all_settings.push_back(image_cache_size_in_bytes_.get());
+  all_settings.push_back(javascript_gc_threshold_in_bytes_.get());
+  all_settings.push_back(skia_atlas_texture_dimensions_.get());
+  all_settings.push_back(skia_cache_size_in_bytes_.get());
+  all_settings.push_back(software_surface_cache_size_in_bytes_.get());
+  return all_settings;
+}
+
+std::string AutoMem::ToPrettyPrintString() const {
+  std::vector<const MemorySetting*> all_settings = AllMemorySetttings();
+  return GeneratePrettyPrintTable(all_settings);
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/auto_mem.h b/src/cobalt/browser/memory_settings/auto_mem.h
new file mode 100644
index 0000000..7d4bd2d
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/auto_mem.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_AUTO_MEM_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_AUTO_MEM_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/browser/memory_settings/build_settings.h"
+#include "cobalt/browser/memory_settings/memory_settings.h"
+#include "cobalt/math/size.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+// AutoMem is contains system memory settings. The command line is required to
+// instantiate this object and after construction, all memory settings are
+// calculated.
+class AutoMem {
+ public:
+  explicit AutoMem(const math::Size& ui_resolution,
+                   const CommandLine& command_line,
+                   const BuildSettings& build_settings);
+  ~AutoMem();
+
+  const IntSetting* image_cache_size_in_bytes() const;
+  const IntSetting* javascript_gc_threshold_in_bytes() const;
+  const DimensionSetting* skia_atlas_texture_dimensions() const;
+  const IntSetting* skia_cache_size_in_bytes() const;
+  const IntSetting* software_surface_cache_size_in_bytes() const;
+
+  std::vector<const MemorySetting*> AllMemorySetttings() const;
+
+  // Generates a string table of the memory settings. This is used during
+  // startup to display memory configuration information.
+  std::string ToPrettyPrintString() const;
+
+ private:
+  scoped_ptr<IntSetting> image_cache_size_in_bytes_;
+  scoped_ptr<IntSetting> javascript_gc_threshold_in_bytes_;
+  scoped_ptr<DimensionSetting> skia_atlas_texture_dimensions_;
+  scoped_ptr<IntSetting> skia_cache_size_in_bytes_;
+  scoped_ptr<IntSetting> software_surface_cache_size_in_bytes_;
+};
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_SETTINGS_AUTO_MEM_H_
diff --git a/src/cobalt/browser/memory_settings/auto_mem_test.cc b/src/cobalt/browser/memory_settings/auto_mem_test.cc
new file mode 100644
index 0000000..4f6d6d7
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/auto_mem_test.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/auto_mem.h"
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+#include "cobalt/browser/memory_settings/build_settings.h"
+#include "cobalt/browser/memory_settings/calculations.h"
+#include "cobalt/browser/switches.h"
+#include "cobalt/math/size.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+const math::Size kResolution1080p(1920, 1080);
+
+#define EXPECT_MEMORY_SETTING(SETTING, SOURCE, VALUE)                       \
+  EXPECT_EQ(VALUE, SETTING->value()) << " failure for " << SETTING->name(); \
+  EXPECT_EQ(SOURCE, SETTING->source_type()) << " failure for "              \
+                                            << SETTING->name();
+
+// Tests the expectation that the command-line overrides will be applied.
+// Settings which are enabled/disabled when blitter is enabled/disabled are
+// also tested.
+TEST(AutoMem, CommandLineOverrides) {
+  // Load up command line settings of command lines.
+  CommandLine command_line(CommandLine::NO_PROGRAM);
+  command_line.AppendSwitchASCII(switches::kImageCacheSizeInBytes, "1234");
+  command_line.AppendSwitchASCII(switches::kJavaScriptGcThresholdInBytes,
+                                 "2345");
+  command_line.AppendSwitchASCII(switches::kSkiaCacheSizeInBytes, "3456");
+  command_line.AppendSwitchASCII(switches::kSkiaTextureAtlasDimensions,
+                                 "1234x5678");
+  command_line.AppendSwitchASCII(switches::kSoftwareSurfaceCacheSizeInBytes,
+                                 "4567");
+
+  for (int i = 0; i <= 1; ++i) {
+    BuildSettings builtin_settings = GetDefaultBuildSettings();
+    builtin_settings.has_blitter = (i == 0);
+
+    AutoMem auto_mem(kResolution1080p, command_line, builtin_settings);
+
+    // image_cache_size_in_bytes and javascript_gc_threshold_in_bytes settings
+    // ignore the blitter type.
+    EXPECT_MEMORY_SETTING(auto_mem.image_cache_size_in_bytes(),
+                          MemorySetting::kCmdLine, 1234);
+
+    EXPECT_MEMORY_SETTING(auto_mem.javascript_gc_threshold_in_bytes(),
+                          MemorySetting::kCmdLine, 2345);
+
+    // Certain features are only available for the blitter, and some features
+    // are disabled, vice versa.
+    if (builtin_settings.has_blitter) {
+      // When blitter is active then skia_atlas_texture_dimensions are
+      // not applicable because this is an OpenGl egl feature.
+      EXPECT_MEMORY_SETTING(auto_mem.skia_atlas_texture_dimensions(),
+                            MemorySetting::kNotApplicable, math::Size(0, 0));
+
+      // Skia cache is also an egl-only feature, which is not applicable
+      // for blitter.
+      EXPECT_MEMORY_SETTING(auto_mem.skia_cache_size_in_bytes(),
+                            MemorySetting::kNotApplicable, 0);
+
+      EXPECT_MEMORY_SETTING(auto_mem.software_surface_cache_size_in_bytes(),
+                            MemorySetting::kCmdLine, 4567);
+    } else {
+      // Skia atlas is an egl-only feature and therefore enabled.
+      EXPECT_MEMORY_SETTING(auto_mem.skia_atlas_texture_dimensions(),
+                            MemorySetting::kCmdLine, math::Size(1234, 5678));
+
+      // Skia cache is an egl-only feature therefore it is enabled for egl.
+      EXPECT_MEMORY_SETTING(auto_mem.skia_cache_size_in_bytes(),
+                            MemorySetting::kCmdLine, 3456);
+
+      // Software surface cache is a blitter-only feature, therefore disabled
+      // in egl.
+      EXPECT_MEMORY_SETTING(auto_mem.software_surface_cache_size_in_bytes(),
+                            MemorySetting::kNotApplicable, 0);
+    }
+  }
+}
+
+// Tests that skia atlas texture will be bind to the built in value, iff it has
+// been set.
+TEST(AutoMem, SkiaAtlasTextureAtlasSize) {
+  CommandLine empty_command_line(CommandLine::NO_PROGRAM);
+  BuildSettings builtin_settings;
+  BuildSettings builtin_settings_with_default;
+
+  builtin_settings_with_default.skia_texture_atlas_dimensions =
+      math::Size(1234, 5678);
+
+  AutoMem auto_mem(kResolution1080p, empty_command_line, builtin_settings);
+  AutoMem auto_mem_with_default(kResolution1080p, empty_command_line,
+                                builtin_settings_with_default);
+
+  // Expect that when the skia_atlas_texture_dimensions is specified in the
+  // build settings that it will bind to the auto-set value (computed from
+  // CalculateSkiaGlyphAtlasTextureSize(...)).
+  EXPECT_MEMORY_SETTING(auto_mem.skia_atlas_texture_dimensions(),
+                        MemorySetting::kAutoSet,
+                        CalculateSkiaGlyphAtlasTextureSize(kResolution1080p));
+
+  // Expect that when the skia_atlas_texture_dimensions is specified in the
+  // build settings that it will bind to the final value.
+  EXPECT_MEMORY_SETTING(auto_mem_with_default.skia_atlas_texture_dimensions(),
+                        MemorySetting::kBuildSetting, math::Size(1234, 5678));
+}
+
+// Tests that software surface cache will be bind to the built in value, iff
+// it has been set.
+TEST(AutoMem, SoftwareSurfaceCacheSizeInBytes) {
+  CommandLine empty_command_line(CommandLine::NO_PROGRAM);
+  BuildSettings builtin_settings;
+  BuildSettings builtin_settings_with_default;
+  // Enable the setting by enabling the blitter.
+  builtin_settings.has_blitter = true;
+  builtin_settings_with_default.has_blitter = true;
+  builtin_settings_with_default.software_surface_cache_size_in_bytes = 1234;
+
+  AutoMem auto_mem(kResolution1080p, empty_command_line, builtin_settings);
+  AutoMem auto_mem_with_surface_cache(kResolution1080p, empty_command_line,
+                                      builtin_settings_with_default);
+
+  // Expect that when the software_surface_cache_size_in_bytes is specified in
+  // the/ build settings that it will bind to the auto-set value (computed from
+  // CalculateSoftwareSurfaceCacheSizeInBytes(...)).
+  EXPECT_MEMORY_SETTING(
+      auto_mem.software_surface_cache_size_in_bytes(), MemorySetting::kAutoSet,
+      CalculateSoftwareSurfaceCacheSizeInBytes(kResolution1080p));
+
+  EXPECT_MEMORY_SETTING(
+      auto_mem_with_surface_cache.software_surface_cache_size_in_bytes(),
+      MemorySetting::kBuildSetting, 1234);
+}
+
+// Tests that skia cache will be bind to the built in value, iff
+// it has been set.
+TEST(AutoMem, SkiaCacheSizeInBytes) {
+  CommandLine empty_command_line(CommandLine::NO_PROGRAM);
+  BuildSettings builtin_settings;
+  BuildSettings builtin_settings_with_default;
+  builtin_settings_with_default.skia_cache_size_in_bytes = 1234;
+
+  AutoMem auto_mem(kResolution1080p, empty_command_line, builtin_settings);
+  AutoMem auto_mem_with_skia_cache(kResolution1080p, empty_command_line,
+                                   builtin_settings_with_default);
+
+  EXPECT_MEMORY_SETTING(auto_mem.skia_cache_size_in_bytes(),
+                        MemorySetting::kAutoSet,
+                        CalculateSkiaCacheSize(kResolution1080p));
+
+  EXPECT_MEMORY_SETTING(auto_mem_with_skia_cache.skia_cache_size_in_bytes(),
+                        MemorySetting::kBuildSetting, 1234);
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
+
+#undef EXPECT_MEMORY_SETTING
diff --git a/src/cobalt/browser/memory_settings/build_settings.cc b/src/cobalt/browser/memory_settings/build_settings.cc
new file mode 100644
index 0000000..65c646e
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/build_settings.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/build_settings.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+bool HasBlitter() {
+#if SB_HAS(BLITTER)
+  const bool has_blitter = true;
+#else
+  const bool has_blitter = false;
+#endif
+  return has_blitter;
+}
+
+base::optional<int64_t> MakeValidIfGreaterThanOrEqualToZero(int64_t value) {
+  base::optional<int64_t> output;
+  if (value >= 0) {
+    output = value;
+  }
+  return output;
+}
+
+base::optional<math::Size> MakeDimensionsIfPositive(int width, int height) {
+  base::optional<math::Size> output;
+  if ((width > 0) && (height > 0)) {
+    output = math::Size(width, height);
+  }
+  return output;
+}
+}  // namespace
+
+BuildSettings GetDefaultBuildSettings() {
+  BuildSettings settings;
+  settings.has_blitter = HasBlitter();
+  settings.cobalt_image_cache_size_in_bytes =
+      MakeValidIfGreaterThanOrEqualToZero(COBALT_IMAGE_CACHE_SIZE_IN_BYTES);
+  settings.javascript_garbage_collection_threshold_in_bytes =
+      MakeValidIfGreaterThanOrEqualToZero(
+          COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES);
+  settings.skia_cache_size_in_bytes =
+      MakeValidIfGreaterThanOrEqualToZero(COBALT_SKIA_CACHE_SIZE_IN_BYTES);
+  settings.skia_texture_atlas_dimensions = MakeDimensionsIfPositive(
+      COBALT_SKIA_GLYPH_ATLAS_WIDTH, COBALT_SKIA_GLYPH_ATLAS_HEIGHT);
+  settings.software_surface_cache_size_in_bytes =
+      MakeValidIfGreaterThanOrEqualToZero(
+          COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES);
+  return settings;
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/build_settings.h b/src/cobalt/browser/memory_settings/build_settings.h
new file mode 100644
index 0000000..c57daea
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/build_settings.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_BUILD_SETTINGS_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_BUILD_SETTINGS_H_
+
+#include "base/optional.h"
+#include "cobalt/math/size.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+struct BuildSettings {
+  BuildSettings() : has_blitter(false) {}
+  bool has_blitter;
+  base::optional<int64_t> cobalt_image_cache_size_in_bytes;
+  base::optional<int64_t> javascript_garbage_collection_threshold_in_bytes;
+  base::optional<int64_t> skia_cache_size_in_bytes;
+  base::optional<math::Size> skia_texture_atlas_dimensions;
+  base::optional<int64_t> software_surface_cache_size_in_bytes;
+};
+
+BuildSettings GetDefaultBuildSettings();
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_SETTINGS_BUILD_SETTINGS_H_
diff --git a/src/cobalt/browser/memory_settings/calculations.cc b/src/cobalt/browser/memory_settings/calculations.cc
new file mode 100644
index 0000000..6111d95
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/calculations.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/calculations.h"
+
+#include <algorithm>
+
+#include "cobalt/math/size.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+
+template <typename T>
+T ClampValue(const T& input, const T& minimum, const T& maximum) {
+  return std::max<T>(minimum, std::min<T>(maximum, input));
+}
+
+double DisplayScaleTo1080p(const math::Size& dimensions) {
+  static const double kNumReferencePixels = 1920. * 1080.;
+  const double num_pixels = static_cast<double>(dimensions.width()) *
+                            static_cast<double>(dimensions.height());
+  return num_pixels / kNumReferencePixels;
+}
+
+// LinearRemap is a type of linear interpolation which maps a value from
+// one number line to a corresponding value on another number line.
+// Example:
+//  LinearRemap linear_remap(0, 1, 5, 10);
+//  EXPECT_EQ(5.0f, linear_remap.Map(0));
+//  EXPECT_EQ(7.5f, linear_remap.Map(.5));
+//  EXPECT_EQ(10.f, linear_remap.Map(1));
+class LinearRemap {
+ public:
+  LinearRemap(double amin, double amax, double bmin, double bmax)
+      : amin_(amin), amax_(amax), bmin_(bmin), bmax_(bmax) {}
+
+  // Maps the value from the number line [amin,amax] to [bmin,bmax]. For
+  // example:
+  //   Map(amin) -> bmin.
+  //   Map(amax) -> bmax.
+  //   Map((amax+amin)/2) -> (bmax+bmin)/2.
+  double Map(double value) const {
+    value -= amin_;
+    value /= (amax_ - amin_);
+    value *= (bmax_ - bmin_);
+    value += bmin_;
+    return value;
+  }
+
+ private:
+  const double amin_, amax_, bmin_, bmax_;
+};
+
+math::Size ExpandTextureSizeToContain(const int64_t num_pixels) {
+  // Iterate through to find size to contain number of pixels.
+  math::Size texture_size(1, 1);
+  bool toggle = false;
+
+  // Expand the texture by powers of two until the specific number of pixels
+  // is contained.
+  while (texture_size.GetArea() < num_pixels) {
+    if (!toggle) {
+      texture_size.set_width(texture_size.width() * 2);
+    } else {
+      texture_size.set_height(texture_size.height() * 2);
+    }
+    toggle = !toggle;
+  }
+  return texture_size;
+}
+
+}  // namespace
+
+int64_t CalculateImageCacheSize(const math::Size& dimensions) {
+  const double display_scale = DisplayScaleTo1080p(dimensions);
+  static const int64_t kReferenceSize1080p = 32 * 1024 * 1024;
+  double output_bytes = kReferenceSize1080p * display_scale;
+
+  return ClampValue<int64_t>(static_cast<int64_t>(output_bytes),
+                             kMinImageCacheSize, kMaxImageCacheSize);
+}
+
+math::Size CalculateSkiaGlyphAtlasTextureSize(const math::Size& ui_resolution) {
+  // LinearRemap defines a mapping function which will map the number
+  // of ui_resolution pixels to the number of texture atlas pixels such that:
+  // 1080p (1920x1080) => maps to => 2048x2048 texture atlas pixels
+  // 4k    (3840x2160) => maps to => 8192x4096 texture atlas pixels
+  LinearRemap remap(1920 * 1080, 3840 * 2160, 2048 * 2048, 4096 * 8192);
+
+  // Apply mapping.
+  const int num_ui_pixels = ui_resolution.GetArea();
+  const int64_t num_atlas_pixels =
+      static_cast<int64_t>(remap.Map(num_ui_pixels));
+
+  // Texture atlas sizes are generated in powers of two. This function will
+  // produce such a texture.
+  math::Size atlas_texture = ExpandTextureSizeToContain(num_atlas_pixels);
+
+  // Clamp the atlas texture to be within a minimum range.
+  math::Size clamped_atlas_texture(
+      std::max<int>(kMinSkiaTextureAtlasWidth, atlas_texture.width()),
+      std::max<int>(kMinSkiaTextureAtlasWidth, atlas_texture.height()));
+  return clamped_atlas_texture;
+}
+
+int64_t CalculateSoftwareSurfaceCacheSizeInBytes(
+    const math::Size& ui_resolution) {
+  // LinearRemap defines a mapping function which will map the number
+  // of ui_resolution pixels to the number of surface texture cache such:
+  // 720p (1280x720)   => maps to => 4MB &
+  // 1080p (1920x1200) => maps to => 9MB
+  LinearRemap remap(1280 * 720, 1920 * 1080, 4 * 1024 * 1024, 9 * 1024 * 1024);
+
+  int64_t surface_cache_size_in_bytes =
+      static_cast<int64_t>(remap.Map(ui_resolution.GetArea()));
+
+  return surface_cache_size_in_bytes;
+}
+
+int64_t CalculateSkiaCacheSize(const math::Size& ui_resolution) {
+  // This is normalized return 4MB @ 1080p and scales accordingly.
+  LinearRemap remap(0, 1920 * 1080, 0, 4 * 1024 * 1024);
+  int64_t output = static_cast<int64_t>(remap.Map(ui_resolution.GetArea()));
+  return std::max<int64_t>(output, kMinSkiaCacheSize);
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/calculations.h b/src/cobalt/browser/memory_settings/calculations.h
new file mode 100644
index 0000000..c90426a
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/calculations.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_CALCULATIONS_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_CALCULATIONS_H_
+
+#include "base/optional.h"
+#include "cobalt/math/size.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+////////////////////////// Calculate Functions ////////////////////////////////
+// These functions are exposed here for testing purposes and should not be used
+// directly.
+// Calculates the ImageCacheSize in bytes.
+// The return ranges from [kMinImageCacheSize, kMaxImageCacheSize].
+int64_t CalculateImageCacheSize(const math::Size& dimensions);
+
+// Calculates the SkiaAtlasTextureSize.
+// When the ui resolution is 1920x1080, then the returned atlas texture size
+// will be 8192x4096. The texture will scale up and down, by powers of two,
+// in relation to the input ui_resolution. The returned value will be clamped
+// such that
+// kMinSkiaTextureAtlasWidth <= output.width() <= kMaxSkiaTextureAtlasWidth
+// and
+// kMinSkiaTextureAtlasHeight <= output.height() <= kMaxSkiaTextureAtlasHeight
+// will be true.
+math::Size CalculateSkiaGlyphAtlasTextureSize(const math::Size& ui_resolution);
+
+// Calculates the SoftwareSurfaceCacheSize given the ui_resolution.
+int64_t CalculateSoftwareSurfaceCacheSizeInBytes(
+    const math::Size& ui_resolution);
+
+// Calculates the SkiaCachSize from the ui_resolution. This is normalized
+// to be 4MB @ 1080p and scales accordingly.
+int64_t CalculateSkiaCacheSize(const math::Size& ui_resolution);
+
+// These internal values are exposed for testing.
+enum MemorySizes {
+  kMinImageCacheSize = 20 * 1024 * 1024,  // 20mb.
+  kMaxImageCacheSize = 64 * 1024 * 1024,  // 64mb
+
+  kMinSkiaTextureAtlasWidth = 2048,
+  kMinSkiaTextureAtlasHeight = 2048,
+
+  kDefaultJsGarbageCollectionThresholdSize = 1 * 1024 * 1024,  // 1mb
+
+  kMinSkiaCacheSize = 4 * 1024 * 1024,  // 4mb.
+};
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_SETTINGS_CALCULATIONS_H_
diff --git a/src/cobalt/browser/memory_settings/calculations_test.cc b/src/cobalt/browser/memory_settings/calculations_test.cc
new file mode 100644
index 0000000..935dc41
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/calculations_test.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/calculations.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "cobalt/browser/switches.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/system.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+///////////////////////////////////
+//
+//            WIDTH | HEIGHT
+// -------------------------
+// 8K:        7,680 x 4,320
+// Cinema 4K: 4,096 x 2,160
+// 4k/UHD:    3,840 x 2,160
+// 2K:        2,560 x 1,440
+// WUXGA:     1,920 x 1,200
+// 1080p:     1,920 x 1,080
+// 720p:      1,280 x 720
+// 480p:        640 x 480
+enum StandardDisplaySettings {
+  k8k,
+  kCinema4k,
+  kUHD4k,
+  k2k,
+  kWUXGA,
+  k1080p,
+  k720p,
+  k480p
+};
+
+math::Size GetDimensions(StandardDisplaySettings enum_value) {
+  switch (enum_value) {
+    case k8k: {
+      return math::Size(7680, 4320);
+    }
+    case kCinema4k: {
+      return math::Size(4096, 2160);
+    }
+    case kUHD4k: {
+      return math::Size(3840, 2160);
+    }
+    case k2k: {
+      return math::Size(2560, 1440);
+    }
+    case kWUXGA: {
+      return math::Size(1920, 1200);
+    }
+    case k1080p: {
+      return math::Size(1920, 1080);
+    }
+    case k720p: {
+      return math::Size(1280, 720);
+    }
+    case k480p: {
+      return math::Size(640, 480);
+    }
+  }
+
+  EXPECT_TRUE(false) << "Should not be reached. Unknown enum_value: "
+                     << enum_value;
+  return GetDimensions(k1080p);
+}
+
+// Tests the expectation that CalculateImageCacheSize() is a pure function
+// (side effect free) and will produce the expected results.
+TEST(MemoryCalculations, CalculateImageCacheSize) {
+  EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k720p)));
+  EXPECT_EQ(32 * 1024 * 1024,  // 32MB.
+            CalculateImageCacheSize(GetDimensions(k1080p)));
+  EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(kUHD4k)));
+
+  // Expect that the floor is hit for smaller values.
+  EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k480p)));
+
+  // Expect that the ceiling is hit for larger values.
+  EXPECT_EQ(kMaxImageCacheSize,
+            CalculateImageCacheSize(GetDimensions(kCinema4k)));
+  EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(k8k)));
+}
+
+// Tests the expectation that CalculateSkiaAtlasTextureSize() is a pure
+// function (side effect free) and will produce expected results.
+TEST(MemoryCalculations, CalculateSkiaGlyphAtlasTextureSize) {
+  math::Size ui_dimensions;
+  math::Size atlas_texture_size;
+
+  // Test that the small resolution of 480p produces a texture atlas
+  // that is minimal.
+  ui_dimensions = GetDimensions(k480p);
+  atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+  EXPECT_EQ(kMinSkiaTextureAtlasWidth, atlas_texture_size.width());
+  EXPECT_EQ(kMinSkiaTextureAtlasHeight, atlas_texture_size.height());
+
+  // Test that expected resolution of 1080p produces a 2048x2048
+  // atlas texture.
+  ui_dimensions = GetDimensions(k1080p);
+  atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+  EXPECT_EQ(2048, atlas_texture_size.width());
+  EXPECT_EQ(2048, atlas_texture_size.height());
+
+  // Test that expected resolution of 4k produces a 8192x4096
+  // atlas texture.
+  ui_dimensions = GetDimensions(kUHD4k);
+  atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+  EXPECT_EQ(8192, atlas_texture_size.width());
+  EXPECT_EQ(4096, atlas_texture_size.height());
+
+  // Test that ultra-high 8k resolution produces expected results.
+  ui_dimensions = GetDimensions(k8k);
+  atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+  EXPECT_EQ(16384, atlas_texture_size.width());
+  EXPECT_EQ(16384, atlas_texture_size.height());
+}
+
+TEST(MemoryCalculations, CalculateSoftwareSurfaceCacheSizeInBytes) {
+  math::Size ui_resolution = GetDimensions(k720p);
+  // Expect that a 720p resolution produces a 4mb SoftwareSurfaceCache.
+  EXPECT_EQ(4 * 1024 * 1024,
+            CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
+
+  ui_resolution = GetDimensions(k1080p);
+  // Expect that a 1080p resolution produces a 9mb SoftwareSurfaceCache.
+  EXPECT_EQ(9 * 1024 * 1024,
+            CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/memory_settings.cc b/src/cobalt/browser/memory_settings/memory_settings.cc
index 91333cc..f6097c4 100644
--- a/src/cobalt/browser/memory_settings/memory_settings.cc
+++ b/src/cobalt/browser/memory_settings/memory_settings.cc
@@ -17,43 +17,95 @@
 #include "cobalt/browser/memory_settings/memory_settings.h"
 
 #include <algorithm>
+#include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/stringprintf.h"
+#include "cobalt/browser/switches.h"
+#include "nb/lexical_cast.h"
 
 namespace cobalt {
 namespace browser {
 namespace memory_settings {
 namespace {
 
-template <typename T>
-T ClampValue(const T& input, const T& minimum, const T& maximum) {
-  return std::max<T>(minimum, std::min<T>(maximum, input));
+struct ParsedIntValue {
+ public:
+  ParsedIntValue() : value_(0), error_(false) {}
+  ParsedIntValue(const ParsedIntValue& other)
+      : value_(other.value_), error_(other.error_) {}
+  int value_;
+  bool error_;  // true if there was a parse error.
+};
+// Parses a string like "1234x5678" to vector of parsed int values.
+std::vector<ParsedIntValue> ParseDimensions(const std::string& value_str) {
+  std::vector<ParsedIntValue> output;
+
+  std::vector<std::string> lengths;
+  base::SplitString(value_str, 'x', &lengths);
+
+  for (size_t i = 0; i < lengths.size(); ++i) {
+    ParsedIntValue parsed_value;
+    parsed_value.error_ = !base::StringToInt(lengths[i], &parsed_value.value_);
+    output.push_back(parsed_value);
+  }
+  return output;
 }
 
-double DisplayScaleTo1080p(const math::Size& dimensions) {
-  static const double kNumReferencePixels = 1920. * 1080.;
-  const double num_pixels = static_cast<double>(dimensions.width()) *
-                            static_cast<double>(dimensions.height());
-  return num_pixels / kNumReferencePixels;
-}
 }  // namespace
 
-size_t GetImageCacheSize(const math::Size& dimensions) {
-  if (COBALT_IMAGE_CACHE_SIZE_IN_BYTES >= 0) {
-    return COBALT_IMAGE_CACHE_SIZE_IN_BYTES;
-  }
-  size_t return_val = CalculateImageCacheSize(dimensions);
-  return static_cast<int>(return_val);
+MemorySetting::MemorySetting(ClassType type, const std::string& name)
+    : class_type_(type), name_(name), source_type_(kUnset) {}
+
+IntSetting::IntSetting(const std::string& name)
+    : MemorySetting(kInt, name), value_() {}
+
+std::string IntSetting::ValueToString() const {
+  std::stringstream ss;
+  ss << value_;
+  return ss.str();
 }
 
-size_t CalculateImageCacheSize(const math::Size& dimensions) {
-  const double display_scale = DisplayScaleTo1080p(dimensions);
-  static const size_t kReferenceSize1080p = 32 * 1024 * 1024;
-  double output_bytes = kReferenceSize1080p * display_scale;
+bool IntSetting::TryParseValue(SourceType source_type,
+                               const std::string& string_value) {
+  bool parse_ok = false;
+  int64_t int_value = nb::lexical_cast<int64_t>(string_value, &parse_ok);
 
-  return ClampValue<size_t>(static_cast<size_t>(output_bytes),
-                            kMinImageCacheSize, kMaxImageCacheSize);
+  if (parse_ok) {
+    set_value(source_type, int_value);
+    return true;
+  } else {
+    LOG(ERROR) << "Invalid value for command line setting: " << string_value;
+    return false;
+  }
+}
+
+DimensionSetting::DimensionSetting(const std::string& name)
+    : MemorySetting(kDimensions, name) {}
+
+std::string DimensionSetting::ValueToString() const {
+  std::stringstream ss;
+  ss << value_.width() << "x" << value_.height();
+  return ss.str();
+}
+
+bool DimensionSetting::TryParseValue(SourceType source_type,
+                                     const std::string& string_value) {
+  std::vector<ParsedIntValue> int_values = ParseDimensions(string_value);
+  const bool parse_ok = (int_values.size() == 2) && (!int_values[0].error_) &&
+                        (!int_values[1].error_);
+  if (parse_ok) {
+    math::Size rectangle(int_values[0].value_, int_values[1].value_);
+    set_value(source_type, rectangle);
+    return true;
+  } else {
+    LOG(ERROR) << "Invalid value for parse value setting: " << string_value;
+    return false;
+  }
 }
 
 }  // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/memory_settings.h b/src/cobalt/browser/memory_settings/memory_settings.h
index b111574..1d13aa7 100644
--- a/src/cobalt/browser/memory_settings/memory_settings.h
+++ b/src/cobalt/browser/memory_settings/memory_settings.h
@@ -17,27 +17,91 @@
 #ifndef COBALT_BROWSER_MEMORY_SETTINGS_MEMORY_SETTINGS_H_
 #define COBALT_BROWSER_MEMORY_SETTINGS_MEMORY_SETTINGS_H_
 
+#include <map>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/optional.h"
 #include "cobalt/math/size.h"
+#include "starboard/configuration.h"
 #include "starboard/types.h"
 
 namespace cobalt {
 namespace browser {
 namespace memory_settings {
 
-// Gets the ImageCacheSize in bytes from the given dimensions. If
-// COBALT_IMAGE_CACHE_SIZE_IN_BYTES is defined, then this is the value
-// that is returned, otherwise the value is generated via a call to
-// CalculateImageCacheSize().
-size_t GetImageCacheSize(const math::Size& dimensions);
+// An abstract MemorySetting. Subclasses will implement how the class parses
+// string values.
+class MemorySetting {
+ public:
+  virtual ~MemorySetting() {}
+  // SourceType defines the location where the setting was set from.
+  // kNotApplicable means the setting is not supported on the current system
+  // configuration.
+  enum SourceType { kUnset, kCmdLine, kBuildSetting, kAutoSet, kNotApplicable };
+  enum ClassType { kInt, kDimensions };
 
-// Calculates the ImageCacheSize in bytes.
-// The return ranges from [kMinImageCacheSize, kMaxImageCacheSize].
-size_t CalculateImageCacheSize(const math::Size& dimensions);
+  virtual std::string ValueToString() const = 0;
+  // Returns true if the TryParseValue() succeeded when converting the string
+  // into the internal value. If false, then the object should not be changed.
+  virtual bool TryParseValue(SourceType source_type,
+                             const std::string& string_value) = 0;
 
-/////////////////////////////// Implementation ///////////////////////////////
-enum MemorySizes {
-  kMinImageCacheSize = 20 * 1024 * 1024,  // 20mb.
-  kMaxImageCacheSize = 64 * 1024 * 1024   // 64mb
+  const std::string& name() const { return name_; }
+  SourceType source_type() const { return source_type_; }
+  ClassType class_type() const { return class_type_; }
+
+ protected:
+  MemorySetting(ClassType type, const std::string& name);
+
+  const ClassType class_type_;
+  const std::string name_;
+  SourceType source_type_;
+
+ private:
+  // Default constructor for MemorySetting is forbidden. Do not use it.
+  MemorySetting();
+  SB_DISALLOW_COPY_AND_ASSIGN(MemorySetting);
+};
+
+// A memory setting for integer values.
+class IntSetting : public MemorySetting {
+ public:
+  explicit IntSetting(const std::string& name);
+
+  std::string ValueToString() const OVERRIDE;
+  int64_t value() const { return value_; }
+  void set_value(SourceType source_type, int64_t val) {
+    source_type_ = source_type;
+    value_ = val;
+  }
+  bool TryParseValue(SourceType source_type,
+                     const std::string& string_value) OVERRIDE;
+
+ private:
+  int64_t value_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(IntSetting);
+};
+
+// A memory setting for dimensions type values like "2048x4096".
+class DimensionSetting : public MemorySetting {
+ public:
+  explicit DimensionSetting(const std::string& name);
+
+  std::string ValueToString() const OVERRIDE;
+  math::Size value() const { return value_; }
+  void set_value(SourceType source_type, const math::Size& val) {
+    source_type_ = source_type;
+    value_ = val;
+  }
+  bool TryParseValue(SourceType source_type,
+                     const std::string& string_value) OVERRIDE;
+
+ private:
+  math::Size value_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(DimensionSetting);
 };
 
 }  // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/memory_settings_test.cc b/src/cobalt/browser/memory_settings/memory_settings_test.cc
index 5fa60b2..565d1c1 100644
--- a/src/cobalt/browser/memory_settings/memory_settings_test.cc
+++ b/src/cobalt/browser/memory_settings/memory_settings_test.cc
@@ -21,108 +21,29 @@
 #include <string>
 #include <vector>
 
-#include "base/logging.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/system.h"
-
+#include "base/memory/scoped_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace browser {
 namespace memory_settings {
 
-///////////////////////////////////
-//
-//            WIDTH | HEIGHT
-// -------------------------
-// 8K:        7,680 x 4,320
-// Cinema 4K: 4,096 x 2,160
-// 4k/UHD:    3,840 x 2,160
-// 2K:        2,048 x 1,080
-// WUXGA:     1,920 x 1,200
-// 1080p:     1,920 x 1,080
-// 720p:      1,280 x 720
-// 480p:        640 x 480
-enum StandardDisplaySettings {
-  k8k,
-  kCinema4k,
-  kUHD4k,
-  k2k,
-  kWUXGA,
-  k1080p,
-  k720p,
-  k480p
-};
-
-math::Size GetDimensions(StandardDisplaySettings enum_value) {
-  switch (enum_value) {
-    case k8k: {
-      return math::Size(7680, 4320);
-    }
-    case kCinema4k: {
-      return math::Size(4096, 2160);
-    }
-    case kUHD4k: {
-      return math::Size(3840, 2160);
-    }
-    case k2k: {
-      return math::Size(2048, 1080);
-    }
-    case kWUXGA: {
-      return math::Size(1920, 1200);
-    }
-    case k1080p: {
-      return math::Size(1920, 1080);
-    }
-    case k720p: {
-      return math::Size(1280, 720);
-    }
-    case k480p: {
-      return math::Size(640, 480);
-    }
-  }
-
-  EXPECT_TRUE(false)
-    << "Should not be reached. Unknown enum_value: " << enum_value;
-  return GetDimensions(k1080p);
+TEST(IntSetting, ParseFromString) {
+  scoped_ptr<IntSetting> int_setting(new IntSetting("dummy"));
+  EXPECT_EQ(std::string("dummy"), int_setting->name());
+  ASSERT_TRUE(int_setting->TryParseValue(MemorySetting::kCmdLine, "123"));
+  EXPECT_EQ(123, int_setting->value());
+  EXPECT_EQ(MemorySetting::kCmdLine, int_setting->source_type());
+  EXPECT_EQ(std::string("123"), int_setting->ValueToString());
 }
 
-TEST(MemorySettings, CalculateImageCacheSize) {
-  EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k720p)));
-  EXPECT_EQ(32 * 1024 * 1024,  // 32MB.
-            CalculateImageCacheSize(GetDimensions(k1080p)));
-  EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(kUHD4k)));
-
-  // Expect that the floor is hit for smaller values.
-  EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k480p)));
-
-  // Expect that the ceiling is hit for larger values.
-  EXPECT_EQ(kMaxImageCacheSize,
-            CalculateImageCacheSize(GetDimensions(kCinema4k)));
-  EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(k8k)));
-}
-
-TEST(MemorySettings, GetImageCacheSize) {
-  if (COBALT_IMAGE_CACHE_SIZE_IN_BYTES >= 0) {
-    // Expect that regardless of the display size, the value returned will
-    // be equal to COBALT_IMAGE_CACHE_SIZE_IN_BYTES
-    EXPECT_EQ(COBALT_IMAGE_CACHE_SIZE_IN_BYTES,
-              GetImageCacheSize(GetDimensions(k720p)));
-    EXPECT_EQ(COBALT_IMAGE_CACHE_SIZE_IN_BYTES,
-              GetImageCacheSize(GetDimensions(k1080p)));
-    EXPECT_EQ(COBALT_IMAGE_CACHE_SIZE_IN_BYTES,
-              GetImageCacheSize(GetDimensions(kUHD4k)));
-  } else {
-    // ... otherwise expect that the GetImageCacheSize() is equal to
-    // CalculateImageCacheSize().
-    EXPECT_EQ(CalculateImageCacheSize(GetDimensions(k720p)),
-              GetImageCacheSize(GetDimensions(k720p)));
-    EXPECT_EQ(CalculateImageCacheSize(GetDimensions(k1080p)),
-              GetImageCacheSize(GetDimensions(k1080p)));
-    EXPECT_EQ(CalculateImageCacheSize(GetDimensions(kUHD4k)),
-              GetImageCacheSize(GetDimensions(kUHD4k)));
-  }
+TEST(DimensionSetting, ParseFromString) {
+  scoped_ptr<DimensionSetting> rect_setting(new DimensionSetting("dummy"));
+  ASSERT_TRUE(
+      rect_setting->TryParseValue(MemorySetting::kCmdLine, "1234x5678"));
+  EXPECT_EQ(math::Size(1234, 5678), rect_setting->value());
+  EXPECT_EQ(MemorySetting::kCmdLine, rect_setting->source_type());
+  EXPECT_EQ(std::string("1234x5678"), rect_setting->ValueToString());
 }
 
 }  // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/pretty_print.cc b/src/cobalt/browser/memory_settings/pretty_print.cc
new file mode 100644
index 0000000..b153b5f
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/pretty_print.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/pretty_print.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "cobalt/browser/memory_settings/memory_settings.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+
+class TablePrinter {
+ public:
+  struct Row {
+    std::string name;
+    std::string value;
+    std::string source;
+  };
+
+  static std::string ToString(const std::vector<Row>& rows) {
+    // First column is the name, the second is the value, then source.
+    size_t name_col_width = 0;
+    size_t value_col_width = 0;
+    size_t source_col_width = 0;
+
+    for (size_t i = 0; i < rows.size(); ++i) {
+      const Row& row = rows[i];
+      name_col_width = std::max<size_t>(name_col_width, row.name.size());
+      value_col_width = std::max<size_t>(value_col_width, row.value.size());
+      source_col_width = std::max<size_t>(source_col_width, row.source.size());
+    }
+
+    TablePrinter printer(name_col_width, value_col_width, source_col_width);
+
+    std::stringstream output_ss;
+    std::string row_delimiter = printer.MakeDelimiterRow();
+
+    output_ss << printer.MakeHeaderRow() << "\n";
+    output_ss << row_delimiter << "\n";
+
+    for (size_t i = 0; i < rows.size(); ++i) {
+      const Row& row = rows[i];
+      std::string row_str =
+          printer.MakeDataRow(row.name, row.value, row.source);
+      output_ss << row_str << "\n";
+      output_ss << row_delimiter << "\n";
+    }
+
+    return output_ss.str();
+  }
+
+ private:
+  TablePrinter(size_t name_col_width, size_t value_col_width,
+               size_t source_col_width)
+      : name_col_width_(name_col_width),
+        value_col_width_(value_col_width),
+        source_col_width_(source_col_width) {
+    Init();
+  }
+
+  void Init() {
+    std::stringstream fmt_row_ss;
+    // Example: "| %-30s | %9s | %9s |\n"
+    fmt_row_ss << "| %-" << name_col_width_ << "s | %" << value_col_width_
+               << "s | %" << source_col_width_ << "s |";
+    fmt_data_row_ = fmt_row_ss.str();
+  }
+
+  std::string MakeHeaderRow() const {
+    std::string name_str = "NAME";
+    std::string value_str = "VALUE";
+    std::string source_str = "SOURCE";
+
+    std::stringstream ss;
+    ss << name_str;
+    for (size_t i = 0; i < name_col_width_ + 3 - name_str.size(); ++i) {
+      ss << " ";
+    }
+
+    ss << value_str;
+    for (size_t i = 0; i < value_col_width_ + 3 - value_str.size(); ++i) {
+      ss << " ";
+    }
+
+    ss << source_str;
+    for (size_t i = 0; i < source_col_width_ + 4 - source_str.size(); ++i) {
+      ss << " ";
+    }
+    return ss.str();
+  }
+
+  std::string MakeDataRow(const std::string& name, const std::string& value,
+                          const std::string& source) const {
+    std::string row_str = base::StringPrintf(
+        fmt_data_row_.c_str(), name.c_str(), value.c_str(), source.c_str());
+    return row_str;
+  }
+
+  std::string MakeDelimiterRow() const {
+    std::stringstream ss;
+    ss << "+";
+    for (size_t i = 0; i < name_col_width_ + 2; ++i) {
+      ss << "-";
+    }
+    ss << "+";
+    for (size_t i = 0; i < value_col_width_ + 2; ++i) {
+      ss << "-";
+    }
+    ss << "+";
+    for (size_t i = 0; i < source_col_width_ + 2; ++i) {
+      ss << "-";
+    }
+    ss << "+";
+    return ss.str();
+  }
+
+  const size_t name_col_width_;
+  const size_t value_col_width_;
+  const size_t source_col_width_;
+  std::string fmt_data_row_;
+};
+
+std::string StringifySourceType(MemorySetting::SourceType source_type) {
+  switch (source_type) {
+    case MemorySetting::kUnset: {
+      return "Unset";
+    }
+    case MemorySetting::kCmdLine: {
+      return "CmdLine";
+    }
+    case MemorySetting::kBuildSetting: {
+      return "BuildSetting";
+    }
+    case MemorySetting::kAutoSet: {
+      return "AutoSet";
+    }
+    case MemorySetting::kNotApplicable: {
+      return "N/A";
+    }
+  }
+
+  SB_NOTIMPLEMENTED() << "Unimplemented string for type " << source_type;
+  return "UNKNOWN";
+}
+
+}  // namespace
+
+std::string GeneratePrettyPrintTable(
+    const std::vector<const MemorySetting*>& settings) {
+  std::vector<TablePrinter::Row> rows;
+
+  for (size_t i = 0; i < settings.size(); ++i) {
+    const MemorySetting* setting = settings[i];
+
+    TablePrinter::Row row;
+    row.name = setting->name();
+    row.value = setting->ValueToString();
+    row.source = StringifySourceType(setting->source_type());
+
+    rows.push_back(row);
+  }
+
+  std::string table_string = TablePrinter::ToString(rows);
+  return table_string;
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/pretty_print.h b/src/cobalt/browser/memory_settings/pretty_print.h
new file mode 100644
index 0000000..dd533c0
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/pretty_print.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_PRETTY_PRINT_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_PRETTY_PRINT_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/stringprintf.h"
+#include "cobalt/browser/memory_settings/memory_settings.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+// Generates a table, ie:
+//    "NAME                                   VALUE       SOURCE          \n"
+//    "+--------------------------------------+-----------+--------------+\n"
+//    "| image_cache_size_in_bytes            |      1234 |      CmdLine |\n"
+//    "+--------------------------------------+-----------+--------------+\n"
+//    "| javascript_gc_threshold_in_bytes     |      1112 |      AutoSet |\n"
+//    "+--------------------------------------+-----------+--------------+\n"
+//    "| skia_atlas_texture_dimensions        | 1234x4567 |      CmdLine |\n"
+//    "+--------------------------------------+-----------+--------------+\n"
+//    "| skia_cache_size_in_bytes             |         0 |          N/A |\n"
+//    "+--------------------------------------+-----------+--------------+\n"
+//    "| software_surface_cache_size_in_bytes |      8910 | BuildSetting |\n"
+//    "+--------------------------------------+-----------+--------------+\n";
+std::string GeneratePrettyPrintTable(
+    const std::vector<const MemorySetting*>& memory_settings);
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_SETTINGS_PRETTY_PRINT_H_
diff --git a/src/cobalt/browser/memory_settings/pretty_print_test.cc b/src/cobalt/browser/memory_settings/pretty_print_test.cc
new file mode 100644
index 0000000..079a099
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/pretty_print_test.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/pretty_print.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/browser/memory_settings/memory_settings.h"
+#include "cobalt/browser/switches.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/system.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+scoped_ptr<MemorySetting> CreateMemorySetting(
+    MemorySetting::ClassType class_type, MemorySetting::SourceType source_type,
+    const std::string& name, const std::string& value) {
+  scoped_ptr<MemorySetting> setting;
+
+  switch (class_type) {
+    case MemorySetting::kInt: {
+      setting.reset(new IntSetting(name));
+      break;
+    }
+    case MemorySetting::kDimensions: {
+      setting.reset(new DimensionSetting(name));
+      break;
+    }
+    default: {
+      EXPECT_TRUE(false) << "Unexpected type " << class_type;
+      setting.reset(new IntSetting(name));
+      break;
+    }
+  }
+  EXPECT_TRUE(setting->TryParseValue(source_type, value));
+  return setting.Pass();
+}
+
+TEST(MemorySettingsPrettyPrint, ToString) {
+  std::vector<const MemorySetting*> memory_settings_ptr;
+  scoped_ptr<MemorySetting> image_cache_setting =
+      CreateMemorySetting(MemorySetting::kInt, MemorySetting::kCmdLine,
+                          switches::kImageCacheSizeInBytes, "1234");
+  memory_settings_ptr.push_back(image_cache_setting.get());
+
+  scoped_ptr<MemorySetting> js_gc_setting =
+      CreateMemorySetting(MemorySetting::kInt, MemorySetting::kAutoSet,
+                          switches::kJavaScriptGcThresholdInBytes, "1112");
+  memory_settings_ptr.push_back(js_gc_setting.get());
+
+  scoped_ptr<MemorySetting> skia_texture_setting =
+      CreateMemorySetting(MemorySetting::kDimensions, MemorySetting::kCmdLine,
+                          switches::kSkiaTextureAtlasDimensions, "1234x4567");
+  memory_settings_ptr.push_back(skia_texture_setting.get());
+
+  scoped_ptr<MemorySetting> skia_cache_setting =
+      CreateMemorySetting(MemorySetting::kInt, MemorySetting::kNotApplicable,
+                          switches::kSkiaCacheSizeInBytes, "0");
+  memory_settings_ptr.push_back(skia_cache_setting.get());
+
+  scoped_ptr<MemorySetting> software_surface_setting =
+      CreateMemorySetting(MemorySetting::kInt, MemorySetting::kBuildSetting,
+                          switches::kSoftwareSurfaceCacheSizeInBytes, "8910");
+  memory_settings_ptr.push_back(software_surface_setting.get());
+
+  std::string actual_string = GeneratePrettyPrintTable(memory_settings_ptr);
+
+  std::string expected_string =
+      "NAME                                   VALUE       SOURCE          \n"
+      "+--------------------------------------+-----------+--------------+\n"
+      "| image_cache_size_in_bytes            |      1234 |      CmdLine |\n"
+      "+--------------------------------------+-----------+--------------+\n"
+      "| javascript_gc_threshold_in_bytes     |      1112 |      AutoSet |\n"
+      "+--------------------------------------+-----------+--------------+\n"
+      "| skia_atlas_texture_dimensions        | 1234x4567 |      CmdLine |\n"
+      "+--------------------------------------+-----------+--------------+\n"
+      "| skia_cache_size_in_bytes             |         0 |          N/A |\n"
+      "+--------------------------------------+-----------+--------------+\n"
+      "| software_surface_cache_size_in_bytes |      8910 | BuildSetting |\n"
+      "+--------------------------------------+-----------+--------------+\n";
+
+  const bool strings_matched = (expected_string == actual_string);
+  EXPECT_TRUE(strings_matched) << "Strings Mismatched:\n\n"
+                               << "Actual:\n" << actual_string << "\n"
+                               << "Expected:\n" << expected_string << "\n";
+}
+
+}  // namespace memory_settings
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
deleted file mode 100644
index b30c612..0000000
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
+++ /dev/null
@@ -1,1743 +0,0 @@
-// 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/browser/memory_tracker/memory_tracker_tool_impl.h"
-
-#include <algorithm>
-#include <cstring>
-#include <iomanip>
-#include <iterator>
-#include <set>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/time.h"
-#include "cobalt/browser/memory_tracker/buffered_file_writer.h"
-#include "cobalt/script/mozjs/util/stack_trace_helpers.h"
-#include "nb/analytics/memory_tracker.h"
-#include "nb/analytics/memory_tracker_helpers.h"
-#include "nb/concurrent_map.h"
-#include "nb/memory_scope.h"
-#include "starboard/common/semaphore.h"
-#include "starboard/configuration.h"
-#include "starboard/file.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-namespace cobalt {
-namespace browser {
-namespace memory_tracker {
-
-using nb::analytics::AllocationGroup;
-using nb::analytics::AllocationRecord;
-using nb::analytics::AllocationVisitor;
-using nb::analytics::GetProcessMemoryStats;
-using nb::analytics::MemoryStats;
-using nb::analytics::MemoryTracker;
-
-namespace {
-const char kQuote[] = "\"";
-const char kDelimiter[] = ",";
-const char kNewLine[] = "\n";
-
-// This is a simple algorithm to remove the "needle" from the haystack. Note
-// that this function is simple and not well optimized.
-std::string RemoveString(const std::string& haystack, const char* needle) {
-  const size_t kNotFound = std::string::npos;
-
-  // Base case. No modification needed.
-  size_t pos = haystack.find(needle);
-  if (pos == kNotFound) {
-    return haystack;
-  }
-  const size_t n = strlen(needle);
-  std::string output;
-  output.reserve(haystack.size());
-
-  // Copy string, omitting the portion containing the "needle".
-  std::copy(haystack.begin(), haystack.begin() + pos,
-            std::back_inserter(output));
-  std::copy(haystack.begin() + pos + n, haystack.end(),
-            std::back_inserter(output));
-
-  // Recursively remove same needle in haystack.
-  return RemoveString(output, needle);
-}
-
-// Not optimized but works ok for a tool that dumps out in user time.
-std::string SanitizeCSVKey(std::string key) {
-  key = RemoveString(key, kQuote);
-  key = RemoveString(key, kDelimiter);
-  key = RemoveString(key, kNewLine);
-  return key;
-}
-
-// 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 (size_t 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;
-}
-
-// Removes odd elements and resizes vector.
-template <typename VectorType>
-void RemoveOddElements(VectorType* v) {
-  typedef typename VectorType::iterator iterator;
-
-  iterator read_it = v->end();
-  iterator write_it = v->end();
-  for (size_t i = 0; i*2 < v->size(); ++i) {
-    write_it = v->begin() + i;
-    read_it = v->begin() + (i*2);
-    *write_it = *read_it;
-  }
-  if (write_it != v->end()) {
-    write_it++;
-  }
-  v->erase(write_it, v->end());
-}
-
-// NoMemoryTracking will disable memory tracking while in the current scope of
-// execution. When the object is destroyed it will reset the previous state
-// of allocation tracking.
-// Example:
-//   void Foo() {
-//     NoMemoryTracking no_memory_tracking_in_scope;
-//     int* ptr = new int();  // ptr is not tracked.
-//     delete ptr;
-//     return;    // Previous memory tracking state is restored.
-//   }
-class NoMemoryTracking {
- public:
-  explicit NoMemoryTracking(nb::analytics::MemoryTracker* owner);
-  ~NoMemoryTracking();
-
- private:
-  bool prev_val_;
-  nb::analytics::MemoryTracker* owner_;
-};
-
-// Simple timer class that fires once after dt time has elapsed.
-class Timer {
- public:
-  explicit Timer(base::TimeDelta dt)
-      : start_time_(base::TimeTicks::Now()), time_before_expiration_(dt) {}
-
-  void Restart() { start_time_ = base::TimeTicks::Now(); }
-
-  bool UpdateAndIsExpired() {
-    base::TimeTicks now_time = base::TimeTicks::Now();
-    base::TimeDelta dt = now_time - start_time_;
-    if (dt > time_before_expiration_) {
-      start_time_ = now_time;
-      return true;
-    } else {
-      return false;
-    }
-  }
-
- private:
-  base::TimeTicks start_time_;
-  base::TimeDelta time_before_expiration_;
-};
-
-// Returns true if linear fit was calculated. False otherwise. Reasons for
-// returning false include passing in an empty range, such that
-// begin_it == end_it, or all x-values being the same.
-//
-// Algorithm adapted from:
-// https://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line
-// Example:
-//    std::vector<std::pair<int, int> > data;
-//    for (int i = 0; i < 10; ++i) {
-//      data.push_back(std::pair<int, int>(i+1, 2*i));
-//    }
-//    double slope = 0;
-//    double y_intercept = 0;
-//    GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
-//    std::cout << "slope: " << slope << "\n";
-//    std::cout << "y_intercept: " << y_intercept<< "\n";
-template <typename PairIterator>
-bool GetLinearFit(PairIterator begin_it, PairIterator end_it, double* out_slope,
-                  double* out_yintercept) {
-  if (begin_it == end_it) {
-    return false;
-  }
-
-  size_t n = 0;
-  double x_avg = 0;
-  double y_avg = 0;
-
-  for (PairIterator it = begin_it; it != end_it; ++it) {
-    x_avg += it->first;
-    y_avg += it->second;
-    n++;
-  }
-
-  x_avg /= n;
-  y_avg /= n;
-
-  double numerator = 0;
-  double denominator = 0;
-
-  for (PairIterator it = begin_it; it != end_it; ++it) {
-    double x_variance = it->first - x_avg;
-    double y_variance = it->second - y_avg;
-    numerator += (x_variance * y_variance);
-    denominator += (x_variance * x_variance);
-  }
-
-  if (denominator == 0.0) {
-    return false;
-  }
-
-  double slope = numerator / denominator;
-  double yintercept = y_avg - slope * x_avg;
-
-  *out_slope = slope;
-  *out_yintercept = yintercept;
-  return true;
-}
-
-// Returns a substring with the directory path removed from the filename.
-// Example:
-//   F::BaseNameFast("directory/filename.cc") => "filename.cc"
-//   F::BaseNameFast("directory\filename.cc") => "filename.cc"
-//
-// Note that base::FilePath::BaseName() isn't used because of performance
-// reasons.
-const char* BaseNameFast(const char* file_name) {
-  const char* end_pos = file_name + strlen(file_name);
-  const char* last_forward_slash = SbStringFindLastCharacter(file_name, '/');
-  if (last_forward_slash) {
-    if (end_pos != last_forward_slash) {
-      ++last_forward_slash;
-    }
-    return last_forward_slash;
-  }
-
-  const char* last_backward_slash = SbStringFindLastCharacter(file_name, '\\');
-  if (last_backward_slash) {
-    if (end_pos != last_backward_slash) {
-      ++last_backward_slash;
-    }
-    return last_backward_slash;
-  }
-  return file_name;
-}
-
-}  // namespace
-
-class Params {
- public:
-  Params(nb::analytics::MemoryTracker* memory_tracker, AbstractLogger* logger,
-         base::Time start_time)
-      : memory_tracker_(memory_tracker),
-        finished_(false),
-        logger_(logger),
-        timer_(start_time) {}
-  bool finished() const { return finished_; }
-  bool wait_for_finish_signal(SbTime wait_us) {
-    return finished_semaphore_.TakeWait(wait_us);
-  }
-  void set_finished(bool val) {
-    finished_ = val;
-    finished_semaphore_.Put();
-  }
-
-  nb::analytics::MemoryTracker* memory_tracker() const {
-    return memory_tracker_;
-  }
-  AbstractLogger* logger() { return logger_.get(); }
-  base::TimeDelta time_since_start() const {
-    return base::Time::NowFromSystemTime() - timer_;
-  }
-  std::string TimeInMinutesString() const {
-    base::TimeDelta delta_t = time_since_start();
-    int64 seconds = delta_t.InSeconds();
-    float time_mins = static_cast<float>(seconds) / 60.f;
-    std::stringstream ss;
-
-    ss << time_mins;
-    return ss.str();
-  }
-
- private:
-  nb::analytics::MemoryTracker* memory_tracker_;
-  bool finished_;
-  scoped_ptr<AbstractLogger> logger_;
-  base::Time timer_;
-  starboard::Semaphore finished_semaphore_;
-};
-
-MemoryTrackerToolThread::MemoryTrackerToolThread(
-    nb::analytics::MemoryTracker* memory_tracker,
-    AbstractMemoryTrackerTool* tool, AbstractLogger* logger)
-    : Super(tool->tool_name()),
-      params_(
-          new Params(memory_tracker, logger, base::Time::NowFromSystemTime())),
-      tool_(tool) {
-  Start();
-}
-
-MemoryTrackerToolThread::~MemoryTrackerToolThread() {
-  Join();
-  tool_.reset();
-  params_.reset();
-}
-
-void MemoryTrackerToolThread::Join() {
-  params_->set_finished(true);
-  Super::Join();
-}
-
-void MemoryTrackerToolThread::Run() {
-  NoMemoryTracking no_mem_tracking_in_this_scope(params_->memory_tracker());
-  // This tool will run until the finished_ if flipped to false.
-  tool_->Run(params_.get());
-}
-
-NoMemoryTracking::NoMemoryTracking(nb::analytics::MemoryTracker* owner)
-    :  prev_val_(false), owner_(owner) {
-  if (owner_) {
-    prev_val_ = owner_->IsMemoryTrackingEnabled();
-    owner_->SetMemoryTrackingEnabled(false);
-  }
-}
-
-NoMemoryTracking::~NoMemoryTracking() {
-  if (owner_) {
-    owner_->SetMemoryTrackingEnabled(prev_val_);
-  }
-}
-
-void MemoryTrackerPrint::Run(Params* params) {
-  const std::string kSeperator
-      = "--------------------------------------------------";
-
-  while (!params->finished()) {
-    std::vector<const AllocationGroup*> vector_output;
-    params->memory_tracker()->GetAllocationGroups(&vector_output);
-
-    typedef std::map<std::string, const AllocationGroup*> Map;
-    typedef Map::const_iterator MapIt;
-
-    Map output;
-    for (size_t i = 0; i < vector_output.size(); ++i) {
-      const AllocationGroup* group = vector_output[i];
-      output[group->name()] = group;
-    }
-
-    int32 num_allocs = 0;
-    int64 total_bytes = 0;
-
-    struct F {
-      static void PrintRow(std::stringstream* ss, const std::string& v1,
-                           const std::string& v2, const std::string& v3) {
-        ss->width(25);
-        *ss << std::left << v1;
-        ss->width(13);
-        *ss << std::right << v2 << "  ";
-        ss->width(10);
-        *ss << std::right << v3 << "\n";
-      }
-    };
-
-    if (params->memory_tracker()->IsMemoryTrackingEnabled()) {
-      // If this isn't true then it would cause an infinite loop. The
-      // following will likely crash.
-      SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
-    }
-
-    std::stringstream ss;
-
-    ss << kNewLine;
-    ss << "TimeNow " << params->TimeInMinutesString()
-       << " (minutes):" << kNewLine << kNewLine;
-
-    ss << kSeperator << kNewLine;
-    MemoryStats memstats = GetProcessMemoryStats();
-
-    F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", "");
-    ss << kSeperator << kNewLine;
-    F::PrintRow(&ss,
-                "Total CPU Reserved",
-                NumberFormatWithCommas(memstats.total_cpu_memory),
-                "");
-
-    F::PrintRow(&ss,
-                "Total CPU Used",
-                NumberFormatWithCommas(memstats.used_cpu_memory),
-                "");
-
-    F::PrintRow(&ss,
-                "Total GPU Reserved",
-                NumberFormatWithCommas(memstats.total_gpu_memory),
-                "");
-
-    F::PrintRow(&ss,
-                "Total GPU Used",
-                NumberFormatWithCommas(memstats.used_gpu_memory),
-                "");
-
-    ss << kSeperator << kNewLine << kNewLine;
-
-    ss << kSeperator << kNewLine;
-    F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS");
-    ss << kSeperator << kNewLine;
-
-    for (MapIt it = output.begin(); it != output.end(); ++it) {
-      const AllocationGroup* group = it->second;
-      if (!group) {
-        continue;
-      }
-
-      int32 num_group_allocs = -1;
-      int64 total_group_bytes = -1;
-
-      group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
-      SB_DCHECK(-1 != num_group_allocs);
-      SB_DCHECK(-1 != total_group_bytes);
-      num_allocs += num_group_allocs;
-      total_bytes += total_group_bytes;
-
-      F::PrintRow(&ss, it->first, NumberFormatWithCommas(total_group_bytes),
-                  NumberFormatWithCommas(num_group_allocs));
-    }
-
-    ss << kNewLine;
-
-    F::PrintRow(&ss,
-                "Total (in groups above)",
-                NumberFormatWithCommas(total_bytes),
-                NumberFormatWithCommas(num_allocs));
-
-    ss << kSeperator << kNewLine;
-    ss << kNewLine << kNewLine;
-
-    params->logger()->Output(ss.str().c_str());
-    // Output once every 5 seconds.
-    base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
-  }
-}
-
-MemoryTrackerPrintCSV::MemoryTrackerPrintCSV(int sampling_interval_ms,
-                                             int sampling_time_ms)
-    : sample_interval_ms_(sampling_interval_ms),
-      sampling_time_ms_(sampling_time_ms) {}
-
-std::string MemoryTrackerPrintCSV::ToCsvString(
-    const MapAllocationSamples& samples_in) {
-  typedef MapAllocationSamples Map;
-  typedef Map::const_iterator MapIt;
-
-  size_t largest_sample_size = 0;
-  size_t smallest_sample_size = INT_MAX;
-
-  // Sanitize samples_in and store as samples.
-  MapAllocationSamples samples;
-  for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
-    std::string name = it->first;
-    const AllocationSamples& value = it->second;
-
-    if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
-      SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
-      return "ERROR";
-    }
-
-    const size_t n = value.allocated_bytes_.size();
-    if (n > largest_sample_size) {
-      largest_sample_size = n;
-    }
-    if (n < smallest_sample_size) {
-      smallest_sample_size = n;
-    }
-
-    const bool duplicate_found = (samples.end() != samples.find(name));
-    if (duplicate_found) {
-      SB_NOTREACHED() << "Error, duplicate found for entry: " << name
-                      << kNewLine;
-    }
-    // Store value as a sanitized sample.
-    samples[name] = value;
-  }
-
-  SB_DCHECK(largest_sample_size == smallest_sample_size);
-
-  std::stringstream ss;
-
-  // Begin output to CSV.
-  // Sometimes we need to skip the CPU memory entry.
-  const MapIt total_cpu_memory_it = samples.find(UntrackedMemoryKey());
-
-  // Preamble
-  ss << kNewLine << "//////////////////////////////////////////////";
-  ss << kNewLine << "// CSV of bytes / allocation" << kNewLine;
-  // HEADER.
-  ss << "Name" << kDelimiter << kQuote << "Bytes/Alloc" << kQuote << kNewLine;
-  // DATA.
-  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-    if (total_cpu_memory_it == it) {
-      continue;
-    }
-
-    const AllocationSamples& samples = it->second;
-    if (samples.allocated_bytes_.empty() ||
-        samples.number_allocations_.empty()) {
-      SB_NOTREACHED() << "Should not be here";
-      return "ERROR";
-    }
-    const int64 n_allocs = samples.number_allocations_.back();
-    const int64 n_bytes = samples.allocated_bytes_.back();
-    int64 bytes_per_alloc = 0;
-    if (n_allocs > 0) {
-      bytes_per_alloc = n_bytes / n_allocs;
-    }
-    const std::string& name = it->first;
-    ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter
-       << bytes_per_alloc << kNewLine;
-  }
-  ss << kNewLine;
-
-  // Preamble
-  ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
-     << "// CSV of bytes allocated per region (MB's)." << kNewLine
-     << "// Units are in Megabytes. This is designed" << kNewLine
-     << "// to be used in a stacked graph." << kNewLine;
-
-  // HEADER.
-  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-    if (total_cpu_memory_it == it) {
-      continue;
-    }
-    // Strip out any characters that could make parsing the csv difficult.
-    const std::string name = SanitizeCSVKey(it->first);
-    ss << kQuote << name << kQuote << kDelimiter;
-  }
-  // Save the total for last.
-  if (total_cpu_memory_it != samples.end()) {
-    const std::string& name = SanitizeCSVKey(total_cpu_memory_it->first);
-    ss << kQuote << name << kQuote << kDelimiter;
-  }
-  ss << kNewLine;
-
-  // Print out the values of each of the samples.
-  for (size_t i = 0; i < smallest_sample_size; ++i) {
-    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-      if (total_cpu_memory_it == it) {
-        continue;
-      }
-      const int64 alloc_bytes = it->second.allocated_bytes_[i];
-      // Convert to float megabytes with decimals of precision.
-      double n = alloc_bytes / (1000 * 10);
-      n = n / (100.);
-      ss << n << kDelimiter;
-    }
-    if (total_cpu_memory_it != samples.end()) {
-      const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i];
-      // Convert to float megabytes with decimals of precision.
-      double n = alloc_bytes / (1000 * 10);
-      n = n / (100.);
-      ss << n << kDelimiter;
-    }
-    ss << kNewLine;
-  }
-
-  ss << kNewLine;
-  // Preamble
-  ss << kNewLine << "//////////////////////////////////////////////";
-  ss << kNewLine << "// CSV of number of allocations per region." << kNewLine;
-
-  // HEADER
-  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-    if (total_cpu_memory_it == it) {
-      continue;
-    }
-    const std::string& name = SanitizeCSVKey(it->first);
-    ss << kQuote << name << kQuote << kDelimiter;
-  }
-  ss << kNewLine;
-  for (size_t i = 0; i < smallest_sample_size; ++i) {
-    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-      if (total_cpu_memory_it == it) {
-        continue;
-      }
-      const int64 n_allocs = it->second.number_allocations_[i];
-      ss << n_allocs << kDelimiter;
-    }
-    ss << kNewLine;
-  }
-  std::string output = ss.str();
-  return output;
-}
-
-const char* MemoryTrackerPrintCSV::UntrackedMemoryKey() {
-  return "Untracked Memory";
-}
-
-void MemoryTrackerPrintCSV::Run(Params* params) {
-  params->logger()->Output("\nMemoryTrackerPrintCSVThread is sampling...\n");
-  int sample_count = 0;
-  MapAllocationSamples map_samples;
-
-  while (!TimeExpiredYet(*params) && !params->finished()) {
-    // Sample total memory used by the system.
-    MemoryStats mem_stats = GetProcessMemoryStats();
-    int64 untracked_used_memory =
-        mem_stats.used_cpu_memory + mem_stats.used_gpu_memory;
-
-    std::vector<const AllocationGroup*> vector_output;
-    params->memory_tracker()->GetAllocationGroups(&vector_output);
-
-    // Sample all known memory scopes.
-    for (size_t i = 0; i < vector_output.size(); ++i) {
-      const AllocationGroup* group = vector_output[i];
-      const std::string& name = group->name();
-
-      const bool first_creation =
-          map_samples.find(group->name()) == map_samples.end();
-
-      AllocationSamples* new_entry = &(map_samples[name]);
-
-      // Didn't see it before so create new entry.
-      if (first_creation) {
-        // Make up for lost samples...
-        new_entry->allocated_bytes_.resize(sample_count, 0);
-        new_entry->number_allocations_.resize(sample_count, 0);
-      }
-
-      int32 num_allocs = -1;
-      int64 allocation_bytes = -1;
-      group->GetAggregateStats(&num_allocs, &allocation_bytes);
-
-      new_entry->allocated_bytes_.push_back(allocation_bytes);
-      new_entry->number_allocations_.push_back(num_allocs);
-
-      untracked_used_memory -= allocation_bytes;
-    }
-
-    // Now push in remaining total.
-    AllocationSamples* process_sample = &(map_samples[UntrackedMemoryKey()]);
-    if (untracked_used_memory < 0) {
-      // On some platforms, total GPU memory may not be correctly reported.
-      // However the allocations from the GPU memory may be reported. In this
-      // case untracked_used_memory will go negative. To protect the memory
-      // reporting the untracked_used_memory is set to 0 so that it doesn't
-      // cause an error in reporting.
-      untracked_used_memory = 0;
-    }
-    process_sample->allocated_bytes_.push_back(untracked_used_memory);
-    process_sample->number_allocations_.push_back(-1);
-
-    ++sample_count;
-    base::PlatformThread::Sleep(
-        base::TimeDelta::FromMilliseconds(sample_interval_ms_));
-  }
-
-  std::stringstream ss;
-  ss.precision(2);
-  ss << "Time now: " << params->TimeInMinutesString() << ",\n";
-  ss << ToCsvString(map_samples);
-  params->logger()->Output(ss.str().c_str());
-  params->logger()->Flush();
-  // Prevents the "thread exited code 0" from being interleaved into the
-  // output. This happens if flush is not implemented correctly in the system.
-  base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
-}
-
-bool MemoryTrackerPrintCSV::TimeExpiredYet(const Params& params) {
-  base::TimeDelta dt = params.time_since_start();
-  int64 dt_ms = dt.InMilliseconds();
-  const bool expired_time = dt_ms > sampling_time_ms_;
-  return expired_time;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-MemoryTrackerCompressedTimeSeries::MemoryTrackerCompressedTimeSeries()
-    : number_samples_(400) {}
-
-void MemoryTrackerCompressedTimeSeries::Run(Params* params) {
-  TimeSeries timeseries;
-  Timer timer_status_message(base::TimeDelta::FromSeconds(1));
-
-  // Outputs CSV once every minute.
-  Timer timer_output_csv(base::TimeDelta::FromMinutes(1));
-
-  // Initial sample time is every 50 milliseconds.
-  base::TimeDelta current_sample_interval =
-      base::TimeDelta::FromMilliseconds(50);
-
-  while (!params->wait_for_finish_signal(current_sample_interval.ToSbTime())) {
-    AcquireSample(params->memory_tracker(), &timeseries,
-                  params->time_since_start());
-
-    if (IsFull(timeseries, number_samples_)) {
-      Compress(&timeseries);           // Remove every other element.
-      current_sample_interval *= 2;    // Double the sample time.
-      timer_status_message.Restart();  // Skip status message.
-    }
-
-    if (timer_output_csv.UpdateAndIsExpired()) {
-      const std::string str = ToCsvString(timeseries);
-      params->logger()->Output(str.c_str());
-      timer_status_message.Restart();  // Skip status message.
-    }
-
-    // Print status message.
-    if (timer_status_message.UpdateAndIsExpired()) {
-      std::stringstream ss;
-      ss << tool_name() << " is running..." << kNewLine;
-      params->logger()->Output(ss.str().c_str());
-    }
-  }
-}
-
-std::string MemoryTrackerCompressedTimeSeries::ToCsvString(
-    const TimeSeries& timeseries) {
-  size_t largest_sample_size = 0;
-  size_t smallest_sample_size = INT_MAX;
-
-  typedef MapAllocationSamples::const_iterator MapIt;
-
-  // Sanitize samples_in and store as samples.
-  const MapAllocationSamples& samples_in = timeseries.samples_;
-  MapAllocationSamples samples;
-  for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
-    std::string name = it->first;
-    const AllocationSamples& value = it->second;
-
-    if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
-      SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
-      return "ERROR";
-    }
-
-    const size_t n = value.allocated_bytes_.size();
-    if (n > largest_sample_size) {
-      largest_sample_size = n;
-    }
-    if (n < smallest_sample_size) {
-      smallest_sample_size = n;
-    }
-
-    const bool duplicate_found = (samples.end() != samples.find(name));
-    if (duplicate_found) {
-      SB_NOTREACHED() << "Error, duplicate found for entry: " << name
-                      << kNewLine;
-    }
-    // Store value as a sanitized sample.
-    samples[name] = value;
-  }
-
-  SB_DCHECK(largest_sample_size == smallest_sample_size);
-
-  std::stringstream ss;
-
-  // Begin output to CSV.
-
-  // Preamble
-  ss << kNewLine
-     << "//////////////////////////////////////////////" << kNewLine
-     << "// CSV of BYTES allocated per region (MB's)." << kNewLine
-     << "// Units are in Megabytes. This is designed" << kNewLine
-     << "// to be used in a stacked graph." << kNewLine;
-
-  // HEADER.
-  ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
-  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-    const std::string& name = it->first;
-    ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
-  }
-  ss << kNewLine;
-
-  // Print out the values of each of the samples.
-  for (size_t i = 0; i < smallest_sample_size; ++i) {
-    // Output time first so that it can be used as an x-axis.
-    const double time_mins =
-        timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
-    ss << time_mins << ",";
-    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-      const int64 alloc_bytes = it->second.allocated_bytes_[i];
-      // Convert to float megabytes with decimals of precision.
-      double n = alloc_bytes / (1000 * 10);
-      n = n / (100.);
-      ss << n << kDelimiter;
-    }
-    ss << kNewLine;
-  }
-  ss << "// END CSV of BYTES allocated per region." << kNewLine;
-  ss << "//////////////////////////////////////////////";
-
-  ss << kNewLine;
-  // Preamble
-  ss << kNewLine << "//////////////////////////////////////////////";
-  ss << kNewLine << "// CSV of COUNT of allocations per region." << kNewLine;
-
-  // HEADER
-  ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
-  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-    const std::string& name = it->first;
-    ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
-  }
-  ss << kNewLine;
-  for (size_t i = 0; i < smallest_sample_size; ++i) {
-    // Output time first so that it can be used as an x-axis.
-    const double time_mins =
-        timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
-    ss << time_mins << ",";
-    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-      const int64 n_allocs = it->second.number_allocations_[i];
-      ss << n_allocs << kDelimiter;
-    }
-    ss << kNewLine;
-  }
-  ss << "// END CSV of COUNT of allocations per region." << kNewLine;
-  ss << "//////////////////////////////////////////////";
-  ss << kNewLine << kNewLine;
-
-  std::string output = ss.str();
-  return output;
-}
-
-void MemoryTrackerCompressedTimeSeries::AcquireSample(
-    MemoryTracker* memory_tracker, TimeSeries* timeseries,
-    const base::TimeDelta& time_now) {
-  const size_t sample_count = timeseries->time_stamps_.size();
-  timeseries->time_stamps_.push_back(time_now);
-
-  MapAllocationSamples& map_samples = timeseries->samples_;
-
-  std::vector<const AllocationGroup*> vector_output;
-  memory_tracker->GetAllocationGroups(&vector_output);
-
-  // Sample all known memory scopes.
-  for (size_t i = 0; i < vector_output.size(); ++i) {
-    const AllocationGroup* group = vector_output[i];
-    const std::string& name = group->name();
-
-    const bool first_creation =
-        map_samples.find(group->name()) == map_samples.end();
-
-    AllocationSamples& new_entry = map_samples[name];
-
-    // Didn't see it before so create new entry.
-    if (first_creation) {
-      // Make up for lost samples...
-      new_entry.allocated_bytes_.resize(sample_count, 0);
-      new_entry.number_allocations_.resize(sample_count, 0);
-    }
-
-    int32 num_allocs = -1;
-    int64 allocation_bytes = -1;
-    group->GetAggregateStats(&num_allocs, &allocation_bytes);
-
-    new_entry.allocated_bytes_.push_back(allocation_bytes);
-    new_entry.number_allocations_.push_back(num_allocs);
-  }
-
-  // Sample the in use memory bytes reported by malloc.
-  MemoryStats memory_stats = GetProcessMemoryStats();
-  AllocationSamples& process_mem_in_use = map_samples["ProcessMemoryInUse"];
-  process_mem_in_use.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
-  process_mem_in_use.number_allocations_.push_back(0);
-
-  // Sample the reserved memory bytes reported by malloc.
-  AllocationSamples& process_mem_reserved
-      = map_samples["ProcessMemoryReserved"];
-  process_mem_reserved.allocated_bytes_
-      .push_back(memory_stats.used_cpu_memory);
-  process_mem_reserved.number_allocations_.push_back(0);
-}
-
-bool MemoryTrackerCompressedTimeSeries::IsFull(const TimeSeries& timeseries,
-                                               size_t samples_limit) {
-  return timeseries.time_stamps_.size() >= samples_limit;
-}
-
-void MemoryTrackerCompressedTimeSeries::Compress(TimeSeries* timeseries) {
-  typedef MapAllocationSamples::iterator MapIt;
-  MapAllocationSamples& samples = timeseries->samples_;
-  RemoveOddElements(&(timeseries->time_stamps_));
-  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
-    AllocationSamples& data = it->second;
-    RemoveOddElements(&data.allocated_bytes_);
-    RemoveOddElements(&data.number_allocations_);
-  }
-}
-
-MemorySizeBinner::MemorySizeBinner(const std::string& memory_scope_name)
-    : memory_scope_name_(memory_scope_name) {}
-
-const AllocationGroup* FindAllocationGroup(const std::string& name,
-                                           MemoryTracker* memory_tracker) {
-  std::vector<const AllocationGroup*> groups;
-  memory_tracker->GetAllocationGroups(&groups);
-  // Find by exact string match.
-  for (size_t i = 0; i < groups.size(); ++i) {
-    const AllocationGroup* group = groups[i];
-    if (group->name().compare(name) == 0) {
-      return group;
-    }
-  }
-  return NULL;
-}
-
-void MemorySizeBinner::Run(Params* params) {
-  const AllocationGroup* target_group = NULL;
-
-  while (!params->finished()) {
-    if (target_group == NULL && !memory_scope_name_.empty()) {
-      target_group =
-          FindAllocationGroup(memory_scope_name_, params->memory_tracker());
-    }
-
-    std::stringstream ss;
-    ss.precision(2);
-    if (target_group || memory_scope_name_.empty()) {
-      AllocationSizeBinner visitor_binner = AllocationSizeBinner(target_group);
-      params->memory_tracker()->Accept(&visitor_binner);
-
-      size_t min_size = 0;
-      size_t max_size = 0;
-
-      visitor_binner.GetLargestSizeRange(&min_size, &max_size);
-
-      FindTopSizes top_size_visitor =
-          FindTopSizes(min_size, max_size, target_group);
-      params->memory_tracker()->Accept(&top_size_visitor);
-
-      ss << kNewLine;
-      ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):";
-      ss << kNewLine;
-      if (!memory_scope_name_.empty()) {
-        ss << "Tracking Memory Scope \"" << memory_scope_name_ << "\", ";
-      } else {
-        ss << "Tracking whole program, ";
-      }
-      ss << "first row is allocation size range, second row is number of "
-         << kNewLine << "allocations in that range." << kNewLine;
-      ss << visitor_binner.ToCSVString();
-      ss << kNewLine;
-      ss << "Largest allocation range: \"" << min_size << "..." << max_size
-         << "\"" << kNewLine;
-      ss << "Printing out top allocations from this range: " << kNewLine;
-      ss << top_size_visitor.ToString(5) << kNewLine;
-    } else {
-      ss << "No allocations for \"" << memory_scope_name_ << "\".";
-    }
-
-    params->logger()->Output(ss.str().c_str());
-    params->logger()->Flush();
-
-    // Sleep until the next sample.
-    base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
-  }
-}
-
-size_t AllocationSizeBinner::GetBucketIndexForAllocationSize(size_t size) {
-  for (int i = 0; i < 32; ++i) {
-    size_t val = 0x1 << i;
-    if (val > size) {
-      return i;
-    }
-  }
-  SB_NOTREACHED();
-  return 32;
-}
-
-void AllocationSizeBinner::GetSizeRange(size_t size, size_t* min_value,
-                                        size_t* max_value) {
-  size_t idx = GetBucketIndexForAllocationSize(size);
-  IndexToSizeRange(idx, min_value, max_value);
-}
-
-void AllocationSizeBinner::IndexToSizeRange(size_t idx, size_t* min_value,
-                                            size_t* max_value) {
-  if (idx == 0) {
-    *min_value = 0;
-    *max_value = 0;
-    return;
-  }
-  *min_value = 0x1 << (idx - 1);
-  *max_value = (*min_value << 1) - 1;
-  return;
-}
-
-size_t AllocationSizeBinner::GetIndexRepresentingMostMemoryConsumption() const {
-  int64 largest_allocation_total = 0;
-  size_t largest_allocation_total_idx = 0;
-
-  for (size_t i = 0; i < allocation_histogram_.size(); ++i) {
-    size_t alloc_size = 0x1 << i;
-    size_t count = allocation_histogram_[i];
-    int64 allocation_total =
-        static_cast<int64>(alloc_size) * static_cast<int64>(count);
-
-    if (largest_allocation_total < allocation_total) {
-      largest_allocation_total = allocation_total;
-      largest_allocation_total_idx = i;
-    }
-  }
-  return largest_allocation_total_idx;
-}
-
-void AllocationSizeBinner::GetLargestSizeRange(size_t* min_value,
-                                               size_t* max_value) const {
-  size_t index = GetIndexRepresentingMostMemoryConsumption();
-  IndexToSizeRange(index, min_value, max_value);
-}
-
-AllocationSizeBinner::AllocationSizeBinner(const AllocationGroup* group_filter)
-    : group_filter_(group_filter) {
-  allocation_histogram_.resize(33);
-}
-
-bool AllocationSizeBinner::PassesFilter(
-    const AllocationRecord& alloc_record) const {
-  if (group_filter_ == NULL) {
-    return true;
-  }
-
-  return alloc_record.allocation_group == group_filter_;
-}
-
-bool AllocationSizeBinner::Visit(const void* /*memory*/,
-                                 const AllocationRecord& alloc_record) {
-  if (PassesFilter(alloc_record)) {
-    const size_t idx = GetBucketIndexForAllocationSize(alloc_record.size);
-    allocation_histogram_[idx]++;
-  }
-  return true;
-}
-
-std::string AllocationSizeBinner::ToCSVString() const {
-  size_t first_idx = 0;
-  size_t end_idx = allocation_histogram_.size();
-
-  // Determine the start index by skipping all consecutive head entries
-  // that are 0.
-  while (first_idx < allocation_histogram_.size()) {
-    const size_t num_allocs = allocation_histogram_[first_idx];
-    if (num_allocs > 0) {
-      break;
-    }
-    first_idx++;
-  }
-
-  // Determine the end index by skipping all consecutive tail entries
-  // that are 0.
-  while (end_idx > 0) {
-    if (end_idx < allocation_histogram_.size()) {
-      const size_t num_allocs = allocation_histogram_[end_idx];
-      if (num_allocs > 0) {
-        ++end_idx;
-        break;
-      }
-    }
-    end_idx--;
-  }
-
-  std::stringstream ss;
-  for (size_t i = first_idx; i < end_idx; ++i) {
-    size_t min = 0;
-    size_t max = 0;
-    IndexToSizeRange(i, &min, &max);
-    std::stringstream name_ss;
-    name_ss << kQuote << min << "..." << max << kQuote;
-    ss << name_ss.str() << kDelimiter;
-  }
-  ss << kNewLine;
-
-  for (size_t i = first_idx; i < end_idx; ++i) {
-    const size_t num_allocs = allocation_histogram_[i];
-    ss << num_allocs << kDelimiter;
-  }
-  ss << kNewLine;
-  return ss.str();
-}
-
-FindTopSizes::FindTopSizes(size_t minimum_size, size_t maximum_size,
-                           const AllocationGroup* group)
-    : minimum_size_(minimum_size),
-      maximum_size_(maximum_size),
-      group_filter_(group) {}
-
-bool FindTopSizes::Visit(const void* /*memory*/,
-                         const AllocationRecord& alloc_record) {
-  if (PassesFilter(alloc_record)) {
-    size_counter_[alloc_record.size]++;
-  }
-  return true;
-}
-
-std::string FindTopSizes::ToString(size_t max_elements_to_print) const {
-  std::vector<GroupAllocation> group_allocs = GetTopAllocations();
-  const size_t n = std::min(max_elements_to_print, group_allocs.size());
-
-  if (!group_allocs.empty()) {
-    std::stringstream ss;
-
-    for (size_t i = 0; i < n; ++i) {
-      GroupAllocation g = group_allocs[i];
-      size_t total_size = g.allocation_count * g.allocation_size;
-      ss << "    " << total_size
-         << " bytes allocated with object size: " << g.allocation_size
-         << " bytes in " << g.allocation_count << " instances " << kNewLine;
-    }
-    return ss.str();
-  } else {
-    return std::string();
-  }
-}
-
-std::vector<FindTopSizes::GroupAllocation> FindTopSizes::GetTopAllocations()
-    const {
-  std::vector<GroupAllocation> group_allocs;
-  // Push objects to a vector.
-  for (SizeCounterMap::const_iterator it = size_counter_.begin();
-       it != size_counter_.end(); ++it) {
-    GroupAllocation alloc = {it->first, it->second};
-    group_allocs.push_back(alloc);
-  }
-
-  std::sort(group_allocs.begin(), group_allocs.end(),
-            GroupAllocation::LessAllocationSize);
-  // Biggest first.
-  std::reverse(group_allocs.begin(), group_allocs.end());
-  return group_allocs;
-}
-
-bool FindTopSizes::PassesFilter(const AllocationRecord& alloc_record) const {
-  if (alloc_record.size < minimum_size_) return false;
-  if (alloc_record.size > maximum_size_) return false;
-  if (!group_filter_) return true;  // No group filter when null.
-  return group_filter_ == alloc_record.allocation_group;
-}
-
-MemoryTrackerLogWriter::MemoryTrackerLogWriter() : start_time_(NowTime()) {
-  buffered_file_writer_.reset(new BufferedFileWriter(MemoryLogPath()));
-  InitAndRegisterMemoryReporter();
-}
-
-MemoryTrackerLogWriter::~MemoryTrackerLogWriter() {
-  // No locks are used for the thread reporter, so when it's set to null
-  // we allow one second for any suspended threads to run through and finish
-  // their reporting.
-  SbMemorySetReporter(NULL);
-  SbThreadSleep(kSbTimeSecond);
-  buffered_file_writer_.reset(NULL);
-}
-
-std::string MemoryTrackerLogWriter::tool_name() const {
-  return "MemoryTrackerLogWriter";
-}
-
-void MemoryTrackerLogWriter::Run(Params* params) {
-  // Run function does almost nothing.
-  params->logger()->Output("MemoryTrackerLogWriter running...");
-}
-
-void MemoryTrackerLogWriter::OnMemoryAllocation(const void* memory_block,
-                                                size_t size) {
-  void* addresses[kMaxStackSize] = {};
-  // Though the SbSystemGetStack API documentation does not specify any possible
-  // negative return values, we take no chance.
-  const size_t count = std::max(SbSystemGetStack(addresses, kMaxStackSize), 0);
-
-  const size_t n = 256;
-  char buff[n] = {0};
-  size_t buff_pos = 0;
-
-  int time_since_start_ms = GetTimeSinceStartMs();
-  // Writes "+ <ALLOCATION ADDRESS> <size> <time>"
-  int bytes_written =
-      SbStringFormatF(buff, sizeof(buff), "+ %" PRIXPTR " %x %d",
-                      reinterpret_cast<uintptr_t>(memory_block),
-                      static_cast<unsigned int>(size), time_since_start_ms);
-
-  buff_pos += bytes_written;
-  const size_t end_index = std::min(count, kStartIndex + kNumAddressPrints);
-
-  // For each of the stack addresses that we care about, concat them to the
-  // buffer. This was originally written to do multiple stack addresses but
-  // this tends to overflow on some lower platforms so it's possible that
-  // this loop only iterates once.
-  for (size_t i = kStartIndex; i < end_index; ++i) {
-    void* p = addresses[i];
-    int bytes_written = SbStringFormatF(buff + buff_pos,
-                                        sizeof(buff) - buff_pos,
-                                        " %" PRIXPTR "",
-                                        reinterpret_cast<uintptr_t>(p));
-    DCHECK_GE(bytes_written, 0);
-
-    if (bytes_written < 0) {
-      DCHECK(false) << "Error occurred while writing string.";
-      continue;
-    }
-
-    buff_pos += static_cast<size_t>(bytes_written);
-  }
-  // Adds a "\n" at the end.
-  SbStringConcat(buff + buff_pos, "\n", static_cast<int>(n - buff_pos));
-  buffered_file_writer_->Append(buff, strlen(buff));
-}
-
-void MemoryTrackerLogWriter::OnMemoryDeallocation(const void* memory_block) {
-  const size_t n = 256;
-  char buff[n] = {0};
-  // Writes "- <ADDRESS OF ALLOCATION> \n"
-  SbStringFormatF(buff, sizeof(buff), "- %" PRIXPTR "\n",
-                  reinterpret_cast<uintptr_t>(memory_block));
-  buffered_file_writer_->Append(buff, strlen(buff));
-}
-
-void MemoryTrackerLogWriter::OnAlloc(void* context, const void* memory,
-                                     size_t size) {
-  MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
-  self->OnMemoryAllocation(memory, size);
-}
-
-void MemoryTrackerLogWriter::OnDealloc(void* context, const void* memory) {
-  MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
-  self->OnMemoryDeallocation(memory);
-}
-
-void MemoryTrackerLogWriter::OnMapMemory(void* context, const void* memory,
-                                         size_t size) {
-  MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
-  self->OnMemoryAllocation(memory, size);
-}
-
-void MemoryTrackerLogWriter::OnUnMapMemory(void* context, const void* memory,
-                                           size_t size) {
-  SB_UNREFERENCED_PARAMETER(size);
-  MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
-  self->OnMemoryDeallocation(memory);
-}
-
-std::string MemoryTrackerLogWriter::MemoryLogPath() {
-  char file_name_buff[2048] = {};
-  SbSystemGetPath(kSbSystemPathDebugOutputDirectory, file_name_buff,
-                  arraysize(file_name_buff));
-  std::string path(file_name_buff);
-  if (!path.empty()) {  // Protect against a dangling "/" at end.
-    const int back_idx_signed = static_cast<int>(path.length()) - 1;
-    if (back_idx_signed >= 0) {
-      const size_t idx = back_idx_signed;
-      if (path[idx] == '/') {
-        path.erase(idx);
-      }
-    }
-  }
-  path.append("/memory_log.txt");
-  return path;
-}
-
-base::TimeTicks MemoryTrackerLogWriter::NowTime() {
-  // NowFromSystemTime() is slower but more accurate. However it might
-  // be useful to use the faster but less accurate version if there is
-  // a speedup.
-  return base::TimeTicks::Now();
-}
-
-int MemoryTrackerLogWriter::GetTimeSinceStartMs() const {
-  base::TimeDelta dt = NowTime() - start_time_;
-  return static_cast<int>(dt.InMilliseconds());
-}
-
-void MemoryTrackerLogWriter::InitAndRegisterMemoryReporter() {
-  DCHECK(!memory_reporter_.get()) << "Memory Reporter already registered.";
-
-  SbMemoryReporter mem_reporter = {OnAlloc, OnDealloc, OnMapMemory,
-                                   OnUnMapMemory, this};
-  memory_reporter_.reset(new SbMemoryReporter(mem_reporter));
-  SbMemorySetReporter(memory_reporter_.get());
-}
-
-MemoryTrackerLeakFinder::MemoryTrackerLeakFinder(StackTraceMode mode)
-    : string_pool_(128),
-      frame_map_(128),
-      callframe_map_(128),
-      stack_trace_mode_(mode) {
-  default_callframe_str_ = &string_pool_.Intern("<Unknown>");
-}
-
-MemoryTrackerLeakFinder::~MemoryTrackerLeakFinder() {
-  frame_map_.Clear();
-  callframe_map_.Clear();
-}
-
-bool MemoryTrackerLeakFinder::IsJavascriptScope(
-    const nb::analytics::CallStack& callstack) {
-  // March through all MemoryScopes in the callstack and check if any of them
-  // contains a javascript scope. If it does return true.
-  for (nb::analytics::CallStack::const_iterator it = callstack.begin();
-       it != callstack.end(); ++it) {
-    const NbMemoryScopeInfo* memory_scope = *it;
-    const bool is_javascript_scope =
-        SbStringFindString(memory_scope->memory_scope_name_, "Javascript");
-    if (is_javascript_scope) {
-      return true;
-    }
-  }
-  return false;
-}
-
-void MemoryTrackerLeakFinder::OnMemoryAllocation(
-    const void* memory_block, const nb::analytics::AllocationRecord& record,
-    const nb::analytics::CallStack& callstack) {
-  // When in javascript mode, filter only allocations with "Javascript" in
-  // the memory scope name.
-  if (stack_trace_mode_ == kJavascript) {
-    if (!IsJavascriptScope(callstack)) {
-      return;
-    }
-  }
-
-  // symbol_str can be used as a unique key. The same value of callstack will
-  // always produce the same string pointer.
-  const std::string* symbol_str = GetOrCreateSymbol(callstack);
-  // Track the allocation.
-  if (!callframe_map_.SetIfMissing(memory_block, symbol_str)) {
-    CallFrameMap::EntryHandle entry_handle;
-    callframe_map_.Get(memory_block, &entry_handle);
-
-    const void* ptr = entry_handle.Valid() ? entry_handle.Key() : NULL;
-    entry_handle.ReleaseLockAndInvalidate();
-
-    DCHECK(false) << "Unexpected memory block at " << memory_block
-                  << " already existed as: " << ptr;
-  }
-
-  AllocationFrameMap::EntryHandle entry_handle;
-  frame_map_.GetOrCreate(symbol_str, &entry_handle);
-
-  // While this entry_handle is in use, no other threads
-  // can modify this entry.
-  AllocRec& alloc_rec = entry_handle.ValueMutable();
-  alloc_rec.total_bytes += record.size;
-  alloc_rec.num_allocs++;
-}
-
-void MemoryTrackerLeakFinder::OnMemoryDeallocation(
-    const void* memory_block, const nb::analytics::AllocationRecord& record,
-    const nb::analytics::CallStack& callstack) {
-  UNREFERENCED_PARAMETER(callstack);
-
-  const std::string* symbol_str = NULL;
-
-  {
-    CallFrameMap::EntryHandle entry_handle;
-    if (!callframe_map_.Get(memory_block, &entry_handle)) {
-      // This happens if the allocation happened before this tool attached
-      // to the memory tracker or if the memory allocation was filtered and
-      // therefore isn't being tracked.
-      return;
-    }
-    symbol_str = entry_handle.Value();
-    callframe_map_.Remove(&entry_handle);
-  }
-
-  // This case can happen if the memory tracker attaches after the allocation.
-  if (!symbol_str) {
-    return;
-  }
-
-  AllocationFrameMap::EntryHandle entry_handle;
-  frame_map_.GetOrCreate(symbol_str, &entry_handle);
-
-  // While entry_handle is in use, no other element can modify
-  // this element.
-  AllocRec& alloc_rec = entry_handle.ValueMutable();
-  alloc_rec.total_bytes -= record.size;
-  alloc_rec.num_allocs--;
-}
-
-std::string MemoryTrackerLeakFinder::tool_name() const {
-  return "MemoryTrackerLeakFinder";
-}
-
-void MemoryTrackerLeakFinder::Run(Params* params) {
-  // Run function does almost nothing.
-  params->logger()->Output("MemoryTrackerLeakFinder running...");
-
-  static const int kMaxSamples = 400;
-
-  // This value will decay whenever the buffer fills up and is compressed via
-  // sample elimination.
-  SbTime sample_time = 50;  // 50 microseconds.
-
-  std::vector<base::TimeDelta> time_values;
-  std::map<const std::string*, std::vector<AllocRec> > map_allocations;
-
-  SbTime start_time = SbTimeGetMonotonicNow();
-  Timer output_trigger(base::TimeDelta::FromMinutes(1));
-
-  const double recording_delay_mins = 5.0;
-
-  // Controls how often an update status message is sent to output.
-  Timer output_status_timer(base::TimeDelta::FromSeconds(1));
-
-  while (true) {
-    if (params->wait_for_finish_signal(sample_time)) {
-      break;  // Finish received, break.
-    }
-    SbTime now_time = SbTimeGetMonotonicNow();
-
-    const base::TimeDelta time_since_start =
-        base::Time::FromSbTime(now_time) - base::Time::FromSbTime(start_time);
-
-    // DELAY RECORDING AS STARTUP MEMORY IS INITIALIZED
-    // Cleaner graphs if we wait until after startup.
-    //
-    const double times_since_start_mins = time_since_start.InSecondsF() / 60.0;
-    if (times_since_start_mins < recording_delay_mins) {
-      if (output_status_timer.UpdateAndIsExpired()) {
-        double remaining_time_mins =
-            (recording_delay_mins - times_since_start_mins);
-        std::stringstream ss;
-        ss << "MemoryTrackerLeakFinder starting in " << remaining_time_mins
-           << " minutes.\n";
-        params->logger()->Output(ss.str().c_str());
-      }
-      continue;
-    }
-
-    time_values.push_back(time_since_start);
-
-    // To improve performance, make a copy of the values to a vector on
-    // the stack.
-    std::vector<std::pair<const std::string*, AllocRec> > samples;
-    SampleSnapshot(&samples);
-
-    // Take a snapshot.
-    for (size_t i = 0; i < samples.size(); ++i) {
-      std::pair<const std::string*, AllocRec> sample = samples[i];
-      std::vector<AllocRec>& sample_history = map_allocations[sample.first];
-
-      sample_history.resize(time_values.size());
-      sample_history.back() = sample.second;
-    }
-
-    if (output_trigger.UpdateAndIsExpired() && time_values.size() > 10) {
-      // Timer expired so dump the current information output.
-
-      std::vector<AllocationProfile> alloc_profiles;
-      GenerateTopLeakingAllocationProfiles(time_values, map_allocations,
-                                           &alloc_profiles);
-
-      if (alloc_profiles.empty()) {
-        params->logger()->Output(
-            "MemoryTrackerLeakFinder: alloc_profiles was "
-            "empty and nothing is written.");
-      } else {
-        if (alloc_profiles.size() > 20) {
-          alloc_profiles.resize(20);
-        }
-
-        std::string csv_str = GenerateCSV(time_values, alloc_profiles);
-        params->logger()->Output(csv_str.c_str());
-      }
-    }
-
-    // Compress the buffer, and modify sample_time so that it's twice as slow.
-    if (time_values.size() >= kMaxSamples) {
-      sample_time *= 2;  // Double the time that it takes to trigger a sample.
-      // Remove every other element in time_values.
-      RemoveOddElements(&time_values);
-
-      // Remove every other element in the samples.
-      for (size_t i = 0; i < samples.size(); ++i) {
-        std::pair<const std::string*, AllocRec> sample = samples[i];
-        std::vector<AllocRec>& sample_history = map_allocations[sample.first];
-        RemoveOddElements(&sample_history);
-      }
-    }
-  }
-}
-
-const std::string* MemoryTrackerLeakFinder::GetOrCreateSymbol(
-    const nb::analytics::CallStack& callstack) {
-  const std::string* symbol_str = NULL;
-
-  // In javascript mode we try and get the javascript symbol. Otherwise
-  // fallback to C++ symbol.
-  if (stack_trace_mode_ == kJavascript) {
-    symbol_str = TryGetJavascriptSymbol();
-    if (symbol_str) {
-      return symbol_str;
-    }
-  }
-
-  symbol_str = GetOrCreateCplusPlusSymbol(callstack);
-  return symbol_str;
-}
-
-const std::string* MemoryTrackerLeakFinder::GetOrCreateCplusPlusSymbol(
-    const nb::analytics::CallStack& callstack) {
-  if (callstack.empty()) {
-    return default_callframe_str_;
-  } else {
-    const NbMemoryScopeInfo* memory_scope = callstack.back();
-
-    const bool skip =
-        SbStringFindString(memory_scope->function_name_, "js_malloc") ||
-        SbStringFindString(memory_scope->function_name_, "js_realloc") ||
-        SbStringFindString(memory_scope->function_name_, "new_");
-
-    // Skip up one callstack because we don't want to track calls to
-    // allocation functions.
-    if (skip && callstack.size() > 1) {
-      memory_scope = callstack[callstack.size() - 2];
-    }
-
-    const char* file_name = BaseNameFast(memory_scope->file_name_);
-
-    // Generates a symbol.
-    // Example:
-    //   "Javascript:Interpreter.cpp(415):RunScript()"
-    char symbol_buff[128];
-    SbStringFormatF(symbol_buff, sizeof(symbol_buff), "%s:%s(%d)::%s()",
-                    memory_scope->memory_scope_name_, file_name,
-                    memory_scope->line_number_, memory_scope->function_name_);
-
-    // Get's a unique pointer to a string containing the symbol. If the symbol
-    // was previously generated then the previous symbol is returned.
-    return &string_pool_.Intern(symbol_buff);
-  }
-}
-
-const std::string* MemoryTrackerLeakFinder::TryGetJavascriptSymbol() {
-  script::mozjs::util::StackTraceGenerator* js_stack_gen =
-      script::mozjs::util::GetThreadLocalStackTraceGenerator();
-  if (!js_stack_gen || !js_stack_gen->Valid()) return NULL;
-
-  // Only get one symbol.
-  char buffer[256];
-  if (!js_stack_gen->GenerateStackTraceString(1, buffer, sizeof(buffer))) {
-    return NULL;
-  }
-  const char* file_name = BaseNameFast(buffer);
-  return &string_pool_.Intern(file_name);
-}
-
-void MemoryTrackerLeakFinder::SampleSnapshot(
-    std::vector<std::pair<const std::string*, AllocRec> >* destination) {
-  destination->erase(destination->begin(), destination->end());
-
-  const size_t sample_size = frame_map_.GetSize();
-
-  // Do this locally.
-  destination->reserve(sample_size + 10);
-  frame_map_.CopyToStdVector(destination);
-}
-
-std::string MemoryTrackerLeakFinder::GenerateCSV(
-    const std::vector<base::TimeDelta>& time_values,
-    const std::vector<AllocationProfile>& data) {
-  std::stringstream ss;
-  ss << std::fixed;  // Turn off scientific notation for CSV values.
-  ss << std::setprecision(3);
-  ss << kNewLine << kNewLine;
-
-  // HEADER
-  ss << "// Allocation in megabytes." << kNewLine;
-  ss << kQuote << "Time(min)" << kQuote << kDelimiter;
-  for (size_t i = 0; i < data.size(); ++i) {
-    const AllocationProfile& alloc_profile = data[i];
-    const std::string& name = *alloc_profile.name_;
-    ss << kQuote << name << kQuote << kDelimiter;
-  }
-  ss << kNewLine;
-
-  // BODY
-  for (size_t i = 0; i < time_values.size(); ++i) {
-    for (size_t j = 0; j < data.size(); ++j) {
-      if (j == 0) {
-        double mins = time_values[i].InSecondsF() / 60.f;
-        if (mins < .001) {
-          mins = 0;
-        }
-        ss << mins << kDelimiter;
-      }
-
-      const AllocationProfile& alloc_profile = data[j];
-      const std::vector<AllocRec>& alloc_history =
-          *alloc_profile.alloc_history_;
-
-      double megabytes = static_cast<double>(alloc_history[i].total_bytes) /
-                         static_cast<double>(1024 * 1024);
-
-      DCHECK_EQ(alloc_history.size(), time_values.size());
-      ss << megabytes << kDelimiter;
-    }
-    ss << kNewLine;
-  }
-  ss << kNewLine << kNewLine;
-  ss << "// Object counts." << kNewLine;
-  ss << kQuote << "Time(min)" << kQuote << kDelimiter;
-  for (size_t i = 0; i < data.size(); ++i) {
-    const AllocationProfile& alloc_profile = data[i];
-    const std::string& name = *alloc_profile.name_;
-    ss << kQuote << name << kQuote << kDelimiter;
-  }
-  ss << kNewLine;
-  for (size_t i = 0; i < time_values.size(); ++i) {
-    for (size_t j = 0; j < data.size(); ++j) {
-      if (j == 0) {
-        double mins = time_values[i].InSecondsF() / 60.f;
-        if (mins < .001) {
-          mins = 0;
-        }
-        ss << mins << kDelimiter;
-      }
-      const AllocationProfile& alloc_profile = data[j];
-      const std::vector<AllocRec>& alloc_history =
-          *alloc_profile.alloc_history_;
-      DCHECK_EQ(alloc_history.size(), time_values.size());
-      ss << alloc_history[i].num_allocs << kDelimiter;
-    }
-    ss << kNewLine;
-  }
-
-  ss << kNewLine << kNewLine;
-  return ss.str();
-}
-
-void MemoryTrackerLeakFinder::GenerateTopLeakingAllocationProfiles(
-    const std::vector<base::TimeDelta>& time_values, const MapSamples& samples,
-    std::vector<AllocationProfile>* destination) {
-  // GENERATE LINEAR REGRESSION LINE
-  // first value is time in microseconds.
-  // second value is total_bytes.
-  std::vector<std::pair<int64_t, int64_t> > sample_data;
-
-  typedef std::map<const std::string*, SlopeYIntercept> LinearRegressionMap;
-  LinearRegressionMap linear_regression_map;
-
-  std::vector<AllocationProfile> allocation_profiles;
-
-  for (MapSamples::const_iterator it = samples.begin(); it != samples.end();
-       ++it) {
-    const std::string* allocation_name = it->first;
-    const std::vector<AllocRec>& allocation_samples = it->second;
-
-    if (allocation_samples.empty()) {
-      continue;
-    }
-
-    // Filter out allocations records that have low number of allocations.
-    if (allocation_samples.back().num_allocs < 10) {
-      continue;
-    }
-
-    // Filter out allocations that are insignificant.
-    int64_t largest_allocation_sample = 0;
-    const int64_t kMinAllocationSize = 1024 * 64;
-    for (size_t i = 0; i < allocation_samples.size(); ++i) {
-      int64_t bytes = allocation_samples[i].total_bytes;
-      largest_allocation_sample = std::max(largest_allocation_sample, bytes);
-    }
-    if (largest_allocation_sample < kMinAllocationSize) {
-      continue;
-    }
-
-    // Filter out allocations where there is no growth between first quartile
-    // and final output.
-    const AllocRec& first_quartile_sample =
-        allocation_samples[allocation_samples.size() / 4];
-    const AllocRec& final_sample = allocation_samples.back();
-
-    const double increase_ratio =
-        static_cast<double>(final_sample.total_bytes) /
-        static_cast<double>(first_quartile_sample.total_bytes);
-
-    // 5% threshold of increase to be qualified as a lead.
-    static const double kMinIncreaseThreshold = .05;
-
-    // If the increase between first quartile and final sample less than 5%
-    // then skip.
-    if (increase_ratio < kMinIncreaseThreshold) {
-      continue;
-    }
-
-    sample_data.clear();  // Recycle.
-    for (size_t i = 0; i < time_values.size(); ++i) {
-      std::pair<int64_t, int64_t> datum(time_values[i].InMicroseconds(),
-                                        allocation_samples[i].total_bytes);
-      sample_data.push_back(datum);
-    }
-
-    double slope = 0;
-    double y_intercept = 0;
-    bool valid = GetLinearFit(sample_data.begin(), sample_data.end(), &slope,
-                              &y_intercept);
-    DCHECK(valid);
-    linear_regression_map[allocation_name] =
-        SlopeYIntercept(slope, y_intercept);
-    AllocationProfile alloc_profile(allocation_name, &allocation_samples, slope,
-                                    y_intercept);
-    alloc_profile.leak_potential_ =
-        allocation_samples.back().total_bytes * slope;
-    allocation_profiles.push_back(alloc_profile);
-  }
-
-  std::sort(allocation_profiles.begin(), allocation_profiles.end(),
-            AllocationProfile::CompareLeakPotential);
-  // Biggest one first.
-  std::reverse(allocation_profiles.begin(), allocation_profiles.end());
-  *destination = allocation_profiles;
-}
-
-}  // namespace memory_tracker
-}  // namespace browser
-}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.h b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.h
deleted file mode 100644
index 706dc8a..0000000
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.h
+++ /dev/null
@@ -1,468 +0,0 @@
-// 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_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_IMPL_H_
-#define COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_IMPL_H_
-
-#include <deque>
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/debug/stack_trace.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/simple_thread.h"
-#include "base/time.h"
-#include "cobalt/browser/memory_tracker/buffered_file_writer.h"
-#include "nb/analytics/memory_tracker.h"
-#include "nb/analytics/memory_tracker_helpers.h"
-#include "nb/concurrent_map.h"
-#include "nb/string_interner.h"
-#include "nb/thread_local_object.h"
-#include "starboard/memory_reporter.h"
-
-namespace starboard {
-class ScopedFile;
-}  // namespace starboard
-
-namespace nb {
-namespace analytics {
-class AllocationGroup;
-class AllocationRecord;
-class MemoryTracker;
-}  // namespace analytics
-}  // namespace nb
-
-namespace cobalt {
-namespace browser {
-namespace memory_tracker {
-
-// Interface for logging. This allows dependency inject to override in the case
-// of future tests.
-class AbstractLogger {
- public:
-  virtual ~AbstractLogger() {}
-  virtual void Output(const char* str) = 0;
-  virtual void Flush() = 0;
-};
-
-// Params holds important data needed by the MemoryTracker tools during
-// their run cycle.
-class Params;
-
-//
-class AbstractMemoryTrackerTool {
- public:
-  virtual ~AbstractMemoryTrackerTool() {}
-  virtual std::string tool_name() const = 0;
-  virtual void Run(Params* params) = 0;
-};
-
-class MemoryTrackerToolThread : public base::SimpleThread {
- public:
-  typedef base::SimpleThread Super;
-  MemoryTrackerToolThread(nb::analytics::MemoryTracker* memory_tracker,
-                          AbstractMemoryTrackerTool* tool,
-                          AbstractLogger* logger);
-  virtual ~MemoryTrackerToolThread();
-
-  virtual void Join() OVERRIDE;
-  virtual void Run() OVERRIDE;
-
- private:
-  scoped_ptr<Params> params_;
-  scoped_ptr<AbstractMemoryTrackerTool> tool_;
-};
-
-// Start() is called when this object is created, and Cancel() & Join() are
-// called during destruction.
-class MemoryTrackerPrint : public AbstractMemoryTrackerTool {
- public:
-  MemoryTrackerPrint() {}
-
-  // Overridden so that the thread can exit gracefully.
-  virtual void Run(Params* params) OVERRIDE;
-  virtual std::string tool_name() const OVERRIDE {
-    return "MemoryTrackerPrintThread";
-  }
-};
-
-// Generates CSV values of the engine.
-// There are three sections of data including:
-//   1. average bytes / alloc
-//   2. # Bytes allocated per memory scope.
-//   3. # Allocations per memory scope.
-// This data can be pasted directly into a Google spreadsheet and visualized.
-// Note that this thread will implicitly call Start() is called during
-// construction and Cancel() & Join() during destruction.
-class MemoryTrackerPrintCSV : public AbstractMemoryTrackerTool {
- public:
-  // This tool will only produce on CSV dump of the engine. This is useful
-  // for profiling startup memory consumption.
-  MemoryTrackerPrintCSV(int sampling_interval_ms, int sampling_time_ms);
-
-  // Overridden so that the thread can exit gracefully.
-  virtual void Run(Params* params) OVERRIDE;
-  virtual std::string tool_name() const OVERRIDE {
-    return "MemoryTrackerPrintCSV";
-  }
-
- private:
-  struct AllocationSamples {
-    std::vector<int32_t> number_allocations_;
-    std::vector<int64_t> allocated_bytes_;
-  };
-  typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
-  static std::string ToCsvString(const MapAllocationSamples& samples);
-  static const char* UntrackedMemoryKey();
-  bool TimeExpiredYet(const Params& params);
-
-  const int sample_interval_ms_;
-  const int sampling_time_ms_;
-};
-
-struct AllocationSamples {
-  std::vector<int32_t> number_allocations_;
-  std::vector<int64_t> allocated_bytes_;
-};
-typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
-typedef std::vector<base::TimeDelta> TimeStamps;
-
-struct TimeSeries {
-  MapAllocationSamples samples_;
-  TimeStamps time_stamps_;
-};
-
-// Generates CSV values of the engine memory. This includes memory
-// consumption in bytes and also the number of allocations.
-// Allocations are segmented by category such as "Javascript" and "Network and
-// are periodically dumped to the Params::abstract_logger_.
-//
-// Compression:
-//  During early run, the sampling rate is extremely high, however the sampling
-//  rate will degrade by half each time the sampling buffer fills up.
-//
-//  Additionally, every time the sampling buffer fills up, space is freed by
-//  evicting every other element.
-//
-// Example output:
-//  //////////////////////////////////////////////
-//  // CSV of BYTES allocated per region (MB's).
-//  // Units are in Megabytes. This is designed
-//  // to be used in a stacked graph.
-//  "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
-//  0,0,0,0,0,0...
-//  1022.54,3.3,7.19,49.93,50.33....
-//  ...
-//  // END CSV of BYTES allocated per region.
-//  //////////////////////////////////////////////
-//
-//  //////////////////////////////////////////////
-//  // CSV of COUNT of allocations per region.
-//  "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
-//  0,0,0,0,0,0...
-//  1022.54,57458,70011,423217,27,54495,43460...
-//  ...
-//  // END CSV of COUNT of allocations per region.
-//  //////////////////////////////////////////////
-class MemoryTrackerCompressedTimeSeries : public AbstractMemoryTrackerTool {
- public:
-  MemoryTrackerCompressedTimeSeries();
-  virtual void Run(Params* params) OVERRIDE;
-  virtual std::string tool_name() const OVERRIDE {
-    return "MemoryTrackerCompressedTimeSeries";
-  }
-
- private:
-  static std::string ToCsvString(const TimeSeries& histogram);
-  static void AcquireSample(nb::analytics::MemoryTracker* memory_tracker,
-                            TimeSeries* histogram, const base::TimeDelta& dt);
-  static bool IsFull(const TimeSeries& histogram, size_t num_samples);
-  static void Compress(TimeSeries* histogram);
-
-  const int number_samples_;
-};
-
-// This tool inspects a memory scope and reports on the memory usage.
-// The output will be a CSV file printed to stdout representing
-// the number of memory allocations for objects. The objects are binned
-// according to the size of the memory allocation. Objects within the same
-// power of two are binned together. For example 1024 will be binned with 1025.
-class MemorySizeBinner : public AbstractMemoryTrackerTool {
- public:
-  // memory_scope_name represents the memory scope that is to be investigated.
-  explicit MemorySizeBinner(const std::string& memory_scope_name);
-
-  virtual void Run(Params* params) OVERRIDE;
-  virtual std::string tool_name() const OVERRIDE {
-    return "MemoryTrackerCompressedTimeSeries";
-  }
-
- private:
-  std::string memory_scope_name_;
-};
-
-// Bins the size according from all the encountered allocations.
-// If AllocationGroup* is non-null, then this is used as a filter such that
-// ONLY allocations belonging to that AllocationGroup are considered.
-class AllocationSizeBinner : public nb::analytics::AllocationVisitor {
- public:
-  typedef std::vector<int> AllocationHistogram;
-  // Maps the input size to a bin number. This function performs a 2^n
-  // mapping. Here is a table of some size mappings:
-  // Example:
-  //   GetIndex(0) == 0;
-  //   GetIndex(1) == 1;
-  //   GetIndex(2) == 2;
-  //   GetIndex(3) == 2;
-  //   GetIndex(4) == 3;
-  //   ...
-  //   GetIndex(15) == 4;
-  //   GetIndex(16) == 5;
-  static size_t GetBucketIndexForAllocationSize(size_t size);
-  static void GetSizeRange(size_t size, size_t* min_value, size_t* max_value);
-  static void IndexToSizeRange(size_t size, size_t* min_value,
-                               size_t* max_value);
-
-  explicit AllocationSizeBinner(
-      const nb::analytics::AllocationGroup* group_filter);
-  virtual bool Visit(
-      const void* memory,
-      const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
-
-  size_t GetIndexRepresentingMostMemoryConsumption() const;
-  void GetLargestSizeRange(size_t* min_value, size_t* max_value) const;
-  bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
-  // Outputs CSV formatted string of the data values.
-  // The header containing the binning range is printed first, then the
-  // the number of allocations in that bin.
-  // Example:
-  //   "16...32","32...64","64...128","128...256",...
-  //   831,3726,3432,10285,...
-  //
-  // In this example there are 831 allocations of size 16-32 bytes.
-  std::string ToCSVString() const;
-  const AllocationHistogram& allocation_histogram() const {
-    return allocation_histogram_;
-  }
-
- private:
-  AllocationHistogram allocation_histogram_;
-  // Only these allocations are tracked.
-  const nb::analytics::AllocationGroup* group_filter_;
-};
-
-// Finds the top allocations by size.
-class FindTopSizes : public nb::analytics::AllocationVisitor {
- public:
-  FindTopSizes(size_t minimum_size, size_t maximum_size,
-               const nb::analytics::AllocationGroup* group);
-
-  virtual bool Visit(
-      const void* memory,
-      const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
-
-  struct GroupAllocation {
-    size_t allocation_size;
-    size_t allocation_count;
-
-    static bool LessAllocationSize(GroupAllocation a, GroupAllocation b) {
-      size_t total_size_a = a.allocation_size * a.allocation_count;
-      size_t total_size_b = b.allocation_size * b.allocation_count;
-      return total_size_a < total_size_b;
-    }
-  };
-
-  std::string ToString(size_t max_elements_to_print) const;
-  std::vector<GroupAllocation> GetTopAllocations() const;
-
- private:
-  typedef std::map<size_t, size_t> SizeCounterMap;
-
-  bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
-  size_t minimum_size_;
-  size_t maximum_size_;
-  const nb::analytics::AllocationGroup* group_filter_;
-  SizeCounterMap size_counter_;
-};
-
-// Outputs memory_log.txt to the output log location. This log contains
-// allocations with a stack trace (non-symbolized) and deallocations without
-// a stack.
-class MemoryTrackerLogWriter : public AbstractMemoryTrackerTool {
- public:
-  MemoryTrackerLogWriter();
-  virtual ~MemoryTrackerLogWriter();
-
-  // Interface AbstrctMemoryTrackerTool
-  virtual std::string tool_name() const OVERRIDE;
-  virtual void Run(Params* params) OVERRIDE;
-
-  void OnMemoryAllocation(const void* memory_block, size_t size);
-  void OnMemoryDeallocation(const void* memory_block);
-
- private:
-  // Callbacks for MemoryReporter
-  static void OnAlloc(void* context, const void* memory, size_t size);
-  static void OnDealloc(void* context, const void* memory);
-  static void OnMapMemory(void* context, const void* memory, size_t size);
-  static void OnUnMapMemory(void* context, const void* memory, size_t size);
-  static std::string MemoryLogPath();
-
-  static base::TimeTicks NowTime();
-  static const size_t kStartIndex = 5;
-  static const size_t kNumAddressPrints = 1;
-  static const size_t kMaxStackSize = 10;
-
-  int GetTimeSinceStartMs() const;
-  void InitAndRegisterMemoryReporter();
-
-  base::TimeTicks start_time_;
-  scoped_ptr<SbMemoryReporter> memory_reporter_;
-  scoped_ptr<BufferedFileWriter> buffered_file_writer_;
-};
-
-// Records allocations and outputs leaks as a CSV. Each column will
-// have an associated symbol.
-class MemoryTrackerLeakFinder
-    : public AbstractMemoryTrackerTool,
-      public nb::analytics::MemoryTrackerDebugCallback {
- public:
-  enum StackTraceMode {
-    // Always get the C++ version of the stack trace.
-    kCPlusPlus,
-    // Whenever possible, get the javascript stack trace,
-    // else fallback to CPlusPlus.
-    kJavascript,
-  };
-
-  explicit MemoryTrackerLeakFinder(StackTraceMode pref);
-  virtual ~MemoryTrackerLeakFinder();
-
-  // OnMemoryAllocation() and OnMemoryDeallocation() are part of
-  // class MemoryTrackerDebugCallback.
-  void OnMemoryAllocation(const void* memory_block,
-                          const nb::analytics::AllocationRecord& record,
-                          const nb::analytics::CallStack& callstack) OVERRIDE;
-
-  void OnMemoryDeallocation(const void* memory_block,
-                            const nb::analytics::AllocationRecord& record,
-                            const nb::analytics::CallStack& callstack) OVERRIDE;
-
-  // Interface AbstractMemoryTrackerTool
-  virtual std::string tool_name() const OVERRIDE;
-  virtual void Run(Params* params) OVERRIDE;
-
-  const std::string* GetOrCreateSymbol(
-      const nb::analytics::CallStack& callstack);
-
-  const std::string* TryGetJavascriptSymbol();
-  const std::string* GetOrCreateCplusPlusSymbol(
-      const nb::analytics::CallStack& callstack);
-
- private:
-  struct AllocRec;
-  // A map from callsite -> allocation size.
-  // typedef std::map<const std::string*, AllocRec> AllocationFrameMap;
-  typedef nb::ConcurrentMap<const std::string*, AllocRec,
-                            nb::PODHasher<const std::string*> >
-      AllocationFrameMap;
-
-  // A map of memory -> callsite.
-  // This keeps track of what callsites belong to which allocations.
-  // typedef std::map<const void*, const std::string*> CallFrameMap;
-  //
-  typedef nb::ConcurrentMap<const void*, const std::string*,
-                            nb::PODHasher<const std::string*> > CallFrameMap;
-
-  // A map from callsite -> allocation data.
-  typedef std::map<const std::string*, std::vector<AllocRec> > MapSamples;
-  // A value type for holding a slope and Y-intercept.
-  typedef std::pair<double, double> SlopeYIntercept;
-  // An AllocationRecord.
-  struct AllocRec {
-    AllocRec() : total_bytes(0), num_allocs(0) {}
-    AllocRec(const AllocRec& other)
-        : total_bytes(other.total_bytes), num_allocs(other.num_allocs) {}
-    AllocRec& operator=(const AllocRec& other) {
-      total_bytes = other.total_bytes;
-      num_allocs = other.num_allocs;
-      return *this;
-    }
-    int64_t total_bytes;
-    int32_t num_allocs;
-  };
-
-  // This data structure is used to for sorting leaks. The important variable
-  // is |leak_potential|, which represents how likely this AllocationProfile
-  // is a leak.
-  struct AllocationProfile {
-    AllocationProfile()
-        : name_(NULL),
-          alloc_history_(NULL),
-          slope_(0),
-          y_intercept_(0),
-          leak_potential_(0) {}
-
-    AllocationProfile(const std::string* name,
-                      const std::vector<AllocRec>* alloc_history, double slope,
-                      double y_intercept)
-        : name_(name),
-          alloc_history_(alloc_history),
-          slope_(slope),
-          y_intercept_(y_intercept),
-          leak_potential_(0) {}
-    const std::string* name_;
-    const std::vector<AllocRec>* alloc_history_;
-    // Linear regression data.
-    double slope_;
-    double y_intercept_;
-
-    // This this value is set externally. Higher values indicate higher chance
-    // that this is a leak.
-    double leak_potential_;
-
-    static bool CompareLeakPotential(const AllocationProfile& a,
-                                     const AllocationProfile& b) {
-      return a.leak_potential_ < b.leak_potential_;
-    }
-  };
-
-  static std::string GenerateCSV(
-      const std::vector<base::TimeDelta>& time_values,
-      const std::vector<AllocationProfile>& data);
-
-  void SampleSnapshot(
-      std::vector<std::pair<const std::string*, AllocRec> >* destination);
-
-  static void GenerateTopLeakingAllocationProfiles(
-      const std::vector<base::TimeDelta>& time_values,
-      const MapSamples& samples, std::vector<AllocationProfile>* destination);
-
-  static bool IsJavascriptScope(const nb::analytics::CallStack& callstack);
-
-  const std::string* default_callframe_str_;
-  nb::ConcurrentStringInterner string_pool_;
-  AllocationFrameMap frame_map_;
-  CallFrameMap callframe_map_;
-  StackTraceMode stack_trace_mode_;
-};
-}  // namespace memory_tracker
-}  // namespace browser
-}  // namespace cobalt
-
-#endif  // COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_IMPL_H_
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool.cc b/src/cobalt/browser/memory_tracker/tool.cc
similarity index 87%
rename from src/cobalt/browser/memory_tracker/memory_tracker_tool.cc
rename to src/cobalt/browser/memory_tracker/tool.cc
index f21b544..72cb675 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/browser/memory_tracker/memory_tracker_tool.h"
+#include "cobalt/browser/memory_tracker/tool.h"
 
 #include <cstdlib>
 #include <map>
@@ -20,7 +20,11 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
-#include "cobalt/browser/memory_tracker/memory_tracker_tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h"
+#include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h"
+#include "cobalt/browser/memory_tracker/tool/log_writer_tool.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/tool_thread.h"
 #include "nb/analytics/memory_tracker_helpers.h"
 #include "nb/lexical_cast.h"
 #include "starboard/log.h"
@@ -31,14 +35,14 @@
 
 #if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
 // Null implementation for memory tracker tool.
-class MemoryTrackerToolNull : public MemoryTrackerTool {
+class NullTool : public Tool {
  public:
-  virtual ~MemoryTrackerToolNull() {}
+  virtual ~NullTool() {}
 };
 
 // Instantiates the memory tracker tool from the command argument.
-scoped_ptr<MemoryTrackerTool> CreateMemoryTrackerTool(const std::string&) {
-  return scoped_ptr<MemoryTrackerTool>(new MemoryTrackerToolNull);
+scoped_ptr<Tool> CreateMemoryTrackerTool(const std::string&) {
+  return scoped_ptr<Tool>(new NullTool);
 }
 
 #else
@@ -98,7 +102,7 @@
   const size_t last_closing_paren_idx = command_arg.find_last_of(')');
 
   if ((first_open_paren_idx == std::string::npos) ||
-     (last_closing_paren_idx == std::string::npos)) {
+      (last_closing_paren_idx == std::string::npos)) {
     return "";
   }
 
@@ -106,14 +110,14 @@
     return "";
   }
 
-  const size_t begin_idx = first_open_paren_idx+1;
-  const size_t length = (last_closing_paren_idx-begin_idx);
+  const size_t begin_idx = first_open_paren_idx + 1;
+  const size_t length = (last_closing_paren_idx - begin_idx);
   std::string arg_str = command_arg.substr(begin_idx, length);
   return arg_str;
 }
 }  // namespace.
 
-class MemoryTrackerThreadImpl : public MemoryTrackerTool {
+class MemoryTrackerThreadImpl : public Tool {
  public:
   explicit MemoryTrackerThreadImpl(base::SimpleThread* ptr)
       : thread_ptr_(ptr) {}
@@ -121,8 +125,7 @@
   scoped_ptr<base::SimpleThread> thread_ptr_;
 };
 
-scoped_ptr<MemoryTrackerTool> CreateMemoryTrackerTool(
-    const std::string& command_arg) {
+scoped_ptr<Tool> CreateMemoryTrackerTool(const std::string& command_arg) {
   // The command line switch for memory_tracker was used. Look into the args
   // and determine the mode that the memory_tracker should be used for.
 
@@ -240,7 +243,7 @@
 
   // Tools are expected to instantiate the MemoryTracker if they need it.
   MemoryTracker* memory_tracker = NULL;
-  scoped_ptr<AbstractMemoryTrackerTool> tool_ptr;
+  scoped_ptr<AbstractTool> tool_ptr;
 
   const SwitchVal& value = found_it->second;
   switch (value.enum_value) {
@@ -290,22 +293,21 @@
       // Time until output is triggered.
       int sampling_time_ms = F::ToMilliseconds(num_mins);
       // Create a thread that will gather memory metrics for startup.
-      tool_ptr.reset(
-          new MemoryTrackerPrintCSV(sampling_interval_ms, sampling_time_ms));
+      tool_ptr.reset(new PrintCSVTool(sampling_interval_ms, sampling_time_ms));
       break;
     }
     case kContinuousPrinter: {
       memory_tracker = MemoryTracker::Get();
       memory_tracker->InstallGlobalTrackingHooks();
       // Create a thread that will continuously report memory use.
-      tool_ptr.reset(new MemoryTrackerPrint);
+      tool_ptr.reset(new PrintTool);
       break;
     }
     case kCompressedTimeseries: {
       memory_tracker = MemoryTracker::Get();
       memory_tracker->InstallGlobalTrackingHooks();
       // Create a thread that will continuously report memory use.
-      tool_ptr.reset(new MemoryTrackerCompressedTimeSeries);
+      tool_ptr.reset(new CompressedTimeSeriesTool);
       break;
     }
     case kBinnerAnalytics: {
@@ -317,14 +319,13 @@
       break;
     }
     case kAllocationLogger: {
-      scoped_ptr<MemoryTrackerLogWriter> disk_writer_tool(
-          new MemoryTrackerLogWriter());
+      scoped_ptr<LogWriterTool> disk_writer_tool(new LogWriterTool());
       tool_ptr.reset(disk_writer_tool.release());
       break;
     }
     case kLeakTracer: {
-      scoped_ptr<MemoryTrackerLeakFinder> leak_finder(
-          new MemoryTrackerLeakFinder(MemoryTrackerLeakFinder::kCPlusPlus));
+      scoped_ptr<LeakFinderTool> leak_finder(
+          new LeakFinderTool(LeakFinderTool::kCPlusPlus));
 
       memory_tracker = MemoryTracker::Get();
       memory_tracker->InstallGlobalTrackingHooks();
@@ -333,8 +334,8 @@
       break;
     }
     case kJavascriptLeakTracer: {
-      scoped_ptr<MemoryTrackerLeakFinder> leak_finder(
-          new MemoryTrackerLeakFinder(MemoryTrackerLeakFinder::kJavascript));
+      scoped_ptr<LeakFinderTool> leak_finder(
+          new LeakFinderTool(LeakFinderTool::kJavascript));
 
       memory_tracker = MemoryTracker::Get();
       memory_tracker->InstallGlobalTrackingHooks();
@@ -350,11 +351,11 @@
 
   if (tool_ptr.get()) {
     base::SimpleThread* thread =
-        new MemoryTrackerToolThread(memory_tracker,  // May be NULL.
-                                    tool_ptr.release(), new SbLogger);
-    return scoped_ptr<MemoryTrackerTool>(new MemoryTrackerThreadImpl(thread));
+        new ToolThread(memory_tracker,  // May be NULL.
+                       tool_ptr.release(), new SbLogger);
+    return scoped_ptr<Tool>(new MemoryTrackerThreadImpl(thread));
   } else {
-    return scoped_ptr<MemoryTrackerTool>();
+    return scoped_ptr<Tool>();
   }
 }
 
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool.h b/src/cobalt/browser/memory_tracker/tool.h
similarity index 72%
rename from src/cobalt/browser/memory_tracker/memory_tracker_tool.h
rename to src/cobalt/browser/memory_tracker/tool.h
index 8d17d9f..cd8eeaf 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool.h
+++ b/src/cobalt/browser/memory_tracker/tool.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_H_
-#define COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_H_
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_H_
 
 #include <string>
 
@@ -23,17 +23,17 @@
 namespace browser {
 namespace memory_tracker {
 
-class MemoryTrackerTool {
+// Self running tool. It only has a destructor defined.
+class Tool {
  public:
-  virtual ~MemoryTrackerTool() {}
+  virtual ~Tool() {}
 };
 
 // Instantiates the memory tracker tool from the command argument.
-scoped_ptr<MemoryTrackerTool> CreateMemoryTrackerTool(
-    const std::string& command_arg);
+scoped_ptr<Tool> CreateMemoryTrackerTool(const std::string& command_arg);
 
 }  // namespace memory_tracker
 }  // namespace browser
 }  // namespace cobalt
 
-#endif  // COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_H_
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/buffered_file_writer.cc b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.cc
similarity index 90%
rename from src/cobalt/browser/memory_tracker/buffered_file_writer.cc
rename to src/cobalt/browser/memory_tracker/tool/buffered_file_writer.cc
index 3a8555a..284119b 100644
--- a/src/cobalt/browser/memory_tracker/buffered_file_writer.cc
+++ b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/browser/memory_tracker/buffered_file_writer.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
 
 #include "base/basictypes.h"
 #include "base/logging.h"
@@ -67,13 +67,11 @@
     return;
   }
 
-  flush_thread_ = SbThreadCreate(0,     // default stack size.
-                                  kSbThreadPriorityHigh,
-                                  kSbThreadNoAffinity,
-                                  true,  // true - joinable.
-                                  "AllocationLoggerWriter",
-                                  ThreadEntryFunc,
-                                  static_cast<void*>(this));
+  flush_thread_ = SbThreadCreate(0,  // default stack size.
+                                 kSbThreadPriorityHigh, kSbThreadNoAffinity,
+                                 true,  // true - joinable.
+                                 "AllocationLoggerWriter", ThreadEntryFunc,
+                                 static_cast<void*>(this));
 }
 
 void BufferedFileWriter::Append(const char* data, size_t num_bytes) {
diff --git a/src/cobalt/browser/memory_tracker/buffered_file_writer.h b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.h
similarity index 92%
rename from src/cobalt/browser/memory_tracker/buffered_file_writer.h
rename to src/cobalt/browser/memory_tracker/tool/buffered_file_writer.h
index 63c25e3..be3b4bb 100644
--- a/src/cobalt/browser/memory_tracker/buffered_file_writer.h
+++ b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_BROWSER_MEMORY_TRACKER_BUFFERED_FILE_WRITER_H_
-#define COBALT_BROWSER_MEMORY_TRACKER_BUFFERED_FILE_WRITER_H_
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_BUFFERED_FILE_WRITER_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_BUFFERED_FILE_WRITER_H_
 
 #include <string>
 
@@ -80,4 +80,4 @@
 }  // namespace browser
 }  // namespace cobalt
 
-#endif  // COBALT_BROWSER_MEMORY_TRACKER_BUFFERED_FILE_WRITER_H_
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_BUFFERED_FILE_WRITER_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
new file mode 100644
index 0000000..2942749
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
@@ -0,0 +1,247 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h"
+
+#include <iomanip>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "nb/analytics/memory_tracker.h"
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+///////////////////////////////////////////////////////////////////////////////
+CompressedTimeSeriesTool::CompressedTimeSeriesTool() : number_samples_(400) {}
+
+void CompressedTimeSeriesTool::Run(Params* params) {
+  TimeSeries timeseries;
+  Timer timer_status_message(base::TimeDelta::FromSeconds(1));
+
+  // Outputs CSV once every minute.
+  Timer timer_output_csv(base::TimeDelta::FromMinutes(1));
+
+  // Initial sample time is every 50 milliseconds.
+  base::TimeDelta current_sample_interval =
+      base::TimeDelta::FromMilliseconds(50);
+
+  while (!params->wait_for_finish_signal(current_sample_interval.ToSbTime())) {
+    AcquireSample(params->memory_tracker(), &timeseries,
+                  params->time_since_start());
+
+    if (IsFull(timeseries, number_samples_)) {
+      Compress(&timeseries);           // Remove every other element.
+      current_sample_interval *= 2;    // Double the sample time.
+      timer_status_message.Restart();  // Skip status message.
+    }
+
+    if (timer_output_csv.UpdateAndIsExpired()) {
+      const std::string str = ToCsvString(timeseries);
+      params->logger()->Output(str.c_str());
+      timer_status_message.Restart();  // Skip status message.
+    }
+
+    // Print status message.
+    if (timer_status_message.UpdateAndIsExpired()) {
+      std::stringstream ss;
+      ss << tool_name() << " is running..." << kNewLine;
+      params->logger()->Output(ss.str().c_str());
+    }
+  }
+}
+
+std::string CompressedTimeSeriesTool::ToCsvString(
+    const TimeSeries& timeseries) {
+  size_t largest_sample_size = 0;
+  size_t smallest_sample_size = INT_MAX;
+
+  typedef MapAllocationSamples::const_iterator MapIt;
+
+  // Sanitize samples_in and store as samples.
+  const MapAllocationSamples& samples_in = timeseries.samples_;
+  MapAllocationSamples samples;
+  for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
+    std::string name = it->first;
+    const AllocationSamples& value = it->second;
+
+    if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
+      SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
+      return "ERROR";
+    }
+
+    const size_t n = value.allocated_bytes_.size();
+    if (n > largest_sample_size) {
+      largest_sample_size = n;
+    }
+    if (n < smallest_sample_size) {
+      smallest_sample_size = n;
+    }
+
+    const bool duplicate_found = (samples.end() != samples.find(name));
+    if (duplicate_found) {
+      SB_NOTREACHED() << "Error, duplicate found for entry: " << name
+                      << kNewLine;
+    }
+    // Store value as a sanitized sample.
+    samples[name] = value;
+  }
+
+  SB_DCHECK(largest_sample_size == smallest_sample_size);
+
+  std::stringstream ss;
+
+  // Begin output to CSV.
+
+  // Preamble
+  ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
+     << "// CSV of BYTES allocated per region (MB's)." << kNewLine
+     << "// Units are in Megabytes. This is designed" << kNewLine
+     << "// to be used in a stacked graph." << kNewLine;
+
+  // HEADER.
+  ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
+  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+    const std::string& name = it->first;
+    ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
+  }
+  ss << kNewLine;
+
+  // Print out the values of each of the samples.
+  for (size_t i = 0; i < smallest_sample_size; ++i) {
+    // Output time first so that it can be used as an x-axis.
+    const double time_mins =
+        timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
+    ss << time_mins << ",";
+    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+      const int64 alloc_bytes = it->second.allocated_bytes_[i];
+      // Convert to float megabytes with decimals of precision.
+      double n = alloc_bytes / (1000 * 10);
+      n = n / (100.);
+      ss << n << kDelimiter;
+    }
+    ss << kNewLine;
+  }
+  ss << "// END CSV of BYTES allocated per region." << kNewLine;
+  ss << "//////////////////////////////////////////////";
+
+  ss << kNewLine;
+  // Preamble
+  ss << kNewLine << "//////////////////////////////////////////////";
+  ss << kNewLine << "// CSV of COUNT of allocations per region." << kNewLine;
+
+  // HEADER
+  ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
+  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+    const std::string& name = it->first;
+    ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
+  }
+  ss << kNewLine;
+  for (size_t i = 0; i < smallest_sample_size; ++i) {
+    // Output time first so that it can be used as an x-axis.
+    const double time_mins =
+        timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
+    ss << time_mins << ",";
+    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+      const int64 n_allocs = it->second.number_allocations_[i];
+      ss << n_allocs << kDelimiter;
+    }
+    ss << kNewLine;
+  }
+  ss << "// END CSV of COUNT of allocations per region." << kNewLine;
+  ss << "//////////////////////////////////////////////";
+  ss << kNewLine << kNewLine;
+
+  std::string output = ss.str();
+  return output;
+}
+
+void CompressedTimeSeriesTool::AcquireSample(
+    nb::analytics::MemoryTracker* memory_tracker, TimeSeries* timeseries,
+    const base::TimeDelta& time_now) {
+  const size_t sample_count = timeseries->time_stamps_.size();
+  timeseries->time_stamps_.push_back(time_now);
+
+  MapAllocationSamples& map_samples = timeseries->samples_;
+
+  std::vector<const nb::analytics::AllocationGroup*> vector_output;
+  memory_tracker->GetAllocationGroups(&vector_output);
+
+  // Sample all known memory scopes.
+  for (size_t i = 0; i < vector_output.size(); ++i) {
+    const nb::analytics::AllocationGroup* group = vector_output[i];
+    const std::string& name = group->name();
+
+    const bool first_creation =
+        map_samples.find(group->name()) == map_samples.end();
+
+    AllocationSamples& new_entry = map_samples[name];
+
+    // Didn't see it before so create new entry.
+    if (first_creation) {
+      // Make up for lost samples...
+      new_entry.allocated_bytes_.resize(sample_count, 0);
+      new_entry.number_allocations_.resize(sample_count, 0);
+    }
+
+    int32 num_allocs = -1;
+    int64 allocation_bytes = -1;
+    group->GetAggregateStats(&num_allocs, &allocation_bytes);
+
+    new_entry.allocated_bytes_.push_back(allocation_bytes);
+    new_entry.number_allocations_.push_back(num_allocs);
+  }
+
+  // Sample the in use memory bytes reported by malloc.
+  nb::analytics::MemoryStats memory_stats =
+      nb::analytics::GetProcessMemoryStats();
+  AllocationSamples& process_mem_in_use = map_samples["ProcessMemoryInUse"];
+  process_mem_in_use.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
+  process_mem_in_use.number_allocations_.push_back(0);
+
+  // Sample the reserved memory bytes reported by malloc.
+  AllocationSamples& process_mem_reserved =
+      map_samples["ProcessMemoryReserved"];
+  process_mem_reserved.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
+  process_mem_reserved.number_allocations_.push_back(0);
+}
+
+bool CompressedTimeSeriesTool::IsFull(const TimeSeries& timeseries,
+                                      size_t samples_limit) {
+  return timeseries.time_stamps_.size() >= samples_limit;
+}
+
+void CompressedTimeSeriesTool::Compress(TimeSeries* timeseries) {
+  typedef MapAllocationSamples::iterator MapIt;
+  MapAllocationSamples& samples = timeseries->samples_;
+  RemoveOddElements(&(timeseries->time_stamps_));
+  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+    AllocationSamples& data = it->second;
+    RemoveOddElements(&data.allocated_bytes_);
+    RemoveOddElements(&data.number_allocations_);
+  }
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h
new file mode 100644
index 0000000..caa5a6b
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h
@@ -0,0 +1,81 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_COMPRESSED_TIME_SERIES_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_COMPRESSED_TIME_SERIES_TOOL_H_
+
+#include <string>
+
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Generates CSV values of the engine memory. This includes memory
+// consumption in bytes and also the number of allocations.
+// Allocations are segmented by category such as "Javascript" and "Network and
+// are periodically dumped to the Params::abstract_logger_.
+//
+// Compression:
+//  During early run, the sampling rate is extremely high, however the sampling
+//  rate will degrade by half each time the sampling buffer fills up.
+//
+//  Additionally, every time the sampling buffer fills up, space is freed by
+//  evicting every other element.
+//
+// Example output:
+//  //////////////////////////////////////////////
+//  // CSV of BYTES allocated per region (MB's).
+//  // Units are in Megabytes. This is designed
+//  // to be used in a stacked graph.
+//  "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
+//  0,0,0,0,0,0...
+//  1022.54,3.3,7.19,49.93,50.33....
+//  ...
+//  // END CSV of BYTES allocated per region.
+//  //////////////////////////////////////////////
+//
+//  //////////////////////////////////////////////
+//  // CSV of COUNT of allocations per region.
+//  "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
+//  0,0,0,0,0,0...
+//  1022.54,57458,70011,423217,27,54495,43460...
+//  ...
+//  // END CSV of COUNT of allocations per region.
+//  //////////////////////////////////////////////
+class CompressedTimeSeriesTool : public AbstractTool {
+ public:
+  CompressedTimeSeriesTool();
+  virtual void Run(Params* params) OVERRIDE;
+  virtual std::string tool_name() const OVERRIDE {
+    return "MemoryTrackerCompressedTimeSeries";
+  }
+
+ private:
+  static std::string ToCsvString(const TimeSeries& histogram);
+  static void AcquireSample(nb::analytics::MemoryTracker* memory_tracker,
+                            TimeSeries* histogram, const base::TimeDelta& dt);
+  static bool IsFull(const TimeSeries& histogram, size_t num_samples);
+  static void Compress(TimeSeries* histogram);
+
+  const int number_samples_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_COMPRESSED_TIME_SERIES_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
new file mode 100644
index 0000000..95313d2
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
@@ -0,0 +1,461 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <map>
+#include <utility>
+
+#include "base/timer.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "nb/memory_scope.h"
+#include "starboard/string.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+LeakFinderTool::LeakFinderTool(StackTraceMode mode)
+    : string_pool_(128),
+      frame_map_(128),
+      callframe_map_(128),
+      stack_trace_mode_(mode) {
+  default_callframe_str_ = &string_pool_.Intern("<Unknown>");
+}
+
+LeakFinderTool::~LeakFinderTool() {
+  frame_map_.Clear();
+  callframe_map_.Clear();
+}
+
+bool LeakFinderTool::IsJavascriptScope(
+    const nb::analytics::CallStack& callstack) {
+  // March through all MemoryScopes in the callstack and check if any of them
+  // contains a javascript scope. If it does return true.
+  for (nb::analytics::CallStack::const_iterator it = callstack.begin();
+       it != callstack.end(); ++it) {
+    const NbMemoryScopeInfo* memory_scope = *it;
+    const bool is_javascript_scope =
+        SbStringFindString(memory_scope->memory_scope_name_, "Javascript");
+    if (is_javascript_scope) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void LeakFinderTool::OnMemoryAllocation(
+    const void* memory_block, const nb::analytics::AllocationRecord& record,
+    const nb::analytics::CallStack& callstack) {
+  // When in javascript mode, filter only allocations with "Javascript" in
+  // the memory scope name.
+  if (stack_trace_mode_ == kJavascript) {
+    if (!IsJavascriptScope(callstack)) {
+      return;
+    }
+  }
+
+  // symbol_str can be used as a unique key. The same value of callstack will
+  // always produce the same string pointer.
+  const std::string* symbol_str = GetOrCreateSymbol(callstack);
+  // Track the allocation.
+  if (!callframe_map_.SetIfMissing(memory_block, symbol_str)) {
+    CallFrameMap::EntryHandle entry_handle;
+    callframe_map_.Get(memory_block, &entry_handle);
+
+    const void* ptr = entry_handle.Valid() ? entry_handle.Key() : NULL;
+    entry_handle.ReleaseLockAndInvalidate();
+
+    DCHECK(false) << "Unexpected memory block at " << memory_block
+                  << " already existed as: " << ptr;
+  }
+
+  AllocationFrameMap::EntryHandle entry_handle;
+  frame_map_.GetOrCreate(symbol_str, &entry_handle);
+
+  // While this entry_handle is in use, no other threads
+  // can modify this entry.
+  AllocRec& alloc_rec = entry_handle.ValueMutable();
+  alloc_rec.total_bytes += record.size;
+  alloc_rec.num_allocs++;
+}
+
+void LeakFinderTool::OnMemoryDeallocation(
+    const void* memory_block, const nb::analytics::AllocationRecord& record,
+    const nb::analytics::CallStack& callstack) {
+  UNREFERENCED_PARAMETER(callstack);
+
+  const std::string* symbol_str = NULL;
+
+  {
+    CallFrameMap::EntryHandle entry_handle;
+    if (!callframe_map_.Get(memory_block, &entry_handle)) {
+      // This happens if the allocation happened before this tool attached
+      // to the memory tracker or if the memory allocation was filtered and
+      // therefore isn't being tracked.
+      return;
+    }
+    symbol_str = entry_handle.Value();
+    callframe_map_.Remove(&entry_handle);
+  }
+
+  // This case can happen if the memory tracker attaches after the allocation.
+  if (!symbol_str) {
+    return;
+  }
+
+  AllocationFrameMap::EntryHandle entry_handle;
+  frame_map_.GetOrCreate(symbol_str, &entry_handle);
+
+  // While entry_handle is in use, no other element can modify
+  // this element.
+  AllocRec& alloc_rec = entry_handle.ValueMutable();
+  alloc_rec.total_bytes -= record.size;
+  alloc_rec.num_allocs--;
+}
+
+std::string LeakFinderTool::tool_name() const {
+  return "MemoryTrackerLeakFinder";
+}
+
+void LeakFinderTool::Run(Params* params) {
+  // Run function does almost nothing.
+  params->logger()->Output("MemoryTrackerLeakFinder running...");
+
+  static const size_t kMaxSamples = 400;
+
+  // This value will decay whenever the buffer fills up and is compressed via
+  // sample elimination.
+  SbTime sample_time = 50;  // 50 microseconds.
+
+  std::vector<base::TimeDelta> time_values;
+  std::map<const std::string*, std::vector<AllocRec> > map_allocations;
+
+  SbTime start_time = SbTimeGetMonotonicNow();
+  Timer output_trigger(base::TimeDelta::FromMinutes(1));
+
+  const double recording_delay_mins = 5.0;
+
+  // Controls how often an update status message is sent to output.
+  Timer output_status_timer(base::TimeDelta::FromSeconds(1));
+
+  while (true) {
+    if (params->wait_for_finish_signal(sample_time)) {
+      break;  // Finish received, break.
+    }
+    SbTime now_time = SbTimeGetMonotonicNow();
+
+    const base::TimeDelta time_since_start =
+        base::Time::FromSbTime(now_time) - base::Time::FromSbTime(start_time);
+
+    // DELAY RECORDING AS STARTUP MEMORY IS INITIALIZED
+    // Cleaner graphs if we wait until after startup.
+    //
+    const double times_since_start_mins = time_since_start.InSecondsF() / 60.0;
+    if (times_since_start_mins < recording_delay_mins) {
+      if (output_status_timer.UpdateAndIsExpired()) {
+        double remaining_time_mins =
+            (recording_delay_mins - times_since_start_mins);
+        std::stringstream ss;
+        ss << "MemoryTrackerLeakFinder starting in " << remaining_time_mins
+           << " minutes.\n";
+        params->logger()->Output(ss.str().c_str());
+      }
+      continue;
+    }
+
+    time_values.push_back(time_since_start);
+
+    // To improve performance, make a copy of the values to a vector on
+    // the stack.
+    std::vector<std::pair<const std::string*, AllocRec> > samples;
+    SampleSnapshot(&samples);
+
+    // Take a snapshot.
+    for (size_t i = 0; i < samples.size(); ++i) {
+      std::pair<const std::string*, AllocRec> sample = samples[i];
+      std::vector<AllocRec>& sample_history = map_allocations[sample.first];
+
+      sample_history.resize(time_values.size());
+      sample_history.back() = sample.second;
+    }
+
+    if (output_trigger.UpdateAndIsExpired() && time_values.size() > 10) {
+      // Timer expired so dump the current information output.
+
+      std::vector<AllocationProfile> alloc_profiles;
+      GenerateTopLeakingAllocationProfiles(time_values, map_allocations,
+                                           &alloc_profiles);
+
+      if (alloc_profiles.empty()) {
+        params->logger()->Output(
+            "MemoryTrackerLeakFinder: alloc_profiles was "
+            "empty and nothing is written.");
+      } else {
+        if (alloc_profiles.size() > 20) {
+          alloc_profiles.resize(20);
+        }
+
+        std::string csv_str = GenerateCSV(time_values, alloc_profiles);
+        params->logger()->Output(csv_str.c_str());
+      }
+    }
+
+    // Compress the buffer, and modify sample_time so that it's twice as slow.
+    if (time_values.size() >= kMaxSamples) {
+      sample_time *= 2;  // Double the time that it takes to trigger a sample.
+      // Remove every other element in time_values.
+      RemoveOddElements(&time_values);
+
+      // Remove every other element in the samples.
+      for (size_t i = 0; i < samples.size(); ++i) {
+        std::pair<const std::string*, AllocRec> sample = samples[i];
+        std::vector<AllocRec>& sample_history = map_allocations[sample.first];
+        RemoveOddElements(&sample_history);
+      }
+    }
+  }
+}
+
+const std::string* LeakFinderTool::GetOrCreateSymbol(
+    const nb::analytics::CallStack& callstack) {
+  const std::string* symbol_str = NULL;
+
+  // In javascript mode we try and get the javascript symbol. Otherwise
+  // fallback to C++ symbol.
+  if (stack_trace_mode_ == kJavascript) {
+    symbol_str = TryGetJavascriptSymbol();
+    if (symbol_str) {
+      return symbol_str;
+    }
+  }
+
+  symbol_str = GetOrCreateCplusPlusSymbol(callstack);
+  return symbol_str;
+}
+
+const std::string* LeakFinderTool::GetOrCreateCplusPlusSymbol(
+    const nb::analytics::CallStack& callstack) {
+  if (callstack.empty()) {
+    return default_callframe_str_;
+  } else {
+    const NbMemoryScopeInfo* memory_scope = callstack.back();
+
+    const bool skip =
+        SbStringFindString(memory_scope->function_name_, "js_malloc") ||
+        SbStringFindString(memory_scope->function_name_, "js_realloc") ||
+        SbStringFindString(memory_scope->function_name_, "new_");
+
+    // Skip up one callstack because we don't want to track calls to
+    // allocation functions.
+    if (skip && callstack.size() > 1) {
+      memory_scope = callstack[callstack.size() - 2];
+    }
+
+    const char* file_name = BaseNameFast(memory_scope->file_name_);
+
+    // Generates a symbol.
+    // Example:
+    //   "Javascript:Interpreter.cpp(415):RunScript()"
+    char symbol_buff[128];
+    SbStringFormatF(symbol_buff, sizeof(symbol_buff), "%s:%s(%d)::%s()",
+                    memory_scope->memory_scope_name_, file_name,
+                    memory_scope->line_number_, memory_scope->function_name_);
+
+    // Get's a unique pointer to a string containing the symbol. If the symbol
+    // was previously generated then the previous symbol is returned.
+    return &string_pool_.Intern(symbol_buff);
+  }
+}
+
+const std::string* LeakFinderTool::TryGetJavascriptSymbol() {
+  // TODO: Actually get and use a stack trace here.
+  return NULL;
+}
+
+void LeakFinderTool::SampleSnapshot(
+    std::vector<std::pair<const std::string*, AllocRec> >* destination) {
+  destination->erase(destination->begin(), destination->end());
+
+  const size_t sample_size = frame_map_.GetSize();
+
+  // Do this locally.
+  destination->reserve(sample_size + 10);
+  frame_map_.CopyToStdVector(destination);
+}
+
+std::string LeakFinderTool::GenerateCSV(
+    const std::vector<base::TimeDelta>& time_values,
+    const std::vector<AllocationProfile>& data) {
+  std::stringstream ss;
+  ss << std::fixed;  // Turn off scientific notation for CSV values.
+  ss << std::setprecision(3);
+  ss << kNewLine << kNewLine;
+
+  // HEADER
+  ss << "// Allocation in megabytes." << kNewLine;
+  ss << kQuote << "Time(min)" << kQuote << kDelimiter;
+  for (size_t i = 0; i < data.size(); ++i) {
+    const AllocationProfile& alloc_profile = data[i];
+    const std::string& name = *alloc_profile.name_;
+    ss << kQuote << name << kQuote << kDelimiter;
+  }
+  ss << kNewLine;
+
+  // BODY
+  for (size_t i = 0; i < time_values.size(); ++i) {
+    for (size_t j = 0; j < data.size(); ++j) {
+      if (j == 0) {
+        double mins = time_values[i].InSecondsF() / 60.f;
+        if (mins < .001) {
+          mins = 0;
+        }
+        ss << mins << kDelimiter;
+      }
+
+      const AllocationProfile& alloc_profile = data[j];
+      const std::vector<AllocRec>& alloc_history =
+          *alloc_profile.alloc_history_;
+
+      double megabytes = static_cast<double>(alloc_history[i].total_bytes) /
+                         static_cast<double>(1024 * 1024);
+
+      DCHECK_EQ(alloc_history.size(), time_values.size());
+      ss << megabytes << kDelimiter;
+    }
+    ss << kNewLine;
+  }
+  ss << kNewLine << kNewLine;
+  ss << "// Object counts." << kNewLine;
+  ss << kQuote << "Time(min)" << kQuote << kDelimiter;
+  for (size_t i = 0; i < data.size(); ++i) {
+    const AllocationProfile& alloc_profile = data[i];
+    const std::string& name = *alloc_profile.name_;
+    ss << kQuote << name << kQuote << kDelimiter;
+  }
+  ss << kNewLine;
+  for (size_t i = 0; i < time_values.size(); ++i) {
+    for (size_t j = 0; j < data.size(); ++j) {
+      if (j == 0) {
+        double mins = time_values[i].InSecondsF() / 60.f;
+        if (mins < .001) {
+          mins = 0;
+        }
+        ss << mins << kDelimiter;
+      }
+      const AllocationProfile& alloc_profile = data[j];
+      const std::vector<AllocRec>& alloc_history =
+          *alloc_profile.alloc_history_;
+      DCHECK_EQ(alloc_history.size(), time_values.size());
+      ss << alloc_history[i].num_allocs << kDelimiter;
+    }
+    ss << kNewLine;
+  }
+
+  ss << kNewLine << kNewLine;
+  return ss.str();
+}
+
+void LeakFinderTool::GenerateTopLeakingAllocationProfiles(
+    const std::vector<base::TimeDelta>& time_values, const MapSamples& samples,
+    std::vector<AllocationProfile>* destination) {
+  // GENERATE LINEAR REGRESSION LINE
+  // first value is time in microseconds.
+  // second value is total_bytes.
+  std::vector<std::pair<int64_t, int64_t> > sample_data;
+
+  typedef std::map<const std::string*, SlopeYIntercept> LinearRegressionMap;
+  LinearRegressionMap linear_regression_map;
+
+  std::vector<AllocationProfile> allocation_profiles;
+
+  for (MapSamples::const_iterator it = samples.begin(); it != samples.end();
+       ++it) {
+    const std::string* allocation_name = it->first;
+    const std::vector<AllocRec>& allocation_samples = it->second;
+
+    if (allocation_samples.empty()) {
+      continue;
+    }
+
+    // Filter out allocations records that have low number of allocations.
+    if (allocation_samples.back().num_allocs < 10) {
+      continue;
+    }
+
+    // Filter out allocations that are insignificant.
+    int64_t largest_allocation_sample = 0;
+    const int64_t kMinAllocationSize = 1024 * 64;
+    for (size_t i = 0; i < allocation_samples.size(); ++i) {
+      int64_t bytes = allocation_samples[i].total_bytes;
+      largest_allocation_sample = std::max(largest_allocation_sample, bytes);
+    }
+    if (largest_allocation_sample < kMinAllocationSize) {
+      continue;
+    }
+
+    // Filter out allocations where there is no growth between first quartile
+    // and final output.
+    const AllocRec& first_quartile_sample =
+        allocation_samples[allocation_samples.size() / 4];
+    const AllocRec& final_sample = allocation_samples.back();
+
+    const double increase_ratio =
+        static_cast<double>(final_sample.total_bytes) /
+        static_cast<double>(first_quartile_sample.total_bytes);
+
+    // 5% threshold of increase to be qualified as a lead.
+    static const double kMinIncreaseThreshold = .05;
+
+    // If the increase between first quartile and final sample less than 5%
+    // then skip.
+    if (increase_ratio < kMinIncreaseThreshold) {
+      continue;
+    }
+
+    sample_data.clear();  // Recycle.
+    for (size_t i = 0; i < time_values.size(); ++i) {
+      std::pair<int64_t, int64_t> datum(time_values[i].InMicroseconds(),
+                                        allocation_samples[i].total_bytes);
+      sample_data.push_back(datum);
+    }
+
+    double slope = 0;
+    double y_intercept = 0;
+    bool valid = GetLinearFit(sample_data.begin(), sample_data.end(), &slope,
+                              &y_intercept);
+    DCHECK(valid);
+    linear_regression_map[allocation_name] =
+        SlopeYIntercept(slope, y_intercept);
+    AllocationProfile alloc_profile(allocation_name, &allocation_samples, slope,
+                                    y_intercept);
+    alloc_profile.leak_potential_ =
+        allocation_samples.back().total_bytes * slope;
+    allocation_profiles.push_back(alloc_profile);
+  }
+
+  std::sort(allocation_profiles.begin(), allocation_profiles.end(),
+            AllocationProfile::CompareLeakPotential);
+  // Biggest one first.
+  std::reverse(allocation_profiles.begin(), allocation_profiles.end());
+  *destination = allocation_profiles;
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.h b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.h
new file mode 100644
index 0000000..170ddfd
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.h
@@ -0,0 +1,162 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_LEAK_FINDER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_LEAK_FINDER_TOOL_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/concurrent_map.h"
+#include "nb/hash.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Records allocations and outputs leaks as a CSV. Each column will
+// have an associated symbol.
+class LeakFinderTool : public AbstractTool,
+                       public nb::analytics::MemoryTrackerDebugCallback {
+ public:
+  enum StackTraceMode {
+    // Always get the C++ version of the stack trace.
+    kCPlusPlus,
+    // Whenever possible, get the javascript stack trace,
+    // else fallback to CPlusPlus.
+    kJavascript,
+  };
+
+  explicit LeakFinderTool(StackTraceMode pref);
+  virtual ~LeakFinderTool();
+
+  // OnMemoryAllocation() and OnMemoryDeallocation() are part of
+  // class MemoryTrackerDebugCallback.
+  void OnMemoryAllocation(const void* memory_block,
+                          const nb::analytics::AllocationRecord& record,
+                          const nb::analytics::CallStack& callstack) OVERRIDE;
+
+  void OnMemoryDeallocation(const void* memory_block,
+                            const nb::analytics::AllocationRecord& record,
+                            const nb::analytics::CallStack& callstack) OVERRIDE;
+
+  // Interface AbstractMemoryTrackerTool
+  virtual std::string tool_name() const OVERRIDE;
+  virtual void Run(Params* params) OVERRIDE;
+
+  const std::string* GetOrCreateSymbol(
+      const nb::analytics::CallStack& callstack);
+
+  const std::string* TryGetJavascriptSymbol();
+  const std::string* GetOrCreateCplusPlusSymbol(
+      const nb::analytics::CallStack& callstack);
+
+ private:
+  struct AllocRec;
+  // A map from callsite -> allocation size.
+  // typedef std::map<const std::string*, AllocRec> AllocationFrameMap;
+  typedef nb::ConcurrentMap<const std::string*, AllocRec,
+                            nb::PODHasher<const std::string*> >
+      AllocationFrameMap;
+
+  // A map of memory -> callsite.
+  // This keeps track of what callsites belong to which allocations.
+  // typedef std::map<const void*, const std::string*> CallFrameMap;
+  //
+  typedef nb::ConcurrentMap<const void*, const std::string*,
+                            nb::PODHasher<const std::string*> > CallFrameMap;
+
+  // A map from callsite -> allocation data.
+  typedef std::map<const std::string*, std::vector<AllocRec> > MapSamples;
+  // A value type for holding a slope and Y-intercept.
+  typedef std::pair<double, double> SlopeYIntercept;
+  // An AllocationRecord.
+  struct AllocRec {
+    AllocRec() : total_bytes(0), num_allocs(0) {}
+    AllocRec(const AllocRec& other)
+        : total_bytes(other.total_bytes), num_allocs(other.num_allocs) {}
+    AllocRec& operator=(const AllocRec& other) {
+      total_bytes = other.total_bytes;
+      num_allocs = other.num_allocs;
+      return *this;
+    }
+    int64_t total_bytes;
+    int32_t num_allocs;
+  };
+
+  // This data structure is used to for sorting leaks. The important variable
+  // is |leak_potential|, which represents how likely this AllocationProfile
+  // is a leak.
+  struct AllocationProfile {
+    AllocationProfile()
+        : name_(NULL),
+          alloc_history_(NULL),
+          slope_(0),
+          y_intercept_(0),
+          leak_potential_(0) {}
+
+    AllocationProfile(const std::string* name,
+                      const std::vector<AllocRec>* alloc_history, double slope,
+                      double y_intercept)
+        : name_(name),
+          alloc_history_(alloc_history),
+          slope_(slope),
+          y_intercept_(y_intercept),
+          leak_potential_(0) {}
+    const std::string* name_;
+    const std::vector<AllocRec>* alloc_history_;
+    // Linear regression data.
+    double slope_;
+    double y_intercept_;
+
+    // This this value is set externally. Higher values indicate higher chance
+    // that this is a leak.
+    double leak_potential_;
+
+    static bool CompareLeakPotential(const AllocationProfile& a,
+                                     const AllocationProfile& b) {
+      return a.leak_potential_ < b.leak_potential_;
+    }
+  };
+
+  static std::string GenerateCSV(
+      const std::vector<base::TimeDelta>& time_values,
+      const std::vector<AllocationProfile>& data);
+
+  void SampleSnapshot(
+      std::vector<std::pair<const std::string*, AllocRec> >* destination);
+
+  static void GenerateTopLeakingAllocationProfiles(
+      const std::vector<base::TimeDelta>& time_values,
+      const MapSamples& samples, std::vector<AllocationProfile>* destination);
+
+  static bool IsJavascriptScope(const nb::analytics::CallStack& callstack);
+
+  const std::string* default_callframe_str_;
+  nb::ConcurrentStringInterner string_pool_;
+  AllocationFrameMap frame_map_;
+  CallFrameMap callframe_map_;
+  StackTraceMode stack_trace_mode_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_LEAK_FINDER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
new file mode 100644
index 0000000..65ad229
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
@@ -0,0 +1,168 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/log_writer_tool.h"
+
+#include <algorithm>
+
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "starboard/string.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+LogWriterTool::LogWriterTool() : start_time_(NowTime()) {
+  buffered_file_writer_.reset(new BufferedFileWriter(MemoryLogPath()));
+  InitAndRegisterMemoryReporter();
+}
+
+LogWriterTool::~LogWriterTool() {
+  // No locks are used for the thread reporter, so when it's set to null
+  // we allow one second for any suspended threads to run through and finish
+  // their reporting.
+  SbMemorySetReporter(NULL);
+  SbThreadSleep(kSbTimeSecond);
+  buffered_file_writer_.reset(NULL);
+}
+
+std::string LogWriterTool::tool_name() const {
+  return "MemoryTrackerLogWriter";
+}
+
+void LogWriterTool::Run(Params* params) {
+  // Run function does almost nothing.
+  params->logger()->Output("MemoryTrackerLogWriter running...");
+}
+
+void LogWriterTool::OnMemoryAllocation(const void* memory_block, size_t size) {
+  void* addresses[kMaxStackSize] = {};
+  // Though the SbSystemGetStack API documentation does not specify any possible
+  // negative return values, we take no chance.
+  const size_t count = std::max(SbSystemGetStack(addresses, kMaxStackSize), 0);
+
+  const size_t n = 256;
+  char buff[n] = {0};
+  size_t buff_pos = 0;
+
+  int time_since_start_ms = GetTimeSinceStartMs();
+  // Writes "+ <ALLOCATION ADDRESS> <size> <time>"
+  int bytes_written =
+      SbStringFormatF(buff, sizeof(buff), "+ %" PRIXPTR " %x %d",
+                      reinterpret_cast<uintptr_t>(memory_block),
+                      static_cast<unsigned int>(size), time_since_start_ms);
+
+  buff_pos += bytes_written;
+  const size_t end_index = std::min(count, kStartIndex + kNumAddressPrints);
+
+  // For each of the stack addresses that we care about, concat them to the
+  // buffer. This was originally written to do multiple stack addresses but
+  // this tends to overflow on some lower platforms so it's possible that
+  // this loop only iterates once.
+  for (size_t i = kStartIndex; i < end_index; ++i) {
+    void* p = addresses[i];
+    int bytes_written =
+        SbStringFormatF(buff + buff_pos, sizeof(buff) - buff_pos,
+                        " %" PRIXPTR "", reinterpret_cast<uintptr_t>(p));
+    DCHECK_GE(bytes_written, 0);
+
+    if (bytes_written < 0) {
+      DCHECK(false) << "Error occurred while writing string.";
+      continue;
+    }
+
+    buff_pos += static_cast<size_t>(bytes_written);
+  }
+  // Adds a "\n" at the end.
+  SbStringConcat(buff + buff_pos, "\n", static_cast<int>(n - buff_pos));
+  buffered_file_writer_->Append(buff, strlen(buff));
+}
+
+void LogWriterTool::OnMemoryDeallocation(const void* memory_block) {
+  const size_t n = 256;
+  char buff[n] = {0};
+  // Writes "- <ADDRESS OF ALLOCATION> \n"
+  SbStringFormatF(buff, sizeof(buff), "- %" PRIXPTR "\n",
+                  reinterpret_cast<uintptr_t>(memory_block));
+  buffered_file_writer_->Append(buff, strlen(buff));
+}
+
+void LogWriterTool::OnAlloc(void* context, const void* memory, size_t size) {
+  LogWriterTool* self = static_cast<LogWriterTool*>(context);
+  self->OnMemoryAllocation(memory, size);
+}
+
+void LogWriterTool::OnDealloc(void* context, const void* memory) {
+  LogWriterTool* self = static_cast<LogWriterTool*>(context);
+  self->OnMemoryDeallocation(memory);
+}
+
+void LogWriterTool::OnMapMemory(void* context, const void* memory,
+                                size_t size) {
+  LogWriterTool* self = static_cast<LogWriterTool*>(context);
+  self->OnMemoryAllocation(memory, size);
+}
+
+void LogWriterTool::OnUnMapMemory(void* context, const void* memory,
+                                  size_t size) {
+  SB_UNREFERENCED_PARAMETER(size);
+  LogWriterTool* self = static_cast<LogWriterTool*>(context);
+  self->OnMemoryDeallocation(memory);
+}
+
+std::string LogWriterTool::MemoryLogPath() {
+  char file_name_buff[2048] = {};
+  SbSystemGetPath(kSbSystemPathDebugOutputDirectory, file_name_buff,
+                  arraysize(file_name_buff));
+  std::string path(file_name_buff);
+  if (!path.empty()) {  // Protect against a dangling "/" at end.
+    const int back_idx_signed = static_cast<int>(path.length()) - 1;
+    if (back_idx_signed >= 0) {
+      const size_t idx = back_idx_signed;
+      if (path[idx] == '/') {
+        path.erase(idx);
+      }
+    }
+  }
+  path.append("/memory_log.txt");
+  return path;
+}
+
+base::TimeTicks LogWriterTool::NowTime() {
+  // NowFromSystemTime() is slower but more accurate. However it might
+  // be useful to use the faster but less accurate version if there is
+  // a speedup.
+  return base::TimeTicks::Now();
+}
+
+int LogWriterTool::GetTimeSinceStartMs() const {
+  base::TimeDelta dt = NowTime() - start_time_;
+  return static_cast<int>(dt.InMilliseconds());
+}
+
+void LogWriterTool::InitAndRegisterMemoryReporter() {
+  DCHECK(!memory_reporter_.get()) << "Memory Reporter already registered.";
+
+  SbMemoryReporter mem_reporter = {OnAlloc, OnDealloc, OnMapMemory,
+                                   OnUnMapMemory, this};
+  memory_reporter_.reset(new SbMemoryReporter(mem_reporter));
+  SbMemorySetReporter(memory_reporter_.get());
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/log_writer_tool.h b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.h
new file mode 100644
index 0000000..c22da65
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.h
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_LOG_WRITER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_LOG_WRITER_TOOL_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "starboard/memory_reporter.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class BufferedFileWriter;
+
+// Outputs memory_log.txt to the output log location. This log contains
+// allocations with a stack trace (non-symbolized) and deallocations without
+// a stack.
+class LogWriterTool : public AbstractTool {
+ public:
+  LogWriterTool();
+  virtual ~LogWriterTool();
+
+  // Interface AbstrctMemoryTrackerTool
+  virtual std::string tool_name() const OVERRIDE;
+  virtual void Run(Params* params) OVERRIDE;
+
+  void OnMemoryAllocation(const void* memory_block, size_t size);
+  void OnMemoryDeallocation(const void* memory_block);
+
+ private:
+  // Callbacks for MemoryReporter
+  static void OnAlloc(void* context, const void* memory, size_t size);
+  static void OnDealloc(void* context, const void* memory);
+  static void OnMapMemory(void* context, const void* memory, size_t size);
+  static void OnUnMapMemory(void* context, const void* memory, size_t size);
+  static std::string MemoryLogPath();
+
+  static base::TimeTicks NowTime();
+  static const size_t kStartIndex = 5;
+  static const size_t kNumAddressPrints = 1;
+  static const size_t kMaxStackSize = 10;
+
+  int GetTimeSinceStartMs() const;
+  void InitAndRegisterMemoryReporter();
+
+  base::TimeTicks start_time_;
+  scoped_ptr<SbMemoryReporter> memory_reporter_;
+  scoped_ptr<BufferedFileWriter> buffered_file_writer_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_LOG_WRITER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/params.cc b/src/cobalt/browser/memory_tracker/tool/params.cc
new file mode 100644
index 0000000..42170fb
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/params.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/params.h"
+
+#include <sstream>
+
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+Params::Params(nb::analytics::MemoryTracker* memory_tracker,
+               AbstractLogger* logger, base::Time start_time)
+    : memory_tracker_(memory_tracker),
+      finished_(false),
+      logger_(logger),
+      timer_(start_time) {}
+
+bool Params::finished() const { return finished_; }
+
+bool Params::wait_for_finish_signal(SbTime wait_us) {
+  return finished_semaphore_.TakeWait(wait_us);
+}
+
+void Params::set_finished(bool val) {
+  finished_ = val;
+  finished_semaphore_.Put();
+}
+
+nb::analytics::MemoryTracker* Params::memory_tracker() const {
+  return memory_tracker_;
+}
+
+cobalt::browser::memory_tracker::AbstractLogger* Params::logger() {
+  return logger_.get();
+}
+
+base::TimeDelta Params::time_since_start() const {
+  return base::Time::NowFromSystemTime() - timer_;
+}
+
+std::string Params::TimeInMinutesString() const {
+  base::TimeDelta delta_t = time_since_start();
+  int64 seconds = delta_t.InSeconds();
+  float time_mins = static_cast<float>(seconds) / 60.f;
+  std::stringstream ss;
+
+  ss << time_mins;
+  return ss.str();
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/params.h b/src/cobalt/browser/memory_tracker/tool/params.h
new file mode 100644
index 0000000..516d7ee
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/params.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_PARAMS_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_PARAMS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
+#include "nb/analytics/memory_tracker.h"
+#include "starboard/common/semaphore.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class AbstractLogger;
+
+class Params {
+ public:
+  Params(nb::analytics::MemoryTracker* memory_tracker, AbstractLogger* logger,
+         base::Time start_time);
+  bool finished() const;
+  bool wait_for_finish_signal(SbTime wait_us);
+  void set_finished(bool val);
+
+  nb::analytics::MemoryTracker* memory_tracker() const;
+  AbstractLogger* logger();
+  base::TimeDelta time_since_start() const;
+  std::string TimeInMinutesString() const;
+
+ private:
+  nb::analytics::MemoryTracker* memory_tracker_;
+  bool finished_;
+  scoped_ptr<AbstractLogger> logger_;
+  base::Time timer_;
+  starboard::Semaphore finished_semaphore_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_PARAMS_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
new file mode 100644
index 0000000..3f32416
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
@@ -0,0 +1,678 @@
+// 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/browser/memory_tracker/tool/tool_impl.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iomanip>
+#include <iterator>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/time.h"
+#include "cobalt/base/c_val_collection_entry_stats.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_thread.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "cobalt/script/mozjs/util/stack_trace_helpers.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "nb/concurrent_map.h"
+#include "nb/memory_scope.h"
+#include "starboard/common/semaphore.h"
+#include "starboard/configuration.h"
+#include "starboard/file.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+using nb::analytics::AllocationGroup;
+using nb::analytics::AllocationRecord;
+using nb::analytics::AllocationVisitor;
+using nb::analytics::GetProcessMemoryStats;
+using nb::analytics::MemoryStats;
+using nb::analytics::MemoryTracker;
+
+class PrintTool::CvalsMap {
+ public:
+  typedef base::CVal<base::cval::SizeInBytes> CValType;
+
+  ~CvalsMap() {
+    while (!map_.empty()) {
+      MapCvals::iterator it = map_.begin();
+      delete it->second;
+      map_.erase(it);
+    }
+  }
+
+  void Update(const std::string& name, size_t value) {
+    std::string cval_name = GetCvalName(name);
+    CValType*& val = map_[cval_name];
+    if (!val) {
+      // Create if not found.
+      val = new CValType(cval_name, 0,
+                         "Automatically generated by the "
+                         "browser::memory_tracker system.");
+    }
+    (*val) = value;
+  }
+
+  static std::string GetCvalName(const std::string& name) {
+    std::stringstream ss;
+    ss << "Memory.Scope." << name;
+    return ss.str();
+  }
+
+ private:
+  typedef std::map<std::string, CValType*> MapCvals;
+  MapCvals map_;
+};
+
+PrintTool::PrintTool() : cvals_map_(new CvalsMap) {}
+
+PrintTool::~PrintTool() {}
+
+void PrintTool::Run(Params* params) {
+  const std::string kSeperator =
+      "--------------------------------------------------";
+
+  while (!params->finished()) {
+    std::vector<const AllocationGroup*> vector_output;
+    params->memory_tracker()->GetAllocationGroups(&vector_output);
+
+    typedef std::map<std::string, const AllocationGroup*> Map;
+    typedef Map::const_iterator MapIt;
+
+    Map output;
+    for (size_t i = 0; i < vector_output.size(); ++i) {
+      const AllocationGroup* group = vector_output[i];
+      output[group->name()] = group;
+    }
+
+    int32 num_allocs = 0;
+    int64 total_bytes = 0;
+
+    struct F {
+      static void PrintRow(std::stringstream* ss, const std::string& v1,
+                           const std::string& v2, const std::string& v3) {
+        ss->width(25);
+        *ss << std::left << v1;
+        ss->width(13);
+        *ss << std::right << v2 << "  ";
+        ss->width(10);
+        *ss << std::right << v3 << "\n";
+      }
+    };
+
+    if (params->memory_tracker()->IsMemoryTrackingEnabled()) {
+      // If this isn't true then it would cause an infinite loop. The
+      // following will likely crash.
+      SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
+    }
+
+    std::stringstream ss;
+
+    ss << kNewLine;
+    ss << "TimeNow " << params->TimeInMinutesString()
+       << " (minutes):" << kNewLine << kNewLine;
+
+    ss << kSeperator << kNewLine;
+    MemoryStats memstats = GetProcessMemoryStats();
+
+    F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", "");
+    ss << kSeperator << kNewLine;
+    F::PrintRow(&ss, "Total CPU Reserved",
+                NumberFormatWithCommas(memstats.total_cpu_memory), "");
+
+    F::PrintRow(&ss, "Total CPU Used",
+                NumberFormatWithCommas(memstats.used_cpu_memory), "");
+
+    F::PrintRow(&ss, "Total GPU Reserved",
+                NumberFormatWithCommas(memstats.total_gpu_memory), "");
+
+    F::PrintRow(&ss, "Total GPU Used",
+                NumberFormatWithCommas(memstats.used_gpu_memory), "");
+
+    ss << kSeperator << kNewLine << kNewLine;
+
+    ss << kSeperator << kNewLine;
+    F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS");
+    ss << kSeperator << kNewLine;
+
+    for (MapIt it = output.begin(); it != output.end(); ++it) {
+      const AllocationGroup* group = it->second;
+      if (!group) {
+        continue;
+      }
+
+      int32 num_group_allocs = -1;
+      int64 total_group_bytes = -1;
+
+      group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
+      SB_DCHECK(-1 != num_group_allocs);
+      SB_DCHECK(-1 != total_group_bytes);
+
+      cvals_map_->Update(group->name(), static_cast<size_t>(total_group_bytes));
+
+      num_allocs += num_group_allocs;
+      total_bytes += total_group_bytes;
+
+      F::PrintRow(&ss, it->first, NumberFormatWithCommas(total_group_bytes),
+                  NumberFormatWithCommas(num_group_allocs));
+    }
+
+    cvals_map_->Update("Total", static_cast<size_t>(total_bytes));
+
+    ss << kNewLine;
+
+    F::PrintRow(&ss, "Total (in groups above)",
+                NumberFormatWithCommas(total_bytes),
+                NumberFormatWithCommas(num_allocs));
+
+    ss << kSeperator << kNewLine;
+    ss << kNewLine << kNewLine;
+
+    params->logger()->Output(ss.str().c_str());
+    // Output once every 5 seconds.
+    base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
+  }
+}
+
+PrintCSVTool::PrintCSVTool(int sampling_interval_ms, int sampling_time_ms)
+    : sample_interval_ms_(sampling_interval_ms),
+      sampling_time_ms_(sampling_time_ms) {}
+
+std::string PrintCSVTool::ToCsvString(const MapAllocationSamples& samples_in) {
+  typedef MapAllocationSamples Map;
+  typedef Map::const_iterator MapIt;
+
+  size_t largest_sample_size = 0;
+  size_t smallest_sample_size = INT_MAX;
+
+  // Sanitize samples_in and store as samples.
+  MapAllocationSamples samples;
+  for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
+    std::string name = it->first;
+    const AllocationSamples& value = it->second;
+
+    if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
+      SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
+      return "ERROR";
+    }
+
+    const size_t n = value.allocated_bytes_.size();
+    if (n > largest_sample_size) {
+      largest_sample_size = n;
+    }
+    if (n < smallest_sample_size) {
+      smallest_sample_size = n;
+    }
+
+    const bool duplicate_found = (samples.end() != samples.find(name));
+    if (duplicate_found) {
+      SB_NOTREACHED() << "Error, duplicate found for entry: " << name
+                      << kNewLine;
+    }
+    // Store value as a sanitized sample.
+    samples[name] = value;
+  }
+
+  SB_DCHECK(largest_sample_size == smallest_sample_size);
+
+  std::stringstream ss;
+
+  // Begin output to CSV.
+  // Sometimes we need to skip the CPU memory entry.
+  const MapIt total_cpu_memory_it = samples.find(UntrackedMemoryKey());
+
+  // Preamble
+  ss << kNewLine << "//////////////////////////////////////////////";
+  ss << kNewLine << "// CSV of bytes / allocation" << kNewLine;
+  // HEADER.
+  ss << "Name" << kDelimiter << kQuote << "Bytes/Alloc" << kQuote << kNewLine;
+  // DATA.
+  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+    if (total_cpu_memory_it == it) {
+      continue;
+    }
+
+    const AllocationSamples& samples = it->second;
+    if (samples.allocated_bytes_.empty() ||
+        samples.number_allocations_.empty()) {
+      SB_NOTREACHED() << "Should not be here";
+      return "ERROR";
+    }
+    const int64 n_allocs = samples.number_allocations_.back();
+    const int64 n_bytes = samples.allocated_bytes_.back();
+    int64 bytes_per_alloc = 0;
+    if (n_allocs > 0) {
+      bytes_per_alloc = n_bytes / n_allocs;
+    }
+    const std::string& name = it->first;
+    ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter
+       << bytes_per_alloc << kNewLine;
+  }
+  ss << kNewLine;
+
+  // Preamble
+  ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
+     << "// CSV of bytes allocated per region (MB's)." << kNewLine
+     << "// Units are in Megabytes. This is designed" << kNewLine
+     << "// to be used in a stacked graph." << kNewLine;
+
+  // HEADER.
+  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+    if (total_cpu_memory_it == it) {
+      continue;
+    }
+    // Strip out any characters that could make parsing the csv difficult.
+    const std::string name = SanitizeCSVKey(it->first);
+    ss << kQuote << name << kQuote << kDelimiter;
+  }
+  // Save the total for last.
+  if (total_cpu_memory_it != samples.end()) {
+    const std::string& name = SanitizeCSVKey(total_cpu_memory_it->first);
+    ss << kQuote << name << kQuote << kDelimiter;
+  }
+  ss << kNewLine;
+
+  // Print out the values of each of the samples.
+  for (size_t i = 0; i < smallest_sample_size; ++i) {
+    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+      if (total_cpu_memory_it == it) {
+        continue;
+      }
+      const int64 alloc_bytes = it->second.allocated_bytes_[i];
+      // Convert to float megabytes with decimals of precision.
+      double n = alloc_bytes / (1000 * 10);
+      n = n / (100.);
+      ss << n << kDelimiter;
+    }
+    if (total_cpu_memory_it != samples.end()) {
+      const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i];
+      // Convert to float megabytes with decimals of precision.
+      double n = alloc_bytes / (1000 * 10);
+      n = n / (100.);
+      ss << n << kDelimiter;
+    }
+    ss << kNewLine;
+  }
+
+  ss << kNewLine;
+  // Preamble
+  ss << kNewLine << "//////////////////////////////////////////////";
+  ss << kNewLine << "// CSV of number of allocations per region." << kNewLine;
+
+  // HEADER
+  for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+    if (total_cpu_memory_it == it) {
+      continue;
+    }
+    const std::string& name = SanitizeCSVKey(it->first);
+    ss << kQuote << name << kQuote << kDelimiter;
+  }
+  ss << kNewLine;
+  for (size_t i = 0; i < smallest_sample_size; ++i) {
+    for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+      if (total_cpu_memory_it == it) {
+        continue;
+      }
+      const int64 n_allocs = it->second.number_allocations_[i];
+      ss << n_allocs << kDelimiter;
+    }
+    ss << kNewLine;
+  }
+  std::string output = ss.str();
+  return output;
+}
+
+const char* PrintCSVTool::UntrackedMemoryKey() { return "Untracked Memory"; }
+
+void PrintCSVTool::Run(Params* params) {
+  params->logger()->Output("\nMemoryTrackerPrintCSVThread is sampling...\n");
+  int sample_count = 0;
+  MapAllocationSamples map_samples;
+
+  while (!TimeExpiredYet(*params) && !params->finished()) {
+    // Sample total memory used by the system.
+    MemoryStats mem_stats = GetProcessMemoryStats();
+    int64 untracked_used_memory =
+        mem_stats.used_cpu_memory + mem_stats.used_gpu_memory;
+
+    std::vector<const AllocationGroup*> vector_output;
+    params->memory_tracker()->GetAllocationGroups(&vector_output);
+
+    // Sample all known memory scopes.
+    for (size_t i = 0; i < vector_output.size(); ++i) {
+      const AllocationGroup* group = vector_output[i];
+      const std::string& name = group->name();
+
+      const bool first_creation =
+          map_samples.find(group->name()) == map_samples.end();
+
+      AllocationSamples* new_entry = &(map_samples[name]);
+
+      // Didn't see it before so create new entry.
+      if (first_creation) {
+        // Make up for lost samples...
+        new_entry->allocated_bytes_.resize(sample_count, 0);
+        new_entry->number_allocations_.resize(sample_count, 0);
+      }
+
+      int32 num_allocs = -1;
+      int64 allocation_bytes = -1;
+      group->GetAggregateStats(&num_allocs, &allocation_bytes);
+
+      new_entry->allocated_bytes_.push_back(allocation_bytes);
+      new_entry->number_allocations_.push_back(num_allocs);
+
+      untracked_used_memory -= allocation_bytes;
+    }
+
+    // Now push in remaining total.
+    AllocationSamples* process_sample = &(map_samples[UntrackedMemoryKey()]);
+    if (untracked_used_memory < 0) {
+      // On some platforms, total GPU memory may not be correctly reported.
+      // However the allocations from the GPU memory may be reported. In this
+      // case untracked_used_memory will go negative. To protect the memory
+      // reporting the untracked_used_memory is set to 0 so that it doesn't
+      // cause an error in reporting.
+      untracked_used_memory = 0;
+    }
+    process_sample->allocated_bytes_.push_back(untracked_used_memory);
+    process_sample->number_allocations_.push_back(-1);
+
+    ++sample_count;
+    base::PlatformThread::Sleep(
+        base::TimeDelta::FromMilliseconds(sample_interval_ms_));
+  }
+
+  std::stringstream ss;
+  ss.precision(2);
+  ss << "Time now: " << params->TimeInMinutesString() << ",\n";
+  ss << ToCsvString(map_samples);
+  params->logger()->Output(ss.str().c_str());
+  params->logger()->Flush();
+  // Prevents the "thread exited code 0" from being interleaved into the
+  // output. This happens if flush is not implemented correctly in the system.
+  base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+}
+
+bool PrintCSVTool::TimeExpiredYet(const Params& params) {
+  base::TimeDelta dt = params.time_since_start();
+  int64 dt_ms = dt.InMilliseconds();
+  const bool expired_time = dt_ms > sampling_time_ms_;
+  return expired_time;
+}
+
+MemorySizeBinner::MemorySizeBinner(const std::string& memory_scope_name)
+    : memory_scope_name_(memory_scope_name) {}
+
+const AllocationGroup* FindAllocationGroup(const std::string& name,
+                                           MemoryTracker* memory_tracker) {
+  std::vector<const AllocationGroup*> groups;
+  memory_tracker->GetAllocationGroups(&groups);
+  // Find by exact string match.
+  for (size_t i = 0; i < groups.size(); ++i) {
+    const AllocationGroup* group = groups[i];
+    if (group->name().compare(name) == 0) {
+      return group;
+    }
+  }
+  return NULL;
+}
+
+void MemorySizeBinner::Run(Params* params) {
+  const AllocationGroup* target_group = NULL;
+
+  while (!params->finished()) {
+    if (target_group == NULL && !memory_scope_name_.empty()) {
+      target_group =
+          FindAllocationGroup(memory_scope_name_, params->memory_tracker());
+    }
+
+    std::stringstream ss;
+    ss.precision(2);
+    if (target_group || memory_scope_name_.empty()) {
+      AllocationSizeBinner visitor_binner = AllocationSizeBinner(target_group);
+      params->memory_tracker()->Accept(&visitor_binner);
+
+      size_t min_size = 0;
+      size_t max_size = 0;
+
+      visitor_binner.GetLargestSizeRange(&min_size, &max_size);
+
+      FindTopSizes top_size_visitor =
+          FindTopSizes(min_size, max_size, target_group);
+      params->memory_tracker()->Accept(&top_size_visitor);
+
+      ss << kNewLine;
+      ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):";
+      ss << kNewLine;
+      if (!memory_scope_name_.empty()) {
+        ss << "Tracking Memory Scope \"" << memory_scope_name_ << "\", ";
+      } else {
+        ss << "Tracking whole program, ";
+      }
+      ss << "first row is allocation size range, second row is number of "
+         << kNewLine << "allocations in that range." << kNewLine;
+      ss << visitor_binner.ToCSVString();
+      ss << kNewLine;
+      ss << "Largest allocation range: \"" << min_size << "..." << max_size
+         << "\"" << kNewLine;
+      ss << "Printing out top allocations from this range: " << kNewLine;
+      ss << top_size_visitor.ToString(5) << kNewLine;
+    } else {
+      ss << "No allocations for \"" << memory_scope_name_ << "\".";
+    }
+
+    params->logger()->Output(ss.str().c_str());
+    params->logger()->Flush();
+
+    // Sleep until the next sample.
+    base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+  }
+}
+
+size_t AllocationSizeBinner::GetBucketIndexForAllocationSize(size_t size) {
+  for (int i = 0; i < 32; ++i) {
+    size_t val = 0x1 << i;
+    if (val > size) {
+      return i;
+    }
+  }
+  SB_NOTREACHED();
+  return 32;
+}
+
+void AllocationSizeBinner::GetSizeRange(size_t size, size_t* min_value,
+                                        size_t* max_value) {
+  size_t idx = GetBucketIndexForAllocationSize(size);
+  IndexToSizeRange(idx, min_value, max_value);
+}
+
+void AllocationSizeBinner::IndexToSizeRange(size_t idx, size_t* min_value,
+                                            size_t* max_value) {
+  if (idx == 0) {
+    *min_value = 0;
+    *max_value = 0;
+    return;
+  }
+  *min_value = 0x1 << (idx - 1);
+  *max_value = (*min_value << 1) - 1;
+  return;
+}
+
+size_t AllocationSizeBinner::GetIndexRepresentingMostMemoryConsumption() const {
+  int64 largest_allocation_total = 0;
+  size_t largest_allocation_total_idx = 0;
+
+  for (size_t i = 0; i < allocation_histogram_.size(); ++i) {
+    size_t alloc_size = 0x1 << i;
+    size_t count = allocation_histogram_[i];
+    int64 allocation_total =
+        static_cast<int64>(alloc_size) * static_cast<int64>(count);
+
+    if (largest_allocation_total < allocation_total) {
+      largest_allocation_total = allocation_total;
+      largest_allocation_total_idx = i;
+    }
+  }
+  return largest_allocation_total_idx;
+}
+
+void AllocationSizeBinner::GetLargestSizeRange(size_t* min_value,
+                                               size_t* max_value) const {
+  size_t index = GetIndexRepresentingMostMemoryConsumption();
+  IndexToSizeRange(index, min_value, max_value);
+}
+
+AllocationSizeBinner::AllocationSizeBinner(const AllocationGroup* group_filter)
+    : group_filter_(group_filter) {
+  allocation_histogram_.resize(33);
+}
+
+bool AllocationSizeBinner::PassesFilter(
+    const AllocationRecord& alloc_record) const {
+  if (group_filter_ == NULL) {
+    return true;
+  }
+
+  return alloc_record.allocation_group == group_filter_;
+}
+
+bool AllocationSizeBinner::Visit(const void* /*memory*/,
+                                 const AllocationRecord& alloc_record) {
+  if (PassesFilter(alloc_record)) {
+    const size_t idx = GetBucketIndexForAllocationSize(alloc_record.size);
+    allocation_histogram_[idx]++;
+  }
+  return true;
+}
+
+std::string AllocationSizeBinner::ToCSVString() const {
+  size_t first_idx = 0;
+  size_t end_idx = allocation_histogram_.size();
+
+  // Determine the start index by skipping all consecutive head entries
+  // that are 0.
+  while (first_idx < allocation_histogram_.size()) {
+    const size_t num_allocs = allocation_histogram_[first_idx];
+    if (num_allocs > 0) {
+      break;
+    }
+    first_idx++;
+  }
+
+  // Determine the end index by skipping all consecutive tail entries
+  // that are 0.
+  while (end_idx > 0) {
+    if (end_idx < allocation_histogram_.size()) {
+      const size_t num_allocs = allocation_histogram_[end_idx];
+      if (num_allocs > 0) {
+        ++end_idx;
+        break;
+      }
+    }
+    end_idx--;
+  }
+
+  std::stringstream ss;
+  for (size_t i = first_idx; i < end_idx; ++i) {
+    size_t min = 0;
+    size_t max = 0;
+    IndexToSizeRange(i, &min, &max);
+    std::stringstream name_ss;
+    name_ss << kQuote << min << "..." << max << kQuote;
+    ss << name_ss.str() << kDelimiter;
+  }
+  ss << kNewLine;
+
+  for (size_t i = first_idx; i < end_idx; ++i) {
+    const size_t num_allocs = allocation_histogram_[i];
+    ss << num_allocs << kDelimiter;
+  }
+  ss << kNewLine;
+  return ss.str();
+}
+
+FindTopSizes::FindTopSizes(size_t minimum_size, size_t maximum_size,
+                           const AllocationGroup* group)
+    : minimum_size_(minimum_size),
+      maximum_size_(maximum_size),
+      group_filter_(group) {}
+
+bool FindTopSizes::Visit(const void* /*memory*/,
+                         const AllocationRecord& alloc_record) {
+  if (PassesFilter(alloc_record)) {
+    size_counter_[alloc_record.size]++;
+  }
+  return true;
+}
+
+std::string FindTopSizes::ToString(size_t max_elements_to_print) const {
+  std::vector<GroupAllocation> group_allocs = GetTopAllocations();
+  const size_t n = std::min(max_elements_to_print, group_allocs.size());
+
+  if (!group_allocs.empty()) {
+    std::stringstream ss;
+
+    for (size_t i = 0; i < n; ++i) {
+      GroupAllocation g = group_allocs[i];
+      size_t total_size = g.allocation_count * g.allocation_size;
+      ss << "    " << total_size
+         << " bytes allocated with object size: " << g.allocation_size
+         << " bytes in " << g.allocation_count << " instances " << kNewLine;
+    }
+    return ss.str();
+  } else {
+    return std::string();
+  }
+}
+
+std::vector<FindTopSizes::GroupAllocation> FindTopSizes::GetTopAllocations()
+    const {
+  std::vector<GroupAllocation> group_allocs;
+  // Push objects to a vector.
+  for (SizeCounterMap::const_iterator it = size_counter_.begin();
+       it != size_counter_.end(); ++it) {
+    GroupAllocation alloc = {it->first, it->second};
+    group_allocs.push_back(alloc);
+  }
+
+  std::sort(group_allocs.begin(), group_allocs.end(),
+            GroupAllocation::LessAllocationSize);
+  // Biggest first.
+  std::reverse(group_allocs.begin(), group_allocs.end());
+  return group_allocs;
+}
+
+bool FindTopSizes::PassesFilter(const AllocationRecord& alloc_record) const {
+  if (alloc_record.size < minimum_size_) return false;
+  if (alloc_record.size > maximum_size_) return false;
+  if (!group_filter_) return true;  // No group filter when null.
+  return group_filter_ == alloc_record.allocation_group;
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.h b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
new file mode 100644
index 0000000..5141d6a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
@@ -0,0 +1,246 @@
+// 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_BROWSER_MEMORY_TRACKER_TOOL_TOOL_IMPL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_IMPL_H_
+
+#include <deque>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/debug/stack_trace.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/simple_thread.h"
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "nb/concurrent_map.h"
+#include "nb/string_interner.h"
+#include "nb/thread_local_object.h"
+#include "starboard/memory_reporter.h"
+
+namespace starboard {
+class ScopedFile;
+}  // namespace starboard
+
+namespace nb {
+namespace analytics {
+class AllocationGroup;
+class AllocationRecord;
+class MemoryTracker;
+}  // namespace analytics
+}  // namespace nb
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Interface for logging. This allows dependency inject to override in the case
+// of future tests.
+class AbstractLogger {
+ public:
+  virtual ~AbstractLogger() {}
+  virtual void Output(const char* str) = 0;
+  virtual void Flush() = 0;
+};
+
+// Params holds important data needed by the MemoryTracker tools during
+// their run cycle.
+class Params;
+
+// Base class for all tools.
+class AbstractTool {
+ public:
+  virtual ~AbstractTool() {}
+  virtual std::string tool_name() const = 0;
+  virtual void Run(Params* params) = 0;
+};
+
+// Start() is called when this object is created, and Cancel() & Join() are
+// called during destruction.
+class PrintTool : public AbstractTool {
+ public:
+  PrintTool();
+  ~PrintTool() OVERRIDE;
+
+  // Overridden so that the thread can exit gracefully.
+  virtual void Run(Params* params) OVERRIDE;
+  virtual std::string tool_name() const OVERRIDE {
+    return "MemoryTrackerPrintThread";
+  }
+
+ private:
+  class CvalsMap;
+  scoped_ptr<CvalsMap> cvals_map_;
+};
+
+// Generates CSV values of the engine.
+// There are three sections of data including:
+//   1. average bytes / alloc
+//   2. # Bytes allocated per memory scope.
+//   3. # Allocations per memory scope.
+// This data can be pasted directly into a Google spreadsheet and visualized.
+// Note that this thread will implicitly call Start() is called during
+// construction and Cancel() & Join() during destruction.
+class PrintCSVTool : public AbstractTool {
+ public:
+  // This tool will only produce on CSV dump of the engine. This is useful
+  // for profiling startup memory consumption.
+  PrintCSVTool(int sampling_interval_ms, int sampling_time_ms);
+
+  // Overridden so that the thread can exit gracefully.
+  virtual void Run(Params* params) OVERRIDE;
+  virtual std::string tool_name() const OVERRIDE {
+    return "MemoryTrackerPrintCSV";
+  }
+
+ private:
+  struct AllocationSamples {
+    std::vector<int32_t> number_allocations_;
+    std::vector<int64_t> allocated_bytes_;
+  };
+  typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
+  static std::string ToCsvString(const MapAllocationSamples& samples);
+  static const char* UntrackedMemoryKey();
+  bool TimeExpiredYet(const Params& params);
+
+  const int sample_interval_ms_;
+  const int sampling_time_ms_;
+};
+
+struct AllocationSamples {
+  std::vector<int32_t> number_allocations_;
+  std::vector<int64_t> allocated_bytes_;
+};
+typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
+typedef std::vector<base::TimeDelta> TimeStamps;
+
+struct TimeSeries {
+  MapAllocationSamples samples_;
+  TimeStamps time_stamps_;
+};
+
+// This tool inspects a memory scope and reports on the memory usage.
+// The output will be a CSV file printed to stdout representing
+// the number of memory allocations for objects. The objects are binned
+// according to the size of the memory allocation. Objects within the same
+// power of two are binned together. For example 1024 will be binned with 1025.
+class MemorySizeBinner : public AbstractTool {
+ public:
+  // memory_scope_name represents the memory scope that is to be investigated.
+  explicit MemorySizeBinner(const std::string& memory_scope_name);
+
+  virtual void Run(Params* params) OVERRIDE;
+  virtual std::string tool_name() const OVERRIDE {
+    return "MemoryTrackerCompressedTimeSeries";
+  }
+
+ private:
+  std::string memory_scope_name_;
+};
+
+// Bins the size according from all the encountered allocations.
+// If AllocationGroup* is non-null, then this is used as a filter such that
+// ONLY allocations belonging to that AllocationGroup are considered.
+class AllocationSizeBinner : public nb::analytics::AllocationVisitor {
+ public:
+  typedef std::vector<int> AllocationHistogram;
+  // Maps the input size to a bin number. This function performs a 2^n
+  // mapping. Here is a table of some size mappings:
+  // Example:
+  //   GetIndex(0) == 0;
+  //   GetIndex(1) == 1;
+  //   GetIndex(2) == 2;
+  //   GetIndex(3) == 2;
+  //   GetIndex(4) == 3;
+  //   ...
+  //   GetIndex(15) == 4;
+  //   GetIndex(16) == 5;
+  static size_t GetBucketIndexForAllocationSize(size_t size);
+  static void GetSizeRange(size_t size, size_t* min_value, size_t* max_value);
+  static void IndexToSizeRange(size_t size, size_t* min_value,
+                               size_t* max_value);
+
+  explicit AllocationSizeBinner(
+      const nb::analytics::AllocationGroup* group_filter);
+  virtual bool Visit(
+      const void* memory,
+      const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
+
+  size_t GetIndexRepresentingMostMemoryConsumption() const;
+  void GetLargestSizeRange(size_t* min_value, size_t* max_value) const;
+  bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
+  // Outputs CSV formatted string of the data values.
+  // The header containing the binning range is printed first, then the
+  // the number of allocations in that bin.
+  // Example:
+  //   "16...32","32...64","64...128","128...256",...
+  //   831,3726,3432,10285,...
+  //
+  // In this example there are 831 allocations of size 16-32 bytes.
+  std::string ToCSVString() const;
+  const AllocationHistogram& allocation_histogram() const {
+    return allocation_histogram_;
+  }
+
+ private:
+  AllocationHistogram allocation_histogram_;
+  // Only these allocations are tracked.
+  const nb::analytics::AllocationGroup* group_filter_;
+};
+
+// Finds the top allocations by size.
+class FindTopSizes : public nb::analytics::AllocationVisitor {
+ public:
+  FindTopSizes(size_t minimum_size, size_t maximum_size,
+               const nb::analytics::AllocationGroup* group);
+
+  virtual bool Visit(
+      const void* memory,
+      const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
+
+  struct GroupAllocation {
+    size_t allocation_size;
+    size_t allocation_count;
+
+    static bool LessAllocationSize(GroupAllocation a, GroupAllocation b) {
+      size_t total_size_a = a.allocation_size * a.allocation_count;
+      size_t total_size_b = b.allocation_size * b.allocation_count;
+      return total_size_a < total_size_b;
+    }
+  };
+
+  std::string ToString(size_t max_elements_to_print) const;
+  std::vector<GroupAllocation> GetTopAllocations() const;
+
+ private:
+  typedef std::map<size_t, size_t> SizeCounterMap;
+
+  bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
+  size_t minimum_size_;
+  size_t maximum_size_;
+  const nb::analytics::AllocationGroup* group_filter_;
+  SizeCounterMap size_counter_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_IMPL_H_
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_test.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl_test.cc
similarity index 92%
rename from src/cobalt/browser/memory_tracker/memory_tracker_tool_test.cc
rename to src/cobalt/browser/memory_tracker/tool/tool_impl_test.cc
index 4d1913e..1a963e9 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_test.cc
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl_test.cc
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "build/build_config.h"  // defines OS_STARBOARD
-#include "cobalt/browser/memory_tracker/memory_tracker_tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +31,7 @@
 TEST(MemoryTrackerToolTest, AllocationSizeBinner) {
   const size_t index =
       AllocationSizeBinner::GetBucketIndexForAllocationSize(24);
-  EXPECT_EQ(5, index);
+  EXPECT_EQ(5, static_cast<int>(index));
 
   const size_t kAllocSize = 24;
 
@@ -44,8 +44,8 @@
   size_t min_val = 0;
   size_t max_val = 0;
   AllocationSizeBinner::GetSizeRange(kAllocSize, &min_val, &max_val);
-  EXPECT_EQ(16, min_val);
-  EXPECT_EQ(31, max_val);
+  EXPECT_EQ(16, static_cast<int>(min_val));
+  EXPECT_EQ(31, static_cast<int>(max_val));
 
   // 0 -> [0,0]
   // 1 -> [1,1]
@@ -62,8 +62,8 @@
   size_t min_value = 0;
   size_t max_value = 0;
   binner.GetLargestSizeRange(&min_value, &max_value);
-  EXPECT_EQ(min_value, 8);
-  EXPECT_EQ(max_value, 15);
+  EXPECT_EQ(min_value, static_cast<int>(8));
+  EXPECT_EQ(max_value, static_cast<int>(15));
 
   std::string csv_string = binner.ToCSVString();
 
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_thread.cc b/src/cobalt/browser/memory_tracker/tool/tool_thread.cc
new file mode 100644
index 0000000..23ce566
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_thread.cc
@@ -0,0 +1,86 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/tool_thread.h"
+
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+namespace {
+// NoMemoryTracking will disable memory tracking while in the current scope of
+// execution. When the object is destroyed it will reset the previous state
+// of allocation tracking.
+// Example:
+//   void Foo() {
+//     NoMemoryTracking no_memory_tracking_in_scope;
+//     int* ptr = new int();  // ptr is not tracked.
+//     delete ptr;
+//     return;    // Previous memory tracking state is restored.
+//   }
+class NoMemoryTracking {
+ public:
+  explicit NoMemoryTracking(nb::analytics::MemoryTracker* owner)
+      : prev_val_(false), owner_(owner) {
+    if (owner_) {
+      prev_val_ = owner_->IsMemoryTrackingEnabled();
+      owner_->SetMemoryTrackingEnabled(false);
+    }
+  }
+  ~NoMemoryTracking() {
+    if (owner_) {
+      owner_->SetMemoryTrackingEnabled(prev_val_);
+    }
+  }
+
+ private:
+  bool prev_val_;
+  nb::analytics::MemoryTracker* owner_;
+};
+
+}  // namespace.
+
+ToolThread::ToolThread(nb::analytics::MemoryTracker* memory_tracker,
+                       AbstractTool* tool, AbstractLogger* logger)
+    : Super(tool->tool_name()),
+      params_(
+          new Params(memory_tracker, logger, base::Time::NowFromSystemTime())),
+      tool_(tool) {
+  Start();
+}
+
+ToolThread::~ToolThread() {
+  Join();
+  tool_.reset();
+  params_.reset();
+}
+
+void ToolThread::Join() {
+  params_->set_finished(true);
+  Super::Join();
+}
+
+void ToolThread::Run() {
+  NoMemoryTracking no_mem_tracking_in_this_scope(params_->memory_tracker());
+  // This tool will run until the finished_ if flipped to false.
+  tool_->Run(params_.get());
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_thread.h b/src/cobalt/browser/memory_tracker/tool/tool_thread.h
new file mode 100644
index 0000000..503248a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_thread.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_THREAD_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_THREAD_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/simple_thread.h"
+
+namespace nb {
+namespace analytics {
+class MemoryTracker;
+}  // namespace analytics
+}  // namespace nb
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class AbstractLogger;
+class AbstractTool;
+class Params;
+
+// ToolThread sets up the the thread environment to run an AbstractTool.
+class ToolThread : public base::SimpleThread {
+ public:
+  typedef base::SimpleThread Super;
+  ToolThread(nb::analytics::MemoryTracker* memory_tracker, AbstractTool* tool,
+             AbstractLogger* logger);
+  virtual ~ToolThread();
+
+  virtual void Join() OVERRIDE;
+  virtual void Run() OVERRIDE;
+
+ private:
+  scoped_ptr<Params> params_;
+  scoped_ptr<AbstractTool> tool_;
+};
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_THREAD_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/util.cc b/src/cobalt/browser/memory_tracker/tool/util.cc
new file mode 100644
index 0000000..09068cb
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/util.cc
@@ -0,0 +1,136 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/util.h"
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "base/time.h"
+#include "starboard/string.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+const char kQuote[] = "\"";
+const char kDelimiter[] = ",";
+const char kNewLine[] = "\n";
+
+std::string RemoveString(const std::string& haystack, const char* needle) {
+  const size_t kNotFound = std::string::npos;
+
+  // Base case. No modification needed.
+  size_t pos = haystack.find(needle);
+  if (pos == kNotFound) {
+    return haystack;
+  }
+  const size_t n = SbStringGetLength(needle);
+  std::string output;
+  output.reserve(haystack.size());
+
+  // Copy string, omitting the portion containing the "needle".
+  std::copy(haystack.begin(), haystack.begin() + pos,
+            std::back_inserter(output));
+  std::copy(haystack.begin() + pos + n, haystack.end(),
+            std::back_inserter(output));
+
+  // Recursively remove same needle in haystack.
+  return RemoveString(output, needle);
+}
+
+std::string SanitizeCSVKey(std::string key) {
+  key = RemoveString(key, kQuote);
+  key = RemoveString(key, kDelimiter);
+  key = RemoveString(key, kNewLine);
+  return key;
+}
+
+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 (size_t i = 0; i < out.size(); ++i) {
+    ss << out[i];
+  }
+  return ss.str();
+}
+
+Timer::Timer(base::TimeDelta dt)
+    : start_time_(base::TimeTicks::Now()), time_before_expiration_(dt) {}
+
+void Timer::Restart() { start_time_ = base::TimeTicks::Now(); }
+
+bool Timer::UpdateAndIsExpired() {
+  base::TimeTicks now_time = base::TimeTicks::Now();
+  base::TimeDelta dt = now_time - start_time_;
+  if (dt > time_before_expiration_) {
+    start_time_ = now_time;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+const char* BaseNameFast(const char* file_name) {
+  // Case: Linux.
+  const char* end_pos = file_name + SbStringGetLength(file_name);
+  const char* last_forward_slash = SbStringFindLastCharacter(file_name, '/');
+  if (last_forward_slash) {
+    if (end_pos != last_forward_slash) {
+      ++last_forward_slash;
+    }
+    return last_forward_slash;
+  }
+
+  // Case: Windows.
+  const char* last_backward_slash = SbStringFindLastCharacter(file_name, '\\');
+  if (last_backward_slash) {
+    if (end_pos != last_backward_slash) {
+      ++last_backward_slash;
+    }
+    return last_backward_slash;
+  }
+  return file_name;
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/util.h b/src/cobalt/browser/memory_tracker/tool/util.h
new file mode 100644
index 0000000..3d44f6a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/util.h
@@ -0,0 +1,153 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
+
+#include <sstream>
+#include <string>
+
+#include "base/time.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Used for CSV generation.
+extern const char kQuote[];
+extern const char kDelimiter[];
+extern const char kNewLine[];
+
+// This is a simple algorithm to remove the "needle" from the haystack. Note
+// that this function is simple and not well optimized.
+std::string RemoveString(const std::string& haystack, const char* needle);
+
+// Not optimized but works ok for a tool that dumps out in user time.
+std::string SanitizeCSVKey(std::string key);
+
+// Converts "2345.54" => "2,345.54".
+std::string InsertCommasIntoNumberString(const std::string& input);
+
+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;
+}
+
+// Removes odd elements and resizes vector.
+template <typename VectorType>
+void RemoveOddElements(VectorType* v) {
+  typedef typename VectorType::iterator iterator;
+
+  iterator read_it = v->end();
+  iterator write_it = v->end();
+  for (size_t i = 0; i * 2 < v->size(); ++i) {
+    write_it = v->begin() + i;
+    read_it = v->begin() + (i * 2);
+    *write_it = *read_it;
+  }
+  if (write_it != v->end()) {
+    write_it++;
+  }
+  v->erase(write_it, v->end());
+}
+
+// Simple timer class that fires periodically after dt time has elapsed.
+class Timer {
+ public:
+  explicit Timer(base::TimeDelta dt);
+  void Restart();
+  bool UpdateAndIsExpired();  // Returns true if the expiration was triggered.
+
+ private:
+  base::TimeTicks start_time_;
+  base::TimeDelta time_before_expiration_;
+};
+
+// Generates a linear fit in the form of slope / y-intercept form.
+// Returns true if linear fit was calculated. False otherwise. Reasons for
+// returning false include passing in an empty range, such that
+// begin_it == end_it, or all x-values being the same.
+//
+// Algorithm adapted from:
+// https://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line
+// Example:
+//    std::vector<std::pair<int, int> > data;
+//    for (int i = 0; i < 10; ++i) {
+//      data.push_back(std::pair<int, int>(i+1, 2*i));
+//    }
+//    double slope = 0;
+//    double y_intercept = 0;
+//    GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
+//    std::cout << "slope: " << slope << "\n";
+//    std::cout << "y_intercept: " << y_intercept<< "\n";
+template <typename PairIterator>
+bool GetLinearFit(PairIterator begin_it, PairIterator end_it, double* out_slope,
+                  double* out_yintercept) {
+  if (begin_it == end_it) {
+    return false;
+  }
+
+  size_t n = 0;
+  double x_avg = 0;
+  double y_avg = 0;
+
+  for (PairIterator it = begin_it; it != end_it; ++it) {
+    x_avg += it->first;
+    y_avg += it->second;
+    n++;
+  }
+
+  x_avg /= n;
+  y_avg /= n;
+
+  double numerator = 0;
+  double denominator = 0;
+
+  for (PairIterator it = begin_it; it != end_it; ++it) {
+    double x_variance = it->first - x_avg;
+    double y_variance = it->second - y_avg;
+    numerator += (x_variance * y_variance);
+    denominator += (x_variance * x_variance);
+  }
+
+  if (denominator == 0.0) {
+    return false;  // Failed to generate any value.
+  }
+
+  double slope = numerator / denominator;
+  double yintercept = y_avg - slope * x_avg;
+
+  *out_slope = slope;
+  *out_yintercept = yintercept;
+  return true;
+}
+
+// Returns a substring with the directory path removed from the filename.
+// Example:
+//   F::BaseNameFast("directory/filename.cc") => "filename.cc"
+//   F::BaseNameFast("directory\filename.cc") => "filename.cc"
+//
+// Note that base::FilePath::BaseName() isn't used because of performance
+// reasons.
+const char* BaseNameFast(const char* file_name);
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/util_test.cc b/src/cobalt/browser/memory_tracker/tool/util_test.cc
new file mode 100644
index 0000000..07e9d63
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/util_test.cc
@@ -0,0 +1,112 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/util.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Tests the expectation that AllocationSizeBinner will correctly bin
+// allocations.
+TEST(MemoryTrackerUtilTest, RemoveString) {
+  std::string value = "abba";
+  value = RemoveString(value, "bb");
+  EXPECT_STREQ("aa", value.c_str());
+}
+
+// Tests the expectation that AllocationSizeBinner will correctly bin
+// allocations.
+TEST(MemoryTrackerUtilTest, InsertCommasIntoNumberString) {
+  std::string value = "2345.54";
+  std::string value_with_commas = InsertCommasIntoNumberString(value);
+
+  EXPECT_STREQ("2,345.54", value_with_commas.c_str());
+}
+
+TEST(MemoryTrackerUtilTest, NumberFormatWithCommas) {
+  int value = 1000;
+  std::string value_with_commas = NumberFormatWithCommas<int>(value);
+
+  EXPECT_STREQ("1,000", value_with_commas.c_str());
+}
+
+// Tests the expectation that RemoveOddElements() removes the odd elements of
+// a vector and resizes it.
+TEST(MemoryTrackerUtilTest, RemoveOddElements) {
+  std::vector<int> values;
+
+  // EVEN TEST.
+  values.push_back(0);
+  values.push_back(1);
+  values.push_back(2);
+  values.push_back(3);
+
+  RemoveOddElements(&values);
+
+  ASSERT_EQ(2, values.size());
+  EXPECT_EQ(0, values[0]);
+  EXPECT_EQ(2, values[1]);
+
+  values.clear();
+
+  // ODD TEST
+  values.push_back(0);
+  values.push_back(1);
+  values.push_back(2);
+  values.push_back(3);
+  values.push_back(4);
+  RemoveOddElements(&values);
+
+  ASSERT_EQ(3, values.size());
+  EXPECT_EQ(0, values[0]);
+  EXPECT_EQ(2, values[1]);
+  EXPECT_EQ(4, values[2]);
+}
+
+// Tests the expectation that GetLinearFit() generates the expected linear
+// regression values.
+TEST(MemoryTrackerUtilTest, GetLinearFit) {
+  std::vector<std::pair<int, int> > data;
+  for (int i = 0; i < 10; ++i) {
+    data.push_back(std::pair<int, int>(i+1, 2*i));
+  }
+  double slope = 0;
+  double y_intercept = 0;
+  GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
+
+  EXPECT_DOUBLE_EQ(2.0f, slope);
+  EXPECT_DOUBLE_EQ(-2.0f, y_intercept);
+}
+
+// Test the expectation that BaseNameFast() works correctly for both windows
+// and linux path types.
+TEST(MemoryTrackerUtilTest, BaseNameFast) {
+  const char* linux_path = "directory/filename.cc";
+  const char* win_path = "directory\\filename.cc";
+
+  EXPECT_STREQ("filename.cc", BaseNameFast(linux_path));
+  EXPECT_STREQ("filename.cc", BaseNameFast(win_path));
+}
+
+}  // namespace memory_tracker
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
index 4712185..7f2ee02 100644
--- a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
+++ b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/browser/resource_provider_array_buffer_allocator.h"
 
+#include "starboard/common/scoped_ptr.h"
+
 namespace cobalt {
 namespace browser {
 
@@ -24,9 +26,9 @@
   DCHECK(gpu_memory_buffer_space_);
   DCHECK(gpu_memory_buffer_space_->GetMemory());
 
-  gpu_memory_pool_.reset(new nb::MemoryPool(
-      gpu_memory_buffer_space_->GetMemory(),
-      gpu_memory_buffer_space_->GetSizeInBytes(), true /* thread_safe */));
+  gpu_memory_pool_.set(starboard::make_scoped_ptr(
+      new nb::MemoryPool(gpu_memory_buffer_space_->GetMemory(),
+                         gpu_memory_buffer_space_->GetSizeInBytes())));
 }
 
 void* ResourceProviderArrayBufferAllocator::Allocate(size_t size) {
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.h b/src/cobalt/browser/resource_provider_array_buffer_allocator.h
index d4368c9..c9de006 100644
--- a/src/cobalt/browser/resource_provider_array_buffer_allocator.h
+++ b/src/cobalt/browser/resource_provider_array_buffer_allocator.h
@@ -15,10 +15,10 @@
 #ifndef COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_
 #define COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_
 
-#include "base/memory/scoped_ptr.h"
 #include "cobalt/dom/array_buffer.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "nb/memory_pool.h"
+#include "starboard/common/locked_ptr.h"
 
 namespace cobalt {
 namespace browser {
@@ -37,7 +37,7 @@
   void Free(void* p) OVERRIDE;
 
   scoped_ptr<render_tree::RawImageMemory> gpu_memory_buffer_space_;
-  scoped_ptr<nb::MemoryPool> gpu_memory_pool_;
+  starboard::LockedPtr<nb::MemoryPool> gpu_memory_pool_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index ce8a580..ae60aa5 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -24,27 +24,31 @@
 const char SplashScreen::Options::kDefaultSplashScreenURL[] =
     "h5vcc-embedded://splash_screen.html";
 
-SplashScreen::SplashScreen(
-    const WebModule::OnRenderTreeProducedCallback&
-        render_tree_produced_callback,
-    network::NetworkModule* network_module, const math::Size& window_dimensions,
-    render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
-    const SplashScreen::Options& options)
-    : render_tree_produced_callback_(render_tree_produced_callback)
-    , is_ready_(true, false) {
-  WebModule::Options web_module_options(window_dimensions);
+SplashScreen::SplashScreen(const WebModule::OnRenderTreeProducedCallback&
+                               render_tree_produced_callback,
+                           network::NetworkModule* network_module,
+                           const math::Size& window_dimensions,
+                           render_tree::ResourceProvider* resource_provider,
+                           float layout_refresh_rate,
+                           const SplashScreen::Options& options)
+    : render_tree_produced_callback_(render_tree_produced_callback),
+      is_ready_(true, false) {
+  WebModule::Options web_module_options;
   web_module_options.name = "SplashScreenWebModule";
 
   // We want the splash screen to load and appear as quickly as possible, so
   // we set it and its image decoding thread to be high priority.
   web_module_options.thread_priority = base::kThreadPriority_High;
   web_module_options.loader_thread_priority = base::kThreadPriority_High;
+  web_module_options.animated_image_decode_thread_priority =
+      base::kThreadPriority_High;
 
   web_module_.reset(new WebModule(
       options.url,
       base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
       base::Bind(&SplashScreen::OnError, base::Unretained(this)),
       base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
+      base::Closure(),  // window_minimize_callback
       &stub_media_module_, network_module, window_dimensions, resource_provider,
       stub_media_module_.system_window(), layout_refresh_rate,
       web_module_options));
diff --git a/src/cobalt/browser/starboard/event_handler.cc b/src/cobalt/browser/starboard/event_handler.cc
index 6888ca0..a8ad309 100644
--- a/src/cobalt/browser/starboard/event_handler.cc
+++ b/src/cobalt/browser/starboard/event_handler.cc
@@ -16,6 +16,7 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "cobalt/base/accessibility_settings_changed_event.h"
 #include "cobalt/base/deep_link_event.h"
 #include "cobalt/network/network_event.h"
 #include "cobalt/system_window/application_event.h"
@@ -49,38 +50,50 @@
   g_the_event_handler->DispatchEvent(starboard_event);
 }
 
+void EventHandler::DispatchEventInternal(base::Event* event) const {
+  event_dispatcher_->DispatchEvent(make_scoped_ptr<base::Event>(event));
+}
+
 void EventHandler::DispatchEvent(const SbEvent* starboard_event) const {
   // Create a Cobalt event from the Starboard event, if recognized.
-  scoped_ptr<base::Event> cobalt_event;
-  if (starboard_event->type == kSbEventTypePause) {
-    cobalt_event.reset(new system_window::ApplicationEvent(
-        system_window::ApplicationEvent::kPause));
-  } else if (starboard_event->type == kSbEventTypeUnpause) {
-    cobalt_event.reset(new system_window::ApplicationEvent(
-        system_window::ApplicationEvent::kUnpause));
-  } else if (starboard_event->type == kSbEventTypeSuspend) {
-    cobalt_event.reset(new system_window::ApplicationEvent(
-        system_window::ApplicationEvent::kSuspend));
-  } else if (starboard_event->type == kSbEventTypeResume) {
-    cobalt_event.reset(new system_window::ApplicationEvent(
-        system_window::ApplicationEvent::kResume));
-  } else if (starboard_event->type == kSbEventTypeNetworkConnect) {
-    cobalt_event.reset(
-        new network::NetworkEvent(network::NetworkEvent::kConnection));
-  } else if (starboard_event->type == kSbEventTypeNetworkDisconnect) {
-    cobalt_event.reset(
-        new network::NetworkEvent(network::NetworkEvent::kDisconnection));
-  } else if (starboard_event->type == kSbEventTypeLink) {
-    const char* link = static_cast<const char*>(starboard_event->data);
-    cobalt_event.reset(new base::DeepLinkEvent(link));
-  }
-
-  // Dispatch the Cobalt event, if created.
-  if (cobalt_event) {
-    event_dispatcher_->DispatchEvent(cobalt_event.Pass());
-  } else {
-    DLOG(WARNING) << "Unhandled Starboard event of type: "
-                  << starboard_event->type;
+  switch (starboard_event->type) {
+    case kSbEventTypePause:
+      DispatchEventInternal(new system_window::ApplicationEvent(
+          system_window::ApplicationEvent::kPause));
+      break;
+    case kSbEventTypeUnpause:
+      DispatchEventInternal(new system_window::ApplicationEvent(
+          system_window::ApplicationEvent::kUnpause));
+      break;
+    case kSbEventTypeSuspend:
+      DispatchEventInternal(new system_window::ApplicationEvent(
+          system_window::ApplicationEvent::kSuspend));
+      break;
+    case kSbEventTypeResume:
+      DispatchEventInternal(new system_window::ApplicationEvent(
+          system_window::ApplicationEvent::kResume));
+      break;
+    case kSbEventTypeNetworkConnect:
+      DispatchEventInternal(
+          new network::NetworkEvent(network::NetworkEvent::kConnection));
+      break;
+    case kSbEventTypeNetworkDisconnect:
+      DispatchEventInternal(
+          new network::NetworkEvent(network::NetworkEvent::kDisconnection));
+      break;
+    case kSbEventTypeLink: {
+      const char* link = static_cast<const char*>(starboard_event->data);
+      DispatchEventInternal(new base::DeepLinkEvent(link));
+      break;
+    }
+#if SB_API_VERSION >= 4
+    case kSbEventTypeAccessiblitySettingsChanged:
+      DispatchEventInternal(new base::AccessibilitySettingsChangedEvent());
+      break;
+#endif  // SB_API_VERSION >= 4
+    default:
+      DLOG(WARNING) << "Unhandled Starboard event of type: "
+                    << starboard_event->type;
   }
 }
 
diff --git a/src/cobalt/browser/starboard/event_handler.h b/src/cobalt/browser/starboard/event_handler.h
index dddb0f1..13391d2 100644
--- a/src/cobalt/browser/starboard/event_handler.h
+++ b/src/cobalt/browser/starboard/event_handler.h
@@ -35,6 +35,8 @@
   // of the system via |event_dispatcher_|.
   void DispatchEvent(const SbEvent* event) const;
 
+  void DispatchEventInternal(base::Event*) const;
+
   // The event dispatcher that dispatches Cobalt events to the rest of the
   // system.
   base::EventDispatcher* event_dispatcher_;
diff --git a/src/cobalt/browser/storage_upgrade_handler.cc b/src/cobalt/browser/storage_upgrade_handler.cc
index b40acf6..9be2cda 100644
--- a/src/cobalt/browser/storage_upgrade_handler.cc
+++ b/src/cobalt/browser/storage_upgrade_handler.cc
@@ -42,7 +42,8 @@
 
 void StorageUpgradeHandler::OnUpgrade(storage::StorageManager* storage,
                                       const char* data, int size) {
-  storage::upgrade::UpgradeReader upgrade_reader(data, size);
+  storage::upgrade::UpgradeReader upgrade_reader;
+  upgrade_reader.Parse(data, size);
   int num_cookies = upgrade_reader.GetNumCookies();
   int num_local_storage_entries = upgrade_reader.GetNumLocalStorageEntries();
   DLOG(INFO) << "Upgrading legacy save data: " << num_cookies << " cookies, "
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index e894b9e..59d9850 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -161,6 +161,12 @@
 // Specifies that javascript jit should be disabled.
 const char kDisableJavaScriptJit[] = "disable_javascript_jit";
 
+// Specifies the javascript gc threshold. When this amount of garbage has
+// collected then the garbage collector will begin running.
+const char kJavaScriptGcThresholdInBytes[] = "javascript_gc_threshold_in_bytes";
+
+const char kSkiaTextureAtlasDimensions[] = "skia_atlas_texture_dimensions";
+
 }  // namespace switches
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index faeaf51..6b30b2b 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -56,6 +56,8 @@
 extern const char kSurfaceCacheSizeInBytes[];
 extern const char kViewport[];
 extern const char kDisableJavaScriptJit[];
+extern const char kJavaScriptGcThresholdInBytes[];
+extern const char kSkiaTextureAtlasDimensions[];
 
 }  // namespace switches
 }  // namespace browser
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 0354f78..f9cfc51 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -23,14 +23,9 @@
 #include "base/message_loop_proxy.h"
 #include "base/optional.h"
 #include "base/stringprintf.h"
-#include "cobalt/accessibility/screen_reader.h"
-#include "cobalt/accessibility/starboard_tts_engine.h"
-#include "cobalt/accessibility/tts_engine.h"
-#include "cobalt/accessibility/tts_logger.h"
 #include "cobalt/base/c_val.h"
 #include "cobalt/base/poller.h"
 #include "cobalt/base/tokens.h"
-#include "cobalt/browser/memory_settings/memory_settings.h"
 #include "cobalt/browser/stack_size_constants.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/browser/web_module_stat_tracker.h"
@@ -45,6 +40,8 @@
 #include "cobalt/dom/url.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/h5vcc/h5vcc.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
+#include "cobalt/media_session/media_session_client.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/storage/storage_manager.h"
 #include "cobalt/system_window/system_window.h"
@@ -111,28 +108,6 @@
   base::CVal<base::cval::SizeInBytes, base::CValPublic> js_reserved_memory_;
 };
 
-#if SB_HAS(SPEECH_SYNTHESIS)
-bool IsTextToSpeechEnabled() {
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-  // Check for a command-line override to enable TTS.
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(browser::switches::kUseTTS)) {
-    return true;
-  }
-#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
-  // Check if the tts feature is enabled in Starboard.
-  SbAccessibilityTextToSpeechSettings tts_settings = {0};
-  // Check platform settings.
-  if (SbAccessibilityGetTextToSpeechSettings(&tts_settings)) {
-    return tts_settings.has_text_to_speech_setting &&
-           tts_settings.is_text_to_speech_enabled;
-  }
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
-  return false;
-}
-#endif  // SB_HAS(SPEECH_SYNTHESIS)
-
 // StartupTimer is designed to measure time since the startup of the app.
 // It is loader initialized to have the most accurate start time as possible.
 class StartupTimer {
@@ -177,7 +152,8 @@
   // output parameter and signals |got_result|.
   void ExecuteJavascript(const std::string& script_utf8,
                          const base::SourceLocation& script_location,
-                         base::WaitableEvent* got_result, std::string* result);
+                         base::WaitableEvent* got_result, std::string* result,
+                         bool *out_succeeded);
 
   // Clears disables timer related objects
   // so that the message loop can easily exit
@@ -212,6 +188,12 @@
  private:
   class DocumentLoadedObserver;
 
+  // Purge all resource caches owned by the WebModule.
+  void PurgeResourceCaches();
+
+  // Disable callbacks in all resource caches owned by the WebModule.
+  void DisableCallbacksInResourceCaches();
+
   // Injects a list of custom window attributes into the WebModule's window
   // object.
   void InjectCustomWindowAttributes(
@@ -268,6 +250,8 @@
   // URL.
   scoped_ptr<loader::LoaderFactory> loader_factory_;
 
+  scoped_ptr<loader::image::AnimatedImageTracker> animated_image_tracker_;
+
   // ImageCache that is used to manage image cache logic.
   scoped_ptr<loader::image::ImageCache> image_cache_;
 
@@ -337,12 +321,6 @@
   // Triggers layout whenever the document changes.
   scoped_ptr<layout::LayoutManager> layout_manager_;
 
-  // TTSEngine that the ScreenReader speaks to.
-  scoped_ptr<accessibility::TTSEngine> tts_engine_;
-
-  // Utters the text contents of the focused element.
-  scoped_ptr<accessibility::ScreenReader> screen_reader_;
-
 #if defined(ENABLE_DEBUG_CONSOLE)
   // Allows the debugger to add render components to the web module.
   // Used for DOM node highlighting and overlay messages.
@@ -362,6 +340,8 @@
   scoped_ptr<base::ConsoleCommandManager::CommandHandler>
       partial_layout_command_handler_;
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
+
+  scoped_ptr<media_session::MediaSessionClient> media_session_client_;
 };
 
 class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -427,6 +407,9 @@
       new loader::LoaderFactory(fetcher_factory_.get(), resource_provider_,
                                 data.options.loader_thread_priority));
 
+  animated_image_tracker_.reset(new loader::image::AnimatedImageTracker(
+      data.options.animated_image_decode_thread_priority));
+
   DCHECK_LE(0, data.options.image_cache_capacity);
   image_cache_ = loader::image::CreateImageCache(
       base::StringPrintf("%s.ImageCache", name_.c_str()),
@@ -461,7 +444,8 @@
       new browser::WebModuleStatTracker(name_, data.options.track_event_stats));
   DCHECK(web_module_stat_tracker_);
 
-  javascript_engine_ = script::JavaScriptEngine::CreateEngine();
+  javascript_engine_ =
+      script::JavaScriptEngine::CreateEngine(data.options.javascript_options);
   DCHECK(javascript_engine_);
 
 #if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
@@ -475,8 +459,7 @@
                  base::Unretained(this)),
       base::TimeDelta::FromMilliseconds(kPollerPeriodMs)));
 
-  global_environment_ = javascript_engine_->CreateGlobalEnvironment(
-      data.options.javascript_options);
+  global_environment_ = javascript_engine_->CreateGlobalEnvironment();
   DCHECK(global_environment_);
 
   execution_state_ =
@@ -489,14 +472,16 @@
 
   media_source_registry_.reset(new dom::MediaSource::Registry);
 
+  media_session_client_ = media_session::MediaSessionClient::Create();
+
   window_ = new dom::Window(
       data.window_dimensions.width(), data.window_dimensions.height(),
       css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
-      &resource_provider_, image_cache_.get(),
+      &resource_provider_, animated_image_tracker_.get(), image_cache_.get(),
       reduced_image_cache_capacity_manager_.get(), remote_typeface_cache_.get(),
       mesh_cache_.get(), local_storage_database_.get(), data.media_module,
       data.media_module, execution_state_.get(), script_runner_.get(),
-      media_source_registry_.get(),
+      global_environment_->script_value_factory(), media_source_registry_.get(),
       web_module_stat_tracker_->dom_stat_tracker(), data.initial_url,
       data.network_module->GetUserAgent(),
       data.network_module->preferred_language(),
@@ -507,9 +492,10 @@
       base::Bind(&WebModule::Impl::OnCspPolicyChanged, base::Unretained(this)),
       base::Bind(&WebModule::Impl::OnRanAnimationFrameCallbacks,
                  base::Unretained(this)),
-      data.window_close_callback, data.system_window_,
-      data.options.input_poller, data.options.csp_insecure_allowed_token,
-      data.dom_max_element_depth);
+      data.window_close_callback, data.window_minimize_callback,
+      data.system_window_, data.options.input_poller,
+      media_session_client_->GetMediaSession(),
+      data.options.csp_insecure_allowed_token, data.dom_max_element_depth);
   DCHECK(window_);
 
   window_weak_ = base::AsWeakPtr(window_.get());
@@ -566,26 +552,6 @@
     window_->document()->AddObserver(document_load_observer_.get());
   }
 
-  // If a TTSEngine was provided through the options, use it.
-  accessibility::TTSEngine* tts_engine = data.options.tts_engine;
-  if (!tts_engine) {
-#if SB_HAS(SPEECH_SYNTHESIS)
-    if (IsTextToSpeechEnabled()) {
-      // Create a StarboardTTSEngine if TTS is enabled.
-      tts_engine_.reset(new accessibility::StarboardTTSEngine());
-    }
-#endif  // SB_HAS(SPEECH_SYNTHESIS)
-#if !defined(COBALT_BUILD_TYPE_GOLD)
-    if (!tts_engine_) {
-      tts_engine_.reset(new accessibility::TTSLogger());
-    }
-#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
-    tts_engine = tts_engine_.get();
-  }
-  if (tts_engine) {
-    screen_reader_.reset(new accessibility::ScreenReader(
-        window_->document(), tts_engine, &mutation_observer_task_manager_));
-  }
   is_running_ = true;
 }
 
@@ -596,6 +562,7 @@
   global_environment_->SetReportEvalCallback(base::Closure());
   window_->DispatchEvent(new dom::Event(base::Tokens::unload()));
   document_load_observer_.reset();
+  media_session_client_.reset();
 
 #if defined(ENABLE_DEBUG_CONSOLE)
   debug_overlay_.reset();
@@ -606,10 +573,8 @@
   // callback to occur into a DOM object that is being kept alive by a JS engine
   // reference even after the DOM tree has been destroyed. This can result in a
   // crash when the callback attempts to access a stale Document pointer.
-  remote_typeface_cache_->DisableCallbacks();
-  image_cache_->DisableCallbacks();
+  DisableCallbacksInResourceCaches();
 
-  screen_reader_.reset();
   layout_manager_.reset();
   environment_settings_.reset();
   window_weak_.reset();
@@ -625,6 +590,7 @@
   local_storage_database_.reset();
   mesh_cache_.reset();
   remote_typeface_cache_.reset();
+  animated_image_tracker_.reset();
   image_cache_.reset();
   fetcher_factory_.reset();
   dom_parser_.reset();
@@ -658,11 +624,12 @@
 
 void WebModule::Impl::ExecuteJavascript(
     const std::string& script_utf8, const base::SourceLocation& script_location,
-    base::WaitableEvent* got_result, std::string* result) {
+    base::WaitableEvent* got_result, std::string* result, bool* out_succeeded) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(is_running_);
   DCHECK(script_runner_);
-  *result = script_runner_->Execute(script_utf8, script_location);
+  *result = script_runner_->Execute(script_utf8, script_location,
+      out_succeeded);
   got_result->Signal();
 }
 
@@ -768,16 +735,27 @@
   for (Options::InjectedWindowAttributes::const_iterator iter =
            attributes.begin();
        iter != attributes.end(); ++iter) {
-    global_environment_->Bind(iter->first, iter->second.Run());
+    global_environment_->Bind(
+        iter->first,
+        iter->second.Run(window_, &mutation_observer_task_manager_));
   }
 }
 
 void WebModule::Impl::SuspendLoaders() {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::SuspendLoaders()");
 
+  // Purge the resource caches before running any suspend logic. This will force
+  // any pending callbacks that the caches are batching to run.
+  PurgeResourceCaches();
+
   // Stop the generation of render trees.
   layout_manager_->Suspend();
 
+  // Purge the cached resources prior to the suspend. That may cancel pending
+  // loads, allowing the suspend to occur faster and preventing unnecessary
+  // callbacks.
+  window_->document()->PurgeCachedResources();
+
   // Clear out the loader factory's resource provider, possibly aborting any
   // in-progress loads.
   loader_factory_->Suspend();
@@ -789,14 +767,12 @@
 
   // Ensure the document is not holding onto any more image cached resources so
   // that they are eligible to be purged.
-  window_->document()->PurgeCachedResourceReferencesRecursively();
+  window_->document()->PurgeCachedResources();
 
-  // Clear out all image resources from the image cache. We need to do this
-  // after we abort all in-progress loads, and after we clear all document
-  // references, or they will still be referenced and won't be cleared from the
-  // cache.
-  image_cache_->Purge();
-  remote_typeface_cache_->Purge();
+  // Clear out all resource caches. We need to do this after we abort all
+  // in-progress loads, and after we clear all document references, or they will
+  // still be referenced and won't be cleared from the cache.
+  PurgeResourceCaches();
 
 #if defined(ENABLE_DEBUG_CONSOLE)
   // The debug overlay may be holding onto a render tree, clear that out.
@@ -841,6 +817,18 @@
   SbLogRaw(ss.str().c_str());
 }
 
+void WebModule::Impl::PurgeResourceCaches() {
+  image_cache_->Purge();
+  remote_typeface_cache_->Purge();
+  mesh_cache_->Purge();
+}
+
+void WebModule::Impl::DisableCallbacksInResourceCaches() {
+  image_cache_->DisableCallbacks();
+  remote_typeface_cache_->DisableCallbacks();
+  mesh_cache_->DisableCallbacks();
+}
+
 WebModule::DestructionObserver::DestructionObserver(WebModule* web_module)
     : web_module_(web_module) {}
 
@@ -848,11 +836,10 @@
   web_module_->impl_.reset();
 }
 
-WebModule::Options::Options(const math::Size& ui_dimensions)
+WebModule::Options::Options()
     : name("WebModule"),
       layout_trigger(layout::LayoutManager::kOnDocumentMutation),
-      image_cache_capacity(
-          static_cast<int>(memory_settings::GetImageCacheSize(ui_dimensions))),
+      image_cache_capacity(32 * 1024 * 1024),
       remote_typeface_cache_capacity(
           COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES),
       mesh_cache_capacity(COBALT_MESH_CACHE_SIZE_IN_BYTES),
@@ -862,13 +849,14 @@
       image_cache_capacity_multiplier_when_playing_video(1.0f),
       thread_priority(base::kThreadPriority_Normal),
       loader_thread_priority(base::kThreadPriority_Low),
-      tts_engine(NULL) {}
+      animated_image_decode_thread_priority(base::kThreadPriority_Low) {}
 
 WebModule::WebModule(
     const GURL& initial_url,
     const OnRenderTreeProducedCallback& render_tree_produced_callback,
     const OnErrorCallback& error_callback,
     const base::Closure& window_close_callback,
+    const base::Closure& window_minimize_callback,
     media::MediaModule* media_module, network::NetworkModule* network_module,
     const math::Size& window_dimensions,
     render_tree::ResourceProvider* resource_provider,
@@ -877,9 +865,9 @@
     : thread_(options.name.c_str()) {
   ConstructionData construction_data(
       initial_url, render_tree_produced_callback, error_callback,
-      window_close_callback, media_module, network_module, window_dimensions,
-      resource_provider, kDOMMaxElementDepth, system_window,
-      layout_refresh_rate, options);
+      window_close_callback, window_minimize_callback, media_module,
+      network_module, window_dimensions, resource_provider, kDOMMaxElementDepth,
+      system_window, layout_refresh_rate, options);
 
   // Start the dedicated thread and create the internal implementation
   // object on that thread.
@@ -969,7 +957,8 @@
 
 std::string WebModule::ExecuteJavascript(
     const std::string& script_utf8,
-    const base::SourceLocation& script_location) {
+    const base::SourceLocation& script_location,
+    bool* out_succeeded) {
   TRACE_EVENT0("cobalt::browser", "WebModule::ExecuteJavascript()");
   DCHECK(message_loop());
   DCHECK(impl_);
@@ -979,7 +968,8 @@
   message_loop()->PostTask(
       FROM_HERE, base::Bind(&WebModule::Impl::ExecuteJavascript,
                             base::Unretained(impl_.get()), script_utf8,
-                            script_location, &got_result, &result));
+                            script_location, &got_result, &result,
+                            out_succeeded));
   got_result.Wait();
   return result;
 }
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index c8fdad9..0054f19 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -77,14 +77,16 @@
 class WebModule {
  public:
   struct Options {
-    typedef base::Callback<scoped_refptr<script::Wrappable>()>
+    typedef base::Callback<scoped_refptr<script::Wrappable>(
+        const scoped_refptr<dom::Window>& window,
+        dom::MutationObserverTaskManager* mutation_observer_task_manager)>
         CreateObjectFunction;
     typedef base::hash_map<std::string, CreateObjectFunction>
         InjectedWindowAttributes;
 
     // All optional parameters defined in this structure should have their
     // values initialized in the default constructor to useful defaults.
-    explicit Options(const math::Size& ui_dimensions);
+    Options();
 
     // The name of the WebModule.  This is useful for debugging purposes as in
     // the case where multiple WebModule objects exist, it can be used to
@@ -157,8 +159,11 @@
     // base::kThreadPriority_Low.
     base::ThreadPriority loader_thread_priority;
 
-    // TTSEngine instance to use for text-to-speech.
-    accessibility::TTSEngine* tts_engine;
+    // Specifies the priority tha the web module's animated image decoding
+    // thread will be assigned. This thread is responsible for decoding,
+    // blending and constructing individual frames from animated images. The
+    // default value is base::kThreadPriority_Low.
+    base::ThreadPriority animated_image_decode_thread_priority;
 
     // InputPoller to use for constantly polling the input key position or
     // state. For example, this is used to support 3D camera movements.
@@ -176,6 +181,7 @@
             const OnRenderTreeProducedCallback& render_tree_produced_callback,
             const OnErrorCallback& error_callback,
             const base::Closure& window_close_callback,
+            const base::Closure& window_minimize_callback,
             media::MediaModule* media_module,
             network::NetworkModule* network_module,
             const math::Size& window_dimensions,
@@ -191,7 +197,8 @@
   // thread will block until the JavaScript has executed and the output results
   // are available.
   std::string ExecuteJavascript(const std::string& script_utf8,
-                                const base::SourceLocation& script_location);
+                                const base::SourceLocation& script_location,
+                                bool* out_succeeded);
 
 #if defined(ENABLE_WEBDRIVER)
   // Creates a new webdriver::WindowDriver that interacts with the Window that
@@ -224,6 +231,7 @@
         const OnRenderTreeProducedCallback& render_tree_produced_callback,
         const OnErrorCallback& error_callback,
         const base::Closure& window_close_callback,
+        const base::Closure& window_minimize_callback,
         media::MediaModule* media_module,
         network::NetworkModule* network_module,
         const math::Size& window_dimensions,
@@ -234,6 +242,7 @@
           render_tree_produced_callback(render_tree_produced_callback),
           error_callback(error_callback),
           window_close_callback(window_close_callback),
+          window_minimize_callback(window_minimize_callback),
           media_module(media_module),
           network_module(network_module),
           window_dimensions(window_dimensions),
@@ -247,6 +256,7 @@
     OnRenderTreeProducedCallback render_tree_produced_callback;
     OnErrorCallback error_callback;
     const base::Closure& window_close_callback;
+    const base::Closure& window_minimize_callback;
     media::MediaModule* media_module;
     network::NetworkModule* network_module;
     math::Size window_dimensions;
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 4dfb22e..663e4ba 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -54,6 +54,7 @@
         '<(DEPTH)/cobalt/math/math.gyp:*',
         '<(DEPTH)/cobalt/media/sandbox/sandbox.gyp:*',
         '<(DEPTH)/cobalt/media_session/media_session.gyp:*',
+        '<(DEPTH)/cobalt/media_session/media_session_test.gyp:*',
         '<(DEPTH)/cobalt/network/network.gyp:*',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:*',
         '<(DEPTH)/cobalt/renderer/renderer.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 28b10ee..aa6da0c 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-34413
\ No newline at end of file
+49488
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index e500d8c..cc21f05 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -33,10 +33,77 @@
     # implement spherical video playback.
     'enable_map_to_mesh%': 0,
 
+    # Enables embedding Cobalt as a shared library within another app. This
+    # requires a 'lib' starboard implementation for the corresponding platform.
+    'cobalt_enable_lib%': 0,
+
     # Contains the current font package selection.  This can be used to trade
-    # font quality, coverage, and latency with smaller font package size.
-    # See content/fonts/README.md for more details.
-    'cobalt_font_package%': 'unlimited',
+    # font quality, coverage, and latency for different font package sizes.
+    # The font package can be one of the following options:
+    #   'expanded' -- The largest package. It includes everything in the
+    #                 'standard' package, along with 'bold' weight CJK. It is
+    #                 recommended that 'local_font_cache_size_in_bytes' be
+    #                 increased to 24MB when using this package to account for
+    #                 the extra memory required by bold CJK. This package is
+    #                 ~46.2MB.
+    #   'standard' -- The default package. It includes all non-CJK fallback
+    #                 fonts in both 'normal' and 'bold' weights, 'normal' weight
+    #                 CJK ('bold' weight CJK is synthesized from it), and all
+    #                 FCC fonts. This package is ~26.9MB.
+    #   'limited_with_jp' -- A significantly smaller package than 'standard'.
+    #                 This package removes the 'bold' weighted non-CJK fallback
+    #                 fonts (the 'normal' weight is still included and is used
+    #                 to synthesize bold), removes the FCC fonts (which must be
+    #                 downloaded from the web), and replaces standard CJK with
+    #                 low quality CJK. However, higher quality Japanese is still
+    #                 included. Because low quality CJK cannot synthesize bold,
+    #                 bold glyphs are unavailable in Chinese and Korean. This
+    #                 package is ~10.9MB.
+    #   'limited'  -- A smaller package than 'limited_with_jp'. The two packages
+    #                 are identical with the exception that 'limited' does not
+    #                 include the higher quality Japanese font; instead it
+    #                 relies on low quality CJK for all CJK characters. Because
+    #                 low quality CJK cannot synthesize bold, bold glyphs are
+    #                 unavailable in Chinese, Japanese, and Korean. This package
+    #                 is ~7.7MB.
+    #   'minimal'  -- The smallest possible font package. It only includes
+    #                 Roboto's Basic Latin characters. Everything else must be
+    #                 downloaded from the web. This package is ~16.4KB.
+    # NOTE: When bold is needed, but unavailable, it is typically synthesized,
+    #       resulting in lower quality glyphs than those generated directly from
+    #       a bold font. However, this does not occur with low quality CJK,
+    #       which is not high enough quality to synthesize. Its glyphs always
+    #       have a 'normal' weight.
+    'cobalt_font_package%': 'standard',
+
+    # Font package overrides can be used to modify the files included within the
+    # selected package. The following values are available:
+    #   -1 -- The package value for the specified category is not overridden.
+    #    0 -- The package value is overridden and no fonts for the specified
+    #         category are included.
+    #    1 -- The package value is overridden and fonts from the specified
+    #         category with a weight of 'normal' and a style of 'normal' are
+    #         included.
+    #    2 -- The package value is overridden and fonts from the specified
+    #         category with a weight of either 'normal' or bold' and a style of
+    #         'normal' are included.
+    #    3 -- The package value is overridden and fonts from the specified
+    #         category with a weight of either 'normal' or 'bold' and a style of
+    #         either 'normal' or 'italic' are included.
+    #    4 -- The package value is overridden and all available fonts from the
+    #         specified category are included. This may include additional
+    #         weights beyond 'normal' and 'bold'.
+    # See content/fonts/README.md for details on the specific values used by
+    # each of the packages use for the various font categories.
+    'cobalt_font_package_override_named_sans_serif%': -1,
+    'cobalt_font_package_override_named_serif%': -1,
+    'cobalt_font_package_override_named_fcc_fonts%': -1,
+    'cobalt_font_package_override_fallback_lang_non_cjk%': -1,
+    'cobalt_font_package_override_fallback_lang_cjk%': -1,
+    'cobalt_font_package_override_fallback_lang_cjk_low_quality%': -1,
+    'cobalt_font_package_override_fallback_lang_jp%': -1,
+    'cobalt_font_package_override_fallback_emoji%': -1,
+    'cobalt_font_package_override_fallback_symbols%': -1,
 
     # Build version number.
     'cobalt_version%': 0,
@@ -67,6 +134,19 @@
     #                 still be available and valid, but it will do nothing.
     'rasterizer_type%': 'hardware',
 
+    # If set to 1, will enable support for rendering only the regions of the
+    # display that are modified due to animations, instead of re-rendering the
+    # entire scene each frame.  This feature can reduce startup time where
+    # usually there is a small loading spinner animating on the screen.  On GLES
+    # renderers, Cobalt will attempt to implement this support by using
+    # eglSurfaceAttrib(..., EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED), otherwise
+    # the dirty region will be silently disabled.  On Blitter API platforms,
+    # if this is enabled, we explicitly create an extra offscreen full-size
+    # intermediate surface to render into.  Note that some GLES driver
+    # implementations may internally allocate an extra full screen surface to
+    # support this feature.
+    'render_dirty_region_only%': 1,
+
     # Modify this value to adjust the default rasterizer setting for your
     # platform.
     'default_renderer_options_dependency%': '<(DEPTH)/cobalt/renderer/default_options_starboard.gyp:default_options',
@@ -76,7 +156,11 @@
     # swapping frames may take some additional processing time, so it may be
     # better to specify a lower delay. For example, '33' instead of '33.33'
     # for 30 Hz refresh.
-    'cobalt_minimum_frame_time_in_milliseconds%': '0',
+    'cobalt_minimum_frame_time_in_milliseconds%': '16.4',
+
+    # Cobalt will call eglSwapInterval() and specify this value before calling
+    # eglSwapBuffers() each frame.
+    'cobalt_egl_swap_interval%': 1,
 
     # The variables allow changing the target type on platforms where the
     # native code may require an additional packaging step (ex. Android).
@@ -88,6 +172,9 @@
     # Set to 1 to build with DIAL support.
     'in_app_dial%': 0,
 
+    # Set to 1 to enable a custom MediaSessionClient.
+    'custom_media_session_client%': 0,
+
     # Set to 1 to enable H5vccAccountManager.
     'enable_account_manager%': 0,
 
@@ -166,14 +253,14 @@
     # surface cache facilitates the reuse of temporary offscreen surfaces
     # within a single frame.  This setting is only relevant when using the
     # hardware-accelerated Skia rasterizer.
-    'scratch_surface_cache_size_in_bytes%': 7 * 1024 * 1024,
+    'scratch_surface_cache_size_in_bytes%': 0,
 
     # Determines the capacity of the surface cache.  The surface cache tracks
     # which render tree nodes are being re-used across frames and stores the
     # nodes that are most CPU-expensive to render into surfaces.
     'surface_cache_size_in_bytes%': 0,
 
-    # Determines the capacity of the image cache which manages image surfaces
+    # Determines the capacity of the image cache, which manages image surfaces
     # downloaded from a web page.  While it depends on the platform, often (and
     # ideally) these images are cached within GPU memory.
     # Set to -1 to automatically calculate the value at runtime, based on
@@ -181,9 +268,18 @@
     # SbSystemGetTotalGPUMemory().
     'image_cache_size_in_bytes%': -1,
 
-    # Determines the capacity of the remote typefaces cache which manages all
-    # typefaces downloaded from a web page.
-    'remote_typeface_cache_size_in_bytes%': 5 * 1024 * 1024,
+    # Determines the capacity of the local font cache, which manages all fonts
+    # loaded from local files. Newly encountered sections of font files are
+    # lazily loaded into the cache, enabling subsequent requests to the same
+    # file sections to be handled via direct memory access. Once the limit is
+    # reached, further requests are handled via file stream.
+    # Setting the value to 0 disables memory caching and causes all font file
+    # accesses to be done using file streams.
+    'local_font_cache_size_in_bytes%': 16 * 1024 * 1024,
+
+    # Determines the capacity of the remote font cache, which manages all
+    # fonts downloaded from a web page.
+    'remote_font_cache_size_in_bytes%': 4 * 1024 * 1024,
 
     # Determines the capacity of the mesh cache. Each mesh is held compressed
     # in main memory, to be inflated into a GPU buffer when needed for
@@ -213,8 +309,10 @@
     # result in visible stutter. Such thrashing is more likely to occur when CJK
     # language glyphs are rendered and when the size of the glyphs in pixels is
     # larger, such as for higher resolution displays.
-    'skia_glyph_atlas_width%': '2048',
-    'skia_glyph_atlas_height%': '2048',
+    # The negative default values indicates to the engine that these settings
+    # should be automatically set.
+    'skia_glyph_atlas_width%': '-1',
+    'skia_glyph_atlas_height%': '-1',
 
     # Determines the size of garbage collection threshold. After this many bytes
     # have been allocated, the mozjs garbage collector will run. Lowering this
@@ -266,10 +364,12 @@
     # as expected, rather than requiring it to be set for each platform.
     #'javascript_engine%': 'mozjs',
 
-    # Enable jit by default. It can be set to 0 to run in interpreter-only mode.
+    # Disable jit and run in interpreter-only mode by default. It can be set to
+    # 1 to run in jit mode.  We have found that disabling jit often results in
+    # faster JavaScript execution and lower memory usage.
     # Setting this to 1 on a platform or engine for which there is no JIT
     # implementation is a no-op.
-    'cobalt_enable_jit%': 1,
+    'cobalt_enable_jit%': 0,
 
     # Customize variables used by Chromium's build/common.gypi.
 
@@ -286,7 +386,57 @@
     # Platforms may redefine to 'poll' if necessary.
     # Other mechanisms, e.g. devpoll, kqueue, select, are not yet supported.
     'sb_libevent_method%': 'epoll',
+
+    # Use media source extension implementation that is conformed to the
+    # Candidate Recommandation of July 5th 2016.
     'cobalt_media_source_2016%': 0,
+
+    # Note that the following media buffer related variables are only used when
+    # |cobalt_media_source_2016| is set to 1.
+
+    # This can be set to "memory" or "file".  When it is set to "memory", the
+    # media buffers will be stored in main memory allocated by SbMemory
+    # functions.  When it is set to "file", the media buffers will be stored in
+    # a temporary file in the system cache folder acquired by calling
+    # SbSystemGetPath() with "kSbSystemPathCacheDirectory".  Note that when its
+    # value is "file" the media stack will still allocate memory to cache the
+    # the buffers in use.
+    'cobalt_media_buffer_storage_type%': 'memory',
+    # The amount of memory that will be used to store media buffers allocated
+    # during system startup.  To allocate a large chunk at startup helps with
+    # reducing frafmentation and can avoid failures to allocate incrementally.
+    # This can be set to 0.
+    'cobalt_media_buffer_initial_capacity%': 26 * 1024 * 1024,
+    # When the media stack needs more memory to store media buffers, it will
+    # allocate extra memory in units of |cobalt_media_buffer_allocation_unit|.
+    # This can be set to 0, in which case the media stack will allocate extra
+    # memory on demand.  When |cobalt_media_buffer_initial_capacity| and this
+    # value are both set to 0, the media stack will allocate individual buffers
+    # directly using SbMemory functions.
+    'cobalt_media_buffer_allocation_unit%': 0 * 1024 * 1024,
+
+    # Specifies the maximum amount of memory used by audio or text buffers of
+    # media source before triggering a garbage collection.  A large value will
+    # cause more memory being used by audio buffers but will also make
+    # JavaScript app less likely to re-download audio data.  Note that the
+    # JavaScript app may experience significant difficulty if this value is too
+    # low.
+    'cobalt_media_buffer_non_video_budget%': 5 * 1024 * 1024,
+
+    # Specifies the maximum amount of memory used by video buffers of media
+    # source before triggering a garbage collection when the video resolution is
+    # lower than 1080p (1920x1080).  A large value will cause more memory being
+    # used by video buffers but will also make JavaScript app less likely to
+    # re-download video data.  Note that the JavaScript app may experience
+    # significant difficulty if this value is too low.
+    'cobalt_media_buffer_video_budget_1080p%': 16 * 1024 * 1024,
+    # Specifies the maximum amount of memory used by video buffers of media
+    # source before triggering a garbage collection when the video resolution is
+    # lower than 4k (3840x2160).  A large value will cause more memory being
+    # used by video buffers but will also make JavaScript app less likely to
+    # re-download video data.  Note that the JavaScript app may experience
+    # significant difficulty if this value is too low.
+    'cobalt_media_buffer_video_budget_4k%': 60 * 1024 * 1024,
   },
 
   'target_defaults': {
@@ -304,6 +454,11 @@
     },
     'defines': [
       'COBALT',
+      'COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=<(cobalt_media_buffer_non_video_budget)',
+      'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=<(cobalt_media_buffer_video_budget_1080p)',
+      'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=<(cobalt_media_buffer_video_budget_4k)',
+      'COBALT_MEDIA_BUFFER_INITIAL_CAPACITY=<(cobalt_media_buffer_initial_capacity)',
+      'COBALT_MEDIA_BUFFER_ALLOCATION_UNIT=<(cobalt_media_buffer_allocation_unit)',
     ],
     'cflags': [ '<@(compiler_flags)' ],
     'ldflags': [ '<@(linker_flags)' ],
@@ -328,6 +483,24 @@
     'libraries': [ '<@(platform_libraries)' ],
 
     'conditions': [
+      ['cobalt_media_source_2016 == 1', {
+        'defines': [
+          'COBALT_MEDIA_SOURCE_2016=1',
+        ],
+      }, {
+        'defines': [
+          'COBALT_MEDIA_SOURCE_2012=1',
+        ],
+      }],
+      ['cobalt_media_buffer_storage_type == "memory"', {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY=1',
+        ],
+      }, {
+        'defines': [
+          'COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE=1',
+        ],
+      }],
       ['final_executable_type=="shared_library"', {
         'target_conditions': [
           ['_toolset=="target"', {
@@ -356,15 +529,6 @@
           'COBALT_WEBKIT_SHARED=1',
         ],
       }],
-      ['cobalt_media_source_2016 == 1', {
-        'defines': [
-          'COBALT_MEDIA_SOURCE_2016=1',
-        ],
-      }, {
-        'defines': [
-          'COBALT_MEDIA_SOURCE_2012=1',
-        ],
-      }],
       ['OS == "lb_shell"', {
         'defines': [
           '__LB_SHELL__',
@@ -390,9 +554,6 @@
           }],
         ],
       }],  # OS == "starboard"
-      ['target_arch in ["xb1", "xb360"]', {
-        'defines': ['_USE_MATH_DEFINES'],  # For #define M_PI
-      }],
       ['in_app_dial == 1', {
         'defines': [
           'DIAL_SERVER',
@@ -424,6 +585,7 @@
           'COBALT_BOX_DUMP_ENABLED',
           'COBALT_BUILD_TYPE_DEBUG',
           'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+          'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
           '_DEBUG',
           'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
           'ENABLE_DEBUG_C_VAL',
@@ -449,6 +611,7 @@
           'ALLOCATOR_STATS_TRACKING',
           'COBALT_BUILD_TYPE_DEVEL',
           'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+          'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
           'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
           'ENABLE_DEBUG_C_VAL',
           'ENABLE_DEBUG_CONSOLE',
@@ -472,6 +635,7 @@
           'ALLOCATOR_STATS_TRACKING',
           'COBALT_BUILD_TYPE_QA',
           'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+          'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
           'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
           'ENABLE_DEBUG_C_VAL',
           'ENABLE_DEBUG_CONSOLE',
@@ -506,8 +670,10 @@
 
   # For configurations other than Gold, set the flag that lets test data files
   # be copied and carried along with the build.
+  # Clients must copy over all content; to avoid having to copy over extra data, we
+  # omit the test data
   'conditions': [
-    ['cobalt_config != "gold"', {
+    ['cobalt_config != "gold" and cobalt_enable_lib == 0', {
       'variables' : {
         'cobalt_copy_debug_console': 1,
         'cobalt_copy_test_data': 1,
diff --git a/src/cobalt/build/config/starboard.py b/src/cobalt/build/config/starboard.py
index 799c9f9..562c166 100644
--- a/src/cobalt/build/config/starboard.py
+++ b/src/cobalt/build/config/starboard.py
@@ -67,7 +67,7 @@
         'use_asan': use_asan,
         # Whether to build with clang's Thread Sanitizer instrumentation.
         'use_tsan': use_tsan,
-        # Whether to emable VR.
+        # Whether to enable VR.
         'enable_vr': vr_enabled,
     }
     return variables
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index 698d9b7..69180fb 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -17,6 +17,7 @@
 import argparse
 import logging
 import os
+import shlex
 import sys
 import textwrap
 
@@ -48,6 +49,7 @@
   host_os_names = {
       'linux2': 'linux',
       'linux3': 'linux',
+      'darwin': 'linux',
       'win32': 'win',
   }
 
@@ -162,6 +164,21 @@
     variables.update(self.platform_config.GetVariables(config_name))
     _AppendVariables(variables, args)
 
+    # Add the symbolizer path for ASAN to allow translation of callstacks.
+    asan_symbolizer_path = ''
+    if variables.get('use_asan', 0) == 1:
+      compiler_commandline = shlex.split(os.environ.get('CC', ''))
+      for path in compiler_commandline:
+        if os.path.isfile(path):
+          test_path = os.path.join(os.path.dirname(path), 'llvm-symbolizer')
+          if os.path.isfile(test_path):
+            asan_symbolizer_path = test_path
+            break
+    asan_variables = {
+      'asan_symbolizer_path': asan_symbolizer_path,
+    }
+    _AppendVariables(asan_variables, args)
+
     # Add config specific generator variables.
     generator_config = '{}_{}'.format(self.options.platform, config_name)
 
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index fa38e7d..3f1ac0b 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -28,8 +28,8 @@
 from cobalt.tools import paths
 from starboard.tools import platform
 
-_CLANG_REVISION = '264915-1'
-_CLANG_VERSION = '3.9.0'
+_CLANG_REVISION = '298539-1'
+_CLANG_VERSION = '5.0.0'
 
 _SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
 
@@ -71,7 +71,7 @@
   try:
     revinfo_cmd = ['gclient', 'revinfo', '-a']
 
-    if sys.platform.startswith('linux'):
+    if sys.platform.startswith('linux') or sys.platform == 'darwin':
       use_shell = False
     else:
       # Windows needs shell to find gclient in the PATH.
diff --git a/src/cobalt/content/fonts/10megabytes/fonts.xml b/src/cobalt/content/fonts/10megabytes/fonts.xml
deleted file mode 100644
index 2305fbf..0000000
--- a/src/cobalt/content/fonts/10megabytes/fonts.xml
+++ /dev/null
@@ -1,217 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<familyset version="1">
-<!--
-    Fallback fonts are chosen based on a match: full BCP-47 language tag
-    including script, then just language, and finally order (the first font
-    containing the glyph). Order of appearance is also the tiebreaker for weight
-    matching.
-
-    NOTE: When the optional family attribute "fallback" is included for a
-    family, then it dictates whether or not that family is added to the fallback
-    list. However, when it is not included, then the lack of a "name" attribute
-    causes the family to be added to the fallback list.
-
-    The pages attribute indicates which character pages are contained within
-    the font. It is used with character fallback to allow the system to quickly
-    determine that a character cannot appear in a font without requiring the
-    full character map to be loaded into memory. Character pages are zero
-    indexed, and each page contains 256 characters, so character 1000 would be
-    contained within page 3.
--->
-    <!-- first family is default -->
-    <family name="sans-serif">
-        <font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.ttf</font>
-    </family>
-    <!-- Note that aliases must come after the fonts they reference. -->
-    <alias name="arial" to="sans-serif" />
-    <alias name="helvetica" to="sans-serif" />
-    <alias name="roboto" to="sans-serif" />
-    <alias name="tahoma" to="sans-serif" />
-    <alias name="verdana" to="sans-serif" />
-    <alias name="courier" to="serif-monospace" />
-    <alias name="courier new" to="serif-monospace" />
-    <!-- fallback fonts -->
-    <family fallback="true" name="Noto Naskh Arabic UI" pages="0,6-8,32,37,46,251-254">
-        <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,18-19,45,171,254">
-        <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,5,32,37,251,254">
-        <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,2-3,14,32,37,254">
-        <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,5,251,254">
-        <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,5,16,45,254">
-        <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,2,9,28,32,34,37,168,254">
-        <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
-    </family>
-    <!-- Gujarati should come after Devanagari -->
-    <family fallback="true" pages="0,9-10,32,34,37,168,254">
-        <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
-    </family>
-    <!-- Gurmukhi should come after Devanagari -->
-    <family fallback="true" pages="0,9-10,32,34,37-38,168,254">
-        <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,11,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,3,9,13,32,34,37,254">
-        <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,32,34,37,254">
-        <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,12,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,12,32,34,37,254">
-        <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,11,32,34,37,254">
-        <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,13,32,34,37,254">
-        <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,23,25,32,37">
-        <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,3,14,32,37">
-        <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,6-7,32,37,253-254">
-        <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,3,170">
-        <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,27,32,37,254">
-        <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,166,254,360-362">
-        <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,27,254">
-        <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,26,32,37,169,254">
-        <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0-3,20-22,24,254">
-        <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,19,254">
-        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,3,29,37,44,254">
-        <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,44,254">
-        <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,32,37,169,254">
-        <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,169,254">
-        <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,28,37,254">
-        <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,25,254">
-        <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,2,164,254">
-        <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,6,8,254">
-        <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,170-171,254">
-        <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,25,254">
-        <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,6-7,32,46,253-254">
-        <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,28,254">
-        <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,169,254">
-        <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,32,37,168,254">
-        <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,27-28,254">
-        <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,9,32,37,168,254">
-        <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,3,6-7,32,34,37-38,254">
-        <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,26,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,32,37,167,170,254">
-        <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,15,32,37,254">
-        <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,3,32,45,254">
-        <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,165-166,254">
-        <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,48,160-164,254-255">
-        <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="32-43,497-498">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
-    </family>
-    <family fallback="true" lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
-        <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
-    </family>
-    <family fallback="true" pages="0,32-33,35-39,41,43,48,50,254,496-502,4068,4072">
-        <font weight="400" style="normal">NotoEmoji-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="35,37-39,43,496,498">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
-    </family>
-    <family fallback="true" pages="0,14,17,32,48-51,77-159,172-215,249-250,254-255,260">
-        <font weight="400" style="normal">DroidSansFallback.ttf</font>
-    </family>
-    <!--
-        Tai Le and Mongolian are intentionally kept last, to make sure they don't override
-        the East Asian punctuation for Chinese.
-    -->
-    <family fallback="true" pages="0,3,16,25,48,254">
-        <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
-    </family>
-    <family fallback="true" pages="0,24,32,36-37,48,254">
-        <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
-    </family>
-</familyset>
diff --git a/src/cobalt/content/fonts/README.md b/src/cobalt/content/fonts/README.md
index e3bada1..3bd0d89 100644
--- a/src/cobalt/content/fonts/README.md
+++ b/src/cobalt/content/fonts/README.md
@@ -1,32 +1,207 @@
 ## Description
 
-This directory contains a few different set of font packages one can select
-to be packaged with cobalt.
+This directory contains fonts that can be packaged with Cobalt.
 
 ## How to use
 
 To use this:
 
-1.  Select one of the profiles below.
+1.  Select one of the packages below.
 2.  Add a variable named `cobalt_font_package` in your platform's
-`gyp_configuration.gypi` file.
+    `gyp_configuration.gypi` file.
+3.  Optional: Add package overrides, which can be used to add or remove fonts
+    from the package.
 
 Example:
+    This example uses the 'limited' package, but overrides it to include bold
+    non-CJK language fallback and to not include any CJK language fallback.
 
     'variables': {
-        'cobalt_font_package': '10megabytes',
+        'cobalt_font_package': 'limited',
+        'cobalt_font_package_override_fallback_lang_non_cjk': 2,
+        'cobalt_font_package_override_fallback_lang_cjk_low_quality': 0,
     }
 
+## Packages
+*  'expanded' -- The largest package. It includes everything in the 'standard'
+                 package, along with 'bold' weight CJK. It is recommended that
+                 'local_font_cache_size_in_bytes' be increased to 24MB when
+                 using this package to account for the extra memory required by
+                 bold CJK. This package is ~46.2MB.
+
+                 Package category values:
+                   'package_named_sans_serif': 4,
+                   'package_named_serif': 4,
+                   'package_named_fcc_fonts': 2,
+                   'package_fallback_lang_non_cjk': 2,
+                   'package_fallback_lang_cjk': 2,
+                   'package_fallback_lang_cjk_low_quality': 0,
+                   'package_fallback_lang_jp': 0,
+                   'package_fallback_emoji': 1,
+                   'package_fallback_symbols': 1,
+
+*  'standard' -- The default package. It includes all non-CJK fallback fonts in
+                 both 'normal' and 'bold' weights, 'normal' weight CJK ('bold'
+                 weight CJK is synthesized from it), and all FCC fonts. This
+                 package is ~26.9MB.
+
+                 Package category values:
+                  'package_named_sans_serif': 3,
+                  'package_named_serif': 3,
+                  'package_named_fcc_fonts': 2,
+                  'package_fallback_lang_non_cjk': 2,
+                  'package_fallback_lang_cjk': 1,
+                  'package_fallback_lang_cjk_low_quality': 0,
+                  'package_fallback_lang_jp': 0,
+                  'package_fallback_emoji': 1,
+                  'package_fallback_symbols': 1,
+
+*  'limited_with_jp' -- A significantly smaller package than 'standard'. This
+                 package removes the 'bold' weighted non-CJK fallback fonts (the
+                 'normal' weight is still included and is used to synthesize
+                 bold), removes the FCC fonts (which must be downloaded from the
+                 web), and replaces standard CJK with low quality CJK. However,
+                 higher quality Japanese is still included. Because low quality
+                 CJK cannot synthesize bold, bold glyphs are unavailable in
+                 Chinese and Korean. This package is ~10.9MB.
+
+                 Package category values:
+                  'package_named_sans_serif': 2,
+                  'package_named_serif': 0,
+                  'package_named_fcc_fonts': 0,
+                  'package_fallback_lang_non_cjk': 1,
+                  'package_fallback_lang_cjk': 0,
+                  'package_fallback_lang_cjk_low_quality': 1,
+                  'package_fallback_lang_jp': 1,
+                  'package_fallback_emoji': 1,
+                  'package_fallback_symbols': 1,
+
+*  'limited'  -- A smaller package than 'limited_with_jp'. The two packages are
+                 identical with the exception that 'limited' does not include
+                 the higher quality Japanese font; instead it relies on low
+                 quality CJK for all CJK characters. Because low quality CJK
+                 cannot synthesize bold, bold glyphs are unavailable in Chinese,
+                 Japanese, and Korean. This package is ~7.7MB.
+
+                 Package category values:
+                  'package_named_sans_serif': 2,
+                  'package_named_serif': 0,
+                  'package_named_fcc_fonts': 0,
+                  'package_fallback_lang_non_cjk': 1,
+                  'package_fallback_lang_cjk': 0,
+                  'package_fallback_lang_cjk_low_quality': 1,
+                  'package_fallback_lang_jp': 0,
+                  'package_fallback_emoji': 1,
+                  'package_fallback_symbols': 1,
+
+*  'minimal'  -- The smallest possible font package. It only includes Roboto's
+                 Basic Latin characters. Everything else must be downloaded from
+                 the web. This package is ~16.4KB.
+
+                 Package category values:
+                  'package_named_sans_serif': 0,
+                  'package_named_serif': 0,
+                  'package_named_fcc_fonts': 0,
+                  'package_fallback_lang_non_cjk': 0,
+                  'package_fallback_lang_cjk': 0,
+                  'package_fallback_lang_cjk_low_quality': 0,
+                  'package_fallback_lang_jp': 0,
+                  'package_fallback_emoji': 0,
+                  'package_fallback_symbols': 0,
+
+NOTE: When bold is needed, but unavailable, it is typically synthesized,
+      resulting in lower quality glyphs than those generated directly from a
+      bold font. However, this does not occur with low quality CJK, which is
+      not high enough quality to synthesize. Its glyphs always have a regular
+      weight.
 
 
-## Profiles
+## Package font categories
+Each package contains values for the following categories, which specifies the
+fonts from each category included within the package:
+  *  'package_named_sans_serif':
+       Named sans-serif fonts.
 
-1.  `unlimited`: This font set is preferred, and default.  This will enable the
-use the fonts with highest quality and coverage, without the network latency of
-fetching fonts from the server.
-2.  `10megabytes`: Use this set of fonts if the target space allocated for fonts
-is approximately 10 megabytes.  This directory contains DroidSansFallback, which
-will render many Chinese, and Korean characters at lower quality.  The benefit
-of using this font is space savings at the cost of reduced quality.
-3.  `minimal`: Use this if minimizing space is a goal, and Cobalt should rely
-on web fonts.
+  *  'package_named_serif':
+       Named serif fonts.
+
+  *  'package_named_fcc_fonts':
+       All FCC-required fonts that are not included within sans-serif or serif:
+       monospace, serif-monospace, casual, cursive, and sans-serif-smallcaps.
+
+  *  'package_fallback_lang_non_cjk':
+       All non-CJK language-specific fallback fonts.
+
+  *  'package_fallback_lang_cjk':
+       Higher quality CJK language-specific fallback fonts.
+
+  *  'package_fallback_lang_cjk_low_quality':
+       Low quality CJK language-specific fallback fonts. These should only be
+       included when 'package_fallback_lang_cjk' has a value of '0'. This is the
+       only category of fonts that is not synthetically boldable.
+
+  *  'package_fallback_lang_jp':
+       Higher quality Japanese language-specific fallback fonts. These should
+       only be included when 'package_fallback_lang_cjk' has a value of '0'.
+
+  *  'package_fallback_emoji':
+       Emoji-related fallback fonts.
+
+  *  'package_fallback_symbols':
+       Symbol-related fallback fonts.
+
+
+## Package font category values
+The following explains the meaning behind the values that packages use for each
+of the font categories:
+  *  0 -- No fonts from the specified category are included.
+  *  1 -- Fonts from the specified category with a weight of 'normal' and a
+          style of 'normal' are included.
+  *  2 -- Fonts from the the specified category with a weight of either 'normal'
+          or 'bold' and a style of 'normal' are included.
+  *  3 -- Fonts from the the specified category with a weight of either 'normal'
+          or 'bold' and a style of either 'normal' or 'italic' are included.
+  *  4 -- All available fonts from the specified category are included. This may
+          include additional weights beyond 'normal' and 'bold'.
+
+
+## Overriding packages
+Font package overrides can be used to modify the files included within the
+selected package. The following override values are available for each font
+category:
+  * -1 -- The package value for the specified type is not overridden.
+  *  0 -- The package value is overridden and no fonts from the specified
+          category are included.
+  *  1 -- The package value is overridden and fonts from the specified category
+          with a weight of 'normal' and a style of 'normal' are included.
+  *  2 -- The package value is overridden and fonts from the specified category
+          with a weight of either 'normal' or bold' and a style of 'normal' are
+          included.
+  *  3 -- The package value is overridden and fonts from the specified category
+          with a weight of either 'normal' or 'bold' and a style of either
+          'normal' or 'italic' are included.
+  *  4 -- The package value is overridden and all available fonts from the
+          specified category are included. This may include additional weights
+          beyond 'normal' and 'bold'.
+
+
+## Override mappings
+The mapping between the override category name and the package category name:
+  *  'cobalt_font_package_override_named_sans_serif' ==>
+       'package_named_sans_serif'
+  *  'cobalt_font_package_override_named_serif' ==>
+       'package_named_serif'
+  *  'cobalt_font_package_override_named_fcc_fonts' ==>
+       'package_named_fcc_fonts'
+  *  'cobalt_font_package_override_fallback_lang_non_cjk' ==>
+       'package_fallback_lang_non_cjk'
+  *  'cobalt_font_package_override_fallback_lang_cjk' ==>
+       'package_fallback_lang_cjk'
+  *  'cobalt_font_package_override_fallback_lang_cjk_low_quality' ==>
+       'package_fallback_lang_cjk_low_quality'
+  *  'cobalt_font_package_override_fallback_lang_jp' ==>
+       'package_fallback_lang_jp'
+  *  'cobalt_font_package_override_fallback_emoji' ==>
+       'package_fallback_emoji'
+  *  'cobalt_font_package_override_fallback_symbols' ==>
+       'package_fallback_symbols'
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKR-Regular.otf b/src/cobalt/content/fonts/all_fonts/NotoSansKR-Regular.otf
deleted file mode 100644
index 5d5b429..0000000
--- a/src/cobalt/content/fonts/all_fonts/NotoSansKR-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSC-Regular.otf b/src/cobalt/content/fonts/all_fonts/NotoSansSC-Regular.otf
deleted file mode 100644
index 20f40ce..0000000
--- a/src/cobalt/content/fonts/all_fonts/NotoSansSC-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTC-Regular.otf b/src/cobalt/content/fonts/all_fonts/NotoSansTC-Regular.otf
deleted file mode 100644
index e7d7c8f..0000000
--- a/src/cobalt/content/fonts/all_fonts/NotoSansTC-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Black.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Black.ttf
deleted file mode 100644
index e8a835a..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Black.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-BlackItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-BlackItalic.ttf
deleted file mode 100644
index 49bc496..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-BlackItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Light.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Light.ttf
deleted file mode 100644
index e50ce97..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Light.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-LightItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-LightItalic.ttf
deleted file mode 100644
index a2bfefe..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-LightItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Medium.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Medium.ttf
deleted file mode 100644
index f144b6d..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Medium.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-MediumItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-MediumItalic.ttf
deleted file mode 100644
index e6e8319..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-MediumItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Thin.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Thin.ttf
deleted file mode 100644
index 77b470e..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Thin.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-ThinItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-ThinItalic.ttf
deleted file mode 100644
index 5562612..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-ThinItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/CarroisGothicSC-Regular.ttf b/src/cobalt/content/fonts/font_files/CarroisGothicSC-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/CarroisGothicSC-Regular.ttf
rename to src/cobalt/content/fonts/font_files/CarroisGothicSC-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/ComingSoon.ttf b/src/cobalt/content/fonts/font_files/ComingSoon.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/ComingSoon.ttf
rename to src/cobalt/content/fonts/font_files/ComingSoon.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/CutiveMono.ttf b/src/cobalt/content/fonts/font_files/CutiveMono.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/CutiveMono.ttf
rename to src/cobalt/content/fonts/font_files/CutiveMono.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DancingScript-Bold.ttf b/src/cobalt/content/fonts/font_files/DancingScript-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DancingScript-Bold.ttf
rename to src/cobalt/content/fonts/font_files/DancingScript-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DancingScript-Regular.ttf b/src/cobalt/content/fonts/font_files/DancingScript-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DancingScript-Regular.ttf
rename to src/cobalt/content/fonts/font_files/DancingScript-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DroidSansFallback.ttf b/src/cobalt/content/fonts/font_files/DroidSansFallback.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DroidSansFallback.ttf
rename to src/cobalt/content/fonts/font_files/DroidSansFallback.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DroidSansMono.ttf b/src/cobalt/content/fonts/font_files/DroidSansMono.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DroidSansMono.ttf
rename to src/cobalt/content/fonts/font_files/DroidSansMono.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoEmoji-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoEmoji-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoEmoji-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoEmoji-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansArmenian-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansArmenian-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansArmenian-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansArmenian-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBalinese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBalinese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBalinese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBalinese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBamum-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBamum-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBamum-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBamum-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBatak-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBatak-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBatak-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBatak-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBuginese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBuginese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBuginese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBuginese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBuhid-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBuhid-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBuhid-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBuhid-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/NotoSansCJK-Bold.ttc b/src/cobalt/content/fonts/font_files/NotoSansCJK-Bold.ttc
new file mode 100644
index 0000000..09707f9
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/NotoSansCJK-Bold.ttc
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/NotoSansCJK-Regular.ttc b/src/cobalt/content/fonts/font_files/NotoSansCJK-Regular.ttc
new file mode 100644
index 0000000..d9a2262
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/NotoSansCJK-Regular.ttc
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCanadianAboriginal-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCanadianAboriginal-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCanadianAboriginal-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCanadianAboriginal-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCham-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansCham-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCham-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCham-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCham-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCham-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCham-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCham-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCherokee-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCherokee-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCherokee-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCherokee-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCoptic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCoptic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCoptic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCoptic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansEthiopic-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansEthiopic-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansEthiopic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansEthiopic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansGeorgian-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGeorgian-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGeorgian-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGeorgian-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGlagolitic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGlagolitic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGlagolitic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGlagolitic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansHanunoo-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansHanunoo-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansHanunoo-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansHanunoo-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansHebrew-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansHebrew-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansHebrew-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansHebrew-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansJP-Regular.otf b/src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansJP-Regular.otf
rename to src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansJavanese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansJavanese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansJavanese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansJavanese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKayahLi-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansKayahLi-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKayahLi-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKayahLi-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansLaoUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLaoUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLaoUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLaoUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLepcha-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLepcha-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLepcha-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLepcha-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLimbu-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLimbu-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLimbu-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLimbu-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLisu-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLisu-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLisu-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLisu-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMandaic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMandaic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMandaic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMandaic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMeeteiMayek-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMeeteiMayek-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMeeteiMayek-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMeeteiMayek-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMongolian-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMongolian-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMongolian-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMongolian-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansNKo-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansNKo-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansNKo-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansNKo-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansNewTaiLue-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansNewTaiLue-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansNewTaiLue-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansNewTaiLue-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansOlChiki-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansOlChiki-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansOlChiki-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansOlChiki-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansRejang-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansRejang-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansRejang-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansRejang-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSaurashtra-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSaurashtra-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSaurashtra-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSaurashtra-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansSinhala-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSinhala-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSinhala-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSinhala-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSundanese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSundanese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSundanese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSundanese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSylotiNagri-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSylotiNagri-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSylotiNagri-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSylotiNagri-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted.ttf b/src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted2.ttf b/src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted2.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted2.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted2.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSyriacEstrangela-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSyriacEstrangela-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSyriacEstrangela-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSyriacEstrangela-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTagbanwa-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTagbanwa-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTagbanwa-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTagbanwa-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTaiLe-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTaiLe-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTaiLe-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTaiLe-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTaiTham-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTaiTham-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTaiTham-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTaiTham-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTaiViet-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTaiViet-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTaiViet-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTaiViet-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansTamilUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTamilUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTamilUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTamilUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaana-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaana-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaana-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaana-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaana-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaana-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaana-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaana-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaiUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaiUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaiUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaiUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansTibetan-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTibetan-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTibetan-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTibetan-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTifinagh-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTifinagh-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTifinagh-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTifinagh-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansVai-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansVai-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansVai-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansVai-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansYi-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansYi-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansYi-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansYi-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-BoldItalic.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-BoldItalic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-BoldItalic.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-BoldItalic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-Italic.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-Italic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-Italic.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-Italic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Bold.ttf b/src/cobalt/content/fonts/font_files/Roboto-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Bold.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-BoldItalic.ttf b/src/cobalt/content/fonts/font_files/Roboto-BoldItalic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-BoldItalic.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Italic.ttf b/src/cobalt/content/fonts/font_files/Roboto-Italic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Italic.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Italic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Regular-Subsetted.ttf b/src/cobalt/content/fonts/font_files/Roboto-Regular-Subsetted.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Regular-Subsetted.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Regular-Subsetted.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Regular.ttf b/src/cobalt/content/fonts/font_files/Roboto-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Regular.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/minimal/fonts.xml b/src/cobalt/content/fonts/minimal/fonts.xml
deleted file mode 100644
index c261fc2..0000000
--- a/src/cobalt/content/fonts/minimal/fonts.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<familyset version="1">
-<!--
-    Fallback fonts are chosen based on a match: full BCP-47 language tag
-    including script, then just language, and finally order (the first font
-    containing the glyph). Order of appearance is also the tiebreaker for weight
-    matching.
-
-    NOTE: When the optional family attribute "fallback" is included for a
-    family, then it dictates whether or not that family is added to the fallback
-    list. However, when it is not included, then the lack of a "name" attribute
-    causes the family to be added to the fallback list.
-
-    The pages attribute indicates which character pages are contained within
-    the font. It is used with character fallback to allow the system to quickly
-    determine that a character cannot appear in a font without requiring the
-    full character map to be loaded into memory. Character pages are zero
-    indexed, and each page contains 256 characters, so character 1000 would be
-    contained within page 3.
--->
-    <!-- first family is default -->
-    <family>
-        <font weight="400" style="normal">Roboto-Regular-Subsetted.ttf</font>
-    </family>
-</familyset>
diff --git a/src/cobalt/content/fonts/android_system/fonts.xml b/src/cobalt/content/fonts/xml/android/fonts.xml
similarity index 89%
rename from src/cobalt/content/fonts/android_system/fonts.xml
rename to src/cobalt/content/fonts/xml/android/fonts.xml
index 55219d9..6a714c9 100644
--- a/src/cobalt/content/fonts/android_system/fonts.xml
+++ b/src/cobalt/content/fonts/xml/android/fonts.xml
@@ -10,27 +10,23 @@
     list. However, when it is not included, then the lack of a "name" attribute
     causes the family to be added to the fallback list.
 
-    The pages attribute indicates which character pages are contained within
-    the font. It is used with character fallback to allow the system to quickly
-    determine that a character cannot appear in a font without requiring the
-    full character map to be loaded into memory. Character pages are zero
-    indexed, and each page contains 256 characters, so character 1000 would be
-    contained within page 3.
+    The optional family attribute "pages" indicates which character pages are
+    contained within the family. It is used with character fallback to allow the
+    system to quickly determine that a character cannot appear in a font without
+    requiring the full character map to be loaded into memory. Character pages
+    are zero indexed, and each page contains 256 characters, so character 1000
+    would be contained within page 3.
+
+    The optional font attribute "disable_synthetic_bolding" prevents the font
+    from being synthetically bolded. By default synthetic bolding is done when
+    there is no bold font available and a bold weight (>500) is specified.
 -->
     <!-- first family is default -->
     <family name="sans-serif">
-        <font weight="100" style="normal" font_name="Roboto Thin" postscript_name="Roboto-Thin">Roboto-Thin.ttf</font>
-        <font weight="100" style="italic" font_name="Roboto Thin Italic" postscript_name="Roboto-Thin-Italic">Roboto-ThinItalic.ttf</font>
-        <font weight="300" style="normal" font_name="Roboto Light" postscript_name="Roboto-Light">Roboto-Light.ttf</font>
-        <font weight="300" style="italic" font_name="Roboto Light Italic" postscript_name="Roboto-Light-Italic">Roboto-LightItalic.ttf</font>
         <font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.ttf</font>
         <font weight="400" style="italic" font_name="Roboto Regular Italic" postscript_name="Roboto-Italic">Roboto-Italic.ttf</font>
-        <font weight="500" style="normal" font_name="Roboto Medium" postscript_name="Roboto-Medium">Roboto-Medium.ttf</font>
-        <font weight="500" style="italic" font_name="Roboto Medium Italic" postscript_name="Roboto-Medium-Italic">Roboto-MediumItalic.ttf</font>
         <font weight="700" style="normal" font_name="Roboto Bold" postscript_name="Roboto-Bold">Roboto-Bold.ttf</font>
         <font weight="700" style="italic" font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic">Roboto-BoldItalic.ttf</font>
-        <font weight="900" style="normal" font_name="Roboto Black" postscript_name="Roboto-Black">Roboto-Black.ttf</font>
-        <font weight="900" style="italic" font_name="Roboto Black Italic" postscript_name="Roboto-Black-Italic">Roboto-BlackItalic.ttf</font>
     </family>
     <!-- Note that aliases must come after the fonts they reference. -->
     <alias name="arial" to="sans-serif" />
@@ -38,14 +34,11 @@
     <alias name="roboto" to="sans-serif" />
     <alias name="tahoma" to="sans-serif" />
     <alias name="verdana" to="sans-serif" />
-    <!-- This font should only be used if there are no other fonts in our final
-         image. To ensure this, it has no name and fallback is set to false. -->
-    <family fallback="false">
+    <!-- This is the default font when Roboto is unavailable. -->
+    <family fallback="true" pages="0">
         <font weight="400" style="normal">Roboto-Regular-Subsetted.ttf</font>
     </family>
     <family name="sans-serif-condensed">
-        <font weight="300" style="normal" font_name="Roboto Condensed Light" postscript_name="Roboto-Condensed-Light">RobotoCondensed-Light.ttf</font>
-        <font weight="300" style="italic" font_name="Roboto Condensed Light Italic" postscript_name="Roboto-Condensed-Light-Italic">RobotoCondensed-LightItalic.ttf</font>
         <font weight="400" style="normal" font_name="Roboto Condensed Regular" postscript_name="Roboto-Condensed-Regular">RobotoCondensed-Regular.ttf</font>
         <font weight="400" style="italic" font_name="Roboto Condensed Regular Italic" postscript_name="Roboto-Condensed-Regular-Italic">RobotoCondensed-Italic.ttf</font>
         <font weight="700" style="normal" font_name="Roboto Condensed Bold" postscript_name="Roboto-Condensed-Bold">RobotoCondensed-Bold.ttf</font>
@@ -312,8 +305,13 @@
     <family fallback="true" pages="35,37-39,43,496,498">
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
     </family>
+    <!--
+        Synthetic bolding is explicitly disabled for DroidSansFallback. The
+        quality of its synthetically bolded glyphs are not good enough to meet
+        Cobalt's standards.
+    -->
     <family fallback="true" pages="0,14,17,32,48-159,172-215,249-250,254-255,260">
-        <font weight="400" style="normal">DroidSansFallback.ttf</font>
+        <font weight="400" style="normal" disable_synthetic_bolding="true">DroidSansFallback.ttf</font>
     </family>
     <family fallback="true" lang="ja" pages="0,2-4,32-38,48,50-51,78-159,249-250,255">
         <font weight="400" style="normal">MTLmr3m.ttf</font>
diff --git a/src/cobalt/content/fonts/unlimited/fonts.xml b/src/cobalt/content/fonts/xml/common/fonts.xml
similarity index 85%
rename from src/cobalt/content/fonts/unlimited/fonts.xml
rename to src/cobalt/content/fonts/xml/common/fonts.xml
index 6385638..2ff5097 100644
--- a/src/cobalt/content/fonts/unlimited/fonts.xml
+++ b/src/cobalt/content/fonts/xml/common/fonts.xml
@@ -10,27 +10,23 @@
     list. However, when it is not included, then the lack of a "name" attribute
     causes the family to be added to the fallback list.
 
-    The pages attribute indicates which character pages are contained within
-    the font. It is used with character fallback to allow the system to quickly
-    determine that a character cannot appear in a font without requiring the
-    full character map to be loaded into memory. Character pages are zero
-    indexed, and each page contains 256 characters, so character 1000 would be
-    contained within page 3.
+    The optional family attribute "pages" indicates which character pages are
+    contained within the family. It is used with character fallback to allow the
+    system to quickly determine that a character cannot appear in a font without
+    requiring the full character map to be loaded into memory. Character pages
+    are zero indexed, and each page contains 256 characters, so character 1000
+    would be contained within page 3.
+
+    The optional font attribute "disable_synthetic_bolding" prevents the font
+    from being synthetically bolded. By default synthetic bolding is done when
+    there is no bold font available and a bold weight (>500) is specified.
 -->
     <!-- first family is default -->
     <family name="sans-serif">
-        <font weight="100" style="normal" font_name="Roboto Thin" postscript_name="Roboto-Thin">Roboto-Thin.ttf</font>
-        <font weight="100" style="italic" font_name="Roboto Thin Italic" postscript_name="Roboto-Thin-Italic">Roboto-ThinItalic.ttf</font>
-        <font weight="300" style="normal" font_name="Roboto Light" postscript_name="Roboto-Light">Roboto-Light.ttf</font>
-        <font weight="300" style="italic" font_name="Roboto Light Italic" postscript_name="Roboto-Light-Italic">Roboto-LightItalic.ttf</font>
         <font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.ttf</font>
         <font weight="400" style="italic" font_name="Roboto Regular Italic" postscript_name="Roboto-Italic">Roboto-Italic.ttf</font>
-        <font weight="500" style="normal" font_name="Roboto Medium" postscript_name="Roboto-Medium">Roboto-Medium.ttf</font>
-        <font weight="500" style="italic" font_name="Roboto Medium Italic" postscript_name="Roboto-Medium-Italic">Roboto-MediumItalic.ttf</font>
         <font weight="700" style="normal" font_name="Roboto Bold" postscript_name="Roboto-Bold">Roboto-Bold.ttf</font>
         <font weight="700" style="italic" font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic">Roboto-BoldItalic.ttf</font>
-        <font weight="900" style="normal" font_name="Roboto Black" postscript_name="Roboto-Black">Roboto-Black.ttf</font>
-        <font weight="900" style="italic" font_name="Roboto Black Italic" postscript_name="Roboto-Black-Italic">Roboto-BlackItalic.ttf</font>
     </family>
     <!-- Note that aliases must come after the fonts they reference. -->
     <alias name="arial" to="sans-serif" />
@@ -38,6 +34,10 @@
     <alias name="roboto" to="sans-serif" />
     <alias name="tahoma" to="sans-serif" />
     <alias name="verdana" to="sans-serif" />
+    <!-- This is the default font when Roboto is unavailable. -->
+    <family fallback="true" pages="0">
+        <font weight="400" style="normal">Roboto-Regular-Subsetted.ttf</font>
+    </family>
     <family name="serif">
         <font weight="400" style="normal" font_name="Noto Serif" postscript_name="NotoSerif">NotoSerif-Regular.ttf</font>
         <font weight="400" style="italic" font_name="Noto Serif Italic" postscript_name="NotoSerif-Italic">NotoSerif-Italic.ttf</font>
@@ -259,18 +259,21 @@
     <family fallback="true" pages="32-43,497-498">
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
-    <family fallback="true" lang="zh-Hans" pages="0,2,32-39,41,43,46-159,249-250,254-255,497-498,512-513,518,524,531-533,553,565,572,574,577,584-586,597,602,606,610,612,614-615,617,619-620,632,639,644,646-647,651-652,654,662,664,671,679-682,687,689,691-696,698-702,704-718">
-        <font weight="400" style="normal">NotoSansSC-Regular.otf</font>
+    <family fallback="true" lang="zh-Hans" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+        <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
     </family>
-    <family fallback="true" lang="zh-Hant" pages="0,32,34,46-48,50,52-159,249-250,254-255,498,512-657,660-661,663-678,685,760-761">
-        <font weight="400" style="normal">NotoSansTC-Regular.otf</font>
+    <family fallback="true" lang="zh-Hant" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+        <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
+    </family>
+    <family fallback="true" lang="ja" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+        <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
+    </family>
+    <family fallback="true" lang="ko" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+        <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
     </family>
     <family fallback="true" lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
         <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
     </family>
-    <family fallback="true" lang="ko" pages="0,17,32,48-50,169,172-215,255">
-        <font weight="400" style="normal">NotoSansKR-Regular.otf</font>
-    </family>
     <family fallback="true" pages="0,32-33,35-39,41,43,48,50,254,496-502,4068,4072">
         <font weight="400" style="normal">NotoEmoji-Regular.ttf</font>
     </family>
@@ -278,6 +281,14 @@
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
     </family>
     <!--
+        Synthetic bolding is explicitly disabled for DroidSansFallback. The
+        quality of its synthetically bolded glyphs are not good enough to meet
+        Cobalt's standards.
+    -->
+    <family fallback="true" pages="0,14,17,32,48-51,77-159,172-215,249-250,254-255,260">
+        <font weight="400" style="normal" disable_synthetic_bolding="true">DroidSansFallback.ttf</font>
+    </family>
+    <!--
         Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
     -->
@@ -285,6 +296,6 @@
         <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,24,32,36-37,48,254">
-        <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+        <font weight="400" style="normal" >NotoSansMongolian-Regular.ttf</font>
     </family>
 </familyset>
diff --git a/src/cobalt/csp/csp.gyp b/src/cobalt/csp/csp.gyp
index 87ace1d..a1e9144 100644
--- a/src/cobalt/csp/csp.gyp
+++ b/src/cobalt/csp/csp.gyp
@@ -41,6 +41,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/network/network.gyp:network',
         '<(DEPTH)/net/net.gyp:net',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
       ],
diff --git a/src/cobalt/csp/source_list.cc b/src/cobalt/csp/source_list.cc
index 33f3fe7..1f6fcd1 100644
--- a/src/cobalt/csp/source_list.cc
+++ b/src/cobalt/csp/source_list.cc
@@ -14,14 +14,22 @@
 
 #include "cobalt/csp/source_list.h"
 
+#include <algorithm>
+
 #include "base/base64.h"
 #include "base/string_number_conversions.h"
+#include "cobalt/network/socket_address_parser.h"
+#include "googleurl/src/url_canon_ip.h"
+#include "googleurl/src/url_constants.h"
 #include "net/base/escape.h"
+#include "net/base/net_util.h"
+#include "starboard/socket.h"
 
 namespace cobalt {
 namespace csp {
 
 namespace {
+
 bool IsSourceListNone(const char* begin, const char* end) {
   SkipWhile<IsAsciiWhitespace>(&begin, end);
 
@@ -56,9 +64,14 @@
            url.SchemeIs("filesystem"));
 }
 
+bool IsInsecureScheme(const GURL& url) {
+  return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kWsScheme);
+}
+
 }  // namespace
 
-SourceList::SourceList(ContentSecurityPolicy* policy,
+SourceList::SourceList(const LocalNetworkCheckerInterface* checker,
+                       ContentSecurityPolicy* policy,
                        const std::string& directive_name)
     : policy_(policy),
       directive_name_(directive_name),
@@ -66,7 +79,11 @@
       allow_star_(false),
       allow_inline_(false),
       allow_eval_(false),
-      hash_algorithms_used_(0) {}
+      allow_insecure_connections_to_local_network_(false),
+      allow_insecure_connections_to_localhost_(false),
+      allow_insecure_connections_to_private_range_(false),
+      hash_algorithms_used_(0),
+      local_network_checker_(checker) {}
 
 bool SourceList::Matches(
     const GURL& url,
@@ -85,6 +102,59 @@
     }
   }
 
+  if (!url.is_valid() || url.is_empty()) {
+    return false;
+  }
+  // Since url is valid, we can now use possibly_invalid_spec() below.
+  const std::string& valid_spec = url.possibly_invalid_spec();
+
+  const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
+
+  if (!url.has_host() || !parsed.host.is_valid() ||
+      !parsed.host.is_nonempty()) {
+    return false;
+  }
+
+  if (!IsInsecureScheme(url)) {
+    return false;
+  }
+
+  if (allow_insecure_connections_to_localhost_) {
+    std::string host;
+#if SB_HAS(IPV6)
+    host = url.HostNoBrackets();
+#else
+    host.append(valid_spec.c_str() + parsed.host.begin,
+                valid_spec.c_str() + parsed.host.begin + parsed.host.len);
+#endif
+    if (net::IsLocalhost(host)) {
+      return true;
+    }
+  }
+
+  /* allow mixed content for local network or private ranges? */
+  if (!(allow_insecure_connections_to_local_network_ ||
+        allow_insecure_connections_to_private_range_)) {
+    return false;
+  }
+
+  SbSocketAddress destination;
+  // Note that GetDestinationForHost will only pass if host is a numeric IP.
+  if (!cobalt::network::ParseSocketAddress(valid_spec.c_str(), parsed.host,
+                                           &destination)) {
+    return false;
+  }
+
+  if (allow_insecure_connections_to_private_range_ &&
+      local_network_checker_->IsIPInPrivateRange(destination)) {
+    return true;
+  }
+
+  if (allow_insecure_connections_to_local_network_ &&
+      local_network_checker_->IsIPInLocalNetwork(destination)) {
+    return true;
+  }
+
   return false;
 }
 
@@ -181,6 +251,21 @@
     return true;
   }
 
+  if (directive_name_.compare(ContentSecurityPolicy::kConnectSrc) == 0) {
+    if (LowerCaseEqualsASCII(begin, end, "'cobalt-insecure-localhost'")) {
+      AddSourceLocalhost();
+      return true;
+    }
+    if (LowerCaseEqualsASCII(begin, end, "'cobalt-insecure-local-network'")) {
+      AddSourceLocalNetwork();
+      return true;
+    }
+    if (LowerCaseEqualsASCII(begin, end, "'cobalt-insecure-private-range'")) {
+      AddSourcePrivateRange();
+      return true;
+    }
+  }
+
   std::string nonce;
   if (!ParseNonce(begin, end, &nonce)) {
     return false;
@@ -504,6 +589,18 @@
   return ok;
 }
 
+void SourceList::AddSourceLocalhost() {
+  allow_insecure_connections_to_localhost_ = true;
+}
+
+void SourceList::AddSourceLocalNetwork() {
+  allow_insecure_connections_to_local_network_ = true;
+}
+
+void SourceList::AddSourcePrivateRange() {
+  allow_insecure_connections_to_private_range_ = true;
+}
+
 void SourceList::AddSourceSelf() { allow_self_ = true; }
 
 void SourceList::AddSourceStar() { allow_star_ = true; }
diff --git a/src/cobalt/csp/source_list.h b/src/cobalt/csp/source_list.h
index c1e906f..f3b3d9c 100644
--- a/src/cobalt/csp/source_list.h
+++ b/src/cobalt/csp/source_list.h
@@ -20,9 +20,12 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
 #include "base/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
 #include "cobalt/csp/content_security_policy.h"
 #include "cobalt/csp/source.h"
+#include "cobalt/network/local_network.h"
 #include "googleurl/src/gurl.h"
 
 namespace cobalt {
@@ -30,7 +33,17 @@
 
 class SourceList {
  public:
-  SourceList(ContentSecurityPolicy* policy, const std::string& directive_name);
+  struct LocalNetworkCheckerInterface {
+    virtual bool IsIPInLocalNetwork(
+        const SbSocketAddress& destination) const = 0;
+    virtual bool IsIPInPrivateRange(
+        const SbSocketAddress& destination) const = 0;
+
+    virtual ~LocalNetworkCheckerInterface() {}
+  };
+
+  SourceList(const LocalNetworkCheckerInterface* checker,
+             ContentSecurityPolicy* policy, const std::string& directive_name);
   void Parse(const base::StringPiece& begin);
 
   bool Matches(const GURL& url,
@@ -56,6 +69,9 @@
   bool ParseHash(const char* begin, const char* end, DigestValue* hash,
                  HashAlgorithm* hash_algorithm);
 
+  void AddSourceLocalhost();
+  void AddSourceLocalNetwork();
+  void AddSourcePrivateRange();
   void AddSourceSelf();
   void AddSourceStar();
   void AddSourceUnsafeInline();
@@ -70,12 +86,19 @@
   bool allow_star_;
   bool allow_inline_;
   bool allow_eval_;
+  bool allow_insecure_connections_to_local_network_;
+  bool allow_insecure_connections_to_localhost_;
+  bool allow_insecure_connections_to_private_range_;
   base::hash_set<std::string> nonces_;
   // TODO: This is a hash_set in blink. Need to implement
   // a hash for HashValue.
   std::set<HashValue> hashes_;
   uint8 hash_algorithms_used_;
 
+  const LocalNetworkCheckerInterface* local_network_checker_;
+
+  FRIEND_TEST_ALL_PREFIXES(SourceListTest, TestInsecureLocalNetworkDefault);
+
   DISALLOW_COPY_AND_ASSIGN(SourceList);
 };
 
diff --git a/src/cobalt/csp/source_list_directive.cc b/src/cobalt/csp/source_list_directive.cc
index bc67e2a..532f969 100644
--- a/src/cobalt/csp/source_list_directive.cc
+++ b/src/cobalt/csp/source_list_directive.cc
@@ -17,10 +17,21 @@
 namespace cobalt {
 namespace csp {
 
+bool SourceListDirective::LocalNetworkChecker::IsIPInLocalNetwork(
+    const SbSocketAddress& destination) const {
+  return ::cobalt::network::IsIPInLocalNetwork(destination);
+}
+
+bool SourceListDirective::LocalNetworkChecker::IsIPInPrivateRange(
+    const SbSocketAddress& destination) const {
+  return ::cobalt::network::IsIPInPrivateRange(destination);
+}
+
 SourceListDirective::SourceListDirective(const std::string& name,
                                          const std::string& value,
                                          ContentSecurityPolicy* policy)
-    : Directive(name, value, policy), source_list_(policy, name) {
+    : Directive(name, value, policy),
+      source_list_(&local_network_checker_, policy, name) {
   source_list_.Parse(base::StringPiece(value));
 }
 
diff --git a/src/cobalt/csp/source_list_directive.h b/src/cobalt/csp/source_list_directive.h
index 117003d..ecb3656 100644
--- a/src/cobalt/csp/source_list_directive.h
+++ b/src/cobalt/csp/source_list_directive.h
@@ -26,6 +26,12 @@
 
 class SourceListDirective : public Directive {
  public:
+  class LocalNetworkChecker : public SourceList::LocalNetworkCheckerInterface {
+   public:
+    bool IsIPInLocalNetwork(const SbSocketAddress& destination) const OVERRIDE;
+    bool IsIPInPrivateRange(const SbSocketAddress& destination) const OVERRIDE;
+  };
+
   SourceListDirective(const std::string& name, const std::string& value,
                       ContentSecurityPolicy* policy);
 
@@ -39,6 +45,8 @@
   uint8 hash_algorithms_used() const;
 
  private:
+  // Note: Ensure |local_network_checker_| is initialized before |source_list_|.
+  LocalNetworkChecker local_network_checker_;
   SourceList source_list_;
   DISALLOW_COPY_AND_ASSIGN(SourceListDirective);
 };
diff --git a/src/cobalt/csp/source_list_test.cc b/src/cobalt/csp/source_list_test.cc
index d4ddf1f..febdbce 100644
--- a/src/cobalt/csp/source_list_test.cc
+++ b/src/cobalt/csp/source_list_test.cc
@@ -14,15 +14,30 @@
 
 #include "cobalt/csp/source_list.h"
 
+#include "base/memory/scoped_ptr.h"
 #include "cobalt/csp/content_security_policy.h"
 #include "cobalt/csp/source.h"
+#include "cobalt/network/local_network.h"
 #include "googleurl/src/gurl.h"
+#include "net/base/net_util.h"
+#include "starboard/memory.h"
+#include "starboard/socket.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace csp {
 
-namespace {
+using ::testing::_;
+using ::testing::Return;
+
+class MockLocalNetworkChecker
+    : public SourceList::LocalNetworkCheckerInterface {
+ public:
+  MOCK_CONST_METHOD1(IsIPInLocalNetwork, bool(const SbSocketAddress&));
+  MOCK_CONST_METHOD1(IsIPInPrivateRange, bool(const SbSocketAddress&));
+};
+
 void ParseSourceList(SourceList* source_list, const std::string& sources) {
   base::StringPiece characters(sources);
   source_list->Parse(characters);
@@ -36,12 +51,13 @@
   }
 
   scoped_ptr<ContentSecurityPolicy> csp_;
+  MockLocalNetworkChecker checker_;
   ViolationCallback violation_callback_;
 };
 
 TEST_F(SourceListTest, BasicMatchingNone) {
   std::string sources = "'none'";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_FALSE(source_list.Matches(GURL("http://example.com/")));
@@ -50,7 +66,7 @@
 
 TEST_F(SourceListTest, BasicMatchingStar) {
   std::string sources = "*";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_TRUE(source_list.Matches(GURL("http://example.com/")));
@@ -66,7 +82,7 @@
 
 TEST_F(SourceListTest, BasicMatchingSelf) {
   std::string sources = "'self'";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_FALSE(source_list.Matches(GURL("http://example.com/")));
@@ -76,7 +92,7 @@
 
 TEST_F(SourceListTest, BlobMatchingSelf) {
   std::string sources = "'self'";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_TRUE(source_list.Matches(GURL("https://example.test/")));
@@ -89,7 +105,7 @@
 
 TEST_F(SourceListTest, BlobMatchingBlob) {
   std::string sources = "blob:";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_FALSE(source_list.Matches(GURL("https://example.test/")));
@@ -98,7 +114,7 @@
 
 TEST_F(SourceListTest, BasicMatching) {
   std::string sources = "http://example1.com:8000/foo/ https://example2.com/";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_TRUE(source_list.Matches(GURL("http://example1.com:8000/foo/")));
@@ -115,7 +131,7 @@
 TEST_F(SourceListTest, WildcardMatching) {
   std::string sources =
       "http://example1.com:*/foo/ https://*.example2.com/bar/ http://*.test/";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_TRUE(source_list.Matches(GURL("http://example1.com/foo/")));
@@ -141,7 +157,7 @@
 
 TEST_F(SourceListTest, RedirectMatching) {
   std::string sources = "http://example1.com/foo/ http://example2.com/bar/";
-  SourceList source_list(csp_.get(), "script-src");
+  SourceList source_list(&checker_, csp_.get(), "script-src");
   ParseSourceList(&source_list, sources);
 
   EXPECT_TRUE(source_list.Matches(GURL("http://example1.com/foo/"),
@@ -161,7 +177,353 @@
                                    ContentSecurityPolicy::kDidRedirect));
 }
 
-}  // namespace
+TEST_F(SourceListTest, TestInsecureLocalhostDefaultInsecureV4) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://locaLHost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://locaLHost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost.localdomain/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost.locaLDomain/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost.localdomain./")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.1:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.0.1.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.1.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.255/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.0.255.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.255.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://example.localhost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://example.localhost:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://example.localhost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://example.locaLHost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://example.locaLHost./")));
+
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost.locaLDomain/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.1.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.1.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.255/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.255.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.255.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost./")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalhostDefaultInsecureV6) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost6/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost6:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost6./")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost6.localdomain6/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost6.localdomain6:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://localhost6.localdomain6./")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[::1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[::1]:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]:80/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6/")));
+  EXPECT_FALSE(
+      source_list.Matches(GURL("https://localhost6.localdomain6:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[::1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[::1]:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]:80/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecureLocalhostInsecureV4) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  std::string sources = "'cobalt-insecure-localhost'";
+  ParseSourceList(&source_list, sources);
+
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost:80/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://locaLHost/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost.localdomain/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost.locaLDomain/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.0.0.1/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.0.0.1:80/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.0.1.0/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.1.0.0/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.0.0.255/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.0.255.0/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://127.255.0.0/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalhostInsecureV6) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  std::string sources = "'cobalt-insecure-localhost'";
+  ParseSourceList(&source_list, sources);
+
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost6/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost6:80/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost6.localdomain6/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://localhost6.localdomain6:80/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://[::1]/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://[::1]:80/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]:80/")));
+  EXPECT_TRUE(source_list.Matches(
+      GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+  EXPECT_TRUE(source_list.Matches(
+      GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecureLocalhostSecureV4) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  std::string sources = "'cobalt-insecure-localhost'";
+  ParseSourceList(&source_list, sources);
+
+  // Per CA/Browser forum, issuance of internal names is now prohibited.
+  // See: https://cabforum.org/internal-names/
+  // But, test it anyway.
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost.locaLDomain/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.1.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.1.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.255/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.255.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.255.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost./")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalhostSecureV6) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  std::string sources = "'cobalt-insecure-localhost'";
+  ParseSourceList(&source_list, sources);
+
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6/")));
+  EXPECT_FALSE(
+      source_list.Matches(GURL("https://localhost6.localdomain6:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6./")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[::1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[::1]:80/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]:80/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeDefaultV4) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+  // These test fail by default, since cobalt-insecure-private-range is not set.
+  EXPECT_FALSE(source_list.Matches(GURL("http://10.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://172.16.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://192.168.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://0.0.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://255.255.255.255/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://10.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://172.16.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://192.168.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://0.0.0.0/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://255.255.255.255/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecurePrivateRangeDefaultV6) {
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+  // These test fail by default, since cobalt-insecure-private-range is not set.
+  EXPECT_FALSE(source_list.Matches(GURL("http://[fd00::]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[fd00:1:2:3:4:5::]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[fd00::]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[fd00:1:2:3:4:5::]/")));
+
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[2606:2800:220:1:248:1893:25c8:1946]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[FE80::]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[FE80::]/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV4Private) {
+  std::string sources = "'cobalt-insecure-private-range'";
+
+  EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(true));
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_TRUE(source_list.Matches(GURL("http://10.0.0.1/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://172.16.1.1/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://192.168.1.1/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV4NotPrivate) {
+  std::string sources = "'cobalt-insecure-private-range'";
+  EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(false));
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_FALSE(source_list.Matches(GURL("http://255.255.255.255/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://0.0.0.0/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV4Secure) {
+  // These are secure calls.
+  std::string sources = "'cobalt-insecure-private-range'";
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_FALSE(source_list.Matches(GURL("https://10.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://172.16.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://192.168.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://255.255.255.255/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://0.0.0.0/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecurePrivateRangeV6ULA) {
+  std::string sources = "'cobalt-insecure-private-range'";
+  // These are insecure calls.
+  EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(true));
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_TRUE(source_list.Matches(GURL("http://[fd00::]/")));
+  EXPECT_TRUE(source_list.Matches(GURL("http://[fd00:1:2:3:4:5::]/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV6NotULA) {
+  std::string sources = "'cobalt-insecure-private-range'";
+  EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(false));
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_FALSE(source_list.Matches(GURL("http://[2620::]/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV6Secure) {
+  std::string sources = "'cobalt-insecure-private-range'";
+  // These are secure calls.
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_FALSE(source_list.Matches(GURL("https://[fd00::]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[fd00:1:2:3:4:5::]/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[2606:2800:220:1:248:1893:25c8:1946]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[FE80::]/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV4Local) {
+  std::string sources = "'cobalt-insecure-local-network'";
+
+  // These are insecure calls.
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(false));
+  ParseSourceList(&source_list, sources);
+  EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://172.16.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://143.195.170.2/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV4NotLocal) {
+  std::string sources = "'cobalt-insecure-local-network'";
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(true));
+
+  EXPECT_TRUE(source_list.Matches(GURL("http://143.195.170.1/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV4Secure) {
+  std::string sources = "'cobalt-insecure-local-network'";
+
+  // These are secure calls.
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+  EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://172.16.1.1/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://143.195.170.2/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://143.195.170.1/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV6Local) {
+  std::string sources = "'cobalt-insecure-local-network'";
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+  EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(true));
+
+  ParseSourceList(&source_list, sources);
+
+  // These are insecure calls.
+  EXPECT_TRUE(source_list.Matches(
+      GURL("http://[2606:2800:220:1:248:1893:25c8:1946]/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV6NotLocal) {
+  std::string sources = "'cobalt-insecure-local-network'";
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+  EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(false));
+
+  ParseSourceList(&source_list, sources);
+
+  // These are insecure calls.
+  EXPECT_FALSE(source_list.Matches(GURL("http://[2606:1:2:3:4::1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("http://[::1]/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV6Secure) {
+  std::string sources = "'cobalt-insecure-local-network'";
+
+  // These are secure calls.
+  SourceList source_list(&checker_, csp_.get(), "connect-src");
+  ParseSourceList(&source_list, sources);
+
+  EXPECT_FALSE(source_list.Matches(GURL("https://[::1]/")));
+  EXPECT_FALSE(source_list.Matches(GURL("https://[2606:1:2:3:4::1]/")));
+  EXPECT_FALSE(source_list.Matches(
+      GURL("https://[2606:2800:220:1:248:1893:25c8:1946]/")));
+}
+#endif
 
 }  // namespace csp
 }  // namespace cobalt
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index bb25ee0..86a9ca0 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -6480,18 +6480,22 @@
   | kPropertyValueEntryPointToken maybe_whitespace maybe_declaration {
     scoped_ptr<PropertyDeclaration> property_declaration($3);
     if (property_declaration != NULL) {
-      DCHECK_EQ(1, property_declaration->property_values.size()) <<
-          "Cannot parse shorthand properties as single property values.";
-      if (property_declaration->is_important) {
-        parser_impl->LogWarning(
-          @1, "!important is not allowed when setting single property values.");
+      if (property_declaration->property_values.size() != 1) {
+        parser_impl->LogError(
+            @1, "Cannot parse shorthand properties as single property values.");
       } else {
-        if (!property_declaration->property_values[0].value) {
+        if (property_declaration->is_important) {
           parser_impl->LogWarning(
-            @1, "declaration must have a property value.");
+            @1,
+            "!important is not allowed when setting single property values.");
         } else {
-          parser_impl->set_property_value(
-              property_declaration->property_values[0].value);
+          if (!property_declaration->property_values[0].value) {
+            parser_impl->LogWarning(
+              @1, "declaration must have a property value.");
+          } else {
+            parser_impl->set_property_value(
+                property_declaration->property_values[0].value);
+          }
         }
       }
     }
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index 2991fe6..e51b2d0 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -8402,8 +8402,9 @@
   EXPECT_EQ(cssom::MapToMeshFunction::kUrls,
             mtm_function->mesh_spec().mesh_type());
 
-  EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
-  EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+  EXPECT_EQ(static_cast<float>(M_PI),
+            mtm_function->horizontal_fov_in_radians());
+  EXPECT_EQ(1.5f, mtm_function->vertical_fov_in_radians());
 
   EXPECT_EQ(mtm_function->stereo_mode()->value(),
             cssom::KeywordValue::kMonoscopic);
@@ -8434,8 +8435,9 @@
   EXPECT_EQ(cssom::MapToMeshFunction::kEquirectangular,
             mtm_function->mesh_spec().mesh_type());
 
-  EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
-  EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+  EXPECT_EQ(static_cast<float>(M_PI),
+            mtm_function->horizontal_fov_in_radians());
+  EXPECT_EQ(1.5f, mtm_function->vertical_fov_in_radians());
 
   EXPECT_EQ(mtm_function->stereo_mode()->value(),
             cssom::KeywordValue::kMonoscopic);
@@ -8462,8 +8464,9 @@
       dynamic_cast<const cssom::MapToMeshFunction*>(filter_list->value()[0]);
   ASSERT_TRUE(mtm_function);
 
-  EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
-  EXPECT_EQ(1.2f, mtm_function->vertical_fov());
+  EXPECT_EQ(static_cast<float>(M_PI),
+            mtm_function->horizontal_fov_in_radians());
+  EXPECT_EQ(1.2f, mtm_function->vertical_fov_in_radians());
 
   EXPECT_EQ(mtm_function->stereo_mode()->value(),
             cssom::KeywordValue::kMonoscopic);
diff --git a/src/cobalt/cssom/css_computed_style_declaration_test.cc b/src/cobalt/cssom/css_computed_style_declaration_test.cc
index dc77b98..4eb82c3 100644
--- a/src/cobalt/cssom/css_computed_style_declaration_test.cc
+++ b/src/cobalt/cssom/css_computed_style_declaration_test.cc
@@ -17,7 +17,7 @@
 #include "cobalt/cssom/keyword_value.h"
 #include "cobalt/cssom/length_value.h"
 #include "cobalt/cssom/property_definitions.h"
-#include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/testing/fake_exception_state.h"
 #include "cobalt/script/exception_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -27,29 +27,7 @@
 
 using ::testing::_;
 using ::testing::Return;
-
-namespace {
-class FakeExceptionState : public script::ExceptionState {
- public:
-  void SetException(
-      const scoped_refptr<script::ScriptException>& exception) OVERRIDE {
-    dom_exception_ = make_scoped_refptr(
-        base::polymorphic_downcast<dom::DOMException*>(exception.get()));
-  }
-  void SetSimpleExceptionWithArgs(script::MessageType /*message_type*/,
-                                  int /*dummy*/, ...) OVERRIDE {
-    // no-op
-  }
-  dom::DOMException::ExceptionCode GetExceptionCode() {
-    return dom_exception_ ? static_cast<dom::DOMException::ExceptionCode>(
-                                dom_exception_->code())
-                          : dom::DOMException::kNone;
-  }
-
- private:
-  scoped_refptr<dom::DOMException> dom_exception_;
-};
-}  // namespace
+using dom::testing::FakeExceptionState;
 
 TEST(CSSComputedStyleDeclarationTest, CSSTextSetterRaisesException) {
   scoped_refptr<CSSComputedStyleDeclaration> style =
diff --git a/src/cobalt/cssom/map_to_mesh_function.cc b/src/cobalt/cssom/map_to_mesh_function.cc
index c27f867..800ffa6 100644
--- a/src/cobalt/cssom/map_to_mesh_function.cc
+++ b/src/cobalt/cssom/map_to_mesh_function.cc
@@ -44,8 +44,8 @@
     }
   }
 
-  result.append(base::StringPrintf(", %.7grad", horizontal_fov()));
-  result.append(base::StringPrintf(" %.7grad, ", vertical_fov()));
+  result.append(base::StringPrintf(", %.7grad", horizontal_fov_in_radians()));
+  result.append(base::StringPrintf(" %.7grad, ", vertical_fov_in_radians()));
 
   result.append("matrix3d(");
   for (int col = 0; col <= 3; ++col) {
@@ -107,8 +107,8 @@
 
 bool MapToMeshFunction::operator==(const MapToMeshFunction& rhs) const {
   if (mesh_spec().mesh_type() != rhs.mesh_spec().mesh_type() ||
-      horizontal_fov() != rhs.horizontal_fov() ||
-      horizontal_fov() != rhs.horizontal_fov() ||
+      horizontal_fov_in_radians() != rhs.horizontal_fov_in_radians() ||
+      vertical_fov_in_radians() != rhs.vertical_fov_in_radians() ||
       !stereo_mode()->Equals(*rhs.stereo_mode())) {
     return false;
   }
diff --git a/src/cobalt/cssom/map_to_mesh_function.h b/src/cobalt/cssom/map_to_mesh_function.h
index 974161c..21a7f67 100644
--- a/src/cobalt/cssom/map_to_mesh_function.h
+++ b/src/cobalt/cssom/map_to_mesh_function.h
@@ -98,12 +98,13 @@
     DISALLOW_COPY_AND_ASSIGN(MeshSpec);
   };
 
-  MapToMeshFunction(scoped_ptr<MeshSpec> mesh_spec, float horizontal_fov,
-                    float vertical_fov, const glm::mat4& transform,
+  MapToMeshFunction(scoped_ptr<MeshSpec> mesh_spec,
+                    float horizontal_fov_in_radians,
+                    float vertical_fov_in_radians, const glm::mat4& transform,
                     const scoped_refptr<KeywordValue>& stereo_mode)
       : mesh_spec_(mesh_spec.Pass()),
-        horizontal_fov_(horizontal_fov),
-        vertical_fov_(vertical_fov),
+        horizontal_fov_in_radians_(horizontal_fov_in_radians),
+        vertical_fov_in_radians_(vertical_fov_in_radians),
         transform_(transform),
         stereo_mode_(stereo_mode) {
     DCHECK(mesh_spec_);
@@ -113,25 +114,25 @@
   // Alternate constructor for mesh URL lists.
   MapToMeshFunction(const scoped_refptr<PropertyValue>& mesh_url,
                     ResolutionMatchedMeshListBuilder resolution_matched_meshes,
-                    float horizontal_fov, float vertical_fov,
-                    const glm::mat4& transform,
+                    float horizontal_fov_in_radians,
+                    float vertical_fov_in_radians, const glm::mat4& transform,
                     const scoped_refptr<KeywordValue>& stereo_mode)
       : mesh_spec_(
             new MeshSpec(kUrls, mesh_url, resolution_matched_meshes.Pass())),
-        horizontal_fov_(horizontal_fov),
-        vertical_fov_(vertical_fov),
+        horizontal_fov_in_radians_(horizontal_fov_in_radians),
+        vertical_fov_in_radians_(vertical_fov_in_radians),
         transform_(transform),
         stereo_mode_(stereo_mode) {
     DCHECK(stereo_mode_);
   }
 
   // Alternate constructor for built-in meshes.
-  MapToMeshFunction(MeshSpecType spec_type, float horizontal_fov,
-                    float vertical_fov, const glm::mat4& transform,
+  MapToMeshFunction(MeshSpecType spec_type, float horizontal_fov_in_radians,
+                    float vertical_fov_in_radians, const glm::mat4& transform,
                     const scoped_refptr<KeywordValue>& stereo_mode)
       : mesh_spec_(new MeshSpec(spec_type)),
-        horizontal_fov_(horizontal_fov),
-        vertical_fov_(vertical_fov),
+        horizontal_fov_in_radians_(horizontal_fov_in_radians),
+        vertical_fov_in_radians_(vertical_fov_in_radians),
         transform_(transform),
         stereo_mode_(stereo_mode) {
     DCHECK(stereo_mode_);
@@ -140,8 +141,8 @@
   ~MapToMeshFunction() OVERRIDE {}
 
   const MeshSpec& mesh_spec() const { return *mesh_spec_; }
-  float horizontal_fov() const { return horizontal_fov_; }
-  float vertical_fov() const { return vertical_fov_; }
+  float horizontal_fov_in_radians() const { return horizontal_fov_in_radians_; }
+  float vertical_fov_in_radians() const { return vertical_fov_in_radians_; }
   const glm::mat4& transform() const { return transform_; }
   const scoped_refptr<KeywordValue>& stereo_mode() const {
     return stereo_mode_;
@@ -158,8 +159,8 @@
 
  private:
   const scoped_ptr<MeshSpec> mesh_spec_;
-  const float horizontal_fov_;
-  const float vertical_fov_;
+  const float horizontal_fov_in_radians_;
+  const float vertical_fov_in_radians_;
   const glm::mat4 transform_;
   const scoped_refptr<KeywordValue> stereo_mode_;
 
diff --git a/src/cobalt/debug/debug_web_server.cc b/src/cobalt/debug/debug_web_server.cc
index 32711db..fac8535 100644
--- a/src/cobalt/debug/debug_web_server.cc
+++ b/src/cobalt/debug/debug_web_server.cc
@@ -89,11 +89,39 @@
       ip_addr.FromSockAddr(reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
   DCHECK(result);
 #elif defined(OS_STARBOARD)
+
+#if SB_API_VERSION >= 4
+  SbSocketAddress local_ip;
+  SbMemorySet(&(local_ip.address), 0, sizeof(local_ip.address));
+
+  bool result = false;
+
+  // Prefer IPv4 addresses, as they're easier to type for debugging.
+  SbSocketAddressType address_types[] = {kSbSocketAddressTypeIpv4,
+                                         kSbSocketAddressTypeIpv6};
+
+  for (std::size_t i = 0; i != SB_ARRAY_SIZE(address_types); ++i) {
+    SbSocketAddress destination;
+    SbMemorySet(&(destination.address), 0, sizeof(destination.address));
+    destination.type = address_types[i];
+    if (!SbSocketGetInterfaceAddress(&destination, &local_ip, NULL)) {
+      continue;
+    }
+
+    if (ip_addr.FromSbSocketAddress(&local_ip)) {
+      result = true;
+      break;
+    }
+  }
+
+  DCHECK(result);
+#else
   SbSocketAddress sb_address;
   bool result = SbSocketGetLocalInterfaceAddress(&sb_address);
   DCHECK(result);
   result = ip_addr.FromSbSocketAddress(&sb_address);
-  DCHECK(result);
+#endif  // SB_API_VERSION >= 4
+
 #else
 #error "Not Implemented."
 #endif
diff --git a/src/cobalt/doc/branching.md b/src/cobalt/doc/branching.md
new file mode 100644
index 0000000..ae41373
--- /dev/null
+++ b/src/cobalt/doc/branching.md
@@ -0,0 +1,82 @@
+# Cobalt Branching
+
+*(This document assumes you are already familiar
+with [Cobalt Versioning][versioning] practices.)*
+
+The Cobalt project uses git branches for two main purposes:
+
+  1. To solidify a release as it is hardened for production.
+  2. To isolate `master` branch developers from risky or disruptive work.
+
+
+## Release Branches
+
+A "Cobalt release" is an official, tested version of Cobalt that is intended to
+be deployable to production. We branch for releases to allow development to
+continue on the `master` branch while stabilizing and finalizing a set of sources
+for release.
+
+
+### Release Timeline
+
+  1. Feature work is done in the `master` branch.
+
+  2. Once all feature work is completed,
+     a [Release Number](versioning.md#Release-Number) is reserved and a branch
+     is created. The branch will be named `rc_<release-number>` to indicate that
+     the branch represents a release candidate for version `<release-number>`,
+     and not the final release.
+
+     An RC announcement will be made to [cobalt-dev@googlegroups.com][cobalt-dev].
+
+     It should be understood that RC branches are being stabilized and are
+     subject to changes.
+
+  3. As bugs are discovered and feedback received from partners, fixes will be
+     cherry-picked into the release candidate branch. Each update to
+     `rc_<release-number>` will have an
+     increasing [Build ID](versioning.md#Build-ID).
+
+  4. As time goes on, the number of cherry-picks will decrease in number and
+     scope.
+
+  5. Once the branch is deemed to be feature-complete and stable, it will be
+     **renamed** to `release_<release-number>`, representing an actual Cobalt
+     release. This means that the RC branch will be removed and the release
+     branch will permanently take its place.
+
+     A release announcement will be made
+     to [cobalt-dev@googlegroups.com][cobalt-dev].
+
+  6. The Cobalt project will strongly resist updating released versions, but may
+     need to back-port fixes from time to time. This is especially true if there
+     are any severe security flaws that need to be addressed. So cherry-picks
+     are still possible into `release_<release-number>` release branches.
+
+     In these rare cases, announcements will be made
+     to [cobalt-dev@googlegroups.com][cobalt-dev].
+
+
+## Work Branches
+
+If a set of work is deemed to be particularly risky or disruptive, or if a
+serious contributor wants a sandbox to prepare an extensive patch, a work branch
+may be created to facilitate such development.
+
+Work branch names are of the form `work_<topic>`, where `<topic>` is the purpose
+for which the branch was created. Work branches are generally in use by a
+specific and limited set of people, and may disappear at any time.
+
+
+## Capitalization, Branch Names, and You
+
+Some older branches followed an `ALL_CAPS_SNAKE_CASE` naming convention.  Going
+forward, branch names will follow a `lower_snake_case` naming convention.
+
+
+## Other Reading
+
+  * [Cobalt Versioning][versioning]
+
+[cobalt-dev]: https://groups.google.com/forum/#!forum/cobalt-dev "cobalt-dev@googlegroups.com"
+[versioning]: versioning.md "Cobalt Versioning"
diff --git a/src/cobalt/doc/performance_tuning.md b/src/cobalt/doc/performance_tuning.md
new file mode 100644
index 0000000..b4cfd88
--- /dev/null
+++ b/src/cobalt/doc/performance_tuning.md
@@ -0,0 +1,373 @@
+# Performance Tuning
+
+Cobalt is designed to choose sensible parameters for all performance-related
+options and parameters, however sometimes these need to be explicitly set
+to allow Cobalt to run optimally for a specific platform.  This document
+discusses some of the tweakable parameters in Cobalt that can have an
+affect on performance.
+
+A number of tweaks are listed below in no particular order.  Each item
+has a set of tags keywords to make it easy to search for items related
+to a specific type of performance metric (e.g. "framerate").
+
+Many of the tweaks involve adding a new gyp variable to your platform's
+`gyp_configuration.gypi` file.  The default values for these variables are
+defined in [`base.gypi`](../build/config/base.gypi).
+
+### Use a Release Build
+
+Cobalt has a number of different build configurations (e.g. "debug", "devel",
+"qa" and "gold" in slowest-to-fastest order), with varying degrees of
+optimizations enabled.  For example, while "devel" has compiler optimizations
+enabled, it does not disable DCHECKS (debug assertions) which can decrease
+Cobalt's performance.  The "qa" build is most similar to "gold", but it still
+has some debug features enabled (such as the debug console which can consume
+memory, and decrease performance while it is visible).  For the best
+performance, build Cobalt in the "gold" configuration.
+
+**Tags:** *framerate, startup, browse-to-watch, cpu memory, input latency.*
+
+
+### Framerate throttling
+
+If you're willing to accept a lower framerate, there is potential that
+JavaScript execution can be made to run faster (which can improve startup
+time, browse-to-watch time, and input latency).  Without any special
+settings in place, the renderer will attempt to render each frame as fast
+as it can, limited only by the display's refresh rate, which is usually 60Hz.
+By artificially throttling this rate to a lower value, like 30Hz, CPU
+resources can be freed to work on other tasks.  You can enable framerate
+throttling by setting a value for `cobalt_minimum_frame_time_in_milliseconds`
+in your platform's `gyp_configuration.gypi` file.  Setting it to 33, for
+example, will throttle Cobalt's renderer to 30 frames per second.
+
+**Tags:** *gyp_configuration.gypi, framerate, startup, browse-to-watch,
+           input latency.*
+
+
+### Image cache capacity
+
+Cobalt's image cache is used to cache decoded image data.  The image data
+in the image cache is stored as a texture, and so it will occupy GPU memory.
+The image cache capacity dictates how long images will be kept resident in
+memory even if they are not currently visible on the web page.  By reducing
+this value, you can lower GPU memory usage, at the cost of having Cobalt
+make more network requests and image decodes for previously seen images.
+Cobalt will automatically set the image cache capacity to a reasonable value,
+but if you wish to override this, you can do so by setting the
+`image_cache_size_in_bytes` variable in your `gyp_configuration.gypi` file.  For
+the YouTube web app, we have found that at 1080p, 32MB will allow around
+5 thumbnail shelves to stay resident at a time, with 720p and 4K resolutions
+using proportionally less and more memory, respectively.
+
+**Tags:** *gyp_configuration.gypi, cpu memory, gpu memory.*
+
+
+### Image cache capacity multiplier during video playback
+
+Cobalt provides a feature where the image cache capacity will be reduced
+as soon as video playback begins.  This can be useful for reducing peak
+GPU memory usage, which usually occurs during video playback.  The
+downside to lowering the image cache during video playback is that it
+may need to evict some images when the capacity changes, and so it is
+more likely that Cobalt will have to re-download and decode images after
+returning from video playback.  Note that this feature is not well tested.
+The feature can be activated by setting
+`image_cache_capacity_multiplier_when_playing_video` to a value between
+`0.0` and `1.0` in your `gyp_configuration.gypi` file.  The image cache
+capacity will be multiplied by this value during video playback.
+
+**Tags:** *gyp_configuration.gypi, gpu memory.*
+
+
+### Scratch Surface cache capacity
+
+This only affects GLES renderers.  While rasterizing a frame, it is
+occasionally necessary to render to a temporary offscreen surface and then
+apply that surface to the original render target.  Offscreen surface
+rendering may also need to be performed multiple times per frame.  The
+scratch surface cache will keep allocated a set of scratch textures that
+will be reused (within and across frames) for offscreen rendering.  Reusing
+offscreen surfaces allows render target allocations, which can be expensive
+on some platforms, to be minimized.  However, it has been found that some
+platforms (especially those with tiled renderers, like the Raspberry Pi's
+Broadcom VideoCore), reading and writing again and again to the same texture
+can result in performance degradation.  Memory may also be potentially saved
+by disabling this cache, since when it is enabled, if the cache is filled, it
+may be occupying memory that it is not currently using.  This setting can
+be adjusted by setting `surface_cache_size_in_bytes` in your
+`gyp_configuration.gypi` file.  A value of `0` will disable the surface cache.
+
+**Tags:** *gyp_configuration.gypi, gpu memory, framerate.*
+
+
+### Glyph atlas size
+
+This only affects GLES renderers.  Skia sets up glyph atlases to which
+it software rasterizes glyphs the first time they are encountered, and
+from which the glyphs are used as textures for hardware accelerated glyph
+rendering to the render target.  Adjusting this value will adjust
+GPU memory usage, but at the cost of performance as text glyphs will be
+less likely to be cached already.  Note that if experimenting with
+modifications to this setting, be sure to test many languages, as some
+are more demanding (e.g. Chinese and Japanese) on the glyph cache than
+others.  This value can be adjusted by changing the values of
+the `skia_glyph_atlas_width` and `skia_glyph_atlas_height` variables in your
+`gyp_configuration.gypi` file.  Note that by default, these will be
+automatically configured by Cobalt to values found to be optimal for
+the application's resolution.
+
+**Tags:** *gyp_configuration.gypi, gpu memory, input latency, framerate.*
+
+
+### Software surface cache capacity
+
+This only affects Starboard Blitter API renderers.  The Starboard Blitter API
+has only limited support for rendering special effects, so often Cobalt will
+have to fallback to a software rasterizer for rendering certain visual
+elements (most notably, text).  In order to avoid expensive software
+renders, the results are cached and re-used across frames.  The software
+surface cache is crucial to achieving an acceptable framerate on Blitter API
+platforms.  The size of this cache is specified by the
+`software_surface_cache_size_in_bytes` variable in `gyp_configuration.gypi`.
+
+**Tags:** *gyp_configuration.gypi, gpu memory, framerate.*
+
+
+### Toggle Just-In-Time JavaScript Compilation
+
+Just-in-time (JIT) compilation of JavaScript is well known to significantly
+improve the speed of JavaScript execution.  However, in the context of Cobalt
+and its web apps (like YouTube's HTML5 TV application), JITting may not be
+the best or fastest thing to do.  Enabling JIT can result in Cobalt using
+more memory (to store compiled code) and can also actually slow down
+JavaScript execution (e.g. time must now be spent compiling code).  It is
+recommended that JIT support be left disabled, but you can experiment with
+it by setting the `cobalt_enable_jit` `gyp_configuration.gypi` variable to `1`
+to enable JIT, or `0` to disable it.
+
+**Tags:** *gyp_configuration.gypi, startup, browse-to-watch, input latency,
+           cpu memory.*
+
+
+### Garbage collection trigger threshold
+
+The SpiderMonkey JavaScript engine provides a parameter that describes how
+aggressive it will be at performing garbage collections to reduce memory
+usage.  By lowering this value, garbage collection will occur more often,
+thus reducing performance, but memory usage will be lowered.  We have found
+that performance reductions are modest, so it is not unreasonable to set this
+value to something low like 1MB if your platform is low on memory.  This
+setting can be adjusted by setting the value of
+`mozjs_garbage_collection_threshold_in_bytes` in your `gyp_configuration.gypi`
+file.
+
+**Tags:** *gyp_configuration.gypi, startup, browse-to-watch, input latency,
+           cpu memory.*
+
+
+### Ensure that you are not requesting Cobalt to render unchanging frames
+
+Some platforms require that the display buffer is swapped frequently, and
+so in these cases Cobalt will render the scene every frame, even if it is
+not changing, which consumes CPU resources.  This behavior is defined by the
+value of `SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER` in your platform's
+`configuration_public.h` file.  Unless your platform is restricted in this
+aspect, you should ensure that `SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER`
+is set to `0`.
+
+**Tags:** *configuration_public.h, startup, browse-to-watch, input latency,
+           framerate.*
+
+
+### Ensure that Cobalt is rendering only regions that change
+
+Cobalt has logic to detect which part of the frame has been affected by
+animations and can be configured to only render to that region.  However,
+this feature requires support from the driver for GLES platforms.  In
+particular, `eglChooseConfig()` will first be called with
+`EGL_SWAP_BEHAVIOR_PRESERVED_BIT` set in its attribute list.  If this
+fails, Cobalt will call eglChooseConfig() again without
+`EGL_SWAP_BEHAVIOR_PRESERVED_BIT` set and dirty region rendering will
+be disabled.  By having Cobalt render only small parts of the screen,
+CPU (and GPU) resources can be freed to work on other tasks.  This can
+especially affect startup time since usually only a small part of the
+screen is updating (e.g. displaying an animated spinner).  Thus, if
+possible, ensure that your EGL/GLES driver supports
+`EGL_SWAP_BEHAVIOR_PRESERVED_BIT`.  Note that it is possible (but not
+necessary) that GLES drivers will implement this feature by allocating a new
+offscreen buffer, which can significantly affect GPU memory usage.
+
+**Tags:** *startup, framerate, gpu memory.*
+
+
+### Ensure that thread priorities are respected
+
+Cobalt makes use of thread priorities to ensure that animations remain smooth
+even while JavaScript is being executed, and to ensure that JavaScript is
+processed (e.g. in response to a key press) before images are decoded.  Thus
+having support for priorities can improve the overall performance of the
+application.  To enable thread priority support, you should set the value
+of `SB_HAS_THREAD_PRIORITY_SUPPORT` to `1` in your `configuration_public.h`
+file, and then also ensure that your platform's implementation of
+`SbThreadCreate()` properly forwards the priority parameter down to the
+platform.
+
+**Tags:** *configuration_public.h, framerate, startup, browse-to-watch,
+           input latency.*
+
+
+### Tweak compiler/linker optimization flags
+
+Huge performance improvements can be obtained by ensuring that the right
+optimizations are enabled by your compiler and linker flag settings.  You
+can set these up within `gyp_configuration.gypi` by adjusting the list
+variables `compiler_flags` and `linker_flags`.  See also
+`compiler_flags_gold` and `linker_flags_gold` which describe flags that
+apply only to gold builds where performance is critical.  Note that
+unless you explicitly set this up, it is unlikely that compiler/linker
+flags will carry over from external shell environment settings; they
+must be set explicitly in `gyp_configuration.gypi`.
+
+#### Link Time Optimization (LTO)
+If your toolchain supports it, it is recommended that you enable the LTO
+optimization, as it has been reported to yield significant performance
+improvements in many high profile projects.
+
+#### The GCC '-mplt' flag for MIPS architectures
+The '-mplt' flag has been found to improve all around performance by
+~20% on MIPS architecture platforms.  If your platform has a MIPS
+architecture, it is suggested that you enable this flag in gold builds.
+
+**Tags:** *gyp_configuration.gypi, framerate, startup, browse-to-watch,
+           input latency.*
+
+
+### Close "Stats for Nerds" when measuring performance
+
+The YouTube web app offers a feature called "Stats for Nerds" that enables
+a stats overlay to appear on the screen during video playback.  Rendering
+this overlay requires a significant amount of processing, so it is
+recommended that all performance evaluation is done without the
+"Stats for Nerds" overlay active.  This can greatly affect browse-to-watch
+time and potentially affect the video frame drop rate.
+
+**Tags:** *browse-to-watch, framerate, youtube.*
+
+
+### Close the debug console when measuring performance
+
+Cobalt provides a debug console in non-gold builds to allow the display
+of variables overlayed on top of the application.  This can be helpful
+for debugging issues and keeping track of things like app lifetime, but
+the debug console consumes significant resources when it is visible in order
+to render it, so it should be hidden when performance is being evaluated.
+
+**Tags:** *framerate, startup, browse-to-watch, input latency.*
+
+
+### Toggle between dlmalloc and system allocator
+
+Cobalt includes dlmalloc and can be configured to use it to handle all
+memory allocations.  It should be carefully evaluated however whether
+dlmalloc performs better or worse than your system allocator, in terms
+of both memory fragmentation efficiency as well as runtime performance.
+To use dlmalloc, you should adjust your starboard_platform.gyp file to
+use the Starboard [`starboard/memory.h`](../../starboard/memory.h) function
+implementations defined in
+[`starboard/shared/dlmalloc/`](../../starboard/shared/dlmalloc).  To use
+your system allocator, you should adjust your starboard_platform.gyp file
+to use the Starboard [`starboard/memory.h`](../../starboard/memory.h) function
+implementations defined in
+[`starboard/shared/iso/`](../../starboard/shared/iso).
+
+**Tags:** *framerate, startup, browse-to-watch, input latency, cpu memory.*
+
+
+### Media buffer allocation strategy
+
+During video playback, memory is reserved by Cobalt to contain the encoded
+media data (separated into video and audio), and we refer to this memory
+as the media buffers.  By default, Cobalt pre-allocates the memory and
+wraps it with a custom allocator, in order to avoid fragmentation of main
+memory.  However, depending on your platform and your system allocator,
+overall memory usage may improve if media buffer allocations were made
+normally via the system allocator instead.  TODO: Unfortunately, there is
+currently no flip to switch to toggle one method or the other, however
+we should look into this because switching to using the system allocator
+has been found to reduce memory usage by ~5MB.  In the meantime, it can
+be hacked by modifying
+[`cobalt/media/shell_media_platform_starboard.cc`](../media/shell_media_platform_starboard.cc).
+Note also that if you choose to pre-allocate memory, for 1080p video it has been
+found that 24MB is a good media buffer size.  The pre-allocated media buffer
+capacity size can be adjusted by modifying the value of
+`SB_MEDIA_MAIN_BUFFER_BUDGET` in your platform's `configuration_public.h` file.
+
+**Tags:** *configuration_public.h, cpu memory.*
+
+
+### Avoid using a the YouTube web app FPS counter (i.e. "?fps=1")
+
+The YouTube web app is able to display a Frames Per Second (FPS) counter in the
+corner when the URL parameter "fps=1" is set.  Unfortunately, activating this
+timer will cause Cobalt to re-layout and re-render the scene frequently in
+order to update the FPS counter.  Instead, we recommend instead to either
+measure the framerate in the GLES driver and periodically printing it, or
+hacking Cobalt to measure the framerate and periodically print it.  In order to
+hack in an FPS counter, you will want to look at the
+`HardwareRasterizer::Impl::Submit()` function in
+[`cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc`](../renderer/rasterizer/skia/hardware_rasterizer.cc).
+The work required to update the counter has the potential to affect many
+aspects of performance.  TODO: Cobalt should add a command line switch to
+enable printing of the framerate in gold builds.
+
+**Tags:** *framerate, startup, browse-to-watch, input latency,*
+
+
+### Implement hardware image decoding
+
+The Starboard header file [`starboard/image.h`](../../starboard/image.h) defines
+functions that allow platforms to implement hardware-accelerated image
+decoding, if available.  In particular, if `SbImageIsDecodeSupported()` returns
+true for the specified mime type and output format, then instead of using the
+software-based libpng or libjpeg libraries, Cobalt will instead call
+`SbImageDecode()`.  `SbImageDecode()` is expected to return a decoded image as
+a `SbDecodeTarget` option, from which Cobalt will extract a GL texture or
+Blitter API surface object when rendering.  If non-CPU hardware is used to
+decode images, it would alleviate the load on the CPU, and possibly also
+increase the speed at which images can be decoded.
+
+**Tags:** *startup, browse-to-watch, input latency.*
+
+
+### Use Chromium's about:tracing tool to debug Cobalt performance
+
+Cobalt has support for generating profiling data that is viewable through
+Chromium's about:tracing tool.  This feature is available in all Cobalt
+configurations except for "gold" ("qa" is the best build to use for performance
+investigations here). There are currently two ways to tell Cobalt
+to generate this data:
+
+1. The command line option, "--timed_trace=XX" will instruct Cobalt to trace
+   upon startup, for XX seconds (e.g. "--timed_trace=25").  When completed,
+   the output will be written to the file `timed_trace.json`.
+2. Using the debug console (hit CTRL+O on a keyboard once or twice), type in the
+   command "d.trace()" and hit enter.  Cobalt will begin a trace.  After
+   some time has passed (and presumably you have performed some actions), you
+   can open the debug console again and type "d.trace()" again to end the trace.
+   The trace output will be written to the file `triggered_trace.json`.
+
+The directory the output files will be placed within is the directory that the
+Starboard function `SbSystemGetPath()` returns with a `path_id` of
+`kSbSystemPathDebugOutputDirectory`, so you may need to check your
+implementation of `SbSystemGetPath()` to discover where this is.
+
+Once the trace file is created, it can be opened in Chrome by navigating to
+`about:tracing` or `chrome://tracing`, clicking the "Load" button near the top
+left, and then opening the JSON file created earlier.
+
+Of particular interest in the output view is the `MainWebModule` thread where
+JavaScript and layout are executed, and `Rasterizer` where per-frame rendering
+takes place.
+
+**Tags:** *framerate, startup, browse-to-watch, input latency.*
diff --git a/src/cobalt/doc/spherical_video.md b/src/cobalt/doc/spherical_video.md
new file mode 100644
index 0000000..2a5adf4
--- /dev/null
+++ b/src/cobalt/doc/spherical_video.md
@@ -0,0 +1,40 @@
+# Enabling Spherical Video in Cobalt
+
+Cobalt supports playback of 360 spherical videos.  Cobalt does not expose
+this support to web applications through WebGL (which is currently
+unimplemented in Cobalt), but rather through a custom `map-to-mesh` CSS
+filter and custom [`window.camera3D` Web API](../dom/camera_3d.idl). Support
+for spherical video in Cobalt requires a GLES rasterizer (i.e. it is not
+supported for the Starboard Blitter API), and Starboard platform support for
+the player
+[decode-to-texture output mode](../starboard/doc/howto_decode_to_texture.md).
+
+## Enabling spherical video support
+
+Spherical video support requires `map-to-mesh` support, which must be
+explicitly enabled in your Starboard platform's `gyp_configuration.gypi` file.
+
+In particular, your platform's `gyp_configuration.gypi` file should contain the
+line,
+
+```
+'enable_map_to_mesh': 1,
+```
+
+When `enable_map_to_mesh` is set to 1, Cobalt will make the `map-to-mesh`
+CSS filter parseable.  The web app can then detect whether the browser,
+Cobalt, supports spherical video by evaluating the following JavaScript:
+
+```
+function checkForMapToMeshSupport() {
+  return 'CSS' in window && 'supports' in window.CSS &&
+         CSS.supports(
+             'filter',
+             'map-to-mesh(url(p.msh), 100deg 60deg,' +
+                 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1),' +
+                 'monoscopic)');
+}
+```
+
+Finally, it is required that your platform provides
+[decode-to-texture support](../starboard/doc/howto_decode_to_texture.md).
diff --git a/src/cobalt/doc/versioning.md b/src/cobalt/doc/versioning.md
new file mode 100644
index 0000000..b945062
--- /dev/null
+++ b/src/cobalt/doc/versioning.md
@@ -0,0 +1,43 @@
+# Cobalt Versioning
+
+A Cobalt version consists of two components: The Release Number and the Build
+ID. Some real historical Cobalt version examples:
+
+  * `2.15147`
+  * `3.16138`
+  * `4.16134`
+  * `6.18971`
+
+You get the idea. The number before the dot is the "Release Number." The number
+after the dot is the "Build ID."
+
+## Release Number
+
+A "Cobalt release" is an official, tested version of Cobalt that is intended to
+be deployable to production. The "Release Number" is a single counting number,
+starting at "1" for our first release, and increasing by one for every release
+thereafter. This number is checked into [`src/cobalt/version.h`](../version.h),
+and represents **coarse** information about the state of the source tree when we
+decided to do a release.
+
+It is important to note that there are no point releases, or major or minor
+releases. Each release gets its own unique counting number.
+
+## Build ID
+
+The Cobalt Build ID represents **fine-grained** information about the state of
+the source tree for a given build. An internal Cobalt build server generates a
+monotonically increasing number for each unique set of sources that it
+sees. When an open-source release is published,
+a [`src/cobalt/build/build.id`](../build/build.id) file is included that
+specifies the build ID of that source release. The Cobalt team can reproduce the
+exact sources for a given Build ID.
+
+Note that since the Build ID always increases with time, it means that the
+latest version of an older Cobalt release can have a higher Build ID than the
+latest version of a new Cobalt release. An example from above: Cobalt `4.16134`
+was produced earlier than Cobalt `3.16138`, thus has a lower Build ID.
+
+## Other Reading
+
+  * [Cobalt Branching](branching.md)
diff --git a/src/cobalt/dom/array_buffer.cc b/src/cobalt/dom/array_buffer.cc
index 82046d1..9b2d29a 100644
--- a/src/cobalt/dom/array_buffer.cc
+++ b/src/cobalt/dom/array_buffer.cc
@@ -20,6 +20,7 @@
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/script/javascript_engine.h"
+#include "nb/memory_scope.h"
 
 namespace cobalt {
 namespace dom {
@@ -95,6 +96,7 @@
 
 void ArrayBuffer::Data::Initialize(script::EnvironmentSettings* settings,
                                    size_t size) {
+  TRACK_MEMORY_SCOPE("DOM");
   if (settings) {
     DOMSettings* dom_settings =
         base::polymorphic_downcast<dom::DOMSettings*>(settings);
@@ -143,6 +145,7 @@
 }
 
 void ArrayBuffer::Cache::TryToOffload() {
+  TRACK_MEMORY_SCOPE("DOM");
   if (total_size_in_main_memory_ <= maximum_size_in_main_memory_) {
     return;
   }
@@ -200,6 +203,7 @@
 
 scoped_refptr<ArrayBuffer> ArrayBuffer::Slice(
     script::EnvironmentSettings* settings, int start, int end) const {
+  TRACK_MEMORY_SCOPE("DOM");
   int clamped_start;
   int clamped_end;
   ClampRange(start, end, static_cast<int>(byte_length()), &clamped_start,
diff --git a/src/cobalt/dom/array_buffer_view.cc b/src/cobalt/dom/array_buffer_view.cc
index d3b0868..a9d35d2 100644
--- a/src/cobalt/dom/array_buffer_view.cc
+++ b/src/cobalt/dom/array_buffer_view.cc
@@ -17,6 +17,11 @@
 namespace cobalt {
 namespace dom {
 
+const char* ArrayBufferView::kWrongByteOffsetMultipleErrorFormat =
+    "Byte offset should be a multiple of %d.";
+const char* ArrayBufferView::kWrongByteLengthMultipleErrorFormat =
+    "Byte length should be a multiple of %d.";
+
 ArrayBufferView::ArrayBufferView(const scoped_refptr<ArrayBuffer>& buffer)
     : buffer_(buffer), byte_offset_(0), byte_length_(buffer->byte_length()) {}
 
diff --git a/src/cobalt/dom/array_buffer_view.h b/src/cobalt/dom/array_buffer_view.h
index 5bdb1b4..3db1a07 100644
--- a/src/cobalt/dom/array_buffer_view.h
+++ b/src/cobalt/dom/array_buffer_view.h
@@ -42,6 +42,9 @@
                   uint32 byte_length);
   ~ArrayBufferView();
 
+  static const char* kWrongByteOffsetMultipleErrorFormat;
+  static const char* kWrongByteLengthMultipleErrorFormat;
+
  private:
   scoped_refptr<ArrayBuffer> buffer_;
   uint32 byte_offset_;
diff --git a/src/cobalt/dom/blob.cc b/src/cobalt/dom/blob.cc
new file mode 100644
index 0000000..0a47116
--- /dev/null
+++ b/src/cobalt/dom/blob.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/blob.h"
+
+#include "base/lazy_instance.h"
+#include "cobalt/dom/blob_property_bag.h"
+
+namespace cobalt {
+namespace dom {
+
+namespace {
+
+const uint8* DataStart(const Blob::BlobPart& part) {
+  if (part.IsType<scoped_refptr<ArrayBuffer> >()) {
+    return part.AsType<scoped_refptr<ArrayBuffer> >()->data();
+  } else if (part.IsType<scoped_refptr<ArrayBufferView> >()) {
+    const scoped_refptr<ArrayBufferView>& view =
+        part.AsType<scoped_refptr<ArrayBufferView> >();
+    return view->buffer()->data() + view->byte_offset();
+  } else if (part.IsType<scoped_refptr<DataView> >()) {
+    const scoped_refptr<DataView>& view =
+        part.AsType<scoped_refptr<DataView> >();
+    return view->buffer()->data() + view->byte_offset();
+  } else if (part.IsType<scoped_refptr<Blob> >()) {
+    return part.AsType<scoped_refptr<Blob> >()->data();
+  }
+
+  return NULL;
+}
+
+uint64 DataLength(const Blob::BlobPart& part) {
+  if (part.IsType<scoped_refptr<ArrayBuffer> >()) {
+    return part.AsType<scoped_refptr<ArrayBuffer> >()->byte_length();
+  } else if (part.IsType<scoped_refptr<ArrayBufferView> >()) {
+    return part.AsType<scoped_refptr<ArrayBufferView> >()->byte_length();
+  } else if (part.IsType<scoped_refptr<DataView> >()) {
+    return part.AsType<scoped_refptr<DataView> >()->byte_length();
+  } else if (part.IsType<scoped_refptr<Blob> >()) {
+    return part.AsType<scoped_refptr<Blob> >()->size();
+  }
+
+  return 0;
+}
+
+base::LazyInstance<BlobPropertyBag> empty_blob_property_bag =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+Blob::Blob(script::EnvironmentSettings* settings,
+           const scoped_refptr<ArrayBuffer>& buffer) {
+  if (buffer) {
+    buffer_ = buffer->Slice(settings, 0);
+  } else {
+    buffer_ = new ArrayBuffer(settings, 0);
+  }
+}
+
+// TODO: Do the appropriate steps to convert the type member to lowercase ASCII
+// so the media type can be exposed and used, as described in:
+//    https://www.w3.org/TR/FileAPI/#constructorBlob
+Blob::Blob(script::EnvironmentSettings* settings,
+           script::Sequence<BlobPart> blobParts, const BlobPropertyBag& options)
+    : buffer_(new ArrayBuffer(settings, 0)), type_(options.type()) {
+  size_t byte_length = 0;
+  for (script::Sequence<BlobPart>::size_type i = 0; i < blobParts.size(); i++) {
+    byte_length += DataLength(blobParts.at(i));
+  }
+  buffer_ = new ArrayBuffer(settings, static_cast<uint32>(byte_length));
+
+  uint8* destination = buffer_->data();
+  size_t offset = 0;
+  for (script::Sequence<BlobPart>::size_type i = 0; i < blobParts.size(); i++) {
+    const uint8* source = DataStart(blobParts.at(i));
+    uint64 count = DataLength(blobParts.at(i));
+
+    std::copy(source, source + count, destination + offset);
+    offset += count;
+  }
+}
+
+const BlobPropertyBag& Blob::EmptyBlobPropertyBag() {
+  return empty_blob_property_bag.Get();
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/blob.h b/src/cobalt/dom/blob.h
index 6a49b8b..2ca6d52 100644
--- a/src/cobalt/dom/blob.h
+++ b/src/cobalt/dom/blob.h
@@ -15,43 +15,57 @@
 #ifndef COBALT_DOM_BLOB_H_
 #define COBALT_DOM_BLOB_H_
 
+#include <algorithm>
+#include <string>
 #include <vector>
 
 #include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/array_buffer_view.h"
+#include "cobalt/dom/data_view.h"
 #include "cobalt/dom/url_registry.h"
 #include "cobalt/script/environment_settings.h"
+#include "cobalt/script/sequence.h"
+#include "cobalt/script/union_type.h"
 #include "cobalt/script/wrappable.h"
 #include "googleurl/src/gurl.h"
 
 namespace cobalt {
 namespace dom {
 
+// This ensures this header can be included without depending on generated
+// dictionaries.
+class BlobPropertyBag;
+
 // A Blob object refers to a byte sequence, and has a size attribute which is
 // the total number of bytes in the byte sequence, and a type attribute, which
 // is an ASCII-encoded string in lower case representing the media type of the
 // byte sequence.
-//    https://www.w3.org/TR/2015/WD-FileAPI-20150421/#dfn-Blob
-//
-// Note: Cobalt currently does not implement nor need the type attribute.
+//    https://www.w3.org/TR/FileAPI/#dfn-Blob
 class Blob : public script::Wrappable {
  public:
   typedef UrlRegistry<Blob> Registry;
+  typedef script::UnionType4<
+      scoped_refptr<ArrayBuffer>, scoped_refptr<ArrayBufferView>,
+      scoped_refptr<DataView>, scoped_refptr<Blob> > BlobPart;
 
   Blob(script::EnvironmentSettings* settings,
-       const scoped_refptr<ArrayBuffer>& buffer = NULL)
-      : buffer_(
-            buffer ? buffer->Slice(settings, 0)
-                   : scoped_refptr<ArrayBuffer>(new ArrayBuffer(settings, 0))) {
-  }
+       const scoped_refptr<ArrayBuffer>& buffer = NULL);
+
+  Blob(script::EnvironmentSettings* settings,
+       script::Sequence<BlobPart> blobParts,
+       const BlobPropertyBag& options = EmptyBlobPropertyBag());
 
   const uint8* data() { return buffer_->data(); }
 
-  uint64 size() { return static_cast<uint64>(buffer_->byte_length()); }
+  uint64 size() const { return static_cast<uint64>(buffer_->byte_length()); }
 
   DEFINE_WRAPPABLE_TYPE(Blob);
 
  private:
+  static const BlobPropertyBag& EmptyBlobPropertyBag();
+
   scoped_refptr<ArrayBuffer> buffer_;
+  const std::string type_;
 
   DISALLOW_COPY_AND_ASSIGN(Blob);
 };
diff --git a/src/cobalt/dom/blob.idl b/src/cobalt/dom/blob.idl
index e96b035..1f495fe 100644
--- a/src/cobalt/dom/blob.idl
+++ b/src/cobalt/dom/blob.idl
@@ -12,16 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/2015/WD-FileAPI-20150421/#dfn-Blob
+// https://www.w3.org/TR/FileAPI/#dfn-Blob
 
 [
-  //Constructor,
-  // TODO: When arrays or sequences are supported, support the constructor
-  // defined in the standard with multiple blobparts, and drop the single
-  // ArrayBuffer constructor.
-  Constructor(optional ArrayBuffer buffer),
+  Constructor,
+  // TODO: Accept DOMString blob parts in the following union.
+  Constructor(sequence<(ArrayBuffer or ArrayBufferView or DataView or Blob)> blobParts, optional BlobPropertyBag options),
   ConstructorCallWith=EnvironmentSettings
 ]
 interface Blob {
   readonly attribute unsigned long long size;
-};
+  // TODO: Expose type and use for blob fetching and decoding.
+  // readonly attribute DOMString type;
+};
\ No newline at end of file
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/blob_property_bag.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/blob_property_bag.idl
index d335495..7020c15 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/blob_property_bag.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+// https://www.w3.org/TR/FileAPI/#dfn-BlobPropertyBag
+dictionary BlobPropertyBag {
+  DOMString type = "";
+};
\ No newline at end of file
diff --git a/src/cobalt/dom/blob_test.cc b/src/cobalt/dom/blob_test.cc
index 9a31b78..1e015a5 100644
--- a/src/cobalt/dom/blob_test.cc
+++ b/src/cobalt/dom/blob_test.cc
@@ -38,7 +38,7 @@
   scoped_refptr<DataView> data_view =
       new DataView(array_buffer, &exception_state);
   data_view->SetInt16(0, static_cast<int16>(0x0607), &exception_state);
-
+  data_view->SetInt16(3, static_cast<int16>(0xABCD), &exception_state);
   scoped_refptr<Blob> blob_with_buffer = new Blob(NULL, array_buffer);
 
   ASSERT_EQ(5, blob_with_buffer->size());
@@ -47,8 +47,24 @@
   EXPECT_EQ(0x6, blob_with_buffer->data()[0]);
   EXPECT_EQ(0x7, blob_with_buffer->data()[1]);
   EXPECT_EQ(0, blob_with_buffer->data()[2]);
-  EXPECT_EQ(0, blob_with_buffer->data()[3]);
-  EXPECT_EQ(0, blob_with_buffer->data()[4]);
+  EXPECT_EQ(0xAB, blob_with_buffer->data()[3]);
+  EXPECT_EQ(0xCD, blob_with_buffer->data()[4]);
+
+  scoped_refptr<DataView> data_view_mid_3 =
+      new DataView(array_buffer, 1, 3, &exception_state);
+  script::Sequence<Blob::BlobPart> parts;
+  parts.push_back(Blob::BlobPart(blob_with_buffer));
+  parts.push_back(Blob::BlobPart(data_view_mid_3));
+  parts.push_back(Blob::BlobPart(array_buffer));
+  scoped_refptr<Blob> blob_with_parts = new Blob(NULL, parts);
+
+  ASSERT_EQ(13UL, blob_with_parts->size());
+  ASSERT_TRUE(blob_with_parts->data());
+  EXPECT_EQ(0x6, blob_with_parts->data()[0]);
+  EXPECT_EQ(0x7, blob_with_parts->data()[5]);
+  EXPECT_EQ(0, blob_with_parts->data()[6]);
+  EXPECT_EQ(0xAB, blob_with_parts->data()[11]);
+  EXPECT_EQ(0xCD, blob_with_parts->data()[12]);
 }
 
 // Tests that further changes to a buffer from which a blob was constructed
diff --git a/src/cobalt/dom/camera_3d.cc b/src/cobalt/dom/camera_3d.cc
index 8229d07..376f981 100644
--- a/src/cobalt/dom/camera_3d.cc
+++ b/src/cobalt/dom/camera_3d.cc
@@ -31,5 +31,7 @@
 
 void Camera3D::ClearAllKeyMappings() { impl_->ClearAllKeyMappings(); }
 
+void Camera3D::Reset() { impl_->Reset(); }
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/camera_3d.h b/src/cobalt/dom/camera_3d.h
index 9063ac9..03d04cb 100644
--- a/src/cobalt/dom/camera_3d.h
+++ b/src/cobalt/dom/camera_3d.h
@@ -49,6 +49,9 @@
   // Clears all key mappings created by previous calls to |CreateKeyMapping|.
   void ClearAllKeyMappings();
 
+  // Resets the camera's orientation.
+  void Reset();
+
   // Custom, not in any spec.
   scoped_refptr<Camera3DImpl> impl() { return impl_; }
 
diff --git a/src/cobalt/dom/camera_3d.idl b/src/cobalt/dom/camera_3d.idl
index 1f9e89f..51b40a1 100644
--- a/src/cobalt/dom/camera_3d.idl
+++ b/src/cobalt/dom/camera_3d.idl
@@ -26,4 +26,7 @@
                         float degrees_per_second);
   void clearKeyMapping(long keyCode);
   void clearAllKeyMappings();
+
+  // Resets the camera's orientation.
+  void reset();
 };
diff --git a/src/cobalt/dom/camera_3d_impl.cc b/src/cobalt/dom/camera_3d_impl.cc
index d6ca39b..90a502e 100644
--- a/src/cobalt/dom/camera_3d_impl.cc
+++ b/src/cobalt/dom/camera_3d_impl.cc
@@ -45,6 +45,11 @@
   keycode_map_.clear();
 }
 
+void Camera3DImpl::Reset() {
+  base::AutoLock lock(mutex_);
+  orientation_ = Orientation();
+}
+
 namespace {
 
 const float kPiF = static_cast<float>(M_PI);
@@ -54,7 +59,8 @@
 }  // namespace
 
 glm::mat4 Camera3DImpl::QueryViewPerspectiveMatrix(
-    float width_to_height_aspect_ratio) {
+    float width_over_height_aspect_ratio, float max_horizontal_fov_rad,
+    float max_vertical_fov_rad) {
   base::AutoLock lock(mutex_);
   AccumulateOrientation();
 
@@ -69,12 +75,15 @@
       glm::rotate(-DegreesToRadians(orientation_.yaw), glm::vec3(0, 1, 0));
 
   // Setup a (right-handed) perspective projection matrix.
-  const float kVerticalFOVInDegrees = 60.0f;
+  float vertical_fov_rad =
+      std::min(max_vertical_fov_rad,
+               2 * static_cast<float>(atan(tan(max_horizontal_fov_rad * 0.5f) /
+                                           width_over_height_aspect_ratio)));
+
   const float kNearZ = 0.01f;
   const float kFarZ = 1000.0f;
-  glm::mat4 projection =
-      glm::perspectiveRH(DegreesToRadians(kVerticalFOVInDegrees),
-                         width_to_height_aspect_ratio, kNearZ, kFarZ);
+  glm::mat4 projection = glm::perspectiveRH(
+      vertical_fov_rad, width_over_height_aspect_ratio, kNearZ, kFarZ);
   return projection * camera_rotations;
 }
 
diff --git a/src/cobalt/dom/camera_3d_impl.h b/src/cobalt/dom/camera_3d_impl.h
index 1e368fe..311d2ed 100644
--- a/src/cobalt/dom/camera_3d_impl.h
+++ b/src/cobalt/dom/camera_3d_impl.h
@@ -47,10 +47,14 @@
   void ClearKeyMapping(int keycode);
   void ClearAllKeyMappings();
 
+  void Reset();
+
   // Returns the camera's view-perspective matrix, setup according to the passed
   // in width/height aspect ratio.  It is likely that this function will be
   // called from another thread, like a renderer thread.
-  glm::mat4 QueryViewPerspectiveMatrix(float width_to_height_aspect_ratio);
+  glm::mat4 QueryViewPerspectiveMatrix(float width_over_height_aspect_ratio,
+                                       float max_horizontal_fov_rad,
+                                       float max_vertical_fov_rad);
 
  private:
   struct KeycodeMappingInfo {
diff --git a/src/cobalt/dom/comment_test.cc b/src/cobalt/dom/comment_test.cc
index 34af72f..1e9cf83 100644
--- a/src/cobalt/dom/comment_test.cc
+++ b/src/cobalt/dom/comment_test.cc
@@ -40,7 +40,7 @@
 
 CommentTest::CommentTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
 }
diff --git a/src/cobalt/dom/csp_delegate.cc b/src/cobalt/dom/csp_delegate.cc
index 04d34a1..fc9d41f 100644
--- a/src/cobalt/dom/csp_delegate.cc
+++ b/src/cobalt/dom/csp_delegate.cc
@@ -83,6 +83,9 @@
     case kXhr:
       can_load = csp_->AllowConnectToSource(url, redirect_status);
       break;
+    case kWebSocket:
+      can_load = csp_->AllowConnectToSource(url, redirect_status);
+      break;
     default:
       NOTREACHED() << "Invalid resource type " << type;
       break;
diff --git a/src/cobalt/dom/csp_delegate.h b/src/cobalt/dom/csp_delegate.h
index 51e43c2..2660ea7 100644
--- a/src/cobalt/dom/csp_delegate.h
+++ b/src/cobalt/dom/csp_delegate.h
@@ -38,6 +38,7 @@
     kScript,
     kStyle,
     kXhr,
+    kWebSocket,
   };
 
   CspDelegate();
diff --git a/src/cobalt/dom/csp_delegate_test.cc b/src/cobalt/dom/csp_delegate_test.cc
index 32ee612..fccdcaf 100644
--- a/src/cobalt/dom/csp_delegate_test.cc
+++ b/src/cobalt/dom/csp_delegate_test.cc
@@ -47,6 +47,7 @@
     {CspDelegate::kScript, "script-src"},
     {CspDelegate::kStyle, "style-src"},
     {CspDelegate::kXhr, "connect-src"},
+    {CspDelegate::kWebSocket, "connect-src"},
 };
 
 class MockViolationReporter : public CspViolationReporter {
diff --git a/src/cobalt/dom/data_view_test.cc b/src/cobalt/dom/data_view_test.cc
index 9d03500..d32bdcf 100644
--- a/src/cobalt/dom/data_view_test.cc
+++ b/src/cobalt/dom/data_view_test.cc
@@ -68,25 +68,18 @@
 
 TEST(DataViewTest, ExceptionInConstructors) {
   StrictMock<MockExceptionState> exception_state;
-  script::MessageType message_type;
   scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 10);
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kTypeError, _, _));
   scoped_refptr<DataView> data_view = new DataView(NULL, &exception_state);
-  EXPECT_EQ(script::kTypeError, GetSimpleExceptionType(message_type));
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   data_view = new DataView(array_buffer, array_buffer->byte_length() + 1,
                            &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   data_view = new DataView(array_buffer, 0, array_buffer->byte_length() + 1,
                            &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 }
 
 TEST(DataViewTest, Getters) {
@@ -120,18 +113,16 @@
 
 TEST(DataViewTest, GettersWithException) {
   StrictMock<MockExceptionState> exception_state;
-  script::MessageType message_type;
   scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 13);
   scoped_refptr<DataView> data_view =
       new DataView(array_buffer, &exception_state);
 
 #define DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST(DomType, CppType)          \
   {                                                                            \
-    EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))                   \
-        .WillOnce(SaveArg<0>(&message_type));                                  \
+    EXPECT_CALL(exception_state,                                               \
+                SetSimpleExceptionVA(script::kRangeError, _, _));              \
     data_view->Get##DomType(array_buffer->byte_length() - sizeof(CppType) + 1, \
                             &exception_state);                                 \
-    EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));      \
   }
   DATA_VIEW_ACCESSOR_FOR_EACH(DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST)
 #undef DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST
@@ -186,18 +177,16 @@
 
 TEST(DataViewTest, SettersWithException) {
   StrictMock<MockExceptionState> exception_state;
-  script::MessageType message_type;
   scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 13);
   scoped_refptr<DataView> data_view =
       new DataView(array_buffer, &exception_state);
 
 #define DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST(DomType, CppType)          \
   {                                                                            \
-    EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))                   \
-        .WillOnce(SaveArg<0>(&message_type));                                  \
+    EXPECT_CALL(exception_state,                                               \
+                SetSimpleExceptionVA(script::kRangeError, _, _));              \
     data_view->Set##DomType(array_buffer->byte_length() - sizeof(CppType) + 1, \
                             0, &exception_state);                              \
-    EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));      \
   }
   DATA_VIEW_ACCESSOR_FOR_EACH(DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST)
 #undef DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 131b243..4869925 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -161,9 +161,20 @@
   return implementation_;
 }
 
+// Algorithm for GetElementsByTagName:
+//   https://www.w3.org/TR/dom/#concept-getelementsbytagname
 scoped_refptr<HTMLCollection> Document::GetElementsByTagName(
     const std::string& local_name) const {
-  return HTMLCollection::CreateWithElementsByTagName(this, local_name);
+  // 2. If the document is not an HTML document, then return an HTML collection
+  //    whose name is local name. If it is an HTML document, then return an,
+  //    HTML collection whose name is local name converted to ASCII lowercase.
+  if (IsXMLDocument()) {
+    return HTMLCollection::CreateWithElementsByLocalName(this, local_name);
+  } else {
+    const std::string lower_local_name = StringToLowerASCII(local_name);
+    return HTMLCollection::CreateWithElementsByLocalName(this,
+                                                         lower_local_name);
+  }
 }
 
 scoped_refptr<HTMLCollection> Document::GetElementsByClassName(
@@ -212,10 +223,10 @@
     return new UIEvent(Event::Uninitialized);
   }
 
-  DOMException::Raise(DOMException::kNotSupportedErr,
-                      "document.createEvent does not support \""
-                        + interface_name + "\".",
-                      exception_state);
+  DOMException::Raise(
+      DOMException::kNotSupportedErr,
+      "document.createEvent does not support \"" + interface_name + "\".",
+      exception_state);
 
   // Return value will be ignored.
   return NULL;
@@ -268,11 +279,6 @@
 void Document::set_body(const scoped_refptr<HTMLBodyElement>& body) {
   // 1. If the new value is not a body or frameset element, then throw a
   //    HierarchyRequestError exception and abort these steps.
-  if (body->tag_name() != HTMLBodyElement::kTagName) {
-    // TODO: Throw JS HierarchyRequestError.
-    return;
-  }
-
   // 2. Otherwise, if the new value is the same as the body element, do nothing.
   //    Abort these steps.
   scoped_refptr<HTMLBodyElement> current_body = this->body();
@@ -379,11 +385,26 @@
 }
 
 void Document::SetActiveElement(Element* active_element) {
+  // Invalidate matching rules on old and new active element.
+  if (active_element_) {
+    HTMLElement* html_element = active_element_->AsHTMLElement();
+    if (html_element) {
+      html_element->InvalidateMatchingRulesRecursively();
+    }
+  }
   if (active_element) {
+    HTMLElement* html_element = active_element->AsHTMLElement();
+    if (html_element) {
+      html_element->InvalidateMatchingRulesRecursively();
+    }
     active_element_ = base::AsWeakPtr(active_element);
   } else {
     active_element_.reset();
   }
+
+  // Record mutation and trigger layout.
+  is_computed_style_dirty_ = true;
+  RecordMutation();
   FOR_EACH_OBSERVER(DocumentObserver, observers_, OnFocusChanged());
 }
 
@@ -450,7 +471,7 @@
 
   scoped_refptr<HTMLHtmlElement> current_html = html();
   if (current_html) {
-    current_html->InvalidateComputedStylesRecursively();
+    current_html->InvalidateComputedStylesOfNodeAndDescendants();
   }
 
   RecordMutation();
@@ -465,7 +486,10 @@
 }
 
 void Document::OnTypefaceLoadEvent() {
-  InvalidateLayoutBoxesFromNodeAndDescendants();
+  scoped_refptr<HTMLHtmlElement> current_html = html();
+  if (current_html) {
+    current_html->InvalidateLayoutBoxesOfNodeAndDescendants();
+  }
   RecordMutation();
 }
 
@@ -581,7 +605,10 @@
        mode_token_iterator != mode_tokens.end(); ++mode_token_iterator) {
     const std::string& mode_token = *mode_token_iterator;
     if (mode_token == "wipe") {
-      InvalidateLayoutBoxesFromNodeAndDescendants();
+      scoped_refptr<HTMLHtmlElement> current_html = html();
+      if (current_html) {
+        current_html->InvalidateLayoutBoxesOfNodeAndDescendants();
+      }
       DLOG(INFO) << "Partial Layout state wiped";
     } else if (mode_token == "off") {
       partial_layout_is_enabled_ = false;
@@ -610,7 +637,7 @@
 
   scoped_refptr<HTMLHtmlElement> current_html = html();
   if (current_html) {
-    current_html->InvalidateComputedStylesRecursively();
+    current_html->InvalidateComputedStylesOfNodeAndDescendants();
   }
 }
 
@@ -648,20 +675,22 @@
   }
 }
 
-void Document::InvalidateLayout() {
-  // Set all invalidation flags and recursively invalidate the computed style.
-  is_selector_tree_dirty_ = true;
+void Document::PurgeCachedResources() {
+  // Set the computed style to dirty so that it'll be able to update any
+  // elements that had images purged when it resumes.
   is_computed_style_dirty_ = true;
-  are_font_faces_dirty_ = true;
-  are_keyframes_dirty_ = true;
 
   scoped_refptr<HTMLHtmlElement> current_html = html();
   if (current_html) {
-    current_html->InvalidateComputedStylesRecursively();
+    current_html->PurgeCachedBackgroundImagesOfNodeAndDescendants();
   }
+}
 
-  // Finally, also destroy all cached layout boxes.
-  InvalidateLayoutBoxesFromNodeAndDescendants();
+void Document::InvalidateLayoutBoxes() {
+  scoped_refptr<HTMLHtmlElement> current_html = html();
+  if (current_html) {
+    current_html->InvalidateLayoutBoxesOfNodeAndDescendants();
+  }
 }
 
 void Document::DisableJit() {
@@ -730,7 +759,7 @@
     // the keyframes map changed.
     scoped_refptr<HTMLHtmlElement> current_html = html();
     if (current_html) {
-      current_html->InvalidateComputedStylesRecursively();
+      current_html->InvalidateComputedStylesOfNodeAndDescendants();
     }
   }
 }
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 36d4e3b..f1e121b 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -325,8 +325,8 @@
   // Exposed for test purposes.
   void UpdateSelectorTree();
 
-  // Invalidates the document's cached layout tree and associated data.
-  void InvalidateLayout();
+  void PurgeCachedResources();
+  void InvalidateLayoutBoxes();
 
   // Disable just-in-time compilation of JavaScript code.
   void DisableJit();
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 1900ec6..8544240 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -64,7 +64,7 @@
     : css_parser_(css_parser::Parser::Create()),
       dom_stat_tracker_(new DomStatTracker("DocumentTest")),
       html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                             dom_stat_tracker_.get(), "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 }
@@ -113,7 +113,7 @@
   scoped_refptr<Element> element = document->CreateElement("element");
 
   EXPECT_EQ(Node::kElementNode, element->node_type());
-  EXPECT_EQ("element", element->node_name());
+  EXPECT_EQ("ELEMENT", element->node_name());
 
   EXPECT_EQ(document, element->node_document());
   EXPECT_EQ(NULL, element->parent_node());
@@ -121,7 +121,7 @@
   EXPECT_EQ(NULL, element->last_child());
 
   element = document->CreateElement("ELEMENT");
-  EXPECT_EQ("element", element->node_name());
+  EXPECT_EQ("ELEMENT", element->node_name());
 }
 
 TEST_F(DocumentTest, CreateTextNode) {
diff --git a/src/cobalt/dom/document_type_test.cc b/src/cobalt/dom/document_type_test.cc
index daffa4e..e2e7efe 100644
--- a/src/cobalt/dom/document_type_test.cc
+++ b/src/cobalt/dom/document_type_test.cc
@@ -25,7 +25,7 @@
  protected:
   DocumentTypeTest()
       : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, ""),
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""),
         document_(new Document(&html_element_context_)) {}
   ~DocumentTypeTest() OVERRIDE {}
 
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 44f9329..a815619 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -34,7 +34,9 @@
         'audio_track_list.h',
         'benchmark_stat_names.cc',
         'benchmark_stat_names.h',
+        'blob.cc',
         'blob.h',
+        'blob_property_bag.h',
         'camera_3d.cc',
         'camera_3d.h',
         'camera_3d_impl.cc',
@@ -267,7 +269,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
-        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_dictionaries',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
         '<(DEPTH)/cobalt/csp/csp.gyp:csp',
         '<(DEPTH)/cobalt/cssom/cssom.gyp:cssom',
         '<(DEPTH)/cobalt/dom/dom_exception.gyp:dom_exception',
@@ -319,7 +321,7 @@
         # Additionally, ensure that the include directories for generated
         # headers are put on the include directories for targets that depend
         # on this one.
-        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_dictionaries',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
       ]
     },
 
@@ -335,6 +337,11 @@
         'testing/stub_script_runner.cc',
         'testing/stub_script_runner.h',
       ],
+      'dependencies': [
+        # TODO: Remove the dependency below, it works around the fact that
+        #       ScriptValueFactory has non-virtual method CreatePromise().
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
+      ],
     },
   ],
 }
diff --git a/src/cobalt/dom/dom_implementation_test.cc b/src/cobalt/dom/dom_implementation_test.cc
index 5b0a7bf..f161a30 100644
--- a/src/cobalt/dom/dom_implementation_test.cc
+++ b/src/cobalt/dom/dom_implementation_test.cc
@@ -26,7 +26,7 @@
 TEST(DOMImplementationTest, CreateDocumentShouldCreateXMLDocument) {
   HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL,
                                           NULL, NULL, NULL, NULL, NULL, NULL,
-                                          NULL, "");
+                                          NULL, NULL, NULL, "");
   scoped_refptr<DOMImplementation> dom_implementation =
       new DOMImplementation(&html_element_context);
   scoped_refptr<Document> document =
diff --git a/src/cobalt/dom/dom_parser.cc b/src/cobalt/dom/dom_parser.cc
index 0ab0f7c..7b60408 100644
--- a/src/cobalt/dom/dom_parser.cc
+++ b/src/cobalt/dom/dom_parser.cc
@@ -34,23 +34,21 @@
 DOMParser::DOMParser(HTMLElementContext* html_element_context)
     : html_element_context_(html_element_context) {}
 
-scoped_refptr<Document> DOMParser::ParseFromString(const std::string& str,
-                                                   const SupportedType& type) {
+scoped_refptr<Document> DOMParser::ParseFromString(
+    const std::string& str, DOMParserSupportedType type) {
   switch (type) {
-    case kTextHtml:
+    case kDOMParserSupportedTypeTextHtml:
       return html_element_context_->dom_parser()->ParseDocument(
           str, html_element_context_, GetInlineSourceLocation());
-    case kTextXml:
-    case kApplicationXml:
-    case kApplicationXhtmlXml:
-    case kImageSvgXml:
+    case kDOMParserSupportedTypeTextXml:
+    case kDOMParserSupportedTypeApplicationXml:
+    case kDOMParserSupportedTypeApplicationXhtmlXml:
+    case kDOMParserSupportedTypeImageSvgXml:
       return html_element_context_->dom_parser()->ParseXMLDocument(
           str, html_element_context_, GetInlineSourceLocation());
-    case kMaxSupportedType:
-    default:
-      LOG(WARNING) << "DOMParse.ParseFromString received invalid type value.";
-      return NULL;
   }
+  LOG(WARNING) << "DOMParse.ParseFromString received invalid type value.";
+  return NULL;
 }
 
 }  // namespace dom
diff --git a/src/cobalt/dom/dom_parser.h b/src/cobalt/dom/dom_parser.h
index 5c33df1..9aef630 100644
--- a/src/cobalt/dom/dom_parser.h
+++ b/src/cobalt/dom/dom_parser.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "cobalt/dom/dom_parser_supported_type.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/wrappable.h"
 
@@ -33,21 +34,12 @@
 //   https://www.w3.org/TR/DOM-Parsing/#the-domparser-interface
 class DOMParser : public script::Wrappable {
  public:
-  enum SupportedType {
-    kTextHtml,
-    kTextXml,
-    kApplicationXml,
-    kApplicationXhtmlXml,
-    kImageSvgXml,
-    kMaxSupportedType,
-  };
-
   explicit DOMParser(script::EnvironmentSettings* environment_settings);
   explicit DOMParser(HTMLElementContext* html_element_context);
 
   // Web API: DOMParser
   scoped_refptr<Document> ParseFromString(const std::string& str,
-                                          const SupportedType& type);
+                                          DOMParserSupportedType type);
 
   DEFINE_WRAPPABLE_TYPE(DOMParser);
 
diff --git a/src/cobalt/dom/dom_parser.idl b/src/cobalt/dom/dom_parser.idl
index 75f6f0a..7ff1879 100644
--- a/src/cobalt/dom/dom_parser.idl
+++ b/src/cobalt/dom/dom_parser.idl
@@ -14,17 +14,9 @@
 
 // https://www.w3.org/TR/DOM-Parsing/#the-domparser-interface
 
-enum SupportedType {
-    "text/html",
-    "text/xml",
-    "application/xml",
-    "application/xhtml+xml",
-    "image/svg+xml"
-};
-
 [
   Constructor,
   ConstructorCallWith=EnvironmentSettings
 ] interface DOMParser {
-  [NewObject] Document parseFromString(DOMString str, SupportedType type);
+  [NewObject] Document parseFromString(DOMString str, DOMParserSupportedType type);
 };
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/dom_parser_supported_type.idl
similarity index 68%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/dom_parser_supported_type.idl
index d335495..a041a81 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/dom_parser_supported_type.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/DOM-Parsing/#the-domparser-interface
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum DOMParserSupportedType {
+    "text/html",
+    "text/xml",
+    "application/xml",
+    "application/xhtml+xml",
+    "image/svg+xml"
+};
diff --git a/src/cobalt/dom/dom_parser_test.cc b/src/cobalt/dom/dom_parser_test.cc
index a9cf512..21d7302 100644
--- a/src/cobalt/dom/dom_parser_test.cc
+++ b/src/cobalt/dom/dom_parser_test.cc
@@ -45,8 +45,9 @@
       html_element_context_(
           &fetcher_factory_, &stub_css_parser_, dom_parser_parser_.get(),
           NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
-          &stub_script_runner_, NULL /* media_source_registry */,
-          NULL /* resource_provider */, NULL /* image_cache */,
+          &stub_script_runner_, NULL /* script_value_factory */,
+          NULL /* media_source_registry */, NULL /* resource_provider */,
+          NULL /* animated_image_tracker */, NULL /* image_cache */,
           NULL /* reduced_image_cache_capacity_manager */,
           NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
           NULL /* dom_stat_tracker */, "" /* language */),
@@ -69,15 +70,9 @@
       "  </Period>\n"
       "</MPD>\n";
   scoped_refptr<Document> document =
-      dom_parser_->ParseFromString(input, DOMParser::kTextXml);
+      dom_parser_->ParseFromString(input, kDOMParserSupportedTypeTextXml);
   EXPECT_TRUE(document->IsXMLDocument());
 }
 
-TEST_F(DOMParserTest, InvalidType) {
-  scoped_refptr<Document> document =
-      dom_parser_->ParseFromString("", DOMParser::kMaxSupportedType);
-  EXPECT_FALSE(document);
-}
-
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/dom_string_map.cc b/src/cobalt/dom/dom_string_map.cc
index 9f7f318..e2d71c2 100644
--- a/src/cobalt/dom/dom_string_map.cc
+++ b/src/cobalt/dom/dom_string_map.cc
@@ -136,8 +136,8 @@
   if (attribute_name) {
     return element_->GetAttribute(*attribute_name);
   } else {
-    exception_state->SetSimpleExceptionWithArgs(script::kPropertySyntaxError, 0,
-                                                property_name.c_str());
+    exception_state->SetSimpleException(script::kSyntaxError,
+                                        property_name.c_str());
     return base::nullopt;
   }
 }
@@ -150,8 +150,8 @@
   if (attribute_name) {
     element_->SetAttribute(*attribute_name, value);
   } else {
-    exception_state->SetSimpleExceptionWithArgs(script::kPropertySyntaxError, 0,
-                                                property_name.c_str());
+    exception_state->SetSimpleException(script::kSyntaxError,
+                                        property_name.c_str());
   }
 }
 
diff --git a/src/cobalt/dom/dom_string_map_test.cc b/src/cobalt/dom/dom_string_map_test.cc
index bb5599a..cf8a9a5 100644
--- a/src/cobalt/dom/dom_string_map_test.cc
+++ b/src/cobalt/dom/dom_string_map_test.cc
@@ -43,7 +43,7 @@
 
 DOMStringMapTest::DOMStringMapTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, ""),
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""),
       document_(new Document(&html_element_context_)),
       element_(new Element(document_, base::Token("element"))),
       dom_string_map_(new DOMStringMap(element_)) {}
@@ -163,12 +163,12 @@
 
 TEST_F(DOMStringMapTest, InvalidPropertyName) {
   EXPECT_CALL(exception_state_,
-              SetSimpleExceptionVA(script::kPropertySyntaxError, _));
+              SetSimpleExceptionVA(script::kSyntaxError, _, _));
   dom_string_map_->AnonymousNamedSetter("hyphen-lowercase", "Los Angeles",
                                         &exception_state_);
 
   EXPECT_CALL(exception_state_,
-              SetSimpleExceptionVA(script::kPropertySyntaxError, _));
+              SetSimpleExceptionVA(script::kSyntaxError, _, _));
   dom_string_map_->AnonymousNamedGetter("hyphen-lowercase", &exception_state_);
 }
 
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 7985a1a..7ec5d03 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -50,6 +50,7 @@
         'location_test.cc',
         'media_query_list_test.cc',
         'mutation_observer_test.cc',
+        'named_node_map_test.cc',
         'node_dispatch_event_test.cc',
         'node_list_live_test.cc',
         'node_list_test.cc',
diff --git a/src/cobalt/dom/dom_token_list.cc b/src/cobalt/dom/dom_token_list.cc
index 18e2daf..0bc8b8e 100644
--- a/src/cobalt/dom/dom_token_list.cc
+++ b/src/cobalt/dom/dom_token_list.cc
@@ -107,7 +107,6 @@
     }
   }
 
-  const size_t old_size = tokens_.size();
   for (std::vector<std::string>::const_iterator it = tokens.begin();
        it != tokens.end(); ++it) {
     // 3. For each token in tokens, in given order, that is not in tokens,
@@ -119,9 +118,7 @@
   }
 
   // 4. Run the update steps.
-  if (tokens_.size() != old_size) {
-    RunUpdateSteps();
-  }
+  RunUpdateSteps();
 }
 
 // Algorithm for Remove:
@@ -140,7 +137,6 @@
     }
   }
 
-  const size_t old_size = tokens_.size();
   for (std::vector<std::string>::const_iterator it = tokens.begin();
        it != tokens.end(); ++it) {
     // 3. For each token in tokens, remove token from tokens.
@@ -149,9 +145,7 @@
   }
 
   // 4. Run the update steps.
-  if (tokens_.size() != old_size) {
-    RunUpdateSteps();
-  }
+  RunUpdateSteps();
 }
 
 // Algorithm for AnonymousStringifier:
diff --git a/src/cobalt/dom/dom_token_list_test.cc b/src/cobalt/dom/dom_token_list_test.cc
index e7fe553..a40f8be 100644
--- a/src/cobalt/dom/dom_token_list_test.cc
+++ b/src/cobalt/dom/dom_token_list_test.cc
@@ -38,7 +38,7 @@
 
 DOMTokenListTest::DOMTokenListTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
 }
diff --git a/src/cobalt/dom/element.cc b/src/cobalt/dom/element.cc
index a8ed9a4..521069e 100644
--- a/src/cobalt/dom/element.cc
+++ b/src/cobalt/dom/element.cc
@@ -72,9 +72,9 @@
   ++(element_count_log.Get().count);
 }
 
-Element::Element(Document* document, base::Token tag_name)
+Element::Element(Document* document, base::Token local_name)
     : Node(document),
-      tag_name_(tag_name),
+      local_name_(local_name),
       animations_(new web_animations::AnimationSet()) {
   ++(element_count_log.Get().count);
 }
@@ -97,14 +97,18 @@
 void Element::set_text_content(
     const base::optional<std::string>& text_content) {
   TRACK_MEMORY_SCOPE("DOM");
-  // Remove all children and replace them with a single Text node.
-  while (HasChildNodes()) {
-    RemoveChild(first_child());
-  }
+  // https://www.w3.org/TR/dom/#dom-node-textcontent
+  // 1. Let node be null.
+  scoped_refptr<Node> new_node;
+
+  // 2. If new value is not the empty string, set node to a new Text node whose
+  //    data is new value.
   std::string new_text_content = text_content.value_or("");
   if (!new_text_content.empty()) {
-    AppendChild(new Text(node_document(), new_text_content));
+    new_node = new Text(node_document(), new_text_content);
   }
+  // 3. Replace all with node within the context object.
+  ReplaceAll(new_node);
 }
 
 bool Element::HasAttributes() const { return !attribute_map_.empty(); }
@@ -308,6 +312,25 @@
   OnRemoveAttribute(name);
 }
 
+// Algorithm for tag_name:
+//   https://www.w3.org/TR/dom/#dom-element-tagname
+base::Token Element::tag_name() const {
+  // 1. If context object's namespace prefix is not null, let qualified name be
+  // its namespace prefix, followed by a ":" (U+003A), followed by its local
+  // name. Otherwise, let qualified name be its local name.
+  std::string qualified_name = local_name_.c_str();
+
+  // 2. If the context object is in the HTML namespace and its node document is
+  // an HTML document, let qualified name be converted to ASCII uppercase.
+  Document* document = node_document();
+  if (document && !document->IsXMLDocument()) {
+    StringToUpperASCII(&qualified_name);
+  }
+
+  // 3. Return qualified name.
+  return base::Token(qualified_name);
+}
+
 // Algorithm for HasAttribute:
 //   https://www.w3.org/TR/2014/WD-dom-20140710/#dom-element-hasattribute
 bool Element::HasAttribute(const std::string& name) const {
@@ -327,9 +350,21 @@
   return iter != attribute_map_.end();
 }
 
+// Algorithm for GetElementsByTagName:
+//   https://www.w3.org/TR/dom/#concept-getelementsbytagname
 scoped_refptr<HTMLCollection> Element::GetElementsByTagName(
-    const std::string& tag_name) const {
-  return HTMLCollection::CreateWithElementsByTagName(this, tag_name);
+    const std::string& local_name) const {
+  Document* document = node_document();
+  // 2. If the document is not an HTML document, then return an HTML collection
+  //    whose name is local name. If it is an HTML document, then return an,
+  //    HTML collection whose name is local name converted to ASCII lowercase.
+  if (document && document->IsXMLDocument()) {
+    return HTMLCollection::CreateWithElementsByLocalName(this, local_name);
+  } else {
+    const std::string lower_local_name = StringToLowerASCII(local_name);
+    return HTMLCollection::CreateWithElementsByLocalName(this,
+                                                         lower_local_name);
+  }
 }
 
 scoped_refptr<HTMLCollection> Element::GetElementsByClassName(
@@ -520,7 +555,7 @@
 
 scoped_refptr<Node> Element::Duplicate() const {
   TRACK_MEMORY_SCOPE("DOM");
-  Element* new_element = new Element(node_document(), tag_name());
+  Element* new_element = new Element(node_document(), local_name_);
   new_element->CopyAttributes(*this);
   return new_element;
 }
@@ -585,7 +620,7 @@
 }
 
 std::string Element::GetDebugName() {
-  std::string name = tag_name_.c_str();
+  std::string name = local_name_.c_str();
   if (HasAttribute("id")) {
     name += "#";
     name += id_attribute_.c_str();
diff --git a/src/cobalt/dom/element.h b/src/cobalt/dom/element.h
index ed1b288..1e64100 100644
--- a/src/cobalt/dom/element.h
+++ b/src/cobalt/dom/element.h
@@ -56,7 +56,7 @@
       AttributeMap;
 
   explicit Element(Document* document);
-  Element(Document* document, base::Token tag_name);
+  Element(Document* document, base::Token local_name);
 
   // Web API: Node
   //
@@ -72,7 +72,7 @@
   // Web API: Element
   //
 
-  base::Token tag_name() const { return tag_name_; }
+  base::Token tag_name() const;
 
   base::Token id() const { return id_attribute_; }
   void set_id(const std::string& value) { SetAttribute("id", value); }
@@ -91,7 +91,7 @@
   bool HasAttribute(const std::string& name) const;
 
   scoped_refptr<HTMLCollection> GetElementsByTagName(
-      const std::string& tag_name) const;
+      const std::string& local_name) const;
   scoped_refptr<HTMLCollection> GetElementsByClassName(
       const std::string& class_name) const;
 
@@ -125,6 +125,9 @@
 
   // Custom, not in any spec.
   //
+
+  base::Token local_name() const { return local_name_; }
+
   // Returns whether the element has no children at all except comments or
   // processing instructions.
   //   https://www.w3.org/TR/selectors4/#empty-pseudo
@@ -184,8 +187,8 @@
   // Callback for error when parsing inner / outer HTML.
   void HTMLParseError(const std::string& error);
 
-  // Tag name of the element.
-  base::Token tag_name_;
+  // Local name of the element.
+  base::Token local_name_;
   // A map that holds the actual element attributes.
   AttributeMap attribute_map_;
   // The "id" attribute for this element. Stored here in addition to being
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc
index 7c927fc..1e84b7a 100644
--- a/src/cobalt/dom/element_test.cc
+++ b/src/cobalt/dom/element_test.cc
@@ -32,6 +32,7 @@
 #include "cobalt/dom/testing/gtest_workarounds.h"
 #include "cobalt/dom/testing/html_collection_testing.h"
 #include "cobalt/dom/text.h"
+#include "cobalt/dom/xml_document.h"
 #include "cobalt/dom_parser/parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -52,6 +53,7 @@
   scoped_ptr<DomStatTracker> dom_stat_tracker_;
   HTMLElementContext html_element_context_;
   scoped_refptr<Document> document_;
+  scoped_refptr<XMLDocument> xml_document_;
 };
 
 ElementTest::ElementTest()
@@ -60,12 +62,14 @@
       dom_stat_tracker_(new DomStatTracker("ElementTest")),
       html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            dom_stat_tracker_.get(), "") {
+                            NULL, NULL, dom_stat_tracker_.get(), "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
+  xml_document_ = new XMLDocument(&html_element_context_);
 }
 
 ElementTest::~ElementTest() {
+  xml_document_ = NULL;
   document_ = NULL;
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 }
@@ -80,9 +84,9 @@
   ASSERT_TRUE(element);
 
   EXPECT_EQ(Node::kElementNode, element->node_type());
-  EXPECT_EQ("element", element->node_name());
-
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("ELEMENT", element->node_name());
+  EXPECT_EQ("ELEMENT", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
   EXPECT_EQ("", element->id());
   EXPECT_EQ("", element->class_name());
 
@@ -119,6 +123,18 @@
   EXPECT_EQ(NULL, text->AsElement());
 }
 
+TEST_F(ElementTest, TagName) {
+  scoped_refptr<Element> element_in_html =
+      new Element(document_, base::Token("eLeMeNt"));
+  EXPECT_EQ("ELEMENT", element_in_html->tag_name());
+  EXPECT_EQ("eLeMeNt", element_in_html->local_name());
+
+  scoped_refptr<Element> element_in_xml =
+      new Element(xml_document_, base::Token("eLeMeNt"));
+  EXPECT_EQ("eLeMeNt", element_in_xml->tag_name());
+  EXPECT_EQ("eLeMeNt", element_in_html->local_name());
+}
+
 TEST_F(ElementTest, AttributeMethods) {
   scoped_refptr<Element> element =
       new Element(document_, base::Token("element"));
@@ -375,8 +391,14 @@
       "  <element_b2>Text</element_b2>\n"
       "</element_a>";
   EXPECT_EQ(kExpectedHTML, root->inner_html());
+}
 
+TEST_F(ElementTest, SetInnerHTML) {
   // Setting inner HTML should remove all previous children.
+  scoped_refptr<Element> root = new Element(document_, base::Token("root"));
+  scoped_refptr<Element> element_a =
+      root->AppendChild(new Element(document_, base::Token("element_a")))
+          ->AsElement();
   root->set_inner_html("");
   EXPECT_FALSE(root->HasChildNodes());
 
diff --git a/src/cobalt/dom/event_queue_test.cc b/src/cobalt/dom/event_queue_test.cc
index 0a46e27..3697091 100644
--- a/src/cobalt/dom/event_queue_test.cc
+++ b/src/cobalt/dom/event_queue_test.cc
@@ -18,6 +18,7 @@
 #include "cobalt/dom/event.h"
 #include "cobalt/dom/event_target.h"
 #include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/script/testing/fake_script_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::AllOf;
@@ -28,7 +29,7 @@
 namespace cobalt {
 namespace dom {
 
-using testing::FakeScriptValue;
+using ::cobalt::script::testing::FakeScriptValue;
 using testing::MockEventListener;
 
 class EventQueueTest : public ::testing::Test {
@@ -54,8 +55,8 @@
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
   EventQueue event_queue(event_target.get());
 
-  event_target->AddEventListener("event",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "event", FakeScriptValue<EventListener>(event_listener.get()), false);
   ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
                                           event_target);
 
@@ -70,8 +71,8 @@
   EventQueue event_queue(event_target.get());
 
   event->set_target(event_target);
-  event_target->AddEventListener("event",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "event", FakeScriptValue<EventListener>(event_listener.get()), false);
   ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
                                           event_target);
 
@@ -86,8 +87,8 @@
   EventQueue event_queue(event_target.get());
 
   event->set_target(event_target);
-  event_target->AddEventListener("event",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "event", FakeScriptValue<EventListener>(event_listener.get()), false);
   event_listener->ExpectNoHandleEventCall();
 
   event_queue.Enqueue(event);
@@ -108,7 +109,7 @@
 
   event->set_target(event_target_2);
   event_target_2->AddEventListener(
-      "event", FakeScriptValue(event_listener.get()), false);
+      "event", FakeScriptValue<EventListener>(event_listener.get()), false);
   ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
                                           event_target_2);
 
diff --git a/src/cobalt/dom/event_target_test.cc b/src/cobalt/dom/event_target_test.cc
index f055f07..0fc5137 100644
--- a/src/cobalt/dom/event_target_test.cc
+++ b/src/cobalt/dom/event_target_test.cc
@@ -17,6 +17,7 @@
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/script/testing/fake_script_value.h"
 #include "cobalt/script/testing/mock_exception_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -35,8 +36,8 @@
 using ::testing::SaveArg;
 using ::testing::StrictMock;
 using ::testing::_;
+using script::testing::FakeScriptValue;
 using script::testing::MockExceptionState;
-using testing::FakeScriptValue;
 using testing::MockEventListener;
 
 base::optional<bool> DispatchEventOnCurrentTarget(
@@ -67,8 +68,8 @@
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
 
   event_listener->ExpectHandleEventCall(event, event_target);
-  event_target->AddEventListener("fired",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -79,8 +80,8 @@
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
 
   event_listener->ExpectNoHandleEventCall();
-  event_target->AddEventListener("notfired",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "notfired", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -103,11 +104,14 @@
   event_listenernot_fired->ExpectNoHandleEventCall();
 
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired_1.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired_1.get()), false);
   event_target->AddEventListener(
-      "notfired", FakeScriptValue(event_listenernot_fired.get()), false);
+      "notfired",
+      FakeScriptValue<EventListener>(event_listenernot_fired.get()), false);
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired_2.get()), true);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired_2.get()), true);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -119,18 +123,18 @@
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
 
   event_listener->ExpectHandleEventCall(event, event_target);
-  event_target->AddEventListener("fired",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
   event_listener->ExpectNoHandleEventCall();
   event_target->RemoveEventListener(
-      "fired", FakeScriptValue(event_listener.get()), false);
+      "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
   event_listener->ExpectHandleEventCall(event, event_target);
-  event_target->AddEventListener("fired",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -147,26 +151,30 @@
       MockEventListener::Create();
 
   event_target->AddEventListener(
-      "fired", FakeScriptValue(non_attribute_event_listener.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(non_attribute_event_listener.get()),
+          false);
 
   non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
   attribute_event_listener1->ExpectHandleEventCall(event, event_target);
   event_target->SetAttributeEventListener(
-      base::Token("fired"), FakeScriptValue(attribute_event_listener1.get()));
+      base::Token("fired"),
+      FakeScriptValue<EventListener>(attribute_event_listener1.get()));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
   non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
   attribute_event_listener1->ExpectNoHandleEventCall();
   attribute_event_listener2->ExpectHandleEventCall(event, event_target);
   event_target->SetAttributeEventListener(
-      base::Token("fired"), FakeScriptValue(attribute_event_listener2.get()));
+      base::Token("fired"),
+      FakeScriptValue<EventListener>(attribute_event_listener2.get()));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 
   non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
   attribute_event_listener1->ExpectNoHandleEventCall();
   attribute_event_listener2->ExpectNoHandleEventCall();
   event_target->SetAttributeEventListener(base::Token("fired"),
-                                          FakeScriptValue(NULL));
+                                          FakeScriptValue<EventListener>(NULL));
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -180,29 +188,29 @@
 
   event_listener->ExpectHandleEventCall(event_1, event_target);
   event_listener->ExpectHandleEventCall(event_2, event_target);
-  event_target->AddEventListener("fired_1",
-                                 FakeScriptValue(event_listener.get()), false);
-  event_target->AddEventListener("fired_2",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "fired_1", FakeScriptValue<EventListener>(event_listener.get()), false);
+  event_target->AddEventListener(
+      "fired_2", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 
   event_listener->ExpectHandleEventCall(event_1, event_target);
   event_target->RemoveEventListener(
-      "fired_2", FakeScriptValue(event_listener.get()), false);
+      "fired_2", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 
   event_listener->ExpectHandleEventCall(event_1, event_target);
   // The capture flag is not the same so the event will not be removed.
   event_target->RemoveEventListener(
-      "fired_1", FakeScriptValue(event_listener.get()), true);
+      "fired_1", FakeScriptValue<EventListener>(event_listener.get()), true);
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 
   event_listener->ExpectNoHandleEventCall();
   event_target->RemoveEventListener(
-      "fired_1", FakeScriptValue(event_listener.get()), false);
+      "fired_1", FakeScriptValue<EventListener>(event_listener.get()), false);
   EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
   EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
 }
@@ -222,9 +230,11 @@
   event_listenerfired_2->ExpectHandleEventCall(event, event_target);
 
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired_1.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired_1.get()), false);
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired_2.get()), true);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired_2.get()), true);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -242,9 +252,11 @@
   event_listenerfired_2->ExpectNoHandleEventCall();
 
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired_1.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired_1.get()), false);
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired_2.get()), true);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired_2.get()), true);
   EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
 }
 
@@ -256,7 +268,8 @@
       MockEventListener::Create();
 
   event_target->AddEventListener(
-      "fired", FakeScriptValue(event_listenerfired.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listenerfired.get()), false);
   event = new Event(base::Token("fired"), Event::kNotBubbles,
                     Event::kNotCancelable);
   event_listenerfired->ExpectHandleEventCall(
@@ -296,8 +309,8 @@
             base::polymorphic_downcast<DOMException*>(exception.get())->code());
   exception = NULL;
 
-  event_target->AddEventListener("fired",
-                                 FakeScriptValue(event_listener.get()), false);
+  event_target->AddEventListener(
+      "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
   event = new Event(base::Token("fired"), Event::kNotBubbles,
                     Event::kNotCancelable);
   // Dispatch event again when it is being dispatched.
@@ -311,7 +324,7 @@
   scoped_refptr<EventTarget> event_target = new EventTarget;
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
-  FakeScriptValue script_object(event_listener.get());
+  FakeScriptValue<EventListener> script_object(event_listener.get());
 
   InSequence in_sequence;
   event_listener->ExpectHandleEventCall(event, event_target);
@@ -328,7 +341,7 @@
   scoped_refptr<EventTarget> event_target = new EventTarget;
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
-  FakeScriptValue script_object(event_listener.get());
+  FakeScriptValue<EventListener> script_object(event_listener.get());
 
   InSequence in_sequence;
   event_listener->ExpectHandleEventCall(event, event_target);
@@ -345,7 +358,7 @@
   scoped_refptr<EventTarget> event_target = new EventTarget;
   scoped_refptr<Event> event = new Event(base::Token("fired"));
   scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
-  FakeScriptValue script_object(event_listener.get());
+  FakeScriptValue<EventListener> script_object(event_listener.get());
 
   InSequence in_sequence;
   event_listener->ExpectHandleEventCall(event, event_target);
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index f9645bd..28da2c0 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -373,7 +373,8 @@
       break;
     }
     const scoped_refptr<render_tree::Typeface>& typeface(
-        resource_provider()->GetLocalTypefaceByFaceNameIfAvailable(font_face));
+        resource_provider()->GetLocalTypefaceByFaceNameIfAvailable(
+            font_face.c_str()));
     if (!typeface) {
       break;
     }
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h
index 062d399..3cbc097 100644
--- a/src/cobalt/dom/font_list.h
+++ b/src/cobalt/dom/font_list.h
@@ -161,6 +161,9 @@
 
   // From render_tree::FontProvider
 
+  const render_tree::FontStyle& style() const OVERRIDE { return style_; }
+  float size() const OVERRIDE { return size_; }
+
   // Returns the first font in the font list that supports the specified
   // UTF-32 character or a fallback font provided by the font cache if none of
   // them do.
@@ -171,9 +174,6 @@
   const scoped_refptr<render_tree::Font>& GetCharacterFont(
       int32 utf32_character, render_tree::GlyphIndex* glyph_index) OVERRIDE;
 
-  render_tree::FontStyle style() const { return style_; }
-  float size() const { return size_; }
-
  private:
   const scoped_refptr<render_tree::Font>& GetFallbackCharacterFont(
       int32 utf32_character, render_tree::GlyphIndex* glyph_index);
diff --git a/src/cobalt/dom/html_collection.cc b/src/cobalt/dom/html_collection.cc
index 40bffac..5bff1e3 100644
--- a/src/cobalt/dom/html_collection.cc
+++ b/src/cobalt/dom/html_collection.cc
@@ -156,10 +156,10 @@
          node->AsElement()->class_list()->Contains(class_name);
 }
 
-// Used to implement HTMLCollection::kElementsByTagName.
-bool IsElementWithTagName(const std::string& tag_name, Node* node) {
+// Used to implement HTMLCollection::kElementsByLocalName.
+bool IsElementWithLocalName(const std::string& local_name, Node* node) {
   return node->IsElement() &&
-         (tag_name == "*" || node->AsElement()->tag_name() == tag_name);
+         (local_name == "*" || node->AsElement()->local_name() == local_name);
 }
 
 }  // namespace
@@ -188,13 +188,13 @@
 }
 
 // static
-scoped_refptr<HTMLCollection> HTMLCollection::CreateWithElementsByTagName(
+scoped_refptr<HTMLCollection> HTMLCollection::CreateWithElementsByLocalName(
     const scoped_refptr<const Node>& base, const std::string& name) {
   if (!base) {
     return NULL;
   }
   return new NodeCollection<NodeDescendantsIterator>(
-      base, base::Bind(&IsElementWithTagName, name));
+      base, base::Bind(&IsElementWithLocalName, name));
 }
 
 HTMLCollection::HTMLCollection() { GlobalStats::GetInstance()->Add(this); }
diff --git a/src/cobalt/dom/html_collection.h b/src/cobalt/dom/html_collection.h
index 0bf427e..ac21b8b 100644
--- a/src/cobalt/dom/html_collection.h
+++ b/src/cobalt/dom/html_collection.h
@@ -44,8 +44,8 @@
   // A collections of all descendants with a given class name.
   static scoped_refptr<HTMLCollection> CreateWithElementsByClassName(
       const scoped_refptr<const Node>& base, const std::string& name);
-  // A collections of all descendants with a given tag name.
-  static scoped_refptr<HTMLCollection> CreateWithElementsByTagName(
+  // A collections of all descendants with a given local name.
+  static scoped_refptr<HTMLCollection> CreateWithElementsByLocalName(
       const scoped_refptr<const Node>& base, const std::string& name);
 
   // Web API: HTMLCollection
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 90f8bd7..e1e9485 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -53,6 +53,7 @@
 #include "cobalt/dom/html_unknown_element.h"
 #include "cobalt/dom/html_video_element.h"
 #include "cobalt/dom/rule_matching.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
 
 namespace cobalt {
 namespace dom {
@@ -482,7 +483,7 @@
   scoped_refptr<HTMLElement> new_html_element =
       document->html_element_context()
           ->html_element_factory()
-          ->CreateHTMLElement(document, tag_name());
+          ->CreateHTMLElement(document, local_name());
   new_html_element->CopyAttributes(*this);
   new_html_element->CopyDirectionality(*this);
   new_html_element->style_->AssignFrom(*this->style_);
@@ -622,11 +623,6 @@
   }
 }
 
-void HTMLElement::InvalidateComputedStylesRecursively() {
-  computed_style_valid_ = false;
-  Node::InvalidateComputedStylesRecursively();
-}
-
 void HTMLElement::UpdateComputedStyleRecursively(
     const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
         parent_computed_style_declaration,
@@ -666,38 +662,50 @@
   }
 }
 
-void HTMLElement::InvalidateLayoutBoxesFromNodeAndAncestors() {
-  layout_boxes_.reset();
-  ReleaseImagesAndInvalidateComputedStyleIfNecessary();
-  Node::InvalidateLayoutBoxesFromNodeAndAncestors();
+void HTMLElement::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
+  ClearActiveBackgroundImages();
+  if (!cached_background_images_.empty()) {
+    cached_background_images_.clear();
+    computed_style_valid_ = false;
+  }
+  PurgeCachedBackgroundImagesOfDescendants();
 }
 
-void HTMLElement::InvalidateLayoutBoxesFromNodeAndDescendants() {
-  layout_boxes_.reset();
-  ReleaseImagesAndInvalidateComputedStyleIfNecessary();
-  Node::InvalidateLayoutBoxesFromNodeAndDescendants();
+void HTMLElement::InvalidateComputedStylesOfNodeAndDescendants() {
+  computed_style_valid_ = false;
+  InvalidateComputedStylesOfDescendants();
 }
 
-void HTMLElement::InvalidateLayoutBoxSizesFromNode() {
+void HTMLElement::InvalidateLayoutBoxesOfNodeAndAncestors() {
+  layout_boxes_.reset();
+  InvalidateLayoutBoxesOfAncestors();
+}
+
+void HTMLElement::InvalidateLayoutBoxesOfNodeAndDescendants() {
+  layout_boxes_.reset();
+  InvalidateLayoutBoxesOfDescendants();
+}
+
+void HTMLElement::InvalidateLayoutBoxSizes() {
   if (layout_boxes_) {
     layout_boxes_->InvalidateSizes();
   }
 }
 
-void HTMLElement::InvalidateLayoutBoxCrossReferencesFromNode() {
+void HTMLElement::InvalidateLayoutBoxCrossReferences() {
   if (layout_boxes_) {
     layout_boxes_->InvalidateCrossReferences();
   }
 }
 
-void HTMLElement::InvalidateRenderTreeNodesFromNode() {
+void HTMLElement::InvalidateLayoutBoxRenderTreeNodes() {
   if (layout_boxes_) {
     layout_boxes_->InvalidateRenderTreeNodes();
   }
 }
 
-HTMLElement::HTMLElement(Document* document, base::Token tag_name)
-    : Element(document, tag_name),
+HTMLElement::HTMLElement(Document* document, base::Token local_name)
+    : Element(document, local_name),
       dom_stat_tracker_(document->html_element_context()->dom_stat_tracker()),
       directionality_(kNoExplicitDirectionality),
       style_(new cssom::CSSDeclaredStyleDeclaration(
@@ -760,8 +768,8 @@
   }
 
   if (directionality_ != previous_directionality) {
-    InvalidateLayoutBoxesFromNodeAndAncestors();
-    InvalidateLayoutBoxesFromNodeAndDescendants();
+    InvalidateLayoutBoxesOfNodeAndAncestors();
+    InvalidateLayoutBoxesOfDescendants();
   }
 }
 
@@ -817,20 +825,29 @@
 // Flags tracking which cached values must be invalidated.
 struct UpdateComputedStyleInvalidationFlags {
   UpdateComputedStyleInvalidationFlags()
-      : invalidate_descendant_computed_styles(false),
+      : purge_cached_background_images_of_descendants(false),
+        invalidate_computed_styles_of_descendants(false),
         invalidate_layout_boxes(false),
         invalidate_sizes(false),
         invalidate_cross_references(false),
         invalidate_render_tree_nodes(false) {}
 
-  bool invalidate_descendant_computed_styles;
+  bool purge_cached_background_images_of_descendants;
+  bool invalidate_computed_styles_of_descendants;
   bool invalidate_layout_boxes;
   bool invalidate_sizes;
   bool invalidate_cross_references;
   bool invalidate_render_tree_nodes;
 };
 
-bool NewComputedStyleInvalidatesDescendantComputedStyles(
+bool NewComputedStylePurgesCachedBackgroundImagesOfDescendants(
+    const scoped_refptr<const cssom::CSSComputedStyleData>& old_computed_style,
+    const scoped_refptr<cssom::CSSComputedStyleData>& new_computed_style) {
+  return old_computed_style->display() != cssom::KeywordValue::GetNone() &&
+         new_computed_style->display() == cssom::KeywordValue::GetNone();
+}
+
+bool NewComputedStyleInvalidatesComputedStylesOfDescendants(
     const scoped_refptr<const cssom::CSSComputedStyleData>& old_computed_style,
     const scoped_refptr<cssom::CSSComputedStyleData>& new_computed_style) {
   return !non_trivial_static_fields.Get()
@@ -865,15 +882,27 @@
                                          new_computed_style);
 }
 
-void UpdateInvalidationFlagsForNewComputedStyle(
+enum IsPseudoElement {
+  kIsNotPseudoElement,
+  kIsPseudoElement,
+};
+
+void UpdateInvalidationFlagsFromNewComputedStyle(
     const scoped_refptr<const cssom::CSSComputedStyleData>& old_computed_style,
     const scoped_refptr<cssom::CSSComputedStyleData>& new_computed_style,
-    bool animations_modified, UpdateComputedStyleInvalidationFlags* flags) {
+    bool animations_modified, IsPseudoElement is_pseudo_element,
+    UpdateComputedStyleInvalidationFlags* flags) {
   if (old_computed_style) {
-    if (!flags->invalidate_descendant_computed_styles &&
-        NewComputedStyleInvalidatesDescendantComputedStyles(
+    if (!flags->purge_cached_background_images_of_descendants &&
+        is_pseudo_element == kIsNotPseudoElement &&
+        NewComputedStylePurgesCachedBackgroundImagesOfDescendants(
             old_computed_style, new_computed_style)) {
-      flags->invalidate_descendant_computed_styles = true;
+      flags->purge_cached_background_images_of_descendants = true;
+    }
+    if (!flags->invalidate_computed_styles_of_descendants &&
+        NewComputedStyleInvalidatesComputedStylesOfDescendants(
+            old_computed_style, new_computed_style)) {
+      flags->invalidate_computed_styles_of_descendants = true;
       flags->invalidate_layout_boxes = true;
     } else if (!flags->invalidate_layout_boxes) {
       if (NewComputedStyleInvalidatesLayoutBoxes(old_computed_style,
@@ -965,9 +994,9 @@
             computed_style(), &css_transitions_, &css_animations_,
             document->keyframes_map(), &animations_modified);
 
-    UpdateInvalidationFlagsForNewComputedStyle(
+    UpdateInvalidationFlagsFromNewComputedStyle(
         computed_style(), new_computed_style, animations_modified,
-        &invalidation_flags);
+        kIsNotPseudoElement, &invalidation_flags);
 
     css_computed_style_declaration_->SetData(new_computed_style);
 
@@ -1003,9 +1032,9 @@
               pseudo_elements_[pseudo_element_type]->css_animations(),
               document->keyframes_map(), &animations_modified);
 
-      UpdateInvalidationFlagsForNewComputedStyle(
+      UpdateInvalidationFlagsFromNewComputedStyle(
           pseudo_elements_[pseudo_element_type]->computed_style(),
-          pseudo_element_computed_style, animations_modified,
+          pseudo_element_computed_style, animations_modified, kIsPseudoElement,
           &invalidation_flags);
 
       pseudo_elements_[pseudo_element_type]
@@ -1014,29 +1043,45 @@
     }
   }
 
-  if (invalidation_flags.invalidate_descendant_computed_styles) {
-    InvalidateComputedStylesRecursively();
+  if (invalidation_flags.purge_cached_background_images_of_descendants) {
+    PurgeCachedBackgroundImagesOfDescendants();
+  }
+  if (invalidation_flags.invalidate_computed_styles_of_descendants) {
+    InvalidateComputedStylesOfDescendants();
   }
 
   if (invalidation_flags.invalidate_layout_boxes) {
-    InvalidateLayoutBoxesFromNodeAndAncestors();
-    InvalidateLayoutBoxesFromNodeAndDescendants();
+    InvalidateLayoutBoxesOfNodeAndAncestors();
+    InvalidateLayoutBoxesOfDescendants();
   } else {
     if (invalidation_flags.invalidate_sizes) {
-      InvalidateLayoutBoxSizesFromNode();
+      InvalidateLayoutBoxSizes();
     }
     if (invalidation_flags.invalidate_cross_references) {
-      InvalidateLayoutBoxCrossReferencesFromNode();
+      InvalidateLayoutBoxCrossReferences();
     }
     if (invalidation_flags.invalidate_render_tree_nodes) {
-      InvalidateRenderTreeNodesFromNode();
+      InvalidateLayoutBoxRenderTreeNodes();
     }
   }
 
   computed_style_valid_ = true;
 }
 
+void HTMLElement::ClearActiveBackgroundImages() {
+  if (html_element_context() &&
+      html_element_context()->animated_image_tracker()) {
+    for (std::vector<GURL>::iterator it = active_background_images_.begin();
+         it != active_background_images_.end(); ++it) {
+      html_element_context()->animated_image_tracker()->DecreaseURLCount(*it);
+    }
+  }
+  active_background_images_.clear();
+}
+
 void HTMLElement::UpdateCachedBackgroundImagesFromComputedStyle() {
+  ClearActiveBackgroundImages();
+
   // Don't fetch or cache the image if the display of this element is turned
   // off.
   if (computed_style()->display() != cssom::KeywordValue::GetNone()) {
@@ -1055,19 +1100,26 @@
         continue;
       }
 
-      cssom::AbsoluteURLValue* absolute_url =
-          base::polymorphic_downcast<cssom::AbsoluteURLValue*>(
-              property_list_value->value()[i].get());
-      if (absolute_url->value().is_valid()) {
-        scoped_refptr<loader::image::CachedImage> cached_image =
-            html_element_context()->image_cache()->CreateCachedResource(
-                absolute_url->value());
-        base::Closure loaded_callback = base::Bind(
-            &HTMLElement::OnBackgroundImageLoaded, base::Unretained(this));
-        cached_images.push_back(
-            new loader::image::CachedImageReferenceWithCallbacks(
-                cached_image, loaded_callback, base::Closure()));
+      // Skip invalid URL.
+      GURL absolute_url = base::polymorphic_downcast<cssom::AbsoluteURLValue*>(
+                              property_list_value->value()[i].get())
+                              ->value();
+      if (!absolute_url.is_valid()) {
+        continue;
       }
+
+      active_background_images_.push_back(absolute_url);
+      html_element_context()->animated_image_tracker()->IncreaseURLCount(
+          absolute_url);
+
+      scoped_refptr<loader::image::CachedImage> cached_image =
+          html_element_context()->image_cache()->CreateCachedResource(
+              absolute_url);
+      base::Closure loaded_callback = base::Bind(
+          &HTMLElement::OnBackgroundImageLoaded, base::Unretained(this));
+      cached_images.push_back(
+          new loader::image::CachedImageReferenceWithCallbacks(
+              cached_image, loaded_callback, base::Closure()));
     }
 
     cached_background_images_ = cached_images.Pass();
@@ -1079,7 +1131,7 @@
 
 void HTMLElement::OnBackgroundImageLoaded() {
   node_document()->RecordMutation();
-  InvalidateRenderTreeNodesFromNode();
+  InvalidateLayoutBoxRenderTreeNodes();
 }
 
 bool HTMLElement::IsRootElement() {
@@ -1088,12 +1140,5 @@
   return AsHTMLHtmlElement() != NULL;
 }
 
-void HTMLElement::ReleaseImagesAndInvalidateComputedStyleIfNecessary() {
-  if (!cached_background_images_.empty()) {
-    cached_background_images_.clear();
-    computed_style_valid_ = false;
-  }
-}
-
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 995ed52..88bbccd 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -16,6 +16,7 @@
 #define COBALT_DOM_HTML_ELEMENT_H_
 
 #include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
@@ -202,8 +203,14 @@
     return css_computed_style_declaration_->data();
   }
 
-  // Invalidates the cached computed style of this element and its descendants.
-  void InvalidateComputedStylesRecursively() OVERRIDE;
+  void PurgeCachedBackgroundImagesOfNodeAndDescendants() OVERRIDE;
+  void InvalidateComputedStylesOfNodeAndDescendants() OVERRIDE;
+  void InvalidateLayoutBoxesOfNodeAndAncestors() OVERRIDE;
+  void InvalidateLayoutBoxesOfNodeAndDescendants() OVERRIDE;
+  void InvalidateLayoutBoxSizes() OVERRIDE;
+  void InvalidateLayoutBoxCrossReferences() OVERRIDE;
+  void InvalidateLayoutBoxRenderTreeNodes() OVERRIDE;
+
   // Updates the cached computed style of this element and its descendants.
   void UpdateComputedStyleRecursively(
       const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
@@ -230,11 +237,6 @@
   }
 
   LayoutBoxes* layout_boxes() const { return layout_boxes_.get(); }
-  void InvalidateLayoutBoxesFromNodeAndAncestors() OVERRIDE;
-  void InvalidateLayoutBoxesFromNodeAndDescendants() OVERRIDE;
-  void InvalidateLayoutBoxSizesFromNode() OVERRIDE;
-  void InvalidateLayoutBoxCrossReferencesFromNode() OVERRIDE;
-  void InvalidateRenderTreeNodesFromNode() OVERRIDE;
 
   // Determines whether this element is focusable.
   bool IsFocusable() const { return HasAttribute("tabindex"); }
@@ -254,15 +256,11 @@
   DEFINE_WRAPPABLE_TYPE(HTMLElement);
 
  protected:
-  HTMLElement(Document* document, base::Token tag_name);
+  HTMLElement(Document* document, base::Token local_name);
   ~HTMLElement() OVERRIDE;
 
   void CopyDirectionality(const HTMLElement& other);
 
-  // Releases image resources and invalidates computed style if there are images
-  // associated with this html element in the image cache.
-  void ReleaseImagesAndInvalidateComputedStyleIfNecessary() OVERRIDE;
-
   // HTMLElement keeps a pointer to the dom stat tracker to ensure that it can
   // make stat updates even after its weak pointer to its document has been
   // deleted. This is protected because some derived classes need access to it.
@@ -286,6 +284,10 @@
   // directionality does not invalidate the computed style.
   void SetDirectionality(const std::string& value);
 
+  // Clear the list of active background images, and notify the animated image
+  // tracker to stop the animations.
+  void ClearActiveBackgroundImages();
+
   void UpdateCachedBackgroundImagesFromComputedStyle();
 
   // This will be called when the image data associated with this element's
@@ -336,6 +338,8 @@
   scoped_ptr<PseudoElement> pseudo_elements_[kMaxPseudoElementType];
   base::WeakPtr<DOMStringMap> dataset_;
 
+  std::vector<GURL> active_background_images_;
+
   // |cached_background_images_| contains a list of CachedImage references for
   // all images referenced by the computed value for the background_image CSS
   // property and a image loaded handle to add and remove image loaded callback.
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index b069028..3cf96f0 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -24,8 +24,10 @@
     Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
     media::WebMediaPlayerFactory* web_media_player_factory,
     script::ScriptRunner* script_runner,
+    script::ScriptValueFactory* script_value_factory,
     MediaSourceRegistry* media_source_registry,
     render_tree::ResourceProvider** resource_provider,
+    loader::image::AnimatedImageTracker* animated_image_tracker,
     loader::image::ImageCache* image_cache,
     loader::image::ReducedCacheCapacityManager*
         reduced_image_cache_capacity_manager,
@@ -38,8 +40,10 @@
       can_play_type_handler_(can_play_type_handler),
       web_media_player_factory_(web_media_player_factory),
       script_runner_(script_runner),
+      script_value_factory_(script_value_factory),
       media_source_registry_(media_source_registry),
       resource_provider_(resource_provider),
+      animated_image_tracker_(animated_image_tracker),
       image_cache_(image_cache),
       reduced_image_cache_capacity_manager_(
           reduced_image_cache_capacity_manager),
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index fa71993..4650c31 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -25,11 +25,13 @@
 #include "cobalt/dom/url_registry.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/font/remote_typeface_cache.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
 #include "cobalt/loader/image/image_cache.h"
 #include "cobalt/loader/mesh/mesh_cache.h"
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/web_media_player_factory.h"
 #include "cobalt/script/script_runner.h"
+#include "cobalt/script/script_value_factory.h"
 
 namespace cobalt {
 namespace dom {
@@ -44,20 +46,21 @@
  public:
   typedef UrlRegistry<MediaSource> MediaSourceRegistry;
 
-  HTMLElementContext(loader::FetcherFactory* fetcher_factory,
-                     cssom::CSSParser* css_parser, Parser* dom_parser,
-                     media::CanPlayTypeHandler* can_play_type_handler,
-                     media::WebMediaPlayerFactory* web_media_player_factory,
-                     script::ScriptRunner* script_runner,
-                     MediaSourceRegistry* media_source_registry,
-                     render_tree::ResourceProvider** resource_provider,
-                     loader::image::ImageCache* image_cache,
-                     loader::image::ReducedCacheCapacityManager*
-                         reduced_image_cache_capacity_manager,
-                     loader::font::RemoteTypefaceCache* remote_typeface_cache,
-                     loader::mesh::MeshCache* mesh_cache,
-                     DomStatTracker* dom_stat_tracker,
-                     const std::string& language);
+  HTMLElementContext(
+      loader::FetcherFactory* fetcher_factory, cssom::CSSParser* css_parser,
+      Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
+      media::WebMediaPlayerFactory* web_media_player_factory,
+      script::ScriptRunner* script_runner,
+      script::ScriptValueFactory* script_value_factory,
+      MediaSourceRegistry* media_source_registry,
+      render_tree::ResourceProvider** resource_provider,
+      loader::image::AnimatedImageTracker* animated_image_tracker,
+      loader::image::ImageCache* image_cache,
+      loader::image::ReducedCacheCapacityManager*
+          reduced_image_cache_capacity_manager,
+      loader::font::RemoteTypefaceCache* remote_typeface_cache,
+      loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
+      const std::string& language);
   ~HTMLElementContext();
 
   loader::FetcherFactory* fetcher_factory() { return fetcher_factory_; }
@@ -73,7 +76,12 @@
     return web_media_player_factory_;
   }
 
-  script::ScriptRunner* script_runner() { return script_runner_; }
+  script::ScriptRunner* script_runner() const { return script_runner_; }
+
+  script::ScriptValueFactory* script_value_factory() const {
+    return script_value_factory_;
+  }
+
   MediaSourceRegistry* media_source_registry() {
     return media_source_registry_;
   }
@@ -82,6 +90,10 @@
     return resource_provider_;
   }
 
+  loader::image::AnimatedImageTracker* animated_image_tracker() const {
+    return animated_image_tracker_;
+  }
+
   loader::image::ImageCache* image_cache() const { return image_cache_; }
 
   loader::font::RemoteTypefaceCache* remote_typeface_cache() const {
@@ -112,8 +124,10 @@
   media::CanPlayTypeHandler* can_play_type_handler_;
   media::WebMediaPlayerFactory* const web_media_player_factory_;
   script::ScriptRunner* const script_runner_;
+  script::ScriptValueFactory* const script_value_factory_;
   MediaSourceRegistry* const media_source_registry_;
   render_tree::ResourceProvider** resource_provider_;
+  loader::image::AnimatedImageTracker* const animated_image_tracker_;
   loader::image::ImageCache* const image_cache_;
   loader::image::ReducedCacheCapacityManager* const
       reduced_image_cache_capacity_manager_;
diff --git a/src/cobalt/dom/html_element_factory.cc b/src/cobalt/dom/html_element_factory.cc
index a061e2f..e4cfeee 100644
--- a/src/cobalt/dom/html_element_factory.cc
+++ b/src/cobalt/dom/html_element_factory.cc
@@ -49,8 +49,8 @@
 
 template <typename T>
 scoped_refptr<HTMLElement> CreateHTMLElementWithTagNameT(
-    const std::string& tag_name, Document* document) {
-  return new T(document, base::Token(tag_name));
+    const std::string& local_name, Document* document) {
+  return new T(document, base::Token(local_name));
 }
 
 }  // namespace
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index 5c1828d..0b27f6f 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -55,7 +55,8 @@
             &fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
             NULL /* can_play_type_handler */,
             NULL /* web_media_player_factory */, &stub_script_runner_,
-            NULL /* media_source_registry */, NULL /* resource_provider */,
+            NULL /* script_value_factory */, NULL /* media_source_registry */,
+            NULL /* resource_provider */, NULL /* animated_image_tracker */,
             NULL /* image_cache */,
             NULL /* reduced_image_cache_capacity_manager */,
             NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index db94236..685ccab 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -108,7 +108,7 @@
   HTMLElementTest()
       : dom_stat_tracker_(new DomStatTracker("HTMLElementTest")),
         html_element_context_(NULL, &css_parser_, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               dom_stat_tracker_.get(), ""),
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementTest() OVERRIDE {}
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 06b08da..5e98f55 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -19,6 +19,7 @@
 
 #include "base/bind.h"
 #include "base/compiler_specific.h"
+#include "base/debug/trace_event.h"
 #include "base/guid.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -37,6 +38,7 @@
 #include "cobalt/dom/media_key_message_event.h"
 #include "cobalt/dom/media_key_needed_event.h"
 #include "cobalt/dom/media_source.h"
+#include "cobalt/dom/media_source_ready_state.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media/fetcher_buffered_data_source.h"
 #include "cobalt/media/web_media_player_factory.h"
@@ -144,11 +146,13 @@
       pending_load_(false),
       sent_stalled_event_(false),
       sent_end_event_(false) {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::HTMLMediaElement");
   MLOG();
   html_media_element_count_log.Get().count++;
 }
 
 HTMLMediaElement::~HTMLMediaElement() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::~HTMLMediaElement");
   MLOG();
   ClearMediaSource();
   html_media_element_count_log.Get().count--;
@@ -165,6 +169,7 @@
 }
 
 void HTMLMediaElement::set_src(const std::string& src) {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::set_src");
   MLOG() << src;
   SetAttribute("src", src);
   ClearMediaPlayer();
@@ -199,6 +204,7 @@
 }
 
 void HTMLMediaElement::Load() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Load");
   // LoadInternal may result in a 'beforeload' event, which can make arbitrary
   // DOM mutations.
   scoped_refptr<HTMLMediaElement> protect(this);
@@ -486,6 +492,7 @@
 }
 
 void HTMLMediaElement::Play() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Play");
   MLOG();
   // 4.8.10.9. Playing the media resource
   if (!player_ || network_state_ == kNetworkEmpty) {
@@ -512,6 +519,7 @@
 }
 
 void HTMLMediaElement::Pause() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Pause");
   MLOG();
   // 4.8.10.9. Playing the media resource
   if (!player_ || network_state_ == kNetworkEmpty) {
@@ -608,11 +616,13 @@
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
 void HTMLMediaElement::ScheduleEvent(const scoped_refptr<Event>& event) {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleEvent");
   MLOG() << event->type();
   event_queue_.Enqueue(event);
 }
 
 void HTMLMediaElement::CreateMediaPlayer() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::CreateMediaPlayer");
   MLOG();
   if (src().empty()) {
     reduced_image_cache_capacity_request_ = base::nullopt;
@@ -629,9 +639,7 @@
     // Set a new reduced cache capacity.
     reduced_image_cache_capacity_request_.emplace(
         html_element_context()->reduced_image_cache_capacity_manager());
-    // Empty all non-referenced images from the image cache to make room for
-    // the video decoder.
-    html_element_context()->image_cache()->Purge();
+
     // Ensure that all resource destructions are flushed and the memory is
     // reclaimed.
     if (*html_element_context()->resource_provider()) {
@@ -649,10 +657,11 @@
   }
 #endif  // !defined(COBALT_MEDIA_SOURCE_2016)
   node_document()->OnDOMMutation();
-  InvalidateLayoutBoxesFromNodeAndAncestors();
+  InvalidateLayoutBoxesOfNodeAndAncestors();
 }
 
 void HTMLMediaElement::ScheduleLoad() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleLoad");
   if (!pending_load_) {
     PrepareForLoad();
     pending_load_ = true;
@@ -665,6 +674,7 @@
 }
 
 void HTMLMediaElement::PrepareForLoad() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PrepareForLoad");
   // Perform the cleanup required for the resource load algorithm to run.
   StopPeriodicTimers();
   load_timer_.Stop();
@@ -728,6 +738,7 @@
 }
 
 void HTMLMediaElement::LoadInternal() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::LoadInternal");
   DCHECK(node_document());
 
   // Select media resource.
@@ -858,6 +869,7 @@
 }
 
 void HTMLMediaElement::ClearMediaPlayer() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ClearMediaPlayer");
   MLOG();
 
   ClearMediaSource();
@@ -879,6 +891,9 @@
 
 void HTMLMediaElement::NoneSupported() {
   MLOG();
+
+  DLOG(WARNING) << "HTMLMediaElement::NoneSupported() error.";
+
   StopPeriodicTimers();
   load_state_ = kWaitingForSource;
 
@@ -921,6 +936,7 @@
 // This is kept as a one shot timer to be in sync with the original code. It
 // should be replaced by PostTask if this is any future rewrite.
 void HTMLMediaElement::OnLoadTimer() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::OnLoadTimer");
   scoped_refptr<HTMLMediaElement> protect(this);
 
   if (pending_load_) {
@@ -1202,7 +1218,8 @@
   // Always notify the media engine of a seek if the source is not closed. This
   // ensures that the source is always in a flushed state when the 'seeking'
   // event fires.
-  if (media_source_ && media_source_->ready_state() != MediaSource::kClosed) {
+  if (media_source_ &&
+      media_source_->ready_state() != kMediaSourceReadyStateClosed) {
     no_seek_required = false;
   }
 
@@ -1499,6 +1516,7 @@
 
 #if defined(COBALT_MEDIA_SOURCE_2016)
 void HTMLMediaElement::SourceOpened(media::ChunkDemuxer* chunk_demuxer) {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SourceOpened");
   BeginProcessingMediaPlayerCallback();
   DCHECK(media_source_);
   media_source_->SetChunkDemuxerAndOpen(chunk_demuxer);
@@ -1507,7 +1525,7 @@
 #else   // defined(COBALT_MEDIA_SOURCE_2016)
 void HTMLMediaElement::SourceOpened() {
   BeginProcessingMediaPlayerCallback();
-  SetSourceState(MediaSource::kOpen);
+  SetSourceState(kMediaSourceReadyStateOpen);
   EndProcessingMediaPlayerCallback();
 }
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
@@ -1528,6 +1546,15 @@
   return map_to_mesh_filter;
 }
 
+#if defined(COBALT_MEDIA_SOURCE_2016)
+void HTMLMediaElement::EncryptedMediaInitData(
+    media::EmeInitDataType /*init_data_type*/,
+    const unsigned char* /*init_data*/, unsigned int /*init_data_length*/) {
+  // TODO: Implement as per EME 2017.
+  NOTIMPLEMENTED();
+}
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+
 void HTMLMediaElement::KeyAdded(const std::string& key_system,
                                 const std::string& session_id) {
   MLOG() << key_system;
@@ -1596,18 +1623,18 @@
     media_source_ = NULL;
   }
 #else  // defined(COBALT_MEDIA_SOURCE_2016)
-  SetSourceState(MediaSource::kClosed);
+  SetSourceState(kMediaSourceReadyStateClosed);
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 }
 
 #if !defined(COBALT_MEDIA_SOURCE_2016)
-void HTMLMediaElement::SetSourceState(MediaSource::ReadyState ready_state) {
+void HTMLMediaElement::SetSourceState(MediaSourceReadyState ready_state) {
   MLOG() << ready_state;
   if (!media_source_) {
     return;
   }
   media_source_->SetReadyState(ready_state);
-  if (ready_state == MediaSource::kClosed) {
+  if (ready_state == kMediaSourceReadyStateClosed) {
     media_source_ = NULL;
   }
 }
@@ -1618,7 +1645,7 @@
   // the video render tree differently depending on whether we are in punch-out
   // or decode-to-texture.
   node_document()->OnDOMMutation();
-  InvalidateLayoutBoxesFromNodeAndAncestors();
+  InvalidateLayoutBoxesOfNodeAndAncestors();
 }
 
 }  // namespace dom
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index ce08323..73b7a8e 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -231,6 +231,11 @@
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
   std::string SourceURL() const OVERRIDE;
   bool PreferDecodeToTexture() const OVERRIDE;
+#if defined(COBALT_MEDIA_SOURCE_2016)
+  void EncryptedMediaInitData(media::EmeInitDataType init_data_type,
+                              const unsigned char* init_data,
+                              unsigned int init_data_length) OVERRIDE;
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
   void KeyAdded(const std::string& key_system,
                 const std::string& session_id) OVERRIDE;
   void KeyError(const std::string& key_system, const std::string& session_id,
@@ -243,7 +248,7 @@
                  unsigned int init_data_length) OVERRIDE;
   void ClearMediaSource();
 #if !defined(COBALT_MEDIA_SOURCE_2016)
-  void SetSourceState(MediaSource::ReadyState ready_state);
+  void SetSourceState(MediaSourceReadyState ready_state);
 #endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 
   // Called whenever the player's output mode (e.g. punch-out,
diff --git a/src/cobalt/dom/html_meta_element.cc b/src/cobalt/dom/html_meta_element.cc
index 325a366..9e2ea63 100644
--- a/src/cobalt/dom/html_meta_element.cc
+++ b/src/cobalt/dom/html_meta_element.cc
@@ -19,6 +19,7 @@
 #include "cobalt/csp/content_security_policy.h"
 #include "cobalt/dom/csp_delegate.h"
 #include "cobalt/dom/document.h"
+#include "cobalt/dom/html_head_element.h"
 #include "cobalt/dom/node.h"
 
 namespace cobalt {
@@ -56,7 +57,9 @@
 bool HTMLMetaElement::IsDescendantOfHeadElement() const {
   for (scoped_refptr<Node> node = parent_node(); node;
        node = node->parent_node()) {
-    if (node->node_name() == "head") return true;
+    if (node->AsElement() && node->AsElement()->AsHTMLElement() &&
+        node->AsElement()->AsHTMLElement()->AsHTMLHeadElement())
+      return true;
   }
   return false;
 }
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index 840458d..c7a3362 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -517,7 +517,8 @@
   // script was obtained, the script block's type as the scripting language, and
   // the script settings object of the script element's Document's Window
   // object.
-  html_element_context()->script_runner()->Execute(content, script_location);
+  html_element_context()->script_runner()->Execute(content, script_location,
+      NULL /* output: succeeded */);
 
   // 5. 6. Not needed by Cobalt.
 
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_key_status.idl
similarity index 64%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_key_status.idl
index d335495..583215a 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_key_status.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/2016/CR-encrypted-media-20160705/#mediakeystatusmap-interface
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum MediaKeyStatus {
+    "usable",
+    "expired",
+    "released",
+    "output-restricted",
+    "output-downscaled",
+    "status-pending",
+    "internal-error"
+};
diff --git a/src/cobalt/dom/media_key_status_map.h b/src/cobalt/dom/media_key_status_map.h
index abcee44..414b0f6 100644
--- a/src/cobalt/dom/media_key_status_map.h
+++ b/src/cobalt/dom/media_key_status_map.h
@@ -27,17 +27,6 @@
 
 class MediaKeyStatusMap : public script::Wrappable {
  public:
-  enum MediaKeyStatus {
-    kUsable,
-    kExpired,
-    kReleased,
-    kOutputRestricted,
-    kOutputDownscaled,
-    kStatusPending,
-    kInternalError,
-    kMaxMediaKeyStatus,
-  };
-
   typedef script::UnionType2<scoped_refptr<ArrayBufferView>,
                              scoped_refptr<ArrayBuffer> > BufferSource;
   uint32_t size() const { return 0; }
diff --git a/src/cobalt/dom/media_key_system_configuration.idl b/src/cobalt/dom/media_key_system_configuration.idl
index 6bbde5c..0ac303b 100644
--- a/src/cobalt/dom/media_key_system_configuration.idl
+++ b/src/cobalt/dom/media_key_system_configuration.idl
@@ -14,12 +14,6 @@
 
 // https://www.w3.org/TR/2016/CR-encrypted-media-20160705/#mediakeysystemconfiguration-dictionary
 
-enum MediaKeysRequirement {
-  "required",
-  "optional",
-  "not-allowed"
-};
-
 dictionary MediaKeySystemConfiguration {
   DOMString                               label = "";
   sequence<DOMString>                     initDataTypes = [];
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_keys_requirement.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_keys_requirement.idl
index d335495..cf125bb 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_keys_requirement.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/2016/CR-encrypted-media-20160705/#mediakeysystemconfiguration-dictionary
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum MediaKeysRequirement {
+  "required",
+  "optional",
+  "not-allowed"
+};
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index 335fb8d..20cc705 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -73,7 +73,7 @@
 }  // namespace
 
 MediaSource::MediaSource()
-    : ready_state_(kClosed),
+    : ready_state_(kMediaSourceReadyStateClosed),
       player_(NULL),
       ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
       source_buffers_(new SourceBufferList(&event_queue_)) {}
@@ -90,7 +90,7 @@
 double MediaSource::duration(script::ExceptionState* exception_state) const {
   UNREFERENCED_PARAMETER(exception_state);
 
-  if (ready_state_ == kClosed) {
+  if (ready_state_ == kMediaSourceReadyStateClosed) {
     return std::numeric_limits<float>::quiet_NaN();
   }
 
@@ -104,7 +104,7 @@
     DOMException::Raise(DOMException::kInvalidAccessErr, exception_state);
     return;
   }
-  if (ready_state_ != kOpen) {
+  if (ready_state_ != kMediaSourceReadyStateOpen) {
     DOMException::Raise(DOMException::kInvalidAccessErr, exception_state);
     return;
   }
@@ -131,7 +131,7 @@
     return NULL;
   }
 
-  if (!player_ || ready_state_ != kOpen) {
+  if (!player_ || ready_state_ != kMediaSourceReadyStateOpen) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     // Return value should be ignored.
     return NULL;
@@ -182,18 +182,16 @@
   player_->SourceRemoveId(source_buffer->id());
 }
 
-MediaSource::ReadyState MediaSource::ready_state() const {
-  return ready_state_;
-}
+MediaSourceReadyState MediaSource::ready_state() const { return ready_state_; }
 
 void MediaSource::EndOfStream(script::ExceptionState* exception_state) {
   // If there is no error string provided, treat it as empty.
-  EndOfStream(kNoError, exception_state);
+  EndOfStream(kMediaSourceEndOfStreamErrorNoError, exception_state);
 }
 
-void MediaSource::EndOfStream(EndOfStreamError error,
+void MediaSource::EndOfStream(MediaSourceEndOfStreamError error,
                               script::ExceptionState* exception_state) {
-  if (!player_ || ready_state_ != kOpen) {
+  if (!player_ || ready_state_ != kMediaSourceReadyStateOpen) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
@@ -201,18 +199,18 @@
   WebMediaPlayer::EndOfStreamStatus eos_status =
       WebMediaPlayer::kEndOfStreamStatusNoError;
 
-  if (error == kNoError) {
+  if (error == kMediaSourceEndOfStreamErrorNoError) {
     eos_status = WebMediaPlayer::kEndOfStreamStatusNoError;
-  } else if (error == kNetwork) {
+  } else if (error == kMediaSourceEndOfStreamErrorNetwork) {
     eos_status = WebMediaPlayer::kEndOfStreamStatusNetworkError;
-  } else if (error == kDecode) {
+  } else if (error == kMediaSourceEndOfStreamErrorDecode) {
     eos_status = WebMediaPlayer::kEndOfStreamStatusDecodeError;
   } else {
     DOMException::Raise(DOMException::kInvalidAccessErr, exception_state);
     return;
   }
 
-  SetReadyState(kEnded);
+  SetReadyState(kMediaSourceReadyStateEnded);
   player_->SourceEndOfStream(eos_status);
 }
 
@@ -243,7 +241,7 @@
 scoped_refptr<TimeRanges> MediaSource::GetBuffered(
     const SourceBuffer* source_buffer,
     script::ExceptionState* exception_state) {
-  if (!player_ || ready_state_ == kClosed) {
+  if (!player_ || ready_state_ == kMediaSourceReadyStateClosed) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     // Return value should be ignored.
     return NULL;
@@ -263,7 +261,7 @@
 bool MediaSource::SetTimestampOffset(const SourceBuffer* source_buffer,
                                      double timestamp_offset,
                                      script::ExceptionState* exception_state) {
-  if (!player_ || ready_state_ == kClosed) {
+  if (!player_ || ready_state_ == kMediaSourceReadyStateClosed) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     // Return value should be ignored.
     return false;
@@ -284,13 +282,13 @@
     return;
   }
 
-  if (!player_ || ready_state_ == kClosed) {
+  if (!player_ || ready_state_ == kMediaSourceReadyStateClosed) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
 
-  if (ready_state_ == kEnded) {
-    SetReadyState(kOpen);
+  if (ready_state_ == kMediaSourceReadyStateEnded) {
+    SetReadyState(kMediaSourceReadyStateOpen);
   }
 
   TRACE_EVENT1("media_stack", "MediaSource::Append()", "size", size);
@@ -314,7 +312,7 @@
 
 void MediaSource::Abort(const SourceBuffer* source_buffer,
                         script::ExceptionState* exception_state) {
-  if (!player_ || ready_state_ != kOpen) {
+  if (!player_ || ready_state_ != kMediaSourceReadyStateOpen) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
@@ -323,27 +321,28 @@
   DCHECK(aborted);
 }
 
-void MediaSource::SetReadyState(ReadyState ready_state) {
+void MediaSource::SetReadyState(MediaSourceReadyState ready_state) {
   if (ready_state_ == ready_state) {
     return;
   }
 
-  ReadyState old_state = ready_state_;
+  MediaSourceReadyState old_state = ready_state_;
   ready_state_ = ready_state;
 
-  if (ready_state_ == kClosed) {
+  if (ready_state_ == kMediaSourceReadyStateClosed) {
     source_buffers_->Clear();
     player_ = NULL;
     ScheduleEvent(base::Tokens::sourceclose());
     return;
   }
 
-  if (old_state == kOpen && ready_state_ == kEnded) {
+  if (old_state == kMediaSourceReadyStateOpen &&
+      ready_state_ == kMediaSourceReadyStateEnded) {
     ScheduleEvent(base::Tokens::sourceended());
     return;
   }
 
-  if (ready_state_ == kOpen) {
+  if (ready_state_ == kMediaSourceReadyStateOpen) {
     ScheduleEvent(base::Tokens::sourceopen());
   }
 }
diff --git a/src/cobalt/dom/media_source.h b/src/cobalt/dom/media_source.h
index 352983d..d266621 100644
--- a/src/cobalt/dom/media_source.h
+++ b/src/cobalt/dom/media_source.h
@@ -26,6 +26,8 @@
 #include "base/memory/ref_counted.h"
 #include "cobalt/dom/event_queue.h"
 #include "cobalt/dom/event_target.h"
+#include "cobalt/dom/media_source_end_of_stream_error.h"
+#include "cobalt/dom/media_source_ready_state.h"
 #include "cobalt/dom/source_buffer.h"
 #include "cobalt/dom/source_buffer_list.h"
 #include "cobalt/dom/url_registry.h"
@@ -48,12 +50,6 @@
  public:
   typedef UrlRegistry<MediaSource> Registry;
 
-  // Web API: MediaSource
-  //
-  enum EndOfStreamError { kNoError, kNetwork, kDecode };
-
-  enum ReadyState { kClosed, kOpen, kEnded };
-
   // Custom, not in any spec.
   //
   MediaSource();
@@ -69,10 +65,10 @@
   void RemoveSourceBuffer(const scoped_refptr<SourceBuffer>& source_buffer,
                           script::ExceptionState* exception_state);
 
-  ReadyState ready_state() const;
+  MediaSourceReadyState ready_state() const;
 
   void EndOfStream(script::ExceptionState* exception_state);
-  void EndOfStream(EndOfStreamError error,
+  void EndOfStream(MediaSourceEndOfStreamError error,
                    script::ExceptionState* exception_state);
 
   static bool IsTypeSupported(script::EnvironmentSettings* settings,
@@ -99,7 +95,7 @@
              script::ExceptionState* exception_state);
 
   // Methods used by HTMLMediaElement
-  void SetReadyState(ReadyState ready_state);
+  void SetReadyState(MediaSourceReadyState ready_state);
 
   DEFINE_WRAPPABLE_TYPE(MediaSource);
 
@@ -107,7 +103,7 @@
   // From EventTarget.
   std::string GetDebugName() OVERRIDE { return "MediaSource"; }
 
-  ReadyState ready_state_;
+  MediaSourceReadyState ready_state_;
   ::media::WebMediaPlayer* player_;
   EventQueue event_queue_;
   scoped_refptr<SourceBufferList> source_buffers_;
diff --git a/src/cobalt/dom/media_source.idl b/src/cobalt/dom/media_source.idl
index ee69d48..e1c230d 100644
--- a/src/cobalt/dom/media_source.idl
+++ b/src/cobalt/dom/media_source.idl
@@ -15,30 +15,19 @@
 // https://www.w3.org/TR/media-source/#idl-def-mediasource
 // https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
 
-enum EndOfStreamError {
-  "network",
-  "decode"
-};
-
-enum ReadyState {
-  "closed",
-  "open",
-  "ended"
-};
-
 [Constructor] interface MediaSource : EventTarget {
   // All the source buffers created by this object.
   readonly attribute SourceBufferList sourceBuffers;
   // Subset of sourceBuffers that provide data for the selected/enabled tracks.
   readonly attribute SourceBufferList activeSourceBuffers;
 
-  readonly attribute ReadyState readyState;
+  readonly attribute MediaSourceReadyState readyState;
   [RaisesException] attribute unrestricted double duration;
 
   [RaisesException] SourceBuffer addSourceBuffer(DOMString type);
   [RaisesException] void removeSourceBuffer(SourceBuffer sourceBuffer);
 
-  [RaisesException] void endOfStream(optional EndOfStreamError error);
+  [RaisesException] void endOfStream(optional MediaSourceEndOfStreamError error);
 
   [Conditional=COBALT_MEDIA_SOURCE_2016, RaisesException]
       void setLiveSeekableRange(double start, double end);
diff --git a/src/cobalt/dom/media_source/media_source.cc b/src/cobalt/dom/media_source/media_source.cc
index 614d305..63abdec 100644
--- a/src/cobalt/dom/media_source/media_source.cc
+++ b/src/cobalt/dom/media_source/media_source.cc
@@ -110,13 +110,13 @@
 
 MediaSource::MediaSource()
     : chunk_demuxer_(NULL),
-      ready_state_(kClosed),
+      ready_state_(kMediaSourceReadyStateClosed),
       ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
       source_buffers_(new SourceBufferList(&event_queue_)),
       active_source_buffers_(new SourceBufferList(&event_queue_)),
       live_seekable_range_(new TimeRanges) {}
 
-MediaSource::~MediaSource() { SetReadyState(kClosed); }
+MediaSource::~MediaSource() { SetReadyState(kMediaSourceReadyStateClosed); }
 
 scoped_refptr<SourceBufferList> MediaSource::source_buffers() const {
   return source_buffers_;
@@ -126,14 +126,12 @@
   return active_source_buffers_;
 }
 
-MediaSource::ReadyState MediaSource::ready_state() const {
-  return ready_state_;
-}
+MediaSourceReadyState MediaSource::ready_state() const { return ready_state_; }
 
 double MediaSource::duration(script::ExceptionState* exception_state) const {
   UNREFERENCED_PARAMETER(exception_state);
 
-  if (ready_state_ == kClosed) {
+  if (ready_state_ == kMediaSourceReadyStateClosed) {
     return std::numeric_limits<float>::quiet_NaN();
   }
 
@@ -262,23 +260,23 @@
 
 void MediaSource::EndOfStream(script::ExceptionState* exception_state) {
   // If there is no error string provided, treat it as empty.
-  EndOfStream(kNoError, exception_state);
+  EndOfStream(kMediaSourceEndOfStreamErrorNoError, exception_state);
 }
 
-void MediaSource::EndOfStream(EndOfStreamError error,
+void MediaSource::EndOfStream(MediaSourceEndOfStreamError error,
                               script::ExceptionState* exception_state) {
   if (!IsOpen() || IsUpdating()) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
 
-  SetReadyState(kEnded);
+  SetReadyState(kMediaSourceReadyStateEnded);
 
   PipelineStatus pipeline_status = PIPELINE_OK;
 
-  if (error == kNetwork) {
+  if (error == kMediaSourceEndOfStreamErrorNetwork) {
     pipeline_status = CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR;
-  } else if (error == kDecode) {
+  } else if (error == kMediaSourceEndOfStreamErrorDecode) {
     pipeline_status = CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR;
   }
 
@@ -352,12 +350,14 @@
   DCHECK(!chunk_demuxer_);
   DCHECK(attached_element_);
   chunk_demuxer_ = chunk_demuxer;
-  SetReadyState(kOpen);
+  SetReadyState(kMediaSourceReadyStateOpen);
 }
 
-void MediaSource::Close() { SetReadyState(kClosed); }
+void MediaSource::Close() { SetReadyState(kMediaSourceReadyStateClosed); }
 
-bool MediaSource::IsClosed() const { return ready_state_ == kClosed; }
+bool MediaSource::IsClosed() const {
+  return ready_state_ == kMediaSourceReadyStateClosed;
+}
 
 scoped_refptr<TimeRanges> MediaSource::GetBufferedRange() const {
   std::vector<scoped_refptr<TimeRanges> > ranges(
@@ -386,7 +386,7 @@
   scoped_refptr<TimeRanges> intersection_ranges =
       new TimeRanges(0, highest_end_time);
 
-  bool ended = ready_state() == kEnded;
+  bool ended = ready_state() == kMediaSourceReadyStateEnded;
   for (size_t i = 0; i < ranges.size(); ++i) {
     scoped_refptr<TimeRanges> source_ranges = ranges[i].get();
 
@@ -471,15 +471,17 @@
 }
 
 void MediaSource::OpenIfInEndedState() {
-  if (ready_state_ != kEnded) {
+  if (ready_state_ != kMediaSourceReadyStateEnded) {
     return;
   }
 
-  SetReadyState(kOpen);
+  SetReadyState(kMediaSourceReadyStateOpen);
   chunk_demuxer_->UnmarkEndOfStream();
 }
 
-bool MediaSource::IsOpen() const { return ready_state_ == kOpen; }
+bool MediaSource::IsOpen() const {
+  return ready_state_ == kMediaSourceReadyStateOpen;
+}
 
 void MediaSource::SetSourceBufferActive(SourceBuffer* source_buffer,
                                         bool is_active) {
@@ -513,8 +515,8 @@
   return attached_element_;
 }
 
-void MediaSource::SetReadyState(ReadyState ready_state) {
-  if (ready_state == kClosed) {
+void MediaSource::SetReadyState(MediaSourceReadyState ready_state) {
+  if (ready_state == kMediaSourceReadyStateClosed) {
     chunk_demuxer_ = NULL;
   }
 
@@ -522,7 +524,7 @@
     return;
   }
 
-  ReadyState old_state = ready_state_;
+  MediaSourceReadyState old_state = ready_state_;
   ready_state_ = ready_state;
 
   if (IsOpen()) {
@@ -530,7 +532,8 @@
     return;
   }
 
-  if (old_state == kOpen && ready_state_ == kEnded) {
+  if (old_state == kMediaSourceReadyStateOpen &&
+      ready_state_ == kMediaSourceReadyStateEnded) {
     ScheduleEvent(base::Tokens::sourceended());
     return;
   }
diff --git a/src/cobalt/dom/media_source/media_source.h b/src/cobalt/dom/media_source/media_source.h
index e205317..234768d 100644
--- a/src/cobalt/dom/media_source/media_source.h
+++ b/src/cobalt/dom/media_source/media_source.h
@@ -57,6 +57,8 @@
 #include "cobalt/dom/html_media_element.h"
 #include "cobalt/dom/media_source/source_buffer.h"
 #include "cobalt/dom/media_source/source_buffer_list.h"
+#include "cobalt/dom/media_source_end_of_stream_error.h"
+#include "cobalt/dom/media_source_ready_state.h"
 #include "cobalt/dom/time_ranges.h"
 #include "cobalt/dom/url_registry.h"
 #include "cobalt/dom/video_track.h"
@@ -74,12 +76,6 @@
  public:
   typedef UrlRegistry<MediaSource> Registry;
 
-  // Web API: MediaSource
-  //
-  enum EndOfStreamError { kNoError, kNetwork, kDecode };
-
-  enum ReadyState { kClosed, kOpen, kEnded };
-
   // Custom, not in any spec.
   //
   MediaSource();
@@ -89,7 +85,7 @@
   //
   scoped_refptr<SourceBufferList> source_buffers() const;
   scoped_refptr<SourceBufferList> active_source_buffers() const;
-  ReadyState ready_state() const;
+  MediaSourceReadyState ready_state() const;
   double duration(script::ExceptionState* exception_state) const;
   void set_duration(double duration, script::ExceptionState* exception_state);
   scoped_refptr<SourceBuffer> AddSourceBuffer(
@@ -98,7 +94,7 @@
                           script::ExceptionState* exception_state);
 
   void EndOfStream(script::ExceptionState* exception_state);
-  void EndOfStream(EndOfStreamError error,
+  void EndOfStream(MediaSourceEndOfStreamError error,
                    script::ExceptionState* exception_state);
   void SetLiveSeekableRange(double start, double end,
                             script::ExceptionState* exception_state);
@@ -128,12 +124,12 @@
   DEFINE_WRAPPABLE_TYPE(MediaSource);
 
  private:
-  void SetReadyState(ReadyState ready_state);
+  void SetReadyState(MediaSourceReadyState ready_state);
   bool IsUpdating() const;
   void ScheduleEvent(base::Token eventName);
 
   media::ChunkDemuxer* chunk_demuxer_;
-  ReadyState ready_state_;
+  MediaSourceReadyState ready_state_;
   EventQueue event_queue_;
   base::WeakPtr<HTMLMediaElement> attached_element_;
 
diff --git a/src/cobalt/dom/media_source/source_buffer.cc b/src/cobalt/dom/media_source/source_buffer.cc
index 9ea4ce5..0f1981d 100644
--- a/src/cobalt/dom/media_source/source_buffer.cc
+++ b/src/cobalt/dom/media_source/source_buffer.cc
@@ -93,7 +93,7 @@
       media_source_(media_source),
       track_defaults_(new TrackDefaultList(NULL)),
       event_queue_(event_queue),
-      mode_(kSegments),
+      mode_(kSourceBufferAppendModeSegments),
       updating_(false),
       timestamp_offset_(0),
       audio_tracks_(new AudioTrackList(media_source->GetMediaElement())),
@@ -113,7 +113,7 @@
       id_, base::Bind(&SourceBuffer::InitSegmentReceived, this));
 }
 
-void SourceBuffer::set_mode(AppendMode mode,
+void SourceBuffer::set_mode(SourceBufferAppendMode mode,
                             script::ExceptionState* exception_state) {
   if (media_source_ == NULL) {
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
@@ -131,7 +131,7 @@
     return;
   }
 
-  chunk_demuxer_->SetSequenceMode(id_, mode == kSequence);
+  chunk_demuxer_->SetSequenceMode(id_, mode == kSourceBufferAppendModeSequence);
 
   mode_ = mode;
 }
@@ -471,7 +471,7 @@
 
   ScheduleEvent(base::Tokens::error());
   ScheduleEvent(base::Tokens::updateend());
-  media_source_->EndOfStream(MediaSource::kDecode, NULL);
+  media_source_->EndOfStream(kMediaSourceEndOfStreamErrorDecode, NULL);
 }
 
 void SourceBuffer::OnRemoveTimer() {
diff --git a/src/cobalt/dom/media_source/source_buffer.h b/src/cobalt/dom/media_source/source_buffer.h
index 9685837..7e508bc 100644
--- a/src/cobalt/dom/media_source/source_buffer.h
+++ b/src/cobalt/dom/media_source/source_buffer.h
@@ -59,6 +59,7 @@
 #include "cobalt/dom/audio_track_list.h"
 #include "cobalt/dom/event_queue.h"
 #include "cobalt/dom/event_target.h"
+#include "cobalt/dom/source_buffer_append_mode.h"
 #include "cobalt/dom/time_ranges.h"
 #include "cobalt/dom/track_default_list.h"
 #include "cobalt/dom/video_track_list.h"
@@ -76,13 +77,6 @@
 //   https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
 class SourceBuffer : public dom::EventTarget {
  public:
-  // Web API: SourceBuffer
-  //
-  enum AppendMode {
-    kSegments,
-    kSequence,
-  };
-
   // Custom, not in any spec.
   //
   SourceBuffer(const std::string& id, MediaSource* media_source,
@@ -90,11 +84,12 @@
 
   // Web API: SourceBuffer
   //
-  AppendMode mode(script::ExceptionState* exception_state) const {
+  SourceBufferAppendMode mode(script::ExceptionState* exception_state) const {
     UNREFERENCED_PARAMETER(exception_state);
     return mode_;
   }
-  void set_mode(AppendMode mode, script::ExceptionState* exception_state);
+  void set_mode(SourceBufferAppendMode mode,
+                script::ExceptionState* exception_state);
   bool updating() const { return updating_; }
   scoped_refptr<TimeRanges> buffered(
       script::ExceptionState* exception_state) const;
@@ -173,7 +168,7 @@
   scoped_refptr<TrackDefaultList> track_defaults_;
   EventQueue* event_queue_;
 
-  AppendMode mode_;
+  SourceBufferAppendMode mode_;
   bool updating_;
   double timestamp_offset_;
   scoped_refptr<AudioTrackList> audio_tracks_;
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_source_end_of_stream_error.idl
similarity index 63%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_source_end_of_stream_error.idl
index d335495..ca51be0 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_source_end_of_stream_error.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/media-source/#idl-def-mediasource
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum MediaSourceEndOfStreamError {
+  "network",
+  "decode",
+
+  // not part of spec, used in Cobalt implementation
+  "no-error",
+};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_source_ready_state.idl
similarity index 68%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_source_ready_state.idl
index d335495..7528b74 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_source_ready_state.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/media-source/#idl-def-mediasource
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum MediaSourceReadyState {
+  "closed",
+  "open",
+  "ended"
+};
diff --git a/src/cobalt/dom/mutation_observer.cc b/src/cobalt/dom/mutation_observer.cc
index f8a5a82..b237491 100644
--- a/src/cobalt/dom/mutation_observer.cc
+++ b/src/cobalt/dom/mutation_observer.cc
@@ -18,9 +18,24 @@
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
 #include "cobalt/dom/node.h"
+#include "cobalt/script/exception_message.h"
 
 namespace cobalt {
 namespace dom {
+namespace {
+// script::ExceptionState that will DCHECK on exception.
+class NativeExceptionState : public script::ExceptionState {
+ public:
+  void SetException(const scoped_refptr<script::ScriptException>&) OVERRIDE {
+    NOTREACHED();
+  }
+
+  void SetSimpleExceptionVA(script::SimpleExceptionType, const char*,
+                            va_list) OVERRIDE {
+    NOTREACHED();
+  }
+};
+}  // namespace
 // Abstract base class for a MutationCallback.
 class MutationObserver::CallbackInternal {
  public:
@@ -87,17 +102,8 @@
 
 void MutationObserver::Observe(const scoped_refptr<Node>& target,
                                const MutationObserverInit& options) {
-  if (!target) {
-    // |target| is not nullable, so if this is NULL that indicates a bug in the
-    // bindings layer.
-    NOTREACHED();
-    return;
-  }
-  if (!target->RegisterMutationObserver(make_scoped_refptr(this), options)) {
-    // TODO: Throw TypeError.
-    NOTREACHED();
-  }
-  TrackObservedNode(target);
+  NativeExceptionState exception_state;
+  ObserveInternal(target, options, &exception_state);
 }
 
 void MutationObserver::Disconnect() {
@@ -164,5 +170,22 @@
   observed_nodes_.push_back(base::AsWeakPtr(node.get()));
 }
 
+void MutationObserver::ObserveInternal(
+    const scoped_refptr<Node>& target, const MutationObserverInit& options,
+    script::ExceptionState* exception_state) {
+  if (!target) {
+    // |target| is not nullable, so if this is NULL that indicates a bug in the
+    // bindings layer.
+    NOTREACHED();
+    return;
+  }
+  if (!target->RegisterMutationObserver(make_scoped_refptr(this), options)) {
+    // This fails if the options are invalid.
+    exception_state->SetSimpleException(script::kTypeError, "Invalid options.");
+    return;
+  }
+  TrackObservedNode(target);
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/mutation_observer.h b/src/cobalt/dom/mutation_observer.h
index 87817e6..ea7acb8 100644
--- a/src/cobalt/dom/mutation_observer.h
+++ b/src/cobalt/dom/mutation_observer.h
@@ -24,6 +24,7 @@
 #include "cobalt/dom/mutation_record.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/environment_settings.h"
+#include "cobalt/script/exception_state.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
@@ -63,7 +64,15 @@
   ~MutationObserver();
 
   void Observe(const scoped_refptr<Node>& target,
+               const MutationObserverInit& options,
+               script::ExceptionState* exception_state) {
+    ObserveInternal(target, options, exception_state);
+  }
+
+  // Call this from native code. Will DCHECK on exception.
+  void Observe(const scoped_refptr<Node>& target,
                const MutationObserverInit& options);
+
   void Disconnect();
   MutationRecordSequence TakeRecords();
 
@@ -87,6 +96,10 @@
  private:
   void TrackObservedNode(const scoped_refptr<dom::Node>& node);
 
+  void ObserveInternal(const scoped_refptr<Node>& target,
+                       const MutationObserverInit& options,
+                       script::ExceptionState* exception_state);
+
   scoped_ptr<CallbackInternal> callback_;
   typedef std::vector<base::WeakPtr<dom::Node> > WeakNodeVector;
   WeakNodeVector observed_nodes_;
diff --git a/src/cobalt/dom/mutation_observer.idl b/src/cobalt/dom/mutation_observer.idl
index 7312a56..a90e18c 100644
--- a/src/cobalt/dom/mutation_observer.idl
+++ b/src/cobalt/dom/mutation_observer.idl
@@ -21,7 +21,7 @@
   ConstructorCallWith=EnvironmentSettings,
 ]
 interface MutationObserver {
-  void observe(Node target, MutationObserverInit options);
+  [RaisesException] void observe(Node target, MutationObserverInit options);
   void disconnect();
   sequence<MutationRecord> takeRecords();
 };
diff --git a/src/cobalt/dom/mutation_observer_test.cc b/src/cobalt/dom/mutation_observer_test.cc
index a8fccb1..d56eb76 100644
--- a/src/cobalt/dom/mutation_observer_test.cc
+++ b/src/cobalt/dom/mutation_observer_test.cc
@@ -23,6 +23,7 @@
 #include "cobalt/dom/node_list.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/testing/mock_exception_state.h"
 #include "cobalt/test/empty_document.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -397,6 +398,16 @@
   EXPECT_EQ(0, observer_list.registered_observers().size());
 }
 
+TEST_F(MutationObserverTest, InvalidOptionsRaisesException) {
+  scoped_refptr<dom::Element> target = CreateDiv();
+  scoped_refptr<MutationObserver> observer = CreateObserver();
+  MutationObserverInit invalid_options;
+
+  script::testing::MockExceptionState exception_state;
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kTypeError, _, _));
+  observer->Observe(target, invalid_options, &exception_state);
+}
+
 TEST_F(MutationObserverTest, AddChildNodes) {
   scoped_refptr<Element> root = CreateDiv();
   scoped_refptr<MutationObserver> observer = CreateObserver();
diff --git a/src/cobalt/dom/named_node_map.cc b/src/cobalt/dom/named_node_map.cc
index d6c9aa8..a0ef093 100644
--- a/src/cobalt/dom/named_node_map.cc
+++ b/src/cobalt/dom/named_node_map.cc
@@ -15,7 +15,9 @@
 #include "cobalt/dom/named_node_map.h"
 
 #include <algorithm>
+#include <iterator>
 
+#include "base/optional.h"
 #include "cobalt/dom/attr.h"
 #include "cobalt/dom/element.h"
 #include "cobalt/dom/global_stats.h"
@@ -26,86 +28,114 @@
 
 NamedNodeMap::NamedNodeMap(const scoped_refptr<Element>& element)
     : element_(element) {
-  ConstructProxyAttributes();
+  DCHECK(element_);
   GlobalStats::GetInstance()->Add(this);
 }
 
+// The length attribute's getter must return the attribute list's size.
+//  https://dom.spec.whatwg.org/#dom-namednodemap-length
 unsigned int NamedNodeMap::length() const {
-  return static_cast<unsigned int>(attribute_names_.size());
+  return static_cast<unsigned int>(element_->attribute_map().size());
 }
 
+// Algorithm for Item:
+//   https://dom.spec.whatwg.org/#dom-namednodemap-item
 scoped_refptr<Attr> NamedNodeMap::Item(unsigned int item) {
-  if (item < attribute_names_.size()) {
-    return GetNamedItem(attribute_names_[item]);
+  // 1. If index is equal to or greater than context object's attribute list's
+  // size, then return null.
+  if (item >= element_->attribute_map().size()) {
+    return NULL;
   }
-  return NULL;
+
+  // 2. Otherwise, return context object's attribute list[index].
+  Element::AttributeMap::const_iterator iter =
+      element_->attribute_map().begin();
+  std::advance(iter, item);
+  return GetOrCreateAttr(iter->first);
 }
 
+// The getNamedItem(qualifiedName) method, when invoked, must return the result
+// of getting an attribute given qualifiedName and element.
+//   https://dom.spec.whatwg.org/#dom-namednodemap-getnameditem
 scoped_refptr<Attr> NamedNodeMap::GetNamedItem(const std::string& name) const {
+  // 1. if element is in the HTML namespace and its node document is an HTML
+  // document, then set qualifiedName to qualifiedName in ASCII lowercase.
+  // 2. Return the first attribute in element's attribute list whose qualified
+  // name is qualifiedName, and null otherwise.
   if (!element_->HasAttribute(name)) {
-    // Not need to throw an exception, fail gracefully.
     return NULL;
   }
   return GetOrCreateAttr(name);
 }
 
+// The setNamedItem(attr) and setNamedItemNS(attr) methods, when invoked, must
+// return the result of setting an attribute given attr and element.
+//   https://dom.spec.whatwg.org/#dom-namednodemap-setnameditem
 scoped_refptr<Attr> NamedNodeMap::SetNamedItem(
     const scoped_refptr<Attr>& attribute) {
+  // Custom, not in any spec.
   if (!attribute) {
     // TODO: Throw JS NotFoundError.
     return NULL;
   }
 
-  if (attribute->container()) {
-    if (attribute->container() == this) {
-      // Already attached to this node map, don't do anything.
-      return attribute;
-    }
-    // Attribute is already attached to a different NamedNodeMap.
+  // To set an attribute given an attr and element, run these steps:
+  //   https://dom.spec.whatwg.org/#concept-element-attributes-set
+
+  // 1. If attr's element is neither null nor element, throw an
+  // InUseAttributeError.
+  if (attribute->container() && attribute->container() != this) {
     // TODO: Throw JS InUseAttributeError.
     return NULL;
   }
 
+  // 2. Let oldAttr be the result of getting an attribute given attr's
+  // namespace, attr's local name, and element.
+  // 3. If oldAttr is attr, return attr.
+  // 4. If oldAttr is non-null, replace it by attr in element.
+  // 5. Otherwise, append attr to element.
   const std::string& name = attribute->name();
-  scoped_refptr<Attr> previous_attribute;
-
+  scoped_refptr<Attr> old_attr;
   if (element_->HasAttribute(name)) {
-    // Get the previous attribute that was associated with 'name' and detach it
-    // from NamedNodeMap.
-    previous_attribute = GetOrCreateAttr(name);
-    previous_attribute->set_container(NULL);
-  }
+    old_attr = GetOrCreateAttr(name);
 
-  // Insert the new attribute and attach it to the NamedNodeMap.
+    if (attribute->value() == old_attr->value()) {
+      return old_attr;
+    }
+    old_attr->set_container(NULL);
+  }
   attribute->set_container(this);
   proxy_attributes_[name] = attribute->AsWeakPtr();
-
   // Inform the element about the new attribute. This should trigger a call to
   // NamedNodeMap::SetAttributeInternal and continue the update there.
   element_->SetAttribute(name, attribute->value());
-  return previous_attribute;
+
+  // 6. Return oldAttr.
+  return old_attr;
 }
 
+// Algorithm for RemoveNamedItem:
+//  https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
 scoped_refptr<Attr> NamedNodeMap::RemoveNamedItem(const std::string& name) {
-  ProxyAttributeMap::iterator iter = proxy_attributes_.find(name);
-  if (iter == proxy_attributes_.end()) {
-    // TODO: Throw JS NotFoundError.
-    return NULL;
-  }
-
-  scoped_refptr<Attr> previous_attribute;
+  // 1. Let attr be the result of removing an attribute given qualifiedName and
+  // element.
+  // 2. If attr is null, then throw a NotFoundError.
+  scoped_refptr<Attr> attr;
   if (element_->HasAttribute(name)) {
     // Get the previous attribute that was associated with 'name' and detach it
     // from NamedNodeMap.
-    previous_attribute = GetOrCreateAttr(name);
-    previous_attribute->set_container(NULL);
+    attr = GetOrCreateAttr(name);
+    attr->set_container(NULL);
+  } else {
+    // TODO: Throw JS NotFoundError.
+    return NULL;
   }
-
   // Inform the element about the removed attribute. This should trigger a call
   // to NamedNodeMap::RemoveAttributeInternal and continue the update there.
   element_->RemoveAttribute(name);
 
-  return previous_attribute;
+  // 3. Return attr.
+  return attr;
 }
 
 bool NamedNodeMap::CanQueryNamedProperty(const std::string& name) const {
@@ -114,34 +144,25 @@
 
 void NamedNodeMap::EnumerateNamedProperties(
     script::PropertyEnumerator* enumerator) const {
-  for (size_t i = 0; i < attribute_names_.size(); ++i) {
-    enumerator->AddProperty(attribute_names_[i]);
+  const Element::AttributeMap& attribute_map = element_->attribute_map();
+  for (Element::AttributeMap::const_iterator iter = attribute_map.begin();
+       iter != attribute_map.end(); ++iter) {
+    enumerator->AddProperty(iter->first);
   }
 }
 
 void NamedNodeMap::SetAttributeInternal(const std::string& name,
                                         const std::string& value) {
-  // If this a new name, add to it to the name vector.
-  AttributeNameVector::const_iterator name_iter =
-      std::find(attribute_names_.begin(), attribute_names_.end(), name);
-  if (name_iter == attribute_names_.end()) {
-    attribute_names_.push_back(name);
-  }
-
   // Update the associated Attr object.
-  ProxyAttributeMap::iterator attribute_iter = proxy_attributes_.find(name);
+  NameToAttrMap::iterator attribute_iter = proxy_attributes_.find(name);
   if (attribute_iter != proxy_attributes_.end() && attribute_iter->second) {
     attribute_iter->second->SetValueInternal(value);
   }
 }
 
 void NamedNodeMap::RemoveAttributeInternal(const std::string& name) {
-  attribute_names_.erase(
-      std::remove(attribute_names_.begin(), attribute_names_.end(), name),
-      attribute_names_.end());
-
   // Make sure to detach the proxy attribute from NamedNodeMap.
-  ProxyAttributeMap::iterator iter = proxy_attributes_.find(name);
+  NameToAttrMap::iterator iter = proxy_attributes_.find(name);
   if (iter != proxy_attributes_.end()) {
     if (iter->second) {
       iter->second->set_container(NULL);
@@ -154,23 +175,10 @@
 
 NamedNodeMap::~NamedNodeMap() { GlobalStats::GetInstance()->Remove(this); }
 
-void NamedNodeMap::ConstructProxyAttributes() {
-  // Construct the attribute name vector.
-  attribute_names_.clear();
-  const Element::AttributeMap& attribute_map = element_->attribute_map();
-  for (Element::AttributeMap::const_iterator iter = attribute_map.begin();
-       iter != attribute_map.end(); ++iter) {
-    attribute_names_.push_back(iter->first);
-  }
-
-  // There's no need to create the ProxyAttributeMap since it'll be created
-  // on demand when accessed by the user.
-}
-
 scoped_refptr<Attr> NamedNodeMap::GetOrCreateAttr(
     const std::string& name) const {
   TRACK_MEMORY_SCOPE("DOM");
-  ProxyAttributeMap::iterator iter = proxy_attributes_.find(name);
+  NameToAttrMap::iterator iter = proxy_attributes_.find(name);
   if (iter != proxy_attributes_.end() && iter->second) {
     return iter->second.get();
   }
diff --git a/src/cobalt/dom/named_node_map.h b/src/cobalt/dom/named_node_map.h
index 0cd8dc0..57073d5 100644
--- a/src/cobalt/dom/named_node_map.h
+++ b/src/cobalt/dom/named_node_map.h
@@ -31,18 +31,19 @@
 class Element;
 
 // The NamedNodeMap interface represents a collection of Attr objects. Objects
-// inside a NamedNodeMap are not in any particular order.
-//   https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1780488922
+// inside a NamedNodeMap are not in any particular order, unlike NodeList,
+// although they may be accessed by an index as in an array.
+// A NamedNodeMap object is live and will thus be auto-updated if changes are
+// made to its contents internally or elsewhere.
+//   https://dom.spec.whatwg.org/#interface-namednodemap
 //
-// Although called NamedNodeMap, this interface doesn't deal with Node objects
-// but with Attr objects, which were originally a specialized class of Node.
+// This interface is a legacy interface defined in DOM Level 3 Core, and is
+// referenced in DOM4. The whatwg live spec changes some definitions that is
+// adopted by major browsers. The whatwg version is followed here.
 //
 // Using Attr objects directly to represent Element attributes would incur
 // a significant memory overhead. Instead, NamedNodeMap and Attr are created
 // on demand and proxy the content of the actual attributes.
-//
-// Note that DOM4 deprecates this class and replaces it with a read-only
-// array of Attr objects.
 class NamedNodeMap : public script::Wrappable,
                      public base::SupportsWeakPtr<NamedNodeMap> {
  public:
@@ -57,10 +58,11 @@
   scoped_refptr<Attr> SetNamedItem(const scoped_refptr<Attr>& attribute);
   scoped_refptr<Attr> RemoveNamedItem(const std::string& name);
 
-  // Custom, not in any spec.
-  //
   bool CanQueryNamedProperty(const std::string& name) const;
   void EnumerateNamedProperties(script::PropertyEnumerator* enumerator) const;
+
+  // Custom, not in any spec.
+  //
   void SetAttributeInternal(const std::string& name, const std::string& value);
   void RemoveAttributeInternal(const std::string& name);
 
@@ -71,17 +73,14 @@
  private:
   ~NamedNodeMap();
 
-  void ConstructProxyAttributes();
   scoped_refptr<Attr> GetOrCreateAttr(const std::string& name) const;
 
+  typedef base::hash_map<std::string, base::WeakPtr<Attr> > NameToAttrMap;
+
   // The element that contains the actual attributes.
   scoped_refptr<Element> element_;
-  // This vector contains the names of the proxy attributes.
-  typedef std::vector<std::string> AttributeNameVector;
-  AttributeNameVector attribute_names_;
   // Vector of weak pointers to Attr objects that proxies the actual attributes.
-  typedef base::hash_map<std::string, base::WeakPtr<Attr> > ProxyAttributeMap;
-  mutable ProxyAttributeMap proxy_attributes_;
+  mutable NameToAttrMap proxy_attributes_;
 
   DISALLOW_COPY_AND_ASSIGN(NamedNodeMap);
 };
diff --git a/src/cobalt/dom/named_node_map.idl b/src/cobalt/dom/named_node_map.idl
index 6bc6a42..7b5db82 100644
--- a/src/cobalt/dom/named_node_map.idl
+++ b/src/cobalt/dom/named_node_map.idl
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1780488922
+// https://dom.spec.whatwg.org/#interface-namednodemap
 
 interface NamedNodeMap {
   readonly attribute unsigned long length;
   getter Attr? item(unsigned long index);
-  getter Attr? getNamedItem(DOMString name);
+  getter Attr? getNamedItem(DOMString qualifiedName);
+  Attr? setNamedItem(Attr attr);
+  Attr removeNamedItem(DOMString qualifiedName);
 };
diff --git a/src/cobalt/dom/named_node_map_test.cc b/src/cobalt/dom/named_node_map_test.cc
new file mode 100644
index 0000000..d99df3b
--- /dev/null
+++ b/src/cobalt/dom/named_node_map_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/named_node_map.h"
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/attr.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/element.h"
+#include "cobalt/dom/global_stats.h"
+#include "cobalt/dom/html_element_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+class NamedNodeMapTest : public ::testing::Test {
+ protected:
+  NamedNodeMapTest();
+  ~NamedNodeMapTest() OVERRIDE;
+
+  HTMLElementContext html_element_context_;
+  scoped_refptr<Document> document_;
+  scoped_refptr<Element> element_;
+};
+
+NamedNodeMapTest::NamedNodeMapTest()
+    : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
+  EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
+  document_ = new Document(&html_element_context_);
+  element_ = new Element(document_, base::Token("element"));
+}
+
+NamedNodeMapTest::~NamedNodeMapTest() {
+  element_ = NULL;
+  document_ = NULL;
+  EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
+}
+
+TEST_F(NamedNodeMapTest, EmptyNamedNodeMap) {
+  scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+  EXPECT_EQ(0, named_node_map->length());
+  EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->Item(0));
+  EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->GetNamedItem("name"));
+}
+
+TEST_F(NamedNodeMapTest, ShouldBeAlive) {
+  scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+  EXPECT_EQ(0, named_node_map->length());
+
+  element_->SetAttribute("name", "value");
+  ASSERT_EQ(1, named_node_map->length());
+  EXPECT_EQ("name", named_node_map->Item(0)->name());
+  EXPECT_EQ("value", named_node_map->Item(0)->value());
+  EXPECT_EQ("name", named_node_map->GetNamedItem("name")->name());
+  EXPECT_EQ("value", named_node_map->GetNamedItem("name")->value());
+  EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->Item(1));
+}
+
+TEST_F(NamedNodeMapTest, CanSetNamedItem) {
+  scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+  EXPECT_EQ(0, named_node_map->length());
+
+  named_node_map->SetNamedItem(new Attr("name", "value", named_node_map));
+  ASSERT_EQ(1, named_node_map->length());
+  EXPECT_EQ("name", named_node_map->Item(0)->name());
+  EXPECT_EQ("value", named_node_map->Item(0)->value());
+  EXPECT_EQ("name", named_node_map->GetNamedItem("name")->name());
+  EXPECT_EQ("value", named_node_map->GetNamedItem("name")->value());
+  EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->Item(1));
+}
+
+TEST_F(NamedNodeMapTest, CanRemoveNamedItem) {
+  scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+  element_->SetAttribute("name", "value");
+  ASSERT_EQ(1, named_node_map->length());
+
+  named_node_map->RemoveNamedItem("name");
+  EXPECT_EQ(0, named_node_map->length());
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index 15ae59a..185385c 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -14,19 +14,22 @@
 
 #include "cobalt/dom/navigator.h"
 
-#include "cobalt/media_session/media_session.h"
+#include "cobalt/media_session/media_session_client.h"
 
 using cobalt::media_session::MediaSession;
 
 namespace cobalt {
 namespace dom {
 
-Navigator::Navigator(const std::string& user_agent, const std::string& language)
+Navigator::Navigator(const std::string& user_agent, const std::string& language,
+                     scoped_refptr<MediaSession> media_session,
+                     script::ScriptValueFactory* script_value_factory)
     : user_agent_(user_agent),
       language_(language),
       mime_types_(new MimeTypeArray()),
       plugins_(new PluginArray()),
-      media_session_(new MediaSession()) {}
+      media_session_(media_session),
+      script_value_factory_(script_value_factory) {}
 
 const std::string& Navigator::language() const { return language_; }
 
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 8ee28cd..a962803 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -21,6 +21,7 @@
 #include "cobalt/dom/mime_type_array.h"
 #include "cobalt/dom/plugin_array.h"
 #include "cobalt/media_session/media_session.h"
+#include "cobalt/script/script_value_factory.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -32,7 +33,9 @@
 // https://www.w3.org/TR/html5/webappapis.html#navigator
 class Navigator : public script::Wrappable {
  public:
-  Navigator(const std::string& user_agent, const std::string& language);
+  Navigator(const std::string& user_agent, const std::string& language,
+            scoped_refptr<cobalt::media_session::MediaSession> media_session,
+            script::ScriptValueFactory* script_value_factory);
 
   // Web API: NavigatorID
   const std::string& user_agent() const;
@@ -61,6 +64,7 @@
   scoped_refptr<MimeTypeArray> mime_types_;
   scoped_refptr<PluginArray> plugins_;
   scoped_refptr<cobalt::media_session::MediaSession> media_session_;
+  script::ScriptValueFactory* script_value_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Navigator);
 };
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index 77834fe..fdc872e 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -482,34 +482,48 @@
   }
 }
 
-void Node::InvalidateComputedStylesRecursively() {
+void Node::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
+  PurgeCachedBackgroundImagesOfDescendants();
+}
+
+void Node::InvalidateComputedStylesOfNodeAndDescendants() {
+  InvalidateComputedStylesOfDescendants();
+}
+
+void Node::InvalidateLayoutBoxesOfNodeAndAncestors() {
+  InvalidateLayoutBoxesOfAncestors();
+}
+
+void Node::InvalidateLayoutBoxesOfNodeAndDescendants() {
+  InvalidateLayoutBoxesOfDescendants();
+}
+
+void Node::PurgeCachedBackgroundImagesOfDescendants() {
   Node* child = first_child_;
   while (child) {
-    child->InvalidateComputedStylesRecursively();
+    child->PurgeCachedBackgroundImagesOfNodeAndDescendants();
     child = child->next_sibling_;
   }
 }
 
-void Node::PurgeCachedResourceReferencesRecursively() {
-  ReleaseImagesAndInvalidateComputedStyleIfNecessary();
-
+void Node::InvalidateComputedStylesOfDescendants() {
   Node* child = first_child_;
   while (child) {
-    child->PurgeCachedResourceReferencesRecursively();
+    child->InvalidateComputedStylesOfNodeAndDescendants();
     child = child->next_sibling_;
   }
 }
 
-void Node::InvalidateLayoutBoxesFromNodeAndAncestors() {
+void Node::InvalidateLayoutBoxesOfAncestors() {
   if (parent_) {
-    parent_->InvalidateLayoutBoxesFromNodeAndAncestors();
+    parent_->InvalidateLayoutBoxesOfNodeAndAncestors();
   }
 }
 
-void Node::InvalidateLayoutBoxesFromNodeAndDescendants() {
+void Node::InvalidateLayoutBoxesOfDescendants() {
   Node* child = first_child_;
   while (child) {
-    child->InvalidateLayoutBoxesFromNodeAndDescendants();
+    child->InvalidateLayoutBoxesOfNodeAndDescendants();
     child = child->next_sibling_;
   }
 }
@@ -638,8 +652,7 @@
       scoped_refptr<dom::NodeList> added_nodes = new dom::NodeList();
       added_nodes->AppendNode(node);
       mutation_reporter.ReportChildListMutation(
-          added_nodes, NULL, child && child->previous_sibling_
-                                 ? child->previous_sibling_
+          added_nodes, NULL, child ? child->previous_sibling_
                                  : this->last_child_ /* previous_sibling */,
           child /* next_sibling */);
     }
@@ -674,7 +687,7 @@
   // being changed.
   // NOTE: The added node does not have any invalidations done, because they
   // occur on the remove and are guaranteed to not be needed at this point.
-  InvalidateLayoutBoxesFromNodeAndAncestors();
+  InvalidateLayoutBoxesOfNodeAndAncestors();
 
   if (inserted_into_document_) {
     node->OnInsertedIntoDocument();
@@ -711,12 +724,18 @@
 
   // Invalidate the layout boxes of the previous parent as a result of its
   // children being changed.
-  InvalidateLayoutBoxesFromNodeAndAncestors();
+  InvalidateLayoutBoxesOfNodeAndAncestors();
+
+  // Purge any cached background images now that this node and its descendants
+  // are no longer in the tree, so that the images can be released from the
+  // resource cache.
+  node->PurgeCachedBackgroundImagesOfNodeAndDescendants();
+
   // Invalidate the styles and layout boxes of the node being removed from
   // the tree. These are no longer valid as a result of the child and its
   // descendants losing their inherited styles.
-  node->InvalidateComputedStylesRecursively();
-  node->InvalidateLayoutBoxesFromNodeAndDescendants();
+  node->InvalidateComputedStylesOfNodeAndDescendants();
+  node->InvalidateLayoutBoxesOfNodeAndDescendants();
 
   bool was_inserted_to_document = node->inserted_into_document_;
   if (was_inserted_to_document) {
@@ -775,5 +794,58 @@
   }
 }
 
+// Algorithm for ReplaceAll:
+//   https://www.w3.org/TR/dom/#concept-node-replace-all
+void Node::ReplaceAll(const scoped_refptr<Node>& node) {
+  // 1. If node is not null, adopt node into parent's node document.
+  if (node) {
+    node->AdoptIntoDocument(this->node_document());
+  }
+
+  // 2. Let removedNodes be parent's children.
+  scoped_refptr<NodeList> removed_nodes = new NodeList();
+  scoped_refptr<Node> next_child = first_child_;
+  while (next_child) {
+    removed_nodes->AppendNode(next_child);
+    next_child = next_child->next_sibling();
+  }
+
+  // 3. Let addedNodes be the empty list if node is null, node's children if
+  //    node is a DocumentFragment node, and a list containing node otherwise.
+  scoped_refptr<NodeList> added_nodes = new NodeList();
+  if (node) {
+    added_nodes->AppendNode(node);
+  }
+
+  // 4. Remove all parent's children, in tree order, with the suppress observers
+  //    flag set.
+  while (HasChildNodes()) {
+    Remove(first_child(), true);
+  }
+
+  // 5. If node is not null, insert node into parent before null with the
+  //    suppress observers flag set.
+  if (node) {
+    Insert(node, NULL, true);
+  }
+
+  // 6. Queue a mutation record of "childList" for parent with addedNodes
+  //    addedNodes and removedNodes removedNodes.
+  scoped_ptr<RegisteredObserverVector> observers =
+      GatherInclusiveAncestorsObservers();
+  if (!observers->empty()) {
+    MutationReporter mutation_reporter(this, observers.Pass());
+    scoped_refptr<dom::NodeList> added_nodes = new dom::NodeList();
+    if (node) {
+      added_nodes->AppendNode(node);
+    }
+    if (added_nodes->length() > 0 || removed_nodes->length() > 0) {
+      mutation_reporter.ReportChildListMutation(
+          added_nodes, removed_nodes, NULL /* previous_sibling */,
+          NULL /* next_sibling */);
+    }
+  }
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index dfeec89..695ce4d 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -246,22 +246,19 @@
   // removed from to its owner document.
   virtual void OnRemovedFromDocument();
 
-  // Invalidate computed styles from this node and all child nodes.
-  virtual void InvalidateComputedStylesRecursively();
-  // Invalidate layout boxes from this node and all parent nodes.
-  virtual void InvalidateLayoutBoxesFromNodeAndAncestors();
-  // Invalidate layout boxes from this node and all child nodes.
-  virtual void InvalidateLayoutBoxesFromNodeAndDescendants();
-  // Invalidate the sizes within the layout boxes of this node.
-  virtual void InvalidateLayoutBoxSizesFromNode() {}
-  // Invalidate the cross references within the layout boxes of this node.
-  virtual void InvalidateLayoutBoxCrossReferencesFromNode() {}
-  // Invalidate the render tree nodes within the layout boxes of this node.
-  virtual void InvalidateRenderTreeNodesFromNode() {}
+  virtual void PurgeCachedBackgroundImagesOfNodeAndDescendants();
+  virtual void InvalidateComputedStylesOfNodeAndDescendants();
+  virtual void InvalidateLayoutBoxesOfNodeAndAncestors();
+  virtual void InvalidateLayoutBoxesOfNodeAndDescendants();
 
-  // Releases image resources and invalidates computed style if there are images
-  // associated with this html element in the image cache.
-  virtual void ReleaseImagesAndInvalidateComputedStyleIfNecessary() {}
+  virtual void InvalidateLayoutBoxSizes() {}
+  virtual void InvalidateLayoutBoxCrossReferences() {}
+  virtual void InvalidateLayoutBoxRenderTreeNodes() {}
+
+  void PurgeCachedBackgroundImagesOfDescendants();
+  void InvalidateComputedStylesOfDescendants();
+  void InvalidateLayoutBoxesOfAncestors();
+  void InvalidateLayoutBoxesOfDescendants();
 
   // Triggers a generation update in this node and all its ancestor nodes.
   void UpdateGenerationForNodeAndAncestors();
@@ -270,6 +267,8 @@
   typedef std::vector<RegisteredObserver> RegisteredObserverVector;
   scoped_ptr<RegisteredObserverVector> GatherInclusiveAncestorsObservers();
 
+  void ReplaceAll(const scoped_refptr<Node>& node);
+
  private:
   // From EventTarget.
   std::string GetDebugName() OVERRIDE { return node_name().c_str(); }
diff --git a/src/cobalt/dom/node_dispatch_event_test.cc b/src/cobalt/dom/node_dispatch_event_test.cc
index 8b9e300..5758126 100644
--- a/src/cobalt/dom/node_dispatch_event_test.cc
+++ b/src/cobalt/dom/node_dispatch_event_test.cc
@@ -19,6 +19,7 @@
 #include "cobalt/dom/global_stats.h"
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/script/testing/fake_script_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::AllOf;
@@ -33,7 +34,7 @@
 namespace cobalt {
 namespace dom {
 
-using testing::FakeScriptValue;
+using ::cobalt::script::testing::FakeScriptValue;
 using testing::MockEventListener;
 
 //////////////////////////////////////////////////////////////////////////
@@ -57,7 +58,7 @@
 
 NodeDispatchEventTest::NodeDispatchEventTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 
   document_ = new Document(&html_element_context_);
@@ -69,17 +70,23 @@
   event_listener_bubbling_ = MockEventListener::Create();
 
   grand_parent_->AddEventListener(
-      "fired", FakeScriptValue(event_listener_bubbling_.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listener_bubbling_.get()), false);
   grand_parent_->AddEventListener(
-      "fired", FakeScriptValue(event_listener_capture_.get()), true);
+      "fired",
+      FakeScriptValue<EventListener>(event_listener_capture_.get()), true);
   parent_->AddEventListener(
-      "fired", FakeScriptValue(event_listener_bubbling_.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listener_bubbling_.get()), false);
   parent_->AddEventListener(
-      "fired", FakeScriptValue(event_listener_capture_.get()), true);
+      "fired",
+      FakeScriptValue<EventListener>(event_listener_capture_.get()), true);
   child_->AddEventListener(
-      "fired", FakeScriptValue(event_listener_bubbling_.get()), false);
+      "fired",
+      FakeScriptValue<EventListener>(event_listener_bubbling_.get()), false);
   child_->AddEventListener(
-      "fired", FakeScriptValue(event_listener_capture_.get()), true);
+      "fired",
+      FakeScriptValue<EventListener>(event_listener_capture_.get()), true);
 }
 
 NodeDispatchEventTest::~NodeDispatchEventTest() {
diff --git a/src/cobalt/dom/node_list_live_test.cc b/src/cobalt/dom/node_list_live_test.cc
index 3645485..a9762af 100644
--- a/src/cobalt/dom/node_list_live_test.cc
+++ b/src/cobalt/dom/node_list_live_test.cc
@@ -28,7 +28,8 @@
   NodeListLiveTest()
       : dom_stat_tracker_("NodeListLiveTest"),
         html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, &dom_stat_tracker_, ""),
+                              NULL, NULL, NULL, NULL, NULL, NULL,
+                              &dom_stat_tracker_, ""),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListLiveTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_list_test.cc b/src/cobalt/dom/node_list_test.cc
index 7bea9d9..df03cba 100644
--- a/src/cobalt/dom/node_list_test.cc
+++ b/src/cobalt/dom/node_list_test.cc
@@ -28,8 +28,8 @@
   NodeListTest()
       : dom_stat_tracker_(new DomStatTracker("NodeListTest")),
         html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
-                              ""),
+                              NULL, NULL, NULL, NULL, NULL, NULL,
+                              dom_stat_tracker_.get(), ""),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_test.cc b/src/cobalt/dom/node_test.cc
index 9056195..cd8e4f1 100644
--- a/src/cobalt/dom/node_test.cc
+++ b/src/cobalt/dom/node_test.cc
@@ -76,7 +76,7 @@
 
 NodeTest::NodeTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 
   document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc
index 61ecb59..2e1348a 100644
--- a/src/cobalt/dom/rule_matching.cc
+++ b/src/cobalt/dom/rule_matching.cc
@@ -171,7 +171,7 @@
   // tree.
   //   https://www.w3.org/TR/selectors4/#type-selector
   void VisitTypeSelector(cssom::TypeSelector* type_selector) OVERRIDE {
-    if (type_selector->element_name() != element_->tag_name()) {
+    if (type_selector->element_name() != element_->local_name()) {
       element_ = NULL;
     }
   }
@@ -468,7 +468,7 @@
       // Type selector.
       if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) {
         GatherCandidateNodesFromMap(cssom::kTypeSelector, combinator_type,
-                                    selector_nodes_map, element->tag_name(),
+                                    selector_nodes_map, element->local_name(),
                                     &candidate_nodes);
       }
 
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index 5f1e58e..e7ef524 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -44,7 +44,7 @@
         dom_stat_tracker_(new DomStatTracker("RuleMatchingTest")),
         html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              dom_stat_tracker_.get(), ""),
+                              NULL, NULL, dom_stat_tracker_.get(), ""),
         document_(new Document(&html_element_context_)),
         root_(document_->CreateElement("html")->AsHTMLElement()) {
     document_->AppendChild(root_);
diff --git a/src/cobalt/dom/serializer.cc b/src/cobalt/dom/serializer.cc
index e2f641a..1f55741 100644
--- a/src/cobalt/dom/serializer.cc
+++ b/src/cobalt/dom/serializer.cc
@@ -113,11 +113,11 @@
 
 void Serializer::Visit(const Element* element) {
   if (entering_node_) {
-    *out_stream_ << "<" << element->tag_name();
+    *out_stream_ << "<" << element->local_name();
     WriteAtttributes(element, out_stream_);
     *out_stream_ << ">";
   } else {
-    *out_stream_ << "</" << element->tag_name() << ">";
+    *out_stream_ << "</" << element->local_name() << ">";
   }
 }
 
diff --git a/src/cobalt/dom/serializer_test.cc b/src/cobalt/dom/serializer_test.cc
index ede650a..625e4cc 100644
--- a/src/cobalt/dom/serializer_test.cc
+++ b/src/cobalt/dom/serializer_test.cc
@@ -45,8 +45,8 @@
     : dom_parser_(new dom_parser::Parser()),
       dom_stat_tracker_(new DomStatTracker("SerializerTest")),
       html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
-                            ""),
+                            NULL, NULL, NULL, NULL, NULL, NULL,
+                            dom_stat_tracker_.get(), ""),
       document_(new Document(&html_element_context_)),
       root_(new Element(document_, base::Token("root"))),
       source_location_(base::SourceLocation("[object SerializerTest]", 1, 1)) {}
diff --git a/src/cobalt/dom/source_buffer.h b/src/cobalt/dom/source_buffer.h
index 0d72d64..c7f5a59 100644
--- a/src/cobalt/dom/source_buffer.h
+++ b/src/cobalt/dom/source_buffer.h
@@ -42,10 +42,6 @@
 // shared between different versions of MSE.
 class SourceBuffer : public dom::EventTarget {
  public:
-  // Web API: SourceBuffer
-  //
-  enum AppendMode { kSegments, kSequence };
-
   // Custom, not in any spec.
   //
   SourceBuffer(const scoped_refptr<MediaSource>& media_source,
diff --git a/src/cobalt/dom/source_buffer.idl b/src/cobalt/dom/source_buffer.idl
index 914e6b2..e20aebf 100644
--- a/src/cobalt/dom/source_buffer.idl
+++ b/src/cobalt/dom/source_buffer.idl
@@ -15,11 +15,6 @@
 // https://www.w3.org/TR/media-source/#idl-def-SourceBuffer
 // https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
 
-enum AppendMode {
-  "segments",
-  "sequence"
-};
-
 interface SourceBuffer : EventTarget {
   [RaisesException] readonly attribute TimeRanges buffered;
   [RaisesException] attribute double timestampOffset;
@@ -28,7 +23,7 @@
   [RaisesException] void abort();
 
   // MSE 2016 Interface
-  [Conditional=COBALT_MEDIA_SOURCE_2016, RaisesException] attribute AppendMode mode;
+  [Conditional=COBALT_MEDIA_SOURCE_2016, RaisesException] attribute SourceBufferAppendMode mode;
   [Conditional=COBALT_MEDIA_SOURCE_2016] readonly attribute boolean updating;
   // [RaisesException] readonly attribute TimeRanges buffered;
   // [RaisesException] attribute double timestampOffset;
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/source_buffer_append_mode.idl
similarity index 69%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/source_buffer_append_mode.idl
index d335495..5fa5586 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/source_buffer_append_mode.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/media-source/#idl-def-SourceBuffer
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum SourceBufferAppendMode {
+  "segments",
+  "sequence"
+};
diff --git a/src/cobalt/dom/testing/fake_exception_state.h b/src/cobalt/dom/testing/fake_exception_state.h
new file mode 100644
index 0000000..85aafdb
--- /dev/null
+++ b/src/cobalt/dom/testing/fake_exception_state.h
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_TESTING_FAKE_EXCEPTION_STATE_H_
+#define COBALT_DOM_TESTING_FAKE_EXCEPTION_STATE_H_
+
+#include "cobalt/script/exception_state.h"
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/dom_exception.h"
+
+namespace cobalt {
+namespace dom {
+namespace testing {
+
+class FakeExceptionState : public script::ExceptionState {
+ public:
+  void SetException(
+      const scoped_refptr<script::ScriptException>& exception) OVERRIDE {
+    dom_exception_ = make_scoped_refptr(
+        base::polymorphic_downcast<dom::DOMException*>(exception.get()));
+  }
+  void SetSimpleExceptionVA(script::SimpleExceptionType /*type*/,
+                            const char* /*format*/, va_list /*args*/) OVERRIDE {
+    // no-op
+  }
+  dom::DOMException::ExceptionCode GetExceptionCode() {
+    return dom_exception_ ? static_cast<dom::DOMException::ExceptionCode>(
+                                dom_exception_->code())
+                          : dom::DOMException::kNone;
+  }
+
+ private:
+  scoped_refptr<dom::DOMException> dom_exception_;
+};
+
+}  // namespace testing
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_TESTING_FAKE_EXCEPTION_STATE_H_
diff --git a/src/cobalt/dom/testing/html_collection_testing.h b/src/cobalt/dom/testing/html_collection_testing.h
index de31f0d..5aeebe3 100644
--- a/src/cobalt/dom/testing/html_collection_testing.h
+++ b/src/cobalt/dom/testing/html_collection_testing.h
@@ -168,9 +168,9 @@
   scoped_refptr<Node> b1 = a1->AppendChild(
       html_element_factory.CreateHTMLElement(document, base::Token("b1")));
   scoped_refptr<Node> c1 = b1->AppendChild(
-      html_element_factory.CreateHTMLElement(document, base::Token("tag")));
+      html_element_factory.CreateHTMLElement(document, base::Token("element")));
   scoped_refptr<Node> d1 = a3->AppendChild(
-      html_element_factory.CreateHTMLElement(document, base::Token("tag")));
+      html_element_factory.CreateHTMLElement(document, base::Token("element")));
 
   // GetElementsByTagName should return all elements when provided with
   // parameter "*".
@@ -183,9 +183,9 @@
   EXPECT_EQ(d1, collection->Item(4));
   EXPECT_EQ(kNullNode, collection->Item(5));
 
-  // GetElementsByTagName should only return elements with the specific tag name
-  // when that is provided.
-  collection = node->GetElementsByTagName("tag");
+  // GetElementsByTagName should only return elements with the specific local
+  // name when that is provided.
+  collection = node->GetElementsByTagName("element");
   EXPECT_EQ(2, collection->length());
   EXPECT_EQ(c1, collection->Item(0));
   EXPECT_EQ(d1, collection->Item(1));
@@ -202,7 +202,8 @@
 
   // Add a new node with a matching tag.
   scoped_refptr<Node> a2 = node->InsertBefore(
-      html_element_factory.CreateHTMLElement(document, base::Token("tag")), a3);
+      html_element_factory.CreateHTMLElement(document, base::Token("element")),
+      a3);
   EXPECT_EQ(3, collection->length());
   EXPECT_EQ(c1, collection->Item(0));
   EXPECT_EQ(a2, collection->Item(1));
diff --git a/src/cobalt/dom/testing/mock_event_listener.h b/src/cobalt/dom/testing/mock_event_listener.h
index 108f4ee..93445f5 100644
--- a/src/cobalt/dom/testing/mock_event_listener.h
+++ b/src/cobalt/dom/testing/mock_event_listener.h
@@ -139,33 +139,6 @@
             DoAll(SetArgPointee<2>(false), Return(base::optional<bool>())));
   }
 };
-
-class FakeScriptValue : public script::ScriptValue<EventListener> {
- public:
-  typedef script::ScriptValue<EventListener> BaseClass;
-  explicit FakeScriptValue(const MockEventListener* listener)
-      : mock_listener_(listener) {}
-
-  void RegisterOwner(script::Wrappable*) OVERRIDE {}
-  void DeregisterOwner(script::Wrappable*) OVERRIDE {}
-  void PreventGarbageCollection() OVERRIDE {}
-  void AllowGarbageCollection() OVERRIDE {}
-  const EventListener* GetScriptValue(void) const OVERRIDE {
-    return mock_listener_;
-  }
-  scoped_ptr<BaseClass> MakeCopy() const OVERRIDE {
-    return make_scoped_ptr<BaseClass>(new FakeScriptValue(mock_listener_));
-  }
-
-  bool EqualTo(const BaseClass& other) const OVERRIDE {
-    const FakeScriptValue* other_script_object =
-        base::polymorphic_downcast<const FakeScriptValue*>(&other);
-    return mock_listener_ == other_script_object->mock_listener_;
-  }
-
- private:
-  const MockEventListener* mock_listener_;
-};
 }  // namespace testing
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/testing/stub_script_runner.cc b/src/cobalt/dom/testing/stub_script_runner.cc
index b252908..148e67f 100644
--- a/src/cobalt/dom/testing/stub_script_runner.cc
+++ b/src/cobalt/dom/testing/stub_script_runner.cc
@@ -20,9 +20,13 @@
 
 std::string StubScriptRunner::Execute(
     const std::string& script_utf8,
-    const base::SourceLocation& script_location) {
+    const base::SourceLocation& script_location,
+    bool* out_succeeded) {
   UNREFERENCED_PARAMETER(script_utf8);
   UNREFERENCED_PARAMETER(script_location);
+  if (out_succeeded) {
+    *out_succeeded = true;
+  }
   return "";
 }
 
diff --git a/src/cobalt/dom/testing/stub_script_runner.h b/src/cobalt/dom/testing/stub_script_runner.h
index 1b64e1a..9c68840 100644
--- a/src/cobalt/dom/testing/stub_script_runner.h
+++ b/src/cobalt/dom/testing/stub_script_runner.h
@@ -26,7 +26,8 @@
 class StubScriptRunner : public script::ScriptRunner {
  public:
   std::string Execute(const std::string& script_utf8,
-                      const base::SourceLocation& script_location) OVERRIDE;
+                      const base::SourceLocation& script_location,
+                      bool* out_succeeded) OVERRIDE;
 };
 
 }  // namespace testing
diff --git a/src/cobalt/dom/text_test.cc b/src/cobalt/dom/text_test.cc
index 54d7304..75ab826 100644
--- a/src/cobalt/dom/text_test.cc
+++ b/src/cobalt/dom/text_test.cc
@@ -39,7 +39,7 @@
 
 TextTest::TextTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, "") {
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
 }
diff --git a/src/cobalt/dom/time_ranges_test.cc b/src/cobalt/dom/time_ranges_test.cc
index 8bf6fb4..be89db2 100644
--- a/src/cobalt/dom/time_ranges_test.cc
+++ b/src/cobalt/dom/time_ranges_test.cc
@@ -17,34 +17,13 @@
 #include <limits>
 
 #include "cobalt/base/polymorphic_downcast.h"
-#include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/testing/fake_exception_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace dom {
 namespace {
 
-class FakeExceptionState : public script::ExceptionState {
- public:
-  void SetException(
-      const scoped_refptr<script::ScriptException>& exception) OVERRIDE {
-    dom_exception_ = make_scoped_refptr(
-        base::polymorphic_downcast<DOMException*>(exception.get()));
-  }
-  void SetSimpleExceptionWithArgs(script::MessageType /*message_type*/,
-                                  int /*dummy*/, ...) OVERRIDE {
-    // no-op
-  }
-  dom::DOMException::ExceptionCode GetExceptionCode() {
-    return dom_exception_ ? static_cast<dom::DOMException::ExceptionCode>(
-                                dom_exception_->code())
-                          : dom::DOMException::kNone;
-  }
-
- private:
-  scoped_refptr<dom::DOMException> dom_exception_;
-};
-
 void CheckEqual(const scoped_refptr<TimeRanges>& time_ranges1,
                 const scoped_refptr<TimeRanges>& time_ranges2) {
   ASSERT_EQ(time_ranges1->length(), time_ranges2->length());
@@ -56,6 +35,8 @@
 
 }  // namespace
 
+using testing::FakeExceptionState;
+
 TEST(TimeRangesTest, Constructors) {
   scoped_refptr<TimeRanges> time_ranges = new TimeRanges;
   FakeExceptionState exception_state;
diff --git a/src/cobalt/dom/track_default.h b/src/cobalt/dom/track_default.h
index 7b459f6..89d0e4b 100644
--- a/src/cobalt/dom/track_default.h
+++ b/src/cobalt/dom/track_default.h
@@ -19,6 +19,7 @@
 
 #include "base/logging.h"
 #include "cobalt/dom/audio_track.h"
+#include "cobalt/dom/track_default_type.h"
 #include "cobalt/dom/video_track.h"
 #include "cobalt/script/exception_state.h"
 #include "cobalt/script/sequence.h"
@@ -35,12 +36,6 @@
  public:
   // Web API: TrackDefault
   //
-  enum TrackDefaultType {
-    kAudio,
-    kVideo,
-    kText,
-  };
-
   TrackDefault(TrackDefaultType type, const std::string& language,
                const std::string& label,
                const script::Sequence<std::string>& kinds,
@@ -51,14 +46,14 @@
         language_(language),
         label_(label),
         kinds_(kinds) {
-    if (type == kAudio) {
+    if (type == kTrackDefaultTypeAudio) {
       for (script::Sequence<std::string>::size_type i = 0; i < kinds.size();
            ++i) {
         if (!AudioTrack::IsValidKind(kinds.at(i).c_str())) {
           exception_state->SetSimpleException(script::kSimpleTypeError);
         }
       }
-    } else if (type == kVideo) {
+    } else if (type == kTrackDefaultTypeVideo) {
       for (script::Sequence<std::string>::size_type i = 0; i < kinds.size();
            ++i) {
         if (!VideoTrack::IsValidKind(kinds.at(i).c_str())) {
diff --git a/src/cobalt/dom/track_default.idl b/src/cobalt/dom/track_default.idl
index e07a509..1049d49 100644
--- a/src/cobalt/dom/track_default.idl
+++ b/src/cobalt/dom/track_default.idl
@@ -14,12 +14,6 @@
 
 // https://www.w3.org/TR/2016/CR-media-source-20160705/#trackdefault
 
-enum TrackDefaultType {
-  "audio",
-  "video",
-  "text"
-};
-
 [
   Constructor(TrackDefaultType type,
               DOMString language,
diff --git a/src/cobalt/dom/track_default_list.h b/src/cobalt/dom/track_default_list.h
index a0b82a4..e6e7904 100644
--- a/src/cobalt/dom/track_default_list.h
+++ b/src/cobalt/dom/track_default_list.h
@@ -43,7 +43,7 @@
       script::ExceptionState* exception_state) {
     // |track_defaults| shouldn't contain two or more TrackDefault objects with
     // the same |type| and the same |byte_stream_track_id|.
-    typedef std::pair<TrackDefault::TrackDefaultType, std::string> TypeAndId;
+    typedef std::pair<TrackDefaultType, std::string> TypeAndId;
     std::set<TypeAndId> type_and_ids;
     for (script::Sequence<scoped_refptr<TrackDefault> >::size_type i = 0;
          i < track_defaults.size(); ++i) {
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/track_default_type.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/track_default_type.idl
index d335495..2207c92 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/track_default_type.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#trackdefault
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum TrackDefaultType {
+  "audio",
+  "video",
+  "text"
+};
diff --git a/src/cobalt/dom/typed_array.h b/src/cobalt/dom/typed_array.h
index 46cefec..b5ff828 100644
--- a/src/cobalt/dom/typed_array.h
+++ b/src/cobalt/dom/typed_array.h
@@ -72,8 +72,9 @@
              script::ExceptionState* exception_state)
       : ArrayBufferView(buffer) {
     if (buffer->byte_length() % kBytesPerElement != 0) {
-      exception_state->SetSimpleExceptionWithArgs(
-          script::kWrongByteLengthMultiple, 0, kBytesPerElement);
+      exception_state->SetSimpleException(script::kRangeError,
+                                          kWrongByteLengthMultipleErrorFormat,
+                                          kBytesPerElement);
     }
   }
 
@@ -82,11 +83,13 @@
       : ArrayBufferView(buffer, byte_offset,
                         buffer->byte_length() - byte_offset) {
     if (this->byte_offset() % kBytesPerElement != 0) {
-      exception_state->SetSimpleExceptionWithArgs(
-          script::kWrongByteOffsetMultiple, 0, kBytesPerElement);
+      exception_state->SetSimpleException(script::kRangeError,
+                                          kWrongByteOffsetMultipleErrorFormat,
+                                          kBytesPerElement);
     } else if (buffer->byte_length() % kBytesPerElement != 0) {
-      exception_state->SetSimpleExceptionWithArgs(
-          script::kWrongByteLengthMultiple, 0, kBytesPerElement);
+      exception_state->SetSimpleException(script::kRangeError,
+                                          kWrongByteLengthMultipleErrorFormat,
+                                          kBytesPerElement);
     }
   }
 
@@ -94,8 +97,9 @@
              uint32 length, script::ExceptionState* exception_state)
       : ArrayBufferView(buffer, byte_offset, length * kBytesPerElement) {
     if (this->byte_offset() % kBytesPerElement != 0) {
-      exception_state->SetSimpleExceptionWithArgs(
-          script::kWrongByteOffsetMultiple, 0, kBytesPerElement);
+      exception_state->SetSimpleException(script::kRangeError,
+                                          kWrongByteOffsetMultipleErrorFormat,
+                                          kBytesPerElement);
     } else if (byte_offset + length * kBytesPerElement >
                buffer->byte_length()) {
       exception_state->SetSimpleException(script::kInvalidLength);
diff --git a/src/cobalt/dom/typed_array_test.cc b/src/cobalt/dom/typed_array_test.cc
index 02332d8..8bd93b0 100644
--- a/src/cobalt/dom/typed_array_test.cc
+++ b/src/cobalt/dom/typed_array_test.cc
@@ -335,43 +335,34 @@
   }
 
   StrictMock<MockExceptionState> exception_state;
-  script::MessageType message_type;
-
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
 
   // Create an ArrayBuffer whose size isn't a multiple of
   // TypeParam::kBytesPerElement.
   scoped_refptr<ArrayBuffer> array_buffer =
       new ArrayBuffer(NULL, TypeParam::kBytesPerElement + 1);
+
   // The size of the array_buffer isn't a multiple of
   // TypeParam::kBytesPerElement.
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   scoped_refptr<TypeParam> array =
       new TypeParam(NULL, array_buffer, &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
   // Neither the size of the array_buffer nor the byte_offset is a multiple of
   // TypeParam::kBytesPerElement, but SetSimpleException() should only be called
   // once.
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   array = new TypeParam(NULL, array_buffer, 1, &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
   // Now the size of the array_buffer is a multiple of
   // TypeParam::kBytesPerElement but the byte_offset isn't.
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   array_buffer = new ArrayBuffer(NULL, TypeParam::kBytesPerElement);
   array = new TypeParam(NULL, array_buffer, 1, &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
   // Both the size of the array_buffer and the byte_offset are multiples of
   // TypeParam::kBytesPerElement but array_buffer cannot hold 2 elements.
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   array = new TypeParam(NULL, array_buffer, 0, 2, &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
   // The size of the array_buffer isn't a multiple of
   // TypeParam::kBytesPerElement but the byte_offset is.  Also the whole typed
@@ -389,25 +380,19 @@
   }
 
   StrictMock<MockExceptionState> exception_state;
-  script::MessageType message_type;
-
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
 
   static const uint32 kLength = 5;
   scoped_refptr<TypeParam> source =
       new TypeParam(NULL, kLength + 1, &exception_state);
   scoped_refptr<TypeParam> dest =
       new TypeParam(NULL, kLength, &exception_state);
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   dest->Set(source, &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
-  EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
-      .WillOnce(SaveArg<0>(&message_type));
   source = new TypeParam(NULL, kLength, &exception_state);
   dest = new TypeParam(NULL, kLength + 1, &exception_state);
+  EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
   dest->Set(source, 2, &exception_state);
-  EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
 
   dest->Set(source, 1, &exception_state);
 }
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 7add6f2..16bdb99 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -39,7 +39,11 @@
 #include "cobalt/dom/screen.h"
 #include "cobalt/dom/storage.h"
 #include "cobalt/dom/window_timers.h"
+#include "cobalt/media_session/media_session_client.h"
 #include "cobalt/script/javascript_engine.h"
+#include "cobalt/speech/speech_synthesis.h"
+
+using cobalt::media_session::MediaSession;
 
 namespace cobalt {
 namespace dom {
@@ -65,6 +69,7 @@
 Window::Window(int width, int height, cssom::CSSParser* css_parser,
                Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
                render_tree::ResourceProvider** resource_provider,
+               loader::image::AnimatedImageTracker* animated_image_tracker,
                loader::image::ImageCache* image_cache,
                loader::image::ReducedCacheCapacityManager*
                    reduced_image_cache_capacity_manager,
@@ -75,6 +80,7 @@
                media::WebMediaPlayerFactory* web_media_player_factory,
                script::ExecutionState* execution_state,
                script::ScriptRunner* script_runner,
+               script::ScriptValueFactory* script_value_factory,
                MediaSource::Registry* media_source_registry,
                DomStatTracker* dom_stat_tracker, const GURL& url,
                const std::string& user_agent, const std::string& language,
@@ -87,15 +93,18 @@
                const base::Closure& csp_policy_changed_callback,
                const base::Closure& ran_animation_frame_callbacks_callback,
                const base::Closure& window_close_callback,
+               const base::Closure& window_minimize_callback,
                system_window::SystemWindow* system_window,
                const scoped_refptr<input::InputPoller>& input_poller,
+               const scoped_refptr<MediaSession>& media_session,
                int csp_insecure_allowed_token, int dom_max_element_depth)
     : width_(width),
       height_(height),
       html_element_context_(new HTMLElementContext(
           fetcher_factory, css_parser, dom_parser, can_play_type_handler,
-          web_media_player_factory, script_runner, media_source_registry,
-          resource_provider, image_cache, reduced_image_cache_capacity_manager,
+          web_media_player_factory, script_runner, script_value_factory,
+          media_source_registry, resource_provider, animated_image_tracker,
+          image_cache, reduced_image_cache_capacity_manager,
           remote_typeface_cache, mesh_cache, dom_stat_tracker, language)),
       performance_(new Performance(new base::SystemMonotonicClock())),
       ALLOW_THIS_IN_INITIALIZER_LIST(document_(new Document(
@@ -111,7 +120,8 @@
               dom_max_element_depth)))),
       document_loader_(NULL),
       history_(new History()),
-      navigator_(new Navigator(user_agent, language)),
+      navigator_(new Navigator(user_agent, language, media_session,
+                               script_value_factory)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           relay_on_load_event_(new RelayLoadEvent(this))),
       console_(new Console(execution_state)),
@@ -129,6 +139,7 @@
       ran_animation_frame_callbacks_callback_(
           ran_animation_frame_callbacks_callback),
       window_close_callback_(window_close_callback),
+      window_minimize_callback_(window_minimize_callback),
       system_window_(system_window) {
 #if defined(ENABLE_TEST_RUNNER)
   test_runner_ = new TestRunner();
@@ -182,6 +193,12 @@
   }
 }
 
+void Window::Minimize() {
+  if (!window_minimize_callback_.is_null()) {
+    window_minimize_callback_.Run();
+  }
+}
+
 const scoped_refptr<Navigator>& Window::navigator() const { return navigator_; }
 
 scoped_refptr<cssom::CSSStyleDeclaration> Window::GetComputedStyle(
@@ -319,6 +336,10 @@
   return performance_;
 }
 
+scoped_refptr<speech::SpeechSynthesis> Window::speech_synthesis() const {
+  return speech_synthesis_;
+}
+
 const scoped_refptr<Console>& Window::console() const { return console_; }
 
 const scoped_refptr<Camera3D>& Window::camera_3d() const { return camera_3d_; }
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 77c67c3..d162f0a 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -42,6 +42,7 @@
 #include "cobalt/loader/decoder.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/font/remote_typeface_cache.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
 #include "cobalt/loader/image/image_cache.h"
 #include "cobalt/loader/loader.h"
 #include "cobalt/loader/mesh/mesh_cache.h"
@@ -51,12 +52,21 @@
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/execution_state.h"
 #include "cobalt/script/script_runner.h"
-#include "cobalt/speech/speech_synthesis.h"
+#include "cobalt/script/script_value_factory.h"
 #include "cobalt/system_window/system_window.h"
 #include "googleurl/src/gurl.h"
 #include "starboard/window.h"
 
 namespace cobalt {
+namespace media_session {
+class MediaSession;
+}  // namespace media_session
+namespace speech {
+class SpeechSynthesis;
+}  // namespace speech
+}  // namespace cobalt
+
+namespace cobalt {
 namespace dom {
 
 class Camera3D;
@@ -91,34 +101,39 @@
       HTMLDecoderCreatorCallback;
   typedef UrlRegistry<MediaSource> MediaSourceRegistry;
 
-  Window(int width, int height, cssom::CSSParser* css_parser,
-         Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
-         render_tree::ResourceProvider** resource_provider,
-         loader::image::ImageCache* image_cache,
-         loader::image::ReducedCacheCapacityManager*
-             reduced_image_cache_capacity_manager,
-         loader::font::RemoteTypefaceCache* remote_typeface_cache,
-         loader::mesh::MeshCache* mesh_cache,
-         LocalStorageDatabase* local_storage_database,
-         media::CanPlayTypeHandler* can_play_type_handler,
-         media::WebMediaPlayerFactory* web_media_player_factory,
-         script::ExecutionState* execution_state,
-         script::ScriptRunner* script_runner,
-         MediaSourceRegistry* media_source_registry,
-         DomStatTracker* dom_stat_tracker, const GURL& url,
-         const std::string& user_agent, const std::string& language,
-         const base::Callback<void(const GURL&)> navigation_callback,
-         const base::Callback<void(const std::string&)>& error_callback,
-         network_bridge::CookieJar* cookie_jar,
-         const network_bridge::PostSender& post_sender,
-         const std::string& default_security_policy,
-         dom::CspEnforcementType csp_enforcement_mode,
-         const base::Closure& csp_policy_changed_callback,
-         const base::Closure& ran_animation_frame_callbacks_callback,
-         const base::Closure& window_close_callback,
-         system_window::SystemWindow* system_window,
-         const scoped_refptr<input::InputPoller>& input_poller,
-         int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0);
+  Window(
+      int width, int height, cssom::CSSParser* css_parser, Parser* dom_parser,
+      loader::FetcherFactory* fetcher_factory,
+      render_tree::ResourceProvider** resource_provider,
+      loader::image::AnimatedImageTracker* animated_image_tracker,
+      loader::image::ImageCache* image_cache,
+      loader::image::ReducedCacheCapacityManager*
+          reduced_image_cache_capacity_manager,
+      loader::font::RemoteTypefaceCache* remote_typeface_cache,
+      loader::mesh::MeshCache* mesh_cache,
+      LocalStorageDatabase* local_storage_database,
+      media::CanPlayTypeHandler* can_play_type_handler,
+      media::WebMediaPlayerFactory* web_media_player_factory,
+      script::ExecutionState* execution_state,
+      script::ScriptRunner* script_runner,
+      script::ScriptValueFactory* script_value_factory,
+      MediaSourceRegistry* media_source_registry,
+      DomStatTracker* dom_stat_tracker, const GURL& url,
+      const std::string& user_agent, const std::string& language,
+      const base::Callback<void(const GURL&)> navigation_callback,
+      const base::Callback<void(const std::string&)>& error_callback,
+      network_bridge::CookieJar* cookie_jar,
+      const network_bridge::PostSender& post_sender,
+      const std::string& default_security_policy,
+      dom::CspEnforcementType csp_enforcement_mode,
+      const base::Closure& csp_policy_changed_callback,
+      const base::Closure& ran_animation_frame_callbacks_callback,
+      const base::Closure& window_close_callback,
+      const base::Closure& window_minimize_callback,
+      system_window::SystemWindow* system_window,
+      const scoped_refptr<input::InputPoller>& input_poller,
+      const scoped_refptr<cobalt::media_session::MediaSession>& media_session,
+      int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0);
 
   // Web API: Window
   //
@@ -128,6 +143,7 @@
   const scoped_refptr<Location>& location() const;
   const scoped_refptr<History>& history() const;
   void Close();
+  void Minimize();
 
   scoped_refptr<Window> frames() { return this; }
   unsigned int length() { return 0; }
@@ -230,9 +246,7 @@
 
   // Web API: SpeechSynthesisGetter (implements)
   //   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
-  scoped_refptr<speech::SpeechSynthesis> speech_synthesis() const {
-    return speech_synthesis_;
-  }
+  scoped_refptr<speech::SpeechSynthesis> speech_synthesis() const;
 
   // Custom, not in any spec.
   //
@@ -307,6 +321,7 @@
 
   const base::Closure ran_animation_frame_callbacks_callback_;
   const base::Closure window_close_callback_;
+  const base::Closure window_minimize_callback_;
 
   system_window::SystemWindow* system_window_;
 
diff --git a/src/cobalt/dom/window.idl b/src/cobalt/dom/window.idl
index bfb6fd8..d04fb7a 100644
--- a/src/cobalt/dom/window.idl
+++ b/src/cobalt/dom/window.idl
@@ -42,6 +42,9 @@
   readonly attribute Camera3D camera3D;
   [Conditional=ENABLE_TEST_RUNNER] readonly attribute TestRunner testRunner;
   [CallWith=EnvironmentSettings] void gc();
+  // Mozilla's non-standard minimize() function.
+  // https://developer.mozilla.org/en-US/docs/Web/API/Window/minimize
+  void minimize();
 };
 Window implements GlobalEventHandlers;
 Window implements WindowEventHandlers;
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index efc84d8..f087c28 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -25,6 +25,7 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/network_bridge/net_poster.h"
 #include "googleurl/src/gurl.h"
@@ -51,10 +52,10 @@
         url_("about:blank"),
         window_(new Window(
             1920, 1080, css_parser_.get(), dom_parser_.get(),
-            fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
+            fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
             &local_storage_database_, stub_media_module_.get(),
-            stub_media_module_.get(), NULL, NULL, NULL, NULL, url_, "", "en-US",
-            base::Callback<void(const GURL &)>(),
+            stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
+            "en-US", base::Callback<void(const GURL &)>(),
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
@@ -62,7 +63,8 @@
             base::Closure() /* csp_policy_changed */,
             base::Closure() /* ran_animation_frame_callbacks */,
             base::Closure() /* window_close */,
-            stub_media_module_->system_window(), NULL)) {}
+            base::Closure() /* window_minimize */,
+            stub_media_module_->system_window(), NULL, NULL)) {}
 
   ~WindowTest() OVERRIDE {}
 
diff --git a/src/cobalt/dom/xml_document_test.cc b/src/cobalt/dom/xml_document_test.cc
index 6509968..d4294eb 100644
--- a/src/cobalt/dom/xml_document_test.cc
+++ b/src/cobalt/dom/xml_document_test.cc
@@ -25,7 +25,7 @@
 TEST(XMLDocumentTest, IsXMLDocument) {
   HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL,
                                           NULL, NULL, NULL, NULL, NULL, NULL,
-                                          NULL, "");
+                                          NULL, NULL, NULL, "");
   scoped_refptr<Document> document = new XMLDocument(&html_element_context);
   EXPECT_TRUE(document->IsXMLDocument());
 }
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index 082a006..367607d 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -64,11 +64,11 @@
     : fetcher_factory_(NULL /* network_module */),
       dom_parser_(new Parser()),
       dom_stat_tracker_(new dom::DomStatTracker("HTMLDecoderTest")),
-      html_element_context_(&fetcher_factory_, &stub_css_parser_,
-                            dom_parser_.get(), NULL /* can_play_type_handler */,
-                            NULL /* web_media_player_factory */,
-                            &stub_script_runner_, NULL, NULL, NULL, NULL, NULL,
-                            NULL, dom_stat_tracker_.get(), ""),
+      html_element_context_(
+          &fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
+          NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
+          &stub_script_runner_, NULL /* script_value_factory */, NULL, NULL,
+          NULL, NULL, NULL, NULL, NULL, dom_stat_tracker_.get(), ""),
       document_(
           new dom::Document(&html_element_context_, dom::Document::Options())),
       root_(new dom::Element(document_, base::Token("element"))),
@@ -87,11 +87,11 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("html", element->tag_name());
+  EXPECT_EQ("html", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("body", element->tag_name());
+  EXPECT_EQ("body", element->local_name());
 }
 
 // TODO: Currently HTMLDecoder is using libxml2 SAX parser. It doesn't
@@ -110,11 +110,11 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("html", element->tag_name());
+  EXPECT_EQ("html", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("body", element->tag_name());
+  EXPECT_EQ("body", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlySpaces) {
@@ -129,11 +129,11 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("html", element->tag_name());
+  EXPECT_EQ("html", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("body", element->tag_name());
+  EXPECT_EQ("body", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlyHTML) {
@@ -148,11 +148,11 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("html", element->tag_name());
+  EXPECT_EQ("html", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("body", element->tag_name());
+  EXPECT_EQ("body", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, CanParseDocumentWithOnlyBody) {
@@ -167,11 +167,11 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("html", element->tag_name());
+  EXPECT_EQ("html", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("body", element->tag_name());
+  EXPECT_EQ("body", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, DecodingWholeDocumentShouldAddImpliedTags) {
@@ -186,15 +186,15 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("html", element->tag_name());
+  EXPECT_EQ("html", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("body", element->tag_name());
+  EXPECT_EQ("body", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
   EXPECT_FALSE(element->HasChildNodes());
 }
 
@@ -210,7 +210,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
   EXPECT_FALSE(element->HasChildNodes());
 }
 
@@ -226,7 +226,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("div", element->tag_name());
+  EXPECT_EQ("div", element->local_name());
   EXPECT_TRUE(element->HasAttributes());
   EXPECT_EQ(4, element->attributes()->length());
   EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -251,7 +251,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("div", element->tag_name());
+  EXPECT_EQ("div", element->local_name());
   EXPECT_TRUE(element->HasAttributes());
   EXPECT_EQ(2, element->attributes()->length());
   EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -272,11 +272,11 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   element = element->next_element_sibling();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, CanParseNormalCharacters) {
@@ -291,7 +291,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   dom::Text* text = element->first_child()->AsText();
   ASSERT_TRUE(text);
@@ -325,7 +325,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   dom::Text* text = element->first_child()->AsText();
   ASSERT_TRUE(text);
@@ -333,7 +333,7 @@
 
   element = element->next_element_sibling();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   text = element->first_child()->AsText();
   ASSERT_TRUE(text);
@@ -353,7 +353,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   dom::Text* text = element->first_child()->AsText();
   ASSERT_TRUE(text);
@@ -373,7 +373,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   dom::Text* text = element->first_child()->AsText();
   ASSERT_TRUE(text);
@@ -400,7 +400,7 @@
 
     dom::Element* element = root_->first_element_child();
     ASSERT_TRUE(element);
-    EXPECT_EQ("p", element->tag_name());
+    EXPECT_EQ("p", element->local_name());
 
     dom::Text* text = element->first_child()->AsText();
     ASSERT_TRUE(text);
@@ -424,15 +424,15 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("b", element->tag_name());
+  EXPECT_EQ("b", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("i", element->tag_name());
+  EXPECT_EQ("i", element->local_name());
 }
 
 // Misnested tags: <b><p></b></p>
@@ -451,11 +451,11 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("b", element->tag_name());
+  EXPECT_EQ("b", element->local_name());
 
   element = element->next_element_sibling();
   ASSERT_TRUE(element);
-  EXPECT_EQ("p", element->tag_name());
+  EXPECT_EQ("p", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, TagNamesShouldBeCaseInsensitive) {
@@ -470,7 +470,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("div", element->tag_name());
+  EXPECT_EQ("div", element->local_name());
 }
 
 TEST_F(HTMLDecoderTest, AttributesShouldBeCaseInsensitive) {
@@ -485,7 +485,7 @@
 
   dom::Element* element = root_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("div", element->tag_name());
+  EXPECT_EQ("div", element->local_name());
   EXPECT_TRUE(element->HasAttributes());
   EXPECT_EQ(1, element->attributes()->length());
   EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -509,11 +509,11 @@
 
   root_ = document_->first_element_child();
   ASSERT_TRUE(root_);
-  EXPECT_EQ("html", root_->tag_name());
+  EXPECT_EQ("html", root_->local_name());
 
   dom::Element* head = root_->first_element_child();
   ASSERT_TRUE(head);
-  EXPECT_EQ("head", head->tag_name());
+  EXPECT_EQ("head", head->local_name());
 }
 
 }  // namespace dom_parser
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
index 324f8fd..b85ba7a 100644
--- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -32,6 +32,7 @@
 #include "starboard/ps4/core_dump_handler.h"
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 #endif  // defined(OS_STARBOARD)
+#include "third_party/libxml/src/include/libxml/xmlerror.h"
 
 namespace cobalt {
 namespace dom_parser {
@@ -257,7 +258,7 @@
       element->OnParserEndTag();
     }
 
-    if (element->node_name() == name) {
+    if (element->local_name() == name) {
       return;
     }
   }
@@ -290,6 +291,12 @@
 void LibxmlParserWrapper::OnParsingIssue(IssueSeverity severity,
                                          const std::string& message) {
   DCHECK(severity >= kWarning && severity <= kFatal);
+
+  xmlErrorPtr error = xmlGetLastError();
+  if (error->code == XML_HTML_UNKNOWN_TAG) {
+    return;
+  }
+
   if (severity > max_severity_) {
     max_severity_ = severity;
   }
diff --git a/src/cobalt/dom_parser/xml_decoder_test.cc b/src/cobalt/dom_parser/xml_decoder_test.cc
index 59b2ee4..3fb2732 100644
--- a/src/cobalt/dom_parser/xml_decoder_test.cc
+++ b/src/cobalt/dom_parser/xml_decoder_test.cc
@@ -49,7 +49,7 @@
 
 XMLDecoderTest::XMLDecoderTest()
     : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, NULL, NULL, ""),
+                            NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""),
       document_(new dom::XMLDocument(&html_element_context_)),
       source_location_(base::SourceLocation("[object XMLDecoderTest]", 1, 1)) {}
 
@@ -64,7 +64,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("ELEMENT", element->tag_name());
+  EXPECT_EQ("ELEMENT", element->local_name());
   EXPECT_FALSE(element->HasChildNodes());
 }
 
@@ -88,7 +88,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("ELEMENT", element->tag_name());
+  EXPECT_EQ("ELEMENT", element->local_name());
 
   dom::CDATASection* cdata_section1 = element->first_child()->AsCDATASection();
   ASSERT_TRUE(cdata_section1);
@@ -111,7 +111,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("ELEMENT", element->tag_name());
+  EXPECT_EQ("ELEMENT", element->local_name());
   EXPECT_TRUE(element->HasAttributes());
   EXPECT_EQ(2, element->attributes()->length());
   EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -131,11 +131,11 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("ELEMENT", element->tag_name());
+  EXPECT_EQ("ELEMENT", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, AttributesShouldBeCaseSensitive) {
@@ -149,7 +149,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("ELEMENT", element->tag_name());
+  EXPECT_EQ("ELEMENT", element->local_name());
   EXPECT_TRUE(element->HasAttributes());
   EXPECT_EQ(2, element->attributes()->length());
   EXPECT_EQ("A", element->attributes()->Item(0)->name());
@@ -171,7 +171,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, CanDealWithHTMLAttack) {
@@ -187,7 +187,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, CanDealWithAttack) {
@@ -203,7 +203,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, CanDealWithPEAttack) {
@@ -219,7 +219,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, CanDealWithDTDAttack) {
@@ -235,7 +235,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, CanDealWithLaughsAttack) {
@@ -267,7 +267,7 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("element", element->tag_name());
+  EXPECT_EQ("element", element->local_name());
 }
 
 TEST_F(XMLDecoderTest, CanDealWithXIncludeAttack) {
@@ -287,19 +287,19 @@
 
   dom::Element* element = document_->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("root", element->tag_name());
+  EXPECT_EQ("root", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("child", element->tag_name());
+  EXPECT_EQ("child", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("name", element->tag_name());
+  EXPECT_EQ("name", element->local_name());
 
   element = element->first_element_child();
   ASSERT_TRUE(element);
-  EXPECT_EQ("xi:include", element->tag_name());
+  EXPECT_EQ("xi:include", element->local_name());
 }
 
 }  // namespace dom_parser
diff --git a/src/cobalt/h5vcc/h5vcc.cc b/src/cobalt/h5vcc/h5vcc.cc
index 6f29c36..f3617cf 100644
--- a/src/cobalt/h5vcc/h5vcc.cc
+++ b/src/cobalt/h5vcc/h5vcc.cc
@@ -17,8 +17,10 @@
 namespace cobalt {
 namespace h5vcc {
 
-H5vcc::H5vcc(const Settings& settings) {
-  accessibility_ = new H5vccAccessibility();
+H5vcc::H5vcc(const Settings& settings, const scoped_refptr<dom::Window>& window,
+             dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+  accessibility_ = new H5vccAccessibility(settings.event_dispatcher, window,
+                                          mutation_observer_task_manager);
   account_info_ = new H5vccAccountInfo(settings.account_manager);
   audio_config_array_ = new H5vccAudioConfigArray();
   c_val_ = new H5vccCVal();
diff --git a/src/cobalt/h5vcc/h5vcc.h b/src/cobalt/h5vcc/h5vcc.h
index 71996eb..b316784 100644
--- a/src/cobalt/h5vcc/h5vcc.h
+++ b/src/cobalt/h5vcc/h5vcc.h
@@ -18,6 +18,8 @@
 #include <string>
 
 #include "cobalt/base/event_dispatcher.h"
+#include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/window.h"
 #include "cobalt/h5vcc/h5vcc_accessibility.h"
 #include "cobalt/h5vcc/h5vcc_account_info.h"
 #include "cobalt/h5vcc/h5vcc_audio_config_array.h"
@@ -48,7 +50,8 @@
     std::string initial_deep_link;
   };
 
-  explicit H5vcc(const Settings& config);
+  H5vcc(const Settings& config, const scoped_refptr<dom::Window>& window,
+        dom::MutationObserverTaskManager* mutation_observer_task_manager);
   const scoped_refptr<H5vccAccessibility>& accessibility() const {
     return accessibility_;
   }
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.cc b/src/cobalt/h5vcc/h5vcc_accessibility.cc
index 61edda0..d6f9aec 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.cc
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.cc
@@ -14,14 +14,84 @@
 
 #include "cobalt/h5vcc/h5vcc_accessibility.h"
 
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "cobalt/accessibility/starboard_tts_engine.h"
+#include "cobalt/accessibility/tts_engine.h"
+#include "cobalt/accessibility/tts_logger.h"
+#include "cobalt/base/accessibility_settings_changed_event.h"
+#include "cobalt/browser/switches.h"
 #include "starboard/accessibility.h"
 #include "starboard/memory.h"
 
 namespace cobalt {
 namespace h5vcc {
 
+namespace {
+#if SB_HAS(SPEECH_SYNTHESIS)
+bool IsTextToSpeechEnabled() {
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+  // Check for a command-line override to enable TTS.
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(browser::switches::kUseTTS)) {
+    return true;
+  }
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+#if SB_API_VERSION >= 4
+  // Check if the tts feature is enabled in Starboard.
+  SbAccessibilityTextToSpeechSettings tts_settings = {0};
+  // Check platform settings.
+  if (SbAccessibilityGetTextToSpeechSettings(&tts_settings)) {
+    return tts_settings.has_text_to_speech_setting &&
+           tts_settings.is_text_to_speech_enabled;
+  }
+#endif  // SB_API_VERSION >= 4
+  return false;
+}
+#endif  // SB_HAS(SPEECH_SYNTHESIS)
+}  // namespace
+
+H5vccAccessibility::H5vccAccessibility(
+    base::EventDispatcher* event_dispatcher,
+    const scoped_refptr<dom::Window>& window,
+    dom::MutationObserverTaskManager* mutation_observer_task_manager)
+    : event_dispatcher_(event_dispatcher) {
+  message_loop_proxy_ = base::MessageLoopProxy::current();
+  event_dispatcher_->AddEventCallback(
+      base::AccessibilitySettingsChangedEvent::TypeId(),
+      base::Bind(&H5vccAccessibility::OnApplicationEvent,
+                 base::Unretained(this)));
+#if SB_HAS(SPEECH_SYNTHESIS)
+  if (IsTextToSpeechEnabled()) {
+    // Create a StarboardTTSEngine if TTS is enabled.
+    tts_engine_.reset(new accessibility::StarboardTTSEngine());
+  }
+#endif  // SB_HAS(SPEECH_SYNTHESIS)
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+  if (!tts_engine_) {
+    tts_engine_.reset(new accessibility::TTSLogger());
+  }
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+  if (tts_engine_) {
+    screen_reader_.reset(new accessibility::ScreenReader(
+        window->document(), tts_engine_.get(), mutation_observer_task_manager));
+  }
+}
+
+bool H5vccAccessibility::built_in_screen_reader() const {
+  return screen_reader_ && screen_reader_->enabled();
+}
+
+void H5vccAccessibility::set_built_in_screen_reader(bool value) {
+  if (!screen_reader_) {
+    LOG(WARNING) << "h5vcc.accessibility.builtInScreenReader: not available";
+    return;
+  }
+  screen_reader_->set_enabled(value);
+}
+
 bool H5vccAccessibility::high_contrast_text() const {
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   SbAccessibilityDisplaySettings settings;
   SbMemorySet(&settings, 0, sizeof(settings));
 
@@ -30,13 +100,13 @@
   }
 
   return settings.is_high_contrast_text_enabled;
-#else  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#else   // SB_API_VERSION >= 4
   return  false;
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
 }
 
 bool H5vccAccessibility::text_to_speech() const {
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   SbAccessibilityTextToSpeechSettings settings;
   SbMemorySet(&settings, 0, sizeof(settings));
 
@@ -46,9 +116,32 @@
 
   return settings.has_text_to_speech_setting &&
       settings.is_text_to_speech_enabled;
-#else  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#else   // SB_API_VERSION >= 4
   return  false;
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
+}
+
+void H5vccAccessibility::AddHighContrastTextListener(
+    const H5vccAccessibilityCallbackHolder& holder) {
+  DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_proxy_);
+  high_contrast_text_listener_.reset(
+      new H5vccAccessibilityCallbackReference(this, holder));
+}
+
+void H5vccAccessibility::OnApplicationEvent(const base::Event* event) {
+  UNREFERENCED_PARAMETER(event);
+  // This method should be called from the application event thread.
+  DCHECK_NE(base::MessageLoopProxy::current(), message_loop_proxy_);
+  message_loop_proxy_->PostTask(
+      FROM_HERE, base::Bind(&H5vccAccessibility::InternalOnApplicationEvent,
+                            base::Unretained(this)));
+}
+
+void H5vccAccessibility::InternalOnApplicationEvent() {
+  DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_proxy_);
+  if (high_contrast_text_listener_) {
+    high_contrast_text_listener_->value().Run();
+  }
 }
 
 }  // namespace h5vcc
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.h b/src/cobalt/h5vcc/h5vcc_accessibility.h
index e93c1ab..6d6721f 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.h
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.h
@@ -15,6 +15,11 @@
 #ifndef COBALT_H5VCC_H5VCC_ACCESSIBILITY_H_
 #define COBALT_H5VCC_H5VCC_ACCESSIBILITY_H_
 
+#include "base/message_loop_proxy.h"
+#include "cobalt/accessibility/screen_reader.h"
+#include "cobalt/accessibility/tts_engine.h"
+#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/dom/window.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/wrappable.h"
@@ -28,19 +33,36 @@
   typedef script::CallbackFunction<void()> H5vccAccessibilityCallback;
   typedef script::ScriptValue<H5vccAccessibilityCallback>
       H5vccAccessibilityCallbackHolder;
-  H5vccAccessibility() {}
+  H5vccAccessibility(
+      base::EventDispatcher* event_dispatcher,
+      const scoped_refptr<dom::Window>& window,
+      dom::MutationObserverTaskManager* mutation_observer_task_manager);
 
   bool high_contrast_text() const;
   void AddHighContrastTextListener(
-      const H5vccAccessibilityCallbackHolder& holder) {
-    UNREFERENCED_PARAMETER(holder);
-  }
+      const H5vccAccessibilityCallbackHolder& holder);
+  void OnApplicationEvent(const base::Event* event);
 
   bool text_to_speech() const;
 
+  bool built_in_screen_reader() const;
+  void set_built_in_screen_reader(bool value);
+
   DEFINE_WRAPPABLE_TYPE(H5vccAccessibility);
 
  private:
+  typedef H5vccAccessibilityCallbackHolder::Reference
+      H5vccAccessibilityCallbackReference;
+
+  void InternalOnApplicationEvent();
+
+  scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
+
+  base::EventDispatcher* event_dispatcher_;
+  scoped_ptr<H5vccAccessibilityCallbackReference> high_contrast_text_listener_;
+  scoped_ptr<accessibility::TTSEngine> tts_engine_;
+  scoped_ptr<accessibility::ScreenReader> screen_reader_;
+
   DISALLOW_COPY_AND_ASSIGN(H5vccAccessibility);
 };
 
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.idl b/src/cobalt/h5vcc/h5vcc_accessibility.idl
index 7f62abf..14b5b90 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.idl
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.idl
@@ -21,6 +21,10 @@
   // True if the host operating system's "text to speech" accessibility
   // option is enabled.
   readonly attribute boolean textToSpeech;
+
+  // True if the built-in screen reader is enabled.
+  // Setting it to false disables the built-in screen reader.
+  attribute boolean builtInScreenReader;
 };
 
 callback H5vccAccessibilityCallback = void();
diff --git a/src/cobalt/input/input.gyp b/src/cobalt/input/input.gyp
index 56ca050..27a850e 100644
--- a/src/cobalt/input/input.gyp
+++ b/src/cobalt/input/input.gyp
@@ -45,14 +45,6 @@
             'input_device_manager_starboard.cc',
           ],
         }],
-        ['OS!="starboard" and actual_target_arch=="ps3"', {
-          'sources': [
-            'input_device_manager_ps3.cc',
-            'input_device_manager_ps3.h',
-            'keycode_conversion_ps3.cc',
-            'keycode_conversion_ps3.h',
-          ],
-        }],
         ['OS!="starboard" and actual_target_arch=="win"', {
           'sources': [
             'input_device_manager_<(actual_target_arch).cc',
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 3e71639..269f2d1 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -624,22 +624,22 @@
 Border CreateBorderFromStyle(
     const scoped_refptr<const cssom::CSSComputedStyleData>& style) {
   render_tree::BorderSide left(
-      GetUsedLength(style->border_left_width()).toFloat(),
+      GetUsedNonNegativeLength(style->border_left_width()).toFloat(),
       GetRenderTreeBorderStyle(style->border_left_style()),
       GetUsedColor(style->border_left_color()));
 
   render_tree::BorderSide right(
-      GetUsedLength(style->border_right_width()).toFloat(),
+      GetUsedNonNegativeLength(style->border_right_width()).toFloat(),
       GetRenderTreeBorderStyle(style->border_right_style()),
       GetUsedColor(style->border_right_color()));
 
   render_tree::BorderSide top(
-      GetUsedLength(style->border_top_width()).toFloat(),
+      GetUsedNonNegativeLength(style->border_top_width()).toFloat(),
       GetRenderTreeBorderStyle(style->border_top_style()),
       GetUsedColor(style->border_top_color()));
 
   render_tree::BorderSide bottom(
-      GetUsedLength(style->border_bottom_width()).toFloat(),
+      GetUsedNonNegativeLength(style->border_bottom_width()).toFloat(),
       GetRenderTreeBorderStyle(style->border_bottom_style()),
       GetUsedColor(style->border_bottom_color()));
 
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 80fff36..e1f5288 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -64,7 +64,7 @@
 scoped_refptr<render_tree::Image> GetVideoFrame(
     const scoped_refptr<ShellVideoFrameProvider>& frame_provider,
     render_tree::ResourceProvider* resource_provider) {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget decode_target = frame_provider->GetCurrentSbDecodeTarget();
   if (SbDecodeTargetIsValid(decode_target)) {
 #if SB_HAS(GRAPHICS)
@@ -74,7 +74,7 @@
     return NULL;
 #endif
   } else {
-#else  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else  // SB_API_VERSION >= 4
   UNREFERENCED_PARAMETER(resource_provider);
   {
 #endif
diff --git a/src/cobalt/layout/layout.cc b/src/cobalt/layout/layout.cc
index db09515..2331e2e 100644
--- a/src/cobalt/layout/layout.cc
+++ b/src/cobalt/layout/layout.cc
@@ -152,6 +152,11 @@
         ->RenderAndAnimate(&render_tree_root_builder, math::Vector2dF(0, 0));
   }
 
+  // During computed style update and RenderAndAnimate, we get the actual images
+  // that are linked to their URLs. Now go through them and update the playing
+  // status for animated images.
+  used_style_provider->UpdateAnimatedImages();
+
   render_tree::CompositionNode* static_root_node =
       new render_tree::CompositionNode(render_tree_root_builder.Pass());
 
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index b9d6c75..88f5a41 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -105,16 +105,19 @@
 
 void UpdateCamera(
     float width_to_height_aspect_ratio, scoped_refptr<dom::Camera3DImpl> camera,
+    float max_horizontal_fov_rad, float max_vertical_fov_rad,
     render_tree::MatrixTransform3DNode::Builder* transform_node_builder,
     base::TimeDelta time) {
   UNREFERENCED_PARAMETER(time);
-  transform_node_builder->transform =
-      camera->QueryViewPerspectiveMatrix(width_to_height_aspect_ratio);
+  transform_node_builder->transform = camera->QueryViewPerspectiveMatrix(
+      width_to_height_aspect_ratio, max_horizontal_fov_rad,
+      max_vertical_fov_rad);
 }
 
 scoped_refptr<render_tree::Node> AttachCameraNodes(
     const scoped_refptr<dom::Window> window,
-    const scoped_refptr<render_tree::Node>& source) {
+    const scoped_refptr<render_tree::Node>& source,
+    float max_horizontal_fov_rad, float max_vertical_fov_rad) {
   // Attach a 3D transform node that applies the current camera matrix transform
   // to the rest of the render tree.
   scoped_refptr<render_tree::MatrixTransform3DNode> transform_node =
@@ -127,7 +130,8 @@
   animate_node_builder.Add(
       transform_node,
       base::Bind(&UpdateCamera, window->inner_width() / window->inner_height(),
-                 window->camera_3d()->impl()));
+                 window->camera_3d()->impl(), max_horizontal_fov_rad,
+                 max_vertical_fov_rad));
   return new render_tree::animations::AnimateNode(animate_node_builder,
                                                   transform_node);
 }
@@ -142,12 +146,9 @@
     LayoutStatTracker* layout_stat_tracker)
     : window_(window),
       locale_(icu::Locale::createCanonical(language.c_str())),
-      used_style_provider_(
-          new UsedStyleProvider(window->html_element_context(),
-                                window->html_element_context()->image_cache(),
-                                window->document()->font_cache(),
-                                base::Bind(&AttachCameraNodes, window),
-                                window->html_element_context()->mesh_cache())),
+      used_style_provider_(new UsedStyleProvider(
+          window->html_element_context(), window->document()->font_cache(),
+          base::Bind(&AttachCameraNodes, window))),
       on_render_tree_produced_callback_(on_render_tree_produced),
       layout_trigger_(layout_trigger),
       layout_dirty_(StringPrintf("%s.Layout.IsDirty", name.c_str()), true,
@@ -230,13 +231,15 @@
   // Mark that we are suspended so that we don't try to perform any layouts.
   suspended_ = true;
 
+  // Invalidate any cached layout boxes from the document prior to clearing
+  // the initial containing block. That'll ensure that the full box tree is
+  // destroyed when the containing block is destroyed and that no children of
+  // |initial_containing_block_| are holding on to stale parent pointers.
+  window_->document()->InvalidateLayoutBoxes();
+
   // Clear our reference to the initial containing block to allow any resources
   // like images that were referenced by it to be released.
   initial_containing_block_ = NULL;
-
-  // Invalidate the document's layout so that all references to any resources
-  // such as images will be released.
-  window_->document()->InvalidateLayout();
 }
 
 void LayoutManager::Impl::Resume() {
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 755f400..4811118 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 
 #include "base/bind.h"
+#include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/cssom/filter_function_list_value.h"
@@ -64,6 +65,13 @@
 const char* kEquirectangularMeshURL =
     "h5vcc-embedded://equirectangular_40_40.msh";
 
+const char* kWarningInvalidMeshUrl =
+    "Mesh specification invalid in map-to-mesh filter: "
+    "must be either a valid URL or 'equirectangular'.";
+
+const char* kWarningLoadingMeshFailed =
+    "Could not load mesh specified by map-to-mesh filter.";
+
 // Convert the parsed keyword value for a stereo mode into a stereo mode enum
 // value.
 render_tree::StereoMode ReadStereoMode(
@@ -572,38 +580,88 @@
 
   // Fetch either the embedded equirectangular mesh or a custom one depending
   // on the spec.
-  GURL url;
+  MapToMeshFilter::Builder builder;
   if (spec.mesh_type() == cssom::MapToMeshFunction::kUrls) {
     // Custom mesh URLs.
+    // Set a default mesh (in case no resolution-specific mesh matches).
     cssom::URLValue* url_value =
         base::polymorphic_downcast<cssom::URLValue*>(spec.mesh_url().get());
-    url = GURL(url_value->value());
+    GURL default_url(url_value->value());
+
+    if (!default_url.is_valid()) {
+      DLOG(WARNING) << kWarningInvalidMeshUrl;
+      return;
+    }
+
+    scoped_refptr<loader::mesh::MeshProjection> default_mesh_projection(
+        used_style_provider()->ResolveURLToMeshProjection(default_url));
+
+    if (!default_mesh_projection) {
+      DLOG(WARNING) << kWarningLoadingMeshFailed;
+      return;
+    }
+
+    builder.SetDefaultMeshes(
+        default_mesh_projection->GetMesh(
+            loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
+        default_mesh_projection->GetMesh(
+            loader::mesh::MeshProjection::kRightEyeCollection));
+
+    // Lookup among the list of resolutions for a match and use that mesh URL.
+    const cssom::MapToMeshFunction::ResolutionMatchedMeshListBuilder& meshes =
+        spec.resolution_matched_meshes();
+    for (size_t i = 0; i < meshes.size(); i++) {
+      cssom::URLValue* url_value = base::polymorphic_downcast<cssom::URLValue*>(
+          meshes[i]->mesh_url().get());
+      GURL url(url_value->value());
+
+      if (!url.is_valid()) {
+        DLOG(WARNING) << kWarningInvalidMeshUrl;
+        return;
+      }
+      scoped_refptr<loader::mesh::MeshProjection> mesh_projection(
+          used_style_provider()->ResolveURLToMeshProjection(url));
+
+      if (!mesh_projection) {
+        DLOG(WARNING) << kWarningLoadingMeshFailed;
+      }
+
+      TRACE_EVENT2("cobalt::layout",
+                   "ReplacedBox::RenderAndAnimateContentWithMapToMesh()",
+                   "height", meshes[i]->height_match(),
+                   "crc", mesh_projection->crc().value_or(-1));
+
+      builder.AddResolutionMatchedMeshes(
+          math::Size(meshes[i]->width_match(), meshes[i]->height_match()),
+          mesh_projection->GetMesh(
+              loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
+          mesh_projection->GetMesh(
+              loader::mesh::MeshProjection::kRightEyeCollection));
+    }
   } else if (spec.mesh_type() == cssom::MapToMeshFunction::kEquirectangular) {
-    url = GURL(kEquirectangularMeshURL);
+    GURL url(kEquirectangularMeshURL);
+    scoped_refptr<loader::mesh::MeshProjection> mesh_projection(
+        used_style_provider()->ResolveURLToMeshProjection(url));
+
+    if (!mesh_projection) {
+      DLOG(WARNING) << kWarningLoadingMeshFailed;
+    }
+
+    builder.SetDefaultMeshes(
+        mesh_projection->GetMesh(
+            loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
+        mesh_projection->GetMesh(
+            loader::mesh::MeshProjection::kRightEyeCollection));
   }
 
-  DCHECK(url.is_valid())
-      << "Mesh specification invalid in map-to-mesh filter: must be either a"
-         " valid URL or 'equirectangular'";
-
-  scoped_refptr<loader::mesh::MeshProjection> mesh_projection(
-      used_style_provider()->ResolveURLToMeshProjection(url));
-
-  DCHECK(mesh_projection)
-      << "Could not load mesh specified by map-to-mesh filter.";
-
-  scoped_refptr<render_tree::Mesh> left_eye_mesh = mesh_projection->GetMesh(
-      loader::mesh::MeshProjection::kLeftEyeOrMonoCollection);
-  scoped_refptr<render_tree::Mesh> right_eye_mesh = mesh_projection->GetMesh(
-      loader::mesh::MeshProjection::kRightEyeCollection);
-
-  scoped_refptr<render_tree::Node> filter_node = new FilterNode(
-      MapToMeshFilter(stereo_mode, left_eye_mesh, right_eye_mesh),
-      animate_node);
+  scoped_refptr<render_tree::Node> filter_node =
+      new FilterNode(MapToMeshFilter(stereo_mode, builder), animate_node);
 
   // Attach a 3D camera to the map-to-mesh node.
   border_node_builder->AddChild(
-      used_style_provider()->attach_camera_node_function().Run(filter_node));
+      used_style_provider()->attach_camera_node_function().Run(
+          filter_node, mtm_function->horizontal_fov_in_radians(),
+          mtm_function->vertical_fov_in_radians()));
 }
 
 void ReplacedBox::RenderAndAnimateContentWithLetterboxing(
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index d7d0b88..91e64d4 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -407,15 +407,13 @@
 }  // namespace
 
 UsedStyleProvider::UsedStyleProvider(
-    dom::HTMLElementContext* html_element_context,
-    loader::image::ImageCache* image_cache, dom::FontCache* font_cache,
-    const AttachCameraNodeFunction& attach_camera_node_function,
-    loader::mesh::MeshCache* mesh_cache)
-    : html_element_context_(html_element_context),
-      image_cache_(image_cache),
-      font_cache_(font_cache),
-      attach_camera_node_function_(attach_camera_node_function),
-      mesh_cache_(mesh_cache) {}
+    dom::HTMLElementContext* html_element_context, dom::FontCache* font_cache,
+    const AttachCameraNodeFunction& attach_camera_node_function)
+    : font_cache_(font_cache),
+      animated_image_tracker_(html_element_context->animated_image_tracker()),
+      image_cache_(html_element_context->image_cache()),
+      mesh_cache_(html_element_context->mesh_cache()),
+      attach_camera_node_function_(attach_camera_node_function) {}
 
 scoped_refptr<dom::FontList> UsedStyleProvider::GetUsedFontList(
     const scoped_refptr<cssom::PropertyValue>& font_family_refptr,
@@ -469,8 +467,16 @@
 
 scoped_refptr<loader::image::Image> UsedStyleProvider::ResolveURLToImage(
     const GURL& url) {
+  DCHECK(animated_image_tracker_);
   DCHECK(image_cache_);
-  return image_cache_->CreateCachedResource(url)->TryGetResource();
+  scoped_refptr<loader::image::Image> image =
+      image_cache_->CreateCachedResource(url)->TryGetResource();
+  if (image && image->IsAnimated()) {
+    loader::image::AnimatedImage* animated_image =
+        base::polymorphic_downcast<loader::image::AnimatedImage*>(image.get());
+    animated_image_tracker_->RecordImage(url, animated_image);
+  }
+  return image;
 }
 
 scoped_refptr<loader::mesh::MeshProjection>
@@ -479,6 +485,10 @@
   return mesh_cache_->CreateCachedResource(url)->TryGetResource();
 }
 
+void UsedStyleProvider::UpdateAnimatedImages() {
+  animated_image_tracker_->ProcessRecordedImages();
+}
+
 void UsedStyleProvider::CleanupAfterLayout() {
   // Clear out the last font properties prior to requesting that the font cache
   // process inactive font lists. The reason for this is that the font cache
@@ -516,6 +526,21 @@
   return LayoutUnit(length->value());
 }
 
+LayoutUnit GetUsedNonNegativeLength(
+    const scoped_refptr<cssom::PropertyValue>& length_refptr) {
+  cssom::LengthValue* length =
+      base::polymorphic_downcast<cssom::LengthValue*>(length_refptr.get());
+  DCHECK_EQ(length->unit(), cssom::kPixelsUnit);
+  LayoutUnit layout_unit(length->value());
+  if (layout_unit < LayoutUnit(0)) {
+    DLOG(WARNING) << "Invalid non-negative layout length "
+                  << layout_unit.toFloat() << ", original length was "
+                  << length->value();
+    layout_unit = LayoutUnit(0);
+  }
+  return layout_unit;
+}
+
 class UsedLengthValueProvider : public cssom::NotReachedPropertyValueVisitor {
  public:
   explicit UsedLengthValueProvider(LayoutUnit percentage_base,
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h
index bfcdfa1..3e670df 100644
--- a/src/cobalt/layout/used_style.h
+++ b/src/cobalt/layout/used_style.h
@@ -28,6 +28,7 @@
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/layout/layout_unit.h"
 #include "cobalt/layout/size_layout_unit.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
 #include "cobalt/loader/image/image.h"
 #include "cobalt/loader/image/image_cache.h"
 #include "cobalt/loader/mesh/mesh_projection.h"
@@ -57,14 +58,19 @@
 
 class UsedStyleProvider {
  public:
+  // A function that will attach a camera matrix projection transform to a given
+  // sub render tree.  |max_horizontal_fov_rad| and |max_vertical_fov_rad|
+  // dictate the minimum field of view to use in that direction, in radians.
+  // These minimum FOV values are compared against the values derived from
+  // applying the aspect ratio to the other FOV value, and using whichever one
+  // is most minimum.
   typedef base::Callback<scoped_refptr<render_tree::Node>(
-      const scoped_refptr<render_tree::Node>&)> AttachCameraNodeFunction;
+      const scoped_refptr<render_tree::Node>&, float max_horizontal_fov_rad,
+      float max_vertical_fov_rad)> AttachCameraNodeFunction;
 
-  UsedStyleProvider(dom::HTMLElementContext* html_element_context,
-                    loader::image::ImageCache* image_cache,
-                    dom::FontCache* font_cache,
-                    const AttachCameraNodeFunction& attach_camera_node_function,
-                    loader::mesh::MeshCache* mesh_cache = NULL);
+  UsedStyleProvider(
+      dom::HTMLElementContext* html_element_context, dom::FontCache* font_cache,
+      const AttachCameraNodeFunction& attach_camera_node_function);
 
   scoped_refptr<dom::FontList> GetUsedFontList(
       const scoped_refptr<cssom::PropertyValue>& font_family_refptr,
@@ -75,29 +81,26 @@
   scoped_refptr<loader::image::Image> ResolveURLToImage(const GURL& url);
   scoped_refptr<loader::mesh::MeshProjection> ResolveURLToMeshProjection(
       const GURL& url);
-  bool has_image_cache(const loader::image::ImageCache* image_cache) const {
-    return image_cache == image_cache_;
-  }
-
-  dom::HTMLElementContext* html_element_context() const {
-    return html_element_context_;
-  }
 
   const AttachCameraNodeFunction attach_camera_node_function() const {
     return attach_camera_node_function_;
   }
 
+  // Notifies animated image tracker to update the playing status of animated
+  // images.
+  void UpdateAnimatedImages();
+
  private:
   // Called after layout is completed so that it can perform any necessary
   // cleanup. Its primary current purpose is making a request to the font cache
   // to remove any font lists that are no longer being referenced by boxes.
   void CleanupAfterLayout();
 
-  dom::HTMLElementContext* html_element_context_;
-  loader::image::ImageCache* const image_cache_;
   dom::FontCache* const font_cache_;
-  AttachCameraNodeFunction attach_camera_node_function_;
+  loader::image::AnimatedImageTracker* const animated_image_tracker_;
+  loader::image::ImageCache* const image_cache_;
   loader::mesh::MeshCache* const mesh_cache_;
+  AttachCameraNodeFunction attach_camera_node_function_;
 
   // |font_list_key_| is retained in between lookups so that the font names
   // vector will not need to allocate elements each time that it is populated.
@@ -133,6 +136,9 @@
 LayoutUnit GetUsedLength(
     const scoped_refptr<cssom::PropertyValue>& length_refptr);
 
+LayoutUnit GetUsedNonNegativeLength(
+    const scoped_refptr<cssom::PropertyValue>& length_refptr);
+
 class UsedBackgroundNodeProvider
     : public cssom::NotReachedPropertyValueVisitor {
  public:
diff --git a/src/cobalt/layout_tests/layout_snapshot.cc b/src/cobalt/layout_tests/layout_snapshot.cc
index 756d84f..cb375a7 100644
--- a/src/cobalt/layout_tests/layout_snapshot.cc
+++ b/src/cobalt/layout_tests/layout_snapshot.cc
@@ -68,12 +68,14 @@
   scoped_ptr<media::MediaModule> stub_media_module(
       new media::MediaModuleStub());
 
+  // Use 128M of image cache to minimize the effect of image loading.
+  const size_t kImageCacheCapacity = 128 * 1024 * 1024;
+
   // Use test runner mode to allow the content itself to dictate when it is
   // ready for layout should be performed.  See cobalt/dom/test_runner.h.
-  browser::WebModule::Options web_module_options(viewport_size);
+  browser::WebModule::Options web_module_options;
   web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
-  // Use 128M of image cache to minimize the effect of image loading.
-  web_module_options.image_cache_capacity = 128 * 1024 * 1024;
+  web_module_options.image_cache_capacity = kImageCacheCapacity;
 
   // Prepare a slot for our results to be placed when ready.
   base::optional<browser::WebModule::LayoutResults> results;
@@ -83,9 +85,11 @@
       url, base::Bind(&WebModuleOnRenderTreeProducedCallback, &results,
                       &run_loop, MessageLoop::current()),
       base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
-      base::Closure() /* window_close_callback */, stub_media_module.get(),
-      &network_module, viewport_size, resource_provider,
-      stub_media_module->system_window(), 60.0f, web_module_options);
+      base::Closure() /* window_close_callback */,
+      base::Closure() /* window_minimize_callback */,
+      stub_media_module.get(), &network_module, viewport_size,
+      resource_provider, stub_media_module->system_window(), 60.0f,
+      web_module_options);
 
   run_loop.Run();
 
diff --git a/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
index aa33681..d7da758 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
+++ b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
@@ -36,10 +36,7 @@
       image_array[i] = image_bytes[i];
     }
 
-    // TODO(jsalvadorp): When the proper Blob constructor that takes an array
-    // of arguments is supported, change the following to reflect the new
-    // signature.
-    var image_blob = new Blob(image_array.buffer);
+    var image_blob = new Blob([image_array.buffer]);
     var image_url = URL.createObjectURL(image_blob);
 
     var image = new Image();
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png
new file mode 100644
index 0000000..b265af2
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name.html
new file mode 100644
index 0000000..01a1ebd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+ | The "local" string is used to match the Postscript name or the full font name
+ | in the name table of locally available fonts. While not explicitly specified
+ | this is done in a case insensitive manner to mimic the other browsers.
+ |   https://www.w3.org/TR/css-fonts-3/#descdef-src
+ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <style>
+    body {
+      margin: 0;
+      font-family: Roboto;
+      font-size: 30px;
+      font-weight: normal;
+      color: #fff;
+    }
+    .containing-block {
+      background-color: #03a9f4;
+      width: 700px;
+    }
+    .roboto-regular {
+      font-family: font-face-roboto-regular;
+    }
+    .roboto-bold {
+      font-family: font-face-roboto-bold;
+    }
+    .coming-soon-regular {
+      font-family: font-face-coming-soon-regular;
+    }
+    .dancing-script-regular {
+      font-family: font-face-dancing-script-regular;
+    }
+    .dancing-script-bold {
+      font-family: font-face-dancing-script-bold;
+    }
+    @font-face {
+      font-family: font-face-roboto-regular;
+      src: local(Roboto-Regular);
+    }
+    @font-face {
+      font-family: font-face-roboto-bold;
+      src: local(RoBoTo-BoLd);
+    }
+    @font-face {
+      font-family: font-face-coming-soon-regular;
+      src: local(comingsoon);
+    }
+    @font-face {
+      font-family: font-face-dancing-script-regular;
+      src: local(DANCINGscript);
+    }
+    @font-face {
+      font-family: font-face-dancing-script-bold;
+      src: local(dancingSCRIPT-bold);
+    }
+  </style>
+</head>
+<body>
+  <div class="containing-block">
+    <div class="roboto-regulary">The Hegemony Consul</div>
+    <div class="roboto-bold">The Hegemony Consul</div>
+    <div class="coming-soon-regular">The Hegemony Consul</div>
+    <div class="dancing-script-regular">The Hegemony Consul</div>
+    <div class="dancing-script-bold">The Hegemony Consul</div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name-expected.png
new file mode 100644
index 0000000..b265af2
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name.html
new file mode 100644
index 0000000..f6072d6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+ | The "local" string is used to match the Postscript name or the full font name
+ | in the name table of locally available fonts. While not explicitly specified,
+ | this is done in a case insensitive manner to mimic the other browsers.
+ |   https://www.w3.org/TR/css-fonts-3/#descdef-src
+ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <style>
+    body {
+      margin: 0;
+      font-family: Roboto;
+      font-size: 30px;
+      font-weight: normal;
+      color: #fff;
+    }
+    .containing-block {
+      background-color: #03a9f4;
+      width: 700px;
+    }
+    .roboto-regular {
+      font-family: font-face-roboto-regular;
+    }
+    .roboto-bold {
+      font-family: font-face-roboto-bold;
+    }
+    .coming-soon-regular {
+      font-family: font-face-coming-soon-regular;
+    }
+    .dancing-script-regular {
+      font-family: font-face-dancing-script-regular;
+    }
+    .dancing-script-bold {
+      font-family: font-face-dancing-script-bold;
+    }
+    @font-face {
+      font-family: font-face-roboto-regular;
+      src: local(Roboto Regular);
+    }
+    @font-face {
+      font-family: font-face-roboto-bold;
+      src: local(RoBoTo BoLd);
+    }
+    @font-face {
+      font-family: font-face-coming-soon-regular;
+      src: local(coming soon);
+    }
+    @font-face {
+      font-family: font-face-dancing-script-regular;
+      src: local(DANCING script);
+    }
+    @font-face {
+      font-family: font-face-dancing-script-bold;
+      src: local(dancing SCRIPT bold);
+    }
+  </style>
+</head>
+<body>
+  <div class="containing-block">
+    <div class="roboto-regulary">The Hegemony Consul</div>
+    <div class="roboto-bold">The Hegemony Consul</div>
+    <div class="coming-soon-regular">The Hegemony Consul</div>
+    <div class="dancing-script-regular">The Hegemony Consul</div>
+    <div class="dancing-script-bold">The Hegemony Consul</div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
index e894c76..018a47c 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
index b9bd6d1..419b372 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
@@ -40,11 +40,6 @@
       font-weight: bold;
       font-style: italic;
     }
-    .font-face-1-normal-italic {
-      font-family: font-face-1;
-      font-weight: normal;
-      font-style: italic;
-    }
     .font-face-2-normal-normal {
       font-family: font-face-2;
       font-weight: normal;
@@ -99,7 +94,8 @@
   <div class="containing-block">
     <div class="font-face-1-normal-normal">The Hegemony Consul</div>
     <!-- Note: font-face-1 selects a local font file, which only has an regular
-      style.  So, for the next three cases, a normal style is rendered.
+      style.  So, for the next three cases, a normal style is rendered,
+      although synthetic bolding is used for the bold weights.
     -->
     <div class="font-face-1-normal-italic">The Hegemony Consul</div>
     <div class="font-face-1-bold-normal">The Hegemony Consul</div>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
index 76ce4dd..eb5beaa 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
@@ -1,4 +1,6 @@
 4-2-font-face-font-family-hides-system-font-family
+4-3-src-local-can-match-font-postscript-name
+4-3-src-local-can-match-full-font-name
 4-3-use-first-available-local-font-face
 4-3-use-next-font-family-if-font-face-sources-unavailable
 4-4-use-correct-style-in-font-face-set
@@ -7,4 +9,6 @@
 5-2-use-numerical-font-weights-in-family-face-matching
 5-2-use-specified-font-family-if-available
 5-2-use-system-fallback-if-no-matching-family-is-found
-text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs
\ No newline at end of file
+synthetic-bolding-should-not-occur-on-bold-font
+synthetic-bolding-should-occur-on-non-bold-font
+text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png
new file mode 100644
index 0000000..5c923af
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html
new file mode 100644
index 0000000..aa176c3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+ | In Cobalt, bold fonts are never synthetically boldable.
+ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <style>
+    body {
+      margin: 0;
+      font-family: Roboto;
+      font-size: 30px;
+      font-weight: normal;
+      color: #fff;
+    }
+    .containing-block {
+      background-color: #03a9f4;
+      width: 700px;
+    }
+    .roboto-normal {
+      font-family: roboto-face;
+      font-weight: normal;
+    }
+    .roboto-bold {
+      font-family: roboto-face;
+      font-weight: bold;
+    }
+    .noto-serif-normal {
+      font-family: serif-face;
+      font-weight: normal;
+    }
+    .noto-serif-bold {
+      font-family: serif-face;
+      font-weight: bold;
+    }
+    .dancing-script-normal {
+      font-family: dancing-script-face;
+      font-weight: normal;
+    }
+    .dancing-script-bold {
+      font-family: dancing-script-face;
+      font-weight: bold;
+    }
+    @font-face {
+      font-family: roboto-face;
+      src: local(Roboto-Bold);
+    }
+    @font-face {
+      font-family: serif-face;
+      src: local(NotoSerif-Bold);
+    }
+    @font-face {
+      font-family: dancing-script-face;
+      src: local(DancingScript-Bold);
+    }
+  </style>
+</head>
+<body>
+  <div class="containing-block">
+    <div class="roboto-normal">The Hegemony Consul</div>
+    <div class="roboto-bold">The Hegemony Consul</div>
+    <div class="noto-serif-normal">The Hegemony Consul</div>
+    <div class="noto-serif-bold">The Hegemony Consul</div>
+    <div class="dancing-script-normal">The Hegemony Consul</div>
+    <div class="dancing-script-bold">The Hegemony Consul</div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png
new file mode 100644
index 0000000..324f60e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html
new file mode 100644
index 0000000..504805b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+ | In Cobalt, non-bold fonts are synthetically boldable if the desired weight is
+ | bold.
+ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <style>
+    body {
+      margin: 0;
+      font-family: Roboto;
+      font-size: 30px;
+      font-weight: normal;
+      color: #fff;
+    }
+    .containing-block {
+      background-color: #03a9f4;
+      width: 700px;
+    }
+    .roboto-normal {
+      font-family: roboto-face;
+      font-weight: normal;
+    }
+    .roboto-bold {
+      font-family: roboto-face;
+      font-weight: bold;
+    }
+    .noto-serif-normal {
+      font-family: serif-face;
+      font-weight: normal;
+    }
+    .noto-serif-bold {
+      font-family: serif-face;
+      font-weight: bold;
+    }
+    .dancing-script-normal {
+      font-family: dancing-script-face;
+      font-weight: normal;
+    }
+    .dancing-script-bold {
+      font-family: dancing-script-face;
+      font-weight: bold;
+    }
+    @font-face {
+      font-family: roboto-face;
+      src: local(Roboto-Regular);
+    }
+    @font-face {
+      font-family: serif-face;
+      src: local(NotoSerif);
+    }
+    @font-face {
+      font-family: dancing-script-face;
+      src: local(DancingScript);
+    }
+  </style>
+</head>
+<body>
+  <div class="containing-block">
+    <div class="roboto-normal">The Hegemony Consul</div>
+    <div class="roboto-bold">The Hegemony Consul</div>
+    <div class="noto-serif-normal">The Hegemony Consul</div>
+    <div class="noto-serif-bold">The Hegemony Consul</div>
+    <div class="dancing-script-normal">The Hegemony Consul</div>
+    <div class="dancing-script-bold">The Hegemony Consul</div>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png
index 3a2da46..2afbcce 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html
index e17019c..ba5bc69 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html
@@ -10,7 +10,7 @@
       margin: 0;
       font-family: Roboto;
       font-size: 75px;
-      font-weight: bold;
+      font-weight: normal;
       color: #fff;
     }
     .absolute-block1 {
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
index b54cb76..8363eef 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
@@ -114,7 +114,7 @@
 # Synchronous
 responsetype.html,DISABLE
 responsexml-basic.htm,FAIL
-responsexml-document-properties.htm,FAIL
+# js_error: responsexml-document-properties.htm,FAIL
 responsexml-media-type.htm,FAIL
 responsexml-non-document-types.htm,PASS
 # Synchronous
@@ -135,7 +135,7 @@
 send-authentication-basic-setrequestheader.htm,FAIL
 send-authentication-competing-names-passwords.htm,FAIL
 send-authentication-cors-basic-setrequestheader.htm,FAIL
-send-authentication-existing-session-manual.htm,FAIL
+# js_error: send-authentication-existing-session-manual.htm,FAIL
 # Synchronous
 send-authentication-prompt-2-manual.htm,FAIL
 send-authentication-prompt-manual.htm,FAIL
@@ -150,7 +150,7 @@
 send-entity-body-basic.htm,FAIL
 send-entity-body-document-bogus.htm,FAIL
 # SyntaxError: Unexpected EOF
-send-entity-body-document.htm,FAIL
+# js_error: send-entity-body-document.htm,FAIL
 send-entity-body-empty.htm,FAIL
 send-entity-body-get-head-async.htm,PASS
 send-entity-body-get-head.htm,FAIL
@@ -211,14 +211,14 @@
 xmlhttprequest-network-error.htm,PASS
 xmlhttprequest-network-error-sync.htm,FAIL
 # Not sure why these fail. Seems to break the harness.
-xmlhttprequest-timeout-aborted.html,FAIL
-xmlhttprequest-timeout-abortedonmain.html,FAIL
-xmlhttprequest-timeout-overridesexpires.html,FAIL
-xmlhttprequest-timeout-overrides.html,FAIL
-xmlhttprequest-timeout-simple.html,FAIL
+# js_error: xmlhttprequest-timeout-aborted.html,FAIL
+# js_error: xmlhttprequest-timeout-abortedonmain.html,FAIL
+# js_error: xmlhttprequest-timeout-overridesexpires.html,FAIL
+# js_error: xmlhttprequest-timeout-overrides.html,FAIL
+# js_error: xmlhttprequest-timeout-simple.html,FAIL
 # Synchronous
-xmlhttprequest-timeout-synconmain.html,FAIL
-xmlhttprequest-timeout-twice.html,FAIL
+# js_error: xmlhttprequest-timeout-synconmain.html,FAIL
+# js_error: xmlhttprequest-timeout-twice.html,FAIL
 # Workers are disabled.
 xmlhttprequest-timeout-worker-aborted.html,DISABLE
 xmlhttprequest-timeout-worker-overridesexpires.html,DISABLE
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt
index 52e488f..4351f85 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt
@@ -5,19 +5,16 @@
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.setAttributeNS: creation mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.setAttributeNS: prefixed attribute creation mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.setAttributeNS: removal mutation
-# TODO: Triage these failures.
+# Sets Element.localName, which isn't implemented in Cobalt.
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.attributes.value: update mutation
+# Input element is not supported in Cobalt
 nodes/MutationObserver-attributes.html,FAIL,attributes HTMLInputElement.type: type update mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.add: same value mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.remove: missing token removal mutation
+# Cobalt doesn't implement DomTokenList.toggle
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: token removal mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: token addition mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token removal mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token removal no mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced missing token removal no mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced existing token addition no mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced existing token removal no mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token addition no mutation
 nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token addition mutation
 
 # Most of these tests use operations such as insertData, replaceData, etc.,
@@ -28,21 +25,15 @@
 nodes/MutationObserver-characterData.html,PASS,characterData/characterDataOldValue alone Text.data: simple mutation
 
 nodes/MutationObserver-childList.html,PASS
-# Node.textContent tests fail because set_text_content doesn't run the
-# replace-all algorithm exactly.
-# TODO: Fix this.
-nodes/MutationObserver-childList.html,FAIL,childList Node.textContent: replace content mutation
 # Cobalt doesn't implement Node.normalize.
 nodes/MutationObserver-childList.html,FAIL,childList Node.normalize mutation
 nodes/MutationObserver-childList.html,FAIL,childList Node.normalize mutations
-# TODO: Investigate failures.
-nodes/MutationObserver-childList.html,FAIL,childList Node.insertBefore: addition mutation
-nodes/MutationObserver-childList.html,FAIL,childList Node.insertBefore: removal and addition mutations
 
 nodes/MutationObserver-disconnect.html,PASS
 
 nodes/MutationObserver-document.html,PASS
-# TODO: Investigate failures.
+# These fail because Cobalt doesn't support Event Loop, and MutationObserver spec relies on the concept of
+# queuing a microtask to ensure that queued events get dispatched at the correct time.
 nodes/MutationObserver-document.html,FAIL,parser insertion mutations
 nodes/MutationObserver-document.html,FAIL,parser script insertion mutation
 nodes/MutationObserver-document.html,FAIL,removal of parent during parsing
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/mediasession/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/mediasession/web_platform_tests.txt
new file mode 100644
index 0000000..ab64abb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/mediasession/web_platform_tests.txt
@@ -0,0 +1,6 @@
+# MediaSession tests
+# TODO
+# idlharness.html
+# mediametadata.html
+# js_error: playbackstate.html,PASS
+setactionhandler.html,PASS
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index b491caf..5794fa7 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -23,7 +23,6 @@
 #include "base/run_loop.h"
 #include "base/string_util.h"
 #include "base/values.h"
-#include "cobalt/browser/memory_settings/memory_settings.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/dom/csp_delegate_factory.h"
 #include "cobalt/layout_tests/test_utils.h"
@@ -134,7 +133,7 @@
   message_loop->PostTask(FROM_HERE, base::Bind(Quit, run_loop));
 }
 
-std::string RunWebPlatformTest(const GURL& url) {
+std::string RunWebPlatformTest(const GURL& url, bool* got_results) {
   LogFilter log_filter;
   for (size_t i = 0; i < arraysize(kLogSuppressions); ++i) {
     log_filter.Add(kLogSuppressions[i]);
@@ -164,7 +163,7 @@
       dom::kCspEnforcementEnable, CspDelegatePermissive::Create);
   // Use test runner mode to allow the content itself to dictate when it is
   // ready for layout should be performed.  See cobalt/dom/test_runner.h.
-  browser::WebModule::Options web_module_options(kDefaultViewportSize);
+  browser::WebModule::Options web_module_options;
   web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
 
   // Prepare a slot for our results to be placed when ready.
@@ -176,14 +175,17 @@
       url, base::Bind(&WebModuleOnRenderTreeProducedCallback, &results,
                       &run_loop, MessageLoop::current()),
       base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
-      base::Closure() /* window_close_callback */, media_module.get(),
-      &network_module, kDefaultViewportSize, &resource_provider,
-      media_module->system_window(), 60.0f, web_module_options);
+      base::Closure() /* window_close_callback */,
+      base::Closure() /* window_minimize_callback */,
+      media_module.get(), &network_module, kDefaultViewportSize,
+      &resource_provider, media_module->system_window(), 60.0f,
+      web_module_options);
   run_loop.Run();
   const std::string extract_results =
       "document.getElementById(\"__testharness__results__\").textContent;";
   std::string output = web_module.ExecuteJavascript(
-      extract_results, base::SourceLocation(__FILE__, __LINE__, 1));
+      extract_results, base::SourceLocation(__FILE__, __LINE__, 1),
+      got_results);
   return output;
 }
 
@@ -274,7 +276,9 @@
 
   std::cout << "(" << test_url << ")" << std::endl;
 
-  std::string json_results = RunWebPlatformTest(test_url);
+  bool got_results = false;
+  std::string json_results = RunWebPlatformTest(test_url, &got_results);
+  EXPECT_TRUE(got_results);
   std::vector<TestResult> results = ParseResults(json_results);
   for (size_t i = 0; i < results.size(); ++i) {
     const WebPlatformTestInfo& test_info = GetParam();
@@ -302,6 +306,10 @@
 
 INSTANTIATE_TEST_CASE_P(dom, WebPlatformTest,
                         ::testing::ValuesIn(EnumerateWebPlatformTests("dom")));
+
+INSTANTIATE_TEST_CASE_P(
+    mediasession, WebPlatformTest,
+    ::testing::ValuesIn(EnumerateWebPlatformTests("mediasession")));
 #endif  // !defined(COBALT_WIN)
 
 }  // namespace layout_tests
diff --git a/src/cobalt/loader/font/typeface_decoder.cc b/src/cobalt/loader/font/typeface_decoder.cc
index cd41c09..8736541 100644
--- a/src/cobalt/loader/font/typeface_decoder.cc
+++ b/src/cobalt/loader/font/typeface_decoder.cc
@@ -26,14 +26,13 @@
       success_callback_(success_callback),
       error_callback_(error_callback),
       is_raw_data_too_large_(false),
-      suspended_(false) {
-  DCHECK(resource_provider_);
+      is_suspended_(!resource_provider_) {
   DCHECK(!success_callback_.is_null());
   DCHECK(!error_callback_.is_null());
 }
 
 void TypefaceDecoder::DecodeChunk(const char* data, size_t size) {
-  if (suspended_) {
+  if (is_suspended_) {
     DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
     return;
   }
@@ -65,7 +64,7 @@
 }
 
 void TypefaceDecoder::Finish() {
-  if (suspended_) {
+  if (is_suspended_) {
     DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
     return;
   }
@@ -75,12 +74,6 @@
     return;
   }
 
-  if (!resource_provider_) {
-    error_callback_.Run(
-        "No resource provider was passed to the TypefaceDecoder.");
-    return;
-  }
-
   std::string error_string;
   scoped_refptr<render_tree::Typeface> decoded_typeface =
       resource_provider_->CreateTypefaceFromRawData(raw_data_.Pass(),
@@ -96,21 +89,24 @@
 void TypefaceDecoder::ReleaseRawData() { raw_data_.reset(); }
 
 bool TypefaceDecoder::Suspend() {
-  DCHECK(!suspended_);
-  suspended_ = true;
-  is_raw_data_too_large_ = false;
+  DCHECK(!is_suspended_);
+  DCHECK(resource_provider_);
+
+  is_suspended_ = true;
   resource_provider_ = NULL;
+
+  is_raw_data_too_large_ = false;
   ReleaseRawData();
+
   return true;
 }
 
 void TypefaceDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
-  if (!suspended_) {
-    DCHECK_EQ(resource_provider_, resource_provider);
-    return;
-  }
+  DCHECK(is_suspended_);
+  DCHECK(!resource_provider_);
+  DCHECK(resource_provider);
 
-  suspended_ = false;
+  is_suspended_ = false;
   resource_provider_ = resource_provider;
 }
 
diff --git a/src/cobalt/loader/font/typeface_decoder.h b/src/cobalt/loader/font/typeface_decoder.h
index 700e1dd..0824691 100644
--- a/src/cobalt/loader/font/typeface_decoder.h
+++ b/src/cobalt/loader/font/typeface_decoder.h
@@ -56,7 +56,7 @@
   scoped_ptr<render_tree::ResourceProvider::RawTypefaceDataVector> raw_data_;
   bool is_raw_data_too_large_;
 
-  bool suspended_;
+  bool is_suspended_;
 };
 
 }  // namespace font
diff --git a/src/cobalt/loader/image/animated_image_tracker.cc b/src/cobalt/loader/image/animated_image_tracker.cc
new file mode 100644
index 0000000..25280aa
--- /dev/null
+++ b/src/cobalt/loader/image/animated_image_tracker.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/loader/image/animated_image_tracker.h"
+
+#include <algorithm>
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+AnimatedImageTracker::AnimatedImageTracker(
+    base::ThreadPriority animated_image_decode_thread_priority)
+    : animated_image_decode_thread_("AnimatedImage") {
+  base::Thread::Options options(MessageLoop::TYPE_DEFAULT,
+                                0 /* default stack size */,
+                                animated_image_decode_thread_priority);
+  animated_image_decode_thread_.StartWithOptions(options);
+}
+
+AnimatedImageTracker::~AnimatedImageTracker() {
+  animated_image_decode_thread_.Stop();
+}
+
+void AnimatedImageTracker::IncreaseURLCount(const GURL& url) {
+  current_url_counts_[url]++;
+}
+
+void AnimatedImageTracker::DecreaseURLCount(const GURL& url) {
+  DCHECK_GT(current_url_counts_[url], 0);
+  current_url_counts_[url]--;
+}
+
+void AnimatedImageTracker::RecordImage(
+    const GURL& url, loader::image::AnimatedImage* animated_image) {
+  DCHECK(animated_image);
+  image_map_[url] = animated_image;
+}
+
+void AnimatedImageTracker::ProcessRecordedImages() {
+  for (URLToIntMap::iterator it = current_url_counts_.begin();
+       it != current_url_counts_.end();) {
+    const GURL& url = it->first;
+    URLSet::iterator playing_url = playing_urls_.find(url);
+    if (it->second == 0) {
+      // Remove the images that are no longer displayed, and stop the
+      // animations.
+      if (playing_url != playing_urls_.end()) {
+        playing_urls_.erase(playing_url);
+        OnDisplayEnd(image_map_[url]);
+      }
+      image_map_.erase(url);
+      previous_url_counts_.erase(url);
+      current_url_counts_.erase(it++);
+    } else {
+      // Record the images that start to be displayed, and start the
+      // animations.
+      if (playing_url == playing_urls_.end()) {
+        URLToImageMap::iterator playing_image = image_map_.find(url);
+        if (playing_image != image_map_.end()) {
+          playing_urls_.insert(std::make_pair(url, base::Unused()));
+          OnDisplayStart(playing_image->second);
+        }
+      }
+      previous_url_counts_[url] = it->second;
+      ++it;
+    }
+  }
+}
+
+void AnimatedImageTracker::OnDisplayStart(
+    loader::image::AnimatedImage* animated_image) {
+  DCHECK(animated_image);
+  animated_image->Play(animated_image_decode_thread_.message_loop_proxy());
+}
+
+void AnimatedImageTracker::OnDisplayEnd(
+    loader::image::AnimatedImage* animated_image) {
+  DCHECK(animated_image);
+  animated_image->Stop();
+}
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
diff --git a/src/cobalt/loader/image/animated_image_tracker.h b/src/cobalt/loader/image/animated_image_tracker.h
new file mode 100644
index 0000000..6fdb115
--- /dev/null
+++ b/src/cobalt/loader/image/animated_image_tracker.h
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_LOADER_IMAGE_ANIMATED_IMAGE_TRACKER_H_
+#define COBALT_LOADER_IMAGE_ANIMATED_IMAGE_TRACKER_H_
+
+#include <map>
+#include <string>
+
+#include "base/containers/small_map.h"
+#include "base/threading/thread.h"
+#include "cobalt/base/unused.h"
+#include "cobalt/loader/image/image.h"
+#include "googleurl/src/gurl.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+// This class maintains the list of all images that are being displayed
+// at the current time. Whenever an animated image enters / exits the list, the
+// playing status is updated hence decoding is turned on / off for it.
+class AnimatedImageTracker {
+ public:
+  explicit AnimatedImageTracker(
+      base::ThreadPriority animated_image_decode_thread_priority);
+  ~AnimatedImageTracker();
+
+  // Updates the count of an image URL that is being displayed.
+  void IncreaseURLCount(const GURL& url);
+  void DecreaseURLCount(const GURL& url);
+
+  // Record an actual animated image that is linked to its URL.
+  void RecordImage(const GURL& url,
+                   loader::image::AnimatedImage* animated_image);
+
+  // This indicates the update calls for the current frame have finished. All
+  // changes during two ProcessRecordedImages calls will be processed. It should
+  // be called at the end of a layout.
+  void ProcessRecordedImages();
+
+ private:
+  void OnDisplayStart(loader::image::AnimatedImage* image);
+  void OnDisplayEnd(loader::image::AnimatedImage* image);
+
+  typedef std::map<GURL, loader::image::AnimatedImage*> URLToImageMap;
+  typedef std::map<GURL, int> URLToIntMap;
+  typedef base::SmallMap<std::map<GURL, base::Unused>, 1> URLSet;
+
+  URLToImageMap image_map_;
+  URLToIntMap previous_url_counts_;
+  URLToIntMap current_url_counts_;
+  URLSet playing_urls_;
+  base::Thread animated_image_decode_thread_;
+};
+
+}  // namespace image
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // COBALT_LOADER_IMAGE_ANIMATED_IMAGE_TRACKER_H_
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index 25da0b1..a6a9373 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -17,6 +17,12 @@
 #include "cobalt/loader/image/animated_webp_image.h"
 
 #include "cobalt/loader/image/webp_image_decoder.h"
+#include "cobalt/render_tree/brush.h"
+#include "cobalt/render_tree/composition_node.h"
+#include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/render_tree/rect_node.h"
+#include "nb/memory_scope.h"
 #include "starboard/memory.h"
 
 namespace cobalt {
@@ -24,37 +30,59 @@
 namespace image {
 namespace {
 
-const int kPixelSize = 4;
 const int kLoopInfinite = 0;
-
-inline uint32_t DivideBy255(uint32_t value) {
-  return (value + 1 + (value >> 8)) >> 8;
-}
+const int kMinimumDelayInMilliseconds = 10;
 
 }  // namespace
 
 AnimatedWebPImage::AnimatedWebPImage(
     const math::Size& size, bool is_opaque,
+    render_tree::PixelFormat pixel_format,
     render_tree::ResourceProvider* resource_provider)
-    : thread_("AnimatedWebPImage Decoding"),
-      size_(size),
+    : size_(size),
       is_opaque_(is_opaque),
+      pixel_format_(pixel_format),
       demux_(NULL),
       demux_state_(WEBP_DEMUX_PARSING_HEADER),
+      received_first_frame_(false),
+      is_playing_(false),
       frame_count_(0),
       loop_count_(kLoopInfinite),
-      background_color_(0),
-      should_dispose_previous_frame_(false),
       current_frame_index_(0),
       next_frame_index_(0),
+      should_dispose_previous_frame_to_background_(false),
       resource_provider_(resource_provider) {}
 
 scoped_refptr<render_tree::Image> AnimatedWebPImage::GetFrame() {
   base::AutoLock lock(lock_);
-  return current_frame_image_;
+  return current_canvas_;
+}
+
+void AnimatedWebPImage::Play(
+    const scoped_refptr<base::MessageLoopProxy>& message_loop) {
+  base::AutoLock lock(lock_);
+
+  if (is_playing_) {
+    return;
+  }
+  is_playing_ = true;
+
+  message_loop_ = message_loop;
+  if (received_first_frame_) {
+    PlayInternal();
+  }
+}
+
+void AnimatedWebPImage::Stop() {
+  if (!decode_closure_.callback().is_null()) {
+    message_loop_->PostTask(
+        FROM_HERE,
+        base::Bind(&AnimatedWebPImage::StopInternal, base::Unretained(this)));
+  }
 }
 
 void AnimatedWebPImage::AppendChunk(const uint8* data, size_t input_byte) {
+  TRACK_MEMORY_SCOPE("Rendering");
   base::AutoLock lock(lock_);
 
   data_buffer_.insert(data_buffer_.end(), data, data + input_byte);
@@ -66,131 +94,168 @@
   // Update frame count.
   int new_frame_count = WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT);
   if (new_frame_count > 0 && frame_count_ == 0) {
-    // We've just received the first frame, start playing animation now.
+    // We've just received the first frame.
 
+    received_first_frame_ = true;
     loop_count_ = WebPDemuxGetI(demux_, WEBP_FF_LOOP_COUNT);
-    background_color_ = WebPDemuxGetI(demux_, WEBP_FF_BACKGROUND_COLOR);
-    if (size_.width() > 0 && size_.height() > 0) {
-      image_buffer_.resize(size_.width() * size_.height() * kPixelSize);
-      FillImageBufferWithBackgroundColor();
 
-      current_frame_time_ = base::TimeTicks::Now();
-      current_frame_index_ = 0;
-      thread_.Start();
-      thread_.message_loop()->PostTask(
-          FROM_HERE,
-          base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+    // The default background color of the canvas in [Blue, Green, Red, Alpha]
+    // byte order. It is read in little endian order as an 32bit int.
+    uint32_t background_color = WebPDemuxGetI(demux_, WEBP_FF_BACKGROUND_COLOR);
+    background_color_ =
+        render_tree::ColorRGBA((background_color >> 16 & 0xff) / 255.0f,
+                               (background_color >> 8 & 0xff) / 255.0f,
+                               (background_color & 0xff) / 255.0f,
+                               (background_color >> 24 & 0xff) / 255.0f);
+
+    if (is_playing_) {
+      PlayInternal();
     }
   }
   frame_count_ = new_frame_count;
 }
 
-void AnimatedWebPImage::DecodeFrames() {
-  TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeFrames()");
+AnimatedWebPImage::~AnimatedWebPImage() {
+  Stop();
   base::AutoLock lock(lock_);
+  WebPDemuxDelete(demux_);
+}
+
+void AnimatedWebPImage::StopInternal() {
+  is_playing_ = false;
+  decode_closure_.Cancel();
+}
+
+void AnimatedWebPImage::PlayInternal() {
+  current_frame_time_ = base::TimeTicks::Now();
+  DCHECK(message_loop_);
+  message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+}
+
+void AnimatedWebPImage::DecodeFrames() {
+  TRACK_MEMORY_SCOPE("Rendering");
+  TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeFrames()");
+  DCHECK(is_playing_ && received_first_frame_);
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  if (decode_closure_.callback().is_null()) {
+    decode_closure_.Reset(
+        base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+  }
 
   UpdateTimelineInfo();
 
   // Decode the frames from current frame to next frame and blend the results.
   for (int frame_index = current_frame_index_ + 1;
        frame_index <= next_frame_index_; ++frame_index) {
-    // Dispose previous frame if necessary.
-    if (should_dispose_previous_frame_) {
-      FillImageBufferWithBackgroundColor();
-    }
-
-    // Decode the current frame.
-    WebPIterator webp_iterator;
-    WebPDemuxGetFrame(demux_, frame_index, &webp_iterator);
-    if (!webp_iterator.complete) {
+    if (!DecodeOneFrame(frame_index)) {
       break;
     }
-    WEBPImageDecoder webp_image_decoder(resource_provider_);
+  }
+  current_frame_index_ = next_frame_index_;
+
+  // Set up the next time to call the decode callback.
+  base::AutoLock lock(lock_);
+  if (is_playing_) {
+    base::TimeDelta delay = next_frame_time_ - base::TimeTicks::Now();
+    const base::TimeDelta min_delay =
+        base::TimeDelta::FromMilliseconds(kMinimumDelayInMilliseconds);
+    if (delay < min_delay) {
+      delay = min_delay;
+    }
+    message_loop_->PostDelayedTask(FROM_HERE, decode_closure_.callback(),
+                                   delay);
+  }
+}
+
+bool AnimatedWebPImage::DecodeOneFrame(int frame_index) {
+  TRACK_MEMORY_SCOPE("Rendering");
+  base::AutoLock lock(lock_);
+  WebPIterator webp_iterator;
+  WEBPImageDecoder webp_image_decoder(resource_provider_);
+  scoped_refptr<render_tree::Image> next_frame_image;
+
+  // Decode the current frame.
+  {
+    TRACE_EVENT0("cobalt::loader::image", "Decoding");
+
+    WebPDemuxGetFrame(demux_, frame_index, &webp_iterator);
+    if (!webp_iterator.complete) {
+      return false;
+    }
     webp_image_decoder.DecodeChunk(webp_iterator.fragment.bytes,
                                    webp_iterator.fragment.size);
     if (!webp_image_decoder.FinishWithSuccess()) {
       LOG(ERROR) << "Failed to decode WebP image frame.";
-      break;
+      return false;
     }
 
-    // Alpha blend the current frame on top of previous frame.
-    {
-      TRACE_EVENT0("cobalt::loader::image", "Blending");
-      DCHECK(!image_buffer_.empty());
-      uint8_t* image_buffer_memory = &image_buffer_[0];
-      uint8_t* next_frame_memory = webp_image_decoder.GetOriginalMemory();
-      // Alpha-blending: Given that each of the R, G, B and A channels is 8-bit,
-      // and the RGB channels are not premultiplied by alpha, the formula for
-      // blending 'dst' onto 'src' is:
-      // blend.A = src.A + dst.A * (1 - src.A / 255)
-      // if blend.A = 0 then
-      //   blend.RGB = 0
-      // else
-      //   blend.RGB = (src.RGB * src.A +
-      //                dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
-      //   https://developers.google.com/speed/webp/docs/riff_container#animation
-      for (int y = 0; y < webp_iterator.height; ++y) {
-        uint8_t* src =
-            next_frame_memory + (y * webp_iterator.width) * kPixelSize;
-        uint8_t* dst = image_buffer_memory +
-                       ((y + webp_iterator.y_offset) * size_.width() +
-                        webp_iterator.x_offset) *
-                           kPixelSize;
-        for (int x = 0; x < webp_iterator.width; ++x) {
-          uint32_t dst_factor = dst[3] * (255 - src[3]);
-          dst[3] = static_cast<uint8_t>(src[3] + DivideBy255(dst_factor));
-          if (dst[3] == 0) {
-            dst[0] = dst[1] = dst[2] = 0;
-          } else {
-            for (int i = 0; i < 3; ++i) {
-              dst[i] = static_cast<uint8_t>(
-                  (src[i] * src[3] + DivideBy255(dst[i] * dst_factor)) /
-                  dst[3]);
-            }
-          }
-
-          src += kPixelSize;
-          dst += kPixelSize;
-        }
-      }
-    }
-
-    // Record the dispose method for current frame.
-    should_dispose_previous_frame_ =
-        webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND;
-    WebPDemuxReleaseIterator(&webp_iterator);
-  }
-
-  // Generate the next frame render tree image if necessary.
-  if (current_frame_index_ != next_frame_index_) {
-    current_frame_index_ = next_frame_index_;
-    scoped_ptr<render_tree::ImageData> next_frame_data = AllocateImageData();
+    scoped_ptr<render_tree::ImageData> next_frame_data = AllocateImageData(
+        math::Size(webp_iterator.width, webp_iterator.height));
     DCHECK(next_frame_data);
-    DCHECK(!image_buffer_.empty());
-    uint8_t* image_buffer_memory = &image_buffer_[0];
-    uint8_t* next_frame_memory = next_frame_data->GetMemory();
-    SbMemoryCopy(next_frame_memory, image_buffer_memory,
-                 size_.width() * size_.height() * kPixelSize);
-    current_frame_image_ =
-        resource_provider_->CreateImage(next_frame_data.Pass());
+    {
+      TRACE_EVENT0("cobalt::loader::image", "SbMemoryCopy");
+      SbMemoryCopy(next_frame_data->GetMemory(),
+                   webp_image_decoder.GetOriginalMemory(),
+                   webp_iterator.width * webp_iterator.height *
+                       render_tree::BytesPerPixel(pixel_format_));
+    }
+    next_frame_image = resource_provider_->CreateImage(next_frame_data.Pass());
   }
 
-  // Set up the next time to call the decode callback.
-  base::TimeDelta delay = next_frame_time_ - base::TimeTicks::Now();
-  if (delay < base::TimeDelta()) {
-    delay = base::TimeDelta();
-  }
-  thread_.message_loop()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)),
-      delay);
+  // Alpha blend the current frame on top of the buffer.
+  {
+    TRACE_EVENT0("cobalt::loader::image", "Blending");
 
-  if (!decoded_callback_.is_null()) {
-    decoded_callback_.Run();
+    render_tree::CompositionNode::Builder builder;
+    // Add the current canvas or, if there is not one, a background color
+    // rectangle;
+    if (current_canvas_) {
+      builder.AddChild(new render_tree::ImageNode(current_canvas_));
+    } else {
+      scoped_ptr<render_tree::Brush> brush(
+          new render_tree::SolidColorBrush(background_color_));
+      builder.AddChild(
+          new render_tree::RectNode(math::RectF(size_), brush.Pass()));
+    }
+    // Dispose previous frame by adding a solid rectangle.
+    if (should_dispose_previous_frame_to_background_) {
+      scoped_ptr<render_tree::Brush> brush(
+          new render_tree::SolidColorBrush(background_color_));
+      builder.AddChild(
+          new render_tree::RectNode(previous_frame_rect_, brush.Pass()));
+    }
+    // Add the current frame.
+    builder.AddChild(new render_tree::ImageNode(
+        next_frame_image,
+        math::Vector2dF(webp_iterator.x_offset, webp_iterator.y_offset)));
+
+    scoped_refptr<render_tree::Node> root =
+        new render_tree::CompositionNode(builder);
+
+    current_canvas_ = resource_provider_->DrawOffscreenImage(root);
   }
+
+  if (webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
+    should_dispose_previous_frame_to_background_ = true;
+    previous_frame_rect_ =
+        math::RectF(webp_iterator.x_offset, webp_iterator.y_offset,
+                    webp_iterator.width, webp_iterator.height);
+  } else if (webp_iterator.dispose_method == WEBP_MUX_DISPOSE_NONE) {
+    should_dispose_previous_frame_to_background_ = false;
+  } else {
+    NOTREACHED();
+  }
+
+  WebPDemuxReleaseIterator(&webp_iterator);
+  return true;
 }
 
 void AnimatedWebPImage::UpdateTimelineInfo() {
+  TRACK_MEMORY_SCOPE("Rendering");
+  base::AutoLock lock(lock_);
   base::TimeTicks current_time = base::TimeTicks::Now();
   next_frame_index_ = current_frame_index_ ? current_frame_index_ : 1;
   while (true) {
@@ -224,29 +289,12 @@
   }
 }
 
-void AnimatedWebPImage::FillImageBufferWithBackgroundColor() {
-  for (int y = 0; y < size_.height(); ++y) {
-    for (int x = 0; x < size_.width(); ++x) {
-      *reinterpret_cast<uint32_t*>(
-          &image_buffer_[(y * size_.width() + x) * kPixelSize]) =
-          background_color_;
-    }
-  }
-}
-
-AnimatedWebPImage::~AnimatedWebPImage() {
-  WebPDemuxDelete(demux_);
-  thread_.Stop();
-}
-
-scoped_ptr<render_tree::ImageData> AnimatedWebPImage::AllocateImageData() {
-  render_tree::PixelFormat pixel_format = render_tree::kPixelFormatRGBA8;
-  if (!resource_provider_->PixelFormatSupported(pixel_format)) {
-    pixel_format = render_tree::kPixelFormatBGRA8;
-  }
+scoped_ptr<render_tree::ImageData> AnimatedWebPImage::AllocateImageData(
+    const math::Size& size) {
+  TRACK_MEMORY_SCOPE("Rendering");
   scoped_ptr<render_tree::ImageData> image_data =
       resource_provider_->AllocateImageData(
-          size_, pixel_format, render_tree::kAlphaFormatPremultiplied);
+          size, pixel_format_, render_tree::kAlphaFormatPremultiplied);
   DCHECK(image_data) << "Failed to allocate image.";
   return image_data.Pass();
 }
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index 3f93216..e988519 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -20,13 +20,15 @@
 #include <vector>
 
 #include "base/basictypes.h"
-#include "base/callback.h"
+#include "base/cancelable_callback.h"
 #include "base/debug/trace_event.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
 #include "base/time.h"
 #include "cobalt/loader/image/image.h"
+#include "cobalt/render_tree/color_rgba.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "third_party/libwebp/webp/demux.h"
 
@@ -37,6 +39,7 @@
 class AnimatedWebPImage : public AnimatedImage {
  public:
   AnimatedWebPImage(const math::Size& size, bool is_opaque,
+                    render_tree::PixelFormat pixel_format,
                     render_tree::ResourceProvider* resource_provider);
 
   const math::Size& GetSize() const OVERRIDE { return size_; }
@@ -49,47 +52,56 @@
 
   scoped_refptr<render_tree::Image> GetFrame() OVERRIDE;
 
-  void AppendChunk(const uint8* data, size_t input_byte);
+  void Play(const scoped_refptr<base::MessageLoopProxy>& message_loop) OVERRIDE;
 
-  void SetDecodedFramesCallback(const base::Closure& callback) {
-    decoded_callback_ = callback;
-  }
+  void Stop() OVERRIDE;
+
+  void AppendChunk(const uint8* data, size_t input_byte);
 
  private:
   ~AnimatedWebPImage() OVERRIDE;
 
+  // To be called the decoding thread, to cancel future decodings.
+  void StopInternal();
+
+  void PlayInternal();
+
+  // Decodes all frames until current time.
   void DecodeFrames();
 
+  // Decodes the frame with the given index, returns if it succeeded.
+  bool DecodeOneFrame(int frame_index);
+
   // Updates the index and time info of the current and next frames.
   void UpdateTimelineInfo();
 
-  void FillImageBufferWithBackgroundColor();
+  scoped_ptr<render_tree::ImageData> AllocateImageData(const math::Size& size);
 
-  scoped_ptr<render_tree::ImageData> AllocateImageData();
-
-  base::Thread thread_;
   const math::Size size_;
   const bool is_opaque_;
+  const render_tree::PixelFormat pixel_format_;
   WebPDemuxer* demux_;
   WebPDemuxState demux_state_;
+  bool received_first_frame_;
+  bool is_playing_;
   int frame_count_;
   // The remaining number of times to loop the animation. kLoopInfinite means
   // looping infinitely.
   int loop_count_;
-  uint32_t background_color_;
-  bool should_dispose_previous_frame_;
   int current_frame_index_;
   int next_frame_index_;
+  bool should_dispose_previous_frame_to_background_;
   render_tree::ResourceProvider* resource_provider_;
+  scoped_refptr<base::MessageLoopProxy> message_loop_;
 
+  render_tree::ColorRGBA background_color_;
+  math::RectF previous_frame_rect_;
+  base::CancelableClosure decode_closure_;
   base::TimeTicks current_frame_time_;
   base::TimeTicks next_frame_time_;
   // The original encoded data.
   std::vector<uint8> data_buffer_;
-  // The alpha-blended frame.
-  std::vector<uint8> image_buffer_;
-  scoped_refptr<render_tree::Image> current_frame_image_;
-  base::Closure decoded_callback_;
+  scoped_refptr<render_tree::Image> current_canvas_;
   base::Lock lock_;
 };
 
diff --git a/src/cobalt/loader/image/image.h b/src/cobalt/loader/image/image.h
index 2330a45..feb1bd3 100644
--- a/src/cobalt/loader/image/image.h
+++ b/src/cobalt/loader/image/image.h
@@ -18,6 +18,7 @@
 #define COBALT_LOADER_IMAGE_IMAGE_H_
 
 #include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
 #include "base/time.h"
 #include "cobalt/math/size_f.h"
 #include "cobalt/math/transform_2d.h"
@@ -83,6 +84,14 @@
   // Get the current frame. Implementation should be thread safe.
   virtual scoped_refptr<render_tree::Image> GetFrame() = 0;
 
+  // Start playing the animation, decoding on the given message loop.
+  // Implementation should be thread safe.
+  virtual void Play(
+      const scoped_refptr<base::MessageLoopProxy>& message_loop) = 0;
+
+  // Stop playing the animation.
+  virtual void Stop() = 0;
+
   // This callback is intended to be used in a render_tree::AnimateNode.
   void AnimateCallback(const math::RectF& destination_rect,
                        const math::Matrix3F& local_transform,
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 9c2b351..831a574 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -19,11 +19,11 @@
 #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__)
+#if defined(USE_PS3_JPEG_IMAGE_DECODER)
 #include "cobalt/loader/image/jpeg_image_decoder_ps3.h"
-#else  // defined(__LB_PS3__)
+#else  // defined(USE_PS3_JPEG_IMAGE_DECODER)
 #include "cobalt/loader/image/jpeg_image_decoder.h"
-#endif  // defined(__LB_PS3__)
+#endif  // defined(USE_PS3_JPEG_IMAGE_DECODER)
 #include "cobalt/loader/image/png_image_decoder.h"
 #include "cobalt/loader/image/stub_image_decoder.h"
 #include "cobalt/loader/image/webp_image_decoder.h"
@@ -83,14 +83,10 @@
     : resource_provider_(resource_provider),
       success_callback_(success_callback),
       error_callback_(error_callback),
-      state_(kWaitingForHeader),
+      state_(resource_provider_ ? kWaitingForHeader : kSuspended),
       is_deletion_pending_(false) {
   TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::ImageDecoder()");
   signature_cache_.position = 0;
-
-  if (!resource_provider_) {
-    state_ = kNoResourceProvider;
-  }
 }
 
 LoadResponseType ImageDecoder::OnResponseStarted(
@@ -197,9 +193,6 @@
     case kUnsupportedImageFormat:
       error_callback_.Run("Unsupported image format.");
       break;
-    case kNoResourceProvider:
-      error_callback_.Run("No resource provider was passed to the decoder.");
-      break;
     case kSuspended:
       DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
       break;
@@ -212,6 +205,9 @@
 
 bool ImageDecoder::Suspend() {
   TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::Suspend()");
+  DCHECK_NE(state_, kSuspended);
+  DCHECK(resource_provider_);
+
   if (state_ == kDecoding) {
     DCHECK(decoder_ || base::subtle::Acquire_Load(&is_deletion_pending_));
     decoder_.reset();
@@ -224,17 +220,12 @@
 
 void ImageDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
   TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::Resume()");
-  if (state_ != kSuspended) {
-    DCHECK_EQ(resource_provider_, resource_provider);
-    return;
-  }
+  DCHECK_EQ(state_, kSuspended);
+  DCHECK(!resource_provider_);
+  DCHECK(resource_provider);
 
   state_ = kWaitingForHeader;
   resource_provider_ = resource_provider;
-
-  if (!resource_provider_) {
-    state_ = kNoResourceProvider;
-  }
 }
 
 void ImageDecoder::SetDeletionPending() {
@@ -268,7 +259,6 @@
     case kNotApplicable:
     case kUnsupportedImageFormat:
     case kSuspended:
-    case kNoResourceProvider:
     default: {
       // Do not attempt to continue processing data.
       DCHECK(!decoder_);
@@ -347,13 +337,13 @@
     return make_scoped_ptr<ImageDataDecoder>(
         new StubImageDecoder(resource_provider));
   } else if (image_type == kImageTypeJPEG) {
-#if defined(__LB_PS3__)
+#if defined(USE_PS3_JPEG_IMAGE_DECODER)
     return make_scoped_ptr<ImageDataDecoder>(
         new JPEGImageDecoderPS3(resource_provider));
-#else   // defined(__LB_PS3__)
+#else   // defined(USE_PS3_JPEG_IMAGE_DECODER)
     return make_scoped_ptr<ImageDataDecoder>(
         new JPEGImageDecoder(resource_provider));
-#endif  // defined(__LB_PS3__)
+#endif  // defined(USE_PS3_JPEG_IMAGE_DECODER)
   } else if (image_type == kImageTypePNG) {
     return make_scoped_ptr<ImageDataDecoder>(
         new PNGImageDecoder(resource_provider));
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h
index c4b03a3..2127a07 100644
--- a/src/cobalt/loader/image/image_decoder.h
+++ b/src/cobalt/loader/image/image_decoder.h
@@ -66,7 +66,6 @@
     kDecoding,
     kNotApplicable,
     kUnsupportedImageFormat,
-    kNoResourceProvider,
     kSuspended,
   };
 
@@ -90,6 +89,7 @@
   State state_;
   std::string error_message_;
   std::string mime_type_;
+
   // Whether or not there is a pending task deleting this decoder on a message
   // loop.
   base::subtle::Atomic32 is_deletion_pending_;
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc
index 34b5e58..a0518c0 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.cc
+++ b/src/cobalt/loader/image/image_decoder_starboard.cc
@@ -23,7 +23,7 @@
 #include "starboard/decode_target.h"
 #include "starboard/image.h"
 
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 
 namespace cobalt {
 namespace loader {
@@ -35,7 +35,11 @@
     : ImageDataDecoder(resource_provider),
       mime_type_(mime_type),
       format_(format),
+#if SB_API_VERSION >= 4
+      provider_(resource_provider->GetSbDecodeTargetGraphicsContextProvider()),
+#else   // #if SB_API_VERSION >= 4
       provider_(resource_provider->GetSbDecodeTargetProvider()),
+#endif  // #if SB_API_VERSION >= 4
       target_(kSbDecodeTargetInvalid) {
   TRACE_EVENT0("cobalt::loader::image",
                "ImageDecoderStarboard::ImageDecoderStarboard()");
@@ -71,6 +75,6 @@
 }  // namespace loader
 }  // namespace cobalt
 
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // SB_API_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
index 0132a1b..2cc98ef 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.h
+++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -51,7 +51,11 @@
   const char* mime_type_;
   SbDecodeTargetFormat format_;
   std::vector<uint8> buffer_;
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider* provider_;
+#else   // #if SB_API_VERSION >= 4
   SbDecodeTargetProvider* provider_;
+#endif  // #if SB_API_VERSION >= 4
   SbDecodeTarget target_;
 };
 
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc
index 7466799b..7f995fc 100644
--- a/src/cobalt/loader/image/image_decoder_test.cc
+++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -21,7 +21,7 @@
 #include "base/file_path.h"
 #include "base/file_util.h"
 #include "base/path_service.h"
-#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/loader/image/animated_webp_image.h"
@@ -652,8 +652,6 @@
       CheckSameColor(pixels, size.width(), size.height(), expected_color));
 }
 
-void SignalWaitable(base::WaitableEvent* event) { event->Signal(); }
-
 // Test that we can properly decode animated WEBP image.
 TEST(ImageDecoderTest, DecodeAnimatedWEBPImage) {
   MockImageDecoder image_decoder;
@@ -669,10 +667,11 @@
           image_decoder.image().get());
   ASSERT_TRUE(animated_webp_image);
 
-  base::WaitableEvent decoded_frames_event(true, false);
-  animated_webp_image->SetDecodedFramesCallback(
-      base::Bind(&SignalWaitable, &decoded_frames_event));
-  decoded_frames_event.Wait();
+  base::Thread thread("AnimatedWebP test");
+  thread.Start();
+  animated_webp_image->Play(thread.message_loop_proxy());
+  animated_webp_image->Stop();
+  thread.Stop();
 
   // The image should contain the whole undecoded data from the file.
   EXPECT_EQ(3224674u, animated_webp_image->GetEstimatedSizeInBytes());
@@ -699,10 +698,11 @@
           image_decoder.image().get());
   ASSERT_TRUE(animated_webp_image);
 
-  base::WaitableEvent decoded_frames_event(true, false);
-  animated_webp_image->SetDecodedFramesCallback(
-      base::Bind(&SignalWaitable, &decoded_frames_event));
-  decoded_frames_event.Wait();
+  base::Thread thread("AnimatedWebP test");
+  thread.Start();
+  animated_webp_image->Play(thread.message_loop_proxy());
+  animated_webp_image->Stop();
+  thread.Stop();
 
   // The image should contain the whole undecoded data from the file.
   EXPECT_EQ(3224674u, animated_webp_image->GetEstimatedSizeInBytes());
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.cc b/src/cobalt/loader/image/jpeg_image_decoder.cc
index 274a133..10ca062 100644
--- a/src/cobalt/loader/image/jpeg_image_decoder.cc
+++ b/src/cobalt/loader/image/jpeg_image_decoder.cc
@@ -18,6 +18,7 @@
 
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
+#include "nb/memory_scope.h"
 #include "third_party/libjpeg/jpegint.h"
 
 namespace cobalt {
@@ -83,6 +84,7 @@
     render_tree::ResourceProvider* resource_provider)
     : ImageDataDecoder(resource_provider) {
   TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::JPEGImageDecoder()");
+  TRACK_MEMORY_SCOPE("Rendering");
   memset(&info_, 0, sizeof(info_));
   memset(&source_manager_, 0, sizeof(source_manager_));
 
@@ -110,6 +112,7 @@
 
 size_t JPEGImageDecoder::DecodeChunkInternal(const uint8* data,
                                              size_t input_byte) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image",
                "JPEGImageDecoder::DecodeChunkInternal()");
   // |client_data| is available for use by application.
@@ -176,6 +179,7 @@
 }
 
 bool JPEGImageDecoder::ReadHeader() {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::ReadHeader()");
   if (jpeg_read_header(&info_, true) == JPEG_SUSPENDED) {
     // Since |jpeg_read_header| doesn't have enough data, go back to the state
@@ -194,6 +198,7 @@
 }
 
 bool JPEGImageDecoder::StartDecompress() {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::StartDecompress()");
   // jpeg_has_multiple_scans() returns TRUE if the incoming image file has more
   // than one scan.
@@ -221,6 +226,7 @@
 // TODO: support displaying the low resolution image while decoding
 // the progressive JPEG.
 bool JPEGImageDecoder::DecodeProgressiveJPEG() {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image",
                "JPEGImageDecoder::DecodeProgressiveJPEG()");
   int status;
@@ -299,6 +305,8 @@
 }
 
 bool JPEGImageDecoder::ReadLines() {
+  TRACK_MEMORY_SCOPE("Rendering");
+
   TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::ReadLines()");
 
   // Creation of 2-D sample arrays which is for one row.
diff --git a/src/cobalt/loader/image/png_image_decoder.cc b/src/cobalt/loader/image/png_image_decoder.cc
index 75921cc..9e9f602 100644
--- a/src/cobalt/loader/image/png_image_decoder.cc
+++ b/src/cobalt/loader/image/png_image_decoder.cc
@@ -16,6 +16,7 @@
 
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
+#include "nb/memory_scope.h"
 
 namespace cobalt {
 namespace loader {
@@ -71,6 +72,7 @@
       info_(NULL),
       has_alpha_(false),
       interlace_buffer_(0) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image", "PNGImageDecoder::PNGImageDecoder()");
 
   png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, DecodingFailed,
@@ -81,6 +83,7 @@
 }
 
 size_t PNGImageDecoder::DecodeChunkInternal(const uint8* data, size_t size) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image",
                "PNGImageDecoder::DecodeChunkInternal()");
   // int setjmp(jmp_buf env) saves the current environment (ths program state),
@@ -130,6 +133,7 @@
 // Called when we have obtained the header information (including the size).
 // static
 void PNGImageDecoder::HeaderAvailable(png_structp png, png_infop info) {
+  TRACK_MEMORY_SCOPE("Rendering");
   UNREFERENCED_PARAMETER(info);
   TRACE_EVENT0("cobalt::loader::image", "PNGImageDecoder::~PNGImageDecoder()");
   PNGImageDecoder* decoder =
@@ -150,6 +154,7 @@
 // Called when decoding is done.
 // static
 void PNGImageDecoder::DecodeDone(png_structp png, png_infop info) {
+  TRACK_MEMORY_SCOPE("Rendering");
   UNREFERENCED_PARAMETER(info);
   TRACE_EVENT0("cobalt::loader::image", "PNGImageDecoder::DecodeDone()");
 
@@ -159,6 +164,7 @@
 }
 
 void PNGImageDecoder::HeaderAvailableCallback() {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image",
                "PNGImageDecoder::HeaderAvailableCallback()");
   DCHECK_EQ(state(), kWaitingForHeader);
@@ -278,6 +284,7 @@
 // from the previous pass.
 void PNGImageDecoder::RowAvailableCallback(png_bytep row_buffer,
                                            png_uint_32 row_index) {
+  TRACK_MEMORY_SCOPE("Rendering");
   DCHECK_EQ(state(), kReadLines);
 
   // Nothing to do if the row is unchanged, or the row is outside
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index f2ae873..74afb79 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -17,6 +17,7 @@
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "cobalt/loader/image/animated_webp_image.h"
+#include "nb/memory_scope.h"
 #include "starboard/memory.h"
 
 namespace cobalt {
@@ -28,6 +29,7 @@
     : ImageDataDecoder(resource_provider),
       internal_decoder_(NULL),
       has_animation_(false) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::WEBPImageDecoder()");
   // Initialize the configuration as empty.
   WebPInitDecoderConfig(&config_);
@@ -53,6 +55,7 @@
 
 size_t WEBPImageDecoder::DecodeChunkInternal(const uint8* data,
                                              size_t input_byte) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image",
                "WEBPImageDecoder::DecodeChunkInternal()");
   if (state() == kWaitingForHeader) {
@@ -73,7 +76,7 @@
       has_animation_ = true;
       animated_webp_image_ = new AnimatedWebPImage(
           math::Size(config_.input.width, config_.input.height),
-          !!config_.input.has_alpha, resource_provider());
+          !!config_.input.has_alpha, pixel_format(), resource_provider());
     }
     set_state(kReadLines);
   }
@@ -110,6 +113,7 @@
 }
 
 bool WEBPImageDecoder::ReadHeader(const uint8* data, size_t size) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::ReadHeader()");
   // Retrieve features from the bitstream. The *features structure is filled
   // with information gathered from the bitstream.
@@ -130,9 +134,12 @@
 }
 
 bool WEBPImageDecoder::CreateInternalDecoder(bool has_alpha) {
+  TRACK_MEMORY_SCOPE("Rendering");
   TRACE_EVENT0("cobalt::loader::image",
                "WEBPImageDecoder::CreateInternalDecoder()");
-  config_.output.colorspace = has_alpha ? MODE_rgbA : MODE_RGBA;
+  config_.output.colorspace = pixel_format() == render_tree::kPixelFormatRGBA8
+                                  ? (has_alpha ? MODE_rgbA : MODE_RGBA)
+                                  : (has_alpha ? MODE_bgrA : MODE_BGRA);
   config_.output.u.RGBA.stride = image_data()->GetDescriptor().pitch_in_bytes;
   config_.output.u.RGBA.size =
       static_cast<size_t>(config_.output.u.RGBA.stride *
diff --git a/src/cobalt/loader/loader.cc b/src/cobalt/loader/loader.cc
index 514f48e..d5e453d 100644
--- a/src/cobalt/loader/loader.cc
+++ b/src/cobalt/loader/loader.cc
@@ -74,41 +74,36 @@
 
 Loader::Loader(const FetcherCreator& fetcher_creator,
                scoped_ptr<Decoder> decoder, const OnErrorFunction& on_error,
-               const OnDestructionFunction& on_destruction)
+               const OnDestructionFunction& on_destruction, bool is_suspended)
     : fetcher_creator_(fetcher_creator),
       decoder_(decoder.Pass()),
       on_error_(on_error),
       on_destruction_(on_destruction),
-      suspended_(false) {
+      is_suspended_(is_suspended) {
+  DCHECK(!fetcher_creator_.is_null());
   DCHECK(decoder_);
   DCHECK(!on_error.is_null());
 
-  Start();
-
-  // Post the error callback on the current message loop in case loader is
-  // destroyed in the callback.
-  if (!fetcher_) {
-    fetcher_creator_error_closure_.Reset(
-        base::Bind(on_error, "Fetcher was not created."));
-    MessageLoop::current()->PostTask(FROM_HERE,
-                                     fetcher_creator_error_closure_.callback());
+  if (!is_suspended_) {
+    Start();
   }
 }
 
 Loader::~Loader() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
   if (!on_destruction_.is_null()) {
     on_destruction_.Run(this);
   }
 
-  DCHECK(thread_checker_.CalledOnValidThread());
   fetcher_creator_error_closure_.Cancel();
 }
 
 void Loader::Suspend() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (suspended_) {
-    return;
-  }
+  DCHECK(!is_suspended_);
+
+  is_suspended_ = true;
 
   bool suspendable = decoder_->Suspend();
   if (fetcher_) {
@@ -116,9 +111,7 @@
   }
 
   fetcher_to_decoder_adaptor_.reset();
-
   fetcher_creator_error_closure_.Cancel();
-  suspended_ = true;
 
   if (!suspendable) {
     on_error_.Run("Aborted.");
@@ -127,21 +120,30 @@
 
 void Loader::Resume(render_tree::ResourceProvider* resource_provider) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!suspended_) {
-    return;
-  }
-  suspended_ = false;
+  DCHECK(is_suspended_);
+
+  is_suspended_ = false;
+
   decoder_->Resume(resource_provider);
   Start();
 }
 
 void Loader::Start() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(decoder_);
-  DCHECK(!fetcher_creator_.is_null());
+  DCHECK(!is_suspended_);
+
   fetcher_to_decoder_adaptor_.reset(
       new FetcherToDecoderAdapter(decoder_.get(), on_error_));
   fetcher_ = fetcher_creator_.Run(fetcher_to_decoder_adaptor_.get());
+
+  // Post the error callback on the current message loop in case the loader is
+  // destroyed in the callback.
+  if (!fetcher_) {
+    fetcher_creator_error_closure_.Reset(
+        base::Bind(on_error_, "Fetcher was not created."));
+    MessageLoop::current()->PostTask(FROM_HERE,
+                                     fetcher_creator_error_closure_.callback());
+  }
 }
 
 }  // namespace loader
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 5b029f9..0c9edff 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -47,6 +47,8 @@
         'image/image_decoder.h',
         'image/image_decoder_starboard.cc',
         'image/image_decoder_starboard.h',
+        'image/animated_image_tracker.cc',
+        'image/animated_image_tracker.h',
         'image/jpeg_image_decoder.cc',
         'image/jpeg_image_decoder.h',
         'image/png_image_decoder.cc',
diff --git a/src/cobalt/loader/loader.h b/src/cobalt/loader/loader.h
index 60afa5f..60c8b3f 100644
--- a/src/cobalt/loader/loader.h
+++ b/src/cobalt/loader/loader.h
@@ -46,7 +46,8 @@
   // It is allowed to destroy the loader in the error callback.
   Loader(const FetcherCreator& fetcher_creator, scoped_ptr<Decoder> decoder,
          const OnErrorFunction& on_error,
-         const OnDestructionFunction& on_destruction = OnDestructionFunction());
+         const OnDestructionFunction& on_destruction = OnDestructionFunction(),
+         bool is_suspended = false);
 
   ~Loader();
 
@@ -75,7 +76,7 @@
   OnErrorFunction on_error_;
   OnDestructionFunction on_destruction_;
 
-  bool suspended_;
+  bool is_suspended_;
 
   DISALLOW_COPY_AND_ASSIGN(Loader);
 };
diff --git a/src/cobalt/loader/loader_factory.cc b/src/cobalt/loader/loader_factory.cc
index 0561dac..b2794a5 100644
--- a/src/cobalt/loader/loader_factory.cc
+++ b/src/cobalt/loader/loader_factory.cc
@@ -34,7 +34,8 @@
                              base::ThreadPriority loader_thread_priority)
     : fetcher_factory_(fetcher_factory),
       resource_provider_(resource_provider),
-      load_thread_("ResourceLoader") {
+      load_thread_("ResourceLoader"),
+      is_suspended_(false) {
   base::Thread::Options options(MessageLoop::TYPE_DEFAULT, kLoadThreadStackSize,
                                 loader_thread_priority);
   load_thread_.StartWithOptions(options);
@@ -52,7 +53,8 @@
           resource_provider_, success_callback, error_callback,
           load_thread_.message_loop())),
       error_callback,
-      base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
+      base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+      is_suspended_));
   OnLoaderCreated(loader.get());
   return loader.Pass();
 }
@@ -68,7 +70,8 @@
       scoped_ptr<Decoder>(new font::TypefaceDecoder(
           resource_provider_, success_callback, error_callback)),
       error_callback,
-      base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
+      base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+      is_suspended_));
   OnLoaderCreated(loader.get());
   return loader.Pass();
 }
@@ -85,7 +88,8 @@
       scoped_ptr<Decoder>(new mesh::MeshDecoder(
           resource_provider_, success_callback, error_callback)),
       error_callback,
-      base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
+      base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+      is_suspended_));
   OnLoaderCreated(loader.get());
   return loader.Pass();
 }
@@ -102,8 +106,11 @@
 void LoaderFactory::Suspend() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(resource_provider_);
+  DCHECK(!is_suspended_);
 
+  is_suspended_ = true;
   resource_provider_ = NULL;
+
   for (LoaderSet::const_iterator iter = active_loaders_.begin();
        iter != active_loaders_.end(); ++iter) {
     (*iter)->Suspend();
@@ -121,8 +128,11 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(resource_provider);
   DCHECK(!resource_provider_);
+  DCHECK(is_suspended_);
 
+  is_suspended_ = false;
   resource_provider_ = resource_provider;
+
   for (LoaderSet::const_iterator iter = active_loaders_.begin();
        iter != active_loaders_.end(); ++iter) {
     (*iter)->Resume(resource_provider);
diff --git a/src/cobalt/loader/loader_factory.h b/src/cobalt/loader/loader_factory.h
index 571a87c..4cd8b25 100644
--- a/src/cobalt/loader/loader_factory.h
+++ b/src/cobalt/loader/loader_factory.h
@@ -90,6 +90,10 @@
   // Thread to run asynchronous fetchers and decoders on.  At the moment,
   // image decoding is the only thing done on this thread.
   base::Thread load_thread_;
+
+  // Whether or not the LoaderFactory is currently suspended. While it is, all
+  // loaders created by it begin in a suspended state.
+  bool is_suspended_;
 };
 
 }  // namespace loader
diff --git a/src/cobalt/loader/mesh/mesh_decoder.cc b/src/cobalt/loader/mesh/mesh_decoder.cc
index 9902879..2964508 100644
--- a/src/cobalt/loader/mesh/mesh_decoder.cc
+++ b/src/cobalt/loader/mesh/mesh_decoder.cc
@@ -199,13 +199,17 @@
     : resource_provider_(resource_provider),
       success_callback_(success_callback),
       error_callback_(error_callback),
-      suspended_(false) {
-  DCHECK(resource_provider_);
+      is_suspended_(!resource_provider_) {
   DCHECK(!success_callback_.is_null());
   DCHECK(!error_callback_.is_null());
 }
 
 void MeshDecoder::DecodeChunk(const char* data, size_t size) {
+  if (is_suspended_) {
+    DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
+    return;
+  }
+
   if (!raw_data_) {
     raw_data_ = make_scoped_ptr(new std::vector<uint8>());
   }
@@ -216,10 +220,8 @@
   memcpy(&((*raw_data_)[start_size]), data, size);
 }
 
-void MeshDecoder::ReleaseRawData() { raw_data_.reset(); }
-
 void MeshDecoder::Finish() {
-  if (suspended_) {
+  if (is_suspended_) {
     DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
     return;
   }
@@ -249,23 +251,27 @@
 }
 
 bool MeshDecoder::Suspend() {
-  DCHECK(!suspended_);
-  suspended_ = true;
+  DCHECK(!is_suspended_);
+  DCHECK(resource_provider_);
+
+  is_suspended_ = true;
   resource_provider_ = NULL;
+
   ReleaseRawData();
   return true;
 }
 
 void MeshDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
-  if (!suspended_) {
-    DCHECK_EQ(resource_provider_, resource_provider);
-    return;
-  }
+  DCHECK(is_suspended_);
+  DCHECK(!resource_provider_);
+  DCHECK(resource_provider);
 
-  suspended_ = false;
+  is_suspended_ = false;
   resource_provider_ = resource_provider;
 }
 
+void MeshDecoder::ReleaseRawData() { raw_data_.reset(); }
+
 }  // namespace mesh
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/loader/mesh/mesh_decoder.h b/src/cobalt/loader/mesh/mesh_decoder.h
index 6d81cc3..5c528b0 100644
--- a/src/cobalt/loader/mesh/mesh_decoder.h
+++ b/src/cobalt/loader/mesh/mesh_decoder.h
@@ -63,7 +63,7 @@
 
   scoped_ptr<std::vector<uint8> > raw_data_;
 
-  bool suspended_;
+  bool is_suspended_;
 };
 
 }  // namespace mesh
diff --git a/src/cobalt/media/base/decryptor.cc b/src/cobalt/media/base/decryptor.cc
deleted file mode 100644
index 91496ec..0000000
--- a/src/cobalt/media/base/decryptor.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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.
-
-#include "cobalt/media/base/decryptor.h"
-
-namespace cobalt {
-namespace media {
-
-Decryptor::Decryptor() {}
-
-Decryptor::~Decryptor() {}
-
-}  // namespace media
-}  // namespace cobalt
diff --git a/src/cobalt/media/base/decryptor.h b/src/cobalt/media/base/decryptor.h
deleted file mode 100644
index c7f0c6d..0000000
--- a/src/cobalt/media/base/decryptor.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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 COBALT_MEDIA_BASE_DECRYPTOR_H_
-#define COBALT_MEDIA_BASE_DECRYPTOR_H_
-
-#include <list>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "cobalt/media/base/audio_buffer.h"
-#include "cobalt/media/base/media_export.h"
-
-namespace cobalt {
-namespace media {
-
-class AudioDecoderConfig;
-class DecoderBuffer;
-class VideoDecoderConfig;
-class VideoFrame;
-
-// Decrypts (and decodes) encrypted buffer.
-//
-// All methods are called on the (video/audio) decoder thread. Decryptor
-// implementations must be thread safe when methods are called this way.
-// Depending on the implementation callbacks may be fired synchronously or
-// asynchronously.
-class MEDIA_EXPORT Decryptor {
- public:
-  // TODO(xhwang): Replace kError with kDecryptError and kDecodeError.
-  // TODO(xhwang): Replace kNeedMoreData with kNotEnoughData.
-  enum Status {
-    kSuccess,  // Decryption successfully completed. Decrypted buffer ready.
-    kNoKey,    // No key is available to decrypt.
-    kNeedMoreData,  // Decoder needs more data to produce a frame.
-    kError          // Key is available but an error occurred during decryption.
-  };
-
-  // TODO(xhwang): Unify this with DemuxerStream::Type.
-  enum StreamType { kAudio, kVideo };
-
-  Decryptor();
-  virtual ~Decryptor();
-
-  // Indicates that a new key has been added to the MediaKeys object associated
-  // with the Decryptor.
-  typedef base::Callback<void()> NewKeyCB;
-
-  // Registers a NewKeyCB which should be called when a new key is added to the
-  // decryptor. Only one NewKeyCB can be registered for one |stream_type|.
-  // If this function is called multiple times for the same |stream_type|, the
-  // previously registered callback will be replaced. In other words,
-  // registering a null callback cancels the originally registered callback.
-  virtual void RegisterNewKeyCB(StreamType stream_type,
-                                const NewKeyCB& key_added_cb) = 0;
-
-  // Indicates completion of a decryption operation.
-  //
-  // First parameter: The status of the decryption operation.
-  // - Set to kSuccess if the encrypted buffer is successfully decrypted and
-  //   the decrypted buffer is ready to be read.
-  // - Set to kNoKey if no decryption key is available to decrypt the encrypted
-  //   buffer. In this case the decrypted buffer must be NULL.
-  // - Set to kError if unexpected error has occurred. In this case the
-  //   decrypted buffer must be NULL.
-  // - This parameter should not be set to kNeedMoreData.
-  // Second parameter: The decrypted buffer.
-  // - Only |data|, |data_size| and |timestamp| are set in the returned
-  //   DecoderBuffer. The callback handler is responsible for setting other
-  //   fields as appropriate.
-  typedef base::Callback<void(Status, const scoped_refptr<DecoderBuffer>&)>
-      DecryptCB;
-
-  // Decrypts the |encrypted| buffer. The decrypt status and decrypted buffer
-  // are returned via the provided callback |decrypt_cb|. The |encrypted| buffer
-  // must not be NULL.
-  // Decrypt() should not be called until any previous DecryptCB of the same
-  // |stream_type| has completed. Thus, only one DecryptCB may be pending at
-  // a time for a given |stream_type|.
-  virtual void Decrypt(StreamType stream_type,
-                       const scoped_refptr<DecoderBuffer>& encrypted,
-                       const DecryptCB& decrypt_cb) = 0;
-
-  // Cancels the scheduled decryption operation for |stream_type| and fires the
-  // pending DecryptCB immediately with kSuccess and NULL.
-  // Decrypt() should not be called again before the pending DecryptCB for the
-  // same |stream_type| is fired.
-  virtual void CancelDecrypt(StreamType stream_type) = 0;
-
-  // Indicates completion of audio/video decoder initialization.
-  //
-  // First Parameter: Indicates initialization success.
-  // - Set to true if initialization was successful. False if an error occurred.
-  typedef base::Callback<void(bool)> DecoderInitCB;
-
-  // Initializes a decoder with the given |config|, executing the |init_cb|
-  // upon completion.
-  virtual void InitializeAudioDecoder(const AudioDecoderConfig& config,
-                                      const DecoderInitCB& init_cb) = 0;
-  virtual void InitializeVideoDecoder(const VideoDecoderConfig& config,
-                                      const DecoderInitCB& init_cb) = 0;
-
-  // Helper structure for managing multiple decoded audio buffers per input.
-  typedef std::list<scoped_refptr<AudioBuffer> > AudioFrames;
-
-  // Indicates completion of audio/video decrypt-and-decode operation.
-  //
-  // First parameter: The status of the decrypt-and-decode operation.
-  // - Set to kSuccess if the encrypted buffer is successfully decrypted and
-  //   decoded. In this case, the decoded frame/buffers can be/contain:
-  //   1) NULL, which means the operation has been aborted.
-  //   2) End-of-stream (EOS) frame, which means that the decoder has hit EOS,
-  //      flushed all internal buffers and cannot produce more video frames.
-  //   3) Decrypted and decoded video frame or audio buffer.
-  // - Set to kNoKey if no decryption key is available to decrypt the encrypted
-  //   buffer. In this case the returned frame(s) must be NULL/empty.
-  // - Set to kNeedMoreData if more data is needed to produce a video frame. In
-  //   this case the returned frame(s) must be NULL/empty.
-  // - Set to kError if unexpected error has occurred. In this case the
-  //   returned frame(s) must be NULL/empty.
-  // Second parameter: The decoded video frame or audio buffers.
-  typedef base::Callback<void(Status, const AudioFrames&)> AudioDecodeCB;
-  typedef base::Callback<void(Status, const scoped_refptr<VideoFrame>&)>
-      VideoDecodeCB;
-
-  // Decrypts and decodes the |encrypted| buffer. The status and the decrypted
-  // buffer are returned via the provided callback.
-  // The |encrypted| buffer must not be NULL.
-  // At end-of-stream, this method should be called repeatedly with
-  // end-of-stream DecoderBuffer until no frame/buffer can be produced.
-  // These methods can only be called after the corresponding decoder has
-  // been successfully initialized.
-  virtual void DecryptAndDecodeAudio(
-      const scoped_refptr<DecoderBuffer>& encrypted,
-      const AudioDecodeCB& audio_decode_cb) = 0;
-  virtual void DecryptAndDecodeVideo(
-      const scoped_refptr<DecoderBuffer>& encrypted,
-      const VideoDecodeCB& video_decode_cb) = 0;
-
-  // Resets the decoder to an initialized clean state, cancels any scheduled
-  // decrypt-and-decode operations, and fires any pending
-  // AudioDecodeCB/VideoDecodeCB immediately with kError and NULL.
-  // This method can only be called after the corresponding decoder has been
-  // successfully initialized.
-  virtual void ResetDecoder(StreamType stream_type) = 0;
-
-  // Releases decoder resources, deinitializes the decoder, cancels any
-  // scheduled initialization or decrypt-and-decode operations, and fires
-  // any pending DecoderInitCB/AudioDecodeCB/VideoDecodeCB immediately.
-  // DecoderInitCB should be fired with false. AudioDecodeCB/VideoDecodeCB
-  // should be fired with kError.
-  // This method can be called any time after Initialize{Audio|Video}Decoder()
-  // has been called (with the correct stream type).
-  // After this operation, the decoder is set to an uninitialized state.
-  // The decoder can be reinitialized after it is uninitialized.
-  virtual void DeinitializeDecoder(StreamType stream_type) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(Decryptor);
-};
-
-}  // namespace media
-}  // namespace cobalt
-
-#endif  // COBALT_MEDIA_BASE_DECRYPTOR_H_
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc
new file mode 100644
index 0000000..38a0be8
--- /dev/null
+++ b/src/cobalt/media/base/drm_system.cc
@@ -0,0 +1,117 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media/base/drm_system.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace cobalt {
+namespace media {
+
+DrmSystem::DrmSystem(const char* key_system, DrmSystemClient* client)
+    : client_(client),
+      wrapped_drm_system_(SbDrmCreateSystem(key_system, this,
+                                            OnSessionUpdateRequestGeneratedFunc,
+                                            OnSessionUpdatedFunc)),
+      message_loop_(MessageLoop::current()) {
+  DCHECK_NE(kSbDrmSystemInvalid, wrapped_drm_system_);
+}
+
+DrmSystem::~DrmSystem() { SbDrmDestroySystem(wrapped_drm_system_); }
+
+void DrmSystem::GenerateSessionUpdateRequest(int ticket,
+                                             const std::string& type,
+                                             const uint8_t* init_data,
+                                             int init_data_length) {
+  SbDrmGenerateSessionUpdateRequest(wrapped_drm_system_, ticket, type.c_str(),
+                                    init_data, init_data_length);
+}
+
+void DrmSystem::UpdateSession(int ticket, const std::string& session_id,
+                              const uint8_t* key, int key_length) {
+  SbDrmUpdateSession(wrapped_drm_system_, ticket, key, key_length,
+                     session_id.c_str(), session_id.size());
+}
+
+void DrmSystem::CloseSession(const std::string& session_id) {
+  SbDrmCloseSession(wrapped_drm_system_, session_id.c_str(), session_id.size());
+}
+
+void DrmSystem::OnSessionUpdateRequestGenerated(int ticket,
+                                                const void* session_id,
+                                                int session_id_size,
+                                                const void* content,
+                                                int content_size) {
+  if (session_id) {
+    std::string session_id_copy(
+        static_cast<const char*>(session_id),
+        static_cast<const char*>(session_id) + session_id_size);
+
+    scoped_array<uint8> content_copy(new uint8[content_size]);
+    SbMemoryCopy(content_copy.get(), content, content_size);
+
+    message_loop_->PostTask(
+        FROM_HERE,
+        base::Bind(&DrmSystemClient::OnSessionUpdateRequestGenerated,
+                   base::Unretained(client_), ticket, session_id_copy,
+                   base::Passed(&content_copy), content_size));
+  } else {
+    message_loop_->PostTask(
+        FROM_HERE,
+        base::Bind(&DrmSystemClient::OnSessionUpdateRequestDidNotGenerate,
+                   base::Unretained(client_), ticket));
+  }
+}
+
+void DrmSystem::OnSessionUpdated(int ticket, const void* /*session_id*/,
+                                 int /*session_id_size*/, bool succeeded) {
+  if (succeeded) {
+    message_loop_->PostTask(FROM_HERE,
+                            base::Bind(&DrmSystemClient::OnSessionUpdated,
+                                       base::Unretained(client_), ticket));
+  } else {
+    message_loop_->PostTask(FROM_HERE,
+                            base::Bind(&DrmSystemClient::OnSessionDidNotUpdate,
+                                       base::Unretained(client_), ticket));
+  }
+}
+
+// static
+void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
+    SbDrmSystem wrapped_drm_system, void* context, int ticket,
+    const void* session_id, int session_id_size, const void* content,
+    int content_size, const char* url) {
+  DCHECK(context);
+  DrmSystem* drm_system = static_cast<DrmSystem*>(context);
+  DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
+
+  drm_system->OnSessionUpdateRequestGenerated(
+      ticket, session_id, session_id_size, content, content_size);
+}
+
+// static
+void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
+                                     void* context, int ticket,
+                                     const void* session_id,
+                                     int session_id_size, bool succeeded) {
+  DCHECK(context);
+  DrmSystem* drm_system = static_cast<DrmSystem*>(context);
+  DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
+
+  drm_system->OnSessionUpdated(ticket, session_id, session_id_size, succeeded);
+}
+
+}  // namespace media
+}  // namespace cobalt
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
new file mode 100644
index 0000000..018c428
--- /dev/null
+++ b/src/cobalt/media/base/drm_system.h
@@ -0,0 +1,78 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_BASE_DRM_SYSTEM_H_
+#define COBALT_MEDIA_BASE_DRM_SYSTEM_H_
+
+#include <string>
+
+#include "base/message_loop.h"
+#include "cobalt/media/base/drm_system_client.h"
+#include "starboard/drm.h"
+
+#if SB_API_VERSION < 4
+#error "Cobalt media stack requires Starboard 4 or above."
+#endif  // SB_API_VERSION < 4
+
+namespace cobalt {
+namespace media {
+
+// A C++ wrapper around |SbDrmSystem|.
+//
+// Ensures that calls to |DrmSystemClient| are always asynchronous and performed
+// from the same thread where |DrmSystem| was instantiated.
+class DrmSystem {
+ public:
+  DrmSystem(const char* key_system, DrmSystemClient* client);
+  ~DrmSystem();
+
+  SbDrmSystem wrapped_drm_system() { return wrapped_drm_system_; }
+
+  // Wraps |SbDrmGenerateSessionUpdateRequest|.
+  void GenerateSessionUpdateRequest(int ticket, const std::string& type,
+                                    const uint8* init_data,
+                                    int init_data_length);
+  // Wraps |SbDrmUpdateSession|.
+  void UpdateSession(int ticket, const std::string& session_id,
+                     const uint8* key, int key_length);
+  // Wraps |SbDrmCloseSession|.
+  void CloseSession(const std::string& session_id);
+
+ private:
+  void OnSessionUpdateRequestGenerated(int ticket, const void* session_id,
+                                       int session_id_size, const void* content,
+                                       int content_size);
+  void OnSessionUpdated(int ticket, const void* session_id, int session_id_size,
+                        bool succeeded);
+
+  static void OnSessionUpdateRequestGeneratedFunc(
+      SbDrmSystem wrapped_drm_system, void* context, int ticket,
+      const void* session_id, int session_id_size, const void* content,
+      int content_size, const char* url);
+  static void OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
+                                   void* context, int ticket,
+                                   const void* session_id,
+                                   int session_id_length, bool succeeded);
+
+  DrmSystemClient* const client_;
+  const SbDrmSystem wrapped_drm_system_;
+  MessageLoop* const message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(DrmSystem);
+};
+
+}  // namespace media
+}  // namespace cobalt
+
+#endif  // COBALT_MEDIA_BASE_DRM_SYSTEM_H_
diff --git a/src/cobalt/media/base/drm_system_client.h b/src/cobalt/media/base/drm_system_client.h
new file mode 100644
index 0000000..a0fddfe
--- /dev/null
+++ b/src/cobalt/media/base/drm_system_client.h
@@ -0,0 +1,62 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_BASE_DRM_SYSTEM_CLIENT_H_
+#define COBALT_MEDIA_BASE_DRM_SYSTEM_CLIENT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace cobalt {
+namespace media {
+
+// An interface to be implemented by clients of |DrmSystem|.
+//
+// Calls to |DrmSystemClient| are always performed from the same thread
+// where |DrmSystem| was instantiated.
+class DrmSystemClient {
+ public:
+  // When called as a result of |DrmSystem::GenerateSessionUpdateRequest|, this
+  // method denotes a successful request generation.
+  //
+  // This method may be called multiple times after a single call to
+  // |GenerateSessionUpdateRequest|, for example when the underlying DRM system
+  // needs to update a license. In this case |ticket| will be
+  // |kSbDrmTicketInvalid|.
+  virtual void OnSessionUpdateRequestGenerated(int ticket,
+                                               const std::string& session_id,
+                                               scoped_array<uint8> message,
+                                               int message_size) = 0;
+  // Called as a result of |GenerateSessionUpdateRequest| and denotes a failure
+  // during request generation.
+  //
+  // Unlike its successful counterpart, never called spontaneously.
+  virtual void OnSessionUpdateRequestDidNotGenerate(int ticket) = 0;
+
+  // Called as a result of |UpdateSession| and denotes a successful session
+  // update.
+  virtual void OnSessionUpdated(int ticket) = 0;
+  // Called as a result of |UpdateSession| and denotes a failure during session
+  // update.
+  virtual void OnSessionDidNotUpdate(int ticket) = 0;
+
+ protected:
+  virtual ~DrmSystemClient() {}
+};
+
+}  // namespace media
+}  // namespace cobalt
+
+#endif  // COBALT_MEDIA_BASE_DRM_SYSTEM_CLIENT_H_
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index a9d3234..82863ce 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -19,21 +19,17 @@
 #include "base/memory/ref_counted.h"
 #include "base/message_loop_proxy.h"
 #include "base/time.h"
-#include "cobalt/media/base/decryptor.h"
 #include "cobalt/media/base/demuxer.h"
 #include "cobalt/media/base/media_export.h"
 #include "cobalt/media/base/pipeline_status.h"
 #include "cobalt/media/base/ranges.h"
+#include "starboard/drm.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/size.h"
 
-#if defined(OS_STARBOARD)
 #if SB_HAS(PLAYER)
-
 #define COBALT_USE_SBPLAYER_PIPELINE
-
 #endif  // SB_HAS(PLAYER)
-#endif  // defined(OS_STARBOARD)
 
 #if defined(COBALT_USE_SBPLAYER_PIPELINE)
 #include "starboard/window.h"
@@ -47,6 +43,12 @@
 
 class MediaLog;
 
+// Callback to notify that a DRM system is ready.
+typedef base::Callback<void(SbDrmSystem)> DrmSystemReadyCB;
+
+// Callback to set a DrmSystemReadyCB.
+typedef base::Callback<void(const DrmSystemReadyCB&)> SetDrmSystemReadyCB;
+
 // Pipeline contains the common interface for media pipelines.  It provides
 // functions to perform asynchronous initialization, pausing, seeking and
 // playing.
@@ -90,8 +92,8 @@
   //
   // The following permanent callbacks will be executed as follows up until
   // Stop() has completed:
-  //   |decryptor_ready_cb| can be used if Pipeline needs to be notified when
-  //                        the Decryptor is ready.
+  //   |set_drm_system_ready_cb| can be used if Pipeline needs to be notified
+  //                             when the |SbDrmSystem| is ready.
   //   |ended_cb| will be executed whenever the media reaches the end.
   //   |error_cb| will be executed whenever an error occurs but hasn't
   //              been reported already through another callback.
@@ -100,12 +102,13 @@
   //   |duration_change_cb| optional callback that will be executed whenever the
   //                        presentation duration changes.
   // It is an error to call this method after the pipeline has already started.
-  virtual void Start(Demuxer* demuxer, const PipelineStatusCB& ended_cb,
+  virtual void Start(Demuxer* demuxer,
+                     const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+                     const PipelineStatusCB& ended_cb,
                      const PipelineStatusCB& error_cb,
                      const PipelineStatusCB& seek_cb,
                      const BufferingStateCB& buffering_state_cb,
-                     const base::Closure& duration_change_cb,
-                     bool prefer_decode_to_texture) = 0;
+                     const base::Closure& duration_change_cb) = 0;
 
   // Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
   // teardown has completed.
@@ -176,8 +179,8 @@
   // Get the SetBoundsCB used to set the bounds of the video frame.
   virtual SetBoundsCB GetSetBoundsCB() { return SetBoundsCB(); }
 
-  // Returns whether the player is configured for outputting in punch out mode.
-  virtual bool IsPunchOutMode() { return false; }
+  // Updates the player's preference for decode-to-texture versus punch through.
+  virtual void SetDecodeToTextureOutputMode(bool /*enabled*/) {}
 };
 
 }  // namespace media
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 98cdfab..40763c6 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -17,9 +17,11 @@
 #include "base/basictypes.h"  // For COMPILE_ASSERT
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
+#include "base/optional.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/time.h"
@@ -53,12 +55,12 @@
 // parameters exceed what base::Bind() can support.
 struct StartTaskParameters {
   Demuxer* demuxer;
+  SetDrmSystemReadyCB set_drm_system_ready_cb;
   PipelineStatusCB ended_cb;
   PipelineStatusCB error_cb;
   PipelineStatusCB seek_cb;
   Pipeline::BufferingStateCB buffering_state_cb;
   base::Closure duration_change_cb;
-  bool prefer_decode_to_texture;
 };
 
 // SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer
@@ -75,11 +77,12 @@
 
   void Suspend() OVERRIDE;
   void Resume() OVERRIDE;
-  void Start(Demuxer* demuxer, const PipelineStatusCB& ended_cb,
-             const PipelineStatusCB& error_cb, const PipelineStatusCB& seek_cb,
+  void Start(Demuxer* demuxer,
+             const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+             const PipelineStatusCB& ended_cb, const PipelineStatusCB& error_cb,
+             const PipelineStatusCB& seek_cb,
              const BufferingStateCB& buffering_state_cb,
-             const base::Closure& duration_change_cb,
-             bool prefer_decode_to_texture) OVERRIDE;
+             const base::Closure& duration_change_cb) OVERRIDE;
 
   void Stop(const base::Closure& stop_cb) OVERRIDE;
   void Seek(TimeDelta time, const PipelineStatusCB& seek_cb);
@@ -99,8 +102,7 @@
   bool DidLoadingProgress() const OVERRIDE;
   PipelineStatistics GetStatistics() const OVERRIDE;
   SetBoundsCB GetSetBoundsCB() OVERRIDE;
-
-  bool IsPunchOutMode() OVERRIDE;
+  void SetDecodeToTextureOutputMode(bool enabled) OVERRIDE;
 
  private:
   void StartTask(const StartTaskParameters& parameters);
@@ -118,7 +120,6 @@
   void RemoveTextStream(DemuxerStream* text_stream) OVERRIDE;
 
   void CreatePlayer(SbDrmSystem drm_system);
-  void SetDecryptor(Decryptor* decryptor);
   void OnDemuxerInitialized(PipelineStatus status);
   void OnDemuxerSeeked(PipelineStatus status);
   void OnDemuxerStopped();
@@ -166,12 +167,11 @@
   // the filters.
   float playback_rate_;
 
-  // Whether the media contains rendered audio and video streams.
-  // TODO(fischman,scherkus): replace these with checks for
-  // {audio,video}_decoder_ once extraction of {Audio,Video}Decoder from the
-  // Filter heirarchy is done.
-  bool has_audio_;
-  bool has_video_;
+  // The saved audio and video demuxer streams.  Note that it is safe to store
+  // raw pointers of the demuxer streams, as the Demuxer guarantees that its
+  // |DemuxerStream|s live as long as the Demuxer itself.
+  DemuxerStream* audio_stream_;
+  DemuxerStream* video_stream_;
 
   mutable PipelineStatistics statistics_;
 
@@ -182,11 +182,12 @@
   base::Closure stop_cb_;
 
   // Permanent callbacks passed in via Start().
+  SetDrmSystemReadyCB set_drm_system_ready_cb_;
   PipelineStatusCB ended_cb_;
   PipelineStatusCB error_cb_;
   BufferingStateCB buffering_state_cb_;
   base::Closure duration_change_cb_;
-  bool prefer_decode_to_texture_;
+  base::optional<bool> decode_to_texture_output_mode_;
 
   // Demuxer reference used for setting the preload value.
   Demuxer* demuxer_;
@@ -207,6 +208,7 @@
   base::TimeDelta seek_time_;
   scoped_ptr<StarboardPlayer> player_;
   bool suspended_;
+  bool stopped_;
 
   DISALLOW_COPY_AND_ASSIGN(SbPlayerPipeline);
 };
@@ -220,14 +222,14 @@
       natural_size_(0, 0),
       volume_(1.f),
       playback_rate_(0.f),
-      has_audio_(false),
-      has_video_(false),
+      audio_stream_(NULL),
+      video_stream_(NULL),
       demuxer_(NULL),
       audio_read_in_progress_(false),
       video_read_in_progress_(false),
       set_bounds_helper_(new SbPlayerSetBoundsHelper),
       suspended_(false),
-      prefer_decode_to_texture_(false) {}
+      stopped_(false) {}
 
 SbPlayerPipeline::~SbPlayerPipeline() { DCHECK(!player_); }
 
@@ -253,12 +255,15 @@
   waitable_event.Wait();
 }
 
-void SbPlayerPipeline::Start(Demuxer* demuxer, const PipelineStatusCB& ended_cb,
+void SbPlayerPipeline::Start(Demuxer* demuxer,
+                             const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+                             const PipelineStatusCB& ended_cb,
                              const PipelineStatusCB& error_cb,
                              const PipelineStatusCB& seek_cb,
                              const BufferingStateCB& buffering_state_cb,
-                             const base::Closure& duration_change_cb,
-                             bool prefer_decode_to_texture) {
+                             const base::Closure& duration_change_cb) {
+  TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::Start");
+
   DCHECK(demuxer);
   DCHECK(!ended_cb.is_null());
   DCHECK(!error_cb.is_null());
@@ -268,18 +273,20 @@
 
   StartTaskParameters parameters;
   parameters.demuxer = demuxer;
+  parameters.set_drm_system_ready_cb = set_drm_system_ready_cb;
   parameters.ended_cb = ended_cb;
   parameters.error_cb = error_cb;
   parameters.seek_cb = seek_cb;
   parameters.buffering_state_cb = buffering_state_cb;
   parameters.duration_change_cb = duration_change_cb;
-  parameters.prefer_decode_to_texture = prefer_decode_to_texture;
 
   message_loop_->PostTask(
       FROM_HERE, base::Bind(&SbPlayerPipeline::StartTask, this, parameters));
 }
 
 void SbPlayerPipeline::Stop(const base::Closure& stop_cb) {
+  TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::Stop");
+
   if (!message_loop_->BelongsToCurrentThread()) {
     message_loop_->PostTask(FROM_HERE,
                             base::Bind(&SbPlayerPipeline::Stop, this, stop_cb));
@@ -289,6 +296,8 @@
   DCHECK(stop_cb_.is_null());
   DCHECK(!stop_cb.is_null());
 
+  stopped_ = true;
+
   if (player_) {
     scoped_ptr<StarboardPlayer> player;
     {
@@ -345,12 +354,12 @@
 
 bool SbPlayerPipeline::HasAudio() const {
   base::AutoLock auto_lock(lock_);
-  return has_audio_;
+  return audio_stream_ != NULL;
 }
 
 bool SbPlayerPipeline::HasVideo() const {
   base::AutoLock auto_lock(lock_);
-  return has_video_;
+  return video_stream_ != NULL;
 }
 
 float SbPlayerPipeline::GetPlaybackRate() const {
@@ -443,25 +452,34 @@
   return base::Bind(&SbPlayerSetBoundsHelper::SetBounds, set_bounds_helper_);
 }
 
-bool SbPlayerPipeline::IsPunchOutMode() {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-  base::AutoLock auto_lock(lock_);
-  if (player_) {
-    return player_->GetSbPlayerOutputMode() == kSbPlayerOutputModePunchOut;
-  } else {
-    return true;
+void SbPlayerPipeline::SetDecodeToTextureOutputMode(bool enabled) {
+  TRACE_EVENT1("cobalt::media",
+               "SbPlayerPipeline::SetDecodeToTextureOutputMode", "mode",
+               enabled);
+
+  if (!message_loop_->BelongsToCurrentThread()) {
+    message_loop_->PostTask(
+        FROM_HERE, base::Bind(&SbPlayerPipeline::SetDecodeToTextureOutputMode,
+                              this, enabled));
+    return;
   }
-#else  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-  return true;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+
+  // The player can't be created yet, if it is, then we're updating the output
+  // mode too late.
+  DCHECK(!player_);
+
+  decode_to_texture_output_mode_ = enabled;
 }
 
 void SbPlayerPipeline::StartTask(const StartTaskParameters& parameters) {
+  TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::StartTask");
+
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   DCHECK(!demuxer_);
 
   demuxer_ = parameters.demuxer;
+  set_drm_system_ready_cb_ = parameters.set_drm_system_ready_cb;
   ended_cb_ = parameters.ended_cb;
   error_cb_ = parameters.error_cb;
   {
@@ -470,7 +488,6 @@
   }
   buffering_state_cb_ = parameters.buffering_state_cb;
   duration_change_cb_ = parameters.duration_change_cb;
-  prefer_decode_to_texture_ = parameters.prefer_decode_to_texture;
 
   const bool kEnableTextTracks = false;
   demuxer_->Initialize(this,
@@ -539,7 +556,11 @@
 }
 
 void SbPlayerPipeline::CreatePlayer(SbDrmSystem drm_system) {
+  TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::CreatePlayer");
+
   DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(audio_stream_);
+  DCHECK(video_stream_);
 
   if (suspended_) {
     message_loop_->PostTask(
@@ -552,15 +573,15 @@
   // player is created.  In this case we should delay creating the player as the
   // creation of player may fail.
   const AudioDecoderConfig& audio_config =
-      demuxer_->GetStream(DemuxerStream::AUDIO)->audio_decoder_config();
+      audio_stream_->audio_decoder_config();
   const VideoDecoderConfig& video_config =
-      demuxer_->GetStream(DemuxerStream::VIDEO)->video_decoder_config();
+      video_stream_->video_decoder_config();
 
   {
     base::AutoLock auto_lock(lock_);
     player_.reset(new StarboardPlayer(
         message_loop_, audio_config, video_config, window_, drm_system, this,
-        set_bounds_helper_.get(), prefer_decode_to_texture_));
+        set_bounds_helper_.get(), *decode_to_texture_output_mode_));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
@@ -580,55 +601,47 @@
   seek_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
 }
 
-void SbPlayerPipeline::SetDecryptor(Decryptor* decryptor) {
+void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
+  TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::OnDemuxerInitialized");
+
   DCHECK(message_loop_->BelongsToCurrentThread());
 
-  if (!decryptor) {
+  if (stopped_) {
     return;
   }
-  NOTREACHED();
-  // TODO: Migrate this
-  // StarboardDecryptor* sb_decryptor =
-  //    reinterpret_cast<StarboardDecryptor*>(decryptor);
-  // CreatePlayer(sb_decryptor->drm_system());
-}
-
-void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
 
   if (status != PIPELINE_OK) {
     ResetAndRunIfNotNull(&error_cb_, status);
     return;
   }
 
-  if (demuxer_->GetStream(DemuxerStream::AUDIO) == NULL ||
-      demuxer_->GetStream(DemuxerStream::VIDEO) == NULL) {
+  if (suspended_) {
+    message_loop_->PostTask(
+        FROM_HERE,
+        base::Bind(&SbPlayerPipeline::OnDemuxerInitialized, this, status));
+    return;
+  }
+
+  DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
+  DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+  if (audio_stream == NULL || video_stream == NULL) {
     ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
     return;
   }
 
   {
     base::AutoLock auto_lock(lock_);
-    has_audio_ = demuxer_->GetStream(DemuxerStream::AUDIO) != NULL;
-    DCHECK(has_audio_);
-    has_video_ = demuxer_->GetStream(DemuxerStream::VIDEO) != NULL;
+    audio_stream_ = audio_stream;
+    video_stream_ = video_stream;
 
     buffering_state_cb_.Run(kHaveMetadata);
 
-    const AudioDecoderConfig& audio_config =
-        demuxer_->GetStream(DemuxerStream::AUDIO)->audio_decoder_config();
-    bool is_encrypted = audio_config.is_encrypted();
-    if (has_video_) {
-      const VideoDecoderConfig& video_config =
-          demuxer_->GetStream(DemuxerStream::VIDEO)->video_decoder_config();
-      natural_size_ = video_config.natural_size();
-      is_encrypted |= video_config.is_encrypted();
-    }
+    bool is_encrypted = audio_stream_->audio_decoder_config().is_encrypted();
+    natural_size_ = video_stream_->video_decoder_config().natural_size();
+    is_encrypted |= video_stream_->video_decoder_config().is_encrypted();
     if (is_encrypted) {
-      // TODO: Migrate this
-      // decryptor_ready_cb_.Run(
-      //    BindToCurrentLoop(base::Bind(&SbPlayerPipeline::SetDecryptor,
-      //    this)));
+      set_drm_system_ready_cb_.Run(
+          BindToCurrentLoop(base::Bind(&SbPlayerPipeline::CreatePlayer, this)));
       return;
     }
   }
@@ -645,6 +658,8 @@
 }
 
 void SbPlayerPipeline::OnDemuxerStopped() {
+  TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::OnDemuxerStopped");
+
   if (!message_loop_->BelongsToCurrentThread()) {
     message_loop_->PostTask(
         FROM_HERE, base::Bind(&SbPlayerPipeline::OnDemuxerStopped, this));
@@ -667,7 +682,9 @@
     return;
   }
 
-  DemuxerStream* stream = demuxer_->GetStream(type);
+  DemuxerStream* stream =
+      type == DemuxerStream::AUDIO ? audio_stream_ : video_stream_;
+  DCHECK(stream);
 
   // In case if Stop() has been called.
   if (!player_) {
@@ -729,7 +746,9 @@
     }
     video_read_in_progress_ = true;
   }
-  DemuxerStream* stream = demuxer_->GetStream(type);
+  DemuxerStream* stream =
+      type == DemuxerStream::AUDIO ? audio_stream_ : video_stream_;
+  DCHECK(stream);
   stream->Read(base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this, type));
 }
 
diff --git a/src/cobalt/media/base/shell_data_source_reader.cc b/src/cobalt/media/base/shell_data_source_reader.cc
index b901b79..ecf501f 100644
--- a/src/cobalt/media/base/shell_data_source_reader.cc
+++ b/src/cobalt/media/base/shell_data_source_reader.cc
@@ -49,9 +49,15 @@
 
   int total_bytes_read = 0;
   while (size > 0 && !read_has_failed_) {
-    data_source_->Read(
-        position, size, data,
-        base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+    {
+      base::AutoLock auto_lock(lock_);
+      if (!data_source_) {
+        break;
+      }
+      data_source_->Read(
+          position, size, data,
+          base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+    }
 
     // wait for callback on read completion
     blocking_read_event_.Wait();
@@ -87,6 +93,8 @@
 void ShellDataSourceReader::Stop() {
   if (data_source_) {
     data_source_->Stop();
+
+    base::AutoLock auto_lock(lock_);
     data_source_ = NULL;
   }
 }
@@ -99,7 +107,8 @@
 
 int64 ShellDataSourceReader::FileSize() {
   if (file_size_ == -1) {
-    if (!data_source_->GetSize(&file_size_)) {
+    base::AutoLock auto_lock(lock_);
+    if (data_source_ && !data_source_->GetSize(&file_size_)) {
       file_size_ = -1;
     }
   }
diff --git a/src/cobalt/media/base/shell_data_source_reader.h b/src/cobalt/media/base/shell_data_source_reader.h
index b6eb9a7..06aef83 100644
--- a/src/cobalt/media/base/shell_data_source_reader.h
+++ b/src/cobalt/media/base/shell_data_source_reader.h
@@ -20,6 +20,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
+#include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "cobalt/media/base/data_source.h"
 
@@ -58,6 +59,7 @@
   // blocking read callback
   virtual void BlockingReadCompleted(int bytes_read);
 
+  base::Lock lock_;
   DataSource* data_source_;
   base::WaitableEvent blocking_read_event_;
   int64 file_size_;
diff --git a/src/cobalt/media/base/shell_media_platform.h b/src/cobalt/media/base/shell_media_platform.h
index 5a05594..73510fd 100644
--- a/src/cobalt/media/base/shell_media_platform.h
+++ b/src/cobalt/media/base/shell_media_platform.h
@@ -22,6 +22,7 @@
 #include "cobalt/media/base/limits.h"
 #include "cobalt/media/base/media_export.h"
 #include "cobalt/media/base/shell_video_frame_provider.h"
+#include "cobalt/render_tree/resource_provider.h"
 #include "starboard/decode_target.h"
 
 namespace cobalt {
@@ -46,7 +47,7 @@
   // The following functions will be called when the application enters or
   // leaves suspending status.
   virtual void Suspend() {}
-  virtual void Resume() {}
+  virtual void Resume(render_tree::ResourceProvider* /*resource_provider*/) {}
 
   // Media stack buffer allocate/free functions currently only used by
   // ShellBufferFactory.
@@ -62,7 +63,12 @@
     return NULL;
   }
 
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+  virtual SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() {
+    return NULL;
+  }
+#elif SB_API_VERSION >= 3
   virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
 #endif  // SB_API_VERSION >= 3
 
diff --git a/src/cobalt/media/base/shell_video_frame_provider.h b/src/cobalt/media/base/shell_video_frame_provider.h
index ba92fb6..eb98efb 100644
--- a/src/cobalt/media/base/shell_video_frame_provider.h
+++ b/src/cobalt/media/base/shell_video_frame_provider.h
@@ -17,6 +17,7 @@
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
 #include "base/time.h"
 #include "starboard/decode_target.h"
 
@@ -46,18 +47,25 @@
     kOutputModeInvalid,
   };
 
-  ShellVideoFrameProvider() : output_mode_(kOutputModePunchOut) {}
+  ShellVideoFrameProvider() : output_mode_(kOutputModeInvalid) {}
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   scoped_refptr<VideoFrame> GetCurrentFrame() { return NULL; }
 
-  void SetOutputMode(OutputMode output_mode) { output_mode_ = output_mode; }
-  OutputMode GetOutputMode() const { return output_mode_; }
+  void SetOutputMode(OutputMode output_mode) {
+    base::AutoLock auto_lock(lock_);
+    output_mode_ = output_mode;
+  }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  ShellVideoFrameProvider::OutputMode GetOutputMode() const {
+    base::AutoLock auto_lock(lock_);
+    return output_mode_;
+  }
+
+#if SB_API_VERSION >= 4
   // For Starboard platforms that have a decode-to-texture player, we enable
   // this ShellVideoFrameProvider to act as a bridge for Cobalt code to query
   // for the current SbDecodeTarget.  In effect, we bypass all of
@@ -66,27 +74,32 @@
   // needed.
   void SetGetCurrentSbDecodeTargetFunction(
       GetCurrentSbDecodeTargetFunction function) {
+    base::AutoLock auto_lock(lock_);
     get_current_sb_decode_target_function_ = function;
   }
 
   void ResetGetCurrentSbDecodeTargetFunction() {
+    base::AutoLock auto_lock(lock_);
     get_current_sb_decode_target_function_.Reset();
   }
 
   SbDecodeTarget GetCurrentSbDecodeTarget() const {
+    base::AutoLock auto_lock(lock_);
     if (get_current_sb_decode_target_function_.is_null()) {
       return kSbDecodeTargetInvalid;
     } else {
       return get_current_sb_decode_target_function_.Run();
     }
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
+  mutable base::Lock lock_;
+
   OutputMode output_mode_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   DISALLOW_COPY_AND_ASSIGN(ShellVideoFrameProvider);
 };
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index cd17293..01d4a3c 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -17,6 +17,8 @@
 #include <algorithm>
 
 #include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/debug/trace_event.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "cobalt/media/base/shell_media_platform.h"
@@ -27,6 +29,46 @@
 namespace cobalt {
 namespace media {
 
+StarboardPlayer::CallbackHelper::CallbackHelper(StarboardPlayer* player)
+    : player_(player) {}
+
+void StarboardPlayer::CallbackHelper::ClearDecoderBufferCache() {
+  base::AutoLock auto_lock(lock_);
+  if (player_) {
+    player_->ClearDecoderBufferCache();
+  }
+}
+
+void StarboardPlayer::CallbackHelper::OnDecoderStatus(
+    SbPlayer player, SbMediaType type, SbPlayerDecoderState state, int ticket) {
+  base::AutoLock auto_lock(lock_);
+  if (player_) {
+    player_->OnDecoderStatus(player, type, state, ticket);
+  }
+}
+
+void StarboardPlayer::CallbackHelper::OnPlayerStatus(SbPlayer player,
+                                                     SbPlayerState state,
+                                                     int ticket) {
+  base::AutoLock auto_lock(lock_);
+  if (player_) {
+    player_->OnPlayerStatus(player, state, ticket);
+  }
+}
+
+void StarboardPlayer::CallbackHelper::OnDeallocateSample(
+    const void* sample_buffer) {
+  base::AutoLock auto_lock(lock_);
+  if (player_) {
+    player_->OnDeallocateSample(sample_buffer);
+  }
+}
+
+void StarboardPlayer::CallbackHelper::ResetPlayer() {
+  base::AutoLock auto_lock(lock_);
+  player_ = NULL;
+}
+
 StarboardPlayer::StarboardPlayer(
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
     const AudioDecoderConfig& audio_config,
@@ -34,13 +76,14 @@
     SbDrmSystem drm_system, Host* host,
     SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture)
     : message_loop_(message_loop),
+      callback_helper_(
+          new CallbackHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this))),
       audio_config_(audio_config),
       video_config_(video_config),
       window_(window),
       drm_system_(drm_system),
       host_(host),
       set_bounds_helper_(set_bounds_helper),
-      weak_this_(AsWeakPtr()),
       frame_width_(1),
       frame_height_(1),
       ticket_(SB_PLAYER_INITIAL_TICKET),
@@ -53,29 +96,33 @@
   DCHECK(host_);
   DCHECK(set_bounds_helper_);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   output_mode_ = ComputeSbPlayerOutputMode(
       MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
       prefer_decode_to_texture);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   CreatePlayer();
 
   message_loop->PostTask(
       FROM_HERE,
-      base::Bind(&StarboardPlayer::ClearDecoderBufferCache, weak_this_));
+      base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache,
+                 callback_helper_));
 }
 
 StarboardPlayer::~StarboardPlayer() {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
+  callback_helper_->ResetPlayer();
   set_bounds_helper_->SetPlayer(NULL);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+      ShellVideoFrameProvider::kOutputModeInvalid);
+#if SB_API_VERSION >= 4
   ShellMediaPlatform::Instance()
       ->GetVideoFrameProvider()
       ->ResetGetCurrentSbDecodeTargetFunction();
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   SbPlayerDestroy(player_);
 }
@@ -129,7 +176,7 @@
   video_info.frame_width = frame_width_;
   video_info.frame_height = frame_height_;
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   SbMediaColorMetadata sb_media_color_metadata =
       MediaToSbMediaColorMetadata(video_config_.webm_color_metadata());
   video_info.color_metadata = &sb_media_color_metadata;
@@ -137,27 +184,44 @@
   if (is_encrypted) {
     FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
   }
+
+#if SB_API_VERSION >= 4
+  const void* sample_buffers[] = {buffer->data()};
+  int sample_buffer_sizes[] = {buffer->data_size()};
+  SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
+                      sample_buffers, sample_buffer_sizes, 1,
+                      TimeDeltaToSbMediaTime(buffer->timestamp()),
+                      type == DemuxerStream::VIDEO ? &video_info : NULL,
+                      drm_info.subsample_count > 0 ? &drm_info : NULL);
+#else   // SB_API_VERSION >= 4
   SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
                       buffer->data(), buffer->data_size(),
                       TimeDeltaToSbMediaTime(buffer->timestamp()),
                       type == DemuxerStream::VIDEO ? &video_info : NULL,
                       drm_info.subsample_count > 0 ? &drm_info : NULL);
+#endif  // SB_API_VERSION >= 4
 }
 
 void StarboardPlayer::SetBounds(const gfx::Rect& rect) {
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION >= 4
+  const int kZIndex = 0;
+  SbPlayerSetBounds(player_, kZIndex, rect.x(), rect.y(), rect.width(),
+                    rect.height());
+#else   // SB_API_VERSION >= 4
   SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
+#endif  // SB_API_VERSION >= 4
 }
 
 void StarboardPlayer::PrepareForSeek() {
   DCHECK(message_loop_->BelongsToCurrentThread());
   ++ticket_;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, true);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.f);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
   seek_pending_ = true;
 }
 
@@ -183,11 +247,11 @@
   ++ticket_;
   SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
   seek_pending_ = false;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else  // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::SetVolume(float volume) {
@@ -209,11 +273,11 @@
   DCHECK(SbPlayerIsValid(player_));
 
   playback_rate_ = playback_rate;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, playback_rate == 0.0);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, playback_rate);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
@@ -263,11 +327,11 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, true);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.0);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 
   base::AutoLock auto_lock(lock_);
 
@@ -304,7 +368,27 @@
   state_ = kResuming;
 }
 
+namespace {
+#if SB_API_VERSION >= 4
+ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
+    SbPlayerOutputMode output_mode) {
+  switch (output_mode) {
+    case kSbPlayerOutputModeDecodeToTexture:
+      return ShellVideoFrameProvider::kOutputModeDecodeToTexture;
+    case kSbPlayerOutputModePunchOut:
+      return ShellVideoFrameProvider::kOutputModePunchOut;
+    case kSbPlayerOutputModeInvalid:
+      return ShellVideoFrameProvider::kOutputModeInvalid;
+  }
+
+  NOTREACHED();
+  return ShellVideoFrameProvider::kOutputModeInvalid;
+}
+#endif  // #if SB_API_VERSION >= 4
+}  // namespace
+
 void StarboardPlayer::CreatePlayer() {
+  TRACE_EVENT0("cobalt::media", "StarboardPlayer::CreatePlayer");
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   SbMediaAudioHeader audio_header;
@@ -330,26 +414,26 @@
   SbMediaVideoCodec video_codec =
       MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   player_ = SbPlayerCreate(
       window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
       &audio_header, &StarboardPlayer::DeallocateSampleCB,
       &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
       ,
-      output_mode_
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+      output_mode_,
+      ShellMediaPlatform::Instance()->GetSbDecodeTargetGraphicsContextProvider()
+#elif SB_API_VERSION >= 3
       ,
       ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
 #endif  // SB_API_VERSION >= 3
-      );
+          );
   DCHECK(SbPlayerIsValid(player_));
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     // If the player is setup to decode to texture, then provide Cobalt with
     // a method of querying that texture.
@@ -359,19 +443,24 @@
             base::Bind(&StarboardPlayer::GetCurrentSbDecodeTarget,
                        base::Unretained(this)));
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+      ToVideoFrameProviderOutputMode(output_mode_));
+#else   // SB_API_VERSION >= 4
+  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+      ShellVideoFrameProvider::kOutputModePunchOut);
+#endif  // SB_API_VERSION >= 4
 
   set_bounds_helper_->SetPlayer(this);
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 SbDecodeTarget StarboardPlayer::GetCurrentSbDecodeTarget() {
   return SbPlayerGetCurrentFrame(player_);
 }
 SbPlayerOutputMode StarboardPlayer::GetSbPlayerOutputMode() {
   return output_mode_;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 void StarboardPlayer::ClearDecoderBufferCache() {
   DCHECK(message_loop_->BelongsToCurrentThread());
@@ -382,7 +471,8 @@
 
   message_loop_->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&StarboardPlayer::ClearDecoderBufferCache, weak_this_),
+      base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache,
+                 callback_helper_),
       base::TimeDelta::FromMilliseconds(
           kClearDecoderCacheIntervalInMilliseconds));
 }
@@ -443,11 +533,11 @@
     }
     SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
     SetVolume(volume_);
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
     SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else  // SB_API_VERSION < 4
     SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
     return;
   }
   host_->OnPlayerStatus(state);
@@ -475,8 +565,9 @@
                                       SbPlayerDecoderState state, int ticket) {
   StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
   helper->message_loop_->PostTask(
-      FROM_HERE, base::Bind(&StarboardPlayer::OnDecoderStatus,
-                            helper->weak_this_, player, type, state, ticket));
+      FROM_HERE,
+      base::Bind(&StarboardPlayer::CallbackHelper::OnDecoderStatus,
+                 helper->callback_helper_, player, type, state, ticket));
 }
 
 // static
@@ -484,8 +575,8 @@
                                      SbPlayerState state, int ticket) {
   StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
   helper->message_loop_->PostTask(
-      FROM_HERE, base::Bind(&StarboardPlayer::OnPlayerStatus,
-                            helper->weak_this_, player, state, ticket));
+      FROM_HERE, base::Bind(&StarboardPlayer::CallbackHelper::OnPlayerStatus,
+                            helper->callback_helper_, player, state, ticket));
 }
 
 // static
@@ -493,11 +584,12 @@
                                          const void* sample_buffer) {
   StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
   helper->message_loop_->PostTask(
-      FROM_HERE, base::Bind(&StarboardPlayer::OnDeallocateSample,
-                            helper->weak_this_, sample_buffer));
+      FROM_HERE,
+      base::Bind(&StarboardPlayer::CallbackHelper::OnDeallocateSample,
+                 helper->callback_helper_, sample_buffer));
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 // static
 SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
     SbMediaVideoCodec codec, SbDrmSystem drm_system,
@@ -511,7 +603,7 @@
     output_mode = kSbPlayerOutputModePunchOut;
   }
   if ((prefer_decode_to_texture || output_mode == kSbPlayerOutputModeInvalid) &&
-      SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, codec,
+      SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, codec,
                                   drm_system)) {
     output_mode = kSbPlayerOutputModeDecodeToTexture;
   }
@@ -519,7 +611,7 @@
 
   return output_mode;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index 548014e..5acfecd 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -19,7 +19,6 @@
 #include <utility>
 
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
 #include "base/message_loop_proxy.h"
 #include "base/synchronization/lock.h"
 #include "base/time.h"
@@ -36,7 +35,7 @@
 namespace media {
 
 // TODO: Add switch to disable caching
-class StarboardPlayer : public base::SupportsWeakPtr<StarboardPlayer> {
+class StarboardPlayer {
  public:
   class Host {
    public:
@@ -74,10 +73,10 @@
   void Suspend();
   void Resume();
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentSbDecodeTarget();
   SbPlayerOutputMode GetSbPlayerOutputMode();
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
   enum State {
@@ -86,6 +85,24 @@
     kResuming,
   };
 
+  // This class ensures that the callbacks posted to |message_loop_| are ignored
+  // automatically once StarboardPlayer is destroyed.
+  class CallbackHelper : public base::RefCountedThreadSafe<CallbackHelper> {
+   public:
+    explicit CallbackHelper(StarboardPlayer* player);
+
+    void ClearDecoderBufferCache();
+    void OnDecoderStatus(SbPlayer player, SbMediaType type,
+                         SbPlayerDecoderState state, int ticket);
+    void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket);
+    void OnDeallocateSample(const void* sample_buffer);
+    void ResetPlayer();
+
+   private:
+    base::Lock lock_;
+    StarboardPlayer* player_;
+  };
+
   static const int64 kClearDecoderCacheIntervalInMilliseconds = 1000;
 
   // A map from raw data pointer returned by DecoderBuffer::GetData() to the
@@ -110,23 +127,24 @@
   static void DeallocateSampleCB(SbPlayer player, void* context,
                                  const void* sample_buffer);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   // Returns the output mode that should be used for a video with the given
   // specifications.
   static SbPlayerOutputMode ComputeSbPlayerOutputMode(
       SbMediaVideoCodec codec, SbDrmSystem drm_system,
       bool prefer_decode_to_texture);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   // The following variables are initialized in the ctor and never changed.
   const scoped_refptr<base::MessageLoopProxy> message_loop_;
+  scoped_refptr<CallbackHelper> callback_helper_;
   AudioDecoderConfig audio_config_;
   VideoDecoderConfig video_config_;
   const SbWindow window_;
   const SbDrmSystem drm_system_;
   Host* const host_;
+  // Consider merge |SbPlayerSetBoundsHelper| into CallbackHelper.
   SbPlayerSetBoundsHelper* const set_bounds_helper_;
-  const base::WeakPtr<StarboardPlayer> weak_this_;
 
   // The following variables are only changed or accessed from the
   // |message_loop_|.
@@ -149,10 +167,10 @@
   uint32 cached_video_frames_dropped_;
   base::TimeDelta preroll_timestamp_;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   // Keep track of the output mode we are supposed to output to.
   SbPlayerOutputMode output_mode_;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace media
diff --git a/src/cobalt/media/base/starboard_utils.cc b/src/cobalt/media/base/starboard_utils.cc
index 4344006..72c4c72 100644
--- a/src/cobalt/media/base/starboard_utils.cc
+++ b/src/cobalt/media/base/starboard_utils.cc
@@ -128,7 +128,7 @@
   }
 }
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 // Ensure that the enums in starboard/media.h match enums in gfx::ColorSpace.
 #define ENUM_EQ(a, b) \
   COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums)
@@ -279,7 +279,7 @@
 
   return sb_media_color_metadata;
 }
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 3
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/starboard_utils.h b/src/cobalt/media/base/starboard_utils.h
index 5f965a6..0cb43c1 100644
--- a/src/cobalt/media/base/starboard_utils.h
+++ b/src/cobalt/media/base/starboard_utils.h
@@ -39,7 +39,7 @@
                        SbDrmSampleInfo* drm_info,
                        SbDrmSubSampleMapping* subsample_mapping);
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 SbMediaColorMetadata MediaToSbMediaColorMetadata(
     const WebMColorMetadata& webm_color_metadata);
 #endif
diff --git a/src/cobalt/media/base/video_resolution.h b/src/cobalt/media/base/video_resolution.h
new file mode 100644
index 0000000..fb4c3fb
--- /dev/null
+++ b/src/cobalt/media/base/video_resolution.h
@@ -0,0 +1,55 @@
+/*
+ * 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_MEDIA_BASE_VIDEO_RESOLUTION_H_
+#define COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
+
+#include "ui/gfx/size.h"
+
+namespace cobalt {
+namespace media {
+
+// Enumerates the various representations of the resolution of videos.  Note
+// that except |kVideoResolutionInvalid|, all other values are guaranteed to be
+// in the same order as its (width, height) pair.
+enum VideoResolution {
+  kVideoResolution1080p,  // 1920 x 1080
+  kVideoResolution2k,     // 2560 x 1440
+  kVideoResolution4k,     // 3840 x 2160
+  kVideoResolutionInvalid
+};
+
+inline VideoResolution GetVideoResolution(int width, int height) {
+  if (width <= 1920 && height <= 1080) {
+    return kVideoResolution1080p;
+  }
+  if (width <= 2560 && height <= 1440) {
+    return kVideoResolution2k;
+  }
+  if (width <= 3840 && height <= 2160) {
+    return kVideoResolution4k;
+  }
+  return kVideoResolutionInvalid;
+}
+
+inline VideoResolution GetVideoResolution(const gfx::Size& size) {
+  return GetVideoResolution(size.width(), size.height());
+}
+
+}  // namespace media
+}  // namespace cobalt
+
+#endif  // COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index bf1f4ac..f60a1c7 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/media/decoder_buffer_allocator.h"
 
+#include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
 #include "starboard/memory.h"
 
@@ -21,30 +22,32 @@
 namespace media {
 
 namespace {
-bool kThreadSafe = true;
 bool kPreAllocateAllMemory = true;
 }  // namespace
 
 DecoderBufferAllocator::DecoderBufferAllocator()
-    : memory_block_(SbMemoryAllocateAligned(DecoderBuffer::kAlignmentSize,
-                                            SB_MEDIA_MAIN_BUFFER_BUDGET)),
-      memory_pool_(memory_block_, SB_MEDIA_MAIN_BUFFER_BUDGET, kThreadSafe,
-                   kPreAllocateAllMemory) {}
+    : memory_block_(
+          SbMemoryAllocateAligned(DecoderBuffer::kAlignmentSize,
+                                  COBALT_MEDIA_BUFFER_INITIAL_CAPACITY)) {
+  memory_pool_.set(starboard::make_scoped_ptr(
+      new nb::MemoryPool(memory_block_, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY,
+                         kPreAllocateAllMemory)));
+}
 
 DecoderBufferAllocator::~DecoderBufferAllocator() {
-  DCHECK_EQ(memory_pool_.GetAllocated(), 0);
+  DCHECK_EQ(memory_pool_->GetAllocated(), 0);
   SbMemoryDeallocateAligned(memory_block_);
 }
 
 void* DecoderBufferAllocator::Allocate(Type type, size_t size,
                                        size_t alignment) {
   UNREFERENCED_PARAMETER(type);
-  return memory_pool_.Allocate(size, alignment);
+  return memory_pool_->Allocate(size, alignment);
 }
 
 void DecoderBufferAllocator::Free(Type type, void* ptr) {
   UNREFERENCED_PARAMETER(type);
-  memory_pool_.Free(ptr);
+  memory_pool_->Free(ptr);
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/decoder_buffer_allocator.h b/src/cobalt/media/decoder_buffer_allocator.h
index d7a9a00..b83426b 100644
--- a/src/cobalt/media/decoder_buffer_allocator.h
+++ b/src/cobalt/media/decoder_buffer_allocator.h
@@ -18,6 +18,7 @@
 #include "base/compiler_specific.h"
 #include "cobalt/media/base/decoder_buffer.h"
 #include "nb/memory_pool.h"
+#include "starboard/common/locked_ptr.h"
 
 namespace cobalt {
 namespace media {
@@ -32,7 +33,7 @@
 
  private:
   void* memory_block_;
-  nb::MemoryPool memory_pool_;
+  starboard::LockedPtr<nb::MemoryPool> memory_pool_;
 };
 
 }  // namespace media
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc
index 0e20745..4ae7605 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.cc
+++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -175,7 +175,6 @@
 
 void FetcherBufferedDataSource::OnURLFetchDownloadData(
     const net::URLFetcher* source, scoped_ptr<std::string> download_data) {
-  UNREFERENCED_PARAMETER(source);
   DCHECK(message_loop_->BelongsToCurrentThread());
   size_t size = download_data->size();
   if (size == 0) {
@@ -252,7 +251,7 @@
 
   const net::URLRequestStatus& status = source->GetStatus();
   if (status.is_success()) {
-    if (total_size_of_resource_ && last_request_size_ != 0) {
+    if (!total_size_of_resource_ && last_request_size_ != 0) {
       total_size_of_resource_ = buffer_offset_ + buffer_.GetLength();
     }
   } else {
@@ -273,7 +272,8 @@
 
   base::AutoLock auto_lock(lock_);
 
-  fetcher_.reset();
+  DCHECK(!fetcher_);
+  fetcher_to_be_destroyed_.reset();
 
   DCHECK_GE(static_cast<int64>(last_request_offset_), 0);
   DCHECK_GE(static_cast<int64>(last_request_size_), 0);
@@ -352,8 +352,8 @@
   pending_read_size_ = size;
   pending_read_data_ = data;
 
-  // Combine the range of the buffer and any ongoing fetch to see if the read
-  // is overlapped with it.
+  // Combine the range of the buffer and any ongoing fetch to see if the read is
+  // overlapped with it.
   if (fetcher_) {
     uint64 begin = last_request_offset_;
     uint64 end = last_request_offset_ + last_request_size_;
@@ -367,9 +367,9 @@
     }
   }
 
-  // Now we have to issue a new fetch and we no longer care about the range
-  // of the current fetch in progress if there is any.  Ideally the request
-  // range starts at |last_read_position_ - kBackwardBytes| with length of
+  // Now we have to issue a new fetch and we no longer care about the range of
+  // the current fetch in progress if there is any.  Ideally the request range
+  // starts at |last_read_position_ - kBackwardBytes| with length of
   // buffer_.GetMaxCapacity().
   if (last_read_position_ > kBackwardBytes) {
     last_request_offset_ = last_read_position_ - kBackwardBytes;
@@ -406,6 +406,7 @@
       &FetcherBufferedDataSource::CreateNewFetcher, base::Unretained(this));
   cancelable_create_fetcher_closure_ =
       new CancelableClosure(create_fetcher_closure);
+  fetcher_to_be_destroyed_.reset(fetcher_.release());
   message_loop_->PostTask(FROM_HERE,
                           cancelable_create_fetcher_closure_->AsClosure());
 }
diff --git a/src/cobalt/media/fetcher_buffered_data_source.h b/src/cobalt/media/fetcher_buffered_data_source.h
index 8cccda9..1afea8c 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.h
+++ b/src/cobalt/media/fetcher_buffered_data_source.h
@@ -115,6 +115,12 @@
   GURL url_;
   network::NetworkModule* network_module_;
   scoped_ptr<net::URLFetcher> fetcher_;
+  // |fetcher_| has to be destroyed on the thread it's created.  So it cannot be
+  // safely destroyed inside Read_Locked().  Save |fetcher_| into
+  // |fetcher_to_be_destroyed_| to ensure that it is properly destroyed either
+  // inside CreateNewFetcher() or in the dtor while still allow |fetcher_| to be
+  // set to NULL to invalidate outstanding read.
+  scoped_ptr<net::URLFetcher> fetcher_to_be_destroyed_;
 
   // |buffer_| stores a continuous block of data of target resource starts from
   // |buffer_offset_|.  When the target resource can be fit into |buffer_|,
diff --git a/src/cobalt/media/filters/chunk_demuxer.cc b/src/cobalt/media/filters/chunk_demuxer.cc
index aae74ed..66cb0c0 100644
--- a/src/cobalt/media/filters/chunk_demuxer.cc
+++ b/src/cobalt/media/filters/chunk_demuxer.cc
@@ -11,6 +11,7 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/debug/trace_event.h"
 #include "base/location.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/metrics/histogram.h"
@@ -422,6 +423,7 @@
 void ChunkDemuxer::Initialize(DemuxerHost* host,
                               const PipelineStatusCB& init_cb,
                               bool enable_text_tracks) {
+  TRACE_EVENT0("cobalt::media", "ChunkDemuxer::Initialize");
   DLOG(INFO) << "This is an ASYNC MEDIA SOURCE playback.";
   DVLOG(1) << "Init()";
 
@@ -1111,6 +1113,7 @@
 
 void ChunkDemuxer::OnSourceInitDone(
     const std::string& source_id, const StreamParser::InitParameters& params) {
+  TRACE_EVENT0("cobalt::media", "ChunkDemuxer::OnSourceInitDone");
   DVLOG(1) << "OnSourceInitDone source_id=" << source_id
            << " duration=" << params.duration.InSecondsF();
   lock_.AssertAcquired();
diff --git a/src/cobalt/media/filters/source_buffer_platform.cc b/src/cobalt/media/filters/source_buffer_platform.cc
deleted file mode 100644
index c87febf..0000000
--- a/src/cobalt/media/filters/source_buffer_platform.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 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 "cobalt/media/filters/source_buffer_platform.h"
-
-#include "starboard/configuration.h"
-
-namespace cobalt {
-namespace media {
-
-// TODO: These limits should be determinated during runtime for individual video
-//       when multiple video tags are supported.
-const size_t kSourceBufferAudioMemoryLimit =
-    SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT;
-const size_t kSourceBufferVideoMemoryLimit =
-    SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT;
-
-}  // namespace media
-}  // namespace cobalt
diff --git a/src/cobalt/media/filters/source_buffer_platform.h b/src/cobalt/media/filters/source_buffer_platform.h
deleted file mode 100644
index da89b57..0000000
--- a/src/cobalt/media/filters/source_buffer_platform.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2014 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 COBALT_MEDIA_FILTERS_SOURCE_BUFFER_PLATFORM_H_
-#define COBALT_MEDIA_FILTERS_SOURCE_BUFFER_PLATFORM_H_
-
-#include "cobalt/media/base/media_export.h"
-#include "starboard/types.h"
-
-namespace cobalt {
-namespace media {
-
-// The maximum amount of data in bytes the stream will keep in memory.
-MEDIA_EXPORT extern const size_t kSourceBufferAudioMemoryLimit;
-MEDIA_EXPORT extern const size_t kSourceBufferVideoMemoryLimit;
-
-}  // namespace media
-}  // namespace cobalt
-
-#endif  // COBALT_MEDIA_FILTERS_SOURCE_BUFFER_PLATFORM_H_
diff --git a/src/cobalt/media/filters/source_buffer_platform_lowmem.cc b/src/cobalt/media/filters/source_buffer_platform_lowmem.cc
deleted file mode 100644
index f07feca..0000000
--- a/src/cobalt/media/filters/source_buffer_platform_lowmem.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 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 "cobalt/media/filters/source_buffer_platform.h"
-#include "starboard/types.h"
-
-namespace cobalt {
-namespace media {
-
-// 2MB: approximately 1 minute of 256Kbps content.
-// 30MB: approximately 1 minute of 4Mbps content.
-const size_t kSourceBufferAudioMemoryLimit = 2 * 1024 * 1024;
-const size_t kSourceBufferVideoMemoryLimit = 30 * 1024 * 1024;
-
-}  // namespace media
-}  // namespace cobalt
diff --git a/src/cobalt/media/filters/source_buffer_stream.cc b/src/cobalt/media/filters/source_buffer_stream.cc
index 7014234..2602fd0 100644
--- a/src/cobalt/media/filters/source_buffer_stream.cc
+++ b/src/cobalt/media/filters/source_buffer_stream.cc
@@ -12,7 +12,7 @@
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "cobalt/media/base/timestamp_constants.h"
-#include "cobalt/media/filters/source_buffer_platform.h"
+#include "cobalt/media/base/video_resolution.h"
 #include "cobalt/media/filters/source_buffer_range.h"
 
 namespace cobalt {
@@ -157,7 +157,7 @@
       range_for_next_append_(ranges_.end()),
       last_output_buffer_timestamp_(kNoDecodeTimestamp()),
       max_interbuffer_distance_(kNoTimestamp),
-      memory_limit_(kSourceBufferAudioMemoryLimit) {
+      memory_limit_(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) {
   DCHECK(audio_config.IsValidConfig());
   audio_configs_.push_back(audio_config);
 }
@@ -171,10 +171,14 @@
       coded_frame_group_start_time_(kNoDecodeTimestamp()),
       range_for_next_append_(ranges_.end()),
       last_output_buffer_timestamp_(kNoDecodeTimestamp()),
-      max_interbuffer_distance_(kNoTimestamp),
-      memory_limit_(kSourceBufferVideoMemoryLimit) {
+      max_interbuffer_distance_(kNoTimestamp) {
   DCHECK(video_config.IsValidConfig());
   video_configs_.push_back(video_config);
+  VideoResolution resolution =
+      GetVideoResolution(video_config.visible_rect().size());
+  memory_limit_ = resolution <= kVideoResolution1080p
+                      ? COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
+                      : COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
 }
 
 SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config,
@@ -188,7 +192,7 @@
       range_for_next_append_(ranges_.end()),
       last_output_buffer_timestamp_(kNoDecodeTimestamp()),
       max_interbuffer_distance_(kNoTimestamp),
-      memory_limit_(kSourceBufferAudioMemoryLimit) {}
+      memory_limit_(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) {}
 
 SourceBufferStream::~SourceBufferStream() {
   while (!ranges_.empty()) {
@@ -1547,6 +1551,14 @@
   DVLOG(2) << "New video config - index: " << append_config_index_;
   video_configs_.resize(video_configs_.size() + 1);
   video_configs_[append_config_index_] = config;
+
+  VideoResolution resolution = GetVideoResolution(config.visible_rect().size());
+
+  // TODO: Reduce the memory limit when there is no more 4k samples cached.
+  if (resolution > kVideoResolution1080p) {
+    memory_limit_ = COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
+  }
+
   return true;
 }
 
diff --git a/src/cobalt/media/media.gyp b/src/cobalt/media/media.gyp
index 8b72dc1..383f25f 100644
--- a/src/cobalt/media/media.gyp
+++ b/src/cobalt/media/media.gyp
@@ -52,8 +52,6 @@
           'sources': [
             'decoder_working_memory_allocator_impl_ps4.cc',
             'decoder_working_memory_allocator_impl_ps4.h',
-            'shell_video_data_allocator_stub.cc',
-            'shell_video_data_allocator_stub.h',
           ],
         }],
         ['OS=="lb_shell"', {
diff --git a/src/cobalt/media/media2.gyp b/src/cobalt/media/media2.gyp
index 79bd59e..18c1683 100644
--- a/src/cobalt/media/media2.gyp
+++ b/src/cobalt/media/media2.gyp
@@ -63,6 +63,9 @@
         'base/demuxer_stream.h',
         'base/demuxer_stream_provider.cc',
         'base/demuxer_stream_provider.h',
+        'base/drm_system.cc',
+        'base/drm_system.h',
+        'base/drm_system_client.h',
         'base/encryption_scheme.cc',
         'base/encryption_scheme.h',
         'base/hdr_metadata.cc',
@@ -116,10 +119,10 @@
         'filters/h264_bit_reader.h',
         'filters/h264_bitstream_buffer.cc',
         'filters/h264_bitstream_buffer.h',
-        'filters/h264_to_annex_b_bitstream_converter.cc',
-        'filters/h264_to_annex_b_bitstream_converter.h',
         'filters/h264_parser.cc',
         'filters/h264_parser.h',
+        'filters/h264_to_annex_b_bitstream_converter.cc',
+        'filters/h264_to_annex_b_bitstream_converter.h',
         'filters/h265_parser.cc',
         'filters/h265_parser.h',
         'filters/shell_au.cc',
@@ -136,8 +139,6 @@
         'filters/shell_parser.h',
         'filters/shell_rbsp_stream.cc',
         'filters/shell_rbsp_stream.h',
-        'filters/source_buffer_platform.cc',
-        'filters/source_buffer_platform.h',
         'filters/source_buffer_range.cc',
         'filters/source_buffer_range.h',
         'filters/source_buffer_state.cc',
@@ -193,9 +194,9 @@
         'formats/webm/webm_constants.cc',
         'formats/webm/webm_constants.h',
         'formats/webm/webm_content_encodings.cc',
+        'formats/webm/webm_content_encodings.h',
         'formats/webm/webm_content_encodings_client.cc',
         'formats/webm/webm_content_encodings_client.h',
-        'formats/webm/webm_content_encodings.h',
         'formats/webm/webm_crypto_helpers.cc',
         'formats/webm/webm_crypto_helpers.h',
         'formats/webm/webm_info_parser.cc',
diff --git a/src/cobalt/media/media_buffer_allocator.h b/src/cobalt/media/media_buffer_allocator.h
index 0247605..b14616e 100644
--- a/src/cobalt/media/media_buffer_allocator.h
+++ b/src/cobalt/media/media_buffer_allocator.h
@@ -15,8 +15,8 @@
 #ifndef COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
 #define COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
 
-#include "base/optional.h"
 #include "nb/memory_pool.h"
+#include "starboard/common/scoped_ptr.h"
 
 namespace cobalt {
 namespace media {
@@ -30,14 +30,13 @@
         main_pool_size_(main_pool_size),
         small_allocation_pool_size_(small_allocation_pool_size),
         small_allocation_threshold_(small_allocation_threshold),
-        main_pool_(pool_, main_pool_size_, true, /* thread_safe */
-                   true /* verify_full_capacity */) {
+        main_pool_(starboard::make_scoped_ptr(new nb::MemoryPool(
+            pool_, main_pool_size_, true /* verify_full_capacity */))) {
     if (small_allocation_pool_size_ > 0u) {
       DCHECK_GT(small_allocation_threshold_, 0u);
-      small_allocation_pool_.emplace(pool_ + main_pool_size_,
-                                     small_allocation_pool_size_,
-                                     true, /* thread_safe */
-                                     true /* verify_full_capacity */);
+      small_allocation_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
+          pool_ + main_pool_size_, small_allocation_pool_size_,
+          true /* verify_full_capacity */)));
     } else {
       DCHECK_EQ(small_allocation_pool_size_, 0u);
       DCHECK_EQ(small_allocation_threshold_, 0u);
@@ -46,11 +45,12 @@
 
   void* Allocate(size_t size, size_t alignment) {
     void* p = NULL;
-    if (size < small_allocation_threshold_ && small_allocation_pool_) {
+    if (size < small_allocation_threshold_ &&
+        small_allocation_pool_->is_valid()) {
       p = small_allocation_pool_->Allocate(size, alignment);
     }
     if (!p) {
-      p = main_pool_.Allocate(size, alignment);
+      p = main_pool_->Allocate(size, alignment);
     }
     if (!p && small_allocation_pool_) {
       p = small_allocation_pool_->Allocate(size, alignment);
@@ -59,14 +59,14 @@
   }
 
   void Free(void* p) {
-    if (p >= pool_ + main_pool_size_ && small_allocation_pool_) {
+    if (p >= pool_ + main_pool_size_ && small_allocation_pool_->is_valid()) {
       DCHECK_LT(p, pool_ + main_pool_size_ + small_allocation_pool_size_);
       small_allocation_pool_->Free(p);
       return;
     }
     DCHECK_GE(p, pool_);
     DCHECK_LT(p, pool_ + main_pool_size_);
-    main_pool_.Free(p);
+    main_pool_->Free(p);
   }
 
  private:
@@ -75,8 +75,8 @@
   size_t small_allocation_pool_size_;
   size_t small_allocation_threshold_;
 
-  nb::MemoryPool main_pool_;
-  base::optional<nb::MemoryPool> small_allocation_pool_;
+  starboard::LockedPtr<nb::MemoryPool> main_pool_;
+  starboard::LockedPtr<nb::MemoryPool> small_allocation_pool_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaBufferAllocator);
 };
diff --git a/src/cobalt/media/media_module.cc b/src/cobalt/media/media_module.cc
index 0c68cbf..d92a105 100644
--- a/src/cobalt/media/media_module.cc
+++ b/src/cobalt/media/media_module.cc
@@ -49,8 +49,8 @@
   OnSuspend();
 }
 
-void MediaModule::Resume() {
-  OnResume();
+void MediaModule::Resume(render_tree::ResourceProvider* resource_provider) {
+  OnResume(resource_provider);
   RunClosureOnMessageLoopAndWait(
       message_loop_,
       base::Bind(&MediaModule::ResumeTask, base::Unretained(this)));
diff --git a/src/cobalt/media/media_module.h b/src/cobalt/media/media_module.h
index d5599c0..f45ef44 100644
--- a/src/cobalt/media/media_module.h
+++ b/src/cobalt/media/media_module.h
@@ -84,12 +84,14 @@
   // from the main thread.  Sub-classes can override these functions for
   // platform specific tasks.
   virtual void OnSuspend() {}
-  virtual void OnResume() {}
+  virtual void OnResume(render_tree::ResourceProvider* resource_provider) {
+    UNREFERENCED_PARAMETER(resource_provider);
+  }
 
   virtual system_window::SystemWindow* system_window() const { return NULL; }
 
   void Suspend();
-  void Resume();
+  void Resume(render_tree::ResourceProvider* resource_provider);
 
 #if !defined(COBALT_MEDIA_SOURCE_2016)
 #if !defined(COBALT_BUILD_TYPE_GOLD)
diff --git a/src/cobalt/media/media_module_starboard.cc b/src/cobalt/media/media_module_starboard.cc
index b8cd20a..cbfd9d2 100644
--- a/src/cobalt/media/media_module_starboard.cc
+++ b/src/cobalt/media/media_module_starboard.cc
@@ -99,6 +99,12 @@
     return system_window_;
   }
 
+  void OnSuspend() OVERRIDE { media_platform_.Suspend(); }
+
+  void OnResume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    media_platform_.Resume(resource_provider);
+  }
+
  private:
   const Options options_;
   system_window::SystemWindow* system_window_;
diff --git a/src/cobalt/media/media_module_stub.cc b/src/cobalt/media/media_module_stub.cc
index 174078a..f857978 100644
--- a/src/cobalt/media/media_module_stub.cc
+++ b/src/cobalt/media/media_module_stub.cc
@@ -26,7 +26,7 @@
 typedef ::media::WebMediaPlayer WebMediaPlayer;
 typedef ::media::WebMediaPlayerClient WebMediaPlayerClient;
 using ::media::Ranges;
-#endif  // !defined(WebMediaPlayerDelegate)
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 
 namespace {
 
@@ -82,7 +82,9 @@
   ReadyState GetReadyState() const OVERRIDE { return kReadyStateHaveNothing; }
 
   bool DidLoadingProgress() const OVERRIDE { return false; }
+#if !defined(COBALT_MEDIA_SOURCE_2016)
   unsigned long long GetTotalBytes() const OVERRIDE { return 0; }
+#endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 
   bool HasSingleSecurityOrigin() const OVERRIDE { return false; }
   bool DidPassCORSAccessCheck() const OVERRIDE { return false; }
@@ -97,6 +99,10 @@
   unsigned GetAudioDecodedByteCount() const OVERRIDE { return 0; }
   unsigned GetVideoDecodedByteCount() const OVERRIDE { return 0; }
 
+#if defined(COBALT_MEDIA_SOURCE_2016)
+  void SetDrmSystem(DrmSystem* drm_system) OVERRIDE {}
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+
   Ranges<base::TimeDelta> buffer_;
 };
 
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index 70ecf2d..6cd7800 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -26,13 +26,11 @@
 #include "ui/gfx/rect.h"
 #include "ui/gfx/size.h"
 
-// Disable `unreferenced formal parameter` as we have many stub functions in
-// this file and we want to keep their parameters.
-MSVC_PUSH_DISABLE_WARNING(4100)
-
 namespace cobalt {
 namespace media {
 
+class DrmSystem;
+
 class WebMediaPlayer {
  public:
   // Return true if the punch through box should be rendered.  Return false if
@@ -203,8 +201,16 @@
                                          size_t* /*out_size*/) {
     return false;
   }
+
+  // Sets the DRM system which must be initialized with the data passed
+  // in |WebMediaPlayerClient::EncryptedMediaInitData|.
+  //
+  // |drm_system| must not be NULL. The method can only be called once.
+  virtual void SetDrmSystem(DrmSystem* drm_system) = 0;
 };
 
+// TODO: Add prefix "On" to all methods that handle events, such
+//       as |NetworkStateChanged|, |SourceOpened|, |EncryptedMediaInitData|.
 class WebMediaPlayerClient {
  public:
   enum MediaKeyErrorCode {
@@ -239,6 +245,12 @@
   // one that is.  This can be used to indicate that, say, for spherical video
   // playback, we would like a decode-to-texture output mode.
   virtual bool PreferDecodeToTexture() const { return false; }
+  // Notifies the client that a video is encrypted. Client is supposed to call
+  // |WebMediaPlayer::SetDrmSystem| as soon as possible to avoid stalling
+  // playback.
+  virtual void EncryptedMediaInitData(EmeInitDataType init_data_type,
+                                      const unsigned char* init_data,
+                                      unsigned init_data_length) = 0;
   // TODO: Make the EME related functions pure virtual again once
   // we have proper EME implementation. Currently empty implementation are
   // provided to make media temporarily work.
@@ -275,6 +287,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-MSVC_POP_WARNING()
-
 #endif  // COBALT_MEDIA_PLAYER_WEB_MEDIA_PLAYER_H_
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 238e585..7bff85f 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -12,12 +12,14 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/debug/trace_event.h"
 #include "base/float_util.h"
 #include "base/message_loop_proxy.h"
 #include "base/metrics/histogram.h"
 #include "base/string_number_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "cobalt/media/base/bind_to_current_loop.h"
+#include "cobalt/media/base/drm_system.h"
 #include "cobalt/media/base/limits.h"
 #include "cobalt/media/base/media_log.h"
 #include "cobalt/media/filters/chunk_demuxer.h"
@@ -118,7 +120,10 @@
       incremented_externally_allocated_memory_(false),
       is_local_source_(false),
       supports_save_(true),
-      suppress_destruction_errors_(false) {
+      suppress_destruction_errors_(false),
+      drm_system_(NULL) {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::WebMediaPlayerImpl");
+
   DCHECK(buffer_allocator_);
   media_log_->AddEvent(
       media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
@@ -136,12 +141,10 @@
 }
 
 WebMediaPlayerImpl::~WebMediaPlayerImpl() {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::~WebMediaPlayerImpl");
+
   DCHECK(!main_loop_ || main_loop_ == MessageLoop::current());
 
-  if (video_frame_provider_) {
-    video_frame_provider_->SetOutputMode(
-        ShellVideoFrameProvider::kOutputModeInvalid);
-  }
   if (delegate_) {
     delegate_->UnregisterPlayer(this);
   }
@@ -196,6 +199,8 @@
 }  // anonymous namespace
 
 void WebMediaPlayerImpl::LoadMediaSource() {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::LoadMediaSource");
+
   DCHECK_EQ(main_loop_, MessageLoop::current());
 
   // Handle any volume changes that occured before load().
@@ -219,6 +224,8 @@
 void WebMediaPlayerImpl::LoadProgressive(
     const GURL& url, scoped_ptr<BufferedDataSource> data_source,
     CORSMode cors_mode) {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::LoadProgressive");
+
   DCHECK_EQ(main_loop_, MessageLoop::current());
 
   UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
@@ -247,6 +254,8 @@
 }
 
 void WebMediaPlayerImpl::Play() {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::Play");
+
   DCHECK_EQ(main_loop_, MessageLoop::current());
 #if defined(__LB_ANDROID__)
   audio_focus_bridge_.RequestAudioFocus();
@@ -299,9 +308,6 @@
     state_.pending_seek_seconds = seconds;
     if (chunk_demuxer_) {
       chunk_demuxer_->CancelPendingSeek(ConvertSecondsToTimestamp(seconds));
-      // TODO: Migrate the following
-      // decryptor_->CancelDecrypt(Decryptor::kAudio);
-      // decryptor_->CancelDecrypt(Decryptor::kVideo);
     }
     return;
   }
@@ -317,9 +323,6 @@
 
   if (chunk_demuxer_) {
     chunk_demuxer_->StartWaitingForSeek(seek_time);
-    // TODO: Migrate the following
-    // decryptor_->CancelDecrypt(Decryptor::kAudio);
-    // decryptor_->CancelDecrypt(Decryptor::kVideo);
   }
 
   // Kick off the asynchronous seek!
@@ -529,6 +532,24 @@
   return true;
 }
 
+void WebMediaPlayerImpl::SetDrmSystem(DrmSystem* drm_system) {
+  DCHECK_EQ(static_cast<DrmSystem*>(NULL), drm_system_);
+  DCHECK_NE(static_cast<DrmSystem*>(NULL), drm_system);
+
+  drm_system_ = drm_system;
+  if (!drm_system_ready_cb_.is_null()) {
+    drm_system_ready_cb_.Run(drm_system_->wrapped_drm_system());
+  }
+}
+
+void WebMediaPlayerImpl::SetDrmSystemReadyCB(
+    const DrmSystemReadyCB& drm_system_ready_cb) {
+  drm_system_ready_cb_ = drm_system_ready_cb;
+  if (drm_system_) {
+    drm_system_ready_cb_.Run(drm_system_->wrapped_drm_system());
+  }
+}
+
 void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
   DCHECK_EQ(main_loop_, MessageLoop::current());
   state_.starting = false;
@@ -615,10 +636,6 @@
 
   switch (buffering_state) {
     case Pipeline::kHaveMetadata:
-      video_frame_provider_->SetOutputMode(
-          (pipeline_->IsPunchOutMode()
-               ? ShellVideoFrameProvider::kOutputModePunchOut
-               : ShellVideoFrameProvider::kOutputModeDecodeToTexture));
       SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
       break;
     case Pipeline::kPrerollCompleted:
@@ -628,8 +645,10 @@
 }
 
 void WebMediaPlayerImpl::OnDemuxerOpened() {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::OnDemuxerOpened");
   DCHECK_EQ(main_loop_, MessageLoop::current());
   DCHECK(chunk_demuxer_);
+
   GetClient()->SourceOpened(chunk_demuxer_.get());
 }
 
@@ -651,15 +670,18 @@
 }
 
 void WebMediaPlayerImpl::StartPipeline(Demuxer* demuxer) {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::StartPipeline");
+
   state_.starting = true;
 
+  pipeline_->SetDecodeToTextureOutputMode(client_->PreferDecodeToTexture());
   pipeline_->Start(
-      demuxer, BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
+      demuxer, BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetDrmSystemReadyCB),
+      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
-      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
-      client_->PreferDecodeToTexture());
+      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged));
 }
 
 void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
@@ -690,6 +712,8 @@
 }
 
 void WebMediaPlayerImpl::Destroy() {
+  TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::Destroy");
+
   DCHECK(!main_loop_ || main_loop_ == MessageLoop::current());
 
   // If |main_loop_| has already stopped, do nothing here.
@@ -735,8 +759,10 @@
 
 void WebMediaPlayerImpl::OnEncryptedMediaInitData(
     EmeInitDataType init_data_type, const std::vector<uint8_t>& init_data) {
-  // TODO: Implement EME.
-  NOTREACHED();
+  DCHECK_EQ(main_loop_, MessageLoop::current());
+
+  GetClient()->EncryptedMediaInitData(init_data_type, &init_data[0],
+                                      init_data.size());
 }
 
 WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index 77f4b32..9df3e68 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -182,6 +182,9 @@
 
   bool GetDebugReportDataAddress(void** out_address, size_t* out_size) OVERRIDE;
 
+  void SetDrmSystem(DrmSystem* drm_system) OVERRIDE;
+  void SetDrmSystemReadyCB(const DrmSystemReadyCB& drm_system_ready_cb);
+
   void OnPipelineSeek(PipelineStatus status);
   void OnPipelineEnded(PipelineStatus status);
   void OnPipelineError(PipelineStatus error);
@@ -212,6 +215,7 @@
   // Getter method to |client_|.
   WebMediaPlayerClient* GetClient();
 
+ private:
   // Callbacks that forward duration change from |pipeline_| to |client_|.
   void OnDurationChanged();
 
@@ -304,6 +308,9 @@
   base::Callback<void(base::TimeDelta*, bool*)>
       media_time_and_seeking_state_cb_;
 
+  DrmSystemReadyCB drm_system_ready_cb_;
+  DrmSystem* drm_system_;
+
   DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
 };
 
diff --git a/src/cobalt/media/sandbox/media_source_sandbox.cc b/src/cobalt/media/sandbox/media_source_sandbox.cc
index 68560ef..02b16ca 100644
--- a/src/cobalt/media/sandbox/media_source_sandbox.cc
+++ b/src/cobalt/media/sandbox/media_source_sandbox.cc
@@ -32,7 +32,7 @@
 #else  // defined(COBALT_MEDIA_SOURCE_2016)
 #include "media/base/video_frame.h"
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
-#include "net/base/net_util.h"
+#include "starboard/file.h"
 
 namespace cobalt {
 namespace media {
@@ -50,56 +50,19 @@
 
 using base::TimeDelta;
 using render_tree::Image;
+using starboard::ScopedFile;
 
-GURL ResolveUrl(const char* arg) {
-  GURL video_url(arg);
-  if (!video_url.is_valid()) {
-    // Assume the input is a path.
-    // Try to figure out the path to this file and convert it to a URL.
-    FilePath result(arg);
-    if (!result.IsAbsolute()) {
-      FilePath content_path;
-      PathService::Get(base::DIR_EXE, &content_path);
-      DCHECK(content_path.IsAbsolute());
-      // TODO: Get the "real" exe path.
-      result = content_path.DirName().DirName().Append(result);
-    }
-    video_url = net::FilePathToFileURL(result);
+FilePath ResolvePath(const char* path) {
+  FilePath result(path);
+  if (!result.IsAbsolute()) {
+    FilePath content_path;
+    PathService::Get(base::DIR_SOURCE_ROOT, &content_path);
+    DCHECK(content_path.IsAbsolute());
+    result = content_path.Append(result);
   }
-  return video_url;
+  return result;
 }
 
-// Note that this class loads the content of the url into memory, so it won't
-// work with large media files.
-class Loader : loader::Fetcher::Handler {
- public:
-  Loader(const GURL& url, loader::FetcherFactory* fetcher_factory)
-      : done_(false), error_(false) {
-    fetcher_ = fetcher_factory->CreateFetcher(url, this);
-  }
-
-  bool done() const { return done_; }
-  bool error() const { return error_; }
-  const std::vector<uint8_t>& buffer() const { return buffer_; }
-
- private:
-  void OnReceived(loader::Fetcher* fetcher, const char* data,
-                  size_t size) OVERRIDE {
-    buffer_.insert(buffer_.end(), reinterpret_cast<const uint8_t*>(data),
-                   reinterpret_cast<const uint8_t*>(data) + size);
-  }
-  void OnDone(loader::Fetcher* fetcher) OVERRIDE { done_ = true; }
-  void OnError(loader::Fetcher* fetcher, const std::string& error) OVERRIDE {
-    buffer_.clear();
-    error_ = true;
-  }
-
-  scoped_ptr<loader::Fetcher> fetcher_;
-  std::vector<uint8_t> buffer_;
-  bool done_;
-  bool error_;
-};
-
 std::vector<std::string> MakeStringVector(const char* string) {
   std::vector<std::string> result;
   result.push_back(string);
@@ -124,18 +87,19 @@
   CHECK_EQ(status, WebMediaPlayer::kAddIdStatusOk);
 }
 
-bool IsWebMURL(const GURL& url) {
-  std::string filename = url.ExtractFileName();
+bool IsWebM(const FilePath& path) {
+  std::string filename = path.value();
   return filename.size() >= 5 &&
          filename.substr(filename.size() - 5) == ".webm";
 }
 
-void AppendData(const std::string& id, const std::vector<uint8_t>& data,
-                size_t* offset, WebMediaPlayer* player) {
+void AppendData(WebMediaPlayer* player, const std::string& id, ScopedFile* file,
+                int64* offset) {
   const float kLowWaterMarkInSeconds = 5.f;
-  const size_t kMaxBytesToAppend = 1024 * 1024;
+  const int64 kMaxBytesToAppend = 1024 * 1024;
+  char buffer[kMaxBytesToAppend];
 
-  while (*offset < data.size()) {
+  while (*offset < file->GetSize()) {
     Ranges<TimeDelta> ranges = player->SourceBuffered(id);
     float end_of_buffer =
         ranges.size() == 0 ? 0.f : ranges.end(ranges.size() - 1).InSecondsF();
@@ -143,8 +107,10 @@
     if (end_of_buffer - media_time > kLowWaterMarkInSeconds) {
       break;
     }
-    size_t bytes_to_append = std::min(kMaxBytesToAppend, data.size() - *offset);
-    player->SourceAppend(id, &data[0] + *offset, bytes_to_append);
+    int64 bytes_to_append =
+        std::min(kMaxBytesToAppend, file->GetSize() - *offset);
+    file->Read(buffer, bytes_to_append);
+    player->SourceAppend(id, reinterpret_cast<uint8*>(buffer), bytes_to_append);
     *offset += bytes_to_append;
   }
 }
@@ -160,7 +126,8 @@
 int SandboxMain(int argc, char** argv) {
   if (argc != 3 && argc != 4) {
     LOG(ERROR) << "Usage: " << argv[0]
-               << " [--null_audio_streamer] <audio url|path> <video url|path>";
+               << " [--null_audio_streamer] <audio file path> "
+               << "<video file path>";
     return 1;
   }
   MediaSandbox media_sandbox(
@@ -169,51 +136,36 @@
   WebMediaPlayerHelper player_helper(media_sandbox.GetMediaModule());
 
   // Note that we can't access PathService until MediaSandbox is initialized.
-  GURL audio_url = ResolveUrl(argv[argc - 2]);
-  GURL video_url = ResolveUrl(argv[argc - 1]);
+  FilePath audio_path = ResolvePath(argv[argc - 2]);
+  FilePath video_path = ResolvePath(argv[argc - 1]);
 
-  if (!audio_url.is_valid()) {
-    LOG(ERROR) << "Invalid Audio URL: " << audio_url;
+  ScopedFile audio_file(audio_path.value().c_str(),
+                        kSbFileOpenOnly | kSbFileRead);
+  ScopedFile video_file(video_path.value().c_str(),
+                        kSbFileOpenOnly | kSbFileRead);
+
+  if (!audio_file.IsValid()) {
+    LOG(ERROR) << "Failed to open audio file: " << audio_path.value();
     return 1;
   }
 
-  if (!video_url.is_valid()) {
-    LOG(ERROR) << "Invalid Video URL: " << video_url;
+  if (!video_file.IsValid()) {
+    LOG(ERROR) << "Failed to open video file: " << video_path.value();
     return 1;
   }
 
-  LOG(INFO) << "Loading " << audio_url << " and " << video_url;
-
-  Loader audio_loader(audio_url, media_sandbox.GetFetcherFactory());
-  Loader video_loader(video_url, media_sandbox.GetFetcherFactory());
-
-  bool audio_finished = false;
-  bool video_finished = false;
-  while (!audio_finished || !video_finished) {
-    MessageLoop::current()->RunUntilIdle();
-    audio_finished = audio_loader.done() || audio_loader.error();
-    video_finished = video_loader.done() || video_loader.error();
-  }
-
-  if (audio_loader.error()) {
-    LOG(ERROR) << "Failed to load audio: " << audio_url;
-    return 1;
-  }
-
-  if (video_loader.error()) {
-    LOG(ERROR) << "Failed to load video: " << video_url;
-    return 1;
-  }
+  LOG(INFO) << "Playing " << audio_path.value() << " and "
+            << video_path.value();
 
   WebMediaPlayer* player = player_helper.player();
 
   const std::string kAudioId = "audio";
   const std::string kVideoId = "video";
 
-  AddSourceBuffers(kAudioId, kVideoId, IsWebMURL(video_url), player);
+  AddSourceBuffers(kAudioId, kVideoId, IsWebM(video_path), player);
 
-  size_t audio_offset = 0;
-  size_t video_offset = 0;
+  int64 audio_offset = 0;
+  int64 video_offset = 0;
   bool eos_appended = false;
 
   scoped_refptr<VideoFrame> last_frame;
@@ -222,12 +174,14 @@
       base::Bind(FrameCB, base::Unretained(&player_helper)));
 
   for (;;) {
-    AppendData(kAudioId, audio_loader.buffer(), &audio_offset, player);
-    AppendData(kVideoId, video_loader.buffer(), &video_offset, player);
-    if (!eos_appended && audio_offset == audio_loader.buffer().size() &&
-        video_offset == video_loader.buffer().size()) {
-      player->SourceEndOfStream(WebMediaPlayer::kEndOfStreamStatusNoError);
-      eos_appended = true;
+    if (!eos_appended) {
+      AppendData(player, kAudioId, &audio_file, &audio_offset);
+      AppendData(player, kVideoId, &video_file, &video_offset);
+      if (video_offset == video_file.GetSize()) {
+        player->SourceAbort(kAudioId);
+        player->SourceEndOfStream(WebMediaPlayer::kEndOfStreamStatusNoError);
+        eos_appended = true;
+      }
     }
 
     if (player_helper.IsPlaybackFinished()) {
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index 73a3b49..1a2a762 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -116,7 +116,7 @@
     },
   ],
   'conditions': [
-    ['sb_media_platform == "starboard"', {
+    ['sb_media_platform == "starboard" and cobalt_media_source_2016==1', {
       'targets': [
         {
           'target_name': 'media2_sandbox',
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index 62de834..2adef29 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -46,6 +46,11 @@
   void SourceOpened() OVERRIDE {}
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
   std::string SourceURL() const OVERRIDE { return ""; }
+#if defined(COBALT_MEDIA_SOURCE_2016)
+  void EncryptedMediaInitData(EmeInitDataType init_data_type,
+                              const unsigned char* init_data,
+                              unsigned init_data_length) OVERRIDE {}
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
 };
 
 WebMediaPlayerHelper::WebMediaPlayerHelper(MediaModule* media_module)
diff --git a/src/cobalt/media/shell_media_platform_starboard.cc b/src/cobalt/media/shell_media_platform_starboard.cc
index c1975b1..92c9194 100644
--- a/src/cobalt/media/shell_media_platform_starboard.cc
+++ b/src/cobalt/media/shell_media_platform_starboard.cc
@@ -22,6 +22,7 @@
 #include "media/audio/shell_audio_streamer.h"
 #include "media/base/shell_buffer_factory.h"
 #include "media/base/shell_cached_decoder_buffer.h"
+#include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
 #include "starboard/media.h"
 
@@ -56,21 +57,20 @@
     DCHECK(gpu_memory_buffer_space_->GetMemory());
     DCHECK_GE(gpu_memory_buffer_space_->GetSizeInBytes(),
               kGPUMemoryBufferBudget);
-    gpu_memory_pool_.reset(new nb::MemoryPool(
+    gpu_memory_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
         gpu_memory_buffer_space_->GetMemory(),
-        gpu_memory_buffer_space_->GetSizeInBytes(), true, /* thread_safe */
-        true /* verify_full_capacity */, kSmallAllocationThreshold));
+        gpu_memory_buffer_space_->GetSizeInBytes(),
+        true /* verify_full_capacity */, kSmallAllocationThreshold)));
   }
 
   DCHECK_LE(0, kMainMemoryBufferBudget > 0);
   main_memory_buffer_space_.reset(static_cast<uint8*>(
       base::AlignedAlloc(kMainMemoryBufferBudget, kMediaBufferAlignment)));
   DCHECK(main_memory_buffer_space_);
-  main_memory_pool_.reset(new nb::MemoryPool(main_memory_buffer_space_.get(),
-                                             kMainMemoryBufferBudget,
-                                             true, /* thread_safe */
-                                             true, /* verify_full_capacity */
-                                             kSmallAllocationThreshold));
+  main_memory_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
+      main_memory_buffer_space_.get(), kMainMemoryBufferBudget,
+      true, /* verify_full_capacity */
+      kSmallAllocationThreshold)));
 
   ShellBufferFactory::Initialize();
   ShellAudioStreamer::Initialize();
@@ -105,16 +105,9 @@
 scoped_refptr<DecoderBuffer>
 ShellMediaPlatformStarboard::ProcessBeforeLeavingDemuxer(
     const scoped_refptr<DecoderBuffer>& buffer) {
-  if (!buffer || buffer->IsEndOfStream() || buffer->GetDataSize() == 0 ||
-      !kGPUMemoryBufferBudget)
-    return buffer;
-  void* cached_buffer =
-      main_memory_pool_->Allocate(buffer->GetDataSize(), kMediaBufferAlignment);
-  if (!cached_buffer) return buffer;
-  return new ShellCachedDecoderBuffer(
-      buffer, cached_buffer,
-      base::Bind(&nb::MemoryPool::Free,
-                 base::Unretained(main_memory_pool_.get())));
+  // TODO: Completely remove GPU buffer for the new DecoderBuffer
+  //       implementation.
+  return buffer;
 }
 
 bool ShellMediaPlatformStarboard::IsOutputProtected() {
diff --git a/src/cobalt/media/shell_media_platform_starboard.h b/src/cobalt/media/shell_media_platform_starboard.h
index 7c7fd3d..0119293 100644
--- a/src/cobalt/media/shell_media_platform_starboard.h
+++ b/src/cobalt/media/shell_media_platform_starboard.h
@@ -44,7 +44,16 @@
     return video_frame_provider_;
   }
 
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+#if SB_HAS(GRAPHICS)
+    return resource_provider_->GetSbDecodeTargetGraphicsContextProvider();
+#else   // SB_HAS(GRAPHICS)
+    return NULL;
+#endif  // SB_HAS(GRAPHICS)
+  }
+#elif SB_API_VERSION >= 3
   SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE {
 #if SB_HAS(GRAPHICS)
     return resource_provider_->GetSbDecodeTargetProvider();
@@ -52,7 +61,12 @@
     return NULL;
 #endif  // SB_HAS(GRAPHICS)
   }
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
+
+  void Suspend() OVERRIDE { resource_provider_ = NULL; }
+  void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    resource_provider_ = resource_provider;
+  }
 
  private:
   void* AllocateBuffer(size_t size) OVERRIDE {
@@ -89,6 +103,7 @@
 #include "cobalt/media/shell_video_data_allocator_common.h"
 #include "media/base/shell_video_frame_provider.h"
 #include "nb/memory_pool.h"
+#include "starboard/common/locked_ptr.h"
 
 namespace media {
 
@@ -119,7 +134,16 @@
       const scoped_refptr<DecoderBuffer>& buffer) OVERRIDE;
   bool IsOutputProtected() OVERRIDE;
 
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+#if SB_HAS(GRAPHICS)
+    return resource_provider_->GetSbDecodeTargetGraphicsContextProvider();
+#else   // SB_HAS(GRAPHICS)
+    return NULL;
+#endif  // SB_HAS(GRAPHICS)
+  }
+#elif SB_API_VERSION >= 3
   virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() {
 #if SB_HAS(GRAPHICS)
     return resource_provider_->GetSbDecodeTargetProvider();
@@ -127,19 +151,25 @@
     return NULL;
 #endif  // SB_HAS(GRAPHICS)
   }
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
+
+  void Suspend() OVERRIDE { resource_provider_ = NULL; }
+  void Resume(
+      cobalt::render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    resource_provider_ = resource_provider;
+  }
 
  private:
   cobalt::render_tree::ResourceProvider* resource_provider_;
 
   // Optional GPU Memory buffer pool, for buffer offloading.
   scoped_ptr<cobalt::render_tree::RawImageMemory> gpu_memory_buffer_space_;
-  scoped_ptr<nb::MemoryPool> gpu_memory_pool_;
+  starboard::LockedPtr<nb::MemoryPool> gpu_memory_pool_;
 
   // Main Memory buffer pool.
   scoped_ptr_malloc<uint8, base::ScopedPtrAlignedFree>
       main_memory_buffer_space_;
-  scoped_ptr<nb::MemoryPool> main_memory_pool_;
+  starboard::LockedPtr<nb::MemoryPool> main_memory_pool_;
 
   ShellVideoDataAllocatorCommon video_data_allocator_;
   scoped_refptr<ShellVideoFrameProvider> video_frame_provider_;
diff --git a/src/cobalt/media/shell_video_data_allocator_stub.cc b/src/cobalt/media/shell_video_data_allocator_stub.cc
deleted file mode 100644
index 96fad8f..0000000
--- a/src/cobalt/media/shell_video_data_allocator_stub.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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/media/shell_video_data_allocator_stub.h"
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-
-namespace media {
-
-ShellVideoDataAllocatorStub::ShellVideoDataAllocatorStub(
-    scoped_ptr<nb::MemoryPool> memory_pool)
-    : memory_pool_(memory_pool.Pass()) {
-  DCHECK(memory_pool_);
-}
-
-scoped_refptr<ShellVideoDataAllocator::FrameBuffer>
-ShellVideoDataAllocatorStub::AllocateFrameBuffer(size_t size,
-                                                 size_t alignment) {
-  return new FrameBufferStub(memory_pool_.get(), size, alignment);
-}
-
-scoped_refptr<VideoFrame> ShellVideoDataAllocatorStub::CreateYV12Frame(
-    const scoped_refptr<FrameBuffer>& frame_buffer, const YV12Param& param,
-    const base::TimeDelta& timestamp) {
-  UNREFERENCED_PARAMETER(frame_buffer);
-  scoped_refptr<VideoFrame> frame =
-      VideoFrame::CreateBlackFrame(param.visible_rect().size());
-  frame->SetTimestamp(timestamp);
-  return frame;
-}
-
-scoped_refptr<VideoFrame> ShellVideoDataAllocatorStub::CreateNV12Frame(
-    const scoped_refptr<FrameBuffer>& frame_buffer, const NV12Param& param,
-    const base::TimeDelta& timestamp) {
-  UNREFERENCED_PARAMETER(frame_buffer);
-  scoped_refptr<VideoFrame> frame =
-      VideoFrame::CreateBlackFrame(param.visible_rect().size());
-  frame->SetTimestamp(timestamp);
-  return frame;
-}
-
-ShellVideoDataAllocatorStub::FrameBufferStub::FrameBufferStub(
-    nb::MemoryPool* memory_pool, size_t size, size_t alignment)
-    : memory_pool_(memory_pool), size_(size) {
-  data_ = reinterpret_cast<uint8*>(memory_pool_->Allocate(size, alignment));
-  DCHECK(data_);
-}
-
-ShellVideoDataAllocatorStub::FrameBufferStub::~FrameBufferStub() {
-  memory_pool_->Free(data_);
-}
-
-}  // namespace media
diff --git a/src/cobalt/media/shell_video_data_allocator_stub.h b/src/cobalt/media/shell_video_data_allocator_stub.h
deleted file mode 100644
index 1539999..0000000
--- a/src/cobalt/media/shell_video_data_allocator_stub.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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_MEDIA_SHELL_VIDEO_DATA_ALLOCATOR_STUB_H_
-#define COBALT_MEDIA_SHELL_VIDEO_DATA_ALLOCATOR_STUB_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/base/shell_video_data_allocator.h"
-#include "nb/memory_pool.h"
-
-namespace media {
-
-class ShellVideoDataAllocatorStub : public ShellVideoDataAllocator {
- public:
-  explicit ShellVideoDataAllocatorStub(scoped_ptr<nb::MemoryPool> memory_pool);
-
-  scoped_refptr<FrameBuffer> AllocateFrameBuffer(size_t size,
-                                                 size_t alignment) OVERRIDE;
-  scoped_refptr<VideoFrame> CreateYV12Frame(
-      const scoped_refptr<FrameBuffer>& frame_buffer, const YV12Param& param,
-      const base::TimeDelta& timestamp) OVERRIDE;
-
-  scoped_refptr<VideoFrame> CreateNV12Frame(
-      const scoped_refptr<FrameBuffer>& frame_buffer, const NV12Param& param,
-      const base::TimeDelta& timestamp) OVERRIDE;
-
- private:
-  class FrameBufferStub : public FrameBuffer {
-   public:
-    FrameBufferStub(nb::MemoryPool* memory_pool, size_t size, size_t alignment);
-    ~FrameBufferStub();
-
-    uint8* data() const OVERRIDE { return data_; }
-    size_t size() const OVERRIDE { return size_; }
-
-   private:
-    nb::MemoryPool* memory_pool_;
-    uint8* data_;
-    size_t size_;
-  };
-
-  scoped_ptr<nb::MemoryPool> memory_pool_;
-};
-
-}  // namespace media
-
-#endif  // COBALT_MEDIA_SHELL_VIDEO_DATA_ALLOCATOR_STUB_H_
diff --git a/src/cobalt/media_session/default_media_session_client.cc b/src/cobalt/media_session/default_media_session_client.cc
new file mode 100644
index 0000000..c616091
--- /dev/null
+++ b/src/cobalt/media_session/default_media_session_client.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
+#define COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
+
+#include "cobalt/media_session/media_session_client.h"
+
+namespace cobalt {
+namespace media_session {
+
+class DefaultMediaSessionClient : public MediaSessionClient {
+ public:
+  DefaultMediaSessionClient() {}
+  virtual ~DefaultMediaSessionClient() {}
+
+ private:
+  virtual void OnMediaSessionChanged() {}
+};
+
+// static
+scoped_ptr<MediaSessionClient> MediaSessionClient::Create() {
+  return make_scoped_ptr<MediaSessionClient>(new DefaultMediaSessionClient());
+}
+
+}  // namespace media_session
+}  // namespace cobalt
+
+#endif  // COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
diff --git a/src/cobalt/media_session/media_metadata.h b/src/cobalt/media_session/media_metadata.h
index 184c569..4c03eb3 100644
--- a/src/cobalt/media_session/media_metadata.h
+++ b/src/cobalt/media_session/media_metadata.h
@@ -16,6 +16,7 @@
 #define COBALT_MEDIA_SESSION_MEDIA_METADATA_H_
 
 #include <string>
+#include <vector>
 
 #include "cobalt/media_session/media_image.h"
 #include "cobalt/media_session/media_metadata_init.h"
@@ -39,6 +40,10 @@
     if (init.has_album()) {
       album_ = init.album();
     }
+
+    if (init.has_artwork()) {
+      set_artwork(init.artwork());
+    }
   }
 
   scoped_refptr<MediaMetadata> metadata() {
@@ -57,10 +62,22 @@
 
   void set_album(const std::string& value) { album_ = value; }
 
-  MediaImage const artwork() { return image_; }
+  script::Sequence<MediaImage> const artwork() {
+    script::Sequence<MediaImage> result;
+
+    for (std::vector<MediaImage>::iterator it = artwork_.begin();
+         it != artwork_.end();
+         ++it) {
+      result.push_back(*it);
+    }
+    return result;
+  }
 
   void set_artwork(script::Sequence<MediaImage> value) {
-    UNREFERENCED_PARAMETER(value);
+    artwork_.clear();
+    for (size_t i = 0; i < value.size(); ++i) {
+      artwork_.push_back(value.at(i));
+    }
   }
 
   DEFINE_WRAPPABLE_TYPE(MediaMetadata);
@@ -69,7 +86,7 @@
   std::string title_;
   std::string artist_;
   std::string album_;
-  MediaImage image_;
+  std::vector<MediaImage> artwork_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaMetadata);
 };
diff --git a/src/cobalt/media_session/media_metadata_init.idl b/src/cobalt/media_session/media_metadata_init.idl
index 1f02514..1bad3e7 100644
--- a/src/cobalt/media_session/media_metadata_init.idl
+++ b/src/cobalt/media_session/media_metadata_init.idl
@@ -18,6 +18,5 @@
   DOMString title = "";
   DOMString artist = "";
   DOMString album = "";
-  // TODO FrozenArray not implemented in current IDL parser.
-  // attribute FrozenArray<MediaImage> artwork;
+  sequence<MediaImage> artwork;
 };
diff --git a/src/cobalt/media_session/media_session.cc b/src/cobalt/media_session/media_session.cc
new file mode 100644
index 0000000..5dc7180
--- /dev/null
+++ b/src/cobalt/media_session/media_session.cc
@@ -0,0 +1,79 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media_session/media_session.h"
+
+#include "cobalt/media_session/media_session_client.h"
+
+namespace cobalt {
+namespace media_session {
+
+MediaSession::MediaSession(MediaSessionClient* client)
+    : media_session_client_(client),
+      state_(kMediaSessionPlaybackStateNone),
+      message_loop_(base::MessageLoopProxy::current()),
+      is_change_task_queued_(false) {}
+
+MediaSession::~MediaSession() {
+  ActionMap::iterator it;
+  for (it = action_map_.begin(); it != action_map_.end(); ++it) {
+    delete it->second;
+  }
+  action_map_.clear();
+}
+
+void MediaSession::set_metadata(scoped_refptr<MediaMetadata> value) {
+  metadata_ = value;
+  MaybeQueueChangeTask();
+}
+
+void MediaSession::set_playback_state(MediaSessionPlaybackState state) {
+  state_ = state;
+  MaybeQueueChangeTask();
+}
+
+void MediaSession::SetActionHandler(
+    MediaSessionAction action, const MediaSessionActionHandlerHolder& handler) {
+  // See algorithm https://wicg.github.io/mediasession/#actions-model
+  DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_.get());
+  ActionMap::iterator it = action_map_.find(action);
+
+  if (it != action_map_.end()) {
+    delete it->second;
+    action_map_.erase(it);
+  }
+  if (!handler.IsNull()) {
+    action_map_[action] = new MediaSessionActionHandlerReference(this, handler);
+  }
+
+  MaybeQueueChangeTask();
+}
+
+void MediaSession::MaybeQueueChangeTask() {
+  DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_.get());
+  if (is_change_task_queued_) {
+    return;
+  }
+  is_change_task_queued_ = true;
+  message_loop_->PostTask(
+      FROM_HERE, base::Bind(&MediaSession::OnChanged, base::Unretained(this)));
+}
+
+void MediaSession::OnChanged() {
+  is_change_task_queued_ = false;
+  media_session_client_->OnMediaSessionChanged();
+}
+
+}  // namespace media_session
+}  // namespace cobalt
diff --git a/src/cobalt/media_session/media_session.gyp b/src/cobalt/media_session/media_session.gyp
index e57aaf3..4de3b9d 100644
--- a/src/cobalt/media_session/media_session.gyp
+++ b/src/cobalt/media_session/media_session.gyp
@@ -21,12 +21,35 @@
       'target_name': 'media_session',
       'type': 'static_library',
       'sources': [
-        'media_session.h'
-        'media_metadata.h'
+        'media_session.h',
+        'media_session.cc',
+        'media_metadata.h',
+        'media_session_client.h',
+        'media_session_client.cc',
+      ],
+      'conditions': [
+        ['custom_media_session_client == 0', {
+          'sources': [
+            'default_media_session_client.cc',
+          ]
+        }]
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types'
       ],
+      # This target doesn't generate any headers, but it exposes generated
+      # header files (for idl dictionaries) through this module's public header
+      # files. So mark this target as a hard dependency to ensure that any
+      # dependent targets wait until this target (and its hard dependencies) are
+      # built.
+      'hard_dependency': 1,
+      'export_dependent_settings': [
+        # Additionally, ensure that the include directories for generated
+        # headers are put on the include directories for targets that depend
+        # on this one.
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+      ]
     },
   ],
 }
diff --git a/src/cobalt/media_session/media_session.h b/src/cobalt/media_session/media_session.h
index 405c04e..3fb6ae7ea 100644
--- a/src/cobalt/media_session/media_session.h
+++ b/src/cobalt/media_session/media_session.h
@@ -15,53 +15,66 @@
 #ifndef COBALT_MEDIA_SESSION_MEDIA_SESSION_H_
 #define COBALT_MEDIA_SESSION_MEDIA_SESSION_H_
 
+#include <map>
+
 #include "cobalt/media_session/media_metadata.h"
 
+#include "base/containers/small_map.h"
+#include "base/location.h"
 #include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "cobalt/media_session/media_session_action.h"
+#include "cobalt/media_session/media_session_playback_state.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/script_value.h"
 
 namespace cobalt {
 namespace media_session {
 
+class MediaSessionClient;
+
 class MediaSession : public script::Wrappable {
+  friend class MediaSessionClient;
+
  public:
   typedef script::CallbackFunction<void()> MediaSessionActionHandler;
   typedef script::ScriptValue<MediaSessionActionHandler>
       MediaSessionActionHandlerHolder;
-  enum MediaSessionPlaybackState { kNone, kPaused, kPlaying };
+  typedef script::ScriptValue<MediaSessionActionHandler>::Reference
+      MediaSessionActionHandlerReference;
 
-  enum MediaSessionAction {
-    kPlay,
-    kPause,
-    kSeekbackward,
-    kSeekforward,
-    kPrevioustrack,
-    kNexttrack
-  };
+ private:
+  typedef base::SmallMap<
+      std::map<MediaSessionAction, MediaSessionActionHandlerReference*>,
+      kMediaSessionActionNumActions> ActionMap;
 
-  MediaSession() : state_(kNone) {}
+ public:
+  explicit MediaSession(MediaSessionClient* client);
+  ~MediaSession() OVERRIDE;
 
   scoped_refptr<MediaMetadata> metadata() const { return metadata_; }
 
-  void set_metadata(scoped_refptr<MediaMetadata> value) { metadata_ = value; }
+  void set_metadata(scoped_refptr<MediaMetadata> value);
 
   MediaSessionPlaybackState playback_state() const { return state_; }
 
-  void set_playback_state(MediaSessionPlaybackState state) { state_ = state; }
+  void set_playback_state(MediaSessionPlaybackState state);
 
   void SetActionHandler(MediaSessionAction action,
-                        const MediaSessionActionHandlerHolder& handler) {
-    UNREFERENCED_PARAMETER(action);
-    UNREFERENCED_PARAMETER(handler);
-    NOTIMPLEMENTED();
-  }
+                        const MediaSessionActionHandlerHolder& handler);
 
   DEFINE_WRAPPABLE_TYPE(MediaSession);
 
  private:
+  void MaybeQueueChangeTask();
+  void OnChanged();
+
+  ActionMap action_map_;
+  MediaSessionClient* media_session_client_;
   scoped_refptr<MediaMetadata> metadata_;
   MediaSessionPlaybackState state_;
+  scoped_refptr<base::MessageLoopProxy> message_loop_;
+  bool is_change_task_queued_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaSession);
 };
diff --git a/src/cobalt/media_session/media_session.idl b/src/cobalt/media_session/media_session.idl
index 8248d3d..ffb06e2 100644
--- a/src/cobalt/media_session/media_session.idl
+++ b/src/cobalt/media_session/media_session.idl
@@ -15,21 +15,6 @@
 // MediaSession interface
 // https://wicg.github.io/mediasession
 
-enum MediaSessionPlaybackState {
-  "none",
-  "paused",
-  "playing"
-};
-
-enum MediaSessionAction {
-  "play",
-  "pause",
-  "seekbackward",
-  "seekforward",
-  "previoustrack",
-  "nexttrack",
-};
-
 callback MediaSessionActionHandler = void();
 
 interface MediaSession {
diff --git a/src/cobalt/base/math.cc b/src/cobalt/media_session/media_session_action.idl
similarity index 63%
copy from src/cobalt/base/math.cc
copy to src/cobalt/media_session/media_session_action.idl
index d335495..c57a01b 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/media_session/media_session_action.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// MediaSession interface
+// https://wicg.github.io/mediasession
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum MediaSessionAction {
+  "play",
+  "pause",
+  "seekbackward",
+  "seekforward",
+  "previoustrack",
+  "nexttrack",
+
+  // Not part of spec, but used in Cobalt implementation.
+  "num-actions"
+};
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
new file mode 100644
index 0000000..f6a635b
--- /dev/null
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -0,0 +1,119 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media_session/media_session_client.h"
+
+namespace cobalt {
+namespace media_session {
+
+MediaSessionPlaybackState MediaSessionClient::GetActualPlaybackState() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Per https://wicg.github.io/mediasession/#guessed-playback-state
+  // - If the "declared playback state" is "playing", then return "playing"
+  // - Otherwise, return the guessed playback state
+  MediaSessionPlaybackState declared_state;
+  declared_state = media_session_->playback_state();
+  if (declared_state == kMediaSessionPlaybackStatePlaying) {
+    return kMediaSessionPlaybackStatePlaying;
+  }
+
+  if (platform_playback_state_ == kMediaSessionPlaybackStatePlaying) {
+    // "...guessed playback state is playing if any of them is
+    // potentially playing and not muted..."
+    return kMediaSessionPlaybackStatePlaying;
+  }
+
+  // It's not super clear what to do when the declared state or the
+  // active media session state is kPaused or kNone
+
+  if (declared_state == kMediaSessionPlaybackStatePaused) {
+    return kMediaSessionPlaybackStatePaused;
+  }
+
+  return kMediaSessionPlaybackStateNone;
+}
+
+MediaSessionClient::AvailableActionsSet
+MediaSessionClient::GetAvailableActions() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // "Available actions" are determined based on active media session
+  // and supported media session actions.
+  // Note for cobalt, there's only one window/tab so there's only one
+  // "active media session"
+  // https://wicg.github.io/mediasession/#actions-model
+  //
+  // Note that this is essentially the "media session actions update algorithm"
+  // inverted.
+  AvailableActionsSet result = AvailableActionsSet();
+
+  for (MediaSession::ActionMap::iterator it =
+         media_session_->action_map_.begin();
+       it != media_session_->action_map_.end();
+       ++it) {
+    result[it->first] = true;
+  }
+
+  switch (GetActualPlaybackState()) {
+    case kMediaSessionPlaybackStatePlaying:
+      // "If the active media session’s actual playback state is playing, remove
+      // play from available actions."
+      result[kMediaSessionActionPlay] = false;
+      break;
+    default:
+      // "Otherwise, remove pause from available actions."
+      result[kMediaSessionActionPause] = false;
+      break;
+  }
+
+  return result;
+}
+
+void MediaSessionClient::UpdatePlatformPlaybackState(
+    MediaSessionPlaybackState state) {
+  if (base::MessageLoopProxy::current() != media_session_->message_loop_) {
+    media_session_->message_loop_->PostTask(
+        FROM_HERE, base::Bind(&MediaSessionClient::UpdatePlatformPlaybackState,
+                              base::Unretained(this), state));
+    return;
+  }
+
+  MediaSessionPlaybackState prev_actual_state = GetActualPlaybackState();
+  platform_playback_state_ = state;
+
+  if (prev_actual_state != GetActualPlaybackState()) {
+    OnMediaSessionChanged();
+  }
+}
+
+void MediaSessionClient::InvokeAction(MediaSessionAction action) {
+  if (base::MessageLoopProxy::current() != media_session_->message_loop_) {
+    media_session_->message_loop_->PostTask(
+        FROM_HERE, base::Bind(&MediaSessionClient::InvokeAction,
+                              base::Unretained(this), action));
+    return;
+  }
+
+  MediaSession::ActionMap::iterator it =
+      media_session_->action_map_.find(action);
+
+  if (it == media_session_->action_map_.end()) {
+    return;
+  }
+
+  it->second->value().Run();
+}
+
+}  // namespace media_session
+}  // namespace cobalt
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
new file mode 100644
index 0000000..d838785
--- /dev/null
+++ b/src/cobalt/media_session/media_session_client.h
@@ -0,0 +1,80 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_SESSION_MEDIA_SESSION_CLIENT_H_
+#define COBALT_MEDIA_SESSION_MEDIA_SESSION_CLIENT_H_
+
+#include <bitset>
+
+#include "base/threading/thread_checker.h"
+
+#include "cobalt/media_session/media_session.h"
+
+namespace cobalt {
+namespace media_session {
+
+// Base class for a platform-level implementation of MediaSession.
+// Platforms should subclass this to connect MediaSession to their platform.
+class MediaSessionClient {
+ public:
+  typedef std::bitset<kMediaSessionActionNumActions> AvailableActionsSet;
+  MediaSessionClient()
+      : media_session_(new MediaSession(this)),
+        platform_playback_state_(kMediaSessionPlaybackStateNone) {}
+
+  virtual ~MediaSessionClient() {}
+
+  // Creates platform-specific instance.
+  static scoped_ptr<MediaSessionClient> Create();
+
+  // Retrieves the singleton MediaSession associated with this client.
+  scoped_refptr<MediaSession>& GetMediaSession() { return media_session_; }
+
+  // Retrieves the current actual playback state.
+  // https://wicg.github.io/mediasession/#actual-playback-state
+  // Must be called on the browser thread.
+  MediaSessionPlaybackState GetActualPlaybackState();
+
+  // Retrieves the set of currently available mediasession actions
+  // per "media session actions update algorithm"
+  // https://wicg.github.io/mediasession/#actions-model
+  AvailableActionsSet GetAvailableActions();
+
+  // Sets the platform's current playback state. This is used to compute
+  // the "guessed playback state"
+  // https://wicg.github.io/mediasession/#guessed-playback-state
+  // Can be invoked from any thread.
+  void UpdatePlatformPlaybackState(MediaSessionPlaybackState state);
+
+  // Invokes a given media session action
+  // https://wicg.github.io/mediasession/#actions-model
+  // Can be invoked from any thread.
+  void InvokeAction(MediaSessionAction action);
+
+  // Invoked on the browser thread when any metadata, playback state,
+  // or supported session actions change.
+  virtual void OnMediaSessionChanged() = 0;
+
+ private:
+  base::ThreadChecker thread_checker_;
+  scoped_refptr<MediaSession> media_session_;
+  MediaSessionPlaybackState platform_playback_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaSessionClient);
+};
+
+}  // namespace media_session
+}  // namespace cobalt
+
+#endif  // COBALT_MEDIA_SESSION_MEDIA_SESSION_CLIENT_H_
diff --git a/src/cobalt/base/math.cc b/src/cobalt/media_session/media_session_playback_state.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/media_session/media_session_playback_state.idl
index d335495..435ffe3 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/media_session/media_session_playback_state.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// MediaSession interface
+// https://wicg.github.io/mediasession
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum MediaSessionPlaybackState {
+  "none",
+  "paused",
+  "playing"
+};
diff --git a/src/cobalt/media_session/media_session_test.cc b/src/cobalt/media_session/media_session_test.cc
new file mode 100644
index 0000000..345dbd9
--- /dev/null
+++ b/src/cobalt/media_session/media_session_test.cc
@@ -0,0 +1,225 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media_session/media_session.h"
+
+#include "cobalt/bindings/testing/script_object_owner.h"
+#include "cobalt/media_session/media_session_client.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/testing/fake_script_value.h"
+#include "cobalt/script/wrappable.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+
+using ::cobalt::script::testing::FakeScriptValue;
+using ::cobalt::script::CallbackResult;
+using ::cobalt::script::ScriptValue;
+using ::cobalt::script::Wrappable;
+
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::_;
+
+namespace cobalt {
+namespace media_session {
+namespace {
+
+class MockCallbackFunction : public MediaSession::MediaSessionActionHandler {
+ public:
+  MOCK_CONST_METHOD0(Run, ReturnValue());
+};
+
+class MockMediaSessionClient : public MediaSessionClient {
+ public:
+  MOCK_METHOD0(OnMediaSessionChanged, void());
+};
+
+TEST(MediaSessionTest, MediaSessionTest) {
+  MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+  base::RunLoop run_loop;
+
+  MockMediaSessionClient client;
+
+  ON_CALL(client, OnMediaSessionChanged())
+      .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+  EXPECT_CALL(client, OnMediaSessionChanged()).Times(1);
+
+  scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+  run_loop.Run();
+}
+
+TEST(MediaSessionTest, GetActualPlaybackState) {
+  MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+  base::RunLoop run_loop;
+
+  MockMediaSessionClient client;
+
+  ON_CALL(client, OnMediaSessionChanged())
+      .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+  EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(2));
+
+  scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePlaying);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePaused);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePaused);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePaused, client.GetActualPlaybackState());
+
+  session->set_playback_state(kMediaSessionPlaybackStateNone);
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStateNone);
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+  run_loop.Run();
+}
+
+TEST(MediaSessionTest, NullActionClears) {
+  MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+  base::RunLoop run_loop;
+
+  MockMediaSessionClient client;
+
+  ON_CALL(client, OnMediaSessionChanged())
+      .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+  EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(0));
+
+  scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+  EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
+
+  MockCallbackFunction cf;
+  EXPECT_CALL(cf, Run())
+      .Times(1)
+      .WillRepeatedly(Return(CallbackResult<void>()));
+  FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
+
+  FakeScriptValue<MediaSession::MediaSessionActionHandler> null_holder(NULL);
+
+  session->SetActionHandler(kMediaSessionActionPlay, holder);
+  EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
+  client.InvokeAction(kMediaSessionActionPlay);
+
+  session->SetActionHandler(kMediaSessionActionPlay, null_holder);
+  EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
+  client.InvokeAction(kMediaSessionActionPlay);
+}
+
+TEST(MediaSessionTest, GetAvailableActions) {
+  MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+  base::RunLoop run_loop;
+
+  MockMediaSessionClient client;
+
+  ON_CALL(client, OnMediaSessionChanged())
+      .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+  EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(0));
+
+  scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+  EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
+
+  MockCallbackFunction cf;
+  EXPECT_CALL(cf, Run()).Times(0);
+  FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
+
+  session->SetActionHandler(kMediaSessionActionPlay, holder);
+
+  EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
+
+  session->SetActionHandler(kMediaSessionActionPause, holder);
+
+  EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
+
+  session->SetActionHandler(kMediaSessionActionSeekbackward, holder);
+
+  EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePlaying);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+  EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+  EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePaused);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+  EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+  EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePaused);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+  EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+  session->set_playback_state(kMediaSessionPlaybackStateNone);
+
+  EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+  EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+  session->set_playback_state(kMediaSessionPlaybackStatePaused);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePaused, client.GetActualPlaybackState());
+  EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+  client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStateNone);
+
+  EXPECT_EQ(kMediaSessionPlaybackStatePaused, client.GetActualPlaybackState());
+  EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace media_session
+}  // namespace cobalt
diff --git a/src/cobalt/media_session/media_session_test.gyp b/src/cobalt/media_session/media_session_test.gyp
new file mode 100644
index 0000000..e4787af
--- /dev/null
+++ b/src/cobalt/media_session/media_session_test.gyp
@@ -0,0 +1,46 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'targets': [
+    {
+      'target_name': 'media_session_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        'media_session_test.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session',
+        '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser.gyp:browser',
+        '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+        '<(DEPTH)/testing/gmock.gyp:gmock',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+    {
+      'target_name': 'media_session_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'media_session_test',
+      ],
+      'variables': {
+        'executable_name': 'media_session_test',
+      },
+      'includes': [ '../../starboard/build/deploy.gypi' ],
+    },
+  ]
+}
+
+
diff --git a/src/cobalt/network/local_network.cc b/src/cobalt/network/local_network.cc
new file mode 100644
index 0000000..725f3e4
--- /dev/null
+++ b/src/cobalt/network/local_network.cc
@@ -0,0 +1,108 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/network/local_network.h"
+
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "net/base/net_util.h"
+
+namespace cobalt {
+namespace network {
+
+namespace {
+
+#if SB_API_VERSION >= 4
+bool CompareNBytesOfAddress(const SbSocketAddress& ip,
+                            const SbSocketAddress& source_address,
+                            const SbSocketAddress& netmask,
+                            size_t number_bytes_to_compare) {
+  for (size_t i = 0; i != number_bytes_to_compare; ++i) {
+    if ((ip.address[i] & netmask.address[i]) !=
+        (source_address.address[i] & netmask.address[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool IsLocalIP(const SbSocketAddress& ip, const SbSocketAddress& source_address,
+               const SbSocketAddress& netmask) {
+  DCHECK(source_address.type == ip.type);
+  DCHECK(netmask.type == ip.type);
+
+  switch (ip.type) {
+    case kSbSocketAddressTypeIpv4:
+      return CompareNBytesOfAddress(ip, source_address, netmask,
+                                    net::kIPv4AddressSize);
+#if SB_HAS(IPV6)
+    case kSbSocketAddressTypeIpv6:
+      return CompareNBytesOfAddress(ip, source_address, netmask,
+                                    net::kIPv6AddressSize);
+#endif
+    default:
+      NOTREACHED() << "Invalid IP type " << ip.type;
+      return false;
+  }
+}
+
+#endif  // SB_API_VERSION >= 4
+
+}  // namespace
+
+bool IsIPInLocalNetwork(const SbSocketAddress& destination) {
+#if SB_API_VERSION >= 4
+  SbSocketAddress source_address;
+  SbSocketAddress netmask;
+  if (!(SbSocketGetInterfaceAddress(&destination, &source_address, &netmask))) {
+    return false;
+  }
+  return IsLocalIP(destination, source_address, netmask);
+#else
+  UNREFERENCED_PARAMETER(destination);
+  return false;
+#endif
+}
+
+bool IsIPInPrivateRange(const SbSocketAddress& ip) {
+  // For IPv4, the private address range is defined by RFC 1918
+  // avaiable at https://tools.ietf.org/html/rfc1918#section-3.
+  if (ip.type == kSbSocketAddressTypeIpv4) {
+    if (ip.address[0] == 10) {
+      // IP is in range 10.0.0.0 - 10.255.255.255 (10/8 prefix).
+      return true;
+    }
+    if ((ip.address[0] == 192) && (ip.address[1] == 168)) {
+      // IP is in range 192.168.0.0 - 192.168.255.255 (192.168/16 prefix).
+      return true;
+    }
+    if ((ip.address[0] == 172) &&
+        ((ip.address[1] >= 16) || (ip.address[1] <= 31))) {
+      // IP is in range 172.16.0.0 - 172.31.255.255 (172.16/12 prefix).
+      return true;
+    }
+  }
+#if SB_HAS(IPV6)
+  if (ip.type == kSbSocketAddressTypeIpv6) {
+    // Unique Local Addresses for IPv6 are _effectively_ fd00::/8.
+    // See https://tools.ietf.org/html/rfc4193#section-3 for details.
+    return ip.address[0] == 0xfd && ip.address[1] == 0;
+  }
+#endif
+
+  return false;
+}
+
+}  // namespace network
+}  // namespace cobalt
diff --git a/src/cobalt/network/local_network.h b/src/cobalt/network/local_network.h
new file mode 100644
index 0000000..9cbb6be
--- /dev/null
+++ b/src/cobalt/network/local_network.h
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_NETWORK_LOCAL_NETWORK_H_
+#define COBALT_NETWORK_LOCAL_NETWORK_H_
+
+#include "starboard/socket.h"
+
+namespace cobalt {
+namespace network {
+
+bool IsIPInLocalNetwork(const SbSocketAddress& destination);
+bool IsIPInPrivateRange(const SbSocketAddress& destination);
+
+}  // namespace network
+}  // namespace cobalt
+
+#endif  // COBALT_NETWORK_LOCAL_NETWORK_H_
diff --git a/src/cobalt/network/local_network_test.cc b/src/cobalt/network/local_network_test.cc
new file mode 100644
index 0000000..025e04d
--- /dev/null
+++ b/src/cobalt/network/local_network_test.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/network/local_network.h"
+
+#include "base/logging.h"
+#include "cobalt/network/socket_address_parser.h"
+#include "googleurl/src/url_parse.h"
+#include "starboard/socket.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace network {
+
+template <size_t N>
+SbSocketAddress ParseSocketAddress(const char(&address)[N]) {
+  const char* spec = address;
+  url_parse::Component component = url_parse::MakeRange(0, N - 1);
+  SbSocketAddress out_socket_address;
+  SbMemorySet(&out_socket_address, 0, sizeof(SbSocketAddress));
+  CHECK(ParseSocketAddress(spec, component, &out_socket_address));
+  return out_socket_address;
+}
+
+TEST(IsPrivateRange, v4) {
+  EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("10.0.0.1")));
+  EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("172.16.1.1")));
+  EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("192.168.1.1")));
+  EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("127.0.0.1")));
+  EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("143.195.170.1")));
+  EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("0.0.0.0")));
+  EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("239.255.255.255")));
+}
+
+#if SB_HAS(IPV6)
+
+TEST(IsPrivateRange, v6) {
+  EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("[fd00::]")));
+  EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("[fd00:1:2:3:4:5::]")));
+  EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("[fe80::]")));
+  EXPECT_FALSE(IsIPInPrivateRange(
+      ParseSocketAddress("[2606:2800:220:1:248:1893:25c8:1946]")));
+}
+
+#endif  // SB_HAS(IPV6)
+
+}  // namespace network
+}  // namespace cobalt
diff --git a/src/cobalt/network/network.gyp b/src/cobalt/network/network.gyp
index e350e3b..d6296e3 100644
--- a/src/cobalt/network/network.gyp
+++ b/src/cobalt/network/network.gyp
@@ -23,10 +23,14 @@
       'target_name': 'network',
       'type': 'static_library',
       'sources': [
+        'socket_address_parser.cc',
+        'socket_address_parser.h',
         'cookie_jar_impl.cc',
         'cookie_jar_impl.h',
         'net_poster.cc',
         'net_poster.h',
+        'local_network.cc',
+        'local_network.h',
         'network_delegate.cc',
         'network_delegate.h',
         'network_event.h',
@@ -103,6 +107,7 @@
       'target_name': 'network_test',
       'type': '<(gtest_target_type)',
       'sources': [
+        'local_network_test.cc',
         'persistent_cookie_store_test.cc',
         'user_agent_string_factory_test.cc',
       ],
diff --git a/src/cobalt/network/network_delegate.cc b/src/cobalt/network/network_delegate.cc
index 39ff273..2755569 100644
--- a/src/cobalt/network/network_delegate.cc
+++ b/src/cobalt/network/network_delegate.cc
@@ -14,7 +14,10 @@
 
 #include "cobalt/network/network_delegate.h"
 
+#include "cobalt/network/local_network.h"
+#include "cobalt/network/socket_address_parser.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_util.h"
 
 namespace cobalt {
 namespace network {
@@ -40,14 +43,50 @@
   bool require_https = require_https_;
 #endif
 
+  const GURL& url = request->url();
   if (!require_https) {
     return net::OK;
-  } else if (request->url().SchemeIsSecure() ||
-             request->url().SchemeIs("data")) {
+  } else if (url.SchemeIsSecure() || url.SchemeIs("data")) {
     return net::OK;
-  } else {
-    return net::ERR_DISALLOWED_URL_SCHEME;
   }
+
+  if (!url.is_valid() || url.is_empty()) {
+    return net::ERR_INVALID_ARGUMENT;
+  }
+
+  const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
+
+  if (!url.has_host() || !parsed.host.is_valid() ||
+      !parsed.host.is_nonempty()) {
+    return net::ERR_INVALID_ARGUMENT;
+  }
+
+  const std::string& valid_spec = url.possibly_invalid_spec();
+  const char* valid_spec_cstr = valid_spec.c_str();
+
+  std::string host;
+#if SB_HAS(IPV6)
+  host = url.HostNoBrackets();
+#else
+  host.append(valid_spec_cstr + parsed.host.begin,
+              valid_spec_cstr + parsed.host.begin + parsed.host.len);
+#endif
+  if (net::IsLocalhost(host)) {
+    return net::OK;
+  }
+
+  SbSocketAddress destination;
+  // Note that ParseSocketAddress will only pass if host is a numeric IP.
+  if (!cobalt::network::ParseSocketAddress(valid_spec_cstr, parsed.host,
+                                           &destination)) {
+    return net::ERR_INVALID_ARGUMENT;
+  }
+
+  if (IsIPInPrivateRange(destination) || IsIPInLocalNetwork(destination)) {
+    return net::OK;
+  }
+
+  return net::ERR_DISALLOWED_URL_SCHEME;
 }
 
 int NetworkDelegate::OnBeforeSendHeaders(
diff --git a/src/cobalt/network/socket_address_parser.cc b/src/cobalt/network/socket_address_parser.cc
new file mode 100644
index 0000000..6241682
--- /dev/null
+++ b/src/cobalt/network/socket_address_parser.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/network/socket_address_parser.h"
+
+#include "base/logging.h"
+#include "googleurl/src/url_canon.h"
+#include "googleurl/src/url_canon_ip.h"
+#include "net/base/net_util.h"
+#include "starboard/memory.h"
+
+namespace cobalt {
+namespace network {
+
+bool ParseSocketAddress(const char* spec,
+                        const url_parse::Component& host_component,
+                        SbSocketAddress* out_socket_address) {
+  DCHECK(out_socket_address);
+
+  unsigned char address_v4[net::kIPv4AddressSize];
+  int num_ipv4_components = 0;
+
+  // IPv4AddressToNumber will return either IPV4, BROKEN, or NEUTRAL.  If the
+  // input IP address is IPv6, then NEUTRAL will be returned, and a different
+  // function will be used to covert the hostname to IPv6 address.
+  // If the return value is IPV4, then address will be in network byte order.
+  url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber(
+      spec, host_component, address_v4, &num_ipv4_components);
+
+  switch (family) {
+    case url_canon::CanonHostInfo::IPV4:
+      if (num_ipv4_components != net::kIPv4AddressSize) {
+        return false;
+      }
+
+      SbMemorySet(out_socket_address, 0, sizeof(SbSocketAddress));
+      out_socket_address->type = kSbSocketAddressTypeIpv4;
+      DCHECK_GE(sizeof(address_v4),
+                static_cast<std::size_t>(num_ipv4_components));
+      SbMemoryCopy(out_socket_address->address, address_v4,
+                   num_ipv4_components);
+
+      return true;
+    case url_canon::CanonHostInfo::NEUTRAL:
+#if SB_HAS(IPV6)
+      unsigned char address_v6[net::kIPv6AddressSize];
+      if (!url_canon::IPv6AddressToNumber(spec, host_component, address_v6)) {
+        break;
+      }
+
+      SbMemorySet(out_socket_address, 0, sizeof(SbSocketAddress));
+      out_socket_address->type = kSbSocketAddressTypeIpv6;
+      COMPILE_ASSERT(sizeof(address_v6), kIPv6AddressLength);
+      SbMemoryCopy(out_socket_address->address, address_v6, sizeof(address_v6));
+      return true;
+#else
+      return false;
+#endif
+    case url_canon::CanonHostInfo::BROKEN:
+      break;
+    case url_canon::CanonHostInfo::IPV6:
+      NOTREACHED() << "Invalid return value from IPv4AddressToNumber.";
+      break;
+    default:
+      NOTREACHED() << "Unexpected return value from IPv4AddressToNumber: "
+                   << family;
+      break;
+  }
+
+  return false;
+}
+
+}  // namespace network
+}  // namespace cobalt
diff --git a/src/cobalt/network/socket_address_parser.h b/src/cobalt/network/socket_address_parser.h
new file mode 100644
index 0000000..091c450
--- /dev/null
+++ b/src/cobalt/network/socket_address_parser.h
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_NETWORK_SOCKET_ADDRESS_PARSER_H_
+#define COBALT_NETWORK_SOCKET_ADDRESS_PARSER_H_
+
+#include "googleurl/src/url_parse.h"
+#include "starboard/socket.h"
+
+namespace cobalt {
+namespace network {
+
+bool ParseSocketAddress(const char* spec,
+                        const url_parse::Component& host_component,
+                        SbSocketAddress* out_socket_address);
+}  // namespace network
+}  // namespace cobalt
+
+#endif  // COBALT_NETWORK_SOCKET_ADDRESS_PARSER_H_
diff --git a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
index f37e90e..867e7ab 100644
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
@@ -32,6 +32,9 @@
     case kSbSystemDeviceTypeOverTheTopBox:
     case kSbSystemDeviceTypeSetTopBox:
     case kSbSystemDeviceTypeTV:
+#if SB_API_VERSION >= 4
+    case kSbSystemDeviceTypeAndroidTV:
+#endif  // SB_API_VERSION >= 4
       return true;
     case kSbSystemDeviceTypeDesktopPC:
     case kSbSystemDeviceTypeUnknown:
@@ -69,6 +72,14 @@
       youtube_tv_info_->network_operator = value;
     }
 
+#if SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+    result = SbSystemGetProperty(kSbSystemPropertyUserAgentAuxField, value,
+                                 kSystemPropertyMaxLength);
+    if (result) {
+      aux_field_ = value;
+    }
+#endif  // SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+
     // Device Type
     switch (device_type) {
       case kSbSystemDeviceTypeBlueRayDiskPlayer:
@@ -86,11 +97,11 @@
       case kSbSystemDeviceTypeTV:
         youtube_tv_info_->device_type = YouTubeTVInfo::kTV;
         break;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
       case kSbSystemDeviceTypeAndroidTV:
         youtube_tv_info_->device_type = YouTubeTVInfo::kAndroidTV;
         break;
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
       case kSbSystemDeviceTypeDesktopPC:
       default:
         youtube_tv_info_->device_type = YouTubeTVInfo::kInvalidDeviceType;
diff --git a/src/cobalt/network/user_agent_string_factory.cc b/src/cobalt/network/user_agent_string_factory.cc
index b0c0639..7ad310b 100644
--- a/src/cobalt/network/user_agent_string_factory.cc
+++ b/src/cobalt/network/user_agent_string_factory.cc
@@ -73,6 +73,11 @@
         youtube_tv_info_->brand.c_str(), youtube_tv_info_->model.c_str(),
         CreateConnectionTypeString().c_str());
   }
+
+  if (!aux_field_.empty()) {
+    user_agent.append(" ");
+    user_agent.append(aux_field_);
+  }
   return user_agent;
 }
 
@@ -107,10 +112,10 @@
       return "STB";
     case YouTubeTVInfo::kTV:
       return "TV";
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
     case YouTubeTVInfo::kAndroidTV:
       return "ATV";
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
     case YouTubeTVInfo::kInvalidDeviceType:
     default:
       NOTREACHED();
diff --git a/src/cobalt/network/user_agent_string_factory.h b/src/cobalt/network/user_agent_string_factory.h
index d9e1b6e..5869855 100644
--- a/src/cobalt/network/user_agent_string_factory.h
+++ b/src/cobalt/network/user_agent_string_factory.h
@@ -42,13 +42,14 @@
   std::string os_name_and_version_;
   std::string architecture_tokens_;
   std::string starboard_version_;
+  std::string aux_field_;
 
   struct YouTubeTVInfo {
     enum DeviceType {
       kInvalidDeviceType,
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
       kAndroidTV,
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
       kBlueRayDiskPlayer,
       kGameConsole,
       kOverTheTopBox,
diff --git a/src/cobalt/render_tree/font_provider.h b/src/cobalt/render_tree/font_provider.h
index 9df5a9c..5dc1a48 100644
--- a/src/cobalt/render_tree/font_provider.h
+++ b/src/cobalt/render_tree/font_provider.h
@@ -22,10 +22,16 @@
 namespace render_tree {
 
 // The FontProvider class is an abstract base class representing a collection of
-// fonts with a matching size and style, which provides fonts for any given
+// fonts with a matching style and size, which provides fonts for any given
 // character based upon what it considers to be the best match.
 class FontProvider {
  public:
+  // The style of the fonts contained within the collection.
+  virtual const render_tree::FontStyle& style() const = 0;
+
+  // The size of the fonts contained within the collection.
+  virtual float size() const = 0;
+
   // Returns the font-glyph combination that the FontProvider considers to be
   // the best match for the passed in character. The returned font is guaranteed
   // to be non-NULL. However, the glyph index may be set to |kInvalidGlyphIndex|
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h
index 4e583f0..2c126e6 100644
--- a/src/cobalt/render_tree/map_to_mesh_filter.h
+++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -15,8 +15,13 @@
 #ifndef COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
 #define COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
 
+#include <map>
+#include <utility>
+
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/math/size.h"
 #include "cobalt/render_tree/mesh.h"
 
 namespace cobalt {
@@ -37,41 +42,94 @@
 };
 
 // A MapToMeshFilter can be used to map source content onto a 3D mesh, within a
-// specified well-defined viewport. A null mesh indicates the equirectangular
-// mesh should be used.
+// specified well-defined viewport.
 class MapToMeshFilter {
  public:
-  MapToMeshFilter(StereoMode stereo_mode,
-                  scoped_refptr<render_tree::Mesh> left_eye_mesh,
-                  scoped_refptr<render_tree::Mesh> right_eye_mesh = NULL)
-      : stereo_mode_(stereo_mode),
-        left_eye_mesh_(left_eye_mesh),
-        right_eye_mesh_(right_eye_mesh) {
-    DCHECK(left_eye_mesh_);
+  struct Builder {
+    Builder() {}
+    void SetDefaultMeshes(
+        const scoped_refptr<render_tree::Mesh>& left_eye_mesh,
+        const scoped_refptr<render_tree::Mesh>& right_eye_mesh) {
+      left_eye_default_mesh_ = left_eye_mesh;
+      right_eye_default_mesh_ = right_eye_mesh;
+    }
+    void AddResolutionMatchedMeshes(
+        math::Size resolution,
+        const scoped_refptr<render_tree::Mesh>& left_eye_mesh,
+        const scoped_refptr<render_tree::Mesh>& right_eye_mesh) {
+      DCHECK_GT(resolution.width(), 0);
+      DCHECK_GT(resolution.height(), 0);
+      DCHECK(left_eye_mesh);
+      resolution_matched_meshes_[resolution] =
+          std::make_pair(left_eye_mesh, right_eye_mesh);
+    }
+    const scoped_refptr<render_tree::Mesh>& left_eye_mesh(
+        math::Size resolution) const {
+      MeshMap::const_iterator match =
+          resolution_matched_meshes_.find(resolution);
+      if (match != resolution_matched_meshes_.end()) {
+        return match->second.first;
+      }
+      return left_eye_default_mesh_;
+    }
+    const scoped_refptr<render_tree::Mesh>& right_eye_mesh(
+        math::Size resolution) const {
+      MeshMap::const_iterator match =
+          resolution_matched_meshes_.find(resolution);
+      if (match != resolution_matched_meshes_.end()) {
+        return match->second.second;
+      }
+      return right_eye_default_mesh_;
+    }
+
+   private:
+    struct SizeLessThan {
+      bool operator()(const math::Size& a, const math::Size& b) const {
+        return a.width() < b.width() ||
+               (a.width() == b.width() && a.height() < b.height());
+      }
+    };
+    typedef std::pair<scoped_refptr<render_tree::Mesh>,
+                      scoped_refptr<render_tree::Mesh> > MeshPair;
+    typedef std::map<math::Size, MeshPair, SizeLessThan> MeshMap;
+    MeshMap resolution_matched_meshes_;
+    scoped_refptr<render_tree::Mesh> left_eye_default_mesh_;
+    scoped_refptr<render_tree::Mesh> right_eye_default_mesh_;
+  };
+
+  MapToMeshFilter(StereoMode stereo_mode, const Builder& builder)
+      : stereo_mode_(stereo_mode), data_(builder) {
+    DCHECK(left_eye_mesh());
     if (stereo_mode == kLeftRightUnadjustedTextureCoords) {
       // This stereo mode implies there are two meshes.
-      DCHECK(left_eye_mesh_ && right_eye_mesh_);
+      DCHECK(right_eye_mesh());
     }
   }
 
   StereoMode stereo_mode() const { return stereo_mode_; }
 
-  const scoped_refptr<render_tree::Mesh>& mono_mesh() const {
-    return left_eye_mesh_;
+  // The omission of the |resolution| parameter will yield the default
+  // meshes in each of the following functions (by failing to match the
+  // invalid resolution).
+  const scoped_refptr<render_tree::Mesh>& mono_mesh(
+      math::Size resolution = InvalidSize()) const {
+    return data_.left_eye_mesh(resolution);
   }
 
-  const scoped_refptr<render_tree::Mesh>& left_eye_mesh() const {
-    return left_eye_mesh_;
+  const scoped_refptr<render_tree::Mesh>& left_eye_mesh(
+      math::Size resolution = InvalidSize()) const {
+    return data_.left_eye_mesh(resolution);
   }
 
-  const scoped_refptr<render_tree::Mesh>& right_eye_mesh() const {
-    return right_eye_mesh_;
+  const scoped_refptr<render_tree::Mesh>& right_eye_mesh(
+      math::Size resolution = InvalidSize()) const {
+    return data_.right_eye_mesh(resolution);
   }
 
  private:
+  static math::Size InvalidSize() { return math::Size(-1, -1); }
   StereoMode stereo_mode_;
-  scoped_refptr<render_tree::Mesh> left_eye_mesh_;
-  scoped_refptr<render_tree::Mesh> right_eye_mesh_;
+  Builder data_;
 };
 
 }  // namespace render_tree
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
index 76edce2..1d13b6c 100644
--- a/src/cobalt/render_tree/mock_resource_provider.h
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -24,6 +24,7 @@
 #include "cobalt/render_tree/font_provider.h"
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/render_tree/typeface.h"
 
@@ -77,6 +78,10 @@
                render_tree::Mesh*(std::vector<render_tree::Mesh::Vertex>*,
                                   render_tree::Mesh::DrawMode));
 
+  MOCK_METHOD1(DrawOffscreenImage,
+               scoped_refptr<render_tree::Image>(
+                   const scoped_refptr<render_tree::Node>& root));
+
   scoped_ptr<ImageData> AllocateImageData(const math::Size& size,
                                           PixelFormat pixel_format,
                                           AlphaFormat alpha_format) {
@@ -87,18 +92,27 @@
     return scoped_refptr<Image>(CreateImageMock(pixel_data.get()));
   }
 
-#if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
+
+#if SB_API_VERSION >= 3
   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)
+#endif  // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() {
+    return NULL;
+  }
+#elif SB_API_VERSION >= 3
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
+#endif  // SB_API_VERSION >= 4
+
+#endif  // SB_HAS(GRAPHICS)
 
   scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
                                                     size_t alignment) {
@@ -117,7 +131,7 @@
         GetLocalTypefaceMock(font_family_name, font_style));
   }
   scoped_refptr<Typeface> GetLocalTypefaceByFaceNameIfAvailable(
-      const std::string& font_face_name) {
+      const char* font_face_name) {
     return scoped_refptr<Typeface>(
         GetLocalTypefaceIfAvailableMock(font_face_name));
   }
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index 817ccd3..aa716d7 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -25,6 +25,7 @@
 #include "cobalt/render_tree/glyph_buffer.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/mesh.h"
+#include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/typeface.h"
 #if defined(STARBOARD)
 #include "starboard/decode_target.h"
@@ -74,22 +75,30 @@
   virtual scoped_refptr<Image> CreateImage(
       scoped_ptr<ImageData> pixel_data) = 0;
 
-#if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3
   // 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;
 
+  // Whether SbDecodeTargetIsSupported or not.
+  virtual bool SupportsSbDecodeTarget() = 0;
+#endif  // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+  // Return the SbDecodeTargetGraphicsContextProvider associated with the
+  // ResourceProvider, if it exists.  Returns NULL if SbDecodeTarget is not
+  // supported.
+  virtual SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() = 0;
+#elif SB_API_VERSION >= 3
   // 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)
+#endif  // SB_HAS(GRAPHICS)
 
   // Returns a raw chunk of memory that can later be passed into a function like
   // CreateMultiPlaneImageFromRawMemory() in order to create a texture.
@@ -134,7 +143,7 @@
   // Font's typeface (aka face name) is combination of a style and a font
   // family.  Font's style consists of weight, and a slant (but not size).
   virtual scoped_refptr<Typeface> GetLocalTypefaceByFaceNameIfAvailable(
-      const std::string& font_face_name) = 0;
+      const char* font_face_name) = 0;
 
   // Given a UTF-32 character, a set of typeface information, and a language,
   // this method returns the best-fit locally available fallback typeface that
@@ -206,6 +215,9 @@
   virtual scoped_refptr<Mesh> CreateMesh(
       scoped_ptr<std::vector<Mesh::Vertex> > vertices,
       Mesh::DrawMode draw_mode) = 0;
+
+  virtual scoped_refptr<Image> DrawOffscreenImage(
+      const scoped_refptr<render_tree::Node>& root) = 0;
 };
 
 }  // namespace render_tree
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index e1e85cf..84658d8 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -212,19 +212,26 @@
   scoped_refptr<Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE {
     NOTREACHED();
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
     SbDecodeTargetDestroy(decode_target);
-#else   // SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // 4
     SbDecodeTargetRelease(decode_target);
-#endif  // SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // 4
     return NULL;
   }
 
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-
   bool SupportsSbDecodeTarget() OVERRIDE { return false; }
 #endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+    return NULL;
+  }
+#elif SB_API_VERSION >= 3
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+#endif  // SB_API_VERSION >= 4
+
   scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
                                                     size_t alignment) OVERRIDE {
     return scoped_ptr<RawImageMemory>(
@@ -252,7 +259,7 @@
   }
 
   scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
-      const std::string& font_face_name) OVERRIDE {
+      const char* font_face_name) OVERRIDE {
     UNREFERENCED_PARAMETER(font_face_name);
     return make_scoped_refptr(new TypefaceStub(NULL));
   }
@@ -316,6 +323,12 @@
       render_tree::Mesh::DrawMode draw_mode) OVERRIDE {
     return new MeshStub(vertices.Pass(), draw_mode);
   }
+
+  scoped_refptr<Image> DrawOffscreenImage(
+      const scoped_refptr<render_tree::Node>& root) OVERRIDE {
+    UNREFERENCED_PARAMETER(root);
+    return scoped_refptr<Image>(NULL);
+  }
 };
 
 }  // namespace render_tree
diff --git a/src/cobalt/renderer/animations_test.cc b/src/cobalt/renderer/animations_test.cc
index 8a4e153..1cdda44 100644
--- a/src/cobalt/renderer/animations_test.cc
+++ b/src/cobalt/renderer/animations_test.cc
@@ -141,7 +141,7 @@
 // in this case, but it really should be working, as the media engine
 // relies on this mechanism to deliver responsive video with minimal frame
 // drops.
-TEST(AnimationsTest, FreshlyCreatedImagesCanBeUsedInAnimations) {
+TEST(AnimationsTest, DISABLED_FreshlyCreatedImagesCanBeUsedInAnimations) {
   scoped_ptr<backend::GraphicsSystem> graphics_system =
       backend::CreateDefaultGraphicsSystem();
   scoped_ptr<backend::GraphicsContext> graphics_context =
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.cc b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
index 19255d7..2bdc05c 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.cc
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
@@ -36,7 +36,9 @@
 }
 
 SurfaceRenderTargetBlitter::~SurfaceRenderTargetBlitter() {
-  SbBlitterDestroySurface(surface_);
+  if (surface_ != kSbBlitterInvalidSurface) {
+    SbBlitterDestroySurface(surface_);
+  }
 }
 
 const math::Size& SurfaceRenderTargetBlitter::GetSize() { return size_; }
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.h b/src/cobalt/renderer/backend/blitter/surface_render_target.h
index b8237a1..1a19e4f 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.h
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.h
@@ -36,6 +36,15 @@
 
   SbBlitterSurface GetSbSurface() const { return surface_; }
 
+  // Returns and gives up ownership of the surface. After this is called, the
+  // SurfaceRenderTargetBlitter's internal surface will be invalid and so the
+  // object itself becomes invalid.
+  SbBlitterSurface TakeSbSurface() {
+    SbBlitterSurface original_surface_ = surface_;
+    surface_ = kSbBlitterInvalidSurface;
+    return original_surface_;
+  }
+
   void Flip() OVERRIDE {}
 
  private:
diff --git a/src/cobalt/renderer/backend/egl/display.cc b/src/cobalt/renderer/backend/egl/display.cc
index 4111f71..c0603ca 100644
--- a/src/cobalt/renderer/backend/egl/display.cc
+++ b/src/cobalt/renderer/backend/egl/display.cc
@@ -52,6 +52,7 @@
   surface_ = eglCreateWindowSurface(display_, config_, native_window_, NULL);
   CHECK_EQ(EGL_SUCCESS, eglGetError());
 
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   // Configure the surface to preserve contents on swap.
   EGLBoolean surface_attrib_set =
       eglSurfaceAttrib(display_, surface_,
@@ -61,6 +62,7 @@
   // set the error condition.
   content_preserved_on_swap_ =
       eglGetError() == EGL_SUCCESS && surface_attrib_set == EGL_TRUE;
+#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 
   // Query and cache information about the surface now that we have created it.
   EGLint egl_surface_width;
diff --git a/src/cobalt/renderer/backend/egl/egl_backend.gypi b/src/cobalt/renderer/backend/egl/egl_backend.gypi
index 3cb7d12..e0d840c 100644
--- a/src/cobalt/renderer/backend/egl/egl_backend.gypi
+++ b/src/cobalt/renderer/backend/egl/egl_backend.gypi
@@ -18,6 +18,7 @@
     'display.h',
     'framebuffer.h',
     'framebuffer.cc',
+    'framebuffer_render_target.h',
     'graphics_context.cc',
     'graphics_context.h',
     'graphics_system.cc',
@@ -38,7 +39,9 @@
     'utils.cc',
     'utils.h',
   ],
-
+  'defines': [
+    'COBALT_EGL_SWAP_INTERVAL=<(cobalt_egl_swap_interval)',
+  ],
   'dependencies': [
     '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
   ],
@@ -46,4 +49,11 @@
     '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
   ],
 
+  'conditions': [
+    ['render_dirty_region_only==1', {
+      'defines': [
+        'COBALT_RENDER_DIRTY_REGION_ONLY',
+      ],
+    }],
+  ],
 }
diff --git a/src/cobalt/renderer/backend/egl/framebuffer.cc b/src/cobalt/renderer/backend/egl/framebuffer.cc
index 16ce201..068da37 100644
--- a/src/cobalt/renderer/backend/egl/framebuffer.cc
+++ b/src/cobalt/renderer/backend/egl/framebuffer.cc
@@ -56,13 +56,7 @@
   // Create and attach a depth buffer if requested.
   depthbuffer_handle_ = 0;
   if (depth_format != GL_NONE) {
-    GL_CALL(glGenRenderbuffers(1, &depthbuffer_handle_));
-    GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_handle_));
-    GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, depth_format, size_.width(),
-        size_.height()));
-    GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-        GL_RENDERBUFFER, depthbuffer_handle_));
-    GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+    CreateDepthAttachment(depth_format);
   }
 
   // Verify the framebuffer object is valid.
@@ -70,6 +64,21 @@
   GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
 }
 
+void FramebufferEGL::EnsureDepthBufferAttached(GLenum depth_format) {
+  if (depthbuffer_handle_ == 0) {
+    GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
+        graphics_context_);
+
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle_));
+    CreateDepthAttachment(depth_format);
+
+    // Verify the framebuffer object is valid.
+    DCHECK_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
+              GL_FRAMEBUFFER_COMPLETE);
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+  }
+}
+
 FramebufferEGL::~FramebufferEGL() {
   GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context_);
   GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle_));
@@ -78,6 +87,16 @@
   }
 }
 
+void FramebufferEGL::CreateDepthAttachment(GLenum depth_format) {
+  GL_CALL(glGenRenderbuffers(1, &depthbuffer_handle_));
+  GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_handle_));
+  GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, depth_format, size_.width(),
+      size_.height()));
+  GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+      GL_RENDERBUFFER, depthbuffer_handle_));
+  GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+}
+
 }  // namespace backend
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/backend/egl/framebuffer.h b/src/cobalt/renderer/backend/egl/framebuffer.h
index d0f6839..20ffd68 100644
--- a/src/cobalt/renderer/backend/egl/framebuffer.h
+++ b/src/cobalt/renderer/backend/egl/framebuffer.h
@@ -39,6 +39,10 @@
 
   const math::Size& GetSize() const { return size_; }
 
+  // Ensure the framebuffer has a depth buffer. If not, then create and
+  // attach one with the specified format.
+  void EnsureDepthBufferAttached(GLenum depth_format);
+
   // Return the color attachment for the framebuffer as a texture.
   TextureEGL* GetColorTexture() const { return color_texture_.get(); }
 
@@ -46,6 +50,8 @@
   GLuint gl_handle() const { return framebuffer_handle_; }
 
  private:
+  void CreateDepthAttachment(GLenum depth_format);
+
   GraphicsContextEGL* graphics_context_;
   math::Size size_;
 
diff --git a/src/cobalt/renderer/backend/egl/framebuffer_render_target.h b/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
new file mode 100644
index 0000000..bdd6a10
--- /dev/null
+++ b/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_BACKEND_EGL_FRAMEBUFFER_RENDER_TARGET_H_
+#define COBALT_RENDERER_BACKEND_EGL_FRAMEBUFFER_RENDER_TARGET_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/math/size.h"
+#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
+
+namespace cobalt {
+namespace renderer {
+namespace backend {
+
+// A framebuffer object wrapped in a RenderTarget interface. This allows
+// an easier path to reading pixels from the render target.
+class FramebufferRenderTargetEGL : public RenderTargetEGL {
+ public:
+  FramebufferRenderTargetEGL(GraphicsContextEGL* graphics_context,
+                             const math::Size& size)
+      : framebuffer_(
+            new FramebufferEGL(graphics_context, size, GL_RGBA, GL_NONE)) {}
+
+  const math::Size& GetSize() OVERRIDE { return framebuffer_->GetSize(); }
+
+  // This render target does not have an EGLSurface.
+  EGLSurface GetSurface() const OVERRIDE { return EGL_NO_SURFACE; }
+
+  // This handle is suitable for use with glBindFramebuffer.
+  intptr_t GetPlatformHandle() OVERRIDE { return framebuffer_->gl_handle(); }
+
+  // Create a depth buffer for the render target if it doesn't already have one.
+  void EnsureDepthBufferAttached(GLenum depth_format) {
+    framebuffer_->EnsureDepthBufferAttached(depth_format);
+  }
+
+  // Get the color texture attachment of the framebuffer.
+  TextureEGL* GetColorTexture() const {
+    return framebuffer_->GetColorTexture();
+  }
+
+ private:
+  ~FramebufferRenderTargetEGL() OVERRIDE {}
+
+  scoped_ptr<FramebufferEGL> framebuffer_;
+};
+
+}  // namespace backend
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_BACKEND_EGL_FRAMEBUFFER_RENDER_TARGET_H_
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc
index 574ddd4..7b8b9bd 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -17,9 +17,13 @@
 
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 
+#include "base/debug/leak_annotations.h"
 #include "base/debug/trace_event.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/graphics_system.h"
+#include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
 #include "cobalt/renderer/backend/egl/texture.h"
 #include "cobalt/renderer/backend/egl/texture_data.h"
 #include "cobalt/renderer/backend/egl/utils.h"
@@ -69,6 +73,13 @@
   bgra_format_supported_ = HasExtension("GL_EXT_texture_format_BGRA8888");
 
   SetupBlitObjects();
+
+  {
+    // The current mesa egl drivers leak memory on the first call to glDraw*.
+    // Get that first draw out of the way, and do something useful with it.
+    ANNOTATE_SCOPED_MEMORY_LEAK;
+    ComputeReadPixelsNeedVerticalFlip();
+  }
 }
 
 GraphicsSystemEGL* GraphicsContextEGL::system_egl() {
@@ -94,13 +105,11 @@
 
   ScopedMakeCurrent scoped_make_current(this);
 
-  scoped_refptr<RenderTarget> render_target = CreateOffscreenRenderTarget(
-      math::Size(kDummyTextureWidth, kDummyTextureHeight));
-  scoped_refptr<PBufferRenderTargetEGL> render_target_egl = make_scoped_refptr(
-      base::polymorphic_downcast<PBufferRenderTargetEGL*>(render_target.get()));
-  {
-    ScopedMakeCurrent scoped_make_current(this, render_target_egl);
+  scoped_ptr<FramebufferEGL> framebuffer(new FramebufferEGL(this,
+      math::Size(kDummyTextureWidth, kDummyTextureHeight), GL_RGBA, GL_NONE));
+  GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_handle()));
 
+  {
     // Create a 2-pixel texture and then immediately blit it to our 2-pixel
     // framebuffer render target.
     GLuint texture;
@@ -135,23 +144,11 @@
   }
 
   // Now read back the texture data using glReadPixels().
-  scoped_ptr<TextureEGL> render_target_texture(
-      new TextureEGL(this, render_target_egl));
-
-  GLuint framebuffer;
-  GL_CALL(glGenFramebuffers(1, &framebuffer));
-  GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer));
-
-  GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                 GL_TEXTURE_2D,
-                                 render_target_texture->gl_handle(), 0));
-
-  uint32_t out_data[2];
+  uint32_t out_data[kDummyTextureWidth * kDummyTextureHeight];
   GL_CALL(glReadPixels(0, 0, kDummyTextureWidth, kDummyTextureHeight, GL_RGBA,
                        GL_UNSIGNED_BYTE, out_data));
 
   GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
-  GL_CALL(glDeleteFramebuffers(1, &framebuffer));
 
   // Ensure the data is in one of two possible states, flipped or not flipped.
   DCHECK((out_data[0] == 0x00000000 && out_data[1] == 0xFFFFFFFF) ||
@@ -243,7 +240,29 @@
       "Use ReleaseCurrentContext().";
 
   EGLSurface egl_surface = surface->GetSurface();
-  EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+  if (egl_surface != EGL_NO_SURFACE) {
+    // This render target is not a frame buffer object. It has an EGLSurface
+    // that can be bound using eglMakeCurrent.
+    DCHECK_EQ(surface->GetPlatformHandle(), 0);
+
+    EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+
+    // Minimize calls to glBindFramebuffer. Normally, nothing keeps their
+    // framebuffer object bound, so 0 is normally bound at this point --
+    // unless the previous MakeCurrentWithSurface bound a framebuffer object.
+    if (current_surface_ && current_surface_->GetPlatformHandle() != 0) {
+      GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+    }
+  } else {
+    // This is a framebuffer object, and it does not have an EGL surface. It
+    // must be bound using glBindFramebuffer. Use the null surface's EGLSurface
+    // with eglMakeCurrent to avoid polluting the previous EGLSurface target.
+    DCHECK_NE(surface->GetPlatformHandle(), 0);
+
+    egl_surface = null_surface_->GetSurface();
+    EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, surface->GetPlatformHandle()));
+  }
 
   if (surface->IsWindowRenderTarget() && !surface->has_been_made_current()) {
     SecurityClear();
@@ -254,11 +273,26 @@
   current_surface_ = surface;
 }
 
+void GraphicsContextEGL::ResetCurrentSurface() {
+  if (is_current_ && current_surface_) {
+    EGLSurface egl_surface = current_surface_->GetSurface();
+    if (egl_surface == EGL_NO_SURFACE) {
+      DCHECK_NE(current_surface_->GetPlatformHandle(), 0);
+      egl_surface = null_surface_->GetSurface();
+    }
+
+    EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
+        current_surface_->GetPlatformHandle()));
+  }
+}
+
 void GraphicsContextEGL::MakeCurrent() {
   MakeCurrentWithSurface(null_surface_);
 }
 
 void GraphicsContextEGL::ReleaseCurrentContext() {
+  GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
   EGL_CALL(eglMakeCurrent(
       display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
 
@@ -292,6 +326,15 @@
   return render_target;
 }
 
+scoped_refptr<RenderTarget>
+  GraphicsContextEGL::CreateDownloadableOffscreenRenderTarget(
+      const math::Size& dimensions) {
+  scoped_refptr<RenderTarget> render_target(new FramebufferRenderTargetEGL(
+      this, dimensions));
+
+  return render_target;
+}
+
 void GraphicsContextEGL::InitializeDebugContext() {
   ComputeReadPixelsNeedVerticalFlip();
 }
@@ -314,40 +357,54 @@
     const scoped_refptr<RenderTarget>& render_target) {
   TRACE_EVENT0("cobalt::renderer",
                "GraphicsContextEGL::DownloadPixelDataAsRGBA()");
-
-  PBufferRenderTargetEGL* pbuffer_render_target =
-      base::polymorphic_downcast<PBufferRenderTargetEGL*>(render_target.get());
-
-  scoped_ptr<TextureEGL> texture(new TextureEGL(this, pbuffer_render_target));
-
   ScopedMakeCurrent scoped_current_context(this);
 
-  // This shouldn't be strictly necessary as glReadPixels() should implicitly
-  // call glFinish(), however it doesn't hurt to be safe and guard against
-  // potentially different implementations.  Performance is not an issue
-  // in this function, because it is only used by tests to verify rendered
-  // output.
-  GL_CALL(glFinish());
-
-  GLuint texture_framebuffer;
-  GL_CALL(glGenFramebuffers(1, &texture_framebuffer));
-  GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, texture_framebuffer));
-
-  GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                 GL_TEXTURE_2D, texture->gl_handle(), 0));
-  DCHECK_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
-
   int pitch_in_bytes =
-      texture->GetSize().width() * BytesPerPixelForGLFormat(GL_RGBA);
-
+      render_target->GetSize().width() * BytesPerPixelForGLFormat(GL_RGBA);
   scoped_array<uint8_t> pixels(
-      new uint8_t[texture->GetSize().height() * pitch_in_bytes]);
-  GL_CALL(glReadPixels(0, 0, texture->GetSize().width(),
-                       texture->GetSize().height(), GL_RGBA, GL_UNSIGNED_BYTE,
-                       pixels.get()));
+      new uint8_t[render_target->GetSize().height() * pitch_in_bytes]);
 
-  GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
-  GL_CALL(glDeleteFramebuffers(1, &texture_framebuffer));
+  if (render_target->GetPlatformHandle() == 0) {
+    // Need to bind the PBufferSurface to a framebuffer object in order to
+    // read its pixels.
+    PBufferRenderTargetEGL* pbuffer_render_target =
+        base::polymorphic_downcast<PBufferRenderTargetEGL*>
+            (render_target.get());
+
+    scoped_ptr<TextureEGL> texture(new TextureEGL(this, pbuffer_render_target));
+    DCHECK(texture->GetSize() == render_target->GetSize());
+
+    // This shouldn't be strictly necessary as glReadPixels() should implicitly
+    // call glFinish(), however it doesn't hurt to be safe and guard against
+    // potentially different implementations.  Performance is not an issue
+    // in this function, because it is only used by tests to verify rendered
+    // output.
+    GL_CALL(glFinish());
+
+    GLuint texture_framebuffer;
+    GL_CALL(glGenFramebuffers(1, &texture_framebuffer));
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, texture_framebuffer));
+
+    GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                   GL_TEXTURE_2D, texture->gl_handle(), 0));
+    DCHECK_EQ(GL_FRAMEBUFFER_COMPLETE,
+              glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+    GL_CALL(glReadPixels(0, 0, texture->GetSize().width(),
+                         texture->GetSize().height(), GL_RGBA, GL_UNSIGNED_BYTE,
+                         pixels.get()));
+
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+    GL_CALL(glDeleteFramebuffers(1, &texture_framebuffer));
+  } else {
+    // The render target is a framebuffer object, so just bind it and read.
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
+                              render_target->GetPlatformHandle()));
+    GL_CALL(glReadPixels(0, 0, render_target->GetSize().width(),
+                         render_target->GetSize().height(), GL_RGBA,
+                         GL_UNSIGNED_BYTE, pixels.get()));
+    GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+  }
 
   // Vertically flip the resulting pixel data before returning so that the 0th
   // pixel is at the top-left.  While computing this is not a fast procedure,
@@ -359,7 +416,7 @@
     // to return already flipped pixels.  So in that case, we flip them again
     // before returning here.
     VerticallyFlipPixels(pixels.get(), pitch_in_bytes,
-                         texture->GetSize().height());
+                         render_target->GetSize().height());
   }
 
   return pixels.Pass();
@@ -404,13 +461,19 @@
 void GraphicsContextEGL::SwapBuffers(RenderTargetEGL* surface) {
   TRACE_EVENT0("cobalt::renderer", "GraphicsContextEGL::SwapBuffers()");
 
-  eglSwapBuffers(display_, surface->GetSurface());
-  EGLint swap_err = eglGetError();
-  if (swap_err != EGL_SUCCESS) {
-    LOG(WARNING) << "Marking surface bad after swap error "
-                 << std::hex << swap_err;
-    surface->set_surface_bad();
-    return;
+  // SwapBuffers should have no effect for offscreen render targets. The
+  // current implementation of eglSwapBuffers() does nothing for PBuffers,
+  // so only check for framebuffer render targets.
+  if (surface->GetPlatformHandle() == 0) {
+    eglSwapInterval(display_, COBALT_EGL_SWAP_INTERVAL);
+    eglSwapBuffers(display_, surface->GetSurface());
+    EGLint swap_err = eglGetError();
+    if (swap_err != EGL_SUCCESS) {
+      LOG(WARNING) << "Marking surface bad after swap error "
+                   << std::hex << swap_err;
+      surface->set_surface_bad();
+      return;
+    }
   }
 
   surface->increment_swap_count();
@@ -423,9 +486,16 @@
 }
 
 void GraphicsContextEGL::SecurityClear() {
+#if defined(COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR)
   // Clear the screen to a color that is bright and gross to exaggerate that
   // this is a problem if it is witnessed.
   GL_CALL(glClearColor(1.0f, 0.4f, 1.0f, 1.0f));
+#else
+  // In release builds we certainly still want to perform this security
+  // clear, but should a poor user actually encounter it, we don't need
+  // to shock them with an ugly pink color.
+  GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
+#endif  // defined(COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR)
   GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
 }
 
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.h b/src/cobalt/renderer/backend/egl/graphics_context.h
index 487b33d..6ed533f 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.h
+++ b/src/cobalt/renderer/backend/egl/graphics_context.h
@@ -55,6 +55,9 @@
   scoped_refptr<RenderTarget> CreateOffscreenRenderTarget(
       const math::Size& dimensions) OVERRIDE;
 
+  scoped_refptr<RenderTarget> CreateDownloadableOffscreenRenderTarget(
+      const math::Size& dimensions) OVERRIDE;
+
   void InitializeDebugContext() OVERRIDE;
 
   scoped_array<uint8_t> DownloadPixelDataAsRGBA(
@@ -100,6 +103,9 @@
   // specified as a surface.
   void MakeCurrentWithSurface(RenderTargetEGL* surface);
 
+  // If this context is current, then forcefully rebind its current surface.
+  void ResetCurrentSurface();
+
   // Alternatively, this call can be made to make the context current along
   // with a null surface.  You would be interested in this method if you don't
   // plan to be making any draw calls, such as if you're setting up a texture.
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.cc b/src/cobalt/renderer/backend/egl/graphics_system.cc
index 0c357f5..86a40d1 100644
--- a/src/cobalt/renderer/backend/egl/graphics_system.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_system.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/renderer/backend/egl/graphics_system.h"
 
+#include "base/debug/leak_annotations.h"
 #if defined(ENABLE_GLIMP_TRACING)
 #include "base/debug/trace_event.h"
 #endif
@@ -58,13 +59,22 @@
   display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
   CHECK_NE(EGL_NO_DISPLAY, display_);
   CHECK_EQ(EGL_SUCCESS, eglGetError());
-  EGL_CALL(eglInitialize(display_, NULL, NULL));
+
+  {
+    // Despite eglTerminate() being used in the destructor, the current
+    // mesa egl drivers still leak memory.
+    ANNOTATE_SCOPED_MEMORY_LEAK;
+    EGL_CALL(eglInitialize(display_, NULL, NULL));
+  }
 
   // Setup our configuration to support RGBA and compatibility with PBuffer
   // objects (for offscreen rendering).
   EGLint attribute_list[] = {EGL_SURFACE_TYPE,    // this must be first
-                             EGL_WINDOW_BIT | EGL_PBUFFER_BIT |
-                                EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
+                             EGL_WINDOW_BIT | EGL_PBUFFER_BIT
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+                                 | EGL_SWAP_BEHAVIOR_PRESERVED_BIT
+#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+                             ,
                              EGL_RED_SIZE,
                              8,
                              EGL_GREEN_SIZE,
@@ -79,19 +89,21 @@
 #endif
                              EGL_RENDERABLE_TYPE,
                              EGL_OPENGL_ES2_BIT,
-#if defined(COBALT_FORCE_CUSTOM_RASTERIZER)
+#if defined(COBALT_RASTERIZER_USES_DEPTH_BUFFER)
                              EGL_DEPTH_SIZE,
                              16,
 #endif
                              EGL_NONE};
 
+  EGLint num_configs;
+  eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs);
+
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   // Try to allow preservation of the frame contents between swap calls --
   // this will allow rendering of only parts of the frame that have changed.
   DCHECK_EQ(EGL_SURFACE_TYPE, attribute_list[0]);
   EGLint& surface_type_value = attribute_list[1];
 
-  EGLint num_configs;
-  eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs);
   if (eglGetError() != EGL_SUCCESS || num_configs == 0) {
     // Swap buffer preservation may not be supported. Try to find a config
     // without the feature.
@@ -99,6 +111,8 @@
     EGL_CALL(
         eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs));
   }
+#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+
   DCHECK_EQ(1, num_configs);
 
 #if defined(GLES3_SUPPORTED)
diff --git a/src/cobalt/renderer/backend/egl/texture.cc b/src/cobalt/renderer/backend/egl/texture.cc
index 49f83b1..ce9cd43 100644
--- a/src/cobalt/renderer/backend/egl/texture.cc
+++ b/src/cobalt/renderer/backend/egl/texture.cc
@@ -17,7 +17,11 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include "base/bind.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/resource_context.h"
 #include "cobalt/renderer/backend/egl/texture_data.h"
 #include "cobalt/renderer/backend/egl/texture_data_cpu.h"
@@ -27,6 +31,11 @@
 namespace renderer {
 namespace backend {
 
+namespace {
+void DoNothing() {
+}
+}  // namespace
+
 TextureEGL::TextureEGL(GraphicsContextEGL* graphics_context,
                        scoped_ptr<TextureDataEGL> texture_source_data,
                        bool bgra_supported)
@@ -62,7 +71,7 @@
 
 TextureEGL::TextureEGL(
     GraphicsContextEGL* graphics_context,
-    const scoped_refptr<PBufferRenderTargetEGL>& render_target)
+    const scoped_refptr<RenderTargetEGL>& render_target)
     : graphics_context_(graphics_context),
       size_(render_target->GetSize()),
       format_(GL_RGBA),
@@ -71,27 +80,51 @@
 
   source_render_target_ = render_target;
 
-  // First we create the OpenGL texture object and maintain a handle to it.
-  GL_CALL(glGenTextures(1, &gl_handle_));
-  GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_handle_));
-  SetupInitialTextureParameters();
+  if (render_target->GetSurface() != EGL_NO_SURFACE) {
+    // This is a PBufferRenderTargetEGL. Need to bind a texture to the surface.
+    const PBufferRenderTargetEGL* pbuffer_target =
+        base::polymorphic_downcast<const PBufferRenderTargetEGL*>
+            (render_target.get());
 
-  // This call attaches the EGL PBuffer object to the currently bound OpenGL
-  // texture object, effectively allowing the PBO render target to be used
-  // as a texture by referencing gl_handle_ from now on.
-  EGL_CALL(eglBindTexImage(render_target->display(),
-                           render_target->GetSurface(),
-                           EGL_BACK_BUFFER));
+    // First we create the OpenGL texture object and maintain a handle to it.
+    GL_CALL(glGenTextures(1, &gl_handle_));
+    GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_handle_));
+    SetupInitialTextureParameters();
 
-  GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+    // This call attaches the EGL PBuffer object to the currently bound OpenGL
+    // texture object, effectively allowing the PBO render target to be used
+    // as a texture by referencing gl_handle_ from now on.
+    EGL_CALL(eglBindTexImage(pbuffer_target->display(),
+                             pbuffer_target->GetSurface(),
+                             EGL_BACK_BUFFER));
+
+    GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+  } else {
+    // This is a FramebufferRenderTargetEGL. Wrap its color texture attachment.
+    const FramebufferRenderTargetEGL* framebuffer_target =
+        base::polymorphic_downcast<const FramebufferRenderTargetEGL*>
+            (render_target.get());
+
+    const TextureEGL* color_attachment = framebuffer_target->GetColorTexture();
+    format_ = color_attachment->GetFormat();
+    target_ = color_attachment->GetTarget();
+    gl_handle_ = color_attachment->gl_handle();
+
+    // Do not destroy the wrapped texture. Let the render target do that.
+    delete_function_ = base::Bind(&DoNothing);
+  }
 }
 
 TextureEGL::~TextureEGL() {
   GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context_);
 
-  if (source_render_target_) {
-    EGL_CALL(eglReleaseTexImage(source_render_target_->display(),
-                                source_render_target_->GetSurface(),
+  if (source_render_target_ &&
+      source_render_target_->GetSurface() != EGL_NO_SURFACE) {
+    const PBufferRenderTargetEGL* pbuffer_target =
+        base::polymorphic_downcast<const PBufferRenderTargetEGL*>
+            (source_render_target_.get());
+    EGL_CALL(eglReleaseTexImage(pbuffer_target->display(),
+                                pbuffer_target->GetSurface(),
                                 EGL_BACK_BUFFER));
   }
 
diff --git a/src/cobalt/renderer/backend/egl/texture.h b/src/cobalt/renderer/backend/egl/texture.h
index b723a05..3da9d46 100644
--- a/src/cobalt/renderer/backend/egl/texture.h
+++ b/src/cobalt/renderer/backend/egl/texture.h
@@ -19,7 +19,7 @@
 
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
-#include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
 #include "cobalt/renderer/backend/egl/texture_data.h"
 
 namespace cobalt {
@@ -52,10 +52,9 @@
              const math::Size& size, GLenum format, GLenum target,
              const base::Closure& delete_function);
 
-  // Create a texture from a pre-existing offscreen PBuffer render target.
-  explicit TextureEGL(
-      GraphicsContextEGL* graphics_context,
-      const scoped_refptr<PBufferRenderTargetEGL>& render_target);
+  // Create a texture from a pre-existing offscreen render target.
+  TextureEGL(GraphicsContextEGL* graphics_context,
+             const scoped_refptr<RenderTargetEGL>& render_target);
   virtual ~TextureEGL();
 
   const math::Size& GetSize() const { return size_; }
@@ -86,7 +85,7 @@
 
   // If the texture was constructed from a render target, we keep a reference
   // to the render target.
-  scoped_refptr<PBufferRenderTargetEGL> source_render_target_;
+  scoped_refptr<RenderTargetEGL> source_render_target_;
 
   // If non-null, will be called upon destruction instead of manually deleting
   // the texture via glDeleteTextures().
diff --git a/src/cobalt/renderer/backend/graphics_context.h b/src/cobalt/renderer/backend/graphics_context.h
index 2e97703..4504cd4 100644
--- a/src/cobalt/renderer/backend/graphics_context.h
+++ b/src/cobalt/renderer/backend/graphics_context.h
@@ -48,6 +48,14 @@
   virtual scoped_refptr<RenderTarget> CreateOffscreenRenderTarget(
       const math::Size& dimensions) = 0;
 
+  // Similar to CreateOffscreenRenderTarget, but this should be used when
+  // the target will be used with DownloadPixelDataAsRGBA. This render target
+  // is more compatible for reading.
+  virtual scoped_refptr<RenderTarget> CreateDownloadableOffscreenRenderTarget(
+      const math::Size& dimensions) {
+    return CreateOffscreenRenderTarget(dimensions);
+  }
+
   // Initializes any values that are needed in a debugging context.
   virtual void InitializeDebugContext() {}
 
diff --git a/src/cobalt/renderer/copy_font_data.gypi b/src/cobalt/renderer/copy_font_data.gypi
index 55ddeb3..971f550 100644
--- a/src/cobalt/renderer/copy_font_data.gypi
+++ b/src/cobalt/renderer/copy_font_data.gypi
@@ -18,205 +18,311 @@
 {
   'includes': [ '../build/contents_dir.gypi' ],
   'variables': {
-    'source_all_fonts_dir': '<(static_contents_source_dir)/fonts/all_fonts',
+    'source_font_files_dir': '<(static_contents_source_dir)/fonts/font_files',
+
+    'conditions': [
+      [ 'cobalt_font_package == "expanded"', {
+        'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+        'package_named_sans_serif': 4,
+        'package_named_serif': 4,
+        'package_named_fcc_fonts': 2,
+        'package_fallback_lang_non_cjk': 2,
+        'package_fallback_lang_cjk': 2,
+        'package_fallback_lang_cjk_low_quality': 0,
+        'package_fallback_lang_jp': 0,
+        'package_fallback_emoji': 1,
+        'package_fallback_symbols': 1,
+      }],
+
+      # 'unlimited' is deprecated but is mapped to 'standard'
+      [ 'cobalt_font_package == "standard" or cobalt_font_package == "unlimited"', {
+        'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+        'package_named_sans_serif': 3,
+        'package_named_serif': 3,
+        'package_named_fcc_fonts': 2,
+        'package_fallback_lang_non_cjk': 2,
+        'package_fallback_lang_cjk': 1,
+        'package_fallback_lang_cjk_low_quality': 0,
+        'package_fallback_lang_jp': 0,
+        'package_fallback_emoji': 1,
+        'package_fallback_symbols': 1,
+      }],
+
+      # '10megabytes' is deprecated but is mapped to 'limited_with_noto_jp'
+      [ 'cobalt_font_package == "limited_with_jp" or cobalt_font_package == "10megabytes"', {
+        'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+        'package_named_sans_serif': 2,
+        'package_named_serif': 0,
+        'package_named_fcc_fonts': 0,
+        'package_fallback_lang_non_cjk': 1,
+        'package_fallback_lang_cjk': 0,
+        'package_fallback_lang_cjk_low_quality': 1,
+        'package_fallback_lang_jp': 1,
+        'package_fallback_emoji': 1,
+        'package_fallback_symbols': 1,
+      }],
+
+      [ 'cobalt_font_package == "limited"', {
+        'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+        'package_named_sans_serif': 2,
+        'package_named_serif': 0,
+        'package_named_fcc_fonts': 0,
+        'package_fallback_lang_non_cjk': 1,
+        'package_fallback_lang_cjk': 0,
+        'package_fallback_lang_cjk_low_quality': 1,
+        'package_fallback_lang_jp': 0,
+        'package_fallback_emoji': 1,
+        'package_fallback_symbols': 1,
+      }],
+
+      [ 'cobalt_font_package == "minimal"', {
+        'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+        'package_named_sans_serif': 0,
+        'package_named_serif': 0,
+        'package_named_fcc_fonts': 0,
+        'package_fallback_lang_non_cjk': 0,
+        'package_fallback_lang_cjk': 0,
+        'package_fallback_lang_cjk_low_quality': 0,
+        'package_fallback_lang_jp': 0,
+        'package_fallback_emoji': 0,
+        'package_fallback_symbols': 0,
+      }],
+
+      [ 'cobalt_font_package == "android_system"', {
+        # fonts.xml contains a superset of what we expect to find on Android
+        # devices. The Android SbFile implementation falls back to system font
+        # files for those not in cobalt content.
+        'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/android',
+
+        # Emojis are currently included because Cobalt's version of Skia does
+        # not support Android's color emojis and will be removed when Skia is
+        # rebased.
+        'package_named_sans_serif': 0,
+        'package_named_serif': 0,
+        'package_named_fcc_fonts': 0,
+        'package_fallback_lang_non_cjk': 0,
+        'package_fallback_lang_cjk': 0,
+        'package_fallback_lang_cjk_low_quality': 0,
+        'package_fallback_lang_jp': 0,
+        'package_fallback_emoji': 1,
+        'package_fallback_symbols': 0,
+      }],
+
+      [ 'cobalt_font_package_override_named_sans_serif >= 0', {
+        'package_named_sans_serif': '<(cobalt_font_package_override_named_sans_serif)',
+      }],
+      [ 'cobalt_font_package_override_named_serif >= 0', {
+        'package_named_serif': '<(cobalt_font_package_override_named_serif)',
+      }],
+      [ 'cobalt_font_package_override_named_fcc_fonts >= 0', {
+        'package_named_fcc_fonts': '<(cobalt_font_package_override_named_fcc_fonts)',
+      }],
+      [ 'cobalt_font_package_override_fallback_lang_non_cjk >= 0', {
+        'package_fallback_lang_non_cjk': '<(cobalt_font_package_override_fallback_lang_non_cjk)',
+      }],
+      [ 'cobalt_font_package_override_fallback_lang_cjk >= 0', {
+        'package_fallback_lang_cjk': '<(cobalt_font_package_override_fallback_lang_cjk)',
+      }],
+      [ 'cobalt_font_package_override_fallback_lang_cjk_low_quality >= 0', {
+        'package_fallback_lang_cjk_low_quality': '<(cobalt_font_package_override_fallback_lang_cjk_low_quality)',
+      }],
+      [ 'cobalt_font_package_override_fallback_lang_jp >= 0', {
+        'package_fallback_lang_jp': '<(cobalt_font_package_override_fallback_lang_jp)',
+      }],
+      [ 'cobalt_font_package_override_fallback_emoji >= 0', {
+        'package_fallback_emoji': '<(cobalt_font_package_override_fallback_emoji)',
+      }],
+      [ 'cobalt_font_package_override_fallback_symbols >= 0', {
+        'package_fallback_symbols': '<(cobalt_font_package_override_fallback_symbols)',
+      }],
+    ],
   },
 
   'copies': [
     {
       'destination': '<(static_contents_output_data_dir)/fonts/',
       'files': [
-        '<(static_contents_source_dir)/fonts/<(cobalt_font_package)/fonts.xml',
+        '<(source_xml_file_dir)/fonts.xml',
       ],
       'conditions': [
-        [ 'cobalt_font_package == "android_system"', {
+        [ 'package_named_sans_serif == 0', {
           'files+': [
-            # Only include minimal fonts in cobalt content, but fonts.xml
-            # contains a superset of what we expect to find on Android devices.
-            # The Android SbFile implementation falls back to system font files
-            # for those not in cobalt content.
-            '<(source_all_fonts_dir)/Roboto-Regular-Subsetted.ttf',
-            '<(source_all_fonts_dir)/NotoEmoji-Regular.ttf',
+            '<(source_font_files_dir)/Roboto-Regular-Subsetted.ttf',
           ],
-        }], # android_system
-        [ 'cobalt_font_package == "minimal"', {
+        }],
+        [ 'package_named_sans_serif >= 1', {
           'files+': [
-            '<(source_all_fonts_dir)/Roboto-Regular-Subsetted.ttf',
+            '<(source_font_files_dir)/Roboto-Regular.ttf',
           ],
-        }], # minimal
-        [ 'cobalt_font_package == "10megabytes"', {
+        }],
+        [ 'package_named_sans_serif >= 2', {
           'files+': [
-            '<(source_all_fonts_dir)/Roboto-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoNaskhArabicUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansEthiopic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansHebrew-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansThaiUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansArmenian-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGeorgian-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansDevanagariUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGujaratiUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGurmukhiUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTamilUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMalayalamUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBengaliUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTeluguUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansKannadaUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansOriyaUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSinhala-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansKhmerUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLaoUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansThaana-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCham-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBalinese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBamum-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBatak-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBuginese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBuhid-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCanadianAboriginal-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCherokee-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCoptic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGlagolitic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansHanunoo-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansJavanese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansKayahLi-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLepcha-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLimbu-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLisu-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMandaic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMeeteiMayek-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansNewTaiLue-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansNKo-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansOlChiki-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansRejang-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSaurashtra-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSundanese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSylotiNagri-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSyriacEstrangela-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTagbanwa-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTaiTham-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTaiViet-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTibetan-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTifinagh-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansVai-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansYi-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted.ttf',
-            '<(source_all_fonts_dir)/NotoSansJP-Regular.otf',
-            '<(source_all_fonts_dir)/NotoEmoji-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted2.ttf',
-            '<(source_all_fonts_dir)/DroidSansFallback.ttf',
-            '<(source_all_fonts_dir)/NotoSansTaiLe-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMongolian-Regular.ttf',
+            '<(source_font_files_dir)/Roboto-Bold.ttf',
           ],
-        }], # 10megabytes
-        [ 'cobalt_font_package == "unlimited"', {
+        }],
+        [ 'package_named_sans_serif >= 3', {
           'files+': [
-            '<(source_all_fonts_dir)/Roboto-Thin.ttf',
-            '<(source_all_fonts_dir)/Roboto-ThinItalic.ttf',
-            '<(source_all_fonts_dir)/Roboto-Light.ttf',
-            '<(source_all_fonts_dir)/Roboto-LightItalic.ttf',
-            '<(source_all_fonts_dir)/Roboto-Regular.ttf',
-            '<(source_all_fonts_dir)/Roboto-Italic.ttf',
-            '<(source_all_fonts_dir)/Roboto-Medium.ttf',
-            '<(source_all_fonts_dir)/Roboto-MediumItalic.ttf',
-            '<(source_all_fonts_dir)/Roboto-Bold.ttf',
-            '<(source_all_fonts_dir)/Roboto-BoldItalic.ttf',
-            '<(source_all_fonts_dir)/Roboto-Black.ttf',
-            '<(source_all_fonts_dir)/Roboto-BlackItalic.ttf',
-            '<(source_all_fonts_dir)/NotoSerif-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSerif-Italic.ttf',
-            '<(source_all_fonts_dir)/NotoSerif-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSerif-BoldItalic.ttf',
-            '<(source_all_fonts_dir)/DroidSansMono.ttf',
-            '<(source_all_fonts_dir)/CutiveMono.ttf',
-            '<(source_all_fonts_dir)/ComingSoon.ttf',
-            '<(source_all_fonts_dir)/DancingScript-Regular.ttf',
-            '<(source_all_fonts_dir)/DancingScript-Bold.ttf',
-            '<(source_all_fonts_dir)/CarroisGothicSC-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoNaskhArabicUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoNaskhArabicUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansEthiopic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansEthiopic-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansHebrew-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansHebrew-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansThaiUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansThaiUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansArmenian-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansArmenian-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansGeorgian-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGeorgian-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansDevanagariUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansDevanagariUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansGujaratiUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGujaratiUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansGurmukhiUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGurmukhiUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansTamilUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTamilUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansMalayalamUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMalayalamUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansBengaliUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBengaliUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansTeluguUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTeluguUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansKannadaUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansKannadaUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansOriyaUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansOriyaUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansSinhala-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSinhala-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansKhmerUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansKhmerUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansLaoUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLaoUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansMyanmarUI-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMyanmarUI-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansThaana-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansThaana-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansCham-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCham-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansBalinese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBamum-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBatak-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBuginese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansBuhid-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCanadianAboriginal-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCherokee-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansCoptic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansGlagolitic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansHanunoo-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansJavanese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansKayahLi-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLepcha-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLimbu-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansLisu-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMandaic-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMeeteiMayek-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansNewTaiLue-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansNKo-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansOlChiki-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansRejang-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSaurashtra-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSundanese-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSylotiNagri-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSyriacEstrangela-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTagbanwa-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTaiTham-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTaiViet-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTibetan-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansTibetan-Bold.ttf',
-            '<(source_all_fonts_dir)/NotoSansTifinagh-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansVai-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansYi-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted.ttf',
-            '<(source_all_fonts_dir)/NotoSansSC-Regular.otf',
-            '<(source_all_fonts_dir)/NotoSansTC-Regular.otf',
-            '<(source_all_fonts_dir)/NotoSansJP-Regular.otf',
-            '<(source_all_fonts_dir)/NotoSansKR-Regular.otf',
-            '<(source_all_fonts_dir)/NotoEmoji-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted2.ttf',
-            '<(source_all_fonts_dir)/NotoSansTaiLe-Regular.ttf',
-            '<(source_all_fonts_dir)/NotoSansMongolian-Regular.ttf',
+            '<(source_font_files_dir)/Roboto-Italic.ttf',
+            '<(source_font_files_dir)/Roboto-BoldItalic.ttf',
           ],
-        }], # unlimited
+        }],
+        [ 'package_named_serif >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSerif-Regular.ttf',
+          ],
+        }],
+        [ 'package_named_serif >= 2', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSerif-Bold.ttf',
+          ],
+        }],
+        [ 'package_named_serif >= 3', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSerif-Italic.ttf',
+            '<(source_font_files_dir)/NotoSerif-BoldItalic.ttf',
+          ],
+        }],
+        [ 'package_named_fcc_fonts >= 1', {
+          'files+': [
+            # sans-serif-monospace
+            '<(source_font_files_dir)/DroidSansMono.ttf',
+            # serif-monospace
+            '<(source_font_files_dir)/CutiveMono.ttf',
+            # casual
+            '<(source_font_files_dir)/ComingSoon.ttf',
+            # cursive
+            '<(source_font_files_dir)/DancingScript-Regular.ttf',
+            # sans-serif-smallcaps
+            '<(source_font_files_dir)/CarroisGothicSC-Regular.ttf',
+          ],
+        }],
+        [ 'package_named_fcc_fonts >= 2', {
+          'files+': [
+            # cursive
+            '<(source_font_files_dir)/DancingScript-Bold.ttf',
+          ],
+        }],
+        [ 'package_fallback_lang_non_cjk >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/NotoNaskhArabicUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansArmenian-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansBalinese-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansBamum-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansBatak-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansBengaliUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansBuginese-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansBuhid-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansCanadianAboriginal-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansCham-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansCherokee-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansCoptic-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansDevanagariUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansEthiopic-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansGeorgian-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansGlagolitic-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansGujaratiUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansGurmukhiUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansHanunoo-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansHebrew-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansJavanese-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansKannadaUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansKayahLi-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansKhmerUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansLaoUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansLepcha-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansLimbu-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansLisu-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansMalayalamUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansMandaic-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansMeeteiMayek-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansMongolian-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansMyanmarUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansNewTaiLue-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansNKo-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansOlChiki-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansOriyaUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansRejang-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansSaurashtra-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansSinhala-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansSundanese-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansSylotiNagri-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansSyriacEstrangela-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTagbanwa-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTaiLe-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTaiTham-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTaiViet-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTamilUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTeluguUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansThaana-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansThaiUI-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTibetan-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansTifinagh-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansVai-Regular.ttf',
+            '<(source_font_files_dir)/NotoSansYi-Regular.ttf',
+          ],
+        }],
+        [ 'package_fallback_lang_non_cjk >= 2', {
+          'files+': [
+            '<(source_font_files_dir)/NotoNaskhArabicUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansArmenian-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansBengaliUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansCham-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansDevanagariUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansEthiopic-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansGeorgian-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansGujaratiUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansGurmukhiUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansHebrew-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansKannadaUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansKhmerUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansLaoUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansMalayalamUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansMyanmarUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansOriyaUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansSinhala-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansTamilUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansTeluguUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansThaana-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansThaiUI-Bold.ttf',
+            '<(source_font_files_dir)/NotoSansTibetan-Bold.ttf',
+          ],
+        }],
+        [ 'package_fallback_lang_cjk >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSansCJK-Regular.ttc',
+          ],
+        }],
+        [ 'package_fallback_lang_cjk >= 2', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSansCJK-Bold.ttc',
+          ],
+        }],
+        [ 'package_fallback_lang_cjk_low_quality >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/DroidSansFallback.ttf',
+          ],
+        }],
+        [ 'package_fallback_lang_jp >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSansJP-Regular.otf',
+          ],
+        }],
+        [ 'package_fallback_emoji >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/NotoEmoji-Regular.ttf',
+          ],
+        }],
+        [ 'package_fallback_symbols >= 1', {
+          'files+': [
+            '<(source_font_files_dir)/NotoSansSymbols-Regular-Subsetted.ttf',
+            '<(source_font_files_dir)/NotoSansSymbols-Regular-Subsetted2.ttf',
+          ],
+        }],
       ],
     },
   ],
diff --git a/src/cobalt/renderer/frame_rate_throttler.cc b/src/cobalt/renderer/frame_rate_throttler.cc
deleted file mode 100644
index 82cebd3..0000000
--- a/src/cobalt/renderer/frame_rate_throttler.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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/renderer/frame_rate_throttler.h"
-
-#include "base/threading/platform_thread.h"
-
-namespace cobalt {
-namespace renderer {
-
-void FrameRateThrottler::BeginInterval() {
-  if (COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS > 0) {
-    begin_time_ = base::TimeTicks::HighResNow();
-  }
-}
-
-void FrameRateThrottler::EndInterval() {
-  // Throttle presentation of new frames if a minimum frame time is specified.
-  if (COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS > 0) {
-    if (!begin_time_.is_null()) {
-      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - begin_time_;
-      base::TimeDelta wait_time =
-          base::TimeDelta::FromMillisecondsD(
-              COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS) -
-          elapsed;
-      if (wait_time > base::TimeDelta::FromMicroseconds(0)) {
-        base::PlatformThread::Sleep(wait_time);
-      }
-    }
-  }
-}
-
-}  // namespace renderer
-}  // namespace cobalt
diff --git a/src/cobalt/renderer/frame_rate_throttler.h b/src/cobalt/renderer/frame_rate_throttler.h
deleted file mode 100644
index 0a2e4c5..0000000
--- a/src/cobalt/renderer/frame_rate_throttler.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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_RENDERER_FRAME_RATE_THROTTLER_H_
-#define COBALT_RENDERER_FRAME_RATE_THROTTLER_H_
-
-#include "base/time.h"
-
-namespace cobalt {
-namespace renderer {
-
-// The FrameRateThrottler is used to enforce a minimum frame time. The
-// rasterizer should call the provided hooks just before and after submitting
-// a new frame. This can be used to throttle the frame rate (to 30 Hz for
-// example) when the presentation interval cannot be specified.
-class FrameRateThrottler {
- public:
-  // To be called just after submitting a new frame.
-  void BeginInterval();
-
-  // To be called just before submitting a new frame.
-  void EndInterval();
-
- private:
-  base::TimeTicks begin_time_;
-};
-
-}  // namespace renderer
-}  // namespace cobalt
-
-#endif  // COBALT_RENDERER_FRAME_RATE_THROTTLER_H_
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 2abe1a3..c70c3ee 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -26,6 +26,7 @@
 #include "cobalt/render_tree/brush.h"
 #include "cobalt/render_tree/dump_render_tree_to_string.h"
 #include "cobalt/render_tree/rect_node.h"
+#include "nb/memory_scope.h"
 
 using cobalt::render_tree::Node;
 using cobalt::render_tree::animations::AnimateNode;
@@ -178,6 +179,7 @@
 void Pipeline::RasterizeToRGBAPixels(
     const Submission& render_tree_submission,
     const RasterizationCompleteCallback& complete) {
+  TRACK_MEMORY_SCOPE("Renderer");
   TRACE_EVENT0("cobalt::renderer", "Pipeline::RasterizeToRGBAPixels()");
 
   if (MessageLoop::current() != rasterizer_thread_.message_loop()) {
@@ -189,7 +191,8 @@
   }
   // Create a new target that is the same dimensions as the display target.
   scoped_refptr<backend::RenderTarget> offscreen_target =
-      graphics_context_->CreateOffscreenRenderTarget(render_target_->GetSize());
+      graphics_context_->CreateDownloadableOffscreenRenderTarget(
+          render_target_->GetSize());
 
   // Rasterize this submission into the newly created target.
   RasterizeSubmissionToRenderTarget(render_tree_submission, offscreen_target);
@@ -214,7 +217,8 @@
     // platform does not rate limit itself during swaps. This was changed from
     // 15ms to accommodate 120fps requirements on some platforms.
     rasterize_timer_.emplace(
-        FROM_HERE, base::TimeDelta::FromMilliseconds(7),
+        FROM_HERE, base::TimeDelta::FromMillisecondsD(
+                       COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS),
         base::Bind(&Pipeline::RasterizeCurrentTree, base::Unretained(this)),
         true, true);
     rasterize_timer_->Reset();
@@ -235,6 +239,7 @@
 }
 
 void Pipeline::RasterizeCurrentTree() {
+  TRACK_MEMORY_SCOPE("Renderer");
   DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("cobalt::renderer", "Pipeline::RasterizeCurrentTree()");
 
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 3a7f257..8fab055 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -190,9 +190,6 @@
   // did not have active animations.
   bool last_render_animations_active_;
 
-  // Tracks whether or not animations are currently playing.
-  base::CVal<bool> has_active_animations_c_val_;
-
   // Timer tracking the amount of time spent in
   // |RasterizeSubmissionToRenderTarget| when the render tree has changed.
   // The tracking is flushed when the max count is hit.
@@ -202,6 +199,9 @@
   // tracking is flushed when the animations expire.
   base::CValCollectionTimerStats<base::CValDebug> rasterize_animations_timer_;
 
+  // Tracks whether or not animations are currently playing.
+  base::CVal<bool> has_active_animations_c_val_;
+
 #if defined(ENABLE_DEBUG_CONSOLE)
   // Dumps the current render tree to the console.
   base::ConsoleCommandManager::CommandHandler
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index c551f4c..a67044e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -16,6 +16,7 @@
 
 #include <string>
 
+#include "base/bind.h"
 #include "base/debug/trace_event.h"
 #include "base/threading/thread_checker.h"
 #if defined(ENABLE_DEBUG_CONSOLE)
@@ -24,7 +25,6 @@
 #include "cobalt/render_tree/resource_provider_stub.h"
 #include "cobalt/renderer/backend/blitter/graphics_context.h"
 #include "cobalt/renderer/backend/blitter/render_target.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
 #include "cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h"
 #include "cobalt/renderer/rasterizer/blitter/render_state.h"
 #include "cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h"
@@ -43,6 +43,7 @@
 class HardwareRasterizer::Impl {
  public:
   explicit Impl(backend::GraphicsContext* graphics_context,
+                int skia_atlas_width, int skia_atlas_height,
                 int scratch_surface_size_in_bytes,
                 int surface_cache_size_in_bytes,
                 int software_surface_cache_size_in_bytes);
@@ -52,14 +53,19 @@
               const scoped_refptr<backend::RenderTarget>& render_target,
               const Options& options);
 
+  void SubmitOffscreenToRenderTarget(
+      const scoped_refptr<render_tree::Node>& render_tree,
+      const scoped_refptr<backend::RenderTarget>& render_target);
+
   render_tree::ResourceProvider* GetResourceProvider();
 
  private:
 #if defined(ENABLE_DEBUG_CONSOLE)
   void OnToggleHighlightSoftwareDraws(const std::string& message);
 #endif
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   void SetupLastFrameSurface(int width, int height);
-
+#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   base::ThreadChecker thread_checker_;
 
   backend::GraphicsContextBlitter* context_;
@@ -80,8 +86,6 @@
   CachedSoftwareRasterizer software_surface_cache_;
   LinearGradientCache linear_gradient_cache_;
 
-  FrameRateThrottler frame_rate_throttler_;
-
 #if defined(ENABLE_DEBUG_CONSOLE)
   // Debug command to toggle cache highlights to help visualize which nodes
   // are being cached.
@@ -92,6 +96,7 @@
 };
 
 HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+                               int skia_atlas_width, int skia_atlas_height,
                                int scratch_surface_size_in_bytes,
                                int surface_cache_size_in_bytes,
                                int software_surface_cache_size_in_bytes)
@@ -118,9 +123,12 @@
           "scene software rasterization is occurring.")
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 {
-  resource_provider_ = scoped_ptr<render_tree::ResourceProvider>(
-      new ResourceProvider(context_->GetSbBlitterDevice(),
-                           software_surface_cache_.GetResourceProvider()));
+  resource_provider_ =
+      scoped_ptr<render_tree::ResourceProvider>(new ResourceProvider(
+          context_->GetSbBlitterDevice(),
+          software_surface_cache_.GetResourceProvider(),
+          base::Bind(&HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget,
+                     base::Unretained(this))));
 
   if (surface_cache_size_in_bytes > 0) {
     surface_cache_delegate_.emplace(context_->GetSbBlitterDevice(),
@@ -131,7 +139,11 @@
   }
 }
 
-HardwareRasterizer::Impl::~Impl() { SbBlitterDestroySurface(current_frame_); }
+HardwareRasterizer::Impl::~Impl() {
+  if (SbBlitterIsSurfaceValid(current_frame_)) {
+    SbBlitterDestroySurface(current_frame_);
+  }
+}
 
 #if defined(ENABLE_DEBUG_CONSOLE)
 void HardwareRasterizer::Impl::OnToggleHighlightSoftwareDraws(
@@ -146,6 +158,7 @@
     const scoped_refptr<backend::RenderTarget>& render_target,
     const Options& options) {
   TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
+  DCHECK(thread_checker_.CalledOnValidThread());
 
   int width = render_target->GetSize().width();
   int height = render_target->GetSize().height();
@@ -156,12 +169,19 @@
       base::polymorphic_downcast<backend::RenderTargetBlitter*>(
           render_target.get());
 
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
   if (!SbBlitterIsSurfaceValid(current_frame_)) {
     SetupLastFrameSurface(width, height);
   }
+#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 
-  CHECK(SbBlitterSetRenderTarget(
-      context, SbBlitterGetRenderTargetFromSurface(current_frame_)));
+  SbBlitterRenderTarget visitor_render_target = kSbBlitterInvalidRenderTarget;
+  if (SbBlitterIsSurfaceValid(current_frame_)) {
+    visitor_render_target = SbBlitterGetRenderTargetFromSurface(current_frame_);
+  } else {
+    visitor_render_target = render_target_blitter->GetSbRenderTarget();
+  }
+  CHECK(SbBlitterSetRenderTarget(context, visitor_render_target));
 
   // Update our surface cache to do per-frame calculations such as deciding
   // which render tree nodes are candidates for caching in this upcoming
@@ -191,14 +211,15 @@
     // Visit the render tree with our Blitter API visitor.
     BoundsStack start_bounds(context_->GetSbBlitterContext(),
                              math::Rect(render_target_blitter->GetSize()));
-    if (options.dirty && !cleared) {
+
+    if (SbBlitterIsSurfaceValid(current_frame_) && options.dirty && !cleared) {
       // If a dirty rectangle was specified, limit our redrawing to within it.
       start_bounds.Push(*options.dirty);
     }
 
-    RenderState initial_render_state(
-        SbBlitterGetRenderTargetFromSurface(current_frame_), Transform(),
-        start_bounds);
+    RenderState initial_render_state(visitor_render_target, Transform(),
+                                     start_bounds);
+
 #if defined(ENABLE_DEBUG_CONSOLE)
     initial_render_state.highlight_software_draws =
         toggle_highlight_software_draws_;
@@ -212,37 +233,74 @@
     render_tree->Accept(&visitor);
   }
 
-  // Finally flip the surface to make visible the rendered results.
-  CHECK(SbBlitterSetRenderTarget(context,
-                                 render_target_blitter->GetSbRenderTarget()));
-  CHECK(SbBlitterSetBlending(context, false));
-  CHECK(SbBlitterSetModulateBlitsWithColor(context, false));
-  CHECK(SbBlitterBlitRectToRect(context, current_frame_,
-                                SbBlitterMakeRect(0, 0, width, height),
-                                SbBlitterMakeRect(0, 0, width, height)));
+  if (SbBlitterIsSurfaceValid(current_frame_)) {
+    // Finally flip the surface to make visible the rendered results.
+    CHECK(SbBlitterSetRenderTarget(context,
+                                   render_target_blitter->GetSbRenderTarget()));
+    CHECK(SbBlitterSetBlending(context, false));
+    CHECK(SbBlitterSetModulateBlitsWithColor(context, false));
+    CHECK(SbBlitterBlitRectToRect(context, current_frame_,
+                                  SbBlitterMakeRect(0, 0, width, height),
+                                  SbBlitterMakeRect(0, 0, width, height)));
+  }
+
   CHECK(SbBlitterFlushContext(context));
-  frame_rate_throttler_.EndInterval();
   render_target_blitter->Flip();
-  frame_rate_throttler_.BeginInterval();
 
   ++submit_count_;
 }
 
+void HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget(
+    const scoped_refptr<render_tree::Node>& render_tree,
+    const scoped_refptr<backend::RenderTarget>& render_target) {
+  TRACE_EVENT0("cobalt::renderer",
+               "Rasterizer::SubmitOffscreenToRenderTarget()");
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  SbBlitterContext context = context_->GetSbBlitterContext();
+
+  backend::RenderTargetBlitter* render_target_blitter =
+      base::polymorphic_downcast<backend::RenderTargetBlitter*>(
+          render_target.get());
+
+  SbBlitterRenderTarget visitor_render_target =
+      render_target_blitter->GetSbRenderTarget();
+  CHECK(SbBlitterSetRenderTarget(context, visitor_render_target));
+
+  BoundsStack start_bounds(context_->GetSbBlitterContext(),
+                           math::Rect(render_target_blitter->GetSize()));
+
+  RenderState initial_render_state(visitor_render_target, Transform(),
+                                   start_bounds);
+
+  RenderTreeNodeVisitor visitor(
+      context_->GetSbBlitterDevice(), context_->GetSbBlitterContext(),
+      initial_render_state, NULL, NULL, NULL, NULL, NULL);
+  render_tree->Accept(&visitor);
+
+  CHECK(SbBlitterFlushContext(context));
+}
+
 render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
   return resource_provider_.get();
 }
 
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 void HardwareRasterizer::Impl::SetupLastFrameSurface(int width, int height) {
   current_frame_ =
       SbBlitterCreateRenderTargetSurface(context_->GetSbBlitterDevice(), width,
                                          height, kSbBlitterSurfaceFormatRGBA8);
 }
+#endif  // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
 
 HardwareRasterizer::HardwareRasterizer(
     backend::GraphicsContext* graphics_context,
+    int skia_atlas_width, int skia_atlas_height,
     int scratch_surface_size_in_bytes, int surface_cache_size_in_bytes,
     int software_surface_cache_size_in_bytes)
-    : impl_(new Impl(graphics_context, scratch_surface_size_in_bytes,
+    : impl_(new Impl(graphics_context,
+                     skia_atlas_width, skia_atlas_height,
+                     scratch_surface_size_in_bytes,
                      surface_cache_size_in_bytes,
                      software_surface_cache_size_in_bytes)) {}
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
index 9ae47e8..20afd3e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
@@ -16,6 +16,7 @@
 #define COBALT_RENDERER_RASTERIZER_BLITTER_HARDWARE_RASTERIZER_H_
 
 #include "base/memory/scoped_ptr.h"
+#include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/renderer/backend/graphics_context.h"
 #include "cobalt/renderer/backend/render_target.h"
@@ -36,6 +37,7 @@
 class HardwareRasterizer : public Rasterizer {
  public:
   explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
+                              int skia_atlas_width, int skia_atlas_height,
                               int scratch_surface_size_in_bytes,
                               int surface_cache_size_in_bytes,
                               int software_surface_cache_size_in_bytes);
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc
index b44ffdc..f51569e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -14,7 +14,9 @@
 
 #include "cobalt/renderer/rasterizer/blitter/image.h"
 
+#include "base/bind.h"
 #include "cobalt/render_tree/image.h"
+#include "cobalt/renderer/backend/blitter/surface_render_target.h"
 #include "cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h"
 #include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
 #include "cobalt/renderer/rasterizer/skia/image.h"
@@ -89,7 +91,25 @@
   size_ = math::Size(info.width, info.height);
 }
 
-bool SinglePlaneImage::EnsureInitialized() { return false; }
+SinglePlaneImage::SinglePlaneImage(
+    const scoped_refptr<render_tree::Node>& root,
+    SubmitOffscreenCallback submit_offscreen_callback, SbBlitterDevice device)
+    : size_(static_cast<int>(root->GetBounds().right()),
+            static_cast<int>(root->GetBounds().bottom())),
+      is_opaque_(false) {
+  initialize_image_ = base::Bind(
+      &SinglePlaneImage::InitializeImageFromRenderTree, base::Unretained(this),
+      root, submit_offscreen_callback, device);
+}
+
+bool SinglePlaneImage::EnsureInitialized() {
+  if (!initialize_image_.is_null()) {
+    initialize_image_.Run();
+    initialize_image_.Reset();
+    return true;
+  }
+  return false;
+}
 
 const SkBitmap* SinglePlaneImage::GetBitmap() const {
   // This function will only ever get called if the Skia software renderer needs
@@ -128,6 +148,18 @@
   }
 }
 
+void SinglePlaneImage::InitializeImageFromRenderTree(
+    const scoped_refptr<render_tree::Node>& root,
+    const SubmitOffscreenCallback& submit_offscreen_callback,
+    SbBlitterDevice device) {
+  scoped_refptr<backend::SurfaceRenderTargetBlitter> render_target(
+      new backend::SurfaceRenderTargetBlitter(device, size_));
+
+  submit_offscreen_callback.Run(root, render_target);
+  surface_ = render_target->TakeSbSurface();
+  CHECK(SbBlitterIsSurfaceValid(surface_));
+}
+
 }  // namespace blitter
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.h b/src/cobalt/renderer/rasterizer/blitter/image.h
index fefb1f0..6489d4f 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.h
+++ b/src/cobalt/renderer/rasterizer/blitter/image.h
@@ -26,6 +26,7 @@
 #include "cobalt/render_tree/font_provider.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/renderer/backend/blitter/render_target.h"
 #include "cobalt/renderer/rasterizer/skia/image.h"
 #include "starboard/blitter.h"
 
@@ -36,6 +37,10 @@
 namespace rasterizer {
 namespace blitter {
 
+typedef base::Callback<void(const scoped_refptr<render_tree::Node>& render_tree,
+                            const scoped_refptr<backend::RenderTarget>&
+                                render_target)> SubmitOffscreenCallback;
+
 // render_tree::ImageData objects are implemented in the Blitter API via the
 // SbBlitterPixelData objects, which is conceptually an exact match for
 // ImageData.
@@ -75,6 +80,10 @@
   SinglePlaneImage(SbBlitterSurface surface, bool is_opaque,
                    const base::Closure& delete_function);
 
+  SinglePlaneImage(const scoped_refptr<render_tree::Node>& root,
+                   SubmitOffscreenCallback submit_offscreen_callback,
+                   SbBlitterDevice device);
+
   const math::Size& GetSize() const OVERRIDE { return size_; }
 
   SbBlitterSurface surface() const { return surface_; }
@@ -92,6 +101,11 @@
  private:
   ~SinglePlaneImage() OVERRIDE;
 
+  void InitializeImageFromRenderTree(
+      const scoped_refptr<render_tree::Node>& root,
+      const SubmitOffscreenCallback& submit_offscreen_callback,
+      SbBlitterDevice device);
+
   math::Size size_;
   SbBlitterSurface surface_;
 
@@ -104,6 +118,10 @@
   // If |delete_function| is provided, it will be called in the destructor
   // instead of manually calling SbBlitterDestroySurface(surface_).
   base::Closure delete_function_;
+
+  // This closure binds the image construction parameters so that we can delay
+  // construction of it until it is accessed by the rasterizer thread.
+  base::Closure initialize_image_;
 };
 
 }  // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index 1cb1ac1..d1e161a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -61,6 +61,14 @@
         '<(DEPTH)/starboard/starboard.gyp:starboard',
         'common',
       ],
+
+      'conditions': [
+        ['render_dirty_region_only==1', {
+          'defines': [
+            'COBALT_RENDER_DIRTY_REGION_ONLY',
+          ],
+        }],
+      ],
     },
 
     # The Blitter software rasterizer uses Skia to rasterize render trees on
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
index b727b35..4a95847 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -28,6 +28,7 @@
 #include "cobalt/renderer/rasterizer/blitter/linear_gradient.h"
 #include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
 #include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
+#include "cobalt/renderer/rasterizer/common/utils.h"
 #include "starboard/blitter.h"
 
 #if SB_HAS(BLITTER)
@@ -111,27 +112,6 @@
   render_state_.transform.ApplyOffset(-composition_node->data().offset());
 }
 
-namespace {
-bool SourceCanRenderWithOpacity(render_tree::Node* source) {
-  if (source->GetTypeId() == base::GetTypeId<render_tree::ImageNode>() ||
-      source->GetTypeId() == base::GetTypeId<render_tree::RectNode>()) {
-    return true;
-  } else if (source->GetTypeId() ==
-             base::GetTypeId<render_tree::CompositionNode>()) {
-    // If we are a composition of valid sources, then we also allow
-    // rendering through a viewport here.
-    render_tree::CompositionNode* composition_node =
-        base::polymorphic_downcast<render_tree::CompositionNode*>(source);
-    typedef render_tree::CompositionNode::Children Children;
-    const Children& children = composition_node->data().children();
-    if (children.size() == 1 && SourceCanRenderWithOpacity(children[0].get())) {
-      return true;
-    }
-  }
-  return false;
-}
-}  // namespace
-
 void RenderTreeNodeVisitor::Visit(render_tree::FilterNode* filter_node) {
   TRACE_EVENT0_IF_ENABLED("Visit(FilterNode)");
 
@@ -170,7 +150,7 @@
     // we know that opacity is in the range (0, 1), exclusive.
     float opacity = filter_node->data().opacity_filter->opacity();
 
-    if (SourceCanRenderWithOpacity(source)) {
+    if (common::utils::NodeCanRenderWithOpacity(source)) {
       float original_opacity = render_state_.opacity;
       render_state_.opacity *= opacity;
 
@@ -237,6 +217,14 @@
   SinglePlaneImage* blitter_image =
       base::polymorphic_downcast<SinglePlaneImage*>(skia_image);
 
+  // Ensure any required backend processing is done to create the necessary
+  // GPU resource.
+  if (blitter_image->EnsureInitialized()) {
+    // Restore the original render target, since it is possible that rendering
+    // took place to another render target in this call.
+    SbBlitterSetRenderTarget(context_, render_state_.render_target);
+  }
+
   // Apply the local image coordinate transform to the source rectangle.  Note
   // that the render tree local transform matrix is normalized, but the Blitter
   // API source rectangle is specified in pixel units, so we must multiply the
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 7932be5..2315836 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -15,7 +15,6 @@
 #include "cobalt/renderer/rasterizer/blitter/resource_provider.h"
 
 #include "base/bind.h"
-#include "cobalt/renderer/rasterizer/blitter/image.h"
 #include "cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h"
 #include "starboard/blitter.h"
 
@@ -37,8 +36,15 @@
 
 ResourceProvider::ResourceProvider(
     SbBlitterDevice device,
-    render_tree::ResourceProvider* skia_resource_provider)
-    : device_(device), skia_resource_provider_(skia_resource_provider) {}
+    render_tree::ResourceProvider* skia_resource_provider,
+    SubmitOffscreenCallback submit_offscreen_callback)
+    : device_(device),
+      skia_resource_provider_(skia_resource_provider),
+      submit_offscreen_callback_(submit_offscreen_callback) {
+#if SB_API_VERSION >= 4
+  decode_target_graphics_context_provider_.device = device;
+#endif  // SB_API_VERSION >= 4
+}
 
 bool ResourceProvider::PixelFormatSupported(PixelFormat pixel_format) {
   return SbBlitterIsPixelFormatSupportedByPixelData(
@@ -77,7 +83,7 @@
 
 scoped_refptr<render_tree::Image>
 ResourceProvider::CreateImageFromSbDecodeTarget(SbDecodeTarget decode_target) {
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
   SbDecodeTargetFormat format = SbDecodeTargetGetFormat(decode_target);
   if (format == kSbDecodeTargetFormat1PlaneRGBA) {
     SbBlitterSurface surface =
@@ -90,7 +96,7 @@
     SbDecodeTargetDestroy(decode_target);
     return make_scoped_refptr(
         new SinglePlaneImage(surface, is_opaque, base::Closure()));
-#else   // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION < 4
   SbDecodeTargetInfo info;
   SbMemorySet(&info, 0, sizeof(info));
   CHECK(SbDecodeTargetGetInfo(decode_target, &info));
@@ -109,16 +115,16 @@
     return make_scoped_refptr(new SinglePlaneImage(
         plane.surface, info.is_opaque,
         base::Bind(&SbDecodeTargetRelease, decode_target)));
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
   }
 
   NOTREACHED()
       << "Only format kSbDecodeTargetFormat1PlaneRGBA is currently supported.";
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
   SbDecodeTargetDestroy(decode_target);
-#else   // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION < 4
   SbDecodeTargetRelease(decode_target);
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
   return NULL;
 }
 
@@ -151,7 +157,7 @@
 
 scoped_refptr<render_tree::Typeface>
 ResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
-    const std::string& font_face_name) {
+    const char* font_face_name) {
   return skia_resource_provider_->GetLocalTypefaceByFaceNameIfAvailable(
       font_face_name);
 }
@@ -201,6 +207,12 @@
   return scoped_refptr<render_tree::Mesh>(NULL);
 }
 
+scoped_refptr<render_tree::Image> ResourceProvider::DrawOffscreenImage(
+    const scoped_refptr<render_tree::Node>& root) {
+  return make_scoped_refptr(
+      new SinglePlaneImage(root, submit_offscreen_callback_, device_));
+}
+
 }  // namespace blitter
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index ed891df..3f01c64 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -23,6 +23,7 @@
 #include "cobalt/render_tree/font_provider.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/renderer/rasterizer/blitter/image.h"
 #include "starboard/blitter.h"
 #include "starboard/decode_target.h"
 
@@ -35,21 +36,28 @@
 
 class ResourceProvider : public render_tree::ResourceProvider {
  public:
-  explicit ResourceProvider(
-      SbBlitterDevice device,
-      render_tree::ResourceProvider* skia_resource_provider);
+  ResourceProvider(SbBlitterDevice device,
+                   render_tree::ResourceProvider* skia_resource_provider,
+                   SubmitOffscreenCallback submit_offscreen_callback);
   ~ResourceProvider() OVERRIDE {}
 
   void Finish() OVERRIDE {}
 
-#if SB_VERSION(3)
+#if SB_API_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)
+#endif  // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+    return &decode_target_graphics_context_provider_;
+  }
+#elif SB_API_VERSION >= 3
+  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+#endif  // SB_API_VERSION >= 4
 
   bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
   bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
@@ -74,7 +82,7 @@
       const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
 
   scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
-      const std::string& font_face_name) OVERRIDE;
+      const char* font_face_name) OVERRIDE;
 
   scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
       int32 utf32_character, render_tree::FontStyle font_style,
@@ -102,6 +110,9 @@
       scoped_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
       render_tree::Mesh::DrawMode draw_mode) OVERRIDE;
 
+  scoped_refptr<render_tree::Image> DrawOffscreenImage(
+      const scoped_refptr<render_tree::Node>& root) OVERRIDE;
+
  private:
   SbBlitterDevice device_;
 
@@ -109,6 +120,13 @@
   // ResourceProvider, as the Blitter API does not natively support font
   // rendering.
   render_tree::ResourceProvider* skia_resource_provider_;
+
+  SubmitOffscreenCallback submit_offscreen_callback_;
+
+#if SB_API_VERSION >= 4
+  SbDecodeTargetGraphicsContextProvider
+      decode_target_graphics_context_provider_;
+#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
index 207d3c6..f8e683c 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
@@ -93,9 +93,7 @@
 
   SbBlitterDestroySurface(surface);
 
-  frame_rate_throttler_.EndInterval();
   render_target_blitter->Flip();
-  frame_rate_throttler_.BeginInterval();
 }
 
 render_tree::ResourceProvider* SoftwareRasterizer::GetResourceProvider() {
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
index 85aabf6..b868493 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
@@ -24,7 +24,6 @@
 #include "cobalt/renderer/backend/blitter/graphics_context.h"
 #include "cobalt/renderer/backend/graphics_context.h"
 #include "cobalt/renderer/backend/render_target.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
 #include "cobalt/renderer/rasterizer/rasterizer.h"
 #include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
 
@@ -50,7 +49,6 @@
  private:
   backend::GraphicsContextBlitter* context_;
   skia::SoftwareRasterizer skia_rasterizer_;
-  FrameRateThrottler frame_rate_throttler_;
 };
 
 }  // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/common/common.gyp b/src/cobalt/renderer/rasterizer/common/common.gyp
index 3db2828..af0e808 100644
--- a/src/cobalt/renderer/rasterizer/common/common.gyp
+++ b/src/cobalt/renderer/rasterizer/common/common.gyp
@@ -27,6 +27,8 @@
         'streaming_best_fit_line.h',
         'surface_cache.cc',
         'surface_cache.h',
+        'utils.h',
+        'utils.cc',
       ],
 
       'dependencies': [
@@ -35,4 +37,4 @@
       ],
     },
   ],
-}
\ No newline at end of file
+}
diff --git a/src/cobalt/renderer/rasterizer/common/utils.cc b/src/cobalt/renderer/rasterizer/common/utils.cc
new file mode 100644
index 0000000..1a7d107
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/common/utils.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/common/utils.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/render_tree/composition_node.h"
+#include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/matrix_transform_node.h"
+#include "cobalt/render_tree/rect_node.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace common {
+namespace utils {
+
+bool NodeCanRenderWithOpacity(render_tree::Node* node) {
+  base::TypeId node_type = node->GetTypeId();
+
+  if (node_type == base::GetTypeId<render_tree::ImageNode>() ||
+      node_type == base::GetTypeId<render_tree::RectNode>()) {
+    return true;
+  } else if (node_type == base::GetTypeId<render_tree::MatrixTransformNode>()) {
+    render_tree::MatrixTransformNode* transform_node =
+        base::polymorphic_downcast<render_tree::MatrixTransformNode*>(node);
+    return NodeCanRenderWithOpacity(transform_node->data().source);
+  } else if (node_type == base::GetTypeId<render_tree::CompositionNode>()) {
+    // If we are a composition of non-overlapping valid children, then we can
+    // also be rendered directly onscreen. As a simplification, just check for
+    // the case of 1 valid child.
+    render_tree::CompositionNode* composition_node =
+        base::polymorphic_downcast<render_tree::CompositionNode*>(node);
+    const render_tree::CompositionNode::Children& children =
+        composition_node->data().children();
+    if (children.size() == 1) {
+      return NodeCanRenderWithOpacity(children[0]);
+    }
+  }
+
+  return false;
+}
+
+}  // namespace utils
+}  // namespace common
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/common/utils.h b/src/cobalt/renderer/rasterizer/common/utils.h
new file mode 100644
index 0000000..a9b17fd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/common/utils.h
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_COMMON_UTILS_H_
+#define COBALT_RENDERER_RASTERIZER_COMMON_UTILS_H_
+
+#include "cobalt/render_tree/node.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace common {
+namespace utils {
+
+// Returns whether the given node (or its child[ren]) can be rendered directly
+// onto the main render target with an opacity value. The alternative is that
+// the node should be rendered onto an offscreen render target, then that
+// target be rendered onscreen using opacity. The difference can best be seen
+// when |node| represents overlapping objects -- rendering them individually
+// with opacity will look different than rendering them fully opaque onto an
+// offscreen target, then rendering that target with opacity.
+bool NodeCanRenderWithOpacity(render_tree::Node* node);
+
+}  // namespace utils
+}  // namespace common
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_COMMON_UTILS_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc
new file mode 100644
index 0000000..dc6664c
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+
+#include <GLES2/gl2.h>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawDepthStencil::DrawDepthStencil(GraphicsState* graphics_state,
+    const BaseState& base_state, const math::RectF& include_scissor,
+    const math::RectF& exclude_scissor)
+    : DrawPolyColor(base_state),
+      include_scissor_first_vert_(-1),
+      exclude_scissor_first_vert_(-1) {
+  attributes_.reserve(MaxVertsNeededForStencil());
+  AddStencil(include_scissor, exclude_scissor);
+  graphics_state->ReserveVertexData(
+      attributes_.size() * sizeof(VertexAttributes));
+}
+
+DrawDepthStencil::DrawDepthStencil(const BaseState& base_state)
+    : DrawPolyColor(base_state),
+      include_scissor_first_vert_(-1),
+      exclude_scissor_first_vert_(-1) {
+}
+
+void DrawDepthStencil::ExecuteOnscreenRasterize(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  SetupShader(graphics_state, program_manager);
+  DrawStencil(graphics_state);
+}
+
+void DrawDepthStencil::UndoStencilState(GraphicsState* graphics_state) {
+  graphics_state->ResetDepthFunc();
+}
+
+void DrawDepthStencil::DrawStencil(GraphicsState* graphics_state) {
+  // This must occur during the transparency pass.
+  SB_DCHECK(graphics_state->IsBlendEnabled());
+  SB_DCHECK(!graphics_state->IsDepthWriteEnabled());
+  SB_DCHECK(graphics_state->IsDepthTestEnabled());
+
+  // Set depth of pixels in the scissor rects.
+  graphics_state->EnableDepthWrite();
+  if (exclude_scissor_first_vert_ >= 0) {
+    GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, exclude_scissor_first_vert_, 4));
+  }
+  SB_DCHECK(include_scissor_first_vert_ >= 0);
+  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, include_scissor_first_vert_, 4));
+  graphics_state->DisableDepthWrite();
+
+  // Set the depth test function for subsequent draws to occur within the
+  // stencil. Be sure to call UndoStencilState() when drawing in the
+  // stencilled area is no longer desired.
+  GL_CALL(glDepthFunc(GL_EQUAL));
+}
+
+void DrawDepthStencil::AddStencil(
+    const math::RectF& include_scissor,
+    const math::RectF& exclude_scissor) {
+  // At least the include_scissor must be specified.
+  SB_DCHECK(!include_scissor.IsEmpty());
+
+  include_scissor_first_vert_ = static_cast<GLint>(attributes_.size());
+  AddRect(include_scissor, 0);
+  if (!exclude_scissor.IsEmpty()) {
+    // Exclude scissor must use the next closest depth.
+    float depth = base_state_.depth;
+    base_state_.depth = GraphicsState::NextClosestDepth(depth);
+    exclude_scissor_first_vert_ = static_cast<GLint>(attributes_.size());
+    AddRect(exclude_scissor, 0);
+    base_state_.depth = depth;
+  }
+}
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h
new file mode 100644
index 0000000..5233355
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
+
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_poly_color.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Handles creation of a depth stencil. Pixels within the include scissor will
+// have their depth set to base_state.depth. Pixels within the exclude scissor,
+// if specified, will have their depth set to the next closest depth value. An
+// |include_scissor| must always be specified.
+//
+// NOTE: This object leaves the graphics state modified so that only the
+// stencilled pixels are affected by later draw calls. Use UndoStencilState
+// once finished with the stencil.
+// NOTE: If an |exclude_scissor| is specified, then this object uses two depth
+// values -- the incoming value in base_state.depth and the next closest.
+// NOTE: Since scissor rects pollute the depth buffer, they should only be
+// used during the tranparency pass because subsequent draws are guaranteed to
+// be above (or not overlap) these pixels.
+class DrawDepthStencil : public DrawPolyColor {
+ public:
+  DrawDepthStencil(GraphicsState* graphics_state,
+                   const BaseState& base_state,
+                   const math::RectF& include_scissor,
+                   const math::RectF& exclude_scissor);
+
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
+
+  // ExecuteOnscreenRasterize / DrawDepthStencil change the graphics state
+  // so that subsequent draw calls affect only the stencilled pixels. This
+  // function reverts the graphics state back to normal.
+  void UndoStencilState(GraphicsState* graphics_state);
+
+ protected:
+  explicit DrawDepthStencil(const BaseState& base_state);
+  void AddStencil(const math::RectF& include_scissor,
+                  const math::RectF& exclude_scissor);
+  void DrawStencil(GraphicsState* graphics_state);
+  static size_t MaxVertsNeededForStencil() { return 8; }
+
+  GLint include_scissor_first_vert_;
+  GLint exclude_scissor_first_vert_;
+};
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
index 7c71e9d..30963a0 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
@@ -37,6 +37,8 @@
     kOnscreenRectTexture = 0,
     kOnscreenRectColorTexture,
     kOnscreenPolyColor,
+    kOnscreenRectShadow,
+    kOnscreenRectShadowBlur,
     kOnscreenTransparent,
     kOnscreenCount,
   };
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
index 93b6c95..dc4c7d1 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
@@ -28,15 +28,17 @@
 DrawPolyColor::DrawPolyColor(GraphicsState* graphics_state,
     const BaseState& base_state, const math::RectF& rect,
     const render_tree::ColorRGBA& color)
-    : DrawObject(base_state) {
-  uint32_t color32 = GetGLRGBA(color * base_state_.opacity);
+    : DrawObject(base_state),
+      vertex_buffer_(NULL) {
   attributes_.reserve(4);
-  AddVertex(rect.x(), rect.y(), color32);
-  AddVertex(rect.x(), rect.bottom(), color32);
-  AddVertex(rect.right(), rect.y(), color32);
-  AddVertex(rect.right(), rect.bottom(), color32);
-  graphics_state->ReserveVertexData(attributes_.size() *
-                                    sizeof(VertexAttributes));
+  AddRect(rect, GetGLRGBA(color * base_state_.opacity));
+  graphics_state->ReserveVertexData(
+      attributes_.size() * sizeof(VertexAttributes));
+}
+
+DrawPolyColor::DrawPolyColor(const BaseState& base_state)
+    : DrawObject(base_state),
+      vertex_buffer_(NULL) {
 }
 
 void DrawPolyColor::ExecuteOnscreenUpdateVertexBuffer(
@@ -51,6 +53,12 @@
 void DrawPolyColor::ExecuteOnscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
+  SetupShader(graphics_state, program_manager);
+  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+}
+
+void DrawPolyColor::SetupShader(GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
   ShaderProgram<ShaderVertexColor,
                 ShaderFragmentColor>* program;
   program_manager->GetProgram(&program);
@@ -71,7 +79,13 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, color));
   graphics_state->VertexAttribFinish();
-  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+}
+
+void DrawPolyColor::AddRect(const math::RectF& rect, uint32_t color) {
+  AddVertex(rect.x(), rect.y(), color);
+  AddVertex(rect.x(), rect.bottom(), color);
+  AddVertex(rect.right(), rect.y(), color);
+  AddVertex(rect.right(), rect.bottom(), color);
 }
 
 void DrawPolyColor::AddVertex(float x, float y, uint32_t color) {
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
index 769299c..bc549f6 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
@@ -39,7 +39,11 @@
   void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
 
- private:
+ protected:
+  explicit DrawPolyColor(const BaseState& base_state);
+  void SetupShader(GraphicsState* graphics_state,
+                   ShaderProgramManager* program_manager);
+  void AddRect(const math::RectF& rect, uint32_t color);
   void AddVertex(float x, float y, uint32_t color);
 
   struct VertexAttributes {
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
index d3f48c9..2c04709 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
@@ -16,6 +16,7 @@
 
 #include <GLES2/gl2.h>
 
+#include "base/basictypes.h"
 #include "cobalt/renderer/backend/egl/utils.h"
 #include "egl/generated_shader_impl.h"
 #include "starboard/memory.h"
@@ -37,12 +38,15 @@
     const BaseState& base_state,
     const math::RectF& rect, const render_tree::ColorRGBA& color,
     const backend::TextureEGL* texture,
-    const math::Matrix3F& texcoord_transform)
+    const math::Matrix3F& texcoord_transform,
+    bool clamp_texcoords)
     : DrawObject(base_state),
       texcoord_transform_(texcoord_transform),
       rect_(rect),
       texture_(texture),
-      vertex_buffer_(NULL) {
+      vertex_buffer_(NULL),
+      clamp_texcoords_(clamp_texcoords),
+      tile_texture_(false) {
   color_ = GetGLRGBA(color * base_state_.opacity);
   graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
 }
@@ -50,13 +54,19 @@
 DrawRectColorTexture::DrawRectColorTexture(GraphicsState* graphics_state,
     const BaseState& base_state,
     const math::RectF& rect, const render_tree::ColorRGBA& color,
-    const GenerateTextureFunction& generate_texture)
+    const backend::TextureEGL* texture,
+    const math::Matrix3F& texcoord_transform,
+    const base::Closure& draw_offscreen,
+    const base::Closure& draw_onscreen)
     : DrawObject(base_state),
-      texcoord_transform_(math::Matrix3F::Identity()),
+      texcoord_transform_(texcoord_transform),
       rect_(rect),
-      texture_(NULL),
-      generate_texture_(generate_texture),
-      vertex_buffer_(NULL) {
+      texture_(texture),
+      draw_offscreen_(draw_offscreen),
+      draw_onscreen_(draw_onscreen),
+      vertex_buffer_(NULL),
+      clamp_texcoords_(false),
+      tile_texture_(false) {
   color_ = GetGLRGBA(color * base_state_.opacity);
   graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
 }
@@ -64,8 +74,8 @@
 void DrawRectColorTexture::ExecuteOffscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  if (!generate_texture_.is_null()) {
-    generate_texture_.Run(&texture_, &texcoord_transform_);
+  if (!draw_offscreen_.is_null()) {
+    draw_offscreen_.Run();
   }
 }
 
@@ -92,11 +102,48 @@
   vertex_buffer_ = graphics_state->AllocateVertexData(
       sizeof(attributes));
   SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
+
+  // Find minimum and maximum texcoord values.
+  texcoord_clamp_[0] = attributes[0].texcoord[0];
+  texcoord_clamp_[1] = attributes[0].texcoord[1];
+  texcoord_clamp_[2] = attributes[0].texcoord[0];
+  texcoord_clamp_[3] = attributes[0].texcoord[1];
+  for (int i = 1; i < arraysize(attributes); ++i) {
+    float texcoord_u = attributes[i].texcoord[0];
+    float texcoord_v = attributes[i].texcoord[1];
+    if (texcoord_clamp_[0] > texcoord_u) {
+      texcoord_clamp_[0] = texcoord_u;
+    } else if (texcoord_clamp_[2] < texcoord_u) {
+      texcoord_clamp_[2] = texcoord_u;
+    }
+    if (texcoord_clamp_[1] > texcoord_v) {
+      texcoord_clamp_[1] = texcoord_v;
+    } else if (texcoord_clamp_[3] < texcoord_v) {
+      texcoord_clamp_[3] = texcoord_v;
+    }
+  }
+
+  tile_texture_ = texcoord_clamp_[0] < 0.0f || texcoord_clamp_[1] < 0.0f ||
+                  texcoord_clamp_[2] > 1.0f || texcoord_clamp_[3] > 1.0f;
+
+  if (clamp_texcoords_) {
+    // Inset 0.5-epsilon so the border texels are still sampled, but nothing
+    // beyond.
+    const float kTexelInset = 0.499f;
+    texcoord_clamp_[0] += kTexelInset / texture_->GetSize().width();
+    texcoord_clamp_[1] += kTexelInset / texture_->GetSize().height();
+    texcoord_clamp_[2] -= kTexelInset / texture_->GetSize().width();
+    texcoord_clamp_[3] -= kTexelInset / texture_->GetSize().height();
+  }
 }
 
 void DrawRectColorTexture::ExecuteOnscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
+  if (!draw_onscreen_.is_null()) {
+    draw_onscreen_.Run();
+  }
+
   ShaderProgram<ShaderVertexColorTexcoord,
                 ShaderFragmentColorTexcoord>* program;
   program_manager->GetProgram(&program);
@@ -121,10 +168,23 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, texcoord));
   graphics_state->VertexAttribFinish();
-  graphics_state->ActiveBindTexture(
-      program->GetFragmentShader().u_texture_texunit(),
-      texture_->GetTarget(), texture_->gl_handle());
-  GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+  GL_CALL(glUniform4fv(program->GetFragmentShader().u_texcoord_clamp(),
+      1, texcoord_clamp_));
+
+  if (tile_texture_) {
+    graphics_state->ActiveBindTexture(
+        program->GetFragmentShader().u_texture_texunit(),
+        texture_->GetTarget(), texture_->gl_handle(), GL_REPEAT);
+    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+    graphics_state->ActiveBindTexture(
+        program->GetFragmentShader().u_texture_texunit(),
+        texture_->GetTarget(), texture_->gl_handle(), GL_CLAMP_TO_EDGE);
+  } else {
+    graphics_state->ActiveBindTexture(
+        program->GetFragmentShader().u_texture_texunit(),
+        texture_->GetTarget(), texture_->gl_handle());
+    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+  }
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
index d459196..11cec54 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
@@ -30,22 +30,22 @@
 // Handles drawing a textured rectangle modulated by a given color.
 class DrawRectColorTexture : public DrawObject {
  public:
-  typedef base::Callback<void(const backend::TextureEGL** out_texture,
-                              math::Matrix3F* out_texcoord_transform)>
-      GenerateTextureFunction;
+  DrawRectColorTexture(GraphicsState* graphics_state,
+                       const BaseState& base_state,
+                       const math::RectF& rect,
+                       const render_tree::ColorRGBA& color,
+                       const backend::TextureEGL* texture,
+                       const math::Matrix3F& texcoord_transform,
+                       bool clamp_texcoords);
 
   DrawRectColorTexture(GraphicsState* graphics_state,
                        const BaseState& base_state,
                        const math::RectF& rect,
                        const render_tree::ColorRGBA& color,
                        const backend::TextureEGL* texture,
-                       const math::Matrix3F& texcoord_transform);
-
-  DrawRectColorTexture(GraphicsState* graphics_state,
-                       const BaseState& base_state,
-                       const math::RectF& rect,
-                       const render_tree::ColorRGBA& color,
-                       const GenerateTextureFunction& generate_texture);
+                       const math::Matrix3F& texcoord_transform,
+                       const base::Closure& draw_offscreen,
+                       const base::Closure& draw_onscreen);
 
   void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
@@ -59,9 +59,13 @@
   math::RectF rect_;
   uint32_t color_;
   const backend::TextureEGL* texture_;
-  GenerateTextureFunction generate_texture_;
+  base::Closure draw_offscreen_;
+  base::Closure draw_onscreen_;
 
   uint8_t* vertex_buffer_;
+  float texcoord_clamp_[4];   // texcoord clamping (min u, min v, max u, max v)
+  bool clamp_texcoords_;
+  bool tile_texture_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
new file mode 100644
index 0000000..bf27f37
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
@@ -0,0 +1,225 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h"
+
+#include <GLES2/gl2.h>
+#include <algorithm>
+
+#include "cobalt/math/transform_2d.h"
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+namespace {
+const float kEpsilon = 0.001f;
+
+size_t MaxVertsNeededForAlignedGradient(
+    const render_tree::LinearGradientBrush& brush) {
+  // Triangle strip for an axis-aligned rectangle. Two vertices are required
+  // per color stop in addition to possible start and/or end vertices.
+  return 4 + 2 * brush.color_stops().size();
+}
+}
+
+DrawRectLinearGradient::DrawRectLinearGradient(GraphicsState* graphics_state,
+    const BaseState& base_state,
+    const math::RectF& rect,
+    const render_tree::LinearGradientBrush& brush)
+    : DrawDepthStencil(base_state),
+      first_rect_vert_(0),
+      gradient_angle_(0.0f) {
+  if (std::abs(brush.dest().y() - brush.source().y()) < kEpsilon) {
+    attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+    AddRectWithHorizontalGradient(
+        rect, brush.source(), brush.dest(), brush.color_stops());
+  } else if (std::abs(brush.dest().x() - brush.source().x()) < kEpsilon) {
+    attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+    AddRectWithVerticalGradient(
+        rect, brush.source(), brush.dest(), brush.color_stops());
+  } else {
+    AddRectWithAngledGradient(rect, brush);
+  }
+  graphics_state->ReserveVertexData(
+      attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectLinearGradient::ExecuteOnscreenRasterize(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  SetupShader(graphics_state, program_manager);
+
+  if (first_rect_vert_ > 0) {
+    // Draw using stencil.
+    DrawStencil(graphics_state);
+
+    // Update the transform for the shader to rotate the horizontal gradient
+    // into the desired angled gradient.
+    ShaderProgram<ShaderVertexColor,
+                  ShaderFragmentColor>* program;
+    program_manager->GetProgram(&program);
+    SB_DCHECK(graphics_state->GetProgram() == program->GetHandle());
+    graphics_state->UpdateTransformMatrix(
+        program->GetVertexShader().u_view_matrix(),
+        base_state_.transform * math::RotateMatrix(gradient_angle_));
+
+    GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, first_rect_vert_,
+        attributes_.size() - first_rect_vert_));
+    UndoStencilState(graphics_state);
+  } else {
+    GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+  }
+}
+
+void DrawRectLinearGradient::AddRectWithHorizontalGradient(
+    const math::RectF& rect, const math::PointF& source,
+    const math::PointF& dest, const render_tree::ColorStopList& color_stops) {
+  SB_DCHECK(source.x() >= rect.x() - kEpsilon &&
+            source.x() <= rect.right() + kEpsilon &&
+            dest.x() >= rect.x() - kEpsilon &&
+            dest.x() <= rect.right() + kEpsilon);
+
+  size_t num_colors = color_stops.size();
+  float start = std::max(rect.x(), std::min(source.x(), rect.right()));
+  float length = std::max(rect.x(), std::min(dest.x(), rect.right())) - start;
+  float position = color_stops[0].position * length + start;
+  uint32_t color32 = GetGLColor(color_stops[0]);
+
+  // Draw a horizontal triangle strip.
+  if (length >= 0.0f) {
+    if (position > rect.x()) {
+      AddVertex(rect.x(), rect.y(), color32);
+      AddVertex(rect.x(), rect.bottom(), color32);
+    }
+  } else if (position < rect.right()) {
+      AddVertex(rect.right(), rect.y(), color32);
+      AddVertex(rect.right(), rect.bottom(), color32);
+  }
+
+  for (size_t i = 0; i < num_colors; ++i) {
+    position = color_stops[i].position * length + start;
+    color32 = GetGLColor(color_stops[i]);
+    AddVertex(position, rect.y(), color32);
+    AddVertex(position, rect.bottom(), color32);
+  }
+
+  if (length >= 0.0f) {
+    if (position < rect.right()) {
+      AddVertex(rect.right(), rect.y(), color32);
+      AddVertex(rect.right(), rect.bottom(), color32);
+    }
+  } else if (position > rect.x()) {
+    AddVertex(rect.x(), rect.y(), color32);
+    AddVertex(rect.x(), rect.bottom(), color32);
+  }
+}
+
+void DrawRectLinearGradient::AddRectWithVerticalGradient(
+    const math::RectF& rect, const math::PointF& source,
+    const math::PointF& dest, const render_tree::ColorStopList& color_stops) {
+  SB_DCHECK(source.y() >= rect.y() - kEpsilon &&
+            source.y() <= rect.bottom() + kEpsilon &&
+            dest.y() >= rect.y() - kEpsilon &&
+            dest.y() <= rect.bottom() + kEpsilon);
+
+  size_t num_colors = color_stops.size();
+  float start = std::max(rect.y(), std::min(source.y(), rect.bottom()));
+  float length = std::max(rect.y(), std::min(dest.y(), rect.bottom())) - start;
+  float position = color_stops[0].position * length + start;
+  uint32_t color32 = GetGLColor(color_stops[0]);
+
+  // Draw a vertical triangle strip.
+  if (length >= 0) {
+    if (position > rect.y()) {
+      AddVertex(rect.x(), rect.y(), color32);
+      AddVertex(rect.right(), rect.y(), color32);
+    }
+  } else if (position < rect.bottom()) {
+    AddVertex(rect.x(), rect.bottom(), color32);
+    AddVertex(rect.right(), rect.bottom(), color32);
+  }
+
+  for (size_t i = 0; i < num_colors; ++i) {
+    position = color_stops[i].position * length + start;
+    color32 = GetGLColor(color_stops[i]);
+    AddVertex(rect.x(), position, color32);
+    AddVertex(rect.right(), position, color32);
+  }
+
+  if (length >= 0) {
+    if (position < rect.bottom()) {
+      AddVertex(rect.x(), rect.bottom(), color32);
+      AddVertex(rect.right(), rect.bottom(), color32);
+    }
+  } else if (position > rect.y()) {
+    AddVertex(rect.x(), rect.y(), color32);
+    AddVertex(rect.right(), rect.y(), color32);
+  }
+}
+
+void DrawRectLinearGradient::AddRectWithAngledGradient(
+    const math::RectF& rect, const render_tree::LinearGradientBrush& brush) {
+  // Use an inclusive scissor stencil to draw within the desired area. Then
+  // draw a rect with horizontal gradient that will be rotated to cover the
+  // desired rect with angled gradient. This is not as efficient as calculating
+  // a triangle strip to describe the rect with angled gradient, but is simpler.
+  attributes_.reserve(MaxVertsNeededForStencil() +
+                      MaxVertsNeededForAlignedGradient(brush));
+  AddStencil(rect, math::RectF());
+
+  // Calculate the angle needed to rotate a horizontal gradient into the
+  // angled gradient. (Flip vertical distance because the input coordinate
+  // system's origin is at the top-left.)
+  gradient_angle_ = atan2(brush.source().y() - brush.dest().y(),
+                          brush.dest().x() - brush.source().x());
+
+  // Calculate the endpoints for the horizontal gradient that, when rotated
+  // by gradient_angle_, will turn into the original angled gradient.
+  math::Matrix3F inverse_transform = math::RotateMatrix(-gradient_angle_);
+  math::PointF mapped_source(inverse_transform * brush.source());
+  math::PointF mapped_dest(inverse_transform * brush.dest());
+  SB_DCHECK(mapped_source.x() <= mapped_dest.x());
+
+  // Calculate a rect large enough that, when rotated by gradient_angle_, it
+  // will contain the original rect.
+  math::RectF mapped_rect = inverse_transform.MapRect(rect);
+
+  // Adjust the mapped_rect to include the gradient endpoints if needed.
+  float left = std::min(mapped_rect.x(), mapped_source.x());
+  float right = std::max(mapped_rect.right(), mapped_dest.x());
+  mapped_rect.SetRect(left, mapped_rect.y(),
+                      right - left, mapped_rect.height());
+
+  first_rect_vert_ = attributes_.size();
+  AddRectWithHorizontalGradient(mapped_rect, mapped_source, mapped_dest,
+                                brush.color_stops());
+}
+
+uint32_t DrawRectLinearGradient::GetGLColor(
+    const render_tree::ColorStop& color_stop) {
+  const render_tree::ColorRGBA& color = color_stop.color;
+  float alpha = base_state_.opacity * color.a();
+  return GetGLRGBA(color.r() * alpha, color.g() * alpha, color.b() * alpha,
+                   alpha);
+}
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
new file mode 100644
index 0000000..e138460
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/brush.h"
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Handles drawing a rectangle with a linear color gradient. This may use a
+// depth stencil, so it must be processed with other transparent draws.
+class DrawRectLinearGradient : public DrawDepthStencil {
+ public:
+  DrawRectLinearGradient(GraphicsState* graphics_state,
+                         const BaseState& base_state,
+                         const math::RectF& rect,
+                         const render_tree::LinearGradientBrush& brush);
+
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
+
+ private:
+  void AddRectWithHorizontalGradient(
+      const math::RectF& rect, const math::PointF& source,
+      const math::PointF& dest, const render_tree::ColorStopList& color_stops);
+  void AddRectWithVerticalGradient(
+      const math::RectF& rect, const math::PointF& source,
+      const math::PointF& dest, const render_tree::ColorStopList& color_stops);
+  void AddRectWithAngledGradient(
+      const math::RectF& rect, const render_tree::LinearGradientBrush& brush);
+  uint32_t GetGLColor(const render_tree::ColorStop& color_stop);
+
+  // Index of the first vertex for the rect with gradient.
+  size_t first_rect_vert_;
+
+  // For angled gradients, this is the rotation angle needed to transform the
+  // submitted rect with horizontal gradient into the desired rect with angled
+  // gradient.
+  float gradient_angle_;
+};
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
new file mode 100644
index 0000000..6090782
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -0,0 +1,149 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h"
+
+#include <GLES2/gl2.h>
+#include <algorithm>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawRectShadowBlur::DrawRectShadowBlur(GraphicsState* graphics_state,
+    const BaseState& base_state, const math::RectF& inner_rect,
+    const math::RectF& outer_rect, const math::RectF& blur_edge,
+    const render_tree::ColorRGBA& color, const math::RectF& exclude_scissor,
+    float blur_sigma, bool inset)
+    : DrawObject(base_state),
+      draw_stencil_(graphics_state, base_state, outer_rect, exclude_scissor),
+      blur_center_(blur_edge.CenterPoint()),
+      // The sigma scale is used to transform pixel distances to sigma-relative
+      // distances. The 0.5 multiplier is used to match skia's implementation.
+      blur_sigma_scale_(0.5f / blur_sigma),
+      vertex_buffer_(NULL) {
+  float color_scale = 1.0f;
+  attributes_.reserve(10);
+
+  // The blur radius dictates the distance from blur center at which the
+  // shadow should be about 50% opacity of the shadow color. This is expressed
+  // in terms of sigma.
+  blur_radius_[0] = blur_edge.width() * 0.5f * blur_sigma_scale_;
+  blur_radius_[1] = blur_edge.height() * 0.5f * blur_sigma_scale_;
+
+  if (inset) {
+    // Invert the shadow by using (1.0 - weight) for the alpha scale.
+    blur_scale_add_[0] = -1.0f;
+    blur_scale_add_[1] = 1.0f;
+  } else {
+    // The calculated weight works for outset shadows.
+    blur_scale_add_[0] = 1.0f;
+    blur_scale_add_[1] = 0.0f;
+
+    // Scale opacity down if the shadow's edge is smaller than the blur kernel.
+    // Use a quick linear scale as the dimension goes below 1.5 sigma (which is
+    // the distance at which the approximated gaussian integral reaches 0).
+    const float size_scale = 1.0f / (1.5f * blur_sigma);
+    color_scale = std::min(blur_edge.width() * size_scale, 1.0f) *
+                  std::min(blur_edge.height() * size_scale, 1.0f);
+  }
+
+  // Add a triangle-strip to draw the area between outer_rect and inner_rect.
+  // This is the area which the shadow covers.
+  uint32_t color32 = GetGLRGBA(color * color_scale * base_state_.opacity);
+  AddVertex(outer_rect.x(), outer_rect.y(), color32);
+  AddVertex(inner_rect.x(), inner_rect.y(), color32);
+  AddVertex(outer_rect.right(), outer_rect.y(), color32);
+  AddVertex(inner_rect.right(), inner_rect.y(), color32);
+  AddVertex(outer_rect.right(), outer_rect.bottom(), color32);
+  AddVertex(inner_rect.right(), inner_rect.bottom(), color32);
+  AddVertex(outer_rect.x(), outer_rect.bottom(), color32);
+  AddVertex(inner_rect.x(), inner_rect.bottom(), color32);
+  AddVertex(outer_rect.x(), outer_rect.y(), color32);
+  AddVertex(inner_rect.x(), inner_rect.y(), color32);
+
+  graphics_state->ReserveVertexData(
+      attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowBlur::ExecuteOnscreenUpdateVertexBuffer(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  draw_stencil_.ExecuteOnscreenUpdateVertexBuffer(
+      graphics_state, program_manager);
+  vertex_buffer_ = graphics_state->AllocateVertexData(
+      attributes_.size() * sizeof(VertexAttributes));
+  SbMemoryCopy(vertex_buffer_, &attributes_[0],
+               attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowBlur::ExecuteOnscreenRasterize(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  // Create a stencil for the pixels to be modified.
+  draw_stencil_.ExecuteOnscreenRasterize(
+      graphics_state, program_manager);
+
+  // Draw the blurred shadow in the stencilled area.
+  ShaderProgram<ShaderVertexColorBlur,
+                ShaderFragmentColorBlur>* program;
+  program_manager->GetProgram(&program);
+  graphics_state->UseProgram(program->GetHandle());
+  graphics_state->UpdateClipAdjustment(
+      program->GetVertexShader().u_clip_adjustment());
+  graphics_state->UpdateTransformMatrix(
+      program->GetVertexShader().u_view_matrix(),
+      base_state_.transform);
+  graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+      base_state_.scissor.width(), base_state_.scissor.height());
+  graphics_state->VertexAttribPointer(
+      program->GetVertexShader().a_position(), 3, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributes), vertex_buffer_ +
+      offsetof(VertexAttributes, position));
+  graphics_state->VertexAttribPointer(
+      program->GetVertexShader().a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
+      sizeof(VertexAttributes), vertex_buffer_ +
+      offsetof(VertexAttributes, color));
+  graphics_state->VertexAttribPointer(
+      program->GetVertexShader().a_blur_position(), 2, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributes), vertex_buffer_ +
+      offsetof(VertexAttributes, blur_position));
+  graphics_state->VertexAttribFinish();
+  GL_CALL(glUniform2fv(program->GetFragmentShader().u_blur_radius(), 1,
+      blur_radius_));
+  GL_CALL(glUniform2fv(program->GetFragmentShader().u_scale_add(), 1,
+      blur_scale_add_));
+
+  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+  draw_stencil_.UndoStencilState(graphics_state);
+}
+
+void DrawRectShadowBlur::AddVertex(float x, float y, uint32_t color) {
+  float blur_x = (x - blur_center_.x()) * blur_sigma_scale_;
+  float blur_y = (y - blur_center_.y()) * blur_sigma_scale_;
+  VertexAttributes attribute = { { x, y, base_state_.depth }, color,
+                                 { blur_x, blur_y } };
+  attributes_.push_back(attribute);
+}
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
new file mode 100644
index 0000000..4d17b25
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
@@ -0,0 +1,101 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
+
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Example CSS box shadow (outset):
+//   +-------------------------------------+
+//   | Box shadow "blur" region            |
+//   |   +-----------------------------+   |
+//   |   | Box shadow "spread" region  |   |
+//   |   |   +---------------------+   |   |
+//   |   |   | Box shadow rect     |   |   |
+//   |   |   | (exclude scissor)   |   |   |
+//   |   |   +---------------------+   |   |
+//   |   |                             |   |
+//   |   +-----------------------------+   |
+//   | (include scissor)                   |
+//   +-------------------------------------+
+// NOTE: Despite the CSS naming, the actual blur effect starts inside the
+// "spread" region.
+
+// Handles drawing a box shadow with blur. This uses a gaussian kernel to fade
+// the "blur" region. A stencil is used to ensure only the desired pixels are
+// touched.
+//
+// This uses a shader to mimic skia's SkBlurMask.cpp.
+// See also http://stereopsis.com/shadowrect/ as reference for the formula
+// used to approximate the gaussian integral (which controls the opacity of
+// the shadow).
+class DrawRectShadowBlur : public DrawObject {
+ public:
+  // Draw a blurred box shadow.
+  // The box shadow exists in the area between |inner_rect| and |outer_rect|.
+  // |blur_edge| specifies the area where the "spread" region transitions
+  //   to the "blur" region. It has 50% opacity of the shadow color.
+  DrawRectShadowBlur(GraphicsState* graphics_state,
+                     const BaseState& base_state,
+                     const math::RectF& inner_rect,
+                     const math::RectF& outer_rect,
+                     const math::RectF& blur_edge,
+                     const render_tree::ColorRGBA& color,
+                     const math::RectF& exclude_scissor,
+                     float blur_sigma, bool inset);
+
+  void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
+
+ private:
+  void AddVertex(float x, float y, uint32_t color);
+
+  struct VertexAttributes {
+    float position[3];
+    uint32_t color;
+    float blur_position[2];   // Expressed in terms of blur_sigma from center.
+  };
+
+  DrawDepthStencil draw_stencil_;
+  std::vector<VertexAttributes> attributes_;
+  math::PointF blur_center_;
+  float blur_radius_[2];      // Expressed in terms of blur_sigma.
+  float blur_scale_add_[2];
+
+  // The sigma scale is used to transform pixel distances to sigma-relative
+  // distances.
+  float blur_sigma_scale_;
+
+  uint8_t* vertex_buffer_;
+};
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
new file mode 100644
index 0000000..8f61e32
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h"
+
+#include <GLES2/gl2.h>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
+    const BaseState& base_state, const math::RectF& inner_rect,
+    const math::RectF& outer_rect, const render_tree::ColorRGBA& color,
+    const math::RectF& include_scissor, const math::RectF& exclude_scissor)
+    : DrawDepthStencil(base_state) {
+  // Reserve space for the box shadow's spread and possible scissor rects.
+  attributes_.reserve(10 + MaxVertsNeededForStencil());
+
+  // Draw the box shadow's spread. This is a triangle strip covering the area
+  // between outer rect and inner rect.
+  uint32_t color32 = GetGLRGBA(color * base_state_.opacity);
+  AddVertex(outer_rect.x(), outer_rect.y(), color32);
+  AddVertex(inner_rect.x(), inner_rect.y(), color32);
+  AddVertex(outer_rect.right(), outer_rect.y(), color32);
+  AddVertex(inner_rect.right(), inner_rect.y(), color32);
+  AddVertex(outer_rect.right(), outer_rect.bottom(), color32);
+  AddVertex(inner_rect.right(), inner_rect.bottom(), color32);
+  AddVertex(outer_rect.x(), outer_rect.bottom(), color32);
+  AddVertex(inner_rect.x(), inner_rect.bottom(), color32);
+  AddVertex(outer_rect.x(), outer_rect.y(), color32);
+  AddVertex(inner_rect.x(), inner_rect.y(), color32);
+
+  AddStencil(include_scissor, exclude_scissor);
+
+  graphics_state->ReserveVertexData(
+      attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowSpread::ExecuteOnscreenRasterize(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  SetupShader(graphics_state, program_manager);
+  DrawStencil(graphics_state);
+  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 10));
+  UndoStencilState(graphics_state);
+}
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
new file mode 100644
index 0000000..28d1cef
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
@@ -0,0 +1,69 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
+
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Example CSS box shadow (outset):
+//   +-------------------------------------+
+//   | Box shadow "blur" region            |
+//   |   +-----------------------------+   |
+//   |   | Box shadow "spread" region  |   |
+//   |   |   +---------------------+   |   |
+//   |   |   | Box shadow rect     |   |   |
+//   |   |   | (exclude scissor)   |   |   |
+//   |   |   +---------------------+   |   |
+//   |   |                             |   |
+//   |   +-----------------------------+   |
+//   | (include scissor)                   |
+//   +-------------------------------------+
+// When an offset is used for the shadow, the include and exclude scissors
+// play a critical role.
+
+// Handles drawing the solid "spread" portion of a box shadow. This also
+// creates a stencil, using the depth buffer, for the pixels inside
+// |include_scissor| excluding those in |exclude_scissor|.
+class DrawRectShadowSpread : public DrawDepthStencil {
+ public:
+  // Fill the area between |inner_rect| and |outer_rect| with the specified
+  // color.
+  DrawRectShadowSpread(GraphicsState* graphics_state,
+                       const BaseState& base_state,
+                       const math::RectF& inner_rect,
+                       const math::RectF& outer_rect,
+                       const render_tree::ColorRGBA& color,
+                       const math::RectF& include_scissor,
+                       const math::RectF& exclude_scissor);
+
+  void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
+};
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
index f4099d8..107f103 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
@@ -16,6 +16,7 @@
 
 #include <GLES2/gl2.h>
 
+#include "base/basictypes.h"
 #include "cobalt/renderer/backend/egl/utils.h"
 #include "egl/generated_shader_impl.h"
 #include "starboard/memory.h"
@@ -34,34 +35,41 @@
 
 DrawRectTexture::DrawRectTexture(GraphicsState* graphics_state,
     const BaseState& base_state,
-    const math::RectF& rect, const backend::TextureEGL* texture,
+    const math::RectF& rect,
+    const backend::TextureEGL* texture,
     const math::Matrix3F& texcoord_transform)
     : DrawObject(base_state),
       texcoord_transform_(texcoord_transform),
       rect_(rect),
       texture_(texture),
-      vertex_buffer_(NULL) {
+      vertex_buffer_(NULL),
+      tile_texture_(false) {
   graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
 }
 
 DrawRectTexture::DrawRectTexture(GraphicsState* graphics_state,
     const BaseState& base_state,
     const math::RectF& rect,
-    const GenerateTextureFunction& generate_texture)
+    const backend::TextureEGL* texture,
+    const math::Matrix3F& texcoord_transform,
+    const base::Closure& draw_offscreen,
+    const base::Closure& draw_onscreen)
     : DrawObject(base_state),
-      texcoord_transform_(math::Matrix3F::Identity()),
+      texcoord_transform_(texcoord_transform),
       rect_(rect),
-      texture_(NULL),
-      generate_texture_(generate_texture),
-      vertex_buffer_(NULL) {
+      texture_(texture),
+      draw_offscreen_(draw_offscreen),
+      draw_onscreen_(draw_onscreen),
+      vertex_buffer_(NULL),
+      tile_texture_(false) {
   graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
 }
 
 void DrawRectTexture::ExecuteOffscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  if (!generate_texture_.is_null()) {
-    generate_texture_.Run(&texture_, &texcoord_transform_);
+  if (!draw_offscreen_.is_null()) {
+    draw_offscreen_.Run();
   }
 }
 
@@ -88,11 +96,21 @@
   vertex_buffer_ = graphics_state->AllocateVertexData(
       sizeof(attributes));
   SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
+
+  for (int i = 0; i < arraysize(attributes); ++i) {
+    tile_texture_ = tile_texture_ ||
+        attributes[i].texcoord[0] < 0.0f || attributes[i].texcoord[0] > 1.0f ||
+        attributes[i].texcoord[1] < 0.0f || attributes[i].texcoord[1] > 1.0f;
+  }
 }
 
 void DrawRectTexture::ExecuteOnscreenRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
+  if (!draw_onscreen_.is_null()) {
+    draw_onscreen_.Run();
+  }
+
   ShaderProgram<ShaderVertexTexcoord,
                 ShaderFragmentTexcoord>* program;
   program_manager->GetProgram(&program);
@@ -113,10 +131,21 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, texcoord));
   graphics_state->VertexAttribFinish();
-  graphics_state->ActiveBindTexture(
-      program->GetFragmentShader().u_texture_texunit(),
-      texture_->GetTarget(), texture_->gl_handle());
-  GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+
+  if (tile_texture_) {
+    graphics_state->ActiveBindTexture(
+        program->GetFragmentShader().u_texture_texunit(),
+        texture_->GetTarget(), texture_->gl_handle(), GL_REPEAT);
+    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+    graphics_state->ActiveBindTexture(
+        program->GetFragmentShader().u_texture_texunit(),
+        texture_->GetTarget(), texture_->gl_handle(), GL_CLAMP_TO_EDGE);
+  } else {
+    graphics_state->ActiveBindTexture(
+        program->GetFragmentShader().u_texture_texunit(),
+        texture_->GetTarget(), texture_->gl_handle());
+    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+  }
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
index 767c3eb..a0a284b 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
@@ -29,10 +29,6 @@
 // Handles drawing a textured rectangle.
 class DrawRectTexture : public DrawObject {
  public:
-  typedef base::Callback<void(const backend::TextureEGL** out_texture,
-                              math::Matrix3F* out_texcoord_transform)>
-      GenerateTextureFunction;
-
   DrawRectTexture(GraphicsState* graphics_state,
                   const BaseState& base_state,
                   const math::RectF& rect,
@@ -42,7 +38,10 @@
   DrawRectTexture(GraphicsState* graphics_state,
                   const BaseState& base_state,
                   const math::RectF& rect,
-                  const GenerateTextureFunction& generate_texture);
+                  const backend::TextureEGL* texture,
+                  const math::Matrix3F& texcoord_transform,
+                  const base::Closure& draw_offscreen,
+                  const base::Closure& draw_onscreen);
 
   void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
@@ -55,9 +54,11 @@
   math::Matrix3F texcoord_transform_;
   math::RectF rect_;
   const backend::TextureEGL* texture_;
-  GenerateTextureFunction generate_texture_;
+  base::Closure draw_offscreen_;
+  base::Closure draw_onscreen_;
 
   uint8_t* vertex_buffer_;
+  bool tile_texture_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
index 904b6ee..3c2d1e9 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
@@ -35,6 +35,12 @@
   depth_test_enabled_ = false;
   depth_write_enabled_ = true;
   Reset();
+
+  // These settings should only need to be set once. Nothing should touch them.
+  GL_CALL(glDepthRangef(0.0f, 1.0f));
+  GL_CALL(glDisable(GL_DITHER));
+  GL_CALL(glDisable(GL_CULL_FACE));
+  GL_CALL(glDisable(GL_STENCIL_TEST));
 }
 
 GraphicsState::~GraphicsState() {
@@ -73,6 +79,7 @@
   GL_CALL(glDisable(GL_BLEND));
   GL_CALL(glDisable(GL_DEPTH_TEST));
   GL_CALL(glDepthMask(GL_TRUE));
+  GL_CALL(glUseProgram(0));
 
   // Since the GL state was changed without going through the cache, mark it
   // as dirty.
@@ -173,6 +180,10 @@
   }
 }
 
+void GraphicsState::ResetDepthFunc() {
+  GL_CALL(glDepthFunc(GL_LESS));
+}
+
 void GraphicsState::ActiveBindTexture(GLenum texture_unit, GLenum target,
                                       GLuint texture) {
   int texunit_index = texture_unit - GL_TEXTURE0;
@@ -194,6 +205,29 @@
   }
 }
 
+void GraphicsState::ActiveBindTexture(GLenum texture_unit, GLenum target,
+    GLuint texture, GLint texture_wrap_mode) {
+  int texunit_index = texture_unit - GL_TEXTURE0;
+
+  if (texture_unit_ != texture_unit) {
+    texture_unit_ = texture_unit;
+    GL_CALL(glActiveTexture(texture_unit));
+    GL_CALL(glBindTexture(target, texture));
+  } else if (texunit_index >= kNumTextureUnitsCached ||
+             texunit_target_[texunit_index] != target ||
+             texunit_texture_[texunit_index] != texture) {
+    GL_CALL(glBindTexture(target, texture));
+  }
+
+  if (texunit_index < kNumTextureUnitsCached) {
+    texunit_target_[texunit_index] = target;
+    texunit_texture_[texunit_index] = texture;
+  }
+
+  GL_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_S, texture_wrap_mode));
+  GL_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_T, texture_wrap_mode));
+}
+
 void GraphicsState::SetClipAdjustment(const math::Size& render_target_size) {
   render_target_size_ = render_target_size;
   clip_adjustment_dirty_ = true;
@@ -325,10 +359,10 @@
 
 void GraphicsState::Reset() {
   program_ = 0;
-  GL_CALL(glUseProgram(0));
 
   Viewport(viewport_.x(), viewport_.y(), viewport_.width(), viewport_.height());
   Scissor(scissor_.x(), scissor_.y(), scissor_.width(), scissor_.height());
+  GL_CALL(glEnable(GL_SCISSOR_TEST));
 
   array_buffer_handle_ = 0;
   texture_unit_ = 0;
@@ -338,6 +372,11 @@
   disable_vertex_attrib_array_mask_ = 0;
   clip_adjustment_dirty_ = true;
 
+  if (vertex_data_buffer_updated_) {
+    array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
+    GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
+  }
+
   if (blend_enabled_) {
     GL_CALL(glEnable(GL_BLEND));
   } else {
@@ -351,13 +390,7 @@
     GL_CALL(glDisable(GL_DEPTH_TEST));
   }
   GL_CALL(glDepthMask(depth_write_enabled_ ? GL_TRUE : GL_FALSE));
-  GL_CALL(glDepthFunc(GL_LESS));
-  GL_CALL(glDepthRangef(0.0f, 1.0f));
-
-  GL_CALL(glDisable(GL_DITHER));
-  GL_CALL(glDisable(GL_CULL_FACE));
-  GL_CALL(glDisable(GL_STENCIL_TEST));
-  GL_CALL(glEnable(GL_SCISSOR_TEST));
+  ResetDepthFunc();
 
   state_dirty_ = false;
 }
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.h b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
index 489b926..a8c48f4 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.h
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
@@ -50,6 +50,7 @@
 
   // Set the current shader program to be used.
   void UseProgram(GLuint program);
+  GLuint GetProgram() const { return program_; }
 
   // Set the viewport.
   void Viewport(int x, int y, int width, int height);
@@ -63,21 +64,32 @@
   // Default = disabled.
   void EnableBlend();
   void DisableBlend();
+  bool IsBlendEnabled() const { return blend_enabled_; }
 
   // Control depth testing.
   // Default = enabled.
   void EnableDepthTest();
   void DisableDepthTest();
+  bool IsDepthTestEnabled() const { return depth_test_enabled_; }
 
   // Control writing to the depth buffer.
   // Default = enabled.
   void EnableDepthWrite();
   void DisableDepthWrite();
+  bool IsDepthWriteEnabled() const { return depth_write_enabled_; }
+
+  // Reset to the default depth function.
+  void ResetDepthFunc();
 
   // Bind a texture to a given texture unit. Combines glActiveTexture and
   // glBindTexture.
   void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture);
 
+  // Bind a texture to the specified texture unit and set its texture wrap
+  // mode.
+  void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture,
+                         GLint texture_wrap_mode);
+
   // Set the clip adjustment to be used with vertex shaders. This transforms
   // the vertex coordinates from view space to clip space.
   void SetClipAdjustment(const math::Size& render_target_size);
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index 59958a5..2cc8005 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -20,55 +20,30 @@
 #include "base/debug/trace_event.h"
 #include "base/memory/scoped_vector.h"
 #include "base/threading/thread_checker.h"
-#include "cobalt/math/size.h"
-#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 #include "cobalt/renderer/backend/egl/graphics_system.h"
 #include "cobalt/renderer/backend/egl/texture.h"
 #include "cobalt/renderer/backend/egl/utils.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
 #include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
 #include "cobalt/renderer/rasterizer/egl/graphics_state.h"
-#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
+#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
 #include "cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h"
 #include "cobalt/renderer/rasterizer/egl/shader_program_manager.h"
+#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/core/SkSurfaceProps.h"
 #include "third_party/skia/include/gpu/GrContext.h"
-#include "third_party/skia/include/gpu/GrRenderTarget.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
 
 namespace cobalt {
 namespace renderer {
 namespace rasterizer {
 namespace egl {
 
-namespace {
-
-int32_t NextPowerOf2(int32_t num) {
-  // Return the smallest power of 2 that is greater than or equal to num.
-  // This flips on all bits <= num, then num+1 will be the next power of 2.
-  --num;
-  num |= num >> 1;
-  num |= num >> 2;
-  num |= num >> 4;
-  num |= num >> 8;
-  num |= num >> 16;
-  return num + 1;
-}
-
-bool IsPowerOf2(int32_t num) {
-  return (num & (num - 1)) == 0;
-}
-
-}  // namespace
-
 class HardwareRasterizer::Impl {
  public:
   explicit Impl(backend::GraphicsContext* graphics_context,
+                int skia_atlas_width, int skia_atlas_height,
                 int skia_cache_size_in_bytes,
                 int scratch_surface_cache_size_in_bytes,
                 int surface_cache_size_in_bytes);
@@ -80,75 +55,52 @@
 
   void SubmitToFallbackRasterizer(
       const scoped_refptr<render_tree::Node>& render_tree,
-      const math::RectF& viewport,
-      const backend::TextureEGL** out_texture,
-      math::Matrix3F* out_texcoord_transform);
+      const math::Matrix3F& transform,
+      const OffscreenTargetManager::TargetInfo& target);
 
   render_tree::ResourceProvider* GetResourceProvider() {
     return fallback_rasterizer_->GetResourceProvider();
   }
 
  private:
-  // Use an atlas for offscreen targets.
-  struct OffscreenAtlas {
-    explicit OffscreenAtlas(const math::Size& size) : allocator(size) {}
-    RectAllocator allocator;
-    scoped_ptr<backend::FramebufferEGL> framebuffer;
-    SkAutoTUnref<SkSurface> skia_surface;
-  };
-
   GrContext* GetFallbackContext() {
     return fallback_rasterizer_->GetGrContext();
   }
 
-  void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree);
+  void ResetFallbackContextDuringFrame();
 
-  void AllocateOffscreenTarget(const math::SizeF& size,
-      OffscreenAtlas** out_atlas, math::RectF* out_target_rect);
-
-  OffscreenAtlas* AddOffscreenAtlas(const math::Size& size);
+  void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree,
+                     backend::RenderTargetEGL* render_target);
 
   scoped_ptr<skia::HardwareRasterizer> fallback_rasterizer_;
   scoped_ptr<GraphicsState> graphics_state_;
   scoped_ptr<ShaderProgramManager> shader_program_manager_;
+  scoped_ptr<OffscreenTargetManager> offscreen_target_manager_;
 
   backend::GraphicsContextEGL* graphics_context_;
-  FrameRateThrottler frame_rate_throttler_;
   base::ThreadChecker thread_checker_;
-
-  typedef ScopedVector<OffscreenAtlas> OffscreenAtlasList;
-  OffscreenAtlasList offscreen_atlases_;
-
-  // Size of the smallest offscreen target atlas that can hold all offscreen
-  // targets requested this frame.
-  math::Size offscreen_atlas_size_;
-
-  // Align offscreen targets to a particular size to more efficiently use the
-  // offscreen target atlas. Use a power of 2 for the alignment so that a bit
-  // mask can be used for the alignment calculation.
-  math::Size offscreen_target_size_mask_;
 };
 
-HardwareRasterizer::Impl::Impl(
-    backend::GraphicsContext* graphics_context,
-    int skia_cache_size_in_bytes,
-    int scratch_surface_cache_size_in_bytes,
-    int surface_cache_size_in_bytes)
+HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+                               int skia_atlas_width, int skia_atlas_height,
+                               int skia_cache_size_in_bytes,
+                               int scratch_surface_cache_size_in_bytes,
+                               int surface_cache_size_in_bytes)
     : fallback_rasterizer_(new skia::HardwareRasterizer(
-          graphics_context,
-          skia_cache_size_in_bytes,
-          scratch_surface_cache_size_in_bytes,
-          surface_cache_size_in_bytes)),
+          graphics_context, skia_atlas_width, skia_atlas_height,
+          skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
+          0 /* fallback rasterizer should not use a surface cache */)),
       graphics_context_(
           base::polymorphic_downcast<backend::GraphicsContextEGL*>(
-              graphics_context)),
-      offscreen_atlas_size_(0, 0),
-      offscreen_target_size_mask_(0, 0) {
+              graphics_context)) {
+  DLOG(INFO) << "surface_cache_size_in_bytes: " << surface_cache_size_in_bytes;
+
   backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
       graphics_context_);
-
   graphics_state_.reset(new GraphicsState());
   shader_program_manager_.reset(new ShaderProgramManager());
+  offscreen_target_manager_.reset(new OffscreenTargetManager(
+      graphics_context_, GetFallbackContext(), surface_cache_size_in_bytes));
 }
 
 HardwareRasterizer::Impl::~Impl() {
@@ -156,8 +108,7 @@
       graphics_context_);
 
   GL_CALL(glFinish());
-  offscreen_atlases_.clear();
-
+  offscreen_target_manager_.reset();
   shader_program_manager_.reset();
   graphics_state_.reset();
 }
@@ -174,6 +125,16 @@
   backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
       graphics_context_, render_target_egl);
 
+  // Make sure this render target has a depth buffer. This is only relevant
+  // for framebuffer render targets. Other render target types should already
+  // have a depth buffer set up by the graphics system's config.
+  if (render_target_egl->GetSurface() == EGL_NO_SURFACE) {
+    backend::FramebufferRenderTargetEGL* framebuffer_render_target =
+        base::polymorphic_downcast<backend::FramebufferRenderTargetEGL*>(
+            render_target_egl);
+    framebuffer_render_target->EnsureDepthBufferAttached(GL_DEPTH_COMPONENT16);
+  }
+
   fallback_rasterizer_->AdvanceFrame();
 
   const math::Size& target_size = render_target->GetSize();
@@ -189,81 +150,75 @@
     graphics_state_->Scissor(0, 0, target_size.width(), target_size.height());
   }
 
-  // Set initial characteristics for offscreen target handling.
-  if (offscreen_atlas_size_.IsEmpty()) {
-    if (target_size.width() >= 64 && target_size.height() >= 64) {
-      offscreen_atlas_size_.SetSize(
-          NextPowerOf2(target_size.width() / 16),
-          NextPowerOf2(target_size.height() / 16));
-      offscreen_target_size_mask_.SetSize(
-          NextPowerOf2(target_size.width() / 64) - 1,
-          NextPowerOf2(target_size.height() / 64) - 1);
-    } else {
-      offscreen_atlas_size_.SetSize(16, 16);
-      offscreen_target_size_mask_.SetSize(0, 0);
-    }
-  }
+  offscreen_target_manager_->Update(target_size);
 
-  // Keep only the largest offscreen target atlas.
-  while (offscreen_atlases_.size() > 1) {
-    offscreen_atlases_.erase(offscreen_atlases_.begin());
-  }
+  RasterizeTree(render_tree, render_target_egl);
 
-  if (!offscreen_atlases_.empty()) {
-    offscreen_atlases_.back()->allocator.Reset();
-    offscreen_atlases_.back()->skia_surface->getCanvas()->clear(
-        SK_ColorTRANSPARENT);
-  }
-
-  RasterizeTree(render_tree);
-
-  frame_rate_throttler_.EndInterval();
   graphics_context_->SwapBuffers(render_target_egl);
-  frame_rate_throttler_.BeginInterval();
 }
 
 void HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const math::RectF& viewport,
-    const backend::TextureEGL** out_texture,
-    math::Matrix3F* out_texcoord_transform) {
+    const math::Matrix3F& transform,
+    const OffscreenTargetManager::TargetInfo& target) {
   DCHECK(thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("cobalt::renderer", "SubmitToFallbackRasterizer");
 
-  math::RectF target_rect(0, 0, 0, 0);
-  OffscreenAtlas* atlas = NULL;
-  AllocateOffscreenTarget(viewport.size(), &atlas, &target_rect);
-
-  backend::FramebufferEGL* framebuffer = atlas->framebuffer.get();
-  SkCanvas* canvas = atlas->skia_surface->getCanvas();
-
   // Use skia to rasterize to the allocated offscreen target.
-  canvas->save();
-  canvas->clipRect(SkRect::MakeXYWH(
-      target_rect.x(), target_rect.y(),
-      viewport.width() + 0.5f, viewport.height() + 0.5f));
-  canvas->translate(viewport.x() + target_rect.x(),
-                    viewport.y() + target_rect.y());
-  fallback_rasterizer_->SubmitOffscreen(render_tree, canvas);
-  canvas->restore();
+  target.skia_canvas->save();
 
-  float scale_x = 1.0f / framebuffer->GetSize().width();
-  float scale_y = 1.0f / framebuffer->GetSize().height();
-  *out_texcoord_transform = math::Matrix3F::FromValues(
-      viewport.width() * scale_x, 0, target_rect.x() * scale_x,
-      0, viewport.height() * scale_y, target_rect.y() * scale_y,
-      0, 0, 1);
-  *out_texture = framebuffer->GetColorTexture();
+  if (target.is_scratch_surface) {
+    // The scratch surface is used immediately after rendering to it. So we
+    // are switching from this rasterizer to skia, then will switch back to
+    // our rasterizer context.
+    ResetFallbackContextDuringFrame();
+    target.skia_canvas->clear(SK_ColorTRANSPARENT);
+  }
+
+  target.skia_canvas->clipRect(SkRect::MakeXYWH(
+      target.region.x(), target.region.y(),
+      target.region.width(), target.region.height()));
+  target.skia_canvas->translate(target.region.x(), target.region.y());
+  target.skia_canvas->concat(skia::CobaltMatrixToSkia(transform));
+  fallback_rasterizer_->SubmitOffscreen(render_tree, target.skia_canvas);
+
+  if (target.is_scratch_surface) {
+    // Flush the skia draw calls so the contents can be used immediately.
+    target.skia_canvas->flush();
+
+    // Switch back to the current render target and context.
+    graphics_context_->ResetCurrentSurface();
+    graphics_state_->SetDirty();
+  }
+
+  target.skia_canvas->restore();
+}
+
+void HardwareRasterizer::Impl::ResetFallbackContextDuringFrame() {
+  // Perform a minimal reset of the fallback context. Only need to invalidate
+  // states that this rasterizer pollutes.
+  uint32_t untouched_states = kMSAAEnable_GrGLBackendState |
+      kStencil_GrGLBackendState | kPixelStore_GrGLBackendState |
+      kFixedFunction_GrGLBackendState | kPathRendering_GrGLBackendState;
+
+  // Manually reset a subset of kMisc_GrGLBackendState
+  untouched_states |= kMisc_GrGLBackendState;
+  GL_CALL(glDisable(GL_DEPTH_TEST));
+  GL_CALL(glDepthMask(GL_FALSE));
+
+  GetFallbackContext()->resetContext(~untouched_states & kAll_GrBackendState);
 }
 
 void HardwareRasterizer::Impl::RasterizeTree(
-    const scoped_refptr<render_tree::Node>& render_tree) {
+    const scoped_refptr<render_tree::Node>& render_tree,
+    backend::RenderTargetEGL* render_target) {
   DrawObjectManager draw_object_manager;
   RenderTreeNodeVisitor::FallbackRasterizeFunction fallback_rasterize =
       base::Bind(&HardwareRasterizer::Impl::SubmitToFallbackRasterizer,
                  base::Unretained(this));
   RenderTreeNodeVisitor visitor(graphics_state_.get(),
                                 &draw_object_manager,
+                                offscreen_target_manager_.get(),
                                 &fallback_rasterize);
 
   // Traverse the render tree to populate the draw object manager.
@@ -277,6 +232,8 @@
   // Rasterize to offscreen targets using skia.
   {
     TRACE_EVENT0("cobalt::renderer", "OffscreenRasterize");
+    backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
+        graphics_context_, render_target);
 
     // Reset the skia graphics context since the egl rasterizer dirtied it.
     GetFallbackContext()->resetContext();
@@ -285,10 +242,7 @@
 
     {
       TRACE_EVENT0("cobalt::renderer", "Skia Flush");
-      for (OffscreenAtlasList::iterator iter = offscreen_atlases_.begin();
-           iter != offscreen_atlases_.end(); ++iter) {
-        (*iter)->skia_surface->getCanvas()->flush();
-      }
+      offscreen_target_manager_->Flush();
     }
 
     // Reset the egl graphics state since skia dirtied it.
@@ -314,104 +268,14 @@
   graphics_state_->EndFrame();
 }
 
-void HardwareRasterizer::Impl::AllocateOffscreenTarget(
-    const math::SizeF& size,
-    OffscreenAtlas** out_atlas, math::RectF* out_target_rect) {
-  // Get an offscreen target for rendering. Align up the requested target size
-  // to improve usage of the atlas (since more requests will have the same
-  // aligned width or height).
-  DCHECK(IsPowerOf2(offscreen_target_size_mask_.width() + 1));
-  DCHECK(IsPowerOf2(offscreen_target_size_mask_.height() + 1));
-  math::Size target_size(
-      (static_cast<int>(size.width() + 0.5f) +
-          offscreen_target_size_mask_.width()) &
-          ~offscreen_target_size_mask_.width(),
-      (static_cast<int>(size.height() + 0.5f) +
-          offscreen_target_size_mask_.height()) &
-          ~offscreen_target_size_mask_.height());
-  math::Rect target_rect(0, 0, 0, 0);
-  OffscreenAtlas* atlas = NULL;
-
-  // See if there's room in the most recently created offscreen target atlas.
-  // Don't search any other atlases since we want to quickly find an offscreen
-  // atlas size large enough to hold all offscreen targets needed in a frame.
-  if (!offscreen_atlases_.empty()) {
-    atlas = offscreen_atlases_.back();
-    target_rect = atlas->allocator.Allocate(target_size);
-  }
-
-  if (target_rect.IsEmpty()) {
-    // Create a new offscreen atlas, bigger than the previous, so that
-    // eventually only one offscreen atlas is needed per frame.
-    bool grew = false;
-    if (offscreen_atlas_size_.width() < target_size.width()) {
-      offscreen_atlas_size_.set_width(NextPowerOf2(target_size.width()));
-      grew = true;
-    }
-    if (offscreen_atlas_size_.height() < target_size.height()) {
-      offscreen_atlas_size_.set_height(NextPowerOf2(target_size.height()));
-      grew = true;
-    }
-    if (!grew) {
-      // Grow the offscreen atlas while keeping it square-ish.
-      if (offscreen_atlas_size_.width() <= offscreen_atlas_size_.height()) {
-        offscreen_atlas_size_.set_width(offscreen_atlas_size_.width() * 2);
-      } else {
-        offscreen_atlas_size_.set_height(offscreen_atlas_size_.height() * 2);
-      }
-    }
-
-    atlas = AddOffscreenAtlas(offscreen_atlas_size_);
-    target_rect = atlas->allocator.Allocate(target_size);
-  }
-  DCHECK(!target_rect.IsEmpty());
-
-  *out_atlas = atlas;
-  *out_target_rect = target_rect;
-}
-
-HardwareRasterizer::Impl::OffscreenAtlas*
-    HardwareRasterizer::Impl::AddOffscreenAtlas(const math::Size& size) {
-  OffscreenAtlas* atlas = new OffscreenAtlas(offscreen_atlas_size_);
-  offscreen_atlases_.push_back(atlas);
-
-  // Create a new framebuffer.
-  atlas->framebuffer.reset(new backend::FramebufferEGL(
-      graphics_context_, offscreen_atlas_size_, GL_RGBA, GL_NONE));
-
-  // Wrap the framebuffer as a skia surface.
-  GrBackendRenderTargetDesc skia_desc;
-  skia_desc.fWidth = offscreen_atlas_size_.width();
-  skia_desc.fHeight = offscreen_atlas_size_.height();
-  skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
-  skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-  skia_desc.fSampleCnt = 0;
-  skia_desc.fStencilBits = 0;
-  skia_desc.fRenderTargetHandle = atlas->framebuffer->gl_handle();
-
-  SkAutoTUnref<GrRenderTarget> skia_render_target(
-      GetFallbackContext()->wrapBackendRenderTarget(skia_desc));
-  SkSurfaceProps skia_surface_props(
-      SkSurfaceProps::kUseDistanceFieldFonts_Flag,
-      SkSurfaceProps::kLegacyFontHost_InitType);
-  atlas->skia_surface.reset(SkSurface::NewRenderTargetDirect(
-      skia_render_target, &skia_surface_props));
-
-  atlas->skia_surface->getCanvas()->clear(SK_ColorTRANSPARENT);
-
-  return atlas;
-}
-
 HardwareRasterizer::HardwareRasterizer(
-    backend::GraphicsContext* graphics_context,
-    int skia_cache_size_in_bytes,
-    int scratch_surface_cache_size_in_bytes,
-    int surface_cache_size_in_bytes)
-    : impl_(new Impl(graphics_context,
+    backend::GraphicsContext* graphics_context, int skia_atlas_width,
+    int skia_atlas_height, int skia_cache_size_in_bytes,
+    int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
+    : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
                      skia_cache_size_in_bytes,
                      scratch_surface_cache_size_in_bytes,
-                     surface_cache_size_in_bytes)) {
-}
+                     surface_cache_size_in_bytes)) {}
 
 void HardwareRasterizer::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
index e136b7f..dc5c9b4 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
@@ -46,6 +46,7 @@
   // a surface cache such that expensive render tree nodes seen multiple times
   // will get saved to offscreen surfaces.
   explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
+                              int skia_atlas_width, int skia_atlas_height,
                               int skia_cache_size_in_bytes,
                               int scratch_surface_cache_size_in_bytes,
                               int surface_cache_size_in_bytes);
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
new file mode 100644
index 0000000..a530a84
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
@@ -0,0 +1,365 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
+
+#include <algorithm>
+
+#include "base/hash_tables.h"
+#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkSurfaceProps.h"
+#include "third_party/skia/include/gpu/GrRenderTarget.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
+
+namespace {
+// Structure describing the key for render target allocations in a given
+// offscreen target atlas.
+struct AllocationKey {
+  AllocationKey(const cobalt::render_tree::Node* tree_node,
+                const cobalt::math::SizeF& alloc_size)
+      : node(tree_node),
+        size(alloc_size) {}
+
+  bool operator==(const AllocationKey& other) const {
+    return node == other.node && size == other.size;
+  }
+
+  bool operator!=(const AllocationKey& other) const {
+    return node != other.node || size != other.size;
+  }
+
+  bool operator<(const AllocationKey& rhs) const {
+    return (node < rhs.node) ||
+           (node == rhs.node &&
+               (size.width() < rhs.size.width() ||
+               (size.width() == rhs.size.width() &&
+                   size.height() < rhs.size.height())));
+  }
+
+  const void* node;
+  cobalt::math::SizeF size;
+};
+}  // namespace
+
+namespace BASE_HASH_NAMESPACE {
+#if defined(BASE_HASH_USE_HASH_STRUCT)
+template <>
+struct hash<AllocationKey> {
+  size_t operator()(const AllocationKey& key) const {
+    return reinterpret_cast<size_t>(key.node);
+  }
+};
+#else
+template <>
+inline size_t hash_value<AllocationKey>(const AllocationKey& key) {
+  return reinterpret_cast<size_t>(key.node);
+}
+#endif
+}  // namespace BASE_HASH_NAMESPACE
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+namespace {
+
+typedef base::hash_map<AllocationKey, math::Rect> AllocationMap;
+
+int32_t NextPowerOf2(int32_t num) {
+  // Return the smallest power of 2 that is greater than or equal to num.
+  // This flips on all bits <= num, then num+1 will be the next power of 2.
+  --num;
+  num |= num >> 1;
+  num |= num >> 2;
+  num |= num >> 4;
+  num |= num >> 8;
+  num |= num >> 16;
+  return num + 1;
+}
+
+bool IsPowerOf2(int32_t num) {
+  return (num & (num - 1)) == 0;
+}
+
+size_t GetMemorySize(const math::Size& target_size) {
+  // RGBA uses 4 bytes per pixel. Assume no rounding to the nearest power of 2.
+  return target_size.width() * target_size.height() * 4;
+}
+
+}  // namespace
+
+struct OffscreenTargetManager::OffscreenAtlas {
+  explicit OffscreenAtlas(const math::Size& size)
+      : allocator(size),
+        allocations_used(0),
+        needs_flush(false) {}
+
+  RectAllocator allocator;
+  AllocationMap allocation_map;
+  size_t allocations_used;
+  scoped_ptr<backend::FramebufferEGL> framebuffer;
+  SkAutoTUnref<SkSurface> skia_surface;
+  bool needs_flush;
+};
+
+OffscreenTargetManager::OffscreenTargetManager(
+    backend::GraphicsContextEGL* graphics_context, GrContext* skia_context,
+    size_t memory_limit)
+    : graphics_context_(graphics_context),
+      skia_context_(skia_context),
+      offscreen_target_size_mask_(0, 0),
+      memory_limit_(memory_limit) {
+}
+
+OffscreenTargetManager::~OffscreenTargetManager() {
+}
+
+void OffscreenTargetManager::Update(const math::Size& frame_size) {
+  if (offscreen_atlases_.empty()) {
+    InitializeTargets(frame_size);
+  }
+
+  // If any of the current atlases have more allocations used than the
+  // current cache, then use that as the new cache.
+  size_t most_used_atlas_index = 0;
+  for (size_t index = 1; index < offscreen_atlases_.size(); ++index) {
+    if (offscreen_atlases_[most_used_atlas_index]->allocations_used <
+        offscreen_atlases_[index]->allocations_used) {
+      most_used_atlas_index = index;
+    }
+  }
+
+  OffscreenAtlas* most_used_atlas = offscreen_atlases_[most_used_atlas_index];
+  if (offscreen_cache_->allocations_used < most_used_atlas->allocations_used) {
+    OffscreenAtlas* new_atlas = offscreen_cache_.release();
+    offscreen_cache_.reset(offscreen_atlases_[most_used_atlas_index]);
+    offscreen_atlases_.weak_erase(offscreen_atlases_.begin() +
+                                  most_used_atlas_index);
+    offscreen_atlases_.push_back(new_atlas);
+  }
+  offscreen_cache_->allocations_used = 0;
+
+  // Reset all current atlases for use this frame.
+  for (size_t index = 0; index < offscreen_atlases_.size(); ++index) {
+    OffscreenAtlas* atlas = offscreen_atlases_[index];
+    atlas->allocator.Reset();
+    atlas->allocation_map.clear();
+    atlas->allocations_used = 0;
+  }
+}
+
+void OffscreenTargetManager::Flush() {
+  if (offscreen_cache_->needs_flush) {
+    offscreen_cache_->needs_flush = false;
+    offscreen_cache_->skia_surface->getCanvas()->flush();
+  }
+  for (size_t index = 0; index < offscreen_atlases_.size(); ++index) {
+    if (offscreen_atlases_[index]->needs_flush) {
+      offscreen_atlases_[index]->needs_flush = false;
+      offscreen_atlases_[index]->skia_surface->getCanvas()->flush();
+    }
+  }
+}
+
+bool OffscreenTargetManager::GetCachedOffscreenTarget(
+    const render_tree::Node* node, const math::SizeF& size,
+    TargetInfo* out_target_info) {
+  AllocationMap::iterator iter = offscreen_cache_->allocation_map.find(
+      AllocationKey(node, size));
+  if (iter != offscreen_cache_->allocation_map.end()) {
+    offscreen_cache_->allocations_used += 1;
+    out_target_info->framebuffer = offscreen_cache_->framebuffer.get();
+    out_target_info->skia_canvas = offscreen_cache_->skia_surface->getCanvas();
+    out_target_info->region = iter->second;
+    out_target_info->is_scratch_surface = false;
+    return true;
+  }
+  return false;
+}
+
+void OffscreenTargetManager::AllocateOffscreenTarget(
+    const render_tree::Node* node, const math::SizeF& size,
+    TargetInfo* out_target_info) {
+  // Pad the offscreen target size to prevent interpolation with unwanted
+  // texels when rendering the results.
+  const int kInterpolatePad = 1;
+
+  // Get an offscreen target for rendering. Align up the requested target size
+  // to improve usage of the atlas (since more requests will have the same
+  // aligned width or height).
+  DCHECK(IsPowerOf2(offscreen_target_size_mask_.width() + 1));
+  DCHECK(IsPowerOf2(offscreen_target_size_mask_.height() + 1));
+  math::Size target_size(
+      (static_cast<int>(size.width()) + 2 * kInterpolatePad +
+          offscreen_target_size_mask_.width()) &
+          ~offscreen_target_size_mask_.width(),
+      (static_cast<int>(size.height()) + 2 * kInterpolatePad +
+          offscreen_target_size_mask_.height()) &
+          ~offscreen_target_size_mask_.height());
+  math::Rect target_rect(0, 0, 0, 0);
+  OffscreenAtlas* atlas = NULL;
+
+  // See if there's room in the offscreen cache for additional targets.
+  atlas = offscreen_cache_.get();
+  target_rect = atlas->allocator.Allocate(target_size);
+
+  if (target_rect.IsEmpty()) {
+    // See if there's room in the other atlases.
+    for (size_t index = offscreen_atlases_.size(); index > 0;) {
+      atlas = offscreen_atlases_[--index];
+      target_rect = atlas->allocator.Allocate(target_size);
+      if (!target_rect.IsEmpty()) {
+        break;
+      }
+    }
+  }
+
+  // Use the scratch surface if needed.
+  bool scratch_surface_used = false;
+  if (target_rect.IsEmpty()) {
+    scratch_surface_used = true;
+    atlas = scratch_surface_.get();
+    target_rect = math::Rect(atlas->framebuffer->GetSize());
+  } else {
+    // Inset to prevent interpolation with unwanted pixels at the edge.
+    target_rect.Inset(kInterpolatePad, kInterpolatePad);
+
+    // Clear the atlas if this will be the first draw into it.
+    if (atlas->allocation_map.empty()) {
+      atlas->skia_surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+    }
+
+    atlas->allocation_map.insert(AllocationMap::value_type(
+        AllocationKey(node, size), target_rect));
+    atlas->allocations_used += 1;
+    atlas->needs_flush = true;
+  }
+
+  out_target_info->framebuffer = atlas->framebuffer.get();
+  out_target_info->skia_canvas = atlas->skia_surface->getCanvas();
+  out_target_info->region = target_rect;
+  out_target_info->is_scratch_surface = scratch_surface_used;
+}
+
+void OffscreenTargetManager::InitializeTargets(const math::Size& frame_size) {
+  DLOG(INFO) << "offscreen render target memory limit: " << memory_limit_;
+
+  if (frame_size.width() >= 64 && frame_size.height() >= 64) {
+    offscreen_target_size_mask_.SetSize(
+        NextPowerOf2(frame_size.width() / 64) - 1,
+        NextPowerOf2(frame_size.height() / 64) - 1);
+  } else {
+    offscreen_target_size_mask_.SetSize(0, 0);
+  }
+
+  math::Size max_size(NextPowerOf2(frame_size.width()) / 2,
+                      NextPowerOf2(frame_size.height()) / 2);
+  max_size.SetToMax(math::Size(1, 1));
+
+  // A full-screen scratch surface is required. This avoids pixelation when an
+  // offscreen target is needed.
+  scratch_surface_.reset(CreateOffscreenAtlas(frame_size));
+
+  // Other offscreen render targets are optional but highly recommended. These
+  // allow caching of render results for improved performance. At least two
+  // must exist -- one for the cache and the other a working scratch.
+  size_t half_memory_limit = memory_limit_ / 2;
+  math::Size atlas_size(1, 1);
+  for (;;) {
+    if (atlas_size == max_size) {
+      break;
+    }
+    if (GetMemorySize(atlas_size) * 2 <= half_memory_limit) {
+      // Memory limit allows a bigger atlas.
+      if (atlas_size.width() <= atlas_size.height()) {
+        // Prefer growing by width.
+        if (atlas_size.width() < max_size.width()) {
+          atlas_size.set_width(atlas_size.width() * 2);
+        } else {
+          atlas_size.set_height(atlas_size.height() * 2);
+        }
+      } else {
+        // Prefer growing by height.
+        if (atlas_size.height() < max_size.height()) {
+          atlas_size.set_height(atlas_size.height() * 2);
+        } else {
+          atlas_size.set_width(atlas_size.width() * 2);
+        }
+      }
+    } else {
+      break;
+    }
+  }
+
+  // It is better to have fewer, large atlases than many small atlases to
+  // minimize the cost of switching render targets. Consider changing the
+  // max_size logic if there's plenty of memory to spare.
+  const int kMaxAtlases = 4;
+  int num_atlases = memory_limit_ / GetMemorySize(atlas_size);
+  if (num_atlases < 2) {
+    // Must have at least two atlases -- even if they are of a token size.
+    // This simplifies code elsewhere.
+    DCHECK(atlas_size.width() == 1 && atlas_size.height() == 1);
+    num_atlases = 2;
+  } else if (num_atlases > kMaxAtlases) {
+    DCHECK(atlas_size == max_size);
+    num_atlases = kMaxAtlases;
+    DLOG(WARNING) << "More memory was allotted for offscreen render targets"
+                  << " than will be used.";
+  }
+  offscreen_cache_.reset(CreateOffscreenAtlas(atlas_size));
+  for (int i = 1; i < num_atlases; ++i) {
+    offscreen_atlases_.push_back(CreateOffscreenAtlas(atlas_size));
+  }
+
+  DLOG(INFO) << "Created " << num_atlases << " offscreen atlases of size "
+             << atlas_size.width() << " x " << atlas_size.height();
+}
+
+OffscreenTargetManager::OffscreenAtlas*
+    OffscreenTargetManager::CreateOffscreenAtlas(const math::Size& size) {
+  OffscreenAtlas* atlas = new OffscreenAtlas(size);
+
+  // Create a new framebuffer.
+  atlas->framebuffer.reset(new backend::FramebufferEGL(
+      graphics_context_, size, GL_RGBA, GL_NONE));
+
+  // Wrap the framebuffer as a skia surface.
+  GrBackendRenderTargetDesc skia_desc;
+  skia_desc.fWidth = size.width();
+  skia_desc.fHeight = size.height();
+  skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
+  skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+  skia_desc.fSampleCnt = 0;
+  skia_desc.fStencilBits = 0;
+  skia_desc.fRenderTargetHandle = atlas->framebuffer->gl_handle();
+
+  SkAutoTUnref<GrRenderTarget> skia_render_target(
+      skia_context_->wrapBackendRenderTarget(skia_desc));
+  SkSurfaceProps skia_surface_props(
+      SkSurfaceProps::kUseDistanceFieldFonts_Flag,
+      SkSurfaceProps::kLegacyFontHost_InitType);
+  atlas->skia_surface.reset(SkSurface::NewRenderTargetDirect(
+      skia_render_target, &skia_surface_props));
+
+  return atlas;
+}
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
new file mode 100644
index 0000000..5d23334
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
@@ -0,0 +1,102 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "cobalt/math/rect_f.h"
+#include "cobalt/math/size.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Manage allocating and caching offscreen render targets for render_tree::Node
+// rendering. This uses atlases instead of individual render targets to
+// minimize the cost of switching render targets.
+class OffscreenTargetManager {
+ public:
+  struct TargetInfo {
+    TargetInfo()
+        : framebuffer(NULL),
+          skia_canvas(NULL),
+          is_scratch_surface(false) {}
+    backend::FramebufferEGL* framebuffer;
+    SkCanvas* skia_canvas;
+    math::RectF region;
+    bool is_scratch_surface;
+  };
+
+  OffscreenTargetManager(backend::GraphicsContextEGL* graphics_context,
+                         GrContext* skia_context, size_t memory_limit);
+  ~OffscreenTargetManager();
+
+  // Update must be called once per frame, before any allocation requests are
+  // made for that frame.
+  void Update(const math::Size& frame_size);
+
+  // Flush all render targets that have been used thus far.
+  void Flush();
+
+  // Return whether a cached version of the requested render target is
+  // available. If a cache does exist, then the output parameters are set,
+  // otherwise, they are untouched.
+  // The returned values are only valid until the next call to Update().
+  bool GetCachedOffscreenTarget(
+      const render_tree::Node* node, const math::SizeF& size,
+      TargetInfo* out_target_info);
+
+  // Allocate an offscreen target of the specified size.
+  // The returned values are only valid until the next call to Update().
+  void AllocateOffscreenTarget(
+      const render_tree::Node* node, const math::SizeF& size,
+      TargetInfo* out_target_info);
+
+ private:
+  // Use an atlas for offscreen targets.
+  struct OffscreenAtlas;
+
+  void InitializeTargets(const math::Size& frame_size);
+  OffscreenAtlas* CreateOffscreenAtlas(const math::Size& size);
+
+  backend::GraphicsContextEGL* graphics_context_;
+  GrContext* skia_context_;
+
+  ScopedVector<OffscreenAtlas> offscreen_atlases_;
+  scoped_ptr<OffscreenAtlas> offscreen_cache_;
+  scoped_ptr<OffscreenAtlas> scratch_surface_;
+
+  // Align offscreen targets to a particular size to more efficiently use the
+  // offscreen target atlas. Use a power of 2 for the alignment so that a bit
+  // mask can be used for the alignment calculation.
+  math::Size offscreen_target_size_mask_;
+
+  // Maximum number of bytes that can be used for offscreen atlases.
+  size_t memory_limit_;
+};
+
+}  // namespace egl
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
index ebb05ff..6ff941a 100644
--- a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
@@ -37,6 +37,8 @@
       'type': 'static_library',
 
       'sources': [
+        'draw_depth_stencil.h',
+        'draw_depth_stencil.cc',
         'draw_object.h',
         'draw_object.cc',
         'draw_object_manager.h',
@@ -45,12 +47,20 @@
         'draw_poly_color.cc',
         'draw_rect_color_texture.h',
         'draw_rect_color_texture.cc',
+        'draw_rect_linear_gradient.h',
+        'draw_rect_linear_gradient.cc',
+        'draw_rect_shadow_spread.h',
+        'draw_rect_shadow_spread.cc',
+        'draw_rect_shadow_blur.h',
+        'draw_rect_shadow_blur.cc',
         'draw_rect_texture.h',
         'draw_rect_texture.cc',
         'graphics_state.h',
         'graphics_state.cc',
         'hardware_rasterizer.cc',
         'hardware_rasterizer.h',
+        'offscreen_target_manager.h',
+        'offscreen_target_manager.cc',
         'rect_allocator.h',
         'rect_allocator.cc',
         'render_tree_node_visitor.h',
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index bc07839..9622bb7 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -18,11 +18,17 @@
 #include <cmath>
 
 #include "base/debug/trace_event.h"
+#include "base/optional.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/base/type_id.h"
 #include "cobalt/math/matrix3_f.h"
+#include "cobalt/math/transform_2d.h"
+#include "cobalt/renderer/rasterizer/common/utils.h"
 #include "cobalt/renderer/rasterizer/egl/draw_poly_color.h"
 #include "cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h"
+#include "cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h"
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h"
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h"
 #include "cobalt/renderer/rasterizer/egl/draw_rect_texture.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
 #include "cobalt/renderer/rasterizer/skia/image.h"
@@ -47,33 +53,46 @@
          matrix(0, 1) == 0 && matrix(1, 0) == 0;
 }
 
+math::Matrix3F GetTexcoordTransform(
+    const OffscreenTargetManager::TargetInfo& target) {
+  float scale_x = 1.0f / target.framebuffer->GetSize().width();
+  float scale_y = 1.0f / target.framebuffer->GetSize().height();
+  return math::Matrix3F::FromValues(
+      target.region.width() * scale_x, 0, target.region.x() * scale_x,
+      0, target.region.height() * scale_y, target.region.y() * scale_y,
+      0, 0, 1);
+}
+
 }  // namespace
 
 RenderTreeNodeVisitor::RenderTreeNodeVisitor(GraphicsState* graphics_state,
     DrawObjectManager* draw_object_manager,
+    OffscreenTargetManager* offscreen_target_manager,
     const FallbackRasterizeFunction* fallback_rasterize)
     : graphics_state_(graphics_state),
       draw_object_manager_(draw_object_manager),
+      offscreen_target_manager_(offscreen_target_manager),
       fallback_rasterize_(fallback_rasterize) {
   // Let the first draw object render in front of the clear depth.
-  draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
+  draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
 
+  draw_state_.scissor.Intersect(graphics_state->GetViewport());
   draw_state_.scissor.Intersect(graphics_state->GetScissor());
 }
 
 void RenderTreeNodeVisitor::Visit(
     render_tree::CompositionNode* composition_node) {
   const render_tree::CompositionNode::Builder& data = composition_node->data();
-  draw_state_.transform(0, 2) += data.offset().x();
-  draw_state_.transform(1, 2) += data.offset().y();
+  math::Matrix3F old_transform = draw_state_.transform;
+  draw_state_.transform = draw_state_.transform *
+      math::TranslateMatrix(data.offset().x(), data.offset().y());
   const render_tree::CompositionNode::Children& children =
       data.children();
   for (render_tree::CompositionNode::Children::const_iterator iter =
        children.begin(); iter != children.end(); ++iter) {
     (*iter)->Accept(this);
   }
-  draw_state_.transform(0, 2) -= data.offset().x();
-  draw_state_.transform(1, 2) -= data.offset().y();
+  draw_state_.transform = old_transform;
 }
 
 void RenderTreeNodeVisitor::Visit(
@@ -139,10 +158,41 @@
       !data.viewport_filter &&
       !data.blur_filter &&
       !data.map_to_mesh_filter) {
-    float old_opacity = draw_state_.opacity;
-    draw_state_.opacity *= data.opacity_filter->opacity();
+    int opacity = static_cast<int>(data.opacity_filter->opacity() * 255.0f);
+    if (opacity <= 0) {
+      // Totally transparent. Ignore the source.
+      return;
+    } else if (opacity >= 255) {
+      // Totally opaque. Render like normal.
+      data.source->Accept(this);
+      return;
+    } else if (common::utils::NodeCanRenderWithOpacity(data.source)) {
+      float old_opacity = draw_state_.opacity;
+      draw_state_.opacity *= data.opacity_filter->opacity();
+      data.source->Accept(this);
+      draw_state_.opacity = old_opacity;
+      return;
+    }
+  }
+
+  // Handle blur-only filter.
+  if (data.blur_filter &&
+      !data.viewport_filter &&
+      !data.opacity_filter &&
+      !data.map_to_mesh_filter) {
+    if (data.blur_filter->blur_sigma() == 0.0f) {
+      // Ignorable blur request. Render normally.
+      data.source->Accept(this);
+      return;
+    }
+  }
+
+  // No filter.
+  if (!data.opacity_filter &&
+      !data.viewport_filter &&
+      !data.blur_filter &&
+      !data.map_to_mesh_filter) {
     data.source->Accept(this);
-    draw_state_.opacity = old_opacity;
     return;
   }
 
@@ -165,6 +215,8 @@
 
   skia::Image* skia_image =
       base::polymorphic_downcast<skia::Image*>(data.source.get());
+  bool clamp_texcoords = false;
+  bool is_opaque = skia_image->IsOpaque() && draw_state_.opacity == 1.0f;
 
   // Ensure any required backend processing is done to create the necessary
   // GPU resource.
@@ -182,36 +234,53 @@
                                data.local_transform(0, 2);
     texcoord_transform(1, 2) = -texcoord_transform(1, 1) *
                                data.local_transform(1, 2);
+    if (texcoord_transform(0, 0) < 1.0f || texcoord_transform(1, 1) < 1.0f) {
+      // Edges may interpolate with texels outside the designated region.
+      // Use a fragment shader that clamps the texture coordinates to prevent
+      // that from happening.
+      clamp_texcoords = true;
+    }
   } else {
     texcoord_transform = data.local_transform.Inverse();
   }
 
   // Different shaders are used depending on whether the image has a single
   // plane or multiple planes.
+  scoped_ptr<DrawObject> draw;
+  DrawObjectManager::OnscreenType onscreen_type;
+
   if (skia_image->GetTypeId() == base::GetTypeId<skia::SinglePlaneImage>()) {
     skia::HardwareFrontendImage* hardware_image =
         base::polymorphic_downcast<skia::HardwareFrontendImage*>(skia_image);
-    if (hardware_image->IsOpaque() && draw_state_.opacity == 1.0f) {
-      scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
-          draw_state_, data.destination_rect,
-          hardware_image->GetTextureEGL(), texcoord_transform));
-      AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
-          DrawObjectManager::kOffscreenNone);
-    } else {
-      scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
-          draw_state_, data.destination_rect,
+    if (clamp_texcoords || !is_opaque) {
+      onscreen_type = DrawObjectManager::kOnscreenRectColorTexture;
+      draw.reset(new DrawRectColorTexture(graphics_state_, draw_state_,
+          data.destination_rect,
           render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
-          hardware_image->GetTextureEGL(), texcoord_transform));
-      AddTransparentDraw(draw.Pass(),
-          DrawObjectManager::kOnscreenRectColorTexture,
-          DrawObjectManager::kOffscreenNone, image_node->GetBounds());
+          hardware_image->GetTextureEGL(), texcoord_transform,
+          clamp_texcoords));
+    } else {
+      onscreen_type = DrawObjectManager::kOnscreenRectTexture;
+      draw.reset(new DrawRectTexture(graphics_state_, draw_state_,
+          data.destination_rect, hardware_image->GetTextureEGL(),
+          texcoord_transform));
     }
   } else if (skia_image->GetTypeId() ==
              base::GetTypeId<skia::MultiPlaneImage>()) {
     FallbackRasterize(image_node,
                       DrawObjectManager::kOffscreenSkiaMultiPlaneImage);
+    return;
   } else {
     NOTREACHED();
+    return;
+  }
+
+  if (is_opaque) {
+    AddOpaqueDraw(draw.Pass(), onscreen_type,
+        DrawObjectManager::kOffscreenNone);
+  } else {
+    AddTransparentDraw(draw.Pass(), onscreen_type,
+        DrawObjectManager::kOffscreenNone, image_node->GetBounds());
   }
 }
 
@@ -244,8 +313,9 @@
   const scoped_ptr<render_tree::Brush>& brush = data.background_brush;
 
   // Only solid color brushes are supported at this time.
-  const bool brush_supported = !brush || brush->GetTypeId() ==
-      base::GetTypeId<render_tree::SolidColorBrush>();
+  const bool brush_supported = !brush ||
+      brush->GetTypeId() == base::GetTypeId<render_tree::SolidColorBrush>() ||
+      brush->GetTypeId() == base::GetTypeId<render_tree::LinearGradientBrush>();
 
   // Borders are not supported natively by this rasterizer at this time. The
   // difficulty lies in getting anti-aliased borders and minimizing state
@@ -267,21 +337,35 @@
 
     // Handle drawing the content.
     if (brush) {
-      const render_tree::SolidColorBrush* solid_brush =
-          base::polymorphic_downcast<const render_tree::SolidColorBrush*>
-              (brush.get());
-      const render_tree::ColorRGBA& brush_color(solid_brush->color());
-      render_tree::ColorRGBA content_color(
-          brush_color.r() * brush_color.a(),
-          brush_color.g() * brush_color.a(),
-          brush_color.b() * brush_color.a(),
-          brush_color.a());
-      scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
-          draw_state_, content_rect, content_color));
-      if (draw_state_.opacity * content_color.a() == 1.0f) {
-        AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
-            DrawObjectManager::kOffscreenNone);
+      base::TypeId brush_type = brush->GetTypeId();
+      if (brush_type == base::GetTypeId<render_tree::SolidColorBrush>()) {
+        const render_tree::SolidColorBrush* solid_brush =
+            base::polymorphic_downcast<const render_tree::SolidColorBrush*>
+                (brush.get());
+        const render_tree::ColorRGBA& brush_color(solid_brush->color());
+        render_tree::ColorRGBA content_color(
+            brush_color.r() * brush_color.a(),
+            brush_color.g() * brush_color.a(),
+            brush_color.b() * brush_color.a(),
+            brush_color.a());
+        scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
+            draw_state_, content_rect, content_color));
+        if (draw_state_.opacity * content_color.a() == 1.0f) {
+          AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+              DrawObjectManager::kOffscreenNone);
+        } else {
+          AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+              DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
+        }
       } else {
+        const render_tree::LinearGradientBrush* linear_brush =
+            base::polymorphic_downcast<const render_tree::LinearGradientBrush*>
+                (brush.get());
+        scoped_ptr<DrawObject> draw(new DrawRectLinearGradient(graphics_state_,
+            draw_state_, content_rect, *linear_brush));
+        // The draw may use transparent colors or a depth stencil (but only
+        // the inclusive one, so only one depth value is needed), so make it
+        // a transparent draw.
         AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
             DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
       }
@@ -290,11 +374,95 @@
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::RectShadowNode* shadow_node) {
-  if (!IsVisible(shadow_node->GetBounds())) {
+  math::RectF node_bounds(shadow_node->GetBounds());
+  if (!IsVisible(node_bounds)) {
     return;
   }
 
-  FallbackRasterize(shadow_node, DrawObjectManager::kOffscreenSkiaShadow);
+  const render_tree::RectShadowNode::Builder& data = shadow_node->data();
+  if (data.rounded_corners) {
+    FallbackRasterize(shadow_node, DrawObjectManager::kOffscreenSkiaShadow);
+    return;
+  }
+
+  scoped_ptr<DrawObject> draw;
+  render_tree::ColorRGBA shadow_color(
+      data.shadow.color.r() * data.shadow.color.a(),
+      data.shadow.color.g() * data.shadow.color.a(),
+      data.shadow.color.b() * data.shadow.color.a(),
+      data.shadow.color.a());
+  DrawObjectManager::OnscreenType onscreen_type =
+      DrawObjectManager::kOnscreenRectShadow;
+
+  math::RectF spread_rect(data.rect);
+  spread_rect.Offset(data.shadow.offset);
+  if (data.inset) {
+    // data.rect is outermost.
+    // spread_rect is in the middle.
+    // blur_rect is innermost.
+    spread_rect.Inset(data.spread, data.spread);
+    spread_rect.Intersect(data.rect);
+    if (spread_rect.IsEmpty()) {
+      // Spread covers the whole data.rect.
+      spread_rect.set_origin(data.rect.CenterPoint());
+      spread_rect.set_size(math::SizeF());
+    }
+    if (!spread_rect.IsEmpty() && data.shadow.blur_sigma > 0.0f) {
+      math::RectF blur_rect(spread_rect);
+      math::Vector2dF blur_extent(data.shadow.BlurExtent());
+      blur_rect.Inset(blur_extent.x(), blur_extent.y());
+      blur_rect.Intersect(spread_rect);
+      if (blur_rect.IsEmpty()) {
+        // Blur covers all of spread.
+        blur_rect.set_origin(spread_rect.CenterPoint());
+        blur_rect.set_size(math::SizeF());
+      }
+      draw.reset(new DrawRectShadowBlur(graphics_state_, draw_state_,
+          blur_rect, data.rect, spread_rect, shadow_color, math::RectF(),
+          data.shadow.blur_sigma, data.inset));
+      onscreen_type = DrawObjectManager::kOnscreenRectShadowBlur;
+    } else {
+      draw.reset(new DrawRectShadowSpread(graphics_state_, draw_state_,
+          spread_rect, data.rect, shadow_color, data.rect, math::RectF()));
+    }
+  } else {
+    // blur_rect is outermost.
+    // spread_rect is in the middle (barring negative |spread| values).
+    // data.rect is innermost (though it may not overlap due to offset).
+    spread_rect.Outset(data.spread, data.spread);
+    if (spread_rect.IsEmpty()) {
+      // Negative spread shenanigans! Nothing to draw.
+      return;
+    }
+    math::RectF blur_rect(spread_rect);
+    if (data.shadow.blur_sigma > 0.0f) {
+      math::Vector2dF blur_extent(data.shadow.BlurExtent());
+      blur_rect.Outset(blur_extent.x(), blur_extent.y());
+      draw.reset(new DrawRectShadowBlur(graphics_state_, draw_state_,
+          data.rect, blur_rect, spread_rect, shadow_color, data.rect,
+          data.shadow.blur_sigma, data.inset));
+      onscreen_type = DrawObjectManager::kOnscreenRectShadowBlur;
+    } else {
+      draw.reset(new DrawRectShadowSpread(graphics_state_, draw_state_,
+          data.rect, spread_rect, shadow_color, spread_rect, data.rect));
+    }
+    node_bounds.Union(blur_rect);
+  }
+
+  // Include or exclude scissor will touch these pixels.
+  node_bounds.Union(data.rect);
+
+  // Since the depth buffer is polluted to create a stencil for pixels to be
+  // modified by the shadow, this draw must occur during the transparency
+  // pass. During this pass, all subsequent draws are guaranteed to be closer
+  // (i.e. pass the depth test) than pixels modified by previous transparency
+  // draws.
+  AddTransparentDraw(draw.Pass(), onscreen_type,
+      DrawObjectManager::kOffscreenNone, node_bounds);
+
+  // Since the box shadow draw objects use the depth stencil object, two depth
+  // values were used. So skip an additional depth value.
+  draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::TextNode* text_node) {
@@ -305,42 +473,113 @@
   FallbackRasterize(text_node, DrawObjectManager::kOffscreenSkiaText);
 }
 
-void RenderTreeNodeVisitor::FallbackRasterize(render_tree::Node* node,
+void RenderTreeNodeVisitor::FallbackRasterize(
+    scoped_refptr<render_tree::Node> node,
     DrawObjectManager::OffscreenType offscreen_type) {
   DCHECK_NE(offscreen_type, DrawObjectManager::kOffscreenNone);
 
-  // Use fallback_rasterize_ to render to an offscreen target. Add a small
-  // buffer to allow anti-aliased edges (e.g. rendered text).
-  const int kBorderWidth = 1;
   math::RectF node_bounds(node->GetBounds());
-  math::RectF viewport(kBorderWidth, kBorderWidth,
-      node_bounds.right() + 2 * kBorderWidth,
-      node_bounds.bottom() + 2 * kBorderWidth);
+  math::RectF mapped_bounds(draw_state_.transform.MapRect(node_bounds));
+  if (mapped_bounds.IsEmpty()) {
+    return;
+  }
 
-  // Adjust the draw rect to accomodate the extra border and align texels with
-  // pixels. Perform the smallest fractional pixel shift for alignment.
-  float trans_x = std::floor(draw_state_.transform(0, 2) + 0.5f) -
-      draw_state_.transform(0, 2);
-  float trans_y = std::floor(draw_state_.transform(1, 2) + 0.5f) -
-      draw_state_.transform(1, 2);
-  math::RectF draw_rect(-kBorderWidth + trans_x, -kBorderWidth + trans_y,
-      viewport.width(), viewport.height());
+  // Use the fallback rasterizer to render the tree. In order to preserve
+  // sharpness, let the fallback rasterizer handle the current transform.
+  math::Matrix3F old_transform = draw_state_.transform;
+  draw_state_.transform = math::Matrix3F::Identity();
 
+  // Request a slightly larger render target than the calculated bounds. The
+  // fallback rasterizer may use an extra pixel along the edge of anti-aliased
+  // objects.
+  const float kBorderWidth = 1.0f;
+
+  // The render target cache keys off the render_tree Node and target size.
+  // Since the size is rounded, it is possible to be a fraction of a pixel
+  // off. However, this in turn allows for a cache hit for "close-enough"
+  // renderings. To minimize offset errors, use the nearest full pixel offset.
+  const float kFractionPad = 1.0f;
+  float offset_x = std::floor(mapped_bounds.x() + 0.5f);
+  float offset_y = std::floor(mapped_bounds.y() + 0.5f);
+  math::PointF content_offset(
+      // Shift contents towards the origin of the render target.
+      kBorderWidth + kFractionPad - offset_x,
+      kBorderWidth + kFractionPad - offset_y);
+  math::SizeF content_size(
+      std::ceil(mapped_bounds.width() + 2.0f * kBorderWidth + kFractionPad),
+      std::ceil(mapped_bounds.height() + 2.0f * kBorderWidth + kFractionPad));
+  OffscreenTargetManager::TargetInfo target_info;
+  bool is_cached = offscreen_target_manager_->GetCachedOffscreenTarget(node,
+      content_size, &target_info);
+  if (!is_cached) {
+    offscreen_target_manager_->AllocateOffscreenTarget(node,
+        content_size, &target_info);
+  }
+
+  // If the render target is the scratch surface, then just render what fits
+  // onscreen for better performance.
+  if (target_info.is_scratch_surface) {
+    mapped_bounds.Outset(kBorderWidth, kBorderWidth);
+    mapped_bounds.Intersect(draw_state_.scissor);
+    if (mapped_bounds.IsEmpty()) {
+      return;
+    }
+    float left = std::floor(mapped_bounds.x());
+    float right = std::ceil(mapped_bounds.right());
+    float top = std::floor(mapped_bounds.y());
+    float bottom = std::ceil(mapped_bounds.bottom());
+    content_offset.SetPoint(-left, -top);
+    content_size.SetSize(right - left, bottom - top);
+  }
+
+  // The returned target may be larger than actually needed. Clamp to just the
+  // needed size.
+  DCHECK_LE(content_size.width(), target_info.region.width());
+  DCHECK_LE(content_size.height(), target_info.region.height());
+  target_info.region.set_size(content_size);
+  math::RectF draw_rect(-content_offset.x(), -content_offset.y(),
+      content_size.width(), content_size.height());
+
+  // Setup draw callbacks as needed.
+  base::Closure draw_offscreen;
+  base::Closure draw_onscreen;
+  if (!is_cached) {
+    // Pre-translate the contents so it starts near the origin.
+    math::Matrix3F content_transform(old_transform);
+    content_transform(0, 2) += content_offset.x();
+    content_transform(1, 2) += content_offset.y();
+
+    if (target_info.is_scratch_surface) {
+      draw_onscreen = base::Bind(*fallback_rasterize_,
+          scoped_refptr<render_tree::Node>(node), content_transform,
+          target_info);
+    } else {
+      draw_offscreen = base::Bind(*fallback_rasterize_,
+          scoped_refptr<render_tree::Node>(node), content_transform,
+          target_info);
+    }
+  }
+
+  // Create the appropriate draw object to call the draw callback, then render
+  // its results onscreen.
+  backend::TextureEGL* texture = target_info.framebuffer->GetColorTexture();
+  math::Matrix3F texcoord_transform = GetTexcoordTransform(target_info);
   if (draw_state_.opacity == 1.0f) {
     scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
-        draw_state_, draw_rect, base::Bind(*fallback_rasterize_,
-            scoped_refptr<render_tree::Node>(node), viewport)));
+        draw_state_, draw_rect, texture, texcoord_transform,
+        draw_offscreen, draw_onscreen));
     AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
         offscreen_type, draw_rect);
   } else {
     scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
         draw_state_, draw_rect, render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
-        base::Bind(*fallback_rasterize_,
-            scoped_refptr<render_tree::Node>(node), viewport)));
+        texture, texcoord_transform, draw_offscreen, draw_onscreen));
     AddTransparentDraw(draw.Pass(),
         DrawObjectManager::kOnscreenRectColorTexture, offscreen_type,
         draw_rect);
   }
+
+  draw_state_.transform = old_transform;
 }
 
 bool RenderTreeNodeVisitor::IsVisible(const math::RectF& bounds) {
@@ -354,7 +593,7 @@
     DrawObjectManager::OffscreenType offscreen_type) {
   draw_object_manager_->AddOpaqueDraw(object.Pass(), onscreen_type,
       offscreen_type);
-  draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
+  draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
 }
 
 void RenderTreeNodeVisitor::AddTransparentDraw(scoped_ptr<DrawObject> object,
@@ -363,7 +602,7 @@
     const math::RectF& local_bounds) {
   draw_object_manager_->AddTransparentDraw(object.Pass(), onscreen_type,
       offscreen_type, draw_state_.transform.MapRect(local_bounds));
-  draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
+  draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
index 704b4bb..c4e70d3 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
@@ -36,6 +36,7 @@
 #include "cobalt/renderer/rasterizer/egl/draw_object.h"
 #include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
 #include "cobalt/renderer/rasterizer/egl/graphics_state.h"
+#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
 
 namespace cobalt {
 namespace renderer {
@@ -48,13 +49,13 @@
  public:
   typedef base::Callback<void(
       const scoped_refptr<render_tree::Node>& render_tree,
-      const math::RectF& viewport,
-      const backend::TextureEGL** out_texture,
-      math::Matrix3F* out_texcoord_transform)>
+      const math::Matrix3F& transform,
+      const OffscreenTargetManager::TargetInfo& target)>
       FallbackRasterizeFunction;
 
   RenderTreeNodeVisitor(GraphicsState* graphics_state,
                         DrawObjectManager* draw_object_manager,
+                        OffscreenTargetManager* offscreen_target_manager,
                         const FallbackRasterizeFunction* fallback_rasterize);
 
   void Visit(render_tree::animations::AnimateNode* /* animate */) OVERRIDE {
@@ -71,7 +72,7 @@
   void Visit(render_tree::TextNode* text_node) OVERRIDE;
 
  private:
-  void FallbackRasterize(render_tree::Node* node,
+  void FallbackRasterize(scoped_refptr<render_tree::Node> node,
                          DrawObjectManager::OffscreenType offscreen_type);
   bool IsVisible(const math::RectF& bounds);
   void AddOpaqueDraw(scoped_ptr<DrawObject> object,
@@ -84,6 +85,7 @@
 
   GraphicsState* graphics_state_;
   DrawObjectManager* draw_object_manager_;
+  OffscreenTargetManager* offscreen_target_manager_;
   const FallbackRasterizeFunction* fallback_rasterize_;
 
   DrawObject::BaseState draw_state_;
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
new file mode 100644
index 0000000..a7831c4
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
@@ -0,0 +1,46 @@
+precision mediump float;

+uniform vec2 u_blur_radius;

+uniform vec2 u_scale_add;

+varying vec2 v_blur_position;   // Relative to the blur center.

+varying vec4 v_color;

+void main() {

+  // Distance from the blur radius.

+  // Both v_blur_position and u_blur_radius are expressed in terms of the

+  //   blur sigma.

+  vec2 pos = abs(v_blur_position) - u_blur_radius;

+  vec2 pos2 = pos * pos;

+  vec2 pos3 = pos2 * pos;

+  vec4 posx = vec4(1.0, pos.x, pos2.x, pos3.x);

+  vec4 posy = vec4(1.0, pos.y, pos2.y, pos3.y);

+

+  // Approximation of the gaussian integral from [x, +inf).

+  // http://stereopsis.com/shadowrect/

+  const vec4 klower = vec4(0.4375, -1.125, -0.75, -0.1666666);

+  const vec4 kmiddle = vec4(0.5, -0.75, 0.0, 0.3333333);

+  const vec4 kupper = vec4(0.5625, -1.125, 0.75, -0.1666666);

+

+  float gaussx = 0.0;

+  if (pos.x < -1.5) {

+    gaussx = 1.0;

+  } else if (pos.x < -0.5) {

+    gaussx = dot(posx, klower);

+  } else if (pos.x < 0.5) {

+    gaussx = dot(posx, kmiddle);

+  } else if (pos.x < 1.5) {

+    gaussx = dot(posx, kupper);

+  }

+

+  float gaussy = 0.0;

+  if (pos.y < -1.5) {

+    gaussy = 1.0;

+  } else if (pos.y < -0.5) {

+    gaussy = dot(posy, klower);

+  } else if (pos.y < 0.5) {

+    gaussy = dot(posy, kmiddle);

+  } else if (pos.y < 1.5) {

+    gaussy = dot(posy, kupper);

+  }

+

+  float alpha_scale = gaussx * gaussy * u_scale_add.x + u_scale_add.y;

+  gl_FragColor = v_color * alpha_scale;

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl
index 7a49bb4..47ec196 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl
@@ -1,7 +1,9 @@
 precision mediump float;

+uniform vec4 u_texcoord_clamp;

 uniform sampler2D u_texture;

 varying vec4 v_color;

 varying vec2 v_texcoord;

 void main() {

-  gl_FragColor = v_color * texture2D(u_texture, v_texcoord);

+  gl_FragColor = v_color * texture2D(u_texture,

+      clamp(v_texcoord, u_texcoord_clamp.xy, u_texcoord_clamp.zw));

 }

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
index a348ff6..140b6b5 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
@@ -21,9 +21,11 @@
     'shader_sources': [
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl',
     ],
   },
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl
new file mode 100644
index 0000000..0b543d1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl
@@ -0,0 +1,14 @@
+uniform vec4 u_clip_adjustment;

+uniform mat3 u_view_matrix;

+attribute vec3 a_position;

+attribute vec4 a_color;

+attribute vec2 a_blur_position;

+varying vec4 v_color;

+varying vec2 v_blur_position;

+void main() {

+  vec3 pos2d = u_view_matrix * vec3(a_position.xy, 1);

+  gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +

+                     u_clip_adjustment.zw, a_position.z, pos2d.z);

+  v_color = a_color;

+  v_blur_position = a_blur_position;

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
index a8811c8..319f6ed 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
@@ -90,9 +90,7 @@
       context_, render_target_egl);
 
   context_->Blit(output_texture->gl_handle(), 0, 0, width, height);
-  frame_rate_throttler_.EndInterval();
   context_->SwapBuffers(render_target_egl);
-  frame_rate_throttler_.BeginInterval();
 }
 
 render_tree::ResourceProvider* SoftwareRasterizer::GetResourceProvider() {
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
index 12387ad..ce906c0 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
@@ -19,7 +19,6 @@
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/renderer/backend/graphics_context.h"
 #include "cobalt/renderer/backend/render_target.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
 #include "cobalt/renderer/rasterizer/rasterizer.h"
 #include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
 
@@ -50,7 +49,6 @@
  private:
   backend::GraphicsContextEGL* context_;
   skia::SoftwareRasterizer skia_rasterizer_;
-  FrameRateThrottler frame_rate_throttler_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/lib/README.md b/src/cobalt/renderer/rasterizer/lib/README.md
new file mode 100644
index 0000000..55af499
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/README.md
@@ -0,0 +1,9 @@
+This directory provides an API for clients that build Cobalt into a lib using
+the 'cobalt_enable_lib' flag.
+
+It is highly experimental at this point and is expected to change significantly
+across releases.
+
+As a general convention, functions whose symbols are exported are defined in an
+'exported' subdirectory and functions that are required to be implemented by
+clients are defined in a 'imported' subdirectory.
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
new file mode 100644
index 0000000..8ac80c8
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -0,0 +1,145 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
+
+#include <cmath>
+#include <vector>
+
+#include "base/threading/thread_checker.h"
+#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
+#include "cobalt/renderer/rasterizer/lib/imported/graphics.h"
+#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
+#include "starboard/shared/gles/gl_call.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace lib {
+
+class ExternalRasterizer::Impl {
+ public:
+  Impl(backend::GraphicsContext* graphics_context,
+       int skia_atlas_width, int skia_atlas_height,
+       int skia_cache_size_in_bytes,
+       int scratch_surface_cache_size_in_bytes,
+       int surface_cache_size_in_bytes);
+  ~Impl();
+
+  void Submit(const scoped_refptr<render_tree::Node>& render_tree,
+              const scoped_refptr<backend::RenderTarget>& render_target);
+
+  render_tree::ResourceProvider* GetResourceProvider();
+
+ private:
+  base::ThreadChecker thread_checker_;
+
+  backend::GraphicsContextEGL* graphics_context_;
+
+  skia::HardwareRasterizer hardware_rasterizer_;
+  skia::HardwareRasterizer::Options options_;
+
+  // The main offscreen render target to use when rendering UI or rectangular
+  // video.
+  scoped_refptr<backend::RenderTarget> main_offscreen_render_target_;
+  scoped_ptr<backend::TextureEGL> main_texture_;
+  // TODO: Add in a RenderTarget for the video texture.
+};
+
+ExternalRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+                               int skia_atlas_width, int skia_atlas_height,
+                               int skia_cache_size_in_bytes,
+                               int scratch_surface_cache_size_in_bytes,
+                               int surface_cache_size_in_bytes)
+    : graphics_context_(
+          base::polymorphic_downcast<backend::GraphicsContextEGL*>(
+              graphics_context)),
+      hardware_rasterizer_(graphics_context, skia_atlas_width,
+                           skia_atlas_height, skia_cache_size_in_bytes,
+                           scratch_surface_cache_size_in_bytes,
+                           surface_cache_size_in_bytes) {
+  options_.flags = skia::HardwareRasterizer::kSubmitFlags_Clear;
+  graphics_context_->MakeCurrent();
+
+  // TODO: Import the correct size for this and any other textures from the lib
+  // client and re-generate the size as appropriate.
+  main_offscreen_render_target_ =
+      graphics_context_->CreateOffscreenRenderTarget(math::Size(1920, 1080));
+  main_texture_.reset(new backend::TextureEGL(
+      graphics_context_,
+      make_scoped_refptr(base::polymorphic_downcast<backend::RenderTargetEGL*>(
+          main_offscreen_render_target_.get()))));
+
+  CbLibOnGraphicsContextCreated();
+}
+
+ExternalRasterizer::Impl::~Impl() { graphics_context_->MakeCurrent(); }
+
+void ExternalRasterizer::Impl::Submit(
+    const scoped_refptr<render_tree::Node>& render_tree,
+    const scoped_refptr<backend::RenderTarget>& render_target) {
+  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+  glClear(0);
+  backend::RenderTargetEGL* render_target_egl =
+      base::polymorphic_downcast<backend::RenderTargetEGL*>(
+          render_target.get());
+  graphics_context_->MakeCurrentWithSurface(render_target_egl);
+
+  backend::RenderTargetEGL* main_texture_render_target_egl =
+      base::polymorphic_downcast<backend::RenderTargetEGL*>(
+          main_offscreen_render_target_.get());
+  hardware_rasterizer_.Submit(render_tree, main_offscreen_render_target_,
+                              options_);
+
+  const intptr_t texture_handle = main_texture_->GetPlatformHandle();
+  // TODO: Provide mesh data to clients for map-to-mesh playbacks and a separate
+  // video texture handle.
+  // TODO: Allow clients to specify arbitrary subtrees to render into different
+  // textures?
+  CbLibRenderFrame(texture_handle);
+
+  graphics_context_->SwapBuffers(render_target_egl);
+}
+
+render_tree::ResourceProvider* ExternalRasterizer::Impl::GetResourceProvider() {
+  return hardware_rasterizer_.GetResourceProvider();
+}
+
+ExternalRasterizer::ExternalRasterizer(
+    backend::GraphicsContext* graphics_context, int skia_atlas_width,
+    int skia_atlas_height, int skia_cache_size_in_bytes,
+    int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
+    : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
+                     skia_cache_size_in_bytes,
+                     scratch_surface_cache_size_in_bytes,
+                     surface_cache_size_in_bytes)) {}
+
+ExternalRasterizer::~ExternalRasterizer() {}
+
+void ExternalRasterizer::Submit(
+    const scoped_refptr<render_tree::Node>& render_tree,
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
+  impl_->Submit(render_tree, render_target);
+}
+
+render_tree::ResourceProvider* ExternalRasterizer::GetResourceProvider() {
+  return impl_->GetResourceProvider();
+}
+
+}  // namespace lib
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
new file mode 100644
index 0000000..49ffc32
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_LIB_EXTERNAL_RASTERIZER_H_
+#define COBALT_RENDERER_RASTERIZER_LIB_EXTERNAL_RASTERIZER_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/renderer/backend/graphics_context.h"
+#include "cobalt/renderer/backend/render_target.h"
+#include "cobalt/renderer/rasterizer/rasterizer.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace lib {
+
+// The ExternalRasterizer is the Rasterizer used by Cobalt client libs to enable
+// them to rasterize into Cobalt's system window.
+// The primary responsibilities of the ExternalRasterizer are to provide clients
+// with a window and GL context as well as rasterizing RenderTrees into
+// textures that are provided to clients to render into their own GL scene.
+class ExternalRasterizer : public Rasterizer {
+ public:
+  ExternalRasterizer(backend::GraphicsContext* graphics_context,
+                     int skia_atlas_width, int skia_atlas_height,
+                     int skia_cache_size_in_bytes,
+                     int scratch_surface_cache_size_in_bytes,
+                     int surface_cache_size_in_bytes);
+  virtual ~ExternalRasterizer();
+
+  void Submit(const scoped_refptr<render_tree::Node>& render_tree,
+              const scoped_refptr<backend::RenderTarget>& render_target,
+              const Options& options) OVERRIDE;
+
+  // Note, this will simply pipe through the HardwareRasterizer's
+  // ResourceProvider.
+  render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
+
+ private:
+  class Impl;
+  scoped_ptr<Impl> impl_;
+};
+
+}  // namespace lib
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_LIB_EXTERNAL_RASTERIZER_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/imported/graphics.h b/src/cobalt/renderer/rasterizer/lib/imported/graphics.h
new file mode 100644
index 0000000..125f85f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/imported/graphics.h
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// All imported functions defined below MUST be implemented by client
+// applications.
+
+#ifndef COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
+#define COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
+
+// TODO: Use starboard/types.h instead.
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Invoked from the rasterization thread after the GL context has been created.
+void CbLibOnGraphicsContextCreated();
+
+// Invoked as often as the platform can swap buffers from the rasterization
+// thread. |render_tree_texture_handle| corresponds to a GLint texture ID for
+// the current RenderTree.
+void CbLibRenderFrame(intptr_t render_tree_texture_handle);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/lib.gyp b/src/cobalt/renderer/rasterizer/lib/lib.gyp
new file mode 100644
index 0000000..3ff61a8
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/lib.gyp
@@ -0,0 +1,38 @@
+# 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': 'external_rasterizer',
+      'type': 'static_library',
+      'includes': [
+        '../../renderer_parameters_setup.gypi',
+      ],
+      'sources': [
+        'external_rasterizer.h',
+        'external_rasterizer.cc',
+        'renderer_module_default_options_lib.cc'
+      ],
+       'dependencies': [
+         '<(DEPTH)/base/base.gyp:base',
+         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
+         '<(DEPTH)/cobalt/renderer/rasterizer/skia/common.gyp:common',
+         '<(DEPTH)/cobalt/renderer/rasterizer/skia/rasterizer.gyp:hardware_rasterizer',
+         '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
+         '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
+       ],
+    }
+  ],
+}
diff --git a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
new file mode 100644
index 0000000..43a1a0d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/renderer_module.h"
+#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
+
+namespace cobalt {
+namespace renderer {
+
+namespace {
+scoped_ptr<rasterizer::Rasterizer> CreateRasterizer(
+    backend::GraphicsContext* graphics_context,
+    const RendererModule::Options& options) {
+  return scoped_ptr<rasterizer::Rasterizer>(
+      new rasterizer::lib::ExternalRasterizer(
+        graphics_context,
+        options.skia_texture_atlas_dimensions.width(),
+        options.skia_texture_atlas_dimensions.height(),
+        options.skia_cache_size_in_bytes,
+        options.scratch_surface_cache_size_in_bytes,
+        options.surface_cache_size_in_bytes));
+}
+}  // namespace
+
+void RendererModule::Options::SetPerPlatformDefaultOptions() {
+  // Set default options from the current build's configuration.
+  surface_cache_size_in_bytes = COBALT_SURFACE_CACHE_SIZE_IN_BYTES;
+
+  // Default to 4MB, but this may be modified externally.
+  skia_cache_size_in_bytes = 4 * 1024 * 1024;
+
+  scratch_surface_cache_size_in_bytes =
+      COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES;
+
+  // Ensure the scene is re-rasterized even if the render tree is unchanged so
+  // that headset look changes are properly rendered.
+  submit_even_if_render_tree_is_unchanged = true;
+
+  create_rasterizer_function = base::Bind(&CreateRasterizer);
+}
+
+}  // namespace renderer
+}  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index ba69cde..5700075 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -2846,6 +2846,14 @@
   TestTree(new CompositionNode(builder.Pass()));
 }
 
+TEST_F(PixelTest, DrawOffscreenImage) {
+  scoped_refptr<Node> root = CreateCascadedRectsOfDifferentColors(
+      ScaleSize(output_surface_size(), 0.5f, 0.5f));
+  scoped_refptr<Image> offscreen_image =
+      GetResourceProvider()->DrawOffscreenImage(root);
+  TestTree(new ImageNode(offscreen_image));
+}
+
 }  // namespace rasterizer
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/skia/font.cc b/src/cobalt/renderer/rasterizer/skia/font.cc
index d90bc42..bd8d0af 100644
--- a/src/cobalt/renderer/rasterizer/skia/font.cc
+++ b/src/cobalt/renderer/rasterizer/skia/font.cc
@@ -52,7 +52,9 @@
   glyph_bounds_thread_checker_.DetachFromThread();
 }
 
-SkTypeface* Font::GetSkTypeface() const { return typeface_->GetSkTypeface(); }
+SkTypeface_Cobalt* Font::GetSkTypeface() const {
+  return typeface_->GetSkTypeface();
+}
 
 render_tree::TypefaceId Font::GetTypefaceId() const {
   return typeface_->GetId();
diff --git a/src/cobalt/renderer/rasterizer/skia/font.h b/src/cobalt/renderer/rasterizer/skia/font.h
index c262783..410c3a2 100644
--- a/src/cobalt/renderer/rasterizer/skia/font.h
+++ b/src/cobalt/renderer/rasterizer/skia/font.h
@@ -25,7 +25,6 @@
 
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkTypeface.h"
 
 namespace cobalt {
 namespace renderer {
@@ -41,7 +40,11 @@
  public:
   Font(SkiaTypeface* typeface, SkScalar size);
 
-  SkTypeface* GetSkTypeface() const;
+  // Returns the contained SkTypeface_Cobalt object, which has its reference
+  // count incremented.
+  // NOTE: The caller is responsible for decrementing the reference count after
+  // finishing with the object.
+  SkTypeface_Cobalt* GetSkTypeface() const;
 
   // Returns the pixel size described by this font.
   SkScalar size() const { return size_; }
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
index 1d118f5..432ae5a 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
@@ -16,6 +16,7 @@
 
 #include "base/bind.h"
 #include "base/debug/trace_event.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/texture.h"
 #include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
 #include "cobalt/renderer/rasterizer/skia/gl_format_conversions.h"
@@ -223,6 +224,23 @@
                  base::Unretained(this), gr_context);
 }
 
+HardwareFrontendImage::HardwareFrontendImage(
+    const scoped_refptr<render_tree::Node>& root,
+    const SubmitOffscreenCallback& submit_offscreen_callback,
+    backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
+    MessageLoop* rasterizer_message_loop)
+    : is_opaque_(false),
+      size_(static_cast<int>(root->GetBounds().right()),
+            static_cast<int>(root->GetBounds().bottom())),
+      rasterizer_message_loop_(rasterizer_message_loop) {
+  TRACE_EVENT0("cobalt::renderer",
+               "HardwareFrontendImage::HardwareFrontendImage()");
+  initialize_backend_image_ =
+      base::Bind(&HardwareFrontendImage::InitializeBackendImageFromRenderTree,
+                 base::Unretained(this), root, submit_offscreen_callback,
+                 cobalt_context, gr_context);
+}
+
 HardwareFrontendImage::~HardwareFrontendImage() {
   TRACE_EVENT0("cobalt::renderer",
                "HardwareFrontendImage::~HardwareFrontendImage()");
@@ -294,6 +312,24 @@
   backend_image_->CommonInitialize(gr_context);
 }
 
+void HardwareFrontendImage::InitializeBackendImageFromRenderTree(
+    const scoped_refptr<render_tree::Node>& root,
+    const SubmitOffscreenCallback& submit_offscreen_callback,
+    backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context) {
+  DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current());
+
+  scoped_refptr<backend::FramebufferRenderTargetEGL> render_target(
+      new backend::FramebufferRenderTargetEGL(cobalt_context, size_));
+
+  submit_offscreen_callback.Run(root, render_target);
+
+  scoped_ptr<backend::TextureEGL> texture(
+      new backend::TextureEGL(cobalt_context, render_target));
+
+  backend_image_.reset(new HardwareBackendImage(texture.Pass()));
+  backend_image_->CommonInitialize(gr_context);
+}
+
 HardwareMultiPlaneImage::HardwareMultiPlaneImage(
     scoped_ptr<HardwareRawImageMemory> raw_image_memory,
     const render_tree::MultiPlaneImageDataDescriptor& descriptor,
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.h b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
index 5865c94..e6d71f2 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
@@ -31,6 +31,10 @@
 namespace rasterizer {
 namespace skia {
 
+typedef base::Callback<void(const scoped_refptr<render_tree::Node>& render_tree,
+                            const scoped_refptr<backend::RenderTarget>&
+                                render_target)> SubmitOffscreenCallback;
+
 // Wraps a Cobalt backend::TextureEGL with a Skia GrTexture, and returns the
 // Skia ref-counted GrTexture object (that takes ownership of the cobalt
 // texture).
@@ -93,6 +97,11 @@
                         GrContext* gr_context,
                         scoped_ptr<math::Rect> content_region,
                         MessageLoop* rasterizer_message_loop);
+  HardwareFrontendImage(
+      const scoped_refptr<render_tree::Node>& root,
+      const SubmitOffscreenCallback& submit_offscreen_callback,
+      backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
+      MessageLoop* rasterizer_message_loop);
 
   const math::Size& GetSize() const OVERRIDE { return size_; }
 
@@ -130,6 +139,10 @@
       intptr_t offset, const render_tree::ImageDataDescriptor& descriptor,
       backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context);
   void InitializeBackendImageFromTexture(GrContext* gr_context);
+  void InitializeBackendImageFromRenderTree(
+      const scoped_refptr<render_tree::Node>& root,
+      const SubmitOffscreenCallback& submit_offscreen_callback,
+      backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context);
 
   // Track if we have any alpha or not, which can enable optimizations in the
   // case that alpha is not present.
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index d3c5415..e569def 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -22,7 +22,6 @@
 #include "cobalt/renderer/backend/egl/graphics_system.h"
 #include "cobalt/renderer/backend/egl/texture.h"
 #include "cobalt/renderer/backend/egl/utils.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
 #include "cobalt/renderer/rasterizer/common/surface_cache.h"
 #include "cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h"
 #include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
@@ -55,7 +54,8 @@
 
 class HardwareRasterizer::Impl {
  public:
-  Impl(backend::GraphicsContext* graphics_context, int skia_cache_size_in_bytes,
+  Impl(backend::GraphicsContext* graphics_context, int skia_atlas_width,
+       int skia_atlas_height, int skia_cache_size_in_bytes,
        int scratch_surface_cache_size_in_bytes,
        int surface_cache_size_in_bytes);
   ~Impl();
@@ -69,6 +69,10 @@
   void SubmitOffscreen(const scoped_refptr<render_tree::Node>& render_tree,
                        SkCanvas* canvas);
 
+  void SubmitOffscreenToRenderTarget(
+      const scoped_refptr<render_tree::Node>& render_tree,
+      const scoped_refptr<backend::RenderTarget>& render_target);
+
   render_tree::ResourceProvider* GetResourceProvider();
   GrContext* GetGrContext();
 
@@ -98,6 +102,12 @@
   scoped_ptr<RenderTreeNodeVisitor::ScratchSurface> CreateScratchSurface(
       const math::Size& size);
 
+  void RasterizeRenderTreeToCanvas(
+      const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas);
+
+  SkCanvas* GetCanvasFromRenderTarget(
+      const scoped_refptr<backend::RenderTarget>& render_target);
+
   void ResetSkiaState();
 
   void RenderTextureEGL(const render_tree::ImageNode* image_node,
@@ -122,8 +132,6 @@
   base::optional<common::SurfaceCache> surface_cache_;
 
   base::optional<egl::TexturedMeshRenderer> textured_mesh_renderer_;
-
-  FrameRateThrottler frame_rate_throttler_;
 };
 
 namespace {
@@ -252,6 +260,42 @@
   gr_context_->resetContext();
 }
 
+namespace {
+
+// For stereoscopic video, the actual video is split (either horizontally or
+// vertically) in two, one video for the left eye and one for the right eye.
+// This function will adjust the content region rectangle to match only the
+// left eye's video region, since we are ultimately presenting to a monoscopic
+// display.
+math::Rect AdjustContentRegionForStereoMode(render_tree::StereoMode stereo_mode,
+                                            const math::Rect& content_region) {
+  switch (stereo_mode) {
+    case render_tree::kLeftRight: {
+      // Use the left half (left eye) of the video only.
+      math::Rect adjusted_content_region(content_region);
+      adjusted_content_region.set_width(content_region.width() / 2);
+      return adjusted_content_region;
+    }
+
+    case render_tree::kTopBottom: {
+      // Use the top half (left eye) of the video only.
+      math::Rect adjusted_content_region(content_region);
+      adjusted_content_region.set_height(content_region.height() / 2);
+      return adjusted_content_region;
+    }
+
+    case render_tree::kMono:
+    case render_tree::kLeftRightUnadjustedTextureCoords:
+      // No modifications needed here, pass content region through unchanged.
+      return content_region;
+  }
+
+  NOTREACHED();
+  return content_region;
+}
+
+}  // namespace
+
 void HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL(
     const render_tree::ImageNode* image_node,
     const render_tree::MapToMeshFilter& mesh_filter,
@@ -296,20 +340,28 @@
     content_region = *image->GetContentRegion();
   }
 
-  const VertexBufferObject* left_vbo =
-      base::polymorphic_downcast<HardwareMesh*>(mesh_filter.mono_mesh().get())
+  const VertexBufferObject* mono_vbo =
+      base::polymorphic_downcast<HardwareMesh*>(
+          mesh_filter.mono_mesh(math::Size(content_region.width(),
+                                           content_region.height()))
+              .get())
           ->GetVBO();
+
+  math::Rect stereo_adjusted_content_region = AdjustContentRegionForStereoMode(
+      mesh_filter.stereo_mode(), content_region);
+
   // Invoke out TexturedMeshRenderer to actually perform the draw call.
-  textured_mesh_renderer_->RenderVBO(left_vbo->GetHandle(),
-                                     left_vbo->GetVertexCount(),
-                                     left_vbo->GetDrawMode(), texture,
-                                     content_region, draw_state->transform_3d);
+  textured_mesh_renderer_->RenderVBO(
+      mono_vbo->GetHandle(), mono_vbo->GetVertexCount(),
+      mono_vbo->GetDrawMode(), texture, stereo_adjusted_content_region,
+      draw_state->transform_3d);
 
   // Let Skia know that we've modified GL state.
   gr_context_->resetContext();
 }
 
 HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+                               int skia_atlas_width, int skia_atlas_height,
                                int skia_cache_size_in_bytes,
                                int scratch_surface_cache_size_in_bytes,
                                int surface_cache_size_in_bytes)
@@ -327,8 +379,9 @@
   // Create a GrContext object that wraps the passed in Cobalt GraphicsContext
   // object.
   gr_context_.reset(GrContext::Create(
-      kCobalt_GrBackend,
-      reinterpret_cast<GrBackendContext>(graphics_context_)));
+      kCobalt_GrBackend, reinterpret_cast<GrBackendContext>(graphics_context_),
+      skia_atlas_width, skia_atlas_height));
+
   DCHECK(gr_context_);
   // The GrContext manages a resource cache internally using GrResourceCache
   // which by default caches 96MB of resources.  This is used for helping with
@@ -348,8 +401,10 @@
 
   // Setup a resource provider for resources to be used with a hardware
   // accelerated Skia rasterizer.
-  resource_provider_.reset(
-      new HardwareResourceProvider(graphics_context_, gr_context_));
+  resource_provider_.reset(new HardwareResourceProvider(
+      graphics_context_, gr_context_,
+      base::Bind(&HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget,
+                 base::Unretained(this))));
 
   graphics_context_->ReleaseCurrentContext();
 
@@ -416,39 +471,9 @@
 
   AdvanceFrame();
 
-  SkSurface* sk_output_surface;
-  SkSurfaceMap::iterator iter = sk_output_surface_map_.find(render_target);
-  if (iter == sk_output_surface_map_.end()) {
-    // Remove the least recently used SkSurface from the map if we exceed the
-    // max allowed saved surfaces.
-    if (sk_output_surface_map_.size() > kMaxSkSurfaceCount) {
-      SkSurfaceMap::iterator iter = sk_output_surface_map_.begin();
-      DLOG(WARNING) << "Erasing the SkSurface for RenderTarget " << iter->first
-        << " this should not happen often or else it means the surface map is"
-        << " probably thrashing.";
-      iter->second->unref();
-      sk_output_surface_map_.erase(iter);
-    }
-    // Setup a Skia render target that wraps the passed in Cobalt render target.
-    SkAutoTUnref<GrRenderTarget> skia_render_target(
-        gr_context_->wrapBackendRenderTarget(
-            CobaltRenderTargetToSkiaBackendRenderTargetDesc(
-                render_target.get())));
-
-    // Create an SkSurface from the render target so that we can acquire a
-    // SkCanvas object from it in Submit().
-    sk_output_surface = CreateSkiaRenderTargetSurface(skia_render_target);
-    sk_output_surface_map_[render_target] = sk_output_surface;
-  } else {
-    sk_output_surface = sk_output_surface_map_[render_target];
-    // Mark this RenderTarget/SkCanvas pair as the most recently used by
-    // popping it and re-adding it.
-    sk_output_surface_map_.erase(iter);
-    sk_output_surface_map_[render_target] = sk_output_surface;
-  }
-
   // Get a SkCanvas that outputs to our hardware render target.
-  SkCanvas* canvas = sk_output_surface->getCanvas();
+  SkCanvas* canvas = GetCanvasFromRenderTarget(render_target);
+
   canvas->save();
 
   if (options.flags & Rasterizer::kSubmitFlags_Clear) {
@@ -461,63 +486,43 @@
     }
   }
 
-  {
-    TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
-    // Rasterize the passed in render tree to our hardware render target.
-    RenderTreeNodeVisitor::CreateScratchSurfaceFunction
-        create_scratch_surface_function =
-            base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
-                       base::Unretained(this));
-    RenderTreeNodeVisitor visitor(
-        canvas, &create_scratch_surface_function,
-        base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
-                   base::Unretained(this)),
-        base::Bind(&HardwareRasterizer::Impl::RenderTextureEGL,
-                   base::Unretained(this)),
-        base::Bind(&HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL,
-                   base::Unretained(this)),
-        surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
-        surface_cache_ ? &surface_cache_.value() : NULL);
-    render_tree->Accept(&visitor);
-  }
+  // Rasterize the passed in render tree to our hardware render target.
+  RasterizeRenderTreeToCanvas(render_tree, canvas);
 
   {
     TRACE_EVENT0("cobalt::renderer", "Skia Flush");
     canvas->flush();
   }
 
-  frame_rate_throttler_.EndInterval();
   graphics_context_->SwapBuffers(render_target_egl);
-  frame_rate_throttler_.BeginInterval();
   canvas->restore();
 }
 
 void HardwareRasterizer::Impl::SubmitOffscreen(
+    const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  RasterizeRenderTreeToCanvas(render_tree, canvas);
+}
+
+void HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget(
     const scoped_refptr<render_tree::Node>& render_tree,
-    SkCanvas* canvas) {
+    const scoped_refptr<backend::RenderTarget>& render_target) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  // Caller is expected to reset gr_context as needed.
+  // Create a canvas from the render target.
+  GrBackendRenderTargetDesc skia_desc =
+      CobaltRenderTargetToSkiaBackendRenderTargetDesc(render_target.get());
+  skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+  SkAutoTUnref<GrRenderTarget> skia_render_target(
+      gr_context_->wrapBackendRenderTarget(skia_desc));
+  SkSurface* sk_output_surface =
+      CreateSkiaRenderTargetSurface(skia_render_target);
+  SkCanvas* canvas = sk_output_surface->getCanvas();
 
-  {
-    TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
-    // Rasterize the passed in render tree to our hardware render target.
-    RenderTreeNodeVisitor::CreateScratchSurfaceFunction
-        create_scratch_surface_function =
-            base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
-                       base::Unretained(this));
-    RenderTreeNodeVisitor visitor(
-        canvas, &create_scratch_surface_function,
-        base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
-                   base::Unretained(this)),
-        base::Bind(&HardwareRasterizer::Impl::RenderTextureEGL,
-                   base::Unretained(this)),
-        base::Bind(&HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL,
-                   base::Unretained(this)),
-        surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
-        surface_cache_ ? &surface_cache_.value() : NULL);
-    render_tree->Accept(&visitor);
-  }
+  // Render to the canvas and clean up.
+  RasterizeRenderTreeToCanvas(render_tree, canvas);
+  canvas->flush();
+  sk_output_surface->unref();
 }
 
 render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
@@ -581,12 +586,73 @@
   }
 }
 
+SkCanvas* HardwareRasterizer::Impl::GetCanvasFromRenderTarget(
+    const scoped_refptr<backend::RenderTarget>& render_target) {
+  SkSurface* sk_output_surface;
+  SkSurfaceMap::iterator iter = sk_output_surface_map_.find(render_target);
+  if (iter == sk_output_surface_map_.end()) {
+    // Remove the least recently used SkSurface from the map if we exceed the
+    // max allowed saved surfaces.
+    if (sk_output_surface_map_.size() > kMaxSkSurfaceCount) {
+      SkSurfaceMap::iterator iter = sk_output_surface_map_.begin();
+      DLOG(WARNING)
+          << "Erasing the SkSurface for RenderTarget " << iter->first
+          << ". This may happen nominally during movement-triggered "
+          << "replacement of SkSurfaces or else it may indicate the surface "
+          << "map is thrashing because the total number of RenderTargets ("
+          << kMaxSkSurfaceCount << ") has been exceeded.";
+      iter->second->unref();
+      sk_output_surface_map_.erase(iter);
+    }
+    // Setup a Skia render target that wraps the passed in Cobalt render target.
+    SkAutoTUnref<GrRenderTarget> skia_render_target(
+        gr_context_->wrapBackendRenderTarget(
+            CobaltRenderTargetToSkiaBackendRenderTargetDesc(
+                render_target.get())));
+
+    // Create an SkSurface from the render target so that we can acquire a
+    // SkCanvas object from it in Submit().
+    sk_output_surface = CreateSkiaRenderTargetSurface(skia_render_target);
+    sk_output_surface_map_[render_target] = sk_output_surface;
+  } else {
+    sk_output_surface = sk_output_surface_map_[render_target];
+    // Mark this RenderTarget/SkCanvas pair as the most recently used by
+    // popping it and re-adding it.
+    sk_output_surface_map_.erase(iter);
+    sk_output_surface_map_[render_target] = sk_output_surface;
+  }
+  return sk_output_surface->getCanvas();
+}
+
+void HardwareRasterizer::Impl::RasterizeRenderTreeToCanvas(
+    const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
+  TRACE_EVENT0("cobalt::renderer", "RasterizeRenderTreeToCanvas");
+  RenderTreeNodeVisitor::CreateScratchSurfaceFunction
+      create_scratch_surface_function =
+          base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
+                     base::Unretained(this));
+  RenderTreeNodeVisitor visitor(
+      canvas, &create_scratch_surface_function,
+      base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
+                 base::Unretained(this)),
+      base::Bind(&HardwareRasterizer::Impl::RenderTextureEGL,
+                 base::Unretained(this)),
+      base::Bind(&HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL,
+                 base::Unretained(this)),
+      surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
+      surface_cache_ ? &surface_cache_.value() : NULL);
+  DCHECK(render_tree);
+  render_tree->Accept(&visitor);
+}
+
 void HardwareRasterizer::Impl::ResetSkiaState() { gr_context_->resetContext(); }
 
 HardwareRasterizer::HardwareRasterizer(
-    backend::GraphicsContext* graphics_context, int skia_cache_size_in_bytes,
+    backend::GraphicsContext* graphics_context, int skia_atlas_width,
+    int skia_atlas_height, int skia_cache_size_in_bytes,
     int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
-    : impl_(new Impl(graphics_context, skia_cache_size_in_bytes,
+    : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
+                     skia_cache_size_in_bytes,
                      scratch_surface_cache_size_in_bytes,
                      surface_cache_size_in_bytes)) {}
 
@@ -602,8 +668,7 @@
 }
 
 void HardwareRasterizer::SubmitOffscreen(
-    const scoped_refptr<render_tree::Node>& render_tree,
-    SkCanvas* canvas) {
+    const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
   TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::SubmitOffscreen()");
   impl_->SubmitOffscreen(render_tree, canvas);
 }
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
index e410883..7343ca0 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
@@ -51,6 +51,7 @@
   // a surface cache such that expensive render tree nodes seen multiple times
   // will get saved to offscreen surfaces.
   explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
+                              int skia_atlas_width, int skia_atlas_height,
                               int skia_cache_size_in_bytes,
                               int scratch_surface_cache_size_in_bytes,
                               int surface_cache_size_in_bytes);
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 0a44a22..2554c09 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -28,6 +28,7 @@
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_mesh.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/typeface.h"
 #include "third_party/ots/include/opentype-sanitiser.h"
 #include "third_party/ots/include/ots-memory-stream.h"
@@ -39,59 +40,31 @@
 using cobalt::render_tree::ImageData;
 using cobalt::render_tree::RawImageMemory;
 
-namespace {
-
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
-    SB_HAS(GRAPHICS)
-
-void DecodeTargetAcquireOnRasterizer(
-    cobalt::renderer::backend::GraphicsContextEGL* context_egl,
-    SbDecodeTargetFormat format, int width, int height,
-    SbDecodeTarget* out_decode_target, base::WaitableEvent* done_event) {
-  cobalt::renderer::backend::GraphicsContextEGL::ScopedMakeCurrent
-      scoped_context(context_egl);
-
-  *out_decode_target =
-      SbDecodeTargetCreate(context_egl->system_egl()->GetDisplay(),
-                           context_egl->GetContext(), format, width, height);
-
-  done_event->Signal();
-}
-
-void DecodeTargetReleaseOnRasterizer(
-    cobalt::renderer::backend::GraphicsContextEGL* context_egl,
-    SbDecodeTarget decode_target) {
-  cobalt::renderer::backend::GraphicsContextEGL::ScopedMakeCurrent
-      scoped_context(context_egl);
-
-  SbDecodeTargetRelease(decode_target);
-}
-
-#endif  // SB_VERSION(SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION) && \
-           SB_HAS(GRAPHICS)
-
-}  // namespace
-
 namespace cobalt {
 namespace renderer {
 namespace rasterizer {
 namespace skia {
 
 HardwareResourceProvider::HardwareResourceProvider(
-    backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context)
+    backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
+    SubmitOffscreenCallback submit_offscreen_callback)
     : cobalt_context_(cobalt_context),
       gr_context_(gr_context),
+      submit_offscreen_callback_(submit_offscreen_callback),
       self_message_loop_(MessageLoop::current()) {
   // Initialize the font manager now to ensure that it doesn't get initialized
   // on multiple threads simultaneously later.
   SkSafeUnref(SkFontMgr::RefDefault());
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
-    SB_HAS(GRAPHICS)
-  decode_target_provider_.acquire = &DecodeTargetAcquire;
-  decode_target_provider_.release = &DecodeTargetRelease;
-  decode_target_provider_.context = this;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
+#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+  decode_target_graphics_context_provider_.egl_display =
+      cobalt_context_->system_egl()->GetDisplay();
+  decode_target_graphics_context_provider_.egl_context =
+      cobalt_context_->GetContext();
+  decode_target_graphics_context_provider_.gles_context_runner =
+      &HardwareResourceProvider::GraphicsContextRunner;
+  decode_target_graphics_context_provider_.gles_context_runner_context = this;
+#endif  // SB_API_VERSION >= 4 && \
            SB_HAS(GRAPHICS)
 }
 
@@ -161,8 +134,7 @@
       self_message_loop_));
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
-    SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
 scoped_refptr<render_tree::Image>
     HardwareResourceProvider::CreateImageFromSbDecodeTarget(
         SbDecodeTarget decode_target) {
@@ -198,39 +170,48 @@
       content_region.Pass(), self_message_loop_));
 }
 
+namespace {
+void RunGraphicsContextRunnerOnRasterizerThread(
+    SbDecodeTargetGlesContextRunnerTarget target_function,
+    void* target_function_context,
+    backend::GraphicsContextEGL* graphics_context,
+    base::WaitableEvent* done_event) {
+  backend::GraphicsContextEGL::ScopedMakeCurrent make_current(graphics_context);
+  target_function(target_function_context);
+  done_event->Signal();
+}
+}  // namespace
+
 // static
-SbDecodeTarget HardwareResourceProvider::DecodeTargetAcquire(void* context,
-    SbDecodeTargetFormat format, int width, int height) {
+void HardwareResourceProvider::GraphicsContextRunner(
+    SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+    SbDecodeTargetGlesContextRunnerTarget target_function,
+    void* target_function_context) {
   SbDecodeTarget decode_target = kSbDecodeTargetInvalid;
 
   HardwareResourceProvider* provider =
-      reinterpret_cast<HardwareResourceProvider*>(context);
+      reinterpret_cast<HardwareResourceProvider*>(
+          graphics_context_provider->gles_context_runner_context);
 
-  base::WaitableEvent done_event(true, false);
-
-  // Texture creation must occur on the rasterizer thread.
-  provider->self_message_loop_->PostTask(FROM_HERE,
-      base::Bind(&DecodeTargetAcquireOnRasterizer,
-                 provider->cobalt_context_,
-                 format, width, height,
-                 &decode_target, &done_event));
-  done_event.Wait();
-
-  return decode_target;
+  if (MessageLoop::current() != provider->self_message_loop_) {
+    // Post a task to the rasterizer thread to have it run the requested
+    // function, and wait for it to complete before returning.
+    base::WaitableEvent done_event(true, false);
+    provider->self_message_loop_->PostTask(
+        FROM_HERE, base::Bind(&RunGraphicsContextRunnerOnRasterizerThread,
+                              target_function, target_function_context,
+                              provider->cobalt_context_, &done_event));
+    done_event.Wait();
+  } else {
+    // If we are already on the rasterizer thread, just run the function
+    // directly.
+    backend::GraphicsContextEGL::ScopedMakeCurrent make_current(
+        provider->cobalt_context_);
+    target_function(target_function_context);
+  }
 }
 
-// static
-void HardwareResourceProvider::DecodeTargetRelease(void* context,
-    SbDecodeTarget decode_target) {
-  HardwareResourceProvider* provider =
-      reinterpret_cast<HardwareResourceProvider*>(context);
-
-  // Texture deletion must occur on the rasterizer thread.
-  provider->self_message_loop_->PostTask(
-      FROM_HERE, base::Bind(&DecodeTargetReleaseOnRasterizer,
-                            provider->cobalt_context_, decode_target));
-}
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
+#endif  // SB_API_VERSION >= 4 && \
            SB_HAS(GRAPHICS)
 
 scoped_ptr<RawImageMemory> HardwareResourceProvider::AllocateRawImageMemory(
@@ -283,14 +264,16 @@
                "HardwareResourceProvider::GetLocalTypeface()");
 
   SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
-  SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyle(
-      font_family_name, CobaltFontStyleToSkFontStyle(font_style)));
+  SkAutoTUnref<SkTypeface_Cobalt> typeface(
+      base::polymorphic_downcast<SkTypeface_Cobalt*>(
+          font_manager->matchFamilyStyle(
+              font_family_name, CobaltFontStyleToSkFontStyle(font_style))));
   return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
 }
 
 scoped_refptr<render_tree::Typeface>
 HardwareResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
-    const std::string& font_face_name) {
+    const char* font_face_name) {
   TRACE_EVENT0("cobalt::renderer",
                "HardwareResourceProvider::GetLocalTypefaceIfAvailable()");
 
@@ -298,7 +281,8 @@
   SkFontMgr_Cobalt* cobalt_font_manager =
       base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager.get());
 
-  SkTypeface* typeface = cobalt_font_manager->matchFaceName(font_face_name);
+  SkTypeface_Cobalt* typeface = base::polymorphic_downcast<SkTypeface_Cobalt*>(
+      cobalt_font_manager->MatchFaceName(font_face_name));
   if (typeface != NULL) {
     SkAutoTUnref<SkTypeface> typeface_unref_helper(typeface);
     return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
@@ -315,9 +299,11 @@
                "HardwareResourceProvider::GetCharacterFallbackTypeface()");
 
   SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
-  SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyleCharacter(
-      0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
-      character));
+  SkAutoTUnref<SkTypeface_Cobalt> typeface(
+      base::polymorphic_downcast<SkTypeface_Cobalt*>(
+          font_manager->matchFamilyStyleCharacter(
+              0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
+              character)));
   return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
 }
 
@@ -347,7 +333,9 @@
       sanitized_data.get(), static_cast<size_t>(sanitized_data.Tell())));
 
   SkAutoTUnref<SkStreamAsset> stream(new SkMemoryStream(skia_data));
-  SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromStream(stream));
+  SkAutoTUnref<SkTypeface_Cobalt> typeface(
+      base::polymorphic_downcast<SkTypeface_Cobalt*>(
+          SkTypeface::CreateFromStream(stream)));
   if (typeface) {
     return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
   } else {
@@ -385,6 +373,13 @@
   return new HardwareMesh(vertices.Pass(), draw_mode);
 }
 
+scoped_refptr<render_tree::Image> HardwareResourceProvider::DrawOffscreenImage(
+    const scoped_refptr<render_tree::Node>& root) {
+  return make_scoped_refptr(new HardwareFrontendImage(
+      root, submit_offscreen_callback_, cobalt_context_, gr_context_,
+      self_message_loop_));
+}
+
 }  // namespace skia
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 3fa75af..6532ff2 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -18,6 +18,8 @@
 #include <string>
 #include <vector>
 
+#include "cobalt/math/size.h"
+#include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/resource_provider.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
@@ -36,7 +38,8 @@
 class HardwareResourceProvider : public render_tree::ResourceProvider {
  public:
   HardwareResourceProvider(backend::GraphicsContextEGL* cobalt_context,
-                           GrContext* gr_context);
+                           GrContext* gr_context,
+                           SubmitOffscreenCallback submit_offscreen_callback);
 
   void Finish() OVERRIDE;
 
@@ -51,15 +54,16 @@
       scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
 
 #if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
   scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE;
 
   // Return the associated SbDecodeTargetProvider with the ResourceProvider,
   // if it exists.  Returns NULL if SbDecodeTarget is not supported.
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE {
-    return &decode_target_provider_;
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+    return &decode_target_graphics_context_provider_;
   }
 
   // Whether SbDecodeTargetIsSupported or not.
@@ -82,7 +86,7 @@
   // Whether SbDecodeTargetIsSupported or not.
   bool SupportsSbDecodeTarget() OVERRIDE { return false; }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 #endif  // SB_HAS(GRAPHICS)
 
   scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
@@ -98,7 +102,7 @@
       const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
 
   scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
-      const std::string& font_face_name) OVERRIDE;
+      const char* font_face_name) OVERRIDE;
 
   scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
       int32 character, render_tree::FontStyle font_style,
@@ -129,20 +133,25 @@
       scoped_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
       render_tree::Mesh::DrawMode draw_mode) OVERRIDE;
 
+  scoped_refptr<render_tree::Image> DrawOffscreenImage(
+      const scoped_refptr<render_tree::Node>& root) OVERRIDE;
+
  private:
   backend::GraphicsContextEGL* cobalt_context_;
   GrContext* gr_context_;
+  SubmitOffscreenCallback submit_offscreen_callback_;
 
   TextShaper text_shaper_;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
-    SB_HAS(GRAPHICS)
-  static SbDecodeTarget DecodeTargetAcquire(void* context,
-                                            SbDecodeTargetFormat format,
-                                            int width, int height);
-  static void DecodeTargetRelease(void* context, SbDecodeTarget decode_target);
-  SbDecodeTargetProvider decode_target_provider_;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
+#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+  static void GraphicsContextRunner(
+      SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+      SbDecodeTargetGlesContextRunnerTarget target_function,
+      void* target_function_context);
+
+  SbDecodeTargetGraphicsContextProvider
+      decode_target_graphics_context_provider_;
+#endif  // SB_API_VERSION >= 4 && \
            SB_HAS(GRAPHICS)
 
   // We keep a handle to the message loop that this resource provider was
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 9a9fa80..1e2f1cf 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -38,6 +38,7 @@
 #include "cobalt/render_tree/rounded_corners.h"
 #include "cobalt/render_tree/text_node.h"
 #include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
+#include "cobalt/renderer/rasterizer/common/utils.h"
 #include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
 #include "cobalt/renderer/rasterizer/skia/font.h"
 #include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
@@ -353,25 +354,6 @@
   return false;
 }
 
-bool SourceCanRenderWithOpacity(render_tree::Node* source) {
-  if (source->GetTypeId() == base::GetTypeId<render_tree::ImageNode>() ||
-      source->GetTypeId() == base::GetTypeId<render_tree::RectNode>()) {
-    return true;
-  } else if (source->GetTypeId() ==
-             base::GetTypeId<render_tree::CompositionNode>()) {
-    // If we are a composition of valid sources, then we also allow
-    // rendering through a viewport here.
-    render_tree::CompositionNode* composition_node =
-        base::polymorphic_downcast<render_tree::CompositionNode*>(source);
-    typedef render_tree::CompositionNode::Children Children;
-    const Children& children = composition_node->data().children();
-    if (children.size() == 1 && SourceCanRenderWithOpacity(children[0].get())) {
-      return true;
-    }
-  }
-  return false;
-}
-
 // Tries to render a mapped-to-mesh node, returning false if it fails (which
 // would happen if we try to apply a map-to-mesh filter to a render tree node
 // that we don't currently support).
@@ -478,7 +460,7 @@
       // If an opacity filter is being applied, we must render to a separate
       // texture first.
       (!filter_node->data().opacity_filter ||
-       SourceCanRenderWithOpacity(filter_node->data().source)) &&
+       common::utils::NodeCanRenderWithOpacity(filter_node->data().source)) &&
       // If transforms are applied to the viewport, then we will render to
       // a separate texture first.
       total_matrix.rectStaysRect() &&
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi b/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
index 5be8703..41bb8a1 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
@@ -27,13 +27,23 @@
     'src/ports/SkFontMgr_cobalt.cc',
     'src/ports/SkFontMgr_cobalt.h',
     'src/ports/SkFontMgr_cobalt_factory.cc',
+    'src/ports/SkFontStyleSet_cobalt.cc',
+    'src/ports/SkFontStyleSet_cobalt.h',
     'src/ports/SkFontUtil_cobalt.cc',
     'src/ports/SkFontUtil_cobalt.h',
     'src/ports/SkOSFile_cobalt.cc',
+    'src/ports/SkStream_cobalt.cc',
+    'src/ports/SkStream_cobalt.h',
+    'src/ports/SkTypeface_cobalt.cc',
+    'src/ports/SkTypeface_cobalt.h',
     'src/ports/SkTLS_cobalt.cc',
     'src/ports/SkTime_cobalt.cc',
   ],
 
+  'defines': [
+    'COBALT_SYSTEM_TYPEFACE_CACHE_CAPACITY_IN_BYTES=<(local_font_cache_size_in_bytes)',
+  ],
+
   'include_dirs': [
     '<(SHARED_INTERMEDIATE_DIR)',
   ],
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
index e7bf46f..be914e6 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
@@ -298,26 +298,6 @@
     const char* value = attributes[i + 1];
 
     switch (strlen(name)) {
-      case 9:
-        if (strncmp("font_name", name, 9) == 0) {
-          file->full_font_name = value;
-          continue;
-        }
-        break;
-      case 15:
-        if (strncmp("postscript_name", name, 15) == 0) {
-          file->postscript_name = value;
-          continue;
-        }
-        break;
-      case 6:
-        if (strncmp("weight", name, 6) == 0) {
-          if (!ParseNonNegativeInteger(value, &file->weight)) {
-            DLOG(WARNING) << "Invalid font weight [" << value << "]";
-          }
-          continue;
-        }
-        break;
       case 5:
         if (strncmp("index", name, 5) == 0) {
           if (!ParseNonNegativeInteger(value, &file->index)) {
@@ -336,6 +316,35 @@
           }
         }
         break;
+      case 6:
+        if (strncmp("weight", name, 6) == 0) {
+          if (!ParseNonNegativeInteger(value, &file->weight)) {
+            DLOG(WARNING) << "Invalid font weight [" << value << "]";
+          }
+          continue;
+        }
+        break;
+      case 9:
+        if (strncmp("font_name", name, 9) == 0) {
+          SkAutoAsciiToLC to_lowercase(value);
+          file->full_font_name = to_lowercase.lc();
+          continue;
+        }
+        break;
+      case 15:
+        if (strncmp("postscript_name", name, 15) == 0) {
+          SkAutoAsciiToLC to_lowercase(value);
+          file->postscript_name = to_lowercase.lc();
+          continue;
+        }
+        break;
+      case 25:
+        if (strncmp("disable_synthetic_bolding", name, 25) == 0) {
+          file->disable_synthetic_bolding =
+              strcmp("true", value) == 0 || strcmp("1", value) == 0;
+          continue;
+        }
+        break;
       default:
         break;
     }
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
index cb09a67..d77460a 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
@@ -14,618 +14,21 @@
 
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
 
-#include <sys/stat.h>
-#include <cmath>
-
-#include "base/at_exit.h"
-#include "base/bind.h"
 #include "base/debug/trace_event.h"
-#include "base/lazy_instance.h"
-#include "base/memory/singleton.h"
-#include "cobalt/base/c_val.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
 #include "SkData.h"
-#include "SkGraphics.h"
-#include "SkOSFile.h"
 #include "SkStream.h"
 #include "SkString.h"
-#include "SkTArray.h"
 #include "SkTSearch.h"
-#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-namespace {
-
-#ifndef SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT
-#define SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT (10 * 1024 * 1024)
-#endif
-
-const int32_t kPeriodicProcessingTimerDelayMs = 5000;
-const int32_t kReleaseInactiveSystemTypefaceOpenStreamsDelayMs = 300000;
-const int32_t kPeriodicFontCachePurgeDelayMs = 900000;
-
-// base::CVal tracking specific to fonts.
-// NOTE: These are put in their own Singleton, because Skia's lazy instance
-// implementation does not guarantee that only one SkFontMgr_Cobalt will be
-// created at a time (see skia/src/core/SkLazyPtr.h), and we cannot have
-// multiple copies of the same CVal.
-class SkFontMgrCVals {
- public:
-  static SkFontMgrCVals* GetInstance() {
-    return Singleton<SkFontMgrCVals,
-                     DefaultSingletonTraits<SkFontMgrCVals> >::get();
-  }
-
-  const base::CVal<base::cval::SizeInBytes, base::CValPublic>&
-  system_typeface_open_stream_cache_limit_in_bytes() {
-    return system_typeface_open_stream_cache_limit_in_bytes_;
-  }
-
-  base::CVal<base::cval::SizeInBytes, base::CValPublic>&
-  system_typeface_open_stream_cache_size_in_bytes() {
-    return system_typeface_open_stream_cache_size_in_bytes_;
-  }
-
-  void IncrementFontFilesLoadedCount() { ++font_files_loaded_count_; }
-
- private:
-  SkFontMgrCVals()
-      : system_typeface_open_stream_cache_limit_in_bytes_(
-            "Memory.Font.SystemTypeface.Capacity",
-            SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT,
-            "The capacity, in bytes, of the system fonts. Exceeding this "
-            "results in *inactive* fonts being released."),
-        system_typeface_open_stream_cache_size_in_bytes_(
-            "Memory.Font.SystemTypeface.Size", 0,
-            "Total number of bytes currently used by the cache."),
-        font_files_loaded_count_(
-            "Count.Font.FilesLoaded", 0,
-            "Total number of font file loads that have occurred.") {}
-
-  const base::CVal<base::cval::SizeInBytes, base::CValPublic>
-      system_typeface_open_stream_cache_limit_in_bytes_;
-  mutable base::CVal<base::cval::SizeInBytes, base::CValPublic>
-      system_typeface_open_stream_cache_size_in_bytes_;
-
-  base::CVal<int> font_files_loaded_count_;
-
-  friend struct DefaultSingletonTraits<SkFontMgrCVals>;
-  DISALLOW_COPY_AND_ASSIGN(SkFontMgrCVals);
-};
-
-// The timer used to trigger periodic processing of open streams, which
-// handles moving open streams from the active to the inactive list and
-// releasing inactive open streams. Create it as a lazy instance to ensure it
-// gets cleaned up correctly at exit, even though the SkFontMgr_Cobalt may be
-// created multiple times and the surviving instance isn't deleted until after
-// the thread has been stopped.
-base::LazyInstance<scoped_ptr<base::PollerWithThread> >
-    process_system_typefaces_with_open_streams_poller =
-        LAZY_INSTANCE_INITIALIZER;
-
-base::LazyInstance<base::Lock> poller_lock = LAZY_INSTANCE_INITIALIZER;
-
-// NOTE: It is the responsibility of the caller to call Unref() on the SkData.
-SkData* NewDataFromFile(const SkString& file_path) {
-  TRACE_EVENT1("cobalt::renderer", "SkFontMgr_cobalt::NewDataFromFile()",
-               "file_path", TRACE_STR_COPY(file_path.c_str()));
-  LOG(INFO) << "Loading font file: " << file_path.c_str();
-  SkFontMgrCVals::GetInstance()->IncrementFontFilesLoadedCount();
-
-  SkAutoTUnref<SkStreamAsset> file_stream(
-      SkStream::NewFromFile(file_path.c_str()));
-  if (file_stream == NULL) {
-    LOG(ERROR) << "Failed to open font file: " << file_path.c_str();
-    return NULL;
-  }
-
-  SkAutoDataUnref data(
-      SkData::NewFromStream(file_stream, file_stream->getLength()));
-  if (data == NULL) {
-    LOG(ERROR) << "Failed to read font file: " << file_path.c_str();
-    return NULL;
-  }
-
-  return data.detach();
-}
-
-int match_score(const SkFontStyle& pattern, const SkFontStyle& candidate) {
-  int score = 0;
-  score += std::abs((pattern.width() - candidate.width()) * 100);
-  score += (pattern.isItalic() == candidate.isItalic()) ? 0 : 1000;
-  score += std::abs(pattern.weight() - candidate.weight());
-  return score;
-}
-
-}  // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkTypeface_Cobalt : public SkTypeface_FreeType {
- public:
-  SkTypeface_Cobalt(int index, Style style, bool is_fixed_pitch,
-                    const SkString family_name)
-      : INHERITED(style, SkTypefaceCache::NewFontID(), is_fixed_pitch),
-        index_(index),
-        family_name_(family_name) {}
-
- protected:
-  virtual void onGetFamilyName(SkString* family_name) const SK_OVERRIDE {
-    *family_name = family_name_;
-  }
-
-  int index_;
-  SkString family_name_;
-
- private:
-  typedef SkTypeface_FreeType INHERITED;
-};
-
-class SkTypeface_CobaltStream : public SkTypeface_Cobalt {
- public:
-  SkTypeface_CobaltStream(SkStreamAsset* stream, int index, Style style,
-                          bool is_fixed_pitch, const SkString family_name)
-      : INHERITED(index, style, is_fixed_pitch, family_name),
-        stream_(SkRef(stream)) {
-    LOG(INFO) << "Created SkTypeface_CobaltStream: " << family_name.c_str()
-              << "(" << style << "); Size: " << stream_->getLength()
-              << " bytes";
-  }
-
-  virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
-                                   bool* serialize) const SK_OVERRIDE {
-    SkASSERT(descriptor);
-    SkASSERT(serialize);
-    descriptor->setFamilyName(family_name_.c_str());
-    descriptor->setFontFileName(NULL);
-    descriptor->setFontIndex(index_);
-    *serialize = true;
-  }
-
-  virtual SkStreamAsset* onOpenStream(int* ttc_index) const SK_OVERRIDE {
-    *ttc_index = index_;
-    return stream_->duplicate();
-  }
-
- private:
-  typedef SkTypeface_Cobalt INHERITED;
-
-  SkAutoTUnref<SkStreamAsset> stream_;
-};
-
-class SkTypeface_CobaltSystem : public SkTypeface_Cobalt {
- public:
-  SkTypeface_CobaltSystem(const SkFontMgr_Cobalt* manager, SkString path_name,
-                          SkMemoryStream* stream, int index, Style style,
-                          bool is_fixed_pitch, const SkString family_name)
-      : INHERITED(index, style, is_fixed_pitch, family_name),
-        font_manager_(manager),
-        path_name_(path_name),
-        stream_(SkRef(stream)) {
-    LOG(INFO) << "Created SkTypeface_CobaltSystem: " << family_name.c_str()
-              << "(" << style << "); File: " << path_name.c_str()
-              << "; Size: " << stream_->getLength() << " bytes";
-
-    SkAutoMutexAcquire scoped_mutex(
-        font_manager_->GetSystemTypefaceStreamMutex());
-    font_manager_->AddSystemTypefaceWithActiveOpenStream(this);
-  }
-
-  virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
-                                   bool* serialize) const SK_OVERRIDE {
-    SkASSERT(descriptor);
-    SkASSERT(serialize);
-    descriptor->setFamilyName(family_name_.c_str());
-    descriptor->setFontFileName(path_name_.c_str());
-    descriptor->setFontIndex(index_);
-    *serialize = false;
-  }
-
-  virtual SkStreamAsset* onOpenStream(int* ttc_index) const SK_OVERRIDE {
-    TRACE_EVENT0("cobalt::renderer", "SkTypeface_CobaltSystem::onOpenStream()");
-    *ttc_index = index_;
-
-    // Scope the initial mutex lock.
-    {
-      // Check for the stream still being open. In this case, the typeface
-      // stream does not need to be re-loaded. Simply notify the manager of the
-      // stream activity, as it potentially needs to move the stream from the
-      // inactive list to the active list.
-      SkAutoMutexAcquire scoped_mutex(
-          font_manager_->GetSystemTypefaceStreamMutex());
-      if (stream_ != NULL) {
-        font_manager_->NotifySystemTypefaceOfOpenStreamActivity(this);
-        return stream_->duplicate();
-      }
-    }
-
-    TRACE_EVENT0("cobalt::renderer", "Load data from file");
-    // This is where the bulk of the time will be spent, so load the font data
-    // outside of a mutex lock.
-    SkAutoTUnref<SkData> data(NewDataFromFile(path_name_));
-
-    // Re-lock the mutex now that the data has been loaded.
-    SkAutoMutexAcquire scoped_mutex(
-        font_manager_->GetSystemTypefaceStreamMutex());
-
-    // Verify that the data was not loaded on another thread while it was being
-    // loaded on this one. If that's the case, then simply use that data.
-    // Otherwise, update the stream and notify the font manager that the stream
-    // is open.
-    if (stream_ == NULL) {
-      stream_.reset(new SkMemoryStream(data));
-      LOG(INFO) << "Setting font stream: " << path_name_.c_str()
-                << "; Size: " << stream_->getLength() << " bytes";
-      font_manager_->AddSystemTypefaceWithActiveOpenStream(this);
-    }
-
-    return stream_->duplicate();
-  }
-
-  size_t GetOpenStreamSizeInBytes() const { return stream_->getLength(); }
-
-  bool IsOpenStreamInactive() const {
-    SkAutoTUnref<SkData> data(stream_->copyToData());
-    // A refcount of 2 indicates that there are no external references to the
-    // data. The first reference comes from SkMemoryStream and the second comes
-    // from the call to copyToData().
-    return data->getRefCnt() == 2;
-  }
-
-  void ReleaseStream() const {
-    LOG(INFO) << "Releasing font stream: " << path_name_.c_str()
-              << "; Size: " << stream_->getLength() << " bytes";
-    stream_.reset(NULL);
-  }
-
- private:
-  typedef SkTypeface_Cobalt INHERITED;
-
-  const SkFontMgr_Cobalt* font_manager_;
-  const SkString path_name_;
-
-  // |stream_| is mutable so that it can be modified within onOpenStream().
-  mutable SkAutoTUnref<SkMemoryStream> stream_;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-//  SkFontStyleSet_Cobalt
-//
-//  This class is used by SkFontMgr_Cobalt to hold SkTypeface_CobaltSystem
-//  families.
-SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt(
-    const SkFontMgr_Cobalt* const manager, const FontFamily& family,
-    const char* base_path, SkMutex* const manager_owned_mutex)
-    : font_manager_(manager),
-      manager_owned_mutex_(manager_owned_mutex),
-      is_fallback_family_(family.is_fallback_family),
-      language_(family.language),
-      page_ranges_(family.page_ranges),
-      is_character_map_generated_(false) {
-  TRACE_EVENT0("cobalt::renderer",
-               "SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt()");
-  DCHECK(manager_owned_mutex_);
-
-  if (family.names.count() == 0) {
-    return;
-  }
-
-  family_name_ = family.names[0];
-
-  for (int i = 0; i < family.fonts.count(); ++i) {
-    const FontFileInfo& font_file = family.fonts[i];
-
-    SkString path_name(SkOSPath::Join(base_path, font_file.file_name.c_str()));
-
-    // Sanity check that something exists at this location.
-    if (!sk_exists(path_name.c_str(), kRead_SkFILE_Flag)) {
-      LOG(ERROR) << "Failed to find font file: " << path_name.c_str();
-      continue;
-    }
-
-    SkFontStyle style(font_file.weight, SkFontStyle::kNormal_Width,
-                      font_file.style == FontFileInfo::kItalic_FontStyle
-                          ? SkFontStyle::kItalic_Slant
-                          : SkFontStyle::kUpright_Slant);
-
-    std::string full_font_name;
-    if (!font_file.full_font_name.isEmpty()) {
-      full_font_name = std::string(font_file.full_font_name.c_str(),
-                                   font_file.full_font_name.size());
-    }
-    std::string postscript_name;
-    if (!font_file.postscript_name.isEmpty()) {
-      postscript_name = std::string(font_file.postscript_name.c_str(),
-                                    font_file.postscript_name.size());
-    }
-
-    styles_.push_back().reset(SkNEW_ARGS(
-        SkFontStyleSetEntry_Cobalt,
-        (path_name, font_file.index, style, full_font_name, postscript_name)));
-  }
-}
-
-int SkFontStyleSet_Cobalt::count() {
-  SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
-  return styles_.count();
-}
-
-void SkFontStyleSet_Cobalt::getStyle(int index, SkFontStyle* style,
-                                     SkString* name) {
-  // SkFontStyleSet_Cobalt does not support publicly interacting with entries
-  // via index, as entries can potentially be removed, thereby invalidating the
-  // indices.
-  NOTREACHED();
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::createTypeface(int index) {
-  // SkFontStyleSet_Cobalt does not support publicly interacting with entries
-  // via index, as entries can potentially be removed, thereby invalidating the
-  // indices.
-  NOTREACHED();
-  return NULL;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::matchStyle(const SkFontStyle& pattern) {
-  SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
-  return MatchStyleWithoutLocking(pattern);
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::MatchStyleWithoutLocking(
-    const SkFontStyle& pattern) {
-  SkTypeface* typeface = NULL;
-  while (typeface == NULL && styles_.count() > 0) {
-    typeface = TryRetrieveTypefaceAndRemoveStyleOnFailure(
-        GetClosestStyleIndex(pattern));
-  }
-  return typeface;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::MatchFullFontName(const std::string& name) {
-  for (int i = 0; i < styles_.count(); ++i) {
-    if (styles_[i]->full_font_name == name) {
-      return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
-    }
-  }
-  return NULL;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::MatchFontPostScriptName(
-    const std::string& name) {
-  for (int i = 0; i < styles_.count(); ++i) {
-    if (styles_[i]->font_postscript_name == name) {
-      return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
-    }
-  }
-  return NULL;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::TryRetrieveTypefaceAndRemoveStyleOnFailure(
-    int style_index) {
-  DCHECK(style_index >= 0 && style_index < styles_.count());
-  SkFontStyleSetEntry_Cobalt* style = styles_[style_index];
-  // If the typeface doesn't already exist, then attempt to create it.
-  if (style->typeface == NULL) {
-    CreateSystemTypeface(style);
-    // If the creation attempt failed and the typeface is still NULL, then
-    // remove the entry from the set's styles.
-    if (style->typeface == NULL) {
-      styles_[style_index].swap(&styles_.back());
-      styles_.pop_back();
-      return NULL;
-    }
-  }
-  return SkRef(style->typeface.get());
-}
-
-bool SkFontStyleSet_Cobalt::ContainsTypeface(const SkTypeface* typeface) {
-  for (int i = 0; i < styles_.count(); ++i) {
-    if (styles_[i]->typeface == typeface) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool SkFontStyleSet_Cobalt::ContainsCharacter(const SkFontStyle& style,
-                                              SkUnichar character) {
-  // If page ranges exist for this style set, then verify that this character
-  // falls within the ranges. Otherwise, the style set is treated as having a
-  // page range containing all characters.
-  size_t num_ranges = page_ranges_.count();
-  if (num_ranges > 0) {
-    int16 page = font_character_map::GetPage(character);
-
-    // Verify that the last page range is not less than the page containing the
-    // character. If it's less, then this character can't be contained
-    // within the pages. Otherwise, this last range acts as a sentinel for the
-    // search.
-    if (page_ranges_[num_ranges - 1].second < page) {
-      return false;
-    }
-
-    int range_index = 0;
-    while (page_ranges_[range_index].second < page) {
-      ++range_index;
-    }
-
-    if (page_ranges_[range_index].first > page) {
-      return false;
-    }
-  }
-
-  // If this point is reached, then the character is contained within one of
-  // the font style set's page ranges. Now, verify that the specific character
-  // is supported by the font style set.
-
-  // The character map is lazily generated. Generate it now if it isn't already
-  // generated.
-  if (!is_character_map_generated_) {
-    TRACE_EVENT0("cobalt::renderer",
-                 "SkFontStyleSet_Cobalt::ContainsCharacter() and "
-                 "!is_character_map_generated_");
-    // Attempt to load the closest font style from the set. If it fails to load,
-    // it will be removed from the set and, as long as font styles remain in the
-    // set, the logic will be attempted again.
-    while (styles_.count() > 0) {
-      int style_index = GetClosestStyleIndex(style);
-      SkFontStyleSetEntry_Cobalt* closest_style = styles_[style_index];
-
-      // Load the font data from the file and use it to generate the character
-      // map.
-      SkAutoTUnref<SkData> font_data(
-          NewDataFromFile(closest_style->font_file_path));
-      // The font failed to load. Remove it from the styles and start the loop
-      // over.
-      if (font_data == NULL) {
-        styles_[style_index].swap(&styles_.back());
-        styles_.pop_back();
-        continue;
-      }
-
-      GenerateCharacterMapFromData(font_data, closest_style->ttc_index);
-
-      // If the character is contained within the font style set, then go ahead
-      // and create the typeface with the loaded font data. Otherwise, creating
-      // the typeface would require an additional load of the file.
-      bool contains_character = CharacterMapContainsCharacter(character);
-      if (contains_character) {
-        CreateSystemTypefaceFromData(closest_style, font_data);
-      }
-      return contains_character;
-    }
-
-    // If this point is reached, none of the fonts were successfully loaded.
-    is_character_map_generated_ = true;
-    return false;
-  } else {
-    return CharacterMapContainsCharacter(character);
-  }
-}
-
-bool SkFontStyleSet_Cobalt::CharacterMapContainsCharacter(SkUnichar character) {
-  font_character_map::CharacterMap::iterator page_iterator =
-      character_map_.find(font_character_map::GetPage(character));
-  return page_iterator != character_map_.end() &&
-         page_iterator->second.test(
-             font_character_map::GetPageCharacterIndex(character));
-}
-
-void SkFontStyleSet_Cobalt::GenerateCharacterMapFromData(SkData* font_data,
-                                                         int ttc_index) {
-  if (is_character_map_generated_) {
-    return;
-  }
-  TRACE_EVENT0("cobalt::renderer", "GenerateCharacterMapFromData()");
-
-  FT_Library freetype_lib;
-  if (FT_Init_FreeType(&freetype_lib) != 0) {
-    return;
-  }
-
-  FT_Face face;
-  FT_Error err = FT_New_Memory_Face(freetype_lib, font_data->bytes(),
-                                    font_data->size(), ttc_index, &face);
-  if (!err) {
-    // map out this font's characters.
-    FT_UInt glyph_index;
-
-    int last_page = -1;
-    font_character_map::PageCharacters* page_characters = NULL;
-
-    SkUnichar code_point = FT_Get_First_Char(face, &glyph_index);
-    while (glyph_index) {
-      int page = font_character_map::GetPage(code_point);
-      if (page != last_page) {
-        page_characters = &character_map_[page];
-        last_page = page;
-      }
-      page_characters->set(
-          font_character_map::GetPageCharacterIndex(code_point));
-
-      code_point = FT_Get_Next_Char(face, code_point, &glyph_index);
-    }
-
-    // release this font.
-    FT_Done_Face(face);
-  }
-
-  // shut down FreeType.
-  FT_Done_FreeType(freetype_lib);
-
-  is_character_map_generated_ = true;
-}
-
-int SkFontStyleSet_Cobalt::GetClosestStyleIndex(const SkFontStyle& pattern) {
-  int closest_index = 0;
-  int min_score = std::numeric_limits<int>::max();
-  for (int i = 0; i < styles_.count(); ++i) {
-    int score = match_score(pattern, styles_[i]->font_style);
-    if (score < min_score) {
-      closest_index = i;
-      min_score = score;
-    }
-  }
-  return closest_index;
-}
-
-void SkFontStyleSet_Cobalt::CreateSystemTypeface(
-    SkFontStyleSetEntry_Cobalt* style_entry) {
-  TRACE_EVENT0("cobalt::renderer",
-               "SkFontStyleSet_Cobalt::CreateSystemTypeface()");
-  SkAutoTUnref<SkData> font_data(NewDataFromFile(style_entry->font_file_path));
-  if (font_data != NULL) {
-    CreateSystemTypefaceFromData(style_entry, font_data);
-  }
-}
-
-void SkFontStyleSet_Cobalt::CreateSystemTypefaceFromData(
-    SkFontStyleSetEntry_Cobalt* style_entry, SkData* data) {
-  TRACE_EVENT0("cobalt::renderer",
-               "SkFontStyleSet_Cobalt::CreateSystemTypefaceFromData()");
-  DCHECK(!style_entry->typeface);
-
-  // Since the font data is available, generate the character map if this is a
-  // fallback font (which means that it'll need the character map for character
-  // lookups during fallback).
-  if (is_fallback_family_) {
-    GenerateCharacterMapFromData(data, style_entry->ttc_index);
-  }
-
-  // Pass the SkData into the SkMemoryStream.
-  SkAutoTUnref<SkMemoryStream> stream(new SkMemoryStream(data));
-
-  bool is_fixed_pitch;
-  SkTypeface::Style style;
-  SkString name;
-  if (SkTypeface_FreeType::ScanFont(stream, style_entry->ttc_index, &name,
-                                    &style, &is_fixed_pitch)) {
-    LOG(INFO) << "Scanned font: " << name.c_str() << "(" << style << ")";
-    style_entry->typeface.reset(SkNEW_ARGS(
-        SkTypeface_CobaltSystem,
-        (font_manager_, style_entry->font_file_path, stream,
-         style_entry->ttc_index, style, is_fixed_pitch, family_name_)));
-  } else {
-    LOG(ERROR) << "Failed to scan font: "
-               << style_entry->font_file_path.c_str();
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//
-//  SkFontMgr_Cobalt
-//
 
 SkFontMgr_Cobalt::SkFontMgr_Cobalt(
-    const char* directory, const SkTArray<SkString, true>& default_fonts)
-    : default_family_(NULL),
-      last_font_cache_purge_time_(base::TimeTicks::Now()) {
+    const char* directory, const SkTArray<SkString, true>& default_families)
+    : system_typeface_stream_manager_(
+          "Font.SystemTypeface",
+          COBALT_SYSTEM_TYPEFACE_CACHE_CAPACITY_IN_BYTES),
+      default_family_(NULL) {
   TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::SkFontMgr_Cobalt()");
-  // Ensure that both the CValManager and SkFontMgrCVals are initialized. The
-  // CValManager is created first as it must outlast the SkFontMgrCVals.
-  base::CValManager::GetInstance();
-  SkFontMgrCVals::GetInstance();
 
   SkTDArray<FontFamily*> font_families;
   {
@@ -635,29 +38,17 @@
   BuildNameToFamilyMap(directory, &font_families);
   font_families.deleteAll();
 
-  FindDefaultFont(default_fonts);
-
-  {
-    // Create the poller. The lazy instance ensures we only get one copy, and
-    // that it will be cleaned up at appl exit, but we still need to lock
-    // so we only construct it once.
-    base::AutoLock lock(poller_lock.Get());
-    if (!process_system_typefaces_with_open_streams_poller.Get()) {
-      process_system_typefaces_with_open_streams_poller.Get().reset(
-          new base::PollerWithThread(
-              base::Bind(&SkFontMgr_Cobalt::HandlePeriodicProcessing,
-                         base::Unretained(this)),
-              base::TimeDelta::FromMilliseconds(
-                  kPeriodicProcessingTimerDelayMs)));
-    }
-  }
+  FindDefaultFamily(default_families);
 }
 
-SkTypeface* SkFontMgr_Cobalt::matchFaceName(const std::string& font_face_name) {
-  if (font_face_name.empty()) {
+SkTypeface* SkFontMgr_Cobalt::MatchFaceName(const char face_name[]) {
+  if (!face_name) {
     return NULL;
   }
 
+  SkAutoAsciiToLC face_name_to_lc(face_name);
+  std::string face_name_string(face_name_to_lc.lc(), face_name_to_lc.length());
+
   // Lock the style sets mutex prior to accessing them.
   SkAutoMutexAcquire scoped_mutex(style_sets_mutex_);
 
@@ -669,12 +60,12 @@
                : full_font_name_to_style_set_map_;
 
     NameToStyleSetMap::iterator style_set_iterator =
-        name_to_style_set_map.find(font_face_name);
+        name_to_style_set_map.find(face_name_string);
     if (style_set_iterator != name_to_style_set_map.end()) {
       SkFontStyleSet_Cobalt* style_set = style_set_iterator->second;
       SkTypeface* typeface =
-          i == 0 ? style_set->MatchFontPostScriptName(font_face_name)
-                 : style_set->MatchFullFontName(font_face_name);
+          i == 0 ? style_set->MatchFontPostScriptName(face_name_string)
+                 : style_set->MatchFullFontName(face_name_string);
       if (typeface != NULL) {
         return typeface;
       } else {
@@ -719,10 +110,10 @@
     return NULL;
   }
 
-  SkAutoAsciiToLC tolc(family_name);
+  SkAutoAsciiToLC family_name_to_lc(family_name);
 
-  NameToStyleSetMap::const_iterator family_iterator =
-      name_to_family_map_.find(tolc.lc());
+  NameToStyleSetMap::const_iterator family_iterator = name_to_family_map_.find(
+      std::string(family_name_to_lc.lc(), family_name_to_lc.length()));
   if (family_iterator != name_to_family_map_.end()) {
     return SkRef(family_iterator->second);
   }
@@ -732,18 +123,18 @@
 
 SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyle(
     const char family_name[], const SkFontStyle& style) const {
-  SkTypeface* tf = NULL;
+  SkTypeface* typeface = NULL;
 
   if (family_name) {
-    SkAutoTUnref<SkFontStyleSet> sset(matchFamily(family_name));
-    tf = sset->matchStyle(style);
+    SkAutoTUnref<SkFontStyleSet> style_set(matchFamily(family_name));
+    typeface = style_set->matchStyle(style);
   }
 
-  if (NULL == tf) {
-    tf = default_family_->matchStyle(style);
+  if (NULL == typeface) {
+    typeface = default_family_->matchStyle(style);
   }
 
-  return tf;
+  return typeface;
 }
 
 SkTypeface* SkFontMgr_Cobalt::onMatchFaceStyle(const SkTypeface* family_member,
@@ -760,17 +151,15 @@
   return NULL;
 }
 
-#ifdef SK_FM_NEW_MATCH_FAMILY_STYLE_CHARACTER
-SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyleCharacter(
-    const char family_name[], const SkFontStyle& style, const char* bcp47[],
-    int bcp47_count, SkUnichar character) const {
-#else
 SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyleCharacter(
     const char family_name[], const SkFontStyle& style, const char bcp47_val[],
     SkUnichar character) const {
   const char** bcp47 = &bcp47_val;
   int bcp47_count = bcp47_val ? 1 : 0;
-#endif
+
+  // Remove const from the manager. SkFontMgr_Cobalt modifies its internals
+  // within FindFamilyStyleCharacter().
+  SkFontMgr_Cobalt* font_mgr = const_cast<SkFontMgr_Cobalt*>(this);
 
   // Lock the style sets mutex prior to calling |FindFamilyStyleCharacter|. It
   // expects the mutex to already be locked.
@@ -782,8 +171,8 @@
   for (int bcp47_index = bcp47_count; bcp47_index-- > 0;) {
     SkLanguage language(bcp47[bcp47_index]);
     while (!language.GetTag().isEmpty()) {
-      SkTypeface* matching_typeface =
-          FindFamilyStyleCharacter(style, language.GetTag(), character);
+      SkTypeface* matching_typeface = font_mgr->FindFamilyStyleCharacter(
+          style, language.GetTag(), character);
       if (matching_typeface) {
         return matching_typeface;
       }
@@ -796,7 +185,7 @@
   // requirement. This will select the first encountered family that contains
   // the character.
   SkTypeface* matching_typeface =
-      FindFamilyStyleCharacter(style, SkString(), character);
+      font_mgr->FindFamilyStyleCharacter(style, SkString(), character);
 
   // If no family was found that supports the character, then just fall back
   // to the default family.
@@ -805,30 +194,30 @@
 }
 
 SkTypeface* SkFontMgr_Cobalt::onCreateFromData(SkData* data,
-                                               int ttc_index) const {
+                                               int face_index) const {
   SkAutoTUnref<SkStreamAsset> stream(new SkMemoryStream(data));
-  return createFromStream(stream, ttc_index);
+  return createFromStream(stream, face_index);
 }
 
 SkTypeface* SkFontMgr_Cobalt::onCreateFromStream(SkStreamAsset* stream,
-                                                 int ttc_index) const {
+                                                 int face_index) const {
   TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::onCreateFromStream()");
   bool is_fixed_pitch;
   SkTypeface::Style style;
   SkString name;
-  if (!SkTypeface_FreeType::ScanFont(stream, ttc_index, &name, &style,
+  if (!SkTypeface_FreeType::ScanFont(stream, face_index, &name, &style,
                                      &is_fixed_pitch)) {
     return NULL;
   }
   return SkNEW_ARGS(SkTypeface_CobaltStream,
-                    (stream, ttc_index, style, is_fixed_pitch, name));
+                    (stream, face_index, style, is_fixed_pitch, name));
 }
 
 SkTypeface* SkFontMgr_Cobalt::onCreateFromFile(const char path[],
-                                               int ttc_index) const {
+                                               int face_index) const {
   TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::onCreateFromFile()");
   SkAutoTUnref<SkStreamAsset> stream(SkStream::NewFromFile(path));
-  return stream.get() ? createFromStream(stream, ttc_index) : NULL;
+  return stream.get() ? createFromStream(stream, face_index) : NULL;
 }
 
 SkTypeface* SkFontMgr_Cobalt::onLegacyCreateTypeface(
@@ -857,8 +246,10 @@
       }
     }
 
-    SkAutoTUnref<SkFontStyleSet_Cobalt> new_set(SkNEW_ARGS(
-        SkFontStyleSet_Cobalt, (this, family, base_path, &style_sets_mutex_)));
+    SkAutoTUnref<SkFontStyleSet_Cobalt> new_set(
+        SkNEW_ARGS(SkFontStyleSet_Cobalt,
+                   (family, base_path, &system_typeface_stream_manager_,
+                    &style_sets_mutex_)));
 
     // Verify that the set successfully added entries.
     if (new_set->styles_.count() == 0) {
@@ -879,8 +270,6 @@
       fallback_families_.push_back(new_set.get());
     }
 
-    // Handle adding the font face names for the style set entries. These are
-    // optional, so many may not have them.
     for (SkAutoTUnref<SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt>*
              font_style_set_entry = new_set->styles_.begin();
          font_style_set_entry != new_set->styles_.end();
@@ -918,62 +307,46 @@
   }
 }
 
-void SkFontMgr_Cobalt::FindDefaultFont(
-    const SkTArray<SkString, true>& default_fonts) {
-  SkASSERT(!font_style_sets_.empty());
+void SkFontMgr_Cobalt::FindDefaultFamily(
+    const SkTArray<SkString, true>& default_families) {
+  CHECK(!font_style_sets_.empty());
 
-  for (size_t i = 0; i < default_fonts.count(); ++i) {
-    SkAutoTUnref<SkFontStyleSet_Cobalt> set(
-        onMatchFamily(default_fonts[i].c_str()));
-    if (NULL == set) {
+  for (size_t i = 0; i < default_families.count(); ++i) {
+    SkAutoTUnref<SkFontStyleSet_Cobalt> check_style_set(
+        onMatchFamily(default_families[i].c_str()));
+    if (NULL == check_style_set) {
       continue;
     }
-    SkAutoTUnref<SkTypeface> tf(set->MatchStyleWithoutLocking(SkFontStyle()));
-    if (NULL == tf) {
-      continue;
-    }
-    default_family_ = set.get();
-    default_typeface_.swap(&tf);
-    break;
-  }
 
-  if (NULL == default_typeface_) {
-    default_family_ = font_style_sets_[0].get();
-    default_typeface_.reset(
-        default_family_->MatchStyleWithoutLocking(SkFontStyle()));
-  }
-
-  SkASSERT(default_family_);
-  SkASSERT(default_typeface_);
-
-  // Remove the default typeface from the tracked system typefaces with open
-  // streams. We never want to release it.
-  for (int i = 0; i < system_typefaces_with_active_open_streams_.count(); ++i) {
-    if (system_typefaces_with_active_open_streams_[i]->uniqueID() ==
-        default_typeface_->uniqueID()) {
-      system_typefaces_with_active_open_streams_.removeShuffle(i);
+    SkAutoTUnref<SkTypeface> check_typeface(
+        check_style_set->MatchStyleWithoutLocking(SkFontStyle()));
+    if (NULL != check_typeface) {
+      default_family_ = check_style_set.get();
       break;
     }
   }
+
+  if (NULL == default_family_) {
+    SkAutoTUnref<SkTypeface> check_typeface(
+        font_style_sets_[0]->MatchStyleWithoutLocking(SkFontStyle()));
+    if (NULL != check_typeface) {
+      default_family_ = font_style_sets_[0].get();
+    }
+  }
+
+  CHECK(default_family_);
 }
 
 SkTypeface* SkFontMgr_Cobalt::FindFamilyStyleCharacter(
     const SkFontStyle& style, const SkString& language_tag,
-    SkUnichar character) const {
+    SkUnichar character) {
   if (!font_character_map::IsCharacterValid(character)) {
     return NULL;
   }
 
-  for (int i = 0; i < fallback_families_.count(); ++i) {
-    SkFontStyleSet_Cobalt* family = fallback_families_[i];
-
-    // If a language tag was provided and this family doesn't meet the language
-    // requirement, then just skip over the family.
-    if (!language_tag.isEmpty() &&
-        !family->language_.GetTag().startsWith(language_tag.c_str())) {
-      continue;
-    }
-
+  StyleSetArray* fallback_families = GetMatchingFallbackFamilies(language_tag);
+  for (int i = 0; i < fallback_families->size(); ++i) {
+    SkFontStyleSet_Cobalt* family = (*fallback_families)[i];
     if (family->ContainsCharacter(style, character)) {
       SkTypeface* matching_typeface = family->MatchStyleWithoutLocking(style);
       if (matching_typeface) {
@@ -985,134 +358,31 @@
   return NULL;
 }
 
-void SkFontMgr_Cobalt::HandlePeriodicProcessing() {
-  base::TimeTicks current_time = base::TimeTicks::Now();
-
-  ProcessSystemTypefacesWithOpenStreams(current_time);
-
-  // If the required delay has elapsed since the last font cache purge, then
-  // it's time to force another. This is accomplished by setting the limit to
-  // 1 byte smaller than it's current size, which initiates a partial purge (it
-  // always purges at least 25% of its contents). After this is done, the cache
-  // is set back to its previous size.
-  if ((current_time - last_font_cache_purge_time_).InMilliseconds() >=
-      kPeriodicFontCachePurgeDelayMs) {
-    last_font_cache_purge_time_ = current_time;
-
-    size_t font_cache_used = SkGraphics::GetFontCacheUsed();
-    if (font_cache_used > 0) {
-      size_t font_cache_limit = SkGraphics::GetFontCacheLimit();
-      SkGraphics::SetFontCacheLimit(font_cache_used - 1);
-      SkGraphics::SetFontCacheLimit(font_cache_limit);
-    }
-  }
-}
-
-// NOTE: It is the responsibility of the caller to lock
-// |system_typeface_stream_mutex_|
-void SkFontMgr_Cobalt::AddSystemTypefaceWithActiveOpenStream(
-    const SkTypeface_CobaltSystem* system_typeface) const {
-  SkFontMgrCVals::GetInstance()
-      ->system_typeface_open_stream_cache_size_in_bytes() +=
-      system_typeface->GetOpenStreamSizeInBytes();
-  system_typefaces_with_active_open_streams_.push_back(system_typeface);
-
-  LOG(INFO) << "Total system typeface memory: "
-            << SkFontMgrCVals::GetInstance()
-                   ->system_typeface_open_stream_cache_size_in_bytes()
-            << " bytes";
-}
-
-// NOTE: It is the responsibility of the caller to lock
-// |system_typeface_stream_mutex_|
-void SkFontMgr_Cobalt::NotifySystemTypefaceOfOpenStreamActivity(
-    const SkTypeface_CobaltSystem* system_typeface) const {
-  // Walk through the system_typefaces with inactive open streams, searching for
-  // the typeface with activity. It potentially is still in the active list and
-  // won't be found here.
-  for (int i = 0; i < system_typefaces_with_inactive_open_streams_.count();
-       ++i) {
-    // We found the typeface in the inactive list. Move it back to the active
-    // list and break out. The typeface will never be in the list more than
-    // once, so we don't need to continue searching.
-    if (system_typefaces_with_inactive_open_streams_[i].typeface ==
-        system_typeface) {
-      system_typefaces_with_active_open_streams_.push_back(system_typeface);
-      system_typefaces_with_inactive_open_streams_.removeShuffle(i);
-      break;
-    }
-  }
-}
-
-void SkFontMgr_Cobalt::ProcessSystemTypefacesWithOpenStreams(
-    const base::TimeTicks& current_time) const {
-  SkAutoMutexAcquire scoped_mutex(system_typeface_stream_mutex_);
-
-  // First, release the inactive open streams that meet the following
-  // conditions:
-  //   1. The cache is over its memory limit.
-  //   2. The stream has been inactive for the required period of time.
-  // Given that the streams are in temporal order, only the first stream in the
-  // list ever needs to be checked. If it cannot be released, then all
-  // subsequent streams are also guaranteed to not be releasable.
-  while (!system_typefaces_with_inactive_open_streams_.empty()) {
-    // The cache size is not over the memory limit. No open streams are released
-    // while the cache is under its limit. Simply break out.
-    if (SkFontMgrCVals::GetInstance()
-            ->system_typeface_open_stream_cache_size_in_bytes()
-            .value() < SkFontMgrCVals::GetInstance()
-                           ->system_typeface_open_stream_cache_limit_in_bytes()
-                           .value()) {
-      break;
-    }
-
-    const TimedSystemTypeface& timed_system_typeface =
-        system_typefaces_with_inactive_open_streams_[0];
-
-    // The current stream has not been inactive long enough to be releasable. No
-    // subsequent streams can meet the threshold either. Simply break out.
-    if ((current_time - timed_system_typeface.time).InMilliseconds() <
-        kReleaseInactiveSystemTypefaceOpenStreamsDelayMs) {
-      break;
-    }
-
-    const SkTypeface_CobaltSystem* system_typeface =
-        timed_system_typeface.typeface;
-    DCHECK(system_typeface->IsOpenStreamInactive());
-
-    SkFontMgrCVals::GetInstance()
-        ->system_typeface_open_stream_cache_size_in_bytes() -=
-        system_typeface->GetOpenStreamSizeInBytes();
-    system_typeface->ReleaseStream();
-    system_typefaces_with_inactive_open_streams_.removeShuffle(0);
-
-    LOG(INFO) << "Total system typeface memory: "
-              << SkFontMgrCVals::GetInstance()
-                     ->system_typeface_open_stream_cache_size_in_bytes()
-              << " bytes";
+SkFontMgr_Cobalt::StyleSetArray* SkFontMgr_Cobalt::GetMatchingFallbackFamilies(
+    const SkString& language_tag) {
+  if (language_tag.isEmpty()) {
+    return &fallback_families_;
   }
 
-  // Now, walk the active open streams. Any that are found to be inactive are
-  // moved from the active list to the inactive list, with the current time
-  // included for tracking when it is time to release the inactive stream.
-  // NOTE: The active streams are processed after the inactive streams so that
-  // regardless of |kReleaseInactiveSystemTypefaceOpenStreamsDelayMs|, it is
-  // guaranteed that active streams cannot immediately be released upon being
-  // moved to the inactive list. They must be retained in the inactive list for
-  // at least |kProcessSystemTypefacesWithOpenStreamsTimerDelayMs|. If this
-  // desire changes, then the active open stream processing should be moved
-  // above the inactive open stream processing.
-  for (int i = 0; i < system_typefaces_with_active_open_streams_.count(); ++i) {
-    const SkTypeface_CobaltSystem* system_typeface =
-        system_typefaces_with_active_open_streams_[i];
-    if (system_typeface->IsOpenStreamInactive()) {
-      system_typefaces_with_active_open_streams_.removeShuffle(i--);
-      system_typefaces_with_inactive_open_streams_.push_back(
-          TimedSystemTypeface(system_typeface, current_time));
+  StyleSetArray*& language_fallback_families =
+      language_fallback_families_map_[language_tag.c_str()];
+
+  // The fallback families for a specific language tag are lazily populated. If
+  // this is the first time that this tag has been encountered, then create and
+  // populate the fallback families now.
+  if (language_fallback_families == NULL) {
+    language_fallback_families_array_.push_back(new StyleSetArray);
+    language_fallback_families = *language_fallback_families_array_.rbegin();
+
+    for (StyleSetArray::iterator iter = fallback_families_.begin();
+         iter != fallback_families_.end(); ++iter) {
+      SkFontStyleSet_Cobalt* fallback_family = *iter;
+      if (fallback_family->language_.GetTag().startsWith(
+              language_tag.c_str())) {
+        language_fallback_families->push_back(fallback_family);
+      }
     }
   }
-}
 
-SkMutex& SkFontMgr_Cobalt::GetSystemTypefaceStreamMutex() const {
-  return system_typeface_stream_mutex_;
+  return language_fallback_families;
 }
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
index a1a983c..081408e 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
@@ -15,117 +15,18 @@
 #ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
 #define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
 
-#include <bitset>
-#include <limits>
 #include <string>
-#include <utility>
+#include <vector>
 
+#include "base/containers/small_map.h"
 #include "base/hash_tables.h"
-#include "cobalt/base/poller.h"
+#include "base/memory/scoped_vector.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h"
-#include "SkFontHost.h"
-#include "SkFontDescriptor.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
 #include "SkFontMgr.h"
-#include "SkThread.h"
-#include "SkTypefaceCache.h"
-
-class SkFontMgr_Cobalt;
-class SkTypeface_CobaltSystem;
-
-//  This class is used by SkFontMgr_Cobalt to hold SkTypeface_Cobalt families.
-//
-//  To avoid the memory hit from keeping all fonts around, both the full
-//  character map for fonts and the font faces for fonts are lazily loaded
-//  when needed. After this, they are retained in memory.
-//
-//  The style sets contain an array of page ranges, providing information on
-//  characters that are potentially contained. To determine whether or not a
-//  character is actually contained, the full character map is generated.
-class SkFontStyleSet_Cobalt : public SkFontStyleSet {
- public:
-  struct SkFontStyleSetEntry_Cobalt : public SkRefCnt {
-    // NOTE: |SkFontStyleSetEntry_Cobalt| objects are not guaranteed to last for
-    // the lifetime of |SkFontMgr_Cobalt| and can be removed by their owning
-    // |SkFontStyleSet_Cobalt| if their typeface fails to load properly. As a
-    // result, it is not safe to store their pointers outside of
-    // |SkFontStyleSet_Cobalt|.
-    SkFontStyleSetEntry_Cobalt(const SkString& file_path, const int index,
-                               const SkFontStyle& style,
-                               const std::string& full_name,
-                               const std::string& postscript_name)
-        : font_file_path(file_path),
-          ttc_index(index),
-          font_style(style),
-          full_font_name(full_name),
-          font_postscript_name(postscript_name),
-          typeface(NULL) {}
-
-    const SkString font_file_path;
-    const int ttc_index;
-    const SkFontStyle font_style;
-
-    // these two members are declared as std::string since we have a hasher
-    // for std::string, but not SkString at this point.
-    const std::string full_font_name;
-    const std::string font_postscript_name;
-
-    SkAutoTUnref<SkTypeface> typeface;
-  };
-
-  SkFontStyleSet_Cobalt(const SkFontMgr_Cobalt* const manager,
-                        const FontFamily& family, const char* base_path,
-                        SkMutex* const manager_owned_mutex);
-
-  // From SkFontStyleSet
-  virtual int count() SK_OVERRIDE;
-  // NOTE: SkFontStyleSet_Cobalt does not support |getStyle|, as publicly
-  // accessing styles by index is unsafe.
-  virtual void getStyle(int index, SkFontStyle* style,
-                        SkString* name) SK_OVERRIDE;
-  // NOTE: SkFontStyleSet_Cobalt does not support |createTypeface|, as
-  // publicly accessing styles by index is unsafe.
-  virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
-
-  virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
-
-  const SkString& get_family_name() const { return family_name_; }
-
- private:
-  // NOTE: It is the responsibility of the caller to lock the mutex before
-  // calling any of the non-const private functions.
-
-  SkTypeface* MatchStyleWithoutLocking(const SkFontStyle& pattern);
-  SkTypeface* MatchFullFontName(const std::string& name);
-  SkTypeface* MatchFontPostScriptName(const std::string& name);
-  SkTypeface* TryRetrieveTypefaceAndRemoveStyleOnFailure(int style_index);
-  bool ContainsTypeface(const SkTypeface* typeface);
-
-  bool ContainsCharacter(const SkFontStyle& style, SkUnichar character);
-  bool CharacterMapContainsCharacter(SkUnichar character);
-  void GenerateCharacterMapFromData(SkData* font_data, int ttc_index);
-
-  int GetClosestStyleIndex(const SkFontStyle& pattern);
-  void CreateSystemTypeface(SkFontStyleSetEntry_Cobalt* style);
-  void CreateSystemTypefaceFromData(SkFontStyleSetEntry_Cobalt* style,
-                                    SkData* data);
-
-  // NOTE: The following variables can safely be accessed outside of a lock.
-  const SkFontMgr_Cobalt* const font_manager_;
-  SkMutex* const manager_owned_mutex_;
-
-  SkString family_name_;
-  bool is_fallback_family_;
-  SkLanguage language_;
-  font_character_map::PageRanges page_ranges_;
-
-  // NOTE: The following characters require locking when being accessed.
-  bool is_character_map_generated_;
-  font_character_map::CharacterMap character_map_;
-
-  SkTArray<SkAutoTUnref<SkFontStyleSetEntry_Cobalt>, true> styles_;
-
-  friend class SkFontMgr_Cobalt;
-};
+#include "SkTArray.h"
+#include "SkTypeface.h"
 
 //  This class is essentially a collection of SkFontStyleSet_Cobalt, one
 //  SkFontStyleSet_Cobalt for each family. This class may be modified to load
@@ -141,11 +42,13 @@
 //  fallback fonts are checked in order.
 class SkFontMgr_Cobalt : public SkFontMgr {
  public:
+  typedef std::vector<SkFontStyleSet_Cobalt*> StyleSetArray;
+
   SkFontMgr_Cobalt(const char* directory,
                    const SkTArray<SkString, true>& default_fonts);
 
   // NOTE: This returns NULL if a match is not found.
-  SkTypeface* matchFaceName(const std::string& font_face_name);
+  SkTypeface* MatchFaceName(const char face_name[]);
 
  protected:
   // From SkFontMgr
@@ -168,15 +71,9 @@
 
 // NOTE: This always returns a non-NULL value. If no match can be found, then
 // the best match among the default family is returned.
-#ifdef SK_FM_NEW_MATCH_FAMILY_STYLE_CHARACTER
-  virtual SkTypeface* onMatchFamilyStyleCharacter(
-      const char family_name[], const SkFontStyle& style, const char* bcp47[],
-      int bcp47_count, SkUnichar character) const SK_OVERRIDE;
-#else
   virtual SkTypeface* onMatchFamilyStyleCharacter(
       const char family_name[], const SkFontStyle& style, const char bcp47[],
       SkUnichar character) const SK_OVERRIDE;
-#endif
 
   // NOTE: This returns NULL if a match is not found.
   virtual SkTypeface* onMatchFaceStyle(const SkTypeface* family_member,
@@ -185,15 +82,15 @@
 
   // NOTE: This returns NULL if the typeface cannot be created.
   virtual SkTypeface* onCreateFromData(SkData* data,
-                                       int ttc_index) const SK_OVERRIDE;
+                                       int face_index) const SK_OVERRIDE;
 
   // NOTE: This returns NULL if the typeface cannot be created.
   virtual SkTypeface* onCreateFromStream(SkStreamAsset* stream,
-                                         int ttc_index) const SK_OVERRIDE;
+                                         int face_index) const SK_OVERRIDE;
 
   // NOTE: This returns NULL if the typeface cannot be created.
   virtual SkTypeface* onCreateFromFile(const char path[],
-                                       int ttc_index) const SK_OVERRIDE;
+                                       int face_index) const SK_OVERRIDE;
 
   // NOTE: This always returns a non-NULL value. If no match can be found, then
   // the best match among the default family is returned.
@@ -201,51 +98,27 @@
       const char family_name[], unsigned style_bits) const SK_OVERRIDE;
 
  private:
-  struct TimedSystemTypeface {
-    TimedSystemTypeface(const SkTypeface_CobaltSystem* system_typeface,
-                        const base::TimeTicks& event_time)
-        : typeface(system_typeface), time(event_time) {}
-
-    const SkTypeface_CobaltSystem* typeface;
-    base::TimeTicks time;
-  };
-
   typedef base::hash_map<std::string, SkFontStyleSet_Cobalt*> NameToStyleSetMap;
+  typedef base::SmallMap<base::hash_map<std::string, StyleSetArray*> >
+      NameToStyleSetArrayMap;
 
   void BuildNameToFamilyMap(const char* base_path,
                             SkTDArray<FontFamily*>* families);
-  void FindDefaultFont(const SkTArray<SkString, true>& default_fonts);
+  void FindDefaultFamily(const SkTArray<SkString, true>& default_families);
 
+  // Returns the first encountered fallback family that matches the language tag
+  // and supports the specified character.
   // NOTE: |style_sets_mutex_| should be locked prior to calling this function.
   SkTypeface* FindFamilyStyleCharacter(const SkFontStyle& style,
-                                       const SkString& lang_tag,
-                                       SkUnichar character) const;
+                                       const SkString& language_tag,
+                                       SkUnichar character);
 
-  void HandlePeriodicProcessing();
+  // Returns every fallback family that matches the language tag. If the tag is
+  // empty, then all fallback families are returned.
+  // NOTE: |style_sets_mutex_| should be locked prior to calling this function.
+  StyleSetArray* GetMatchingFallbackFamilies(const SkString& language_tag);
 
-  // System typeface stream related
-
-  // Called by SkTypeface_CobaltSystem when it opens a new stream, this adds
-  // the system typeface to the active open stream list and adds the stream's
-  // bytes into the open stream cache size.
-  // NOTE1: This assumes that the typeface is not already in the list.
-  // NOTE2: This requires the caller to lock |system_typeface_stream_mutex_|
-  void AddSystemTypefaceWithActiveOpenStream(
-      const SkTypeface_CobaltSystem* system_typeface) const;
-  // Called by SkTypeface_CobaltSystem when an already open stream shows
-  // activity, this moves the system typeface from the inactive open stream list
-  // to the active open stream list.
-  // NOTE: This requires the caller to lock |system_typeface_stream_mutex_|
-  void NotifySystemTypefaceOfOpenStreamActivity(
-      const SkTypeface_CobaltSystem* system_typeface) const;
-  // Called by |system_typeface_open_stream_timer_|, this handles periodic
-  // processing on the open streams, including moving typefaces from the
-  // active to the inactive list and releasing inactive open streams.
-  // NOTE: Handles locking |system_typeface_stream_mutex_| internally
-  void ProcessSystemTypefacesWithOpenStreams(
-      const base::TimeTicks& current_time) const;
-  // Mutex shared by all system typefaces for accessing/modifying stream data.
-  SkMutex& GetSystemTypefaceStreamMutex() const;
+  SkFileMemoryChunkStreamManager system_typeface_stream_manager_;
 
   SkTArray<SkAutoTUnref<SkFontStyleSet_Cobalt>, true> font_style_sets_;
 
@@ -256,41 +129,20 @@
   NameToStyleSetMap full_font_name_to_style_set_map_;
   NameToStyleSetMap font_postscript_name_to_style_set_map_;
 
-  SkTArray<SkFontStyleSet_Cobalt*> fallback_families_;
+  // Fallback families that are used during character fallback.
+  // All fallback families, regardless of language.
+  StyleSetArray fallback_families_;
+  // Language-specific fallback families. These are lazily populated from
+  // |fallback_families_| when a new language tag is requested.
+  ScopedVector<StyleSetArray> language_fallback_families_array_;
+  NameToStyleSetArrayMap language_fallback_families_map_;
 
+  // The default family that is used when no specific match is found during  a
+  // request.
   SkFontStyleSet_Cobalt* default_family_;
-  SkAutoTUnref<SkTypeface> default_typeface_;
-
-  // System typeface stream related
-  // NOTE: mutable is required on all variables potentially modified via calls
-  // from SkFontStyleSet_Cobalt::onOpenStream(), which is const.
-
-  // Tracking of active and inactive open streams among the system typefaces.
-  // The streams are initially active and are moved to the inactive list when
-  // the stream's underlying SkData no longer has any external references.
-  // Inactive open streams are stored and released in temporal order.
-  mutable SkTArray<const SkTypeface_CobaltSystem*>
-      system_typefaces_with_active_open_streams_;
-  mutable SkTArray<TimedSystemTypeface>
-      system_typefaces_with_inactive_open_streams_;
-
-  // This mutex is used when accessing/modifying both the system typeface stream
-  // data itself and the variables used with tracking that data.
-  mutable SkMutex system_typeface_stream_mutex_;
 
   // Mutex shared by all style sets for accessing their modifiable data.
   mutable SkMutex style_sets_mutex_;
-
-  // The last time that a partial purge of Skia's font cache was forced. This is
-  // done periodically to ensure that unused fonts are not indefinitely
-  // referenced by Skia.
-  base::TimeTicks last_font_cache_purge_time_;
-
-  // SkTypeface_CobaltSystem is a friend so that it can make system typeface
-  // open stream calls into SkFontMgr_Cobalt from
-  // SkTypeface_CobaltSystem::onOpenStream(). Those calls should not be
-  // accessible publicly.
-  friend class SkTypeface_CobaltSystem;
 };
 
 #endif  // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc
index aea69d6..9722f21 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc
@@ -23,8 +23,8 @@
   CHECK(PathService::Get(base::DIR_EXE, &font_directory));
   font_directory = font_directory.Append(FILE_PATH_LITERAL("fonts"));
 
-  SkTArray<SkString, true> default_fonts;
-  default_fonts.push_back(SkString("sans-serif"));
+  SkTArray<SkString, true> default_families;
+  default_families.push_back(SkString("sans-serif"));
 
-  return new SkFontMgr_Cobalt(font_directory.value().c_str(), default_fonts);
+  return new SkFontMgr_Cobalt(font_directory.value().c_str(), default_families);
 }
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
new file mode 100644
index 0000000..4a4f6c1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
@@ -0,0 +1,397 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h"
+
+#include <cmath>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <limits>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+#include "SkOSFile.h"
+
+namespace {
+
+int MatchScore(const SkFontStyle& pattern, const SkFontStyle& candidate) {
+  int score = 0;
+  score += std::abs((pattern.width() - candidate.width()) * 100);
+  score += (pattern.isItalic() == candidate.isItalic()) ? 0 : 1000;
+  score += std::abs(pattern.weight() - candidate.weight());
+  return score;
+}
+
+}  // namespace
+
+// These functions are used by FreeType during FT_Open_Face.
+extern "C" {
+
+static unsigned long sk_cobalt_ft_stream_io(FT_Stream ftStream,
+                                            unsigned long offset,
+                                            unsigned char* buffer,
+                                            unsigned long count) {
+  SkStreamAsset* stream =
+      static_cast<SkStreamAsset*>(ftStream->descriptor.pointer);
+  stream->seek(offset);
+  return stream->read(buffer, count);
+}
+
+static void sk_cobalt_ft_stream_close(FT_Stream) {}
+}
+
+//  This class is used by SkFontMgr_Cobalt to hold SkTypeface_CobaltSystem
+//  families.
+SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt(
+    const FontFamily& family, const char* base_path,
+    SkFileMemoryChunkStreamManager* const system_typeface_stream_manager,
+    SkMutex* const manager_owned_mutex)
+    : system_typeface_stream_manager_(system_typeface_stream_manager),
+      manager_owned_mutex_(manager_owned_mutex),
+      is_fallback_family_(family.is_fallback_family),
+      language_(family.language),
+      page_ranges_(family.page_ranges),
+      is_character_map_generated_(!is_fallback_family_) {
+  TRACE_EVENT0("cobalt::renderer",
+               "SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt()");
+  DCHECK(manager_owned_mutex_);
+
+  if (family.names.count() == 0) {
+    return;
+  }
+
+  family_name_ = family.names[0];
+
+  for (int i = 0; i < family.fonts.count(); ++i) {
+    const FontFileInfo& font_file = family.fonts[i];
+
+    SkString file_path(SkOSPath::Join(base_path, font_file.file_name.c_str()));
+
+    // Sanity check that something exists at this location.
+    if (!sk_exists(file_path.c_str(), kRead_SkFILE_Flag)) {
+      LOG(ERROR) << "Failed to find font file: " << file_path.c_str();
+      continue;
+    }
+
+    SkFontStyle style(font_file.weight, SkFontStyle::kNormal_Width,
+                      font_file.style == FontFileInfo::kItalic_FontStyle
+                          ? SkFontStyle::kItalic_Slant
+                          : SkFontStyle::kUpright_Slant);
+
+    std::string full_font_name;
+    if (!font_file.full_font_name.isEmpty()) {
+      full_font_name = std::string(font_file.full_font_name.c_str(),
+                                   font_file.full_font_name.size());
+    }
+    std::string postscript_name;
+    if (!font_file.postscript_name.isEmpty()) {
+      postscript_name = std::string(font_file.postscript_name.c_str(),
+                                    font_file.postscript_name.size());
+    }
+
+    styles_.push_back().reset(
+        SkNEW_ARGS(SkFontStyleSetEntry_Cobalt,
+                   (file_path, font_file.index, style, full_font_name,
+                    postscript_name, font_file.disable_synthetic_bolding)));
+  }
+}
+
+int SkFontStyleSet_Cobalt::count() {
+  SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
+  return styles_.count();
+}
+
+void SkFontStyleSet_Cobalt::getStyle(int index, SkFontStyle* style,
+                                     SkString* name) {
+  // SkFontStyleSet_Cobalt does not support publicly interacting with entries
+  // via index, as entries can potentially be removed, thereby invalidating the
+  // indices.
+  NOTREACHED();
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::createTypeface(int index) {
+  // SkFontStyleSet_Cobalt does not support publicly interacting with entries
+  // via index, as entries can potentially be removed, thereby invalidating the
+  // indices.
+  NOTREACHED();
+  return NULL;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::matchStyle(const SkFontStyle& pattern) {
+  SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
+  return MatchStyleWithoutLocking(pattern);
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::MatchStyleWithoutLocking(
+    const SkFontStyle& pattern) {
+  SkTypeface* typeface = NULL;
+  while (typeface == NULL && styles_.count() > 0) {
+    typeface = TryRetrieveTypefaceAndRemoveStyleOnFailure(
+        GetClosestStyleIndex(pattern));
+  }
+  return typeface;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::MatchFullFontName(const std::string& name) {
+  for (int i = 0; i < styles_.count(); ++i) {
+    if (styles_[i]->full_font_name == name) {
+      return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
+    }
+  }
+  return NULL;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::MatchFontPostScriptName(
+    const std::string& name) {
+  for (int i = 0; i < styles_.count(); ++i) {
+    if (styles_[i]->font_postscript_name == name) {
+      return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
+    }
+  }
+  return NULL;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::TryRetrieveTypefaceAndRemoveStyleOnFailure(
+    int style_index) {
+  DCHECK(style_index >= 0 && style_index < styles_.count());
+  SkFontStyleSetEntry_Cobalt* style = styles_[style_index];
+  // If the typeface doesn't already exist, then attempt to create it.
+  if (style->typeface == NULL) {
+    CreateSystemTypeface(style);
+    // If the creation attempt failed and the typeface is still NULL, then
+    // remove the entry from the set's styles.
+    if (style->typeface == NULL) {
+      styles_[style_index].swap(&styles_.back());
+      styles_.pop_back();
+      return NULL;
+    }
+  }
+  return SkRef(style->typeface.get());
+}
+
+bool SkFontStyleSet_Cobalt::ContainsTypeface(const SkTypeface* typeface) {
+  for (int i = 0; i < styles_.count(); ++i) {
+    if (styles_[i]->typeface == typeface) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool SkFontStyleSet_Cobalt::ContainsCharacter(const SkFontStyle& style,
+                                              SkUnichar character) {
+  // If page ranges exist for this style set, then verify that this character
+  // falls within the ranges. Otherwise, the style set is treated as having a
+  // page range containing all characters.
+  size_t num_ranges = page_ranges_.count();
+  if (num_ranges > 0) {
+    int16 page = font_character_map::GetPage(character);
+
+    // Verify that the last page range is not less than the page containing the
+    // character. If it's less, then this character can't be contained
+    // within the pages. Otherwise, this last range acts as a sentinel for the
+    // search.
+    if (page_ranges_[num_ranges - 1].second < page) {
+      return false;
+    }
+
+    int range_index = 0;
+    while (page_ranges_[range_index].second < page) {
+      ++range_index;
+    }
+
+    if (page_ranges_[range_index].first > page) {
+      return false;
+    }
+  }
+
+  // If this point is reached, then the character is contained within one of
+  // the font style set's page ranges. Now, verify that the specific character
+  // is supported by the font style set.
+
+  // The character map is lazily generated. Generate it now if it isn't already
+  // generated.
+  if (!is_character_map_generated_) {
+    TRACE_EVENT0("cobalt::renderer",
+                 "SkFontStyleSet_Cobalt::ContainsCharacter() and "
+                 "!is_character_map_generated_");
+    // Attempt to load the closest font style from the set. If it fails to load,
+    // it will be removed from the set and, as long as font styles remain in the
+    // set, the logic will be attempted again.
+    while (styles_.count() > 0) {
+      int style_index = GetClosestStyleIndex(style);
+      SkFontStyleSetEntry_Cobalt* closest_style = styles_[style_index];
+
+      SkFileMemoryChunkStreamProvider* stream_provider =
+          system_typeface_stream_manager_->GetStreamProvider(
+              closest_style->font_file_path.c_str());
+
+      // Create a snapshot prior to loading any additional memory chunks. In the
+      // case where the typeface does not end up being created, this enables the
+      // stream provider to purge newly created memory chunks, while retaining
+      // any pre-existing ones.
+      scoped_ptr<const SkFileMemoryChunks> memory_chunks_snapshot(
+          stream_provider->CreateMemoryChunksSnapshot());
+
+      SkAutoTUnref<SkFileMemoryChunkStream> stream(
+          stream_provider->OpenStream());
+      if (GenerateStyleFaceInfo(closest_style, stream)) {
+        if (CharacterMapContainsCharacter(character)) {
+          CreateSystemTypeface(closest_style, stream_provider);
+          return true;
+        } else {
+          // If a typeface was not created, destroy the stream and purge any
+          // newly created memory chunks. The stream must be destroyed first or
+          // it will retain references to memory chunks, preventing them from
+          // being purged.
+          stream.reset(NULL);
+          stream_provider->PurgeUnusedMemoryChunks();
+          return false;
+        }
+      }
+
+      styles_[style_index].swap(&styles_.back());
+      styles_.pop_back();
+    }
+
+    is_character_map_generated_ = true;
+  }
+
+  return CharacterMapContainsCharacter(character);
+}
+
+bool SkFontStyleSet_Cobalt::CharacterMapContainsCharacter(SkUnichar character) {
+  font_character_map::CharacterMap::iterator page_iterator =
+      character_map_.find(font_character_map::GetPage(character));
+  return page_iterator != character_map_.end() &&
+         page_iterator->second.test(
+             font_character_map::GetPageCharacterIndex(character));
+}
+
+bool SkFontStyleSet_Cobalt::GenerateStyleFaceInfo(
+    SkFontStyleSetEntry_Cobalt* style, SkStreamAsset* stream) {
+  if (style->is_face_info_generated) {
+    return true;
+  }
+
+  TRACE_EVENT0("cobalt::renderer", "GenerateStyleFaceInfo()");
+
+  FT_Library freetype_lib;
+  if (FT_Init_FreeType(&freetype_lib) != 0) {
+    return false;
+  }
+
+  FT_StreamRec streamRec;
+  memset(&streamRec, 0, sizeof(streamRec));
+  streamRec.size = stream->getLength();
+  streamRec.descriptor.pointer = stream;
+  streamRec.read = sk_cobalt_ft_stream_io;
+  streamRec.close = sk_cobalt_ft_stream_close;
+
+  FT_Open_Args args;
+  memset(&args, 0, sizeof(args));
+  args.flags = FT_OPEN_STREAM;
+  args.stream = &streamRec;
+
+  FT_Face face;
+  FT_Error err = FT_Open_Face(freetype_lib, &args, style->face_index, &face);
+  if (err) {
+    FT_Done_FreeType(freetype_lib);
+    return false;
+  }
+
+  int face_style = SkTypeface::kNormal;
+  if (face->style_flags & FT_STYLE_FLAG_BOLD) {
+    face_style |= SkTypeface::kBold;
+  }
+  if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
+    face_style |= SkTypeface::kItalic;
+  }
+
+  style->face_name.set(face->family_name);
+  style->face_style = static_cast<SkTypeface::Style>(face_style);
+  style->face_is_fixed_pitch = FT_IS_FIXED_WIDTH(face);
+  style->is_face_info_generated = true;
+
+  // Map out this family's characters if they haven't been generated yet.
+  if (!is_character_map_generated_) {
+    FT_UInt glyph_index;
+
+    int last_page = -1;
+    font_character_map::PageCharacters* page_characters = NULL;
+
+    SkUnichar code_point = FT_Get_First_Char(face, &glyph_index);
+    while (glyph_index) {
+      int page = font_character_map::GetPage(code_point);
+      if (page != last_page) {
+        page_characters = &character_map_[page];
+        last_page = page;
+      }
+      page_characters->set(
+          font_character_map::GetPageCharacterIndex(code_point));
+
+      code_point = FT_Get_Next_Char(face, code_point, &glyph_index);
+    }
+
+    is_character_map_generated_ = true;
+  }
+
+  // release this font.
+  FT_Done_Face(face);
+
+  // shut down FreeType.
+  FT_Done_FreeType(freetype_lib);
+  return true;
+}
+
+int SkFontStyleSet_Cobalt::GetClosestStyleIndex(const SkFontStyle& pattern) {
+  int closest_index = 0;
+  int min_score = std::numeric_limits<int>::max();
+  for (int i = 0; i < styles_.count(); ++i) {
+    int score = MatchScore(pattern, styles_[i]->font_style);
+    if (score < min_score) {
+      closest_index = i;
+      min_score = score;
+    }
+  }
+  return closest_index;
+}
+
+void SkFontStyleSet_Cobalt::CreateSystemTypeface(
+    SkFontStyleSetEntry_Cobalt* style_entry,
+    SkFileMemoryChunkStreamProvider* stream_provider /*=NULL*/) {
+  TRACE_EVENT0("cobalt::renderer",
+               "SkFontStyleSet_Cobalt::CreateSystemTypeface()");
+
+  if (!stream_provider) {
+    stream_provider = system_typeface_stream_manager_->GetStreamProvider(
+        style_entry->font_file_path.c_str());
+  }
+
+  SkAutoTUnref<SkFileMemoryChunkStream> stream(stream_provider->OpenStream());
+  if (GenerateStyleFaceInfo(style_entry, stream)) {
+    LOG(ERROR) << "Scanned font from file: " << style_entry->face_name.c_str()
+               << "(" << style_entry->face_style << ")";
+    style_entry->typeface.reset(
+        SkNEW_ARGS(SkTypeface_CobaltSystem,
+                   (stream_provider, style_entry->face_index,
+                    style_entry->face_style, style_entry->face_is_fixed_pitch,
+                    family_name_, style_entry->disable_synthetic_bolding)));
+  } else {
+    LOG(ERROR) << "Failed to scan font: "
+               << style_entry->font_file_path.c_str();
+  }
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
new file mode 100644
index 0000000..da1b701
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
@@ -0,0 +1,135 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTSTYLESET_COBALT_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTSTYLESET_COBALT_H_
+
+#include <string>
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
+#include "SkFontMgr.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTArray.h"
+#include "SkTypeface.h"
+
+//  This class is used by SkFontMgr_Cobalt to hold SkTypeface_Cobalt families.
+//
+//  To avoid the memory hit from keeping all fonts around, both the full
+//  character map for fonts and the font faces for fonts are lazily loaded
+//  when needed. After this, they are retained in memory.
+//
+//  The style sets contain an array of page ranges, providing information on
+//  characters that are potentially contained. To determine whether or not a
+//  character is actually contained, the full character map is generated.
+class SkFontStyleSet_Cobalt : public SkFontStyleSet {
+ public:
+  struct SkFontStyleSetEntry_Cobalt : public SkRefCnt {
+    // NOTE: |SkFontStyleSetEntry_Cobalt| objects are not guaranteed to last for
+    // the lifetime of |SkFontMgr_Cobalt| and can be removed by their owning
+    // |SkFontStyleSet_Cobalt| if their typeface fails to load properly. As a
+    // result, it is not safe to store their pointers outside of
+    // |SkFontStyleSet_Cobalt|.
+    SkFontStyleSetEntry_Cobalt(const SkString& file_path, const int face_index,
+                               const SkFontStyle& style,
+                               const std::string& full_name,
+                               const std::string& postscript_name,
+                               const bool disable_synthetic_bolding)
+        : font_file_path(file_path),
+          face_index(face_index),
+          font_style(style),
+          full_font_name(full_name),
+          font_postscript_name(postscript_name),
+          disable_synthetic_bolding(disable_synthetic_bolding),
+          is_face_info_generated(false),
+          face_style(SkTypeface::kNormal),
+          face_is_fixed_pitch(false),
+          typeface(NULL) {}
+
+    const SkString font_file_path;
+    const int face_index;
+    const SkFontStyle font_style;
+    const std::string full_font_name;
+    const std::string font_postscript_name;
+    const bool disable_synthetic_bolding;
+
+    // Face info is generated from the font file.
+    bool is_face_info_generated;
+    SkString face_name;
+    SkTypeface::Style face_style;
+    bool face_is_fixed_pitch;
+
+    SkAutoTUnref<SkTypeface> typeface;
+  };
+
+  SkFontStyleSet_Cobalt(
+      const FontFamily& family, const char* base_path,
+      SkFileMemoryChunkStreamManager* const system_typeface_stream_manager,
+      SkMutex* const manager_owned_mutex);
+
+  // From SkFontStyleSet
+  virtual int count() SK_OVERRIDE;
+  // NOTE: SkFontStyleSet_Cobalt does not support |getStyle|, as publicly
+  // accessing styles by index is unsafe.
+  virtual void getStyle(int index, SkFontStyle* style,
+                        SkString* name) SK_OVERRIDE;
+  // NOTE: SkFontStyleSet_Cobalt does not support |createTypeface|, as
+  // publicly accessing styles by index is unsafe.
+  virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
+
+  virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
+
+  const SkString& get_family_name() const { return family_name_; }
+
+ private:
+  // NOTE: It is the responsibility of the caller to lock the mutex before
+  // calling any of the non-const private functions.
+
+  SkTypeface* MatchStyleWithoutLocking(const SkFontStyle& pattern);
+  SkTypeface* MatchFullFontName(const std::string& name);
+  SkTypeface* MatchFontPostScriptName(const std::string& name);
+  SkTypeface* TryRetrieveTypefaceAndRemoveStyleOnFailure(int style_index);
+  bool ContainsTypeface(const SkTypeface* typeface);
+
+  bool ContainsCharacter(const SkFontStyle& style, SkUnichar character);
+  bool CharacterMapContainsCharacter(SkUnichar character);
+
+  bool GenerateStyleFaceInfo(SkFontStyleSetEntry_Cobalt* style,
+                             SkStreamAsset* stream);
+
+  int GetClosestStyleIndex(const SkFontStyle& pattern);
+  void CreateSystemTypeface(
+      SkFontStyleSetEntry_Cobalt* style,
+      SkFileMemoryChunkStreamProvider* stream_provider = NULL);
+
+  // NOTE: The following variables can safely be accessed outside of the mutex.
+  SkFileMemoryChunkStreamManager* const system_typeface_stream_manager_;
+  SkMutex* const manager_owned_mutex_;
+
+  SkString family_name_;
+  bool is_fallback_family_;
+  SkLanguage language_;
+  font_character_map::PageRanges page_ranges_;
+
+  // NOTE: The following characters require locking when being accessed.
+  bool is_character_map_generated_;
+  font_character_map::CharacterMap character_map_;
+
+  SkTArray<SkAutoTUnref<SkFontStyleSetEntry_Cobalt>, true> styles_;
+
+  friend class SkFontMgr_Cobalt;
+};
+
+#endif  // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTSTYLESET_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
index b09b2c1..ef1af47 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
@@ -101,12 +101,17 @@
   };
   typedef uint32_t FontStyle;
 
-  FontFileInfo() : index(0), weight(400), style(kNormal_FontStyle) {}
+  FontFileInfo()
+      : index(0),
+        weight(400),
+        style(kNormal_FontStyle),
+        disable_synthetic_bolding(false) {}
 
   SkString file_name;
   int index;
   int weight;
   FontStyle style;
+  bool disable_synthetic_bolding;
 
   SkString full_font_name;
   SkString postscript_name;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc
index 5179439..7267c89 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc
@@ -57,8 +57,13 @@
 }  // namespace
 
 SkFILE* sk_fopen(const char path[], SkFILE_Flags sk_flags) {
-  return ToSkFILE(base::CreatePlatformFile(
-      FilePath(path), ToPlatformFlags(sk_flags), NULL, NULL));
+  base::PlatformFile platform_file = base::CreatePlatformFile(
+      FilePath(path), ToPlatformFlags(sk_flags), NULL, NULL);
+  if (platform_file == base::kInvalidPlatformFileValue) {
+    return NULL;
+  }
+
+  return ToSkFILE(platform_file);
 }
 
 void sk_fclose(SkFILE* sk_file) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc
new file mode 100644
index 0000000..60d30dc
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc
@@ -0,0 +1,336 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "SkOSFile.h"
+
+SkFileMemoryChunkStreamManager::SkFileMemoryChunkStreamManager(
+    const std::string& name, int cache_size_in_bytes)
+    : available_chunk_count_(cache_size_in_bytes /
+                             SkFileMemoryChunk::kSizeInBytes),
+      cache_limit_in_bytes_(StringPrintf("Memory.%s.Capacity", name.c_str()),
+                            cache_size_in_bytes,
+                            "The byte capacity of the cache."),
+      cache_size_in_bytes_(
+          StringPrintf("Memory.%s.Size", name.c_str()), 0,
+          "Total number of bytes currently used by the cache.") {}
+
+SkFileMemoryChunkStreamProvider*
+SkFileMemoryChunkStreamManager::GetStreamProvider(
+    const std::string& file_path) {
+  SkAutoMutexAcquire scoped_mutex(stream_provider_mutex_);
+
+  // Return a pre-existing stream provider if it exists.
+  SkFileMemoryChunkStreamProviderMap::iterator iter =
+      stream_provider_map_.find(file_path);
+  if (iter != stream_provider_map_.end()) {
+    return iter->second;
+  }
+
+  // Otherwise, create a new stream provider, add it to the containers, and
+  // return it. This stream provider will be returned for any subsequent
+  // requests specifying this file, ensuring that memory chunks will be shared.
+  stream_provider_array_.push_back(
+      new SkFileMemoryChunkStreamProvider(file_path, this));
+  SkFileMemoryChunkStreamProvider* stream_provider =
+      *stream_provider_array_.rbegin();
+  stream_provider_map_[file_path] = stream_provider;
+  return stream_provider;
+}
+
+bool SkFileMemoryChunkStreamManager::TryReserveMemoryChunk() {
+  // First check to see if the count is already 0. If it is, then there's no
+  // available memory chunk to try to reserve. Simply return failure.
+  if (base::subtle::NoBarrier_Load(&available_chunk_count_) <= 0) {
+    return false;
+  }
+
+  // Decrement the available count behind a memory barrier. If the return value
+  // is less than 0, then another requester reserved the last available memory
+  // chunk first. In that case, restore the chunk to the count and return
+  // failure.
+  if (base::subtle::Barrier_AtomicIncrement(&available_chunk_count_, -1) < 0) {
+    base::subtle::NoBarrier_AtomicIncrement(&available_chunk_count_, 1);
+    return false;
+  }
+
+  // The chunk was successfully reserved. Add the reserved chunk to the used
+  // cache size.
+  cache_size_in_bytes_ += SkFileMemoryChunk::kSizeInBytes;
+  return true;
+}
+
+void SkFileMemoryChunkStreamManager::ReleaseReservedMemoryChunks(size_t count) {
+  base::subtle::NoBarrier_AtomicIncrement(&available_chunk_count_, count);
+  cache_size_in_bytes_ -= count * SkFileMemoryChunk::kSizeInBytes;
+}
+
+SkFileMemoryChunkStreamProvider::SkFileMemoryChunkStreamProvider(
+    const std::string& file_path, SkFileMemoryChunkStreamManager* manager)
+    : file_path_(file_path), manager_(manager) {}
+
+SkFileMemoryChunkStream* SkFileMemoryChunkStreamProvider::OpenStream() const {
+  return SkNEW_ARGS(SkFileMemoryChunkStream,
+                    (const_cast<SkFileMemoryChunkStreamProvider*>(this)));
+}
+
+scoped_ptr<const SkFileMemoryChunks>
+SkFileMemoryChunkStreamProvider::CreateMemoryChunksSnapshot() {
+  SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+  return make_scoped_ptr<const SkFileMemoryChunks>(
+      new SkFileMemoryChunks(memory_chunks_));
+}
+
+void SkFileMemoryChunkStreamProvider::PurgeUnusedMemoryChunks() {
+  size_t purge_count = 0;
+
+  // Scope the logic that accesses the memory chunks so that releasing reserved
+  // memory chunks does not happen within the lock.
+  {
+    SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+
+    // Walk the memory chunks, adding any with a single ref. These are not
+    // externally referenced and can be purged.
+    std::vector<size_t> purge_indices;
+    for (SkFileMemoryChunks::iterator iter = memory_chunks_.begin();
+         iter != memory_chunks_.end(); ++iter) {
+      if (iter->second && iter->second->HasOneRef()) {
+        purge_indices.push_back(iter->first);
+      }
+    }
+
+    // If the memory chunks and purge indices have the same size, then every
+    // memory chunk is being purged and they can simply be cleared. Otherwise,
+    // the purge indices must individually be removed from the memory chunks.
+    if (memory_chunks_.size() == purge_indices.size()) {
+      memory_chunks_.clear();
+    } else {
+      for (std::vector<size_t>::iterator iter = purge_indices.begin();
+           iter != purge_indices.end(); ++iter) {
+        memory_chunks_.erase(*iter);
+      }
+    }
+
+    purge_count = purge_indices.size();
+  }
+
+  // Make any memory chunks that were purged available again.
+  if (purge_count > 0) {
+    manager_->ReleaseReservedMemoryChunks(purge_count);
+  }
+}
+
+scoped_refptr<const SkFileMemoryChunk>
+SkFileMemoryChunkStreamProvider::TryGetMemoryChunk(
+    size_t index, SkFileMemoryChunkStream* stream) {
+  // Scope the logic that initially accesses the memory chunks. This allows the
+  // creation of a new memory chunk and the read from the stream into its memory
+  // to occur outside of a lock.
+  {
+    SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+    SkFileMemoryChunks::const_iterator iter = memory_chunks_.find(index);
+    if (iter != memory_chunks_.end()) {
+      return iter->second;
+    }
+  }
+
+  // Verify that the new memory chunk can be reserved before attempting to
+  // create it.
+  if (!manager_->TryReserveMemoryChunk()) {
+    return NULL;
+  }
+
+  // Create the memory chunk and attempt to read from the stream into it. If
+  // this fails, clear out |new_chunk|, which causes it to be deleted; however,
+  // do not return. Subsequent attempts will also fail, so the map is populated
+  // with a NULL value for this index to prevent the attempts from occurring.
+  scoped_refptr<SkFileMemoryChunk> new_chunk(new SkFileMemoryChunk);
+  if (!stream->ReadIndexIntoMemoryChunk(index, new_chunk)) {
+    new_chunk = NULL;
+  }
+
+  // Re-lock the mutex. It's time to potentially add the newly created chunk to
+  // |memory_chunks_|.
+  SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+  scoped_refptr<const SkFileMemoryChunk>& chunk = memory_chunks_[index];
+  // If there isn't a pre-existing chunk (this can occur if there was a race
+  // between two calls to TryGetMemoryChunk() and the other call won), and the
+  // new chunk exists, then set the reference so that it'll be available for
+  // subsequent calls.
+  if (chunk == NULL && new_chunk != NULL) {
+    chunk = new_chunk;
+  } else {
+    // The new chunk will not be retained, so make the reserved chunk
+    // available again.
+    manager_->ReleaseReservedMemoryChunks(1);
+  }
+
+  return chunk;
+}
+
+SkFileMemoryChunkStream::SkFileMemoryChunkStream(
+    SkFileMemoryChunkStreamProvider* stream_provider)
+    : stream_provider_(stream_provider),
+      file_(sk_fopen(stream_provider->file_path().c_str(), kRead_SkFILE_Flag)),
+      file_position_(0),
+      stream_position_(0) {
+  file_length_ = file_ ? sk_fgetsize(file_) : 0;
+}
+
+SkFileMemoryChunkStream::~SkFileMemoryChunkStream() {
+  if (file_) {
+    sk_fclose(file_);
+  }
+}
+
+size_t SkFileMemoryChunkStream::read(void* buffer, size_t size) {
+  if (!file_) {
+    return 0;
+  }
+
+  size_t start_stream_position = stream_position_;
+  size_t end_stream_position = stream_position_ + size;
+
+  size_t start_index = start_stream_position / SkFileMemoryChunk::kSizeInBytes;
+  size_t end_index = end_stream_position / SkFileMemoryChunk::kSizeInBytes;
+
+  // Iterate through all of the chunk indices included within the read. Each is
+  // read into the buffer separately.
+  for (size_t current_index = start_index; current_index <= end_index;
+       ++current_index) {
+    size_t current_index_end_stream_position =
+        current_index == end_index
+            ? end_stream_position
+            : ((current_index + 1) * SkFileMemoryChunk::kSizeInBytes);
+    size_t index_desired_read_size =
+        current_index_end_stream_position - stream_position_;
+
+    // Look for the chunk within the stream's cached chunks. If it isn't there,
+    // then attempt to retrieve it from the stream provider and cache the
+    // result.
+    scoped_refptr<const SkFileMemoryChunk> memory_chunk;
+    SkFileMemoryChunks::const_iterator iter =
+        memory_chunks_.find(current_index);
+    if (iter == memory_chunks_.end()) {
+      memory_chunk = memory_chunks_[current_index] =
+          stream_provider_->TryGetMemoryChunk(current_index, this);
+    } else {
+      memory_chunk = iter->second;
+    }
+
+    // It's time to read the data specified by the index into the buffer. If
+    // the memory chunk exists, then the data can be directly copied from its
+    // memory; otherwise, the data needs to be read from the stream's file.
+    size_t index_actual_read_size;
+    if (memory_chunk) {
+      size_t index_start_position =
+          current_index * SkFileMemoryChunk::kSizeInBytes;
+      memcpy(buffer,
+             memory_chunk->memory + (stream_position_ - index_start_position),
+             index_desired_read_size);
+      index_actual_read_size = index_desired_read_size;
+    } else {
+      // Ensure that the file position matches the stream's position. If the
+      // seek fails, then set file position to an invalid value to ensure that
+      // the next read triggers a new seek, and break out.
+      if (file_position_ != stream_position_ &&
+          !sk_fseek(file_, stream_position_)) {
+        file_position_ = std::numeric_limits<size_t>::max();
+        break;
+      }
+
+      index_actual_read_size = sk_fread(buffer, index_desired_read_size, file_);
+      file_position_ = stream_position_ + index_actual_read_size;
+    }
+
+    stream_position_ += index_actual_read_size;
+
+    // Verify that the read succeeded. If it failed, then break out because
+    // nothing more can be processed; otherwise, if there are additional indices
+    // to process, update the buffer's pointer so that they will write to the
+    // correct memory location.
+    if (index_desired_read_size != index_actual_read_size) {
+      break;
+    } else if (current_index < end_index) {
+      buffer = reinterpret_cast<uint8_t*>(buffer) + index_actual_read_size;
+    }
+  }
+
+  return stream_position_ - start_stream_position;
+}
+
+bool SkFileMemoryChunkStream::isAtEnd() const {
+  return stream_position_ == file_length_;
+}
+
+bool SkFileMemoryChunkStream::rewind() {
+  stream_position_ = 0;
+  return true;
+}
+
+SkFileMemoryChunkStream* SkFileMemoryChunkStream::duplicate() const {
+  return stream_provider_->OpenStream();
+}
+
+size_t SkFileMemoryChunkStream::getPosition() const { return stream_position_; }
+
+bool SkFileMemoryChunkStream::seek(size_t position) {
+  stream_position_ = std::min(position, file_length_);
+  return true;
+}
+
+bool SkFileMemoryChunkStream::move(long offset) {
+  // Rewind back to the start of the stream if the offset would move it to a
+  // negative position.
+  if (offset < 0 && std::abs(offset) > stream_position_) {
+    return rewind();
+  }
+  return seek(stream_position_ + offset);
+}
+
+SkFileMemoryChunkStream* SkFileMemoryChunkStream::fork() const {
+  SkAutoTUnref<SkFileMemoryChunkStream> that(duplicate());
+  that->seek(stream_position_);
+  return that.detach();
+}
+
+size_t SkFileMemoryChunkStream::getLength() const { return file_length_; }
+
+bool SkFileMemoryChunkStream::ReadIndexIntoMemoryChunk(
+    size_t index, SkFileMemoryChunk* chunk) {
+  size_t index_position = index * SkFileMemoryChunk::kSizeInBytes;
+
+  // Ensure that the file position matches the index's position. If the seek
+  // fails, then set file position to an invalid value to ensure that the next
+  // read triggers a new seek, and return false.
+  if (file_position_ != index_position && !sk_fseek(file_, index_position)) {
+    file_position_ = std::numeric_limits<size_t>::max();
+    return false;
+  }
+
+  const size_t kChunkMaxReadSize = SkFileMemoryChunk::kSizeInBytes;
+  size_t desired_read_size =
+      std::min(file_length_ - index_position, kChunkMaxReadSize);
+  size_t actual_read_size = sk_fread(chunk->memory, desired_read_size, file_);
+  file_position_ = index_position + actual_read_size;
+
+  return desired_read_size == actual_read_size;
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h
new file mode 100644
index 0000000..7adb62f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h
@@ -0,0 +1,212 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKSTREAM_COBALT_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKSTREAM_COBALT_H_
+
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/containers/small_map.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "cobalt/base/c_val.h"
+#include "SkStream.h"
+
+// The SkFileMemoryChunkStream classes provide a stream type that mixes features
+// of both file streams and memory streams. While the stream initially reads
+// from its file like a file stream, it lazily caches newly encountered 32KB
+// chunks of the file into memory, enabling it to avoid reading sections of the
+// file more than once--subsequent reads into those cached sections are made
+// directly from memory. This caching occurs until the manager's cache limit is
+// exhausted. After this, reads into uncached sections of the stream are always
+// made to the file and not cached, functioning similarly to a file stream.
+// Reads into the already cached sections are still handled in memory.
+//
+// This stream type provides much of the speed benefits of memory streams, while
+// also guaranteeing that memory will not be wasted on sections of files that
+// are never used and that the combined memory usage of the streams will never
+// exceed the specified limit. If the cache limit is set to 0, then the streams
+// do no caching and fully function like file streams.
+
+class SkFileMemoryChunk;
+class SkFileMemoryChunkStream;
+class SkFileMemoryChunkStreamProvider;
+
+typedef base::SmallMap<
+    base::hash_map<size_t, scoped_refptr<const SkFileMemoryChunk> >, 8>
+    SkFileMemoryChunks;
+typedef base::hash_map<std::string, SkFileMemoryChunkStreamProvider*>
+    SkFileMemoryChunkStreamProviderMap;
+
+class SkFileMemoryChunk : public base::RefCountedThreadSafe<SkFileMemoryChunk> {
+ public:
+  static const size_t kSizeInBytes = 32 * 1024;
+  uint8_t memory[kSizeInBytes];
+};
+
+// SkFileMemoryChunkStreamManager is a thread-safe class that generates
+// file-specific SkFileMemoryChunkStreamProvider objects, which can be
+// used to create streams for those files. It also handles tracking the memory
+// available for memory chunks within the cache and determining whether or not
+// additional chunks can be created.
+class SkFileMemoryChunkStreamManager {
+ public:
+  SkFileMemoryChunkStreamManager(const std::string& name,
+                                 int cache_size_in_bytes);
+
+  // Returns the stream provider associated with the file path. If it does not
+  // exist then it is created.
+  SkFileMemoryChunkStreamProvider* GetStreamProvider(
+      const std::string& file_path);
+
+ private:
+  friend SkFileMemoryChunkStreamProvider;
+
+  // Attempts to reserve an available memory chunk and returns true if
+  // successful. On success, |available_chunk_count_| is decremented by one.
+  bool TryReserveMemoryChunk();
+  // Adds the specified count to |available_chunk_count_|.
+  void ReleaseReservedMemoryChunks(size_t count);
+
+  SkMutex stream_provider_mutex_;
+  ScopedVector<SkFileMemoryChunkStreamProvider> stream_provider_array_;
+  SkFileMemoryChunkStreamProviderMap stream_provider_map_;
+
+  base::subtle::Atomic32 available_chunk_count_;
+
+  const base::CVal<base::cval::SizeInBytes, base::CValPublic>
+      cache_limit_in_bytes_;
+  base::CVal<base::cval::SizeInBytes, base::CValPublic> cache_size_in_bytes_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStreamManager);
+};
+
+// SkFileMemoryChunkStreamProvider is a thread-safe class that handles creating
+// streams for its specified file and processes requests from those streams for
+// memory chunks. This processing work includes verifying that additional memory
+// chunks are available with the manager, creating and caching new memory
+// chunks, and sharing cached memory chunks between all of the streams that it
+// created.
+class SkFileMemoryChunkStreamProvider {
+ public:
+  const std::string& file_path() const { return file_path_; }
+
+  // Returns a newly created SkFileMemoryChunkStream. It is the caller's
+  // responsibility to destroy it.
+  SkFileMemoryChunkStream* OpenStream() const;
+
+  // Returns a newly created snapshot of the provider's current memory chunks.
+  // While the snapshot exists, it retains references to all of those memory
+  // chunks, guaranteeing that they will be retained when
+  // PurgeUnusedMemoryChunks() is called.
+  scoped_ptr<const SkFileMemoryChunks> CreateMemoryChunksSnapshot();
+
+  // Purges all of the provider's memory chunks that are only referenced by the
+  // provider.
+  void PurgeUnusedMemoryChunks();
+
+ private:
+  friend SkFileMemoryChunkStream;
+  friend SkFileMemoryChunkStreamManager;
+
+  SkFileMemoryChunkStreamProvider(const std::string& file_path,
+                                  SkFileMemoryChunkStreamManager* manager);
+
+  // Returns the specified memory chunk if it exists. If it does not, attempts
+  // to create the memory chunk and populate it with data from the stream at the
+  // specified index. On success, the memory chunk is returned; otherwise, NULL
+  // is returned.
+  scoped_refptr<const SkFileMemoryChunk> TryGetMemoryChunk(
+      size_t index, SkFileMemoryChunkStream* stream);
+
+  const std::string file_path_;
+  SkFileMemoryChunkStreamManager* const manager_;
+
+  // The provider maintains a cache of all memory chunks that have been
+  // requested by any streams. This enables it share the memory chunks between
+  // all of the streams that it creates. The cache must be accessed behind a
+  // lock because the stream requests may come from multiple threads.
+  SkMutex memory_chunks_mutex_;
+  SkFileMemoryChunks memory_chunks_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStreamProvider);
+};
+
+// SkFileMemoryChunkStream is a non-thread safe stream used within Skia, which
+// opens a file handle to the file specified by its provider. It can read data
+// from either memory chunks returned by its provider or from the file itself.
+// While it maintains an internal cache to previously retrieved memory chunks,
+// it relies on its provider for memory chunks that it has not already cached.
+// If a memory chunk is available for a location, the stream reads that location
+// directly from memory; otherwise, it reads the location from the file.
+//
+// NOTE: Although the provider handles creating new memory chunks, it does not
+// directly access the file and uses the requesting stream to read the data into
+// memory.
+class SkFileMemoryChunkStream : public SkStreamAsset {
+ public:
+  ~SkFileMemoryChunkStream();
+
+  // Required by SkStream
+  virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+  virtual bool isAtEnd() const SK_OVERRIDE;
+
+  // Required by SkStreamRewindable
+  virtual bool rewind() SK_OVERRIDE;
+  virtual SkFileMemoryChunkStream* duplicate() const SK_OVERRIDE;
+
+  // Required by SkStreamSeekable
+  virtual size_t getPosition() const SK_OVERRIDE;
+  virtual bool seek(size_t position) SK_OVERRIDE;
+  virtual bool move(long offset) SK_OVERRIDE;
+  virtual SkFileMemoryChunkStream* fork() const SK_OVERRIDE;
+
+  // Required by SkStreamAsset
+  virtual size_t getLength() const SK_OVERRIDE;
+
+ private:
+  friend SkFileMemoryChunkStreamProvider;
+
+  explicit SkFileMemoryChunkStream(
+      SkFileMemoryChunkStreamProvider* stream_provider);
+
+  // Attempts to read the file position specified by the index into the chunk's
+  // memory. Returns true if the read was successful.
+  bool ReadIndexIntoMemoryChunk(size_t index, SkFileMemoryChunk* chunk);
+
+  SkFileMemoryChunkStreamProvider* const stream_provider_;
+
+  SkFILE* const file_;
+  size_t file_length_;
+  size_t file_position_;
+
+  size_t stream_position_;
+
+  // The stream maintains its own references to any memory chunks that it has
+  // requested from the provider. This allows it faster access to them, as it
+  // does not need to worry about thread safety within its locally cached
+  // chunks and can directly access them, whereas a lock is required when
+  // the provider accesses its own memory chunks. The stream's memory chunk
+  // references also enable the provider to know which memory chunks are being
+  // used and which can be purged.
+  SkFileMemoryChunks memory_chunks_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStream);
+};
+
+#endif  // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKSTREAM_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
new file mode 100644
index 0000000..9d9f0de
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
@@ -0,0 +1,81 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+
+#include "SkFontDescriptor.h"
+#include "SkTypefaceCache.h"
+
+SkTypeface_Cobalt::SkTypeface_Cobalt(int face_index, Style style,
+                                     bool is_fixed_pitch,
+                                     const SkString family_name)
+    : INHERITED(style, SkTypefaceCache::NewFontID(), is_fixed_pitch),
+      face_index_(face_index),
+      family_name_(family_name),
+      synthesizes_bold_(!isBold()) {}
+
+void SkTypeface_Cobalt::onGetFamilyName(SkString* family_name) const {
+  *family_name = family_name_;
+}
+
+SkTypeface_CobaltStream::SkTypeface_CobaltStream(SkStreamAsset* stream,
+                                                 int face_index, Style style,
+                                                 bool is_fixed_pitch,
+                                                 const SkString family_name)
+    : INHERITED(face_index, style, is_fixed_pitch, family_name),
+      stream_(SkRef(stream)) {
+  LOG(INFO) << "Created SkTypeface_CobaltMemory: " << family_name.c_str() << "("
+            << style << "); Size: " << stream_->getLength() << " bytes";
+}
+
+void SkTypeface_CobaltStream::onGetFontDescriptor(SkFontDescriptor* descriptor,
+                                                  bool* serialize) const {
+  SkASSERT(descriptor);
+  SkASSERT(serialize);
+  descriptor->setFamilyName(family_name_.c_str());
+  descriptor->setFontFileName(NULL);
+  descriptor->setFontIndex(face_index_);
+  *serialize = true;
+}
+
+SkStreamAsset* SkTypeface_CobaltStream::onOpenStream(int* face_index) const {
+  *face_index = face_index_;
+  return stream_->duplicate();
+}
+
+SkTypeface_CobaltSystem::SkTypeface_CobaltSystem(
+    SkFileMemoryChunkStreamProvider* stream_provider, int face_index,
+    Style style, bool is_fixed_pitch, const SkString family_name,
+    bool disable_synthetic_bolding)
+    : INHERITED(face_index, style, is_fixed_pitch, family_name),
+      stream_provider_(stream_provider) {
+  if (disable_synthetic_bolding) {
+    synthesizes_bold_ = false;
+  }
+}
+
+void SkTypeface_CobaltSystem::onGetFontDescriptor(SkFontDescriptor* descriptor,
+                                                  bool* serialize) const {
+  SkASSERT(descriptor);
+  SkASSERT(serialize);
+  descriptor->setFamilyName(family_name_.c_str());
+  descriptor->setFontFileName(stream_provider_->file_path().c_str());
+  descriptor->setFontIndex(face_index_);
+  *serialize = false;
+}
+
+SkStreamAsset* SkTypeface_CobaltSystem::onOpenStream(int* face_index) const {
+  *face_index = face_index_;
+  return stream_provider_->OpenStream();
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
new file mode 100644
index 0000000..0299cf2
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
@@ -0,0 +1,78 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKTYPEFACE_COBALT_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKTYPEFACE_COBALT_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
+
+class SkFontMgr_Cobalt;
+
+class SkTypeface_Cobalt : public SkTypeface_FreeType {
+ public:
+  SkTypeface_Cobalt(int face_index, Style style, bool is_fixed_pitch,
+                    const SkString family_name);
+
+  bool synthesizes_bold() const { return synthesizes_bold_; }
+
+ protected:
+  virtual void onGetFamilyName(SkString* family_name) const SK_OVERRIDE;
+
+  int face_index_;
+  SkString family_name_;
+  bool synthesizes_bold_;
+
+ private:
+  typedef SkTypeface_FreeType INHERITED;
+};
+
+class SkTypeface_CobaltStream : public SkTypeface_Cobalt {
+ public:
+  SkTypeface_CobaltStream(SkStreamAsset* stream, int face_index, Style style,
+                          bool is_fixed_pitch, const SkString family_name);
+
+  virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
+                                   bool* serialize) const SK_OVERRIDE;
+
+  virtual SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+
+ private:
+  typedef SkTypeface_Cobalt INHERITED;
+
+  SkAutoTUnref<SkStreamAsset> stream_;
+};
+
+class SkTypeface_CobaltSystem : public SkTypeface_Cobalt {
+ public:
+  SkTypeface_CobaltSystem(SkFileMemoryChunkStreamProvider* stream_provider,
+                          int face_index, Style style, bool is_fixed_pitch,
+                          const SkString family_name,
+                          bool disable_synthetic_bolding);
+
+  virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
+                                   bool* serialize) const SK_OVERRIDE;
+
+  virtual SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+
+ private:
+  typedef SkTypeface_Cobalt INHERITED;
+
+  SkFileMemoryChunkStreamProvider* const stream_provider_;
+};
+
+#endif  // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKTYPEFACE_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
index 195d9dc..05e9723 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -20,6 +20,7 @@
 #include "cobalt/renderer/rasterizer/skia/font.h"
 #include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/software_image.h"
 #include "cobalt/renderer/rasterizer/skia/software_mesh.h"
 #include "cobalt/renderer/rasterizer/skia/typeface.h"
@@ -117,14 +118,16 @@
                "SoftwareResourceProvider::GetLocalTypeface()");
 
   SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
-  SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyle(
-      font_family_name, CobaltFontStyleToSkFontStyle(font_style)));
+  SkAutoTUnref<SkTypeface_Cobalt> typeface(
+      base::polymorphic_downcast<SkTypeface_Cobalt*>(
+          font_manager->matchFamilyStyle(
+              font_family_name, CobaltFontStyleToSkFontStyle(font_style))));
   return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
 }
 
 scoped_refptr<render_tree::Typeface>
 SoftwareResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
-    const std::string& font_face_name) {
+    const char* font_face_name) {
   TRACE_EVENT0("cobalt::renderer",
                "SoftwareResourceProvider::GetLocalTypefaceIfAvailable()");
 
@@ -132,7 +135,8 @@
   SkFontMgr_Cobalt* cobalt_font_manager =
       base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager.get());
 
-  SkTypeface* typeface = cobalt_font_manager->matchFaceName(font_face_name);
+  SkTypeface_Cobalt* typeface = base::polymorphic_downcast<SkTypeface_Cobalt*>(
+      cobalt_font_manager->MatchFaceName(font_face_name));
   if (typeface != NULL) {
     SkAutoTUnref<SkTypeface> typeface_unref_helper(typeface);
     return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
@@ -149,9 +153,11 @@
                "SoftwareResourceProvider::GetCharacterFallbackTypeface()");
 
   SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
-  SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyleCharacter(
-      0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
-      character));
+  SkAutoTUnref<SkTypeface_Cobalt> typeface(
+      base::polymorphic_downcast<SkTypeface_Cobalt*>(
+          font_manager->matchFamilyStyleCharacter(
+              0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
+              character)));
   return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
 }
 
@@ -181,7 +187,9 @@
       sanitized_data.get(), static_cast<size_t>(sanitized_data.Tell())));
 
   SkAutoTUnref<SkStreamAsset> stream(new SkMemoryStream(skia_data));
-  SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromStream(stream));
+  SkAutoTUnref<SkTypeface_Cobalt> typeface(
+      base::polymorphic_downcast<SkTypeface_Cobalt*>(
+          SkTypeface::CreateFromStream(stream)));
   if (typeface) {
     return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
   } else {
@@ -219,6 +227,12 @@
   return new SoftwareMesh(vertices.Pass(), draw_mode);
 }
 
+scoped_refptr<render_tree::Image> SoftwareResourceProvider::DrawOffscreenImage(
+    const scoped_refptr<render_tree::Node>& root) {
+  UNREFERENCED_PARAMETER(root);
+  return scoped_refptr<render_tree::Image>(NULL);
+}
+
 }  // namespace skia
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index f74491b..4ed9bde 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -45,22 +45,34 @@
   scoped_refptr<render_tree::Image> CreateImage(
       scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
 
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 4
   scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE {
     NOTREACHED();
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-    SbDecodeTargetDestroy(decode_target);
-#else   // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
     SbDecodeTargetRelease(decode_target);
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+    return NULL;
+  }
+
+  SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+    return NULL;
+  }
+
+  bool SupportsSbDecodeTarget() OVERRIDE { return false; }
+#elif SB_API_VERSION >= 3
+  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_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif  // SB_API_VERSION >= 4
+#endif  // SB_HAS(GRAPHICS)
 
   scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
       size_t size_in_bytes, size_t alignment) OVERRIDE;
@@ -75,7 +87,7 @@
       const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
 
   scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
-      const std::string& font_face_name) OVERRIDE;
+      const char* font_face_name) OVERRIDE;
 
   scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
       int32 character, render_tree::FontStyle font_style,
@@ -106,6 +118,9 @@
       scoped_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
       render_tree::Mesh::DrawMode draw_mode) OVERRIDE;
 
+  scoped_refptr<render_tree::Image> DrawOffscreenImage(
+      const scoped_refptr<render_tree::Node>& root) OVERRIDE;
+
  private:
   TextShaper text_shaper_;
 };
diff --git a/src/cobalt/renderer/rasterizer/skia/text_shaper.cc b/src/cobalt/renderer/rasterizer/skia/text_shaper.cc
index 8a2e86e..5d8ee0f 100644
--- a/src/cobalt/renderer/rasterizer/skia/text_shaper.cc
+++ b/src/cobalt/renderer/rasterizer/skia/text_shaper.cc
@@ -35,7 +35,11 @@
 class SingleFontFontProvider : public render_tree::FontProvider {
  public:
   explicit SingleFontFontProvider(const scoped_refptr<render_tree::Font>& font)
-      : font_(font) {}
+      : size_(base::polymorphic_downcast<Font*>(font.get())->size()),
+        font_(font) {}
+
+  const render_tree::FontStyle& style() const OVERRIDE { return style_; }
+  float size() const OVERRIDE { return size_; }
 
   const scoped_refptr<render_tree::Font>& GetCharacterFont(
       int32 utf32_character, render_tree::GlyphIndex* glyph_index) OVERRIDE {
@@ -44,10 +48,12 @@
   }
 
  private:
+  render_tree::FontStyle style_;
+  float size_;
   scoped_refptr<render_tree::Font> font_;
 };
 
-void TryAddFontToUsedFonts(const scoped_refptr<render_tree::Font>& font,
+void TryAddFontToUsedFonts(Font* font,
                            render_tree::FontVector* maybe_used_fonts) {
   if (!maybe_used_fonts) {
     return;
@@ -56,7 +62,7 @@
   // Verify that the font has not already been added to the used fonts, before
   // adding it to the end.
   for (int i = 0; i < maybe_used_fonts->size(); ++i) {
-    if ((*maybe_used_fonts)[i]->GetTypefaceId() == font->GetTypefaceId()) {
+    if ((*maybe_used_fonts)[i] == font) {
       return;
     }
   }
@@ -64,6 +70,20 @@
   maybe_used_fonts->push_back(font);
 }
 
+bool ShouldFakeBoldText(const render_tree::FontProvider* font_provider,
+                        const Font* font) {
+  // A font-weight greater than 500 indicates bold if it is available. Use
+  // synthetic bolding if this is a bold weight and the selected font
+  // synthesizes bold.
+  // https://www.w3.org/TR/css-fonts-3/#font-weight-prop
+  // https://www.w3.org/TR/css-fonts-3/#font-style-matching
+  if (font_provider->style().weight > 500) {
+    SkAutoTUnref<SkTypeface_Cobalt> typeface(font->GetSkTypeface());
+    return typeface->synthesizes_bold();
+  }
+  return false;
+}
+
 }  // namespace
 
 TextShaper::TextShaper()
@@ -232,7 +252,7 @@
       //   2. If the characters use different scripts, the next script isn't
       //      inherited, and the next character doesn't support the current
       //      script.
-      if ((current_font->GetTypefaceId() != next_font->GetTypefaceId()) ||
+      if ((current_font != next_font) ||
           ((current_script != next_script) &&
            (next_script != USCRIPT_INHERITED) &&
            (!uscript_hasScript(next_character, current_script)))) {
@@ -310,6 +330,7 @@
   if (maybe_builder) {
     SkPaint paint = script_run.font->GetSkPaint();
     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    paint.setFakeBoldText(ShouldFakeBoldText(font_provider, script_run.font));
     run_buffer = &(maybe_builder->allocRunPos(paint, glyph_count));
   }
 
@@ -426,22 +447,13 @@
     // If there's a builder (meaning that a glyph buffer is being generated),
     // then we need to update the glyph buffer data for the new glyph.
     if (maybe_builder) {
-      // If at least one glyph has previously been added and the typeface has
-      // changed, then the current run has ended and we need to add the run
-      // into the glyph buffer. Allocate space within the text blob for the
-      // glyphs and copy them into the blob.
-      if (glyph_count > 0 &&
-          last_font->GetTypefaceId() != current_font->GetTypefaceId()) {
+      // If at least one glyph has previously been added and the font has
+      // changed, then the current run has ended and we need to add the font run
+      // into the glyph buffer.
+      if (glyph_count > 0 && last_font != current_font) {
         TryAddFontToUsedFonts(last_font, maybe_used_fonts);
-
-        SkPaint paint = last_font->GetSkPaint();
-        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        const SkTextBlobBuilder::RunBuffer& buffer =
-            maybe_builder->allocRunPosH(paint, glyph_count, 0);
-        std::copy(&local_glyphs_[0], &local_glyphs_[0] + glyph_count,
-                  buffer.glyphs);
-        std::copy(&local_positions_[0], &local_positions_[0] + glyph_count,
-                  buffer.pos);
+        AddFontRunToGlyphBuffer(font_provider, last_font, glyph_count,
+                                maybe_builder);
         glyph_count = 0;
       }
 
@@ -464,22 +476,31 @@
   }
 
   // If there's a builder (meaning that a glyph buffer is being generated), and
-  // the at least one glyph was generated, then allocate space within the text
-  // blob for the glyphs and copy them into the blob.
+  // at least one glyph was generated, then we need to add the final font run
+  // into the glyph buffer.
   if (maybe_builder && glyph_count > 0) {
     TryAddFontToUsedFonts(last_font, maybe_used_fonts);
-
-    SkPaint paint = last_font->GetSkPaint();
-    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    const SkTextBlobBuilder::RunBuffer& buffer =
-        maybe_builder->allocRunPosH(paint, glyph_count, 0);
-    std::copy(&local_glyphs_[0], &local_glyphs_[0] + glyph_count,
-              buffer.glyphs);
-    std::copy(&local_positions_[0], &local_positions_[0] + glyph_count,
-              buffer.pos);
+    AddFontRunToGlyphBuffer(font_provider, last_font, glyph_count,
+                            maybe_builder);
   }
 }
 
+void TextShaper::AddFontRunToGlyphBuffer(
+    const render_tree::FontProvider* font_provider, const Font* font,
+    const int glyph_count, SkTextBlobBuilder* builder) {
+  SkPaint paint = font->GetSkPaint();
+  paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+  paint.setFakeBoldText(ShouldFakeBoldText(font_provider, font));
+
+  // Allocate space within the text blob for the glyphs and copy them into the
+  // blob.
+  const SkTextBlobBuilder::RunBuffer& buffer =
+      builder->allocRunPosH(paint, glyph_count, 0);
+  std::copy(&local_glyphs_[0], &local_glyphs_[0] + glyph_count, buffer.glyphs);
+  std::copy(&local_positions_[0], &local_positions_[0] + glyph_count,
+            buffer.pos);
+}
+
 void TextShaper::EnsureLocalGlyphArraysHaveSize(size_t size) {
   if (local_glyph_array_size_ < size) {
     local_glyph_array_size_ = size;
diff --git a/src/cobalt/renderer/rasterizer/skia/text_shaper.h b/src/cobalt/renderer/rasterizer/skia/text_shaper.h
index 0f00de3..982e1d3 100644
--- a/src/cobalt/renderer/rasterizer/skia/text_shaper.h
+++ b/src/cobalt/renderer/rasterizer/skia/text_shaper.h
@@ -164,6 +164,12 @@
                       render_tree::FontVector* maybe_used_fonts,
                       float* current_width);
 
+  // Add the glyphs from a run of a single font within a simple text run to the
+  // glyph buffer.
+  void AddFontRunToGlyphBuffer(const render_tree::FontProvider* font_provider,
+                               const Font* font, const int glyph_count,
+                               SkTextBlobBuilder* builder);
+
   // Verifies that the glyph arrays have the required size allocated. If they do
   // not, then the arrays are re-allocated with the required size.
   void EnsureLocalGlyphArraysHaveSize(size_t size);
diff --git a/src/cobalt/renderer/rasterizer/skia/typeface.cc b/src/cobalt/renderer/rasterizer/skia/typeface.cc
index b1006d5..e4396fe 100644
--- a/src/cobalt/renderer/rasterizer/skia/typeface.cc
+++ b/src/cobalt/renderer/rasterizer/skia/typeface.cc
@@ -29,11 +29,12 @@
 namespace rasterizer {
 namespace skia {
 
-SkiaTypeface::SkiaTypeface(SkTypeface* typeface) : typeface_(SkRef(typeface)) {
+SkiaTypeface::SkiaTypeface(SkTypeface_Cobalt* typeface)
+    : typeface_(SkRef(typeface)) {
   character_glyph_thread_checker_.DetachFromThread();
 }
 
-SkTypeface* SkiaTypeface::GetSkTypeface() const {
+SkTypeface_Cobalt* SkiaTypeface::GetSkTypeface() const {
   return SkRef(typeface_.get());
 }
 
diff --git a/src/cobalt/renderer/rasterizer/skia/typeface.h b/src/cobalt/renderer/rasterizer/skia/typeface.h
index ebfb5ac..54ae436 100644
--- a/src/cobalt/renderer/rasterizer/skia/typeface.h
+++ b/src/cobalt/renderer/rasterizer/skia/typeface.h
@@ -20,10 +20,10 @@
 #include "base/threading/thread_checker.h"
 #include "cobalt/render_tree/font.h"
 #include "cobalt/render_tree/typeface.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
 
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkTypeface.h"
 
 namespace cobalt {
 namespace renderer {
@@ -36,13 +36,13 @@
 // than the thread making the character glyph queries.
 class SkiaTypeface : public render_tree::Typeface {
  public:
-  explicit SkiaTypeface(SkTypeface* typeface);
+  explicit SkiaTypeface(SkTypeface_Cobalt* typeface);
 
-  // Returns the contained SkTypeface object, which has its reference count
-  // incremented.
+  // Returns the contained SkTypeface_Cobalt object, which has its reference
+  // count incremented.
   // NOTE: The caller is responsible for decrementing the reference count after
   // finishing with the object.
-  SkTypeface* GetSkTypeface() const;
+  SkTypeface_Cobalt* GetSkTypeface() const;
 
   // From render_tree::Typeface
 
@@ -69,7 +69,7 @@
   typedef base::hash_map<int32, render_tree::GlyphIndex> CharacterToGlyphMap;
 
   // The underlying SkTypeface that was used to create this typeface.
-  SkAutoTUnref<SkTypeface> typeface_;
+  SkAutoTUnref<SkTypeface_Cobalt> typeface_;
 
   // The glyphs for characters are lazily computed and cached to speed up later
   // lookups. The page containing indices 0-255 is optimized within an array.
diff --git a/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenImage-expected.png b/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenImage-expected.png
new file mode 100644
index 0000000..378c5c7
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenImage-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc
index 601e6a2..eb92656 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.cc
+++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -64,7 +64,8 @@
   // Create our offscreen surface that will be the target of our test
   // rasterizations.
   test_surface_ =
-      graphics_context_->CreateOffscreenRenderTarget(test_surface_dimensions);
+      graphics_context_->CreateDownloadableOffscreenRenderTarget(
+          test_surface_dimensions);
 
   // Create the rasterizer using the platform default RenderModule options.
   RendererModule::Options render_module_options;
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index 9c50da9..778b895 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -22,8 +22,6 @@
       'target_name': 'renderer',
       'type': 'static_library',
       'sources': [
-        'frame_rate_throttler.cc',
-        'frame_rate_throttler.h',
         'pipeline.cc',
         'pipeline.h',
         'renderer_module.cc',
@@ -49,6 +47,7 @@
         '<(DEPTH)/cobalt/renderer/backend/backend.gyp:renderer_backend',
         '<(DEPTH)/cobalt/renderer/rasterizer/rasterizer.gyp:rasterizer',
         '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
+        '<(DEPTH)/nb/nb.gyp:nb',
       ],
       'conditions': [
         ['OS=="starboard"', {
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index 241707b..04d18b9 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -15,6 +15,7 @@
 #include "cobalt/renderer/renderer_module.h"
 
 #include "base/debug/trace_event.h"
+#include "cobalt/browser/memory_settings/calculations.h"
 #include "cobalt/renderer/backend/default_graphics_system.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
 #include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
@@ -22,7 +23,8 @@
 namespace cobalt {
 namespace renderer {
 
-RendererModule::Options::Options() {
+RendererModule::Options::Options()
+    : skia_texture_atlas_dimensions(2048, 2048) {
   // Call into platform-specific code for setting up render module options.
   SetPerPlatformDefaultOptions();
 }
@@ -31,7 +33,6 @@
                                const Options& options)
     : system_window_(system_window), options_(options) {
   TRACE_EVENT0("cobalt::renderer", "RendererModule::RendererModule()");
-
   Resume();
 }
 
diff --git a/src/cobalt/renderer/renderer_module.h b/src/cobalt/renderer/renderer_module.h
index 65eeafd..8446117 100644
--- a/src/cobalt/renderer/renderer_module.h
+++ b/src/cobalt/renderer/renderer_module.h
@@ -46,6 +46,10 @@
     // hardware-accelerated Skia rasterizer.
     int scratch_surface_cache_size_in_bytes;
 
+    // Represents the dimensions of the skia texture atlas which holds all of
+    // the glyph textures used during rendering.
+    math::Size skia_texture_atlas_dimensions;
+
     // Determines the capacity of the skia cache.  The Skia cache is maintained
     // within Skia and is used to cache the results of complicated effects such
     // as shadows, so that Skia draw calls that are used repeatedly across
diff --git a/src/cobalt/renderer/renderer_module_default_options_starboard.cc b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
index 2e09f6f..443c212 100644
--- a/src/cobalt/renderer/renderer_module_default_options_starboard.cc
+++ b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
@@ -39,16 +39,20 @@
   return scoped_ptr<rasterizer::Rasterizer>(
       new rasterizer::egl::SoftwareRasterizer(
           graphics_context, options.surface_cache_size_in_bytes));
-#elif defined(COBALT_FORCE_CUSTOM_RASTERIZER)
+#elif defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
   return scoped_ptr<rasterizer::Rasterizer>(
       new rasterizer::egl::HardwareRasterizer(
-          graphics_context, options.skia_cache_size_in_bytes,
+          graphics_context, options.skia_texture_atlas_dimensions.width(),
+          options.skia_texture_atlas_dimensions.height(),
+          options.skia_cache_size_in_bytes,
           options.scratch_surface_cache_size_in_bytes,
           options.surface_cache_size_in_bytes));
 #else
   return scoped_ptr<rasterizer::Rasterizer>(
       new rasterizer::skia::HardwareRasterizer(
-          graphics_context, options.skia_cache_size_in_bytes,
+          graphics_context, options.skia_texture_atlas_dimensions.width(),
+          options.skia_texture_atlas_dimensions.height(),
+          options.skia_cache_size_in_bytes,
           options.scratch_surface_cache_size_in_bytes,
           options.surface_cache_size_in_bytes));
 #endif  // COBALT_FORCE_SOFTWARE_RASTERIZER
@@ -60,7 +64,9 @@
 #else
   return scoped_ptr<rasterizer::Rasterizer>(
       new rasterizer::blitter::HardwareRasterizer(
-          graphics_context, options.scratch_surface_cache_size_in_bytes,
+          graphics_context, options.skia_texture_atlas_dimensions.width(),
+          options.skia_texture_atlas_dimensions.height(),
+          options.scratch_surface_cache_size_in_bytes,
           options.surface_cache_size_in_bytes,
           options.software_surface_cache_size_in_bytes));
 #endif  // COBALT_FORCE_SOFTWARE_RASTERIZER
@@ -75,11 +81,12 @@
 void RendererModule::Options::SetPerPlatformDefaultOptions() {
   // Set default options from the current build's Starboard configuration.
   surface_cache_size_in_bytes = COBALT_SURFACE_CACHE_SIZE_IN_BYTES;
-  skia_cache_size_in_bytes = COBALT_SKIA_CACHE_SIZE_IN_BYTES;
+  // Default to 4MB, but this may be modified externally.
+  skia_cache_size_in_bytes = 4 * 1024 * 1024;
   scratch_surface_cache_size_in_bytes =
       COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES;
-  software_surface_cache_size_in_bytes =
-      COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES;
+  // 8MB default for software_surface_cache.
+  software_surface_cache_size_in_bytes = 8 * 1024 * 1024;
 
   // If there is no need to frequently flip the display buffer, then enable
   // support for an optimization where the scene is not re-rasterized each frame
diff --git a/src/cobalt/renderer/renderer_parameters_setup.gypi b/src/cobalt/renderer/renderer_parameters_setup.gypi
index 99d0e28..ce438b5 100644
--- a/src/cobalt/renderer/renderer_parameters_setup.gypi
+++ b/src/cobalt/renderer/renderer_parameters_setup.gypi
@@ -14,10 +14,8 @@
 
 {
   'defines': [
-    'COBALT_SKIA_CACHE_SIZE_IN_BYTES=<(skia_cache_size_in_bytes)',
     'COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES=<(scratch_surface_cache_size_in_bytes)',
     'COBALT_SURFACE_CACHE_SIZE_IN_BYTES=<(surface_cache_size_in_bytes)',
-    'COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES=<(software_surface_cache_size_in_bytes)',
   ],
   'conditions': [
     ['rasterizer_type == "software"', {
@@ -30,9 +28,10 @@
         'COBALT_FORCE_STUB_RASTERIZER',
       ],
     }],
-    ['rasterizer_type == "custom"', {
+    ['rasterizer_type == "direct-gles"', {
       'defines': [
-        'COBALT_FORCE_CUSTOM_RASTERIZER',
+        'COBALT_FORCE_DIRECT_GLES_RASTERIZER',
+        'COBALT_RASTERIZER_USES_DEPTH_BUFFER',
       ],
     }],
   ],
diff --git a/src/cobalt/script/exception_message.cc b/src/cobalt/script/exception_message.cc
index 25b9b87..6a619c4 100644
--- a/src/cobalt/script/exception_message.cc
+++ b/src/cobalt/script/exception_message.cc
@@ -17,6 +17,16 @@
 namespace cobalt {
 namespace script {
 
+// Exception message contains an exception information. It includes a
+// |message_type| which is the index of each exception message, a
+// |exception_type| which is based on the spec. of simple exception, and a
+//  message |format|.
+struct ExceptionMessage {
+  MessageType message_type;
+  SimpleExceptionType exception_type;
+  const char* message;
+};
+
 ExceptionMessage kMessages[kNumMessageTypes] = {
     {kSimpleError, kError, " "},
     {kSimpleTypeError, kTypeError, " "},
@@ -44,17 +54,12 @@
     {kOutsideBounds, kRangeError, "Offset is outside the object's bounds."},
     {kInvalidLength, kRangeError, "Invalid length."},
     {kNotAnArrayBuffer, kTypeError, "Value is not an ArrayBuffer."},
-    {kWrongByteOffsetMultiple, kRangeError,
-     "Byte offset should be a multiple of %d."},
-    {kWrongByteLengthMultiple, kRangeError,
-     "Byte length should be a multiple of %d."},
-    {kPropertySyntaxError, kSyntaxError, "%s."},
 };
 
-const char* GetExceptionMessageFormat(MessageType message_type) {
+const char* GetExceptionMessage(MessageType message_type) {
   DCHECK_GT(message_type, kNoError);
   DCHECK_LT(message_type, kNumMessageTypes);
-  return kMessages[message_type].format;
+  return kMessages[message_type].message;
 }
 
 SimpleExceptionType GetSimpleExceptionType(MessageType message_type) {
diff --git a/src/cobalt/script/exception_message.h b/src/cobalt/script/exception_message.h
index cd9ce9d..8bdafe1 100644
--- a/src/cobalt/script/exception_message.h
+++ b/src/cobalt/script/exception_message.h
@@ -60,24 +60,11 @@
   kOutsideBounds,
   kInvalidLength,
   kNotAnArrayBuffer,
-  kWrongByteOffsetMultiple,
-  kWrongByteLengthMultiple,
-  kPropertySyntaxError,
 
   kNumMessageTypes,
 };
 
-// Exception message contains an exception information. It includes a
-// |message_type| which is the index of each exception message, a
-// |exception_type| which is based on the spec. of simple exception, and a
-//  message |format|.
-struct ExceptionMessage {
-  MessageType message_type;
-  SimpleExceptionType exception_type;
-  const char* format;
-};
-
-const char* GetExceptionMessageFormat(MessageType message_type);
+const char* GetExceptionMessage(MessageType message_type);
 SimpleExceptionType GetSimpleExceptionType(MessageType message_type);
 
 }  // namespace script
diff --git a/src/cobalt/script/exception_state.h b/src/cobalt/script/exception_state.h
index 93eedb8..d478086 100644
--- a/src/cobalt/script/exception_state.h
+++ b/src/cobalt/script/exception_state.h
@@ -14,6 +14,8 @@
 #ifndef COBALT_SCRIPT_EXCEPTION_STATE_H_
 #define COBALT_SCRIPT_EXCEPTION_STATE_H_
 
+#include <stdarg.h>
+
 #include <string>
 
 #include "cobalt/script/exception_message.h"
@@ -28,16 +30,24 @@
   virtual void SetException(
       const scoped_refptr<ScriptException>& exception) = 0;
 
-  // The 'dummy' parameter avoids MessageType being the last (only) parameter
-  // before varadic arguments to avoid promoting the enum to an int, which is an
-  // error in newer clang because passing an object that undergoes default
-  // argument promotion to 'va_start' has undefined behavior.
-  virtual void SetSimpleExceptionWithArgs(MessageType message_type,
-                                          int dummy, ...) = 0;
-
+  // Use MessageType for commonly used exceptions.
   void SetSimpleException(MessageType message_type) {
-    SetSimpleExceptionWithArgs(message_type, 0);
+    const char* message = GetExceptionMessage(message_type);
+    SimpleExceptionType type = GetSimpleExceptionType(message_type);
+    SetSimpleException(type, message);
   }
+
+  // Set a simple exception with the specified error message.
+  void SetSimpleException(SimpleExceptionType type, const char* format, ...) {
+    va_list arguments;
+    va_start(arguments, format);
+    SetSimpleExceptionVA(type, format, arguments);
+    va_end(arguments);
+  }
+
+ protected:
+  virtual void SetSimpleExceptionVA(SimpleExceptionType, const char* format,
+                                    va_list args) = 0;
 };
 
 }  // namespace script
diff --git a/src/cobalt/script/fake_script_runner.h b/src/cobalt/script/fake_script_runner.h
index 9e3b764..1a6bf9e 100644
--- a/src/cobalt/script/fake_script_runner.h
+++ b/src/cobalt/script/fake_script_runner.h
@@ -29,7 +29,11 @@
   FakeScriptRunner() : fake_global_environment_(new FakeGlobalEnvironment()) {}
   std::string Execute(
       const std::string& /*script_utf8*/,
-      const base::SourceLocation& /*script_location*/) OVERRIDE {
+      const base::SourceLocation& /*script_location*/,
+      bool* out_succeeded) OVERRIDE {
+    if (out_succeeded) {
+      *out_succeeded = true;
+    }
     return "";
   }
   GlobalEnvironment* GetGlobalEnvironment() const OVERRIDE {
diff --git a/src/cobalt/script/javascript_engine.h b/src/cobalt/script/javascript_engine.h
index 9d4e49e..39f9c0d 100644
--- a/src/cobalt/script/javascript_engine.h
+++ b/src/cobalt/script/javascript_engine.h
@@ -29,10 +29,16 @@
 class JavaScriptEngine {
  public:
   struct Options {
-    Options() : disable_jit(false) {}
+    Options() : disable_jit(false), gc_threshold_bytes(1024*1024) {}
+
     // Default false. When set to true then the javascript engine should
     // disable the just-in-time compiler.
     bool disable_jit;
+
+    // Determines the size of garbage collection threshold. After this many
+    // bytes have been allocated, the garbage collector will run.
+    // Default is 1MB (see default constructor).
+    size_t gc_threshold_bytes;
   };
 
   typedef base::Callback<void(const base::SourceLocation& location,
@@ -41,11 +47,11 @@
   // Initialize a JavaScript engine. The JavaScript engine should only be
   // accessed from the thread that called CreateEngine.
   // This function is defined per-implementation.
-  static scoped_ptr<JavaScriptEngine> CreateEngine();
+  static scoped_ptr<JavaScriptEngine> CreateEngine(
+      const Options& options = Options());
 
   // Create a new JavaScript global object proxy.
-  virtual scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment(
-      const Options& options) = 0;
+  virtual scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() = 0;
 
   // Kick off the engine's garbage collection synchronously.
   virtual void CollectGarbage() = 0;
diff --git a/src/cobalt/script/logging_exception_state.h b/src/cobalt/script/logging_exception_state.h
index c9ac0dc..0cf0a70 100644
--- a/src/cobalt/script/logging_exception_state.h
+++ b/src/cobalt/script/logging_exception_state.h
@@ -29,16 +29,10 @@
   void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE {
     LogException(exception->name(), exception->message());
   }
-
-  void SetSimpleExceptionWithArgs(MessageType message_type,
-                                  int dummy, ...) OVERRIDE {
-    va_list arguments;
-    va_start(arguments, dummy);
-    LogException(
-        SimpleExceptionToString(GetSimpleExceptionType(message_type)),
-        base::StringPrintV(GetExceptionMessageFormat(message_type),
-                           arguments));
-    va_end(arguments);
+  void SetSimpleExceptionVA(SimpleExceptionType type, const char* format,
+                            va_list arguments) OVERRIDE {
+    LogException(SimpleExceptionToString(type),
+                 base::StringPrintV(format, arguments));
   }
 
   bool is_exception_set() const { return is_exception_set_; }
diff --git a/src/cobalt/script/mozjs-45/conversion_helpers.cc b/src/cobalt/script/mozjs-45/conversion_helpers.cc
index 3331508..1870b1a 100644
--- a/src/cobalt/script/mozjs-45/conversion_helpers.cc
+++ b/src/cobalt/script/mozjs-45/conversion_helpers.cc
@@ -108,6 +108,35 @@
                                         global_environment->wrapper_factory());
 }
 
+// ValueHandle -> JSValue
+void ToJSValue(JSContext* context, const ValueHandleHolder* value_handle_holder,
+               JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  JS::RootedValue js_value(context);
+  if (value_handle_holder) {
+    // Downcast to MozjsValueHandleHolder so we can get the JS object.
+    const MozjsValueHandleHolder* mozjs_value_handle_holder =
+        base::polymorphic_downcast<const MozjsValueHandleHolder*>(
+            value_handle_holder);
+    js_value = mozjs_value_handle_holder->js_value();
+  }
+
+  out_value.set(js_value);
+}
+
+// JSValue -> ValueHandle
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 MozjsValueHandleHolder* out_holder) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  DCHECK_EQ(conversion_flags & ~kConversionFlagsObject, 0)
+      << "Unexpected conversion flags found.";
+  MozjsGlobalEnvironment* global_environment =
+      static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+  *out_holder = MozjsValueHandleHolder(value, context,
+                                       global_environment->wrapper_factory());
+}
+
 }  // namespace mozjs
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/conversion_helpers.h b/src/cobalt/script/mozjs-45/conversion_helpers.h
index d0f8df2..95a752e 100644
--- a/src/cobalt/script/mozjs-45/conversion_helpers.h
+++ b/src/cobalt/script/mozjs-45/conversion_helpers.h
@@ -29,10 +29,12 @@
 #include "cobalt/script/mozjs-45/mozjs_global_environment.h"
 #include "cobalt/script/mozjs-45/mozjs_object_handle.h"
 #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
 #include "cobalt/script/mozjs-45/type_traits.h"
 #include "cobalt/script/mozjs-45/union_type_conversion_forward.h"
 #include "cobalt/script/mozjs-45/util/algorithm_helpers.h"
 #include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
 #include "nb/memory_scope.h"
 #include "third_party/mozjs-45/js/public/CharacterEncoding.h"
 #include "third_party/mozjs-45/js/public/Conversions.h"
@@ -53,9 +55,10 @@
   kConversionFlagNullable = 1 << 1,
   kConversionFlagTreatNullAsEmptyString = 1 << 2,
   kConversionFlagTreatUndefinedAsEmptyString = 1 << 3,
+  kConversionFlagClamped = 1 << 4,
 
   // Valid conversion flags for numeric values.
-  kConversionFlagsNumeric = kConversionFlagRestricted,
+  kConversionFlagsNumeric = kConversionFlagRestricted | kConversionFlagClamped,
 
   // Valid conversion flags for string types.
   kConversionFlagsString = kConversionFlagTreatNullAsEmptyString |
@@ -135,6 +138,52 @@
   out_value.setInt32(in_number);
 }
 
+template <typename T>
+inline const double UpperBound() {
+  return std::numeric_limits<T>::max();
+}
+
+template <typename T>
+inline const double LowerBound() {
+  return std::numeric_limits<T>::min();
+}
+
+// The below specializations of UpperBound<T> and LowerBound<T> for 64
+// bit integers use the (2^(53) - 1) and similar bounds specified in
+// step 1 of ConvertToInt, see:
+// https://heycam.github.io/webidl/#abstract-opdef-converttoint
+template <>
+inline const double UpperBound<int64_t>() {
+  const double kInt64UpperBound = static_cast<double>((1ull << 53) - 1);
+  return kInt64UpperBound;
+}
+
+template <>
+inline const double LowerBound<int64_t>() {
+  const double kInt64LowerBound = static_cast<double>(-(1ull << 53) + 1);
+  return kInt64LowerBound;
+}
+
+template <>
+inline const double UpperBound<uint64_t>() {
+  const double kUInt64UpperBound = static_cast<double>((1ull << 53) - 1);
+  return kUInt64UpperBound;
+}
+
+template <typename T>
+void ClampedValue(JSContext* context, JS::HandleValue value,
+                  JS::MutableHandleValue clamped_value) {
+  double value_double;
+  JS::ToNumber(context, value, &value_double);
+  if (value_double > UpperBound<T>()) {
+    clamped_value.setDouble(UpperBound<T>());
+  } else if (value_double < LowerBound<T>()) {
+    clamped_value.setDouble(LowerBound<T>());
+  } else {
+    clamped_value.set(value);
+  }
+}
+
 // JSValue -> signed integers <= 4 bytes
 template <typename T>
 inline void FromJSValue(
@@ -146,14 +195,21 @@
                                  (sizeof(T) <= 4),
                              T>::type* = NULL) {
   TRACK_MEMORY_SCOPE("Javascript");
-  DCHECK_EQ(conversion_flags, kNoConversionFlags)
-      << "No conversion flags supported.";
   DCHECK(out_number);
 
   int32_t out;
   // Convert a JavaScript value to an integer type as specified by the
   // ECMAScript standard.
-  bool success = JS::ToInt32(context, value, &out);
+  // TODO: Consider only creating |value_to_convert| if the conversion flag is
+  // set.
+  JS::RootedValue value_to_convert(context);
+  if (conversion_flags & kConversionFlagClamped) {
+    ClampedValue<T>(context, value, &value_to_convert);
+  } else {
+    value_to_convert.set(value);
+  }
+
+  bool success = JS::ToInt32(context, value_to_convert, &out);
   DCHECK(success);
 
   *out_number = static_cast<T>(out);
@@ -180,7 +236,15 @@
   DCHECK(out_number);
   int64_t out;
   // This produces an IDL long long.
-  bool success = JS::ToInt64(context, value, &out);
+  // TODO: Consider only creating |value_to_convert| if the conversion flag is
+  // set.
+  JS::RootedValue value_to_convert(context);
+  if (conversion_flags & kConversionFlagClamped) {
+    ClampedValue<T>(context, value, &value_to_convert);
+  } else {
+    value_to_convert.set(value);
+  }
+  bool success = JS::ToInt64(context, value_to_convert, &out);
   DCHECK(success);
   if (!success) {
     exception_state->SetSimpleException(kNotInt64Type);
@@ -228,16 +292,25 @@
                                  (sizeof(T) <= 4),
                              T>::type* = NULL) {
   TRACK_MEMORY_SCOPE("Javascript");
-  DCHECK_EQ(conversion_flags, kNoConversionFlags)
-      << "No conversion flags supported.";
   DCHECK(out_number);
 
   uint32_t out;
   // Convert a JavaScript value to an integer type as specified by the
   // ECMAScript standard.
-  bool success = JS::ToUint32(context, value, &out);
+  // TODO: Consider only creating |value_to_convert| if the conversion flag is
+  // set.
+  JS::RootedValue value_to_convert(context);
+  if (conversion_flags & kConversionFlagClamped) {
+    ClampedValue<T>(context, value, &value_to_convert);
+  } else {
+    value_to_convert.set(value);
+  }
+  bool success = JS::ToUint32(context, value_to_convert, &out);
   DCHECK(success);
-
+  if (!success) {
+    exception_state->SetSimpleException(kNotUint64Type);
+    return;
+  }
   *out_number = static_cast<T>(out);
 }
 
@@ -252,13 +325,19 @@
                                  (sizeof(T) > 4),
                              T>::type* = NULL) {
   TRACK_MEMORY_SCOPE("Javascript");
-  DCHECK_EQ(conversion_flags, kNoConversionFlags)
-      << "No conversion flags supported.";
   DCHECK(out_number);
 
   uint64_t out;
   // This produces and IDL unsigned long long.
-  bool success = JS::ToUint64(context, value, &out);
+  // TODO: Consider only creating |value_to_convert| if the conversion flag is
+  // set.
+  JS::RootedValue value_to_convert(context);
+  if (conversion_flags & kConversionFlagClamped) {
+    ClampedValue<T>(context, value, &value_to_convert);
+  } else {
+    value_to_convert.set(value);
+  }
+  bool success = JS::ToUint64(context, value_to_convert, &out);
   DCHECK(success);
   if (!success) {
     exception_state->SetSimpleException(kNotUint64Type);
@@ -377,6 +456,15 @@
                  int conversion_flags, ExceptionState* exception_state,
                  MozjsObjectHandleHolder* out_holder);
 
+// ValueHandle -> JSValue
+void ToJSValue(JSContext* context, const ValueHandleHolder* value_handle_holder,
+               JS::MutableHandleValue out_value);
+
+// JSValue -> ValueHandle
+void FromJSValue(JSContext* context, JS::HandleValue value,
+                 int conversion_flags, ExceptionState* exception_state,
+                 MozjsValueHandleHolder* out_holder);
+
 // object -> JSValue
 template <typename T>
 inline void ToJSValue(JSContext* context, const scoped_refptr<T>& in_object,
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index 52f085b..d0f416c 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -25,8 +25,10 @@
         'mozjs_exception_state.cc',
         'mozjs_global_environment.cc',
         'mozjs_property_enumerator.cc',
+        'mozjs_script_value_factory.cc',
         'mozjs_source_code.cc',
         'opaque_root_tracker.cc',
+        'promise_wrapper.cc',
         'proxy_handler.cc',
         'referenced_object_map.cc',
         'util/algorithm_helpers.cc',
@@ -41,6 +43,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/script/script.gyp:script',
         '<(DEPTH)/third_party/mozjs-45/mozjs-45.gyp:mozjs-45_lib',
+        'embed_mozjs_resources_as_header_files',
       ],
       'defines': [ 'ENGINE_SUPPORTS_INT64', ],
       'all_dependent_settings': {
@@ -74,5 +77,42 @@
         '<(DEPTH)/third_party/mozjs-45/mozjs-45.gyp:mozjs-45_lib',
       ],
     },
+
+    {
+      # This target takes specified files and embeds them as header files for
+      # inclusion into the binary. The script currently requires all resources
+      # to be embedded to live in the same directory.
+      'target_name': 'embed_mozjs_resources_as_header_files',
+      'type': 'none',
+      # Because we generate a header, we must set the hard_dependency flag.
+      'hard_dependency': 1,
+      'variables': {
+        'script_path': '<(DEPTH)/cobalt/build/generate_data_header.py',
+        'output_path': '<(SHARED_INTERMEDIATE_DIR)/cobalt/script/mozjs-45/embedded_resources.h',
+      },
+      'sources': [
+        '<(DEPTH)/third_party/promise-polyfill/promise.min.js',
+      ],
+      'actions': [
+        {
+          'action_name': 'embed_mozjs_resources_as_header_files',
+          'inputs': [
+            '<(script_path)',
+            '<@(_sources)',
+          ],
+          'outputs': [
+            '<(output_path)',
+          ],
+          'action': ['python', '<(script_path)', 'MozjsEmbeddedResources', '<(output_path)', '<@(_sources)' ],
+          'message': 'Embedding mozjs resources in into header file, "<(output_path)".',
+        },
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(SHARED_INTERMEDIATE_DIR)',
+        ],
+      },
+    },
+
   ],
 }
diff --git a/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi b/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi
index 2da6860..e40bbc5 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi
+++ b/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi
@@ -28,7 +28,9 @@
         'engine_template_files': [
           '<(DEPTH)/cobalt/bindings/mozjs45/templates/callback-interface.cc.template',
           '<(DEPTH)/cobalt/bindings/mozjs45/templates/callback-interface.h.template',
-          '<(DEPTH)/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template',
+          '<(DEPTH)/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template',
+          '<(DEPTH)/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template',
+          '<(DEPTH)/cobalt/bindings/mozjs45/templates/generated-types.h.template',
           '<(DEPTH)/cobalt/bindings/mozjs45/templates/interface.cc.template',
           '<(DEPTH)/cobalt/bindings/mozjs45/templates/interface.h.template',
           '<(DEPTH)/cobalt/bindings/mozjs45/templates/macros.cc.template',
@@ -36,8 +38,12 @@
         'engine_bindings_scripts': [
           '<(DEPTH)/cobalt/bindings/mozjs45/code_generator_mozjs45.py',
           '<(DEPTH)/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py',
+          '<(DEPTH)/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py',
         ],
-        'engine_idl_compiler': '<(DEPTH)/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py',
+        'engine_idl_compiler':
+            '<(DEPTH)/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py',
+        'engine_conversion_header_generator_script':
+            '<(DEPTH)/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py',
       }],
     ],
   },
diff --git a/src/cobalt/script/mozjs-45/mozjs.cc b/src/cobalt/script/mozjs-45/mozjs.cc
index eefa9fe..53edb1f 100644
--- a/src/cobalt/script/mozjs-45/mozjs.cc
+++ b/src/cobalt/script/mozjs-45/mozjs.cc
@@ -60,7 +60,8 @@
 }
 
 int MozjsMain(int argc, char** argv) {
-  cobalt::script::StandaloneJavascriptRunner standalone_runner;
+  JavaScriptEngine::Options js_options;
+  cobalt::script::StandaloneJavascriptRunner standalone_runner(js_options);
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(
           standalone_runner.global_environment().get());
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc
index dca2c22..e1d482d 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -141,10 +141,11 @@
 
 }  // namespace
 
-MozjsEngine::MozjsEngine() : accumulated_extra_memory_cost_(0) {
+MozjsEngine::MozjsEngine(const Options& options)
+    : accumulated_extra_memory_cost_(0), options_(options) {
   TRACE_EVENT0("cobalt::script", "MozjsEngine::MozjsEngine()");
   SbOnce(&g_js_init_once_control, CallInitAndRegisterShutDownOnce);
-  runtime_ = JS_NewRuntime(kGarbageCollectionThresholdBytes);
+  runtime_ = JS_NewRuntime(options_.js_options.gc_threshold_bytes);
   CHECK(runtime_);
 
   // Sets the size of the native stack that should not be exceeded.
@@ -209,7 +210,7 @@
 scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment() {
   TRACE_EVENT0("cobalt::script", "MozjsEngine::CreateGlobalEnvironment()");
   DCHECK(thread_checker_.CalledOnValidThread());
-  return new MozjsGlobalEnvironment(runtime_);
+  return new MozjsGlobalEnvironment(runtime_, options_.js_options);
 }
 
 void MozjsEngine::CollectGarbage() {
@@ -221,7 +222,10 @@
 void MozjsEngine::ReportExtraMemoryCost(size_t bytes) {
   DCHECK(thread_checker_.CalledOnValidThread());
   accumulated_extra_memory_cost_ += bytes;
-  if (accumulated_extra_memory_cost_ > kGarbageCollectionThresholdBytes) {
+
+  const bool do_collect_garbage =
+      accumulated_extra_memory_cost_ > options_.js_options.gc_threshold_bytes;
+  if (do_collect_garbage) {
     accumulated_extra_memory_cost_ = 0;
     CollectGarbage();
   }
@@ -233,9 +237,6 @@
 
 bool MozjsEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) {
   error_handler_ = handler;
-  JSDebugErrorHook hook = ErrorHookCallback;
-  void* closure = this;
-  JS_SetDebugErrorHook(runtime_, hook, closure);
   return true;
 }
 
@@ -294,14 +295,8 @@
   }
 }
 
-JSBool MozjsEngine::ErrorHookCallback(JSContext* context, const char* message,
-                                      JSErrorReport* report, void* closure) {
-  MozjsEngine* this_ptr = static_cast<MozjsEngine*>(closure);
-  return this_ptr->ReportJSError(context, message, report);
-}
-
-JSBool MozjsEngine::ReportJSError(JSContext* context, const char* message,
-                                  JSErrorReport* report) {
+bool MozjsEngine::ReportJSError(JSContext* context, const char* message,
+                                JSErrorReport* report) {
   const bool is_invalid =
       error_handler_.is_null() || !report || !report->filename;
   if (is_invalid) {
@@ -329,9 +324,11 @@
 
 }  // namespace mozjs
 
-scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine() {
+scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine(
+    const JavaScriptEngine::Options& options) {
   TRACE_EVENT0("cobalt::script", "JavaScriptEngine::CreateEngine()");
-  return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine());
+  mozjs::MozjsEngine::Options moz_options(options);
+  return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(moz_options));
 }
 
 }  // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.h b/src/cobalt/script/mozjs-45/mozjs_engine.h
index a8ee7e4..e4a3765 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.h
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.h
@@ -27,7 +27,13 @@
 
 class MozjsEngine : public JavaScriptEngine {
  public:
-  MozjsEngine();
+  struct Options {
+    explicit Options(const JavaScriptEngine::Options& js_options)
+        : js_options(js_options) {}
+    JavaScriptEngine::Options js_options;  // Generic settings.
+  };
+
+  explicit MozjsEngine(const Options& options);
   ~MozjsEngine() OVERRIDE;
 
   scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
@@ -43,10 +49,8 @@
   static void GCCallback(JSRuntime* runtime, JSGCStatus status, void* data);
   static void FinalizeCallback(JSFreeOp* free_op, JSFinalizeStatus status,
                                bool is_compartment, void* data);
-  static JSBool ErrorHookCallback(JSContext* context, const char* message,
-                                  JSErrorReport* report, void* closure);
-  JSBool ReportJSError(JSContext* context, const char* message,
-                       JSErrorReport* report);
+  bool ReportJSError(JSContext* context, const char* message,
+                     JSErrorReport* report);
 
   base::ThreadChecker thread_checker_;
 
@@ -66,6 +70,8 @@
 
   // Used to handle javascript errors.
   ErrorHandler error_handler_;
+
+  Options options_;
 };
 }  // namespace mozjs
 }  // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_exception_state.cc b/src/cobalt/script/mozjs-45/mozjs_exception_state.cc
index e35a8cb..a722e5d 100644
--- a/src/cobalt/script/mozjs-45/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_exception_state.cc
@@ -20,6 +20,7 @@
 #include "base/string_number_conversions.h"
 #include "base/string_util.h"
 #include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "third_party/mozjs-45/js/src/jsexn.h"
 
 namespace cobalt {
 namespace script {
@@ -72,32 +73,40 @@
   is_exception_set_ = true;
 }
 
-void MozjsExceptionState::SetSimpleException(MessageType message_type,
-                                             int dummy, ...) {
+void MozjsExceptionState::SetSimpleExceptionVA(SimpleExceptionType type,
+                                               const char* format,
+                                               va_list arguments) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!is_exception_set_);
 
-  va_list arguments;
-  va_start(arguments, dummy);
-  std::string error_message =
-      base::StringPrintV(GetExceptionMessageFormat(message_type), arguments);
+  std::string error_message = base::StringPrintV(format, arguments);
   JSErrorFormatString format_string;
   format_string.format = error_message.c_str();
   // Already fed arguments for format.
   format_string.argCount = 0;
-  format_string.exnType =
-      ConvertToMozjsExceptionType(GetSimpleExceptionType(message_type));
+  format_string.exnType = ConvertToMozjsExceptionType(type);
 
   // This function creates a JSErrorReport, populate it with an error message
   // obtained from the given JSErrorCallback. The resulting error message is
   // passed to the context's JSErrorReporter callback.
   JS_ReportErrorNumber(context_, GetErrorMessage,
-                       static_cast<void*>(&format_string), message_type);
-  va_end(arguments);
-
+                       static_cast<void*>(&format_string), type);
   is_exception_set_ = true;
 }
 
+// static
+JSObject* MozjsExceptionState::CreateErrorObject(JSContext* context,
+                                                 SimpleExceptionType type) {
+  JSExnType mozjs_type = ConvertToMozjsExceptionType(type);
+  JS::RootedObject error_prototype(context);
+  if (!JS_GetClassPrototype(context, js::GetExceptionProtoKey(mozjs_type),
+                            &error_prototype)) {
+    DLOG(ERROR) << "Failed to get Error prototype.";
+    return NULL;
+  }
+  return JS_NewObjectWithGivenProto(context, NULL, error_prototype);
+}
+
 }  // namespace mozjs
 }  // namespace script
 }  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_exception_state.h b/src/cobalt/script/mozjs-45/mozjs_exception_state.h
index 10120bd..02b5082 100644
--- a/src/cobalt/script/mozjs-45/mozjs_exception_state.h
+++ b/src/cobalt/script/mozjs-45/mozjs_exception_state.h
@@ -30,11 +30,14 @@
       : is_exception_set_(false), context_(context) {}
   // ExceptionState interface
   void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE;
-  void SetSimpleExceptionWithArgs(MessageType message_type,
-                                  int dummy, ...) OVERRIDE;
+  void SetSimpleExceptionVA(SimpleExceptionType type, const char* format,
+                            va_list arguments) OVERRIDE;
 
   bool is_exception_set() const { return is_exception_set_; }
 
+  static JSObject* CreateErrorObject(JSContext* context,
+                                     SimpleExceptionType type);
+
  private:
   bool is_exception_set_;
   JSContext* context_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 0448baf..624d591 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -20,7 +20,9 @@
 #include "base/stringprintf.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "cobalt/script/mozjs-45/embedded_resources.h"
 #include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_script_value_factory.h"
 #include "cobalt/script/mozjs-45/mozjs_source_code.h"
 #include "cobalt/script/mozjs-45/mozjs_wrapper_handle.h"
 #include "cobalt/script/mozjs-45/proxy_handler.h"
@@ -117,7 +119,8 @@
 static base::LazyInstance<MozjsStubHandler> proxy_handler;
 }  // namespace
 
-MozjsGlobalEnvironment::MozjsGlobalEnvironment(JSRuntime* runtime)
+MozjsGlobalEnvironment::MozjsGlobalEnvironment(
+    JSRuntime* runtime, const JavaScriptEngine::Options& options)
     : context_(NULL),
       garbage_collection_count_(0),
       cached_interface_data_deleter_(&cached_interface_data_),
@@ -135,6 +138,7 @@
                              kMaxCodeCacheBytes);
 
   wrapper_factory_.reset(new WrapperFactory(context_));
+  script_value_factory_.reset(new MozjsScriptValueFactory(this));
   referenced_objects_.reset(new ReferencedObjectMap(context_));
   opaque_root_tracker_.reset(new OpaqueRootTracker(
       context_, referenced_objects_.get(), wrapper_factory_.get()));
@@ -173,6 +177,8 @@
       context_, ProxyHandler::NewProxy(context_, proxy_handler.Pointer(),
                                        global_object, NULL));
   global_object_proxy_ = proxy;
+
+  EvaluateAutomatics();
 }
 
 bool MozjsGlobalEnvironment::EvaluateScript(
@@ -339,6 +345,25 @@
   DCHECK(success);
 }
 
+ScriptValueFactory* MozjsGlobalEnvironment::script_value_factory() {
+  DCHECK(script_value_factory_);
+  return script_value_factory_.get();
+}
+
+void MozjsGlobalEnvironment::EvaluateAutomatics() {
+  TRACK_MEMORY_SCOPE("Javascript");
+  std::string source(
+      reinterpret_cast<const char*>(MozjsEmbeddedResources::promise_min_js),
+      sizeof(MozjsEmbeddedResources::promise_min_js));
+  scoped_refptr<SourceCode> source_code =
+      new MozjsSourceCode(source, base::SourceLocation("promise.min.js", 1, 1));
+  std::string result;
+  bool success = EvaluateScript(source_code, &result);
+  if (!success) {
+    DLOG(FATAL) << result;
+  }
+}
+
 InterfaceData* MozjsGlobalEnvironment::GetInterfaceData(intptr_t key) {
   CachedInterfaceData::iterator it = cached_interface_data_.find(key);
   if (it != cached_interface_data_.end()) {
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index 9762530..e23f1bb 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -23,6 +23,7 @@
 #include "base/stl_util.h"
 #include "base/threading/thread_checker.h"
 #include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
 #include "cobalt/script/mozjs-45/interface_data.h"
 #include "cobalt/script/mozjs-45/opaque_root_tracker.h"
 #include "cobalt/script/mozjs-45/util/exception_helpers.h"
@@ -36,6 +37,7 @@
 namespace script {
 namespace mozjs {
 
+class MozjsScriptValueFactory;
 class ReferencedObjectMap;
 class WeakHandle;
 
@@ -44,7 +46,8 @@
 class MozjsGlobalEnvironment : public GlobalEnvironment,
                                public Wrappable::CachedWrapperAccessor {
  public:
-  explicit MozjsGlobalEnvironment(JSRuntime* runtime);
+  MozjsGlobalEnvironment(JSRuntime* runtime,
+                         const JavaScriptEngine::Options& options);
   ~MozjsGlobalEnvironment() OVERRIDE;
 
   void CreateGlobalObject() OVERRIDE;
@@ -76,6 +79,11 @@
   void Bind(const std::string& identifier,
             const scoped_refptr<Wrappable>& impl) OVERRIDE;
 
+  ScriptValueFactory* script_value_factory() OVERRIDE;
+
+  // Evaluates any automatically included Javascript for the environment.
+  void EvaluateAutomatics();
+
   JSContext* context() const { return context_; }
 
   JSObject* global_object_proxy() const { return global_object_proxy_; }
@@ -162,6 +170,7 @@
   STLValueDeleter<CachedInterfaceData> cached_interface_data_deleter_;
   ContextDestructor context_destructor_;
   scoped_ptr<WrapperFactory> wrapper_factory_;
+  scoped_ptr<MozjsScriptValueFactory> script_value_factory_;
   scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_;
   JS::Heap<JSObject*> global_object_proxy_;
   EnvironmentSettings* environment_settings_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc
new file mode 100644
index 0000000..181b729
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/script/mozjs-45/mozjs_script_value_factory.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+MozjsScriptValueFactory::MozjsScriptValueFactory(
+    MozjsGlobalEnvironment* global_environment)
+    : global_environment_(global_environment) {}
+}  // namespace mozjs
+
+// Implementation of template function declared in the base class.
+template <typename T>
+scoped_ptr<ScriptValue<Promise<T> > > ScriptValueFactory::CreatePromise() {
+  mozjs::MozjsScriptValueFactory* mozjs_this =
+      base::polymorphic_downcast<mozjs::MozjsScriptValueFactory*>(this);
+  return mozjs_this->CreatePromise<T>();
+}
+
+}  // namespace script
+}  // namespace cobalt
+
+// Explicit template instantiations must go after the template function
+// implementation.
+#include "cobalt/script/script_value_factory_instantiations.h"
diff --git a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h
new file mode 100644
index 0000000..c2612cf
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_45_MOZJS_SCRIPT_VALUE_FACTORY_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_SCRIPT_VALUE_FACTORY_H_
+
+#include "cobalt/script/mozjs-45/mozjs_global_environment.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
+#include "cobalt/script/mozjs-45/promise_wrapper.h"
+#include "cobalt/script/script_value_factory.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+class MozjsScriptValueFactory : public ScriptValueFactory {
+ public:
+  explicit MozjsScriptValueFactory(MozjsGlobalEnvironment* global_environment);
+  ~MozjsScriptValueFactory() OVERRIDE {}
+
+  template <typename T>
+  scoped_ptr<ScriptValue<Promise<T> > > CreatePromise() {
+    typedef ScriptValue<Promise<T> > ScriptPromiseType;
+    typedef MozjsUserObjectHolder<NativePromise<T> > MozjsPromiseHolderType;
+
+    JSContext* context = global_environment_->context();
+    JS::RootedObject global_object(context,
+                                   global_environment_->global_object());
+    JSAutoRequest auto_request(context);
+    JSAutoCompartment auto_compartment(context, global_object);
+
+    JS::RootedObject promise_wrapper(
+        context, PromiseWrapper::Create(context, global_object));
+    DCHECK(promise_wrapper);
+    scoped_ptr<ScriptPromiseType> promise(new MozjsPromiseHolderType(
+        promise_wrapper, context, global_environment_->wrapper_factory()));
+    return promise.Pass();
+  }
+
+ private:
+  MozjsGlobalEnvironment* global_environment_;
+};
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_MOZJS_45_MOZJS_SCRIPT_VALUE_FACTORY_H_
diff --git a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
index 952b4cc..3796ad3 100644
--- a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
+++ b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
@@ -86,6 +86,20 @@
     }
   }
 
+  void PreventGarbageCollection() OVERRIDE {
+    if (prevent_garbage_collection_count_++ == 0 && handle_) {
+      JSAutoRequest auto_request(context_);
+      persistent_root_ = JS::PersistentRootedValue(context_, handle_->value());
+    }
+  }
+
+  void AllowGarbageCollection() {
+    if (prevent_garbage_collection_count_++ == 0 && handle_) {
+      JSAutoRequest auto_request(context_);
+      persistent_root_ = base::nullopt;
+    }
+  }
+
   const typename MozjsUserObjectType::BaseType* GetScriptValue()
       const OVERRIDE {
     return handle_ ? &handle_.value() : NULL;
@@ -134,6 +148,8 @@
   JSContext* context_;
   base::optional<MozjsUserObjectType> handle_;
   WrapperFactory* wrapper_factory_;
+  int prevent_garbage_collection_count_;
+  base::optional<JS::Value> persistent_root_;
 };
 
 }  // namespace mozjs
diff --git a/src/cobalt/script/mozjs-45/mozjs_value_handle.h b/src/cobalt/script/mozjs-45/mozjs_value_handle.h
new file mode 100644
index 0000000..fae1ae7
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_value_handle.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SCRIPT_MOZJS_45_MOZJS_VALUE_HANDLE_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_VALUE_HANDLE_H_
+
+#include "base/optional.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "cobalt/script/value_handle.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// A wrapper around a JS::Value that can be passed into Cobalt as an script
+// value object.
+//
+// An ValueHandle is never passed into Cobalt as-is, but only when wrapped as a
+// ScriptValue<ValueHandle>.
+class MozjsValueHandle : public ValueHandle {
+ public:
+  typedef ValueHandle BaseType;
+  JSObject* handle() const { return handle_.GetObject(); }
+  const JS::Value& value() const { return handle_.GetValue(); }
+  bool WasCollected() const { return handle_.WasCollected(); }
+
+ private:
+  MozjsValueHandle(JSContext* context, JS::HandleValue object)
+      : handle_(context, object) {}
+  ~MozjsValueHandle() {}
+
+  WeakHeapObject handle_;
+
+  friend class MozjsUserObjectHolder<MozjsValueHandle>;
+  friend class base::optional<MozjsValueHandle>;
+};
+
+typedef MozjsUserObjectHolder<MozjsValueHandle> MozjsValueHandleHolder;
+
+template <>
+struct TypeTraits<ValueHandle> {
+  typedef MozjsValueHandleHolder ConversionType;
+  typedef const ScriptValue<ValueHandle>* ReturnType;
+};
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_45_MOZJS_VALUE_HANDLE_H_
diff --git a/src/cobalt/script/mozjs-45/native_promise.h b/src/cobalt/script/mozjs-45/native_promise.h
new file mode 100644
index 0000000..1dfccb5
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/native_promise.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_
+#define COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_
+
+#include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/promise_wrapper.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "cobalt/script/promise.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// Shared functionality for NativePromise<T>. Does not implement the Resolve
+// function, since that needs to be specialized for Promise<T>.
+template <typename T>
+class NativePromiseBase : public Promise<T> {
+ public:
+  // ScriptObject boilerplate.
+  typedef Promise<T> BaseType;
+  JSObject* handle() const { return promise_resolver_->get().GetObject(); }
+  const JS::Value& value() const { return promise_resolver_->get().GetValue(); }
+  bool WasCollected() const { return promise_resolver_->get().WasCollected(); }
+
+  // The Promise JS object (not the resolver).
+  JSObject* promise() const { return promise_resolver_->GetPromise(); }
+
+  void Reject() const OVERRIDE {
+    JS::RootedObject promise_resolver(context_,
+                                      promise_resolver_->get().GetObject());
+    if (promise_resolver) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_resolver);
+      promise_resolver_->Reject(JS::UndefinedHandleValue);
+    }
+  }
+
+  void Reject(SimpleExceptionType exception) const OVERRIDE {
+    JS::RootedObject promise_resolver(context_,
+                                      promise_resolver_->get().GetObject());
+    if (promise_resolver) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_resolver);
+      JS::RootedValue error_result(context_);
+      error_result.setObject(
+          *MozjsExceptionState::CreateErrorObject(context_, exception));
+      promise_resolver_->Reject(error_result);
+    }
+  }
+
+  void Reject(const scoped_refptr<ScriptException>& result) const OVERRIDE {
+    JS::RootedObject promise_resolver(context_,
+                                      promise_resolver_->get().GetObject());
+    if (promise_resolver) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_resolver);
+      JS::RootedValue converted_result(context_);
+      ToJSValue(context_, result, &converted_result);
+      promise_resolver_->Reject(converted_result);
+    }
+  }
+
+ protected:
+  NativePromiseBase(JSContext* context, JS::HandleObject resolver_object)
+      : context_(context) {
+    promise_resolver_.emplace(context, resolver_object);
+  }
+
+  NativePromiseBase(JSContext* context, JS::HandleValue resolver_value)
+      : context_(context) {
+    DCHECK(resolver_value.isObject());
+    JS::RootedObject resolver_object(context, &resolver_value.toObject());
+    promise_resolver_.emplace(context, resolver_object);
+  }
+
+  JSContext* context_;
+  base::optional<PromiseWrapper> promise_resolver_;
+};
+
+// Implements the Resolve() function for T != void.
+template <typename T>
+class NativePromise : public NativePromiseBase<T> {
+ public:
+  NativePromise(JSContext* context, JS::HandleObject resolver_object)
+      : NativePromiseBase<T>(context, resolver_object) {}
+
+  NativePromise(JSContext* context, JS::HandleValue resolver_value)
+      : NativePromiseBase<T>(context, resolver_value) {}
+
+  void Resolve(const T& value) const OVERRIDE {
+    JS::RootedObject promise_wrapper(
+        this->context_, this->promise_resolver_->get().GetObject());
+    if (promise_wrapper) {
+      JSAutoRequest auto_request(this->context_);
+      JSAutoCompartment auto_compartment(this->context_, promise_wrapper);
+      JS::RootedValue converted_value(this->context_);
+      ToJSValue(this->context_, value, &converted_value);
+      this->promise_resolver_->Resolve(converted_value);
+    }
+  }
+};
+
+// Implements the Resolve() function for T == void.
+template <>
+class NativePromise<void> : public NativePromiseBase<void> {
+ public:
+  NativePromise(JSContext* context, JS::HandleObject resolver_object)
+      : NativePromiseBase<void>(context, resolver_object) {}
+
+  NativePromise(JSContext* context, JS::HandleValue resolver_value)
+      : NativePromiseBase<void>(context, resolver_value) {}
+
+  void Resolve() const OVERRIDE {
+    JS::RootedObject promise_wrapper(context_,
+                                     promise_resolver_->get().GetObject());
+    if (promise_wrapper) {
+      JSAutoRequest auto_request(context_);
+      JSAutoCompartment auto_compartment(context_, promise_wrapper);
+      promise_resolver_->Resolve(JS::UndefinedHandleValue);
+    }
+  }
+};
+
+template <typename T>
+struct TypeTraits<NativePromise<T> > {
+  typedef MozjsUserObjectHolder<NativePromise<T> > ConversionType;
+  typedef const ScriptValue<Promise<T> >* ReturnType;
+};
+
+// Promise<T> -> JSValue
+// Note that JSValue -> Promise<T> is not yet supported.
+template <typename T>
+inline void ToJSValue(JSContext* context,
+                      const ScriptValue<Promise<T> >* promise_holder,
+                      JS::MutableHandleValue out_value) {
+  TRACK_MEMORY_SCOPE("Javascript");
+  if (!promise_holder) {
+    out_value.set(JS::NullValue());
+    return;
+  }
+  const MozjsUserObjectHolder<NativePromise<T> >* user_object_holder =
+      base::polymorphic_downcast<
+          const MozjsUserObjectHolder<NativePromise<T> >*>(promise_holder);
+
+  const NativePromise<T>* native_promise =
+      base::polymorphic_downcast<const NativePromise<T>*>(
+          user_object_holder->GetScriptValue());
+
+  DCHECK(native_promise);
+  out_value.setObjectOrNull(native_promise->promise());
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+#endif  // COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.cc b/src/cobalt/script/mozjs-45/promise_wrapper.cc
new file mode 100644
index 0000000..46af70f
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/promise_wrapper.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/script/mozjs-45/promise_wrapper.h"
+
+#include "base/logging.h"
+#include "third_party/mozjs-45/js/src/jsfun.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+namespace {
+enum ReservedSlots {
+  kResolveFunction,
+  kRejectFunction,
+  kPromiseObject,
+  kNumReservedSlots,
+};
+
+JSClass native_promise_class = {
+    "NativePromise",                                // name
+    JSCLASS_HAS_RESERVED_SLOTS(kNumReservedSlots),  // flags
+    NULL,                                           // addProperty
+    NULL,                                           // delProperty
+    NULL,                                           // getProperty
+    NULL,                                           // setProperty
+    NULL,                                           // enumerate
+    NULL,                                           // resolve
+    NULL,                                           // mayResolve
+    NULL,                                           // finalize
+    NULL,                                           // call
+};
+
+bool NativeExecutor(JSContext* context, unsigned argc, JS::Value* vp) {
+  // Get the resolve/reject functions from the call args.
+  JS::CallArgs call_args = CallArgsFromVp(argc, vp);
+  DCHECK_EQ(call_args.length(), 2);
+
+  // Get the this object. Should be the native_promise object.
+  JS::RootedValue this_value(context, call_args.computeThis(context));
+  DCHECK(this_value.isObject());
+  JS::RootedObject this_object(context, &this_value.toObject());
+  DCHECK_EQ(JS_GetClass(this_object), &native_promise_class);
+
+  // First argument is the resolve function. Second is the reject function.
+  // Stash these in the reserved slots. Reserved slots get visited so there is
+  // no need to define a special trace function.
+  JS::RootedValue resolve_function_value(context, call_args.get(0));
+  JS::RootedValue reject_function_value(context, call_args.get(1));
+  DCHECK(resolve_function_value.isObject());
+  DCHECK(JS_ObjectIsFunction(context, &resolve_function_value.toObject()));
+  DCHECK(reject_function_value.isObject());
+  DCHECK(JS_ObjectIsFunction(context, &reject_function_value.toObject()));
+
+  JS_SetReservedSlot(this_object, kResolveFunction, resolve_function_value);
+  JS_SetReservedSlot(this_object, kRejectFunction, reject_function_value);
+  return true;
+}
+
+// Creates a new NativePromise object and initializes its reserved slots.
+JSObject* CreateNativePromise(JSContext* context) {
+  JS::RootedObject native_promise(context,
+                                  JS_NewObject(context, &native_promise_class));
+  DCHECK(native_promise);
+  for (uint32_t i = 0; i < kNumReservedSlots; ++i) {
+    JS_SetReservedSlot(native_promise, i, JS::NullHandleValue);
+  }
+  return native_promise;
+}
+
+JSObject* BindCallable(JSContext* context, JSObject* target_arg,
+                       JSObject* new_this) {
+  JS::RootedObject target(context, target_arg);
+  JS::AutoValueArray<1> args(context);
+  args[0].setObjectOrNull(new_this);
+  JS::RootedValue result(context);
+  bool call_bind_result =
+      JS_CallFunctionName(context, target, "bind", args, &result);
+  DCHECK(call_bind_result);
+  return &result.toObject();
+}
+
+// Create a new native function with the |native_promise| bound as |this|.
+JSObject* CreateExecutorArgument(JSContext* context,
+                                 JS::HandleObject native_promise) {
+  JS::RootedObject executor_function(context);
+  executor_function =
+      JS_NewFunction(context, &NativeExecutor, 2, 0, "NativeExecutor");
+  DCHECK(executor_function);
+
+  JS::RootedObject bound_executor(context);
+  bound_executor = BindCallable(context, executor_function, native_promise);
+  DCHECK(bound_executor);
+  return bound_executor;
+}
+
+// Get the Promise constructor from the global object.
+JSObject* GetPromiseConstructor(JSContext* context,
+                                JS::HandleObject global_object) {
+  JS::RootedValue promise_constructor_property(context);
+  bool result = JS_GetProperty(context, global_object, "Promise",
+                               &promise_constructor_property);
+  DCHECK(result);
+  if (!promise_constructor_property.isObject() ||
+      !JS_ObjectIsFunction(context, &promise_constructor_property.toObject())) {
+    DLOG(ERROR) << "\"Promise\" property is not a function.";
+    return NULL;
+  }
+
+  return &promise_constructor_property.toObject();
+}
+
+void Settle(JSContext* context, JS::HandleValue result,
+            JS::HandleObject resolver, ReservedSlots slot) {
+  JS::RootedValue slot_value(context, JS_GetReservedSlot(resolver, slot));
+  DCHECK(slot_value.isObject());
+  DCHECK(JS_ObjectIsFunction(context, &slot_value.toObject()));
+
+  JS::RootedValue return_value(context);
+  const size_t kNumArguments = result.isUndefined() ? 0 : 1;
+  JS::AutoValueArray<1> args(context);
+  args[0].set(result);
+  bool call_result =
+      JS_CallFunctionValue(context, resolver, slot_value, args, &return_value);
+  if (!call_result) {
+    DLOG(ERROR) << "Exception calling Promise function.";
+    JS_ClearPendingException(context);
+  }
+}
+}  // namespace
+
+JSObject* PromiseWrapper::Create(JSContext* context,
+                                 JS::HandleObject global_object) {
+  // Get the Promise constructor.
+  JS::RootedObject constructor(context,
+                               GetPromiseConstructor(context, global_object));
+  if (!constructor) {
+    DLOG(ERROR) << "Failed to find Promise constructor.";
+    return NULL;
+  }
+  // Create a new NativePromise JS object, and bind it to the NativeExecutor
+  // function.
+  JS::RootedObject promise_wrapper(context, CreateNativePromise(context));
+  DCHECK(promise_wrapper);
+  JS::RootedObject executor(context,
+                            CreateExecutorArgument(context, promise_wrapper));
+  DCHECK(executor);
+
+  // Invoke the Promise constructor with the native executor function.
+  const size_t kNumArguments = 1;
+  JS::AutoValueArray<kNumArguments> args(context);
+  args[0].set(JS::ObjectOrNullValue(executor));
+  JS::RootedObject promise_object(context);
+  promise_object = JS_New(context, constructor, args);
+  if (!promise_object) {
+    DLOG(ERROR) << "Failed to create a new Promise.";
+    return NULL;
+  }
+  // Maintain a handle to the promise object on the NativePromise.
+  JS::RootedValue promise_value(context);
+  promise_value.setObject(*promise_object);
+  JS_SetReservedSlot(promise_wrapper, kPromiseObject, promise_value);
+
+  return promise_wrapper;
+}
+
+JSObject* PromiseWrapper::GetPromise() const {
+  JS::RootedObject promise(context_);
+  JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+  if (promise_wrapper) {
+    JS::RootedValue slot_value(
+        context_, JS_GetReservedSlot(promise_wrapper, kPromiseObject));
+    DCHECK(slot_value.isObject());
+    promise.set(&slot_value.toObject());
+  }
+  return promise;
+}
+
+void PromiseWrapper::Resolve(JS::HandleValue value) const {
+  JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+  if (promise_wrapper) {
+    Settle(context_, value, promise_wrapper, kResolveFunction);
+  }
+}
+
+void PromiseWrapper::Reject(JS::HandleValue value) const {
+  JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+  if (promise_wrapper) {
+    Settle(context_, value, promise_wrapper, kRejectFunction);
+  }
+}
+
+PromiseWrapper::PromiseWrapper(JSContext* context,
+                               JS::HandleObject promise_wrapper)
+    : context_(context), weak_promise_wrapper_(context, promise_wrapper) {
+  DCHECK_EQ(JS_GetClass(promise_wrapper), &native_promise_class);
+}
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.h b/src/cobalt/script/mozjs-45/promise_wrapper.h
new file mode 100644
index 0000000..d77d6a9
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/promise_wrapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_45_PROMISE_WRAPPER_H_
+#define COBALT_SCRIPT_MOZJS_45_PROMISE_WRAPPER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// Native class that maintains a weak handle to a JS object that is a proxy to
+// a JS Promise object. The wrapper object maintains references to the reject
+// and resolve functions that are passed to the Promise executor function.
+class PromiseWrapper {
+ public:
+  // Creates a new JS object that wraps a new Promise, created using the
+  // Promise constructor on |global_object|. Returns NULL on failure.
+  static JSObject* Create(JSContext* context, JS::HandleObject global_object);
+
+  PromiseWrapper(JSContext* context, JS::HandleObject promise_wrapper);
+
+  const WeakHeapObject& get() const { return weak_promise_wrapper_; }
+  JSObject* GetPromise() const;
+  void Resolve(JS::HandleValue value) const;
+  void Reject(JS::HandleValue value) const;
+
+ private:
+  JSContext* context_;
+  WeakHeapObject weak_promise_wrapper_;
+};
+
+}  // namespace mozjs
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_MOZJS_45_PROMISE_WRAPPER_H_
diff --git a/src/cobalt/script/mozjs/mozjs.cc b/src/cobalt/script/mozjs/mozjs.cc
index 3129dd6..36bb288 100644
--- a/src/cobalt/script/mozjs/mozjs.cc
+++ b/src/cobalt/script/mozjs/mozjs.cc
@@ -59,8 +59,7 @@
 }
 
 int MozjsMain(int argc, char** argv) {
-  JavaScriptEngine::Options js_options;
-  cobalt::script::StandaloneJavascriptRunner standalone_runner(js_options);
+  cobalt::script::StandaloneJavascriptRunner standalone_runner;
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(
           standalone_runner.global_environment().get());
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp
index aa3d0c0..ce2763c 100644
--- a/src/cobalt/script/mozjs/mozjs.gyp
+++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -49,7 +49,6 @@
         'embed_mozjs_resources_as_header_files',
       ],
       'defines': [
-        'MOZJS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES=<(mozjs_garbage_collection_threshold_in_bytes)',
         'ENGINE_SUPPORTS_INT64',
       ],
       'all_dependent_settings': {
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index 2de9e14..7040fb3 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -103,11 +103,13 @@
 }
 }  // namespace
 
-MozjsEngine::MozjsEngine() : accumulated_extra_memory_cost_(0) {
+MozjsEngine::MozjsEngine(const Options& options)
+    : accumulated_extra_memory_cost_(0),
+      moz_options_(options) {
   TRACE_EVENT0("cobalt::script", "MozjsEngine::MozjsEngine()");
   // TODO: Investigate the benefit of helper threads and things like
   // parallel compilation.
-  runtime_ = JS_NewRuntime(MOZJS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES,
+  runtime_ = JS_NewRuntime(moz_options_.js_options.gc_threshold_bytes,
                            JS_NO_HELPER_THREADS);
   CHECK(runtime_);
 
@@ -156,11 +158,10 @@
   JS_DestroyRuntime(runtime_);
 }
 
-scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment(
-    const JavaScriptEngine::Options& options) {
+scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment() {
   TRACE_EVENT0("cobalt::script", "MozjsEngine::CreateGlobalEnvironment()");
   DCHECK(thread_checker_.CalledOnValidThread());
-  return new MozjsGlobalEnvironment(runtime_, options);
+  return new MozjsGlobalEnvironment(runtime_, moz_options_.js_options);
 }
 
 void MozjsEngine::CollectGarbage() {
@@ -172,8 +173,10 @@
 void MozjsEngine::ReportExtraMemoryCost(size_t bytes) {
   DCHECK(thread_checker_.CalledOnValidThread());
   accumulated_extra_memory_cost_ += bytes;
-  if (accumulated_extra_memory_cost_ >
-      MOZJS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES) {
+
+  const bool do_collect_garbage = accumulated_extra_memory_cost_ >
+                                  moz_options_.js_options.gc_threshold_bytes;
+  if (do_collect_garbage) {
     accumulated_extra_memory_cost_ = 0;
     CollectGarbage();
   }
@@ -292,9 +295,12 @@
 
 }  // namespace mozjs
 
-scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine() {
+scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine(
+    const JavaScriptEngine::Options& options) {
   TRACE_EVENT0("cobalt::script", "JavaScriptEngine::CreateEngine()");
-  return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine());
+  mozjs::MozjsEngine::Options moz_options(options);
+  return make_scoped_ptr<JavaScriptEngine>(
+      new mozjs::MozjsEngine(moz_options));
 }
 
 }  // namespace script
diff --git a/src/cobalt/script/mozjs/mozjs_engine.h b/src/cobalt/script/mozjs/mozjs_engine.h
index 447d6d4..3bd5235 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.h
+++ b/src/cobalt/script/mozjs/mozjs_engine.h
@@ -28,11 +28,16 @@
 
 class MozjsEngine : public JavaScriptEngine {
  public:
-  MozjsEngine();
+  struct Options {
+    Options() {}
+    explicit Options(const JavaScriptEngine::Options& opt) : js_options(opt) {}
+    JavaScriptEngine::Options js_options;  // Generic settings.
+  };
+
+  explicit MozjsEngine(const Options& options);
   ~MozjsEngine() OVERRIDE;
 
-  scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment(
-      const JavaScriptEngine::Options& options) OVERRIDE;
+  scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
 
   void CollectGarbage() OVERRIDE;
   void ReportExtraMemoryCost(size_t bytes) OVERRIDE;
@@ -68,6 +73,8 @@
 
   // Used to handle javascript errors.
   ErrorHandler error_handler_;
+
+  Options moz_options_;
 };
 }  // namespace mozjs
 }  // namespace script
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.cc b/src/cobalt/script/mozjs/mozjs_exception_state.cc
index e2fefbc..ab6387d 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.cc
@@ -72,30 +72,24 @@
   is_exception_set_ = true;
 }
 
-void MozjsExceptionState::SetSimpleExceptionWithArgs(MessageType message_type,
-                                                     int dummy, ...) {
+void MozjsExceptionState::SetSimpleExceptionVA(SimpleExceptionType type,
+                                               const char* format,
+                                               va_list arguments) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!is_exception_set_);
 
-  va_list arguments;
-  va_start(arguments, dummy);
-
-  std::string error_message = base::StringPrintV(
-      GetExceptionMessageFormat(message_type), arguments);
+  std::string error_message = base::StringPrintV(format, arguments);
   JSErrorFormatString format_string;
   format_string.format = error_message.c_str();
   // Already fed arguments for format.
   format_string.argCount = 0;
-  format_string.exnType =
-      ConvertToMozjsExceptionType(GetSimpleExceptionType(message_type));
+  format_string.exnType = ConvertToMozjsExceptionType(type);
 
   // This function creates a JSErrorReport, populate it with an error message
   // obtained from the given JSErrorCallback. The resulting error message is
   // passed to the context's JSErrorReporter callback.
   JS_ReportErrorNumber(context_, GetErrorMessage,
-                       static_cast<void*>(&format_string), message_type);
-  va_end(arguments);
-
+                       static_cast<void*>(&format_string), type);
   is_exception_set_ = true;
 }
 
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.h b/src/cobalt/script/mozjs/mozjs_exception_state.h
index 86303a9..440c6b7 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.h
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.h
@@ -30,8 +30,8 @@
       : is_exception_set_(false), context_(context) {}
   // ExceptionState interface
   void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE;
-  void SetSimpleExceptionWithArgs(MessageType message_type,
-                                  int dummy, ...) OVERRIDE;
+  void SetSimpleExceptionVA(SimpleExceptionType type, const char* format,
+                            va_list arguments) OVERRIDE;
 
   bool is_exception_set() const { return is_exception_set_; }
 
diff --git a/src/cobalt/script/mozjs/mozjs_variables.gypi b/src/cobalt/script/mozjs/mozjs_variables.gypi
index 857dfc7..f6ec89d 100644
--- a/src/cobalt/script/mozjs/mozjs_variables.gypi
+++ b/src/cobalt/script/mozjs/mozjs_variables.gypi
@@ -28,7 +28,9 @@
         'engine_template_files': [
           '<(DEPTH)/cobalt/bindings/mozjs/templates/callback-interface.cc.template',
           '<(DEPTH)/cobalt/bindings/mozjs/templates/callback-interface.h.template',
-          '<(DEPTH)/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template',
+          '<(DEPTH)/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template',
+          '<(DEPTH)/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template',
+          '<(DEPTH)/cobalt/bindings/mozjs/templates/generated-types.h.template',
           '<(DEPTH)/cobalt/bindings/mozjs/templates/interface.cc.template',
           '<(DEPTH)/cobalt/bindings/mozjs/templates/interface.h.template',
           '<(DEPTH)/cobalt/bindings/mozjs/templates/macros.cc.template',
@@ -36,8 +38,12 @@
         'engine_bindings_scripts': [
           '<(DEPTH)/cobalt/bindings/mozjs/code_generator_mozjs.py',
           '<(DEPTH)/cobalt/bindings/mozjs/idl_compiler_mozjs.py',
+          '<(DEPTH)/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py',
         ],
-        'engine_idl_compiler': '<(DEPTH)/cobalt/bindings/mozjs/idl_compiler_mozjs.py',
+        'engine_idl_compiler':
+            '<(DEPTH)/cobalt/bindings/mozjs/idl_compiler_mozjs.py',
+        'engine_conversion_header_generator_script':
+            '<(DEPTH)/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py',
       }],
     ],
   },
diff --git a/src/cobalt/script/mozjs/native_promise_test.cc b/src/cobalt/script/mozjs/native_promise_test.cc
index e287f73..9194b9c 100644
--- a/src/cobalt/script/mozjs/native_promise_test.cc
+++ b/src/cobalt/script/mozjs/native_promise_test.cc
@@ -34,8 +34,7 @@
   NativePromiseTest()
       : environment_settings_(new script::EnvironmentSettings),
         engine_(script::JavaScriptEngine::CreateEngine()),
-        global_environment_(engine_->CreateGlobalEnvironment(
-            script::JavaScriptEngine::Options())) {
+        global_environment_(engine_->CreateGlobalEnvironment()) {
     global_environment_->CreateGlobalObject();
   }
 
diff --git a/src/cobalt/script/script_runner.cc b/src/cobalt/script/script_runner.cc
index cc98f3f..0768705 100644
--- a/src/cobalt/script/script_runner.cc
+++ b/src/cobalt/script/script_runner.cc
@@ -69,7 +69,8 @@
       : global_environment_(global_environment) {}
 
   std::string Execute(const std::string& script_utf8,
-                      const base::SourceLocation& script_location) OVERRIDE;
+                      const base::SourceLocation& script_location,
+                      bool* out_succeeded) OVERRIDE;
   GlobalEnvironment* GetGlobalEnvironment() const OVERRIDE {
     return global_environment_;
   }
@@ -80,9 +81,13 @@
 
 std::string ScriptRunnerImpl::Execute(
     const std::string& script_utf8,
-    const base::SourceLocation& script_location) {
+    const base::SourceLocation& script_location,
+    bool* out_succeeded) {
   scoped_refptr<SourceCode> source_code =
       SourceCode::CreateSourceCode(script_utf8, script_location);
+  if (out_succeeded) {
+    *out_succeeded = false;
+  }
   if (source_code == NULL) {
     NOTREACHED() << "Failed to pre-process JavaScript source.";
     return "";
@@ -98,6 +103,9 @@
 #if defined(HANDLE_CORE_DUMP)
   script_runner_log.Get().IncrementSuccessCount();
 #endif
+  if (out_succeeded) {
+    *out_succeeded = true;
+  }
   return result;
 }
 
diff --git a/src/cobalt/script/script_runner.h b/src/cobalt/script/script_runner.h
index a040e3b..142c136 100644
--- a/src/cobalt/script/script_runner.h
+++ b/src/cobalt/script/script_runner.h
@@ -32,8 +32,11 @@
   static scoped_ptr<ScriptRunner> CreateScriptRunner(
       const scoped_refptr<GlobalEnvironment>& global_environment);
 
+  // |out_succeeded| is an optional parameter which reports whether the
+  //   script executed without errors.
   virtual std::string Execute(const std::string& script_utf8,
-                              const base::SourceLocation& script_location) = 0;
+                              const base::SourceLocation& script_location,
+                              bool* out_succeeded) = 0;
   virtual GlobalEnvironment* GetGlobalEnvironment() const { return NULL; }
   virtual ~ScriptRunner() {}
 };
diff --git a/src/cobalt/script/standalone_javascript_runner.cc b/src/cobalt/script/standalone_javascript_runner.cc
index f2f0933..528f379 100644
--- a/src/cobalt/script/standalone_javascript_runner.cc
+++ b/src/cobalt/script/standalone_javascript_runner.cc
@@ -55,8 +55,8 @@
 
 void StandaloneJavascriptRunner::CommonInitialization(
     const JavaScriptEngine::Options& options) {
-  engine_ = JavaScriptEngine::CreateEngine();
-  global_environment_ = engine_->CreateGlobalEnvironment(options);
+  engine_ = JavaScriptEngine::CreateEngine(options);
+  global_environment_ = engine_->CreateGlobalEnvironment();
   environment_settings_.reset(new EnvironmentSettings());
 }
 
diff --git a/src/cobalt/script/standalone_javascript_runner.h b/src/cobalt/script/standalone_javascript_runner.h
index 600653b..16526df 100644
--- a/src/cobalt/script/standalone_javascript_runner.h
+++ b/src/cobalt/script/standalone_javascript_runner.h
@@ -29,10 +29,11 @@
 // execute JavaScript.
 class StandaloneJavascriptRunner {
  public:
-  explicit StandaloneJavascriptRunner(const JavaScriptEngine::Options& options);
+  StandaloneJavascriptRunner(
+      const JavaScriptEngine::Options& options = JavaScriptEngine::Options());
 
   template <typename GlobalInterface>
-  explicit StandaloneJavascriptRunner(
+  StandaloneJavascriptRunner(
       const JavaScriptEngine::Options& options,
       const scoped_refptr<GlobalInterface>& global_object) {
     CommonInitialization(options);
diff --git a/src/cobalt/script/testing/fake_script_value.h b/src/cobalt/script/testing/fake_script_value.h
new file mode 100644
index 0000000..8e4a707
--- /dev/null
+++ b/src/cobalt/script/testing/fake_script_value.h
@@ -0,0 +1,57 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/script_value.h"
+
+#ifndef COBALT_SCRIPT_TESTING_FAKE_SCRIPT_VALUE_H_
+#define COBALT_SCRIPT_TESTING_FAKE_SCRIPT_VALUE_H_
+
+namespace cobalt {
+namespace script {
+namespace testing {
+
+template <class T>
+class FakeScriptValue : public cobalt::script::ScriptValue<T> {
+ public:
+  typedef cobalt::script::ScriptValue<T> BaseClass;
+  explicit FakeScriptValue(const T* listener)
+      : value_(listener) {}
+
+  void RegisterOwner(script::Wrappable*) OVERRIDE {}
+  void DeregisterOwner(script::Wrappable*) OVERRIDE {}
+  void PreventGarbageCollection() OVERRIDE {}
+  void AllowGarbageCollection() OVERRIDE {}
+  const T* GetScriptValue(void) const OVERRIDE {
+    return value_;
+  }
+  scoped_ptr<BaseClass> MakeCopy() const OVERRIDE {
+    return make_scoped_ptr<BaseClass>(new FakeScriptValue(value_));
+  }
+
+  bool EqualTo(const BaseClass& other) const OVERRIDE {
+    const FakeScriptValue* other_script_object =
+        base::polymorphic_downcast<const FakeScriptValue*>(&other);
+    return value_ == other_script_object->value_;
+  }
+
+ private:
+  const T* value_;
+};
+
+}  // namespace testing
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_TESTING_FAKE_SCRIPT_VALUE_H_
diff --git a/src/cobalt/script/testing/mock_exception_state.h b/src/cobalt/script/testing/mock_exception_state.h
index 4613935..e1b008b 100644
--- a/src/cobalt/script/testing/mock_exception_state.h
+++ b/src/cobalt/script/testing/mock_exception_state.h
@@ -27,14 +27,8 @@
 class MockExceptionState : public ExceptionState {
  public:
   MOCK_METHOD1(SetException, void(const scoped_refptr<ScriptException>&));
-  MOCK_METHOD2(SetSimpleExceptionVA, void(MessageType, va_list));
-
-  void SetSimpleExceptionWithArgs(MessageType message_type, int dummy, ...) {
-    va_list arguments;
-    va_start(arguments, dummy);
-    SetSimpleExceptionVA(message_type, arguments);
-    va_end(arguments);
-  }
+  MOCK_METHOD3(SetSimpleExceptionVA,
+               void(SimpleExceptionType, const char*, va_list));
 };
 
 }  // namespace testing
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.cc b/src/cobalt/speech/cobalt_speech_recognizer.cc
new file mode 100644
index 0000000..0a8c161
--- /dev/null
+++ b/src/cobalt/speech/cobalt_speech_recognizer.cc
@@ -0,0 +1,154 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/cobalt_speech_recognizer.h"
+
+#include "base/bind.h"
+#include "cobalt/base/tokens.h"
+#if defined(ENABLE_FAKE_MICROPHONE)
+#include "cobalt/speech/microphone_fake.h"
+#include "cobalt/speech/url_fetcher_fake.h"
+#endif  // defined(ENABLE_FAKE_MICROPHONE)
+#include "cobalt/speech/microphone_manager.h"
+#if defined(SB_USE_SB_MICROPHONE)
+#include "cobalt/speech/microphone_starboard.h"
+#endif  // defined(SB_USE_SB_MICROPHONE)
+#include "net/url_request/url_fetcher.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+const int kSampleRate = 16000;
+const float kAudioPacketDurationInSeconds = 0.1f;
+
+scoped_ptr<net::URLFetcher> CreateURLFetcher(
+    const GURL& url, net::URLFetcher::RequestType request_type,
+    net::URLFetcherDelegate* delegate) {
+  return make_scoped_ptr<net::URLFetcher>(
+      net::URLFetcher::Create(url, request_type, delegate));
+}
+
+scoped_ptr<Microphone> CreateMicrophone(int buffer_size_bytes) {
+#if defined(SB_USE_SB_MICROPHONE)
+  return make_scoped_ptr<Microphone>(
+      new MicrophoneStarboard(kSampleRate, buffer_size_bytes));
+#else
+  UNREFERENCED_PARAMETER(buffer_size_bytes);
+  return scoped_ptr<Microphone>();
+#endif  // defined(SB_USE_SB_MICROPHONE)
+}
+
+#if defined(SB_USE_SB_MICROPHONE)
+#if defined(ENABLE_FAKE_MICROPHONE)
+scoped_ptr<net::URLFetcher> CreateFakeURLFetcher(
+    const GURL& url, net::URLFetcher::RequestType request_type,
+    net::URLFetcherDelegate* delegate) {
+  return make_scoped_ptr<net::URLFetcher>(
+      new URLFetcherFake(url, request_type, delegate));
+}
+
+scoped_ptr<Microphone> CreateFakeMicrophone(const Microphone::Options& options,
+                                            int /*buffer_size_bytes*/) {
+  return make_scoped_ptr<Microphone>(new MicrophoneFake(options));
+}
+#endif  // defined(ENABLE_FAKE_MICROPHONE)
+#endif  // defined(SB_USE_SB_MICROPHONE)
+}  // namespace
+
+CobaltSpeechRecognizer::CobaltSpeechRecognizer(
+    network::NetworkModule* network_module,
+    const Microphone::Options& microphone_options,
+    const EventCallback& event_callback)
+    : SpeechRecognizer(event_callback), endpointer_delegate_(kSampleRate) {
+  UNREFERENCED_PARAMETER(microphone_options);
+
+  GoogleSpeechService::URLFetcherCreator url_fetcher_creator =
+      base::Bind(&CreateURLFetcher);
+  MicrophoneManager::MicrophoneCreator microphone_creator =
+      base::Bind(&CreateMicrophone);
+
+#if defined(SB_USE_SB_MICROPHONE)
+#if defined(ENABLE_FAKE_MICROPHONE)
+  if (microphone_options.enable_fake_microphone) {
+    // If fake microphone is enabled, fake URL fetchers should be enabled as
+    // well.
+    url_fetcher_creator = base::Bind(&CreateFakeURLFetcher);
+    microphone_creator = base::Bind(&CreateFakeMicrophone, microphone_options);
+  }
+#endif  // defined(ENABLE_FAKE_MICROPHONE)
+#endif  // defined(SB_USE_SB_MICROPHONE)
+
+  service_.reset(new GoogleSpeechService(
+      network_module, base::Bind(&CobaltSpeechRecognizer::OnRecognizerEvent,
+                                 base::Unretained(this)),
+      url_fetcher_creator));
+  microphone_manager_.reset(new MicrophoneManager(
+      base::Bind(&CobaltSpeechRecognizer::OnDataReceived,
+                 base::Unretained(this)),
+      base::Bind(&CobaltSpeechRecognizer::OnDataCompletion,
+                 base::Unretained(this)),
+      base::Bind(&CobaltSpeechRecognizer::OnMicError, base::Unretained(this)),
+      microphone_creator));
+}
+
+CobaltSpeechRecognizer::~CobaltSpeechRecognizer() {}
+
+void CobaltSpeechRecognizer::Start(const SpeechRecognitionConfig& config) {
+  service_->Start(config, kSampleRate);
+  microphone_manager_->Open();
+  endpointer_delegate_.Start();
+}
+
+void CobaltSpeechRecognizer::Stop() {
+  endpointer_delegate_.Stop();
+  microphone_manager_->Close();
+  service_->Stop();
+  RunEventCallback(new dom::Event(base::Tokens::soundend()));
+}
+
+void CobaltSpeechRecognizer::OnDataReceived(
+    scoped_ptr<ShellAudioBus> audio_bus) {
+  if (endpointer_delegate_.IsFirstTimeSoundStarted(*audio_bus)) {
+    RunEventCallback(new dom::Event(base::Tokens::soundstart()));
+  }
+  service_->RecognizeAudio(audio_bus.Pass(), false);
+}
+
+void CobaltSpeechRecognizer::OnDataCompletion() {
+  // The encoder requires a non-empty final buffer, so encoding a packet of
+  // silence at the end in case encoder had no data already.
+  size_t dummy_frames =
+      static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
+  scoped_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
+      1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+  dummy_audio_bus->ZeroAllFrames();
+  service_->RecognizeAudio(dummy_audio_bus.Pass(), true);
+}
+
+void CobaltSpeechRecognizer::OnRecognizerEvent(
+    const scoped_refptr<dom::Event>& event) {
+  RunEventCallback(event);
+}
+
+void CobaltSpeechRecognizer::OnMicError(
+    const scoped_refptr<dom::Event>& event) {
+  // An error is occured in Mic, so stop the energy endpointer and recognizer.
+  endpointer_delegate_.Stop();
+  service_->Stop();
+  RunEventCallback(event);
+}
+
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.h b/src/cobalt/speech/cobalt_speech_recognizer.h
new file mode 100644
index 0000000..5c9729d
--- /dev/null
+++ b/src/cobalt/speech/cobalt_speech_recognizer.h
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
+#define COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
+
+#include "cobalt/network/network_module.h"
+#include "cobalt/speech/endpointer_delegate.h"
+#include "cobalt/speech/google_speech_service.h"
+#include "cobalt/speech/microphone.h"
+#include "cobalt/speech/microphone_manager.h"
+#include "cobalt/speech/speech_configuration.h"
+#include "cobalt/speech/speech_recognition_config.h"
+#include "cobalt/speech/speech_recognition_error.h"
+#include "cobalt/speech/speech_recognition_event.h"
+#include "cobalt/speech/speech_recognizer.h"
+#if defined(COBALT_MEDIA_SOURCE_2016)
+#include "cobalt/media/base/shell_audio_bus.h"
+#else  // defined(COBALT_MEDIA_SOURCE_2016)
+#include "media/base/shell_audio_bus.h"
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+
+namespace cobalt {
+namespace speech {
+
+// Cobalt's implementation of speech recognizer which interacts with Google
+// speech service and device's microphone. It collects the microphone PCM audio
+// data and sends it to Google speech service, then gets the recognition results
+// from there.
+class CobaltSpeechRecognizer : public SpeechRecognizer {
+ public:
+#if defined(COBALT_MEDIA_SOURCE_2016)
+  typedef media::ShellAudioBus ShellAudioBus;
+#else   // defined(COBALT_MEDIA_SOURCE_2016)
+  typedef ::media::ShellAudioBus ShellAudioBus;
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+
+  CobaltSpeechRecognizer(network::NetworkModule* network_module,
+                         const Microphone::Options& microphone_options,
+                         const EventCallback& event_callback);
+  ~CobaltSpeechRecognizer();
+
+  void Start(const SpeechRecognitionConfig& config) SB_OVERRIDE;
+  void Stop() SB_OVERRIDE;
+
+ private:
+  // Callbacks from mic.
+  void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus);
+  void OnDataCompletion();
+  void OnMicError(const scoped_refptr<dom::Event>& event);
+
+  // Callbacks from recognizer.
+  void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
+
+  EventCallback event_callback_;
+  scoped_ptr<GoogleSpeechService> service_;
+  scoped_ptr<MicrophoneManager> microphone_manager_;
+  // Delegate of endpointer which is used for detecting sound energy.
+  EndPointerDelegate endpointer_delegate_;
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
new file mode 100644
index 0000000..1b49b80
--- /dev/null
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -0,0 +1,398 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Note to Cobalt porters: To use the v1 speech-api API, you must provide an API
+// key with v1 speech-api quota. The code is provided here, but not an API key.
+//
+// This is similar to how Chromium handles API keys:
+// https://www.chromium.org/developers/how-tos/api-keys
+//
+// The API key is provided by SbSystemGetProperty:
+// http://cobalt.foo/reference/starboard/modules/system.html#sbsystemgetproperty
+//
+// Talk with your Google representative about how to get speech-api quota.
+
+#include "cobalt/speech/google_speech_service.h"
+
+#include "base/bind.h"
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "cobalt/base/language.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/speech/google_streaming_api.pb.h"
+#include "cobalt/speech/microphone.h"
+#include "cobalt/speech/speech_configuration.h"
+#include "cobalt/speech/speech_recognition_error.h"
+#include "net/base/escape.h"
+#include "net/url_request/url_fetcher.h"
+
+#if defined(SB_USE_SB_MICROPHONE)
+#include "starboard/system.h"
+#endif  // defined(SB_USE_SB_MICROPHONE)
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+const char kBaseStreamURL[] =
+    "https://www.google.com/speech-api/full-duplex/v1";
+const char kUp[] = "up";
+const char kDown[] = "down";
+const char kClient[] = "com.speech.tv";
+
+GURL AppendPath(const GURL& url, const std::string& value) {
+  std::string path(url.path());
+
+  if (!path.empty()) path += "/";
+
+  path += net::EscapePath(value);
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path);
+  return url.ReplaceComponents(replacements);
+}
+
+GURL AppendQueryParameter(const GURL& url, const std::string& new_query,
+                          const std::string& value) {
+  std::string query(url.query());
+
+  if (!query.empty()) query += "&";
+
+  query += net::EscapeQueryParamValue(new_query, true);
+
+  if (!value.empty()) {
+    query += "=" + net::EscapeQueryParamValue(value, true);
+  }
+
+  GURL::Replacements replacements;
+  replacements.SetQueryStr(query);
+  return url.ReplaceComponents(replacements);
+}
+
+SpeechRecognitionResultList::SpeechRecognitionResults
+ProcessProtoSuccessResults(const proto::SpeechRecognitionEvent& event) {
+  DCHECK_EQ(event.status(), proto::SpeechRecognitionEvent::STATUS_SUCCESS);
+
+  SpeechRecognitionResultList::SpeechRecognitionResults results;
+  for (int i = 0; i < event.result_size(); ++i) {
+    SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
+    const proto::SpeechRecognitionResult& proto_result = event.result(i);
+    for (int j = 0; j < proto_result.alternative_size(); ++j) {
+      const proto::SpeechRecognitionAlternative& proto_alternative =
+          proto_result.alternative(j);
+      float confidence = 0.0f;
+      if (proto_alternative.has_confidence()) {
+        confidence = proto_alternative.confidence();
+      } else if (proto_result.has_stability()) {
+        confidence = proto_result.stability();
+      }
+      scoped_refptr<SpeechRecognitionAlternative> alternative(
+          new SpeechRecognitionAlternative(proto_alternative.transcript(),
+                                           confidence));
+      alternatives.push_back(alternative);
+    }
+
+    bool final = proto_result.has_final() && proto_result.final();
+    scoped_refptr<SpeechRecognitionResult> recognition_result(
+        new SpeechRecognitionResult(alternatives, final));
+    results.push_back(recognition_result);
+  }
+  return results;
+}
+
+// TODO: Feed error messages when creating SpeechRecognitionError.
+void ProcessAndFireErrorEvent(
+    const proto::SpeechRecognitionEvent& event,
+    const GoogleSpeechService::EventCallback& event_callback) {
+  scoped_refptr<dom::Event> error_event;
+  switch (event.status()) {
+    case proto::SpeechRecognitionEvent::STATUS_SUCCESS:
+      NOTREACHED();
+      return;
+    case proto::SpeechRecognitionEvent::STATUS_NO_SPEECH:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeNoSpeech, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_ABORTED:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeAborted, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_AUDIO_CAPTURE:
+      error_event = new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeAudioCapture, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_NETWORK:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeNetwork, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_NOT_ALLOWED:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeNotAllowed, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_SERVICE_NOT_ALLOWED:
+      error_event = new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeServiceNotAllowed, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_BAD_GRAMMAR:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeBadGrammar, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_LANGUAGE_NOT_SUPPORTED:
+      error_event = new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeLanguageNotSupported, "");
+      break;
+  }
+
+  DCHECK(error_event);
+  event_callback.Run(error_event);
+}
+
+bool IsResponseCodeSuccess(int response_code) {
+  // NetFetcher only considers success to be if the network request
+  // was successful *and* we get a 2xx response back.
+  // TODO: 304s are unexpected since we don't enable the HTTP cache,
+  // meaning we don't add the If-Modified-Since header to our request.
+  // However, it's unclear what would happen if we did, so DCHECK.
+  DCHECK_NE(response_code, 304) << "Unsupported status code";
+  return response_code / 100 == 2;
+}
+
+}  // namespace
+
+GoogleSpeechService::GoogleSpeechService(
+    network::NetworkModule* network_module, const EventCallback& event_callback,
+    const URLFetcherCreator& fetcher_creator)
+    : network_module_(network_module),
+      started_(false),
+      event_callback_(event_callback),
+      fetcher_creator_(fetcher_creator),
+      thread_("speech_recognizer") {
+  thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
+}
+
+GoogleSpeechService::~GoogleSpeechService() {
+  Stop();
+  // Stopping the thread here to ensure that StopInternal has completed before
+  // we finish running the destructor.
+  thread_.Stop();
+}
+
+void GoogleSpeechService::Start(const SpeechRecognitionConfig& config,
+                                int sample_rate) {
+  // Called by the speech recognition manager thread.
+  thread_.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&GoogleSpeechService::StartInternal,
+                            base::Unretained(this), config, sample_rate));
+}
+
+void GoogleSpeechService::Stop() {
+  // Called by the speech recognition manager thread.
+  thread_.message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&GoogleSpeechService::StopInternal, base::Unretained(this)));
+}
+
+void GoogleSpeechService::RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus,
+                                         bool is_last_chunk) {
+  // Called by the speech recognition manager thread.
+  thread_.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&GoogleSpeechService::UploadAudioDataInternal,
+                            base::Unretained(this), base::Passed(&audio_bus),
+                            is_last_chunk));
+}
+
+void GoogleSpeechService::OnURLFetchDownloadData(
+    const net::URLFetcher* source, scoped_ptr<std::string> download_data) {
+  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+  const net::URLRequestStatus& status = source->GetStatus();
+  const int response_code = source->GetResponseCode();
+
+  if (source == downstream_fetcher_.get()) {
+    if (status.is_success() && IsResponseCodeSuccess(response_code)) {
+      chunked_byte_buffer_.Append(*download_data);
+      while (chunked_byte_buffer_.HasChunks()) {
+        scoped_ptr<std::vector<uint8_t> > chunk =
+            chunked_byte_buffer_.PopChunk().Pass();
+
+        proto::SpeechRecognitionEvent event;
+        if (!event.ParseFromString(std::string(chunk->begin(), chunk->end()))) {
+          DLOG(WARNING) << "Parse proto string error.";
+          return;
+        }
+
+        if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
+          ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
+        } else {
+          ProcessAndFireErrorEvent(event, event_callback_);
+        }
+      }
+    } else {
+      event_callback_.Run(new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeNetwork, "Network response failure."));
+    }
+  }
+}
+
+void GoogleSpeechService::OnURLFetchComplete(const net::URLFetcher* source) {
+  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+  UNREFERENCED_PARAMETER(source);
+  // no-op.
+}
+
+void GoogleSpeechService::StartInternal(const SpeechRecognitionConfig& config,
+                                        int sample_rate) {
+  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+  if (started_) {
+    // Recognizer is already started.
+    return;
+  }
+  started_ = true;
+
+  encoder_.reset(new AudioEncoderFlac(sample_rate));
+
+  // Required for streaming on both up and down connections.
+  std::string pair = base::Uint64ToString(base::RandUint64());
+
+  // Set up down stream first.
+  GURL down_url(kBaseStreamURL);
+  down_url = AppendPath(down_url, kDown);
+  down_url = AppendQueryParameter(down_url, "pair", pair);
+  // Use protobuffer as the output format.
+  down_url = AppendQueryParameter(down_url, "output", "pb");
+
+  downstream_fetcher_ =
+      fetcher_creator_.Run(down_url, net::URLFetcher::GET, this);
+  downstream_fetcher_->SetRequestContext(
+      network_module_->url_request_context_getter());
+  downstream_fetcher_->Start();
+
+  // Up stream.
+  GURL up_url(kBaseStreamURL);
+  up_url = AppendPath(up_url, kUp);
+  up_url = AppendQueryParameter(up_url, "client", kClient);
+  up_url = AppendQueryParameter(up_url, "pair", pair);
+  up_url = AppendQueryParameter(up_url, "output", "pb");
+
+  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.
+  if (!config.lang.empty()) {
+    up_url = AppendQueryParameter(up_url, "lang", config.lang);
+  } else {
+    up_url = AppendQueryParameter(up_url, "lang", base::GetSystemLanguage());
+  }
+
+  if (config.max_alternatives) {
+    up_url = AppendQueryParameter(up_url, "maxAlternatives",
+                                  base::UintToString(config.max_alternatives));
+  }
+
+  if (config.continuous) {
+    up_url = AppendQueryParameter(up_url, "continuous", "");
+  }
+  if (config.interim_results) {
+    up_url = AppendQueryParameter(up_url, "interim", "");
+  }
+
+  upstream_fetcher_ = fetcher_creator_.Run(up_url, net::URLFetcher::POST, this);
+  upstream_fetcher_->SetRequestContext(
+      network_module_->url_request_context_getter());
+  upstream_fetcher_->SetChunkedUpload(encoder_->GetMimeType());
+  upstream_fetcher_->Start();
+}
+
+void GoogleSpeechService::StopInternal() {
+  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+  if (!started_) {
+    // Recognizer is not started.
+    return;
+  }
+  started_ = false;
+
+  upstream_fetcher_.reset();
+  downstream_fetcher_.reset();
+  encoder_.reset();
+
+  // Clear the final results.
+  final_results_.clear();
+  // Clear any remaining audio data.
+  chunked_byte_buffer_.Clear();
+}
+
+void GoogleSpeechService::UploadAudioDataInternal(
+    scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
+  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+  DCHECK(audio_bus);
+
+  std::string encoded_audio_data;
+  if (encoder_) {
+    encoder_->Encode(audio_bus.get());
+    if (is_last_chunk) {
+      encoder_->Finish();
+    }
+    encoded_audio_data = encoder_->GetAndClearAvailableEncodedData();
+  }
+
+  if (upstream_fetcher_ && !encoded_audio_data.empty()) {
+    upstream_fetcher_->AppendChunkToUpload(encoded_audio_data, is_last_chunk);
+  }
+}
+
+void GoogleSpeechService::ProcessAndFireSuccessEvent(
+    const SpeechRecognitionResults& new_results) {
+  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+  SpeechRecognitionResults success_results;
+  size_t total_size = final_results_.size() + new_results.size();
+  success_results.reserve(total_size);
+  success_results = final_results_;
+  success_results.insert(success_results.end(), new_results.begin(),
+                         new_results.end());
+
+  size_t result_index = final_results_.size();
+  // Update final results list.
+  for (size_t i = 0; i < new_results.size(); ++i) {
+    if (new_results[i]->is_final()) {
+      final_results_.push_back(new_results[i]);
+    }
+  }
+
+  scoped_refptr<SpeechRecognitionResultList> recognition_list(
+      new SpeechRecognitionResultList(success_results));
+  scoped_refptr<SpeechRecognitionEvent> recognition_event(
+      new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
+                                 static_cast<uint32>(result_index),
+                                 recognition_list));
+  event_callback_.Run(recognition_event);
+}
+
+}  // namespace speech
+}  // namespace cobalt
diff --git a/src/cobalt/speech/google_speech_service.h b/src/cobalt/speech/google_speech_service.h
new file mode 100644
index 0000000..30d0113
--- /dev/null
+++ b/src/cobalt/speech/google_speech_service.h
@@ -0,0 +1,114 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_GOOGLE_SPEECH_SERVICE_H_
+#define COBALT_SPEECH_GOOGLE_SPEECH_SERVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/threading/thread.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/speech/audio_encoder_flac.h"
+#include "cobalt/speech/speech_recognition_config.h"
+#include "cobalt/speech/speech_recognition_event.h"
+#include "content/browser/speech/chunked_byte_buffer.h"
+#if defined(COBALT_MEDIA_SOURCE_2016)
+#include "cobalt/media/base/shell_audio_bus.h"
+#else  // defined(COBALT_MEDIA_SOURCE_2016)
+#include "media/base/shell_audio_bus.h"
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace cobalt {
+namespace speech {
+
+// Interacts with Google speech recognition service, and then parses recognition
+// results and forms speech recognition event.
+// It creates an upstream fetcher to upload the encoded audio and a downstream
+// fetcher to fetch the speech recognition result. The fetched speech
+// recognition result is parsed by JSON parser and a SpeechRecognitionEvent,
+// is formed based on the parsed result, would be sent to speech recognition
+// manager.
+class GoogleSpeechService : public net::URLFetcherDelegate {
+ public:
+#if defined(COBALT_MEDIA_SOURCE_2016)
+  typedef media::ShellAudioBus ShellAudioBus;
+#else   // defined(COBALT_MEDIA_SOURCE_2016)
+  typedef ::media::ShellAudioBus ShellAudioBus;
+#endif  // defined(COBALT_MEDIA_SOURCE_2016)
+  typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
+  typedef SpeechRecognitionResultList::SpeechRecognitionResults
+      SpeechRecognitionResults;
+  typedef base::Callback<scoped_ptr<net::URLFetcher>(
+      const GURL&, net::URLFetcher::RequestType, net::URLFetcherDelegate*)>
+      URLFetcherCreator;
+
+  GoogleSpeechService(network::NetworkModule* network_module,
+                      const EventCallback& event_callback,
+                      const URLFetcherCreator& fetcher_creator);
+  ~GoogleSpeechService() OVERRIDE;
+
+  // Multiple calls to Start/Stop are allowed, the implementation should take
+  // care of multiple calls.
+  // Start speech recognizer.
+  void Start(const SpeechRecognitionConfig& config, int sample_rate);
+  // Stop speech recognizer.
+  void Stop();
+  // An encoded audio data is available and ready to be recognized.
+  void RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk);
+
+  // net::URLFetcherDelegate interface
+  void OnURLFetchDownloadData(const net::URLFetcher* source,
+                              scoped_ptr<std::string> download_data) OVERRIDE;
+  void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+  bool ShouldSendDownloadData() OVERRIDE { return true; }
+  void OnURLFetchUploadProgress(const net::URLFetcher* /*source*/,
+                                int64 /*current*/, int64 /*total*/) OVERRIDE {}
+
+ private:
+  void StartInternal(const SpeechRecognitionConfig& config, int sample_rate);
+  void StopInternal();
+  void UploadAudioDataInternal(scoped_ptr<ShellAudioBus> audio_bus,
+                               bool is_last_chunk);
+  void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
+
+  // This is used for creating fetchers.
+  network::NetworkModule* network_module_;
+  // Track the start/stop state of speech recognizer.
+  bool started_;
+
+  // Encoder for encoding raw audio data to flac codec.
+  scoped_ptr<AudioEncoderFlac> encoder_;
+  // Fetcher for posting the audio data.
+  scoped_ptr<net::URLFetcher> upstream_fetcher_;
+  // Fetcher for receiving the streaming results.
+  scoped_ptr<net::URLFetcher> downstream_fetcher_;
+  // Used to send speech recognition event.
+  const EventCallback event_callback_;
+  // Used to create url fetcher.
+  const URLFetcherCreator fetcher_creator_;
+  // Used for processing proto buffer data.
+  content::ChunkedByteBuffer chunked_byte_buffer_;
+  // Used for accumulating final results.
+  SpeechRecognitionResults final_results_;
+  // Speech recognizer is operating in its own thread.
+  base::Thread thread_;
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // COBALT_SPEECH_GOOGLE_SPEECH_SERVICE_H_
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index 2faf329..ac38787 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -73,7 +73,7 @@
     microphone_.reset();
     state_ = kError;
     error_callback_.Run(new SpeechRecognitionError(
-        SpeechRecognitionError::kAudioCapture, "No microphone available."));
+        kSpeechRecognitionErrorCodeAudioCapture, "No microphone available."));
     return false;
   }
 }
@@ -90,7 +90,7 @@
   if (!microphone_->Open()) {
     state_ = kError;
     error_callback_.Run(new SpeechRecognitionError(
-        SpeechRecognitionError::kAborted, "Microphone open failed."));
+        kSpeechRecognitionErrorCodeAborted, "Microphone open failed."));
     return;
   }
 
@@ -118,7 +118,7 @@
     if (!microphone_->Close()) {
       state_ = kError;
       error_callback_.Run(new SpeechRecognitionError(
-          SpeechRecognitionError::kAborted, "Microphone close failed."));
+          kSpeechRecognitionErrorCodeAborted, "Microphone close failed."));
       return;
     }
     completion_callback_.Run();
@@ -147,7 +147,7 @@
   } else if (read_bytes != 0) {
     state_ = kError;
     error_callback_.Run(new SpeechRecognitionError(
-        SpeechRecognitionError::kAborted, "Microphone read failed."));
+        kSpeechRecognitionErrorCodeAborted, "Microphone read failed."));
     poll_mic_events_timer_->Stop();
   }
 }
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index 9f0c58e..c3b77f8 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -28,8 +28,12 @@
       'sources': [
         'audio_encoder_flac.cc',
         'audio_encoder_flac.h',
+        'cobalt_speech_recognizer.cc',
+        'cobalt_speech_recognizer.h',
         'endpointer_delegate.cc',
         'endpointer_delegate.h',
+        'google_speech_service.cc',
+        'google_speech_service.h',
         'google_streaming_api.pb.cc',
         'google_streaming_api.pb.h',
         'google_streaming_api.pb.proto',
@@ -62,14 +66,23 @@
         'speech_synthesis_utterance.cc',
         'speech_synthesis_utterance.h',
         'speech_synthesis_voice.h',
+        'starboard_speech_recognizer.cc',
+        'starboard_speech_recognizer.h',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/content/browser/speech/speech.gyp:speech',
         '<(DEPTH)/third_party/flac/flac.gyp:libflac',
         '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
       ],
+      'export_dependent_settings': [
+        # Additionally, ensure that the include directories for generated
+        # headers are put on the include directories for targets that depend
+        # on this one.
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+      ],
       'include_dirs': [
         # Get protobuf headers from the chromium tree.
         '<(DEPTH)/third_party/protobuf/src',
diff --git a/src/cobalt/speech/speech_configuration.h b/src/cobalt/speech/speech_configuration.h
index 0750c04..0c421e3 100644
--- a/src/cobalt/speech/speech_configuration.h
+++ b/src/cobalt/speech/speech_configuration.h
@@ -22,6 +22,11 @@
 #if SB_HAS(MICROPHONE) && SB_VERSION(2)
 #define SB_USE_SB_MICROPHONE 1
 #endif  // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#define SB_USE_SB_SPEECH_RECOGNIZER 1
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
 #endif  // defined(OS_STARBOARD)
 
 #endif  // COBALT_SPEECH_SPEECH_CONFIGURATION_H_
diff --git a/src/cobalt/speech/speech_recognition_error.cc b/src/cobalt/speech/speech_recognition_error.cc
index 3d227bf..3fa0ee9 100644
--- a/src/cobalt/speech/speech_recognition_error.cc
+++ b/src/cobalt/speech/speech_recognition_error.cc
@@ -19,8 +19,8 @@
 namespace cobalt {
 namespace speech {
 
-SpeechRecognitionError::SpeechRecognitionError(ErrorCode error_code,
-                                               const std::string& message)
+SpeechRecognitionError::SpeechRecognitionError(
+    SpeechRecognitionErrorCode error_code, const std::string& message)
     : dom::Event(base::Tokens::error()),
       error_code_(error_code),
       message_(message) {}
diff --git a/src/cobalt/speech/speech_recognition_error.h b/src/cobalt/speech/speech_recognition_error.h
index 3791df4..4bd4fef 100644
--- a/src/cobalt/speech/speech_recognition_error.h
+++ b/src/cobalt/speech/speech_recognition_error.h
@@ -21,6 +21,7 @@
 #include "base/memory/ref_counted.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_recognition_error_code.h"
 
 namespace cobalt {
 namespace speech {
@@ -29,20 +30,10 @@
 //   https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechreco-error
 class SpeechRecognitionError : public dom::Event {
  public:
-  enum ErrorCode {
-    kNoSpeech,
-    kAborted,
-    kAudioCapture,
-    kNetwork,
-    kNotAllowed,
-    kServiceNotAllowed,
-    kBadGrammar,
-    kLanguageNotSupported
-  };
+  SpeechRecognitionError(SpeechRecognitionErrorCode error_code,
+                         const std::string& message);
 
-  SpeechRecognitionError(ErrorCode error_code, const std::string& message);
-
-  ErrorCode error() const { return error_code_; }
+  SpeechRecognitionErrorCode error() const { return error_code_; }
   const std::string& message() const { return message_; }
 
   DEFINE_WRAPPABLE_TYPE(SpeechRecognitionError);
@@ -50,7 +41,7 @@
  private:
   ~SpeechRecognitionError() OVERRIDE {}
 
-  const ErrorCode error_code_;
+  const SpeechRecognitionErrorCode error_code_;
   const std::string message_;
 
   DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionError);
diff --git a/src/cobalt/speech/speech_recognition_error.idl b/src/cobalt/speech/speech_recognition_error.idl
index 298d38e..ede9832 100644
--- a/src/cobalt/speech/speech_recognition_error.idl
+++ b/src/cobalt/speech/speech_recognition_error.idl
@@ -14,18 +14,7 @@
 
 // https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechreco-section
 
-enum ErrorCode {
-  "no-speech",
-  "aborted",
-  "audio-capture",
-  "network",
-  "not-allowed",
-  "service-not-allowed",
-  "bad-grammar",
-  "language-not-supported"
-};
-
 interface SpeechRecognitionError : Event {
-  readonly attribute ErrorCode error;
+  readonly attribute SpeechRecognitionErrorCode error;
   readonly attribute DOMString message;
 };
diff --git a/src/cobalt/base/math.cc b/src/cobalt/speech/speech_recognition_error_code.idl
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/cobalt/speech/speech_recognition_error_code.idl
index d335495..1646a5a3 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/speech/speech_recognition_error_code.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// 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.
@@ -12,8 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+// https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechreco-section
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+enum SpeechRecognitionErrorCode {
+  "no-speech",
+  "aborted",
+  "audio-capture",
+  "network",
+  "not-allowed",
+  "service-not-allowed",
+  "bad-grammar",
+  "language-not-supported"
+};
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index 2ad31f2..ff5b480 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -15,60 +15,17 @@
 #include "cobalt/speech/speech_recognition_manager.h"
 
 #include "base/bind.h"
-#include "cobalt/base/tokens.h"
 #include "cobalt/dom/dom_exception.h"
-#if defined(ENABLE_FAKE_MICROPHONE)
-#include "cobalt/speech/microphone_fake.h"
-#include "cobalt/speech/url_fetcher_fake.h"
-#endif  // defined(ENABLE_FAKE_MICROPHONE)
-#include "cobalt/speech/microphone_manager.h"
-#if defined(SB_USE_SB_MICROPHONE)
-#include "cobalt/speech/microphone_starboard.h"
-#endif  // defined(SB_USE_SB_MICROPHONE)
-#include "net/url_request/url_fetcher.h"
+#include "cobalt/speech/speech_configuration.h"
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+#include "cobalt/speech/starboard_speech_recognizer.h"
+#else
+#include "cobalt/speech/cobalt_speech_recognizer.h"
+#endif  // defined(SB_USE_SB_SPEECH_RECOGNIZER)
 
 namespace cobalt {
 namespace speech {
 
-namespace {
-const int kSampleRate = 16000;
-const float kAudioPacketDurationInSeconds = 0.1f;
-
-scoped_ptr<net::URLFetcher> CreateURLFetcher(
-    const GURL& url, net::URLFetcher::RequestType request_type,
-    net::URLFetcherDelegate* delegate) {
-  return make_scoped_ptr<net::URLFetcher>(
-      net::URLFetcher::Create(url, request_type, delegate));
-}
-
-scoped_ptr<Microphone> CreateMicrophone(int buffer_size_bytes) {
-#if defined(SB_USE_SB_MICROPHONE)
-  return make_scoped_ptr<Microphone>(
-      new MicrophoneStarboard(kSampleRate, buffer_size_bytes));
-#else
-  UNREFERENCED_PARAMETER(buffer_size_bytes);
-  return scoped_ptr<Microphone>();
-#endif  // defined(SB_USE_SB_MICROPHONE)
-}
-
-#if defined(SB_USE_SB_MICROPHONE)
-#if defined(ENABLE_FAKE_MICROPHONE)
-scoped_ptr<net::URLFetcher> CreateFakeURLFetcher(
-    const GURL& url, net::URLFetcher::RequestType request_type,
-    net::URLFetcherDelegate* delegate) {
-  return make_scoped_ptr<net::URLFetcher>(
-      new URLFetcherFake(url, request_type, delegate));
-}
-
-scoped_ptr<Microphone> CreateFakeMicrophone(const Microphone::Options& options,
-                                            int /*buffer_size_bytes*/) {
-  return make_scoped_ptr<Microphone>(new MicrophoneFake(options));
-}
-#endif  // defined(ENABLE_FAKE_MICROPHONE)
-#endif  // defined(SB_USE_SB_MICROPHONE)
-
-}  // namespace
-
 SpeechRecognitionManager::SpeechRecognitionManager(
     network::NetworkModule* network_module, const EventCallback& event_callback,
     const Microphone::Options& microphone_options)
@@ -76,40 +33,21 @@
       weak_this_(weak_ptr_factory_.GetWeakPtr()),
       main_message_loop_(base::MessageLoopProxy::current()),
       event_callback_(event_callback),
-      endpointer_delegate_(kSampleRate),
       state_(kStopped) {
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+  UNREFERENCED_PARAMETER(network_module);
   UNREFERENCED_PARAMETER(microphone_options);
-
-  SpeechRecognizer::URLFetcherCreator url_fetcher_creator =
-      base::Bind(&CreateURLFetcher);
-  MicrophoneManager::MicrophoneCreator microphone_creator =
-      base::Bind(&CreateMicrophone);
-
-#if defined(SB_USE_SB_MICROPHONE)
-#if defined(ENABLE_FAKE_MICROPHONE)
-  if (microphone_options.enable_fake_microphone) {
-    // If fake microphone is enabled, fake URL fetchers should be enabled as
-    // well.
-    url_fetcher_creator = base::Bind(&CreateFakeURLFetcher);
-    microphone_creator = base::Bind(&CreateFakeMicrophone, microphone_options);
-  }
-#endif  // defined(ENABLE_FAKE_MICROPHONE)
-#endif  // defined(SB_USE_SB_MICROPHONE)
-
-  recognizer_.reset(new SpeechRecognizer(
-      network_module, base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
-                                 base::Unretained(this)),
-      url_fetcher_creator));
-  microphone_manager_.reset(new MicrophoneManager(
-      base::Bind(&SpeechRecognitionManager::OnDataReceived,
-                 base::Unretained(this)),
-      base::Bind(&SpeechRecognitionManager::OnDataCompletion,
-                 base::Unretained(this)),
-      base::Bind(&SpeechRecognitionManager::OnMicError, base::Unretained(this)),
-      microphone_creator));
+  recognizer_.reset(new StarboardSpeechRecognizer(base::Bind(
+      &SpeechRecognitionManager::OnEventAvailable, base::Unretained(this))));
+#else
+  recognizer_.reset(new CobaltSpeechRecognizer(
+      network_module, microphone_options,
+      base::Bind(&SpeechRecognitionManager::OnEventAvailable,
+                 base::Unretained(this))));
+#endif  // defined(SB_USE_SB_SPEECH_RECOGNIZER)
 }
 
-SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(false); }
+SpeechRecognitionManager::~SpeechRecognitionManager() { Abort(); }
 
 void SpeechRecognitionManager::Start(const SpeechRecognitionConfig& config,
                                      script::ExceptionState* exception_state) {
@@ -123,13 +61,11 @@
     return;
   }
 
-  recognizer_->Start(config, kSampleRate);
-  microphone_manager_->Open();
-  endpointer_delegate_.Start();
+  recognizer_->Start(config);
   state_ = kStarted;
 }
 
-void SpeechRecognitionManager::Stop(bool run_callback) {
+void SpeechRecognitionManager::Stop() {
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
   // If the stop method is called on an object which is already stopped or being
@@ -138,13 +74,8 @@
     return;
   }
 
-  endpointer_delegate_.Stop();
-  microphone_manager_->Close();
   recognizer_->Stop();
   state_ = kStopped;
-  if (run_callback) {
-    event_callback_.Run(new dom::Event(base::Tokens::soundend()));
-  }
 }
 
 void SpeechRecognitionManager::Abort() {
@@ -156,60 +87,17 @@
     return;
   }
 
-  endpointer_delegate_.Stop();
-  microphone_manager_->Close();
-  recognizer_->Stop();
   state_ = kAborted;
-  event_callback_.Run(new dom::Event(base::Tokens::soundend()));
+  recognizer_->Stop();
 }
 
-void SpeechRecognitionManager::OnDataReceived(
-    scoped_ptr<ShellAudioBus> audio_bus) {
-  if (!main_message_loop_->BelongsToCurrentThread()) {
-    // Called from mic thread.
-    main_message_loop_->PostTask(
-        FROM_HERE, base::Bind(&SpeechRecognitionManager::OnDataReceived,
-                              weak_this_, base::Passed(&audio_bus)));
-    return;
-  }
-
-  // Stop recognizing if in the abort state.
-  if (state_ != kAborted) {
-    if (endpointer_delegate_.IsFirstTimeSoundStarted(*audio_bus)) {
-      event_callback_.Run(new dom::Event(base::Tokens::soundstart()));
-    }
-    recognizer_->RecognizeAudio(audio_bus.Pass(), false);
-  }
-}
-
-void SpeechRecognitionManager::OnDataCompletion() {
-  if (!main_message_loop_->BelongsToCurrentThread()) {
-    // Called from mic thread.
-    main_message_loop_->PostTask(
-        FROM_HERE,
-        base::Bind(&SpeechRecognitionManager::OnDataCompletion, weak_this_));
-    return;
-  }
-
-  // Stop recognizing if in the abort state.
-  if (state_ != kAborted) {
-    // The encoder requires a non-empty final buffer, so encoding a packet of
-    // silence at the end in case encoder had no data already.
-    size_t dummy_frames =
-        static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
-    scoped_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
-        1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
-    dummy_audio_bus->ZeroAllFrames();
-    recognizer_->RecognizeAudio(dummy_audio_bus.Pass(), true);
-  }
-}
-
-void SpeechRecognitionManager::OnRecognizerEvent(
+void SpeechRecognitionManager::OnEventAvailable(
     const scoped_refptr<dom::Event>& event) {
   if (!main_message_loop_->BelongsToCurrentThread()) {
-    // Called from recognizer thread.
+    // Called from recognizer. |event_callback_| is required to be run on
+    // the |main_message_loop_|.
     main_message_loop_->PostTask(
-        FROM_HERE, base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
+        FROM_HERE, base::Bind(&SpeechRecognitionManager::OnEventAvailable,
                               weak_this_, event));
     return;
   }
@@ -220,24 +108,5 @@
   }
 }
 
-void SpeechRecognitionManager::OnMicError(
-    const scoped_refptr<dom::Event>& event) {
-  if (!main_message_loop_->BelongsToCurrentThread()) {
-    // Called from mic thread.
-    main_message_loop_->PostTask(
-        FROM_HERE,
-        base::Bind(&SpeechRecognitionManager::OnMicError, weak_this_, event));
-    return;
-  }
-
-  event_callback_.Run(event);
-
-  // An error is occured in Mic, so stop the energy endpointer and recognizer.
-  endpointer_delegate_.Stop();
-  recognizer_->Stop();
-  state_ = kAborted;
-  event_callback_.Run(new dom::Event(base::Tokens::soundend()));
-}
-
 }  // namespace speech
 }  // namespace cobalt
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index b201c07..5efcf1b 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -19,24 +19,13 @@
 
 #include "cobalt/network/network_module.h"
 #include "cobalt/script/exception_state.h"
-#include "cobalt/speech/endpointer_delegate.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"
-#include "cobalt/speech/speech_recognition_event.h"
 #include "cobalt/speech/speech_recognizer.h"
-#if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_audio_bus.h"
-#else  // defined(COBALT_MEDIA_SOURCE_2016)
-#include "media/base/shell_audio_bus.h"
-#endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
 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
@@ -44,11 +33,6 @@
 // class would encode the audio data, then send it to recogniton service.
 class SpeechRecognitionManager {
  public:
-#if defined(COBALT_MEDIA_SOURCE_2016)
-  typedef media::ShellAudioBus ShellAudioBus;
-#else   // defined(COBALT_MEDIA_SOURCE_2016)
-  typedef ::media::ShellAudioBus ShellAudioBus;
-#endif  // defined(COBALT_MEDIA_SOURCE_2016)
   typedef base::Callback<bool(const scoped_refptr<dom::Event>&)> EventCallback;
 
   SpeechRecognitionManager(network::NetworkModule* network_module,
@@ -56,11 +40,10 @@
                            const Microphone::Options& microphone_options);
   ~SpeechRecognitionManager();
 
-  // Start/Stop speech recognizer and microphone. Multiple calls would be
-  // managed by their own class.
+  // Start/Stop speech recognizer.
   void Start(const SpeechRecognitionConfig& config,
              script::ExceptionState* exception_state);
-  void Stop(bool run_callback = true);
+  void Stop();
   void Abort();
 
  private:
@@ -70,13 +53,7 @@
     kAborted,
   };
 
-  // Callbacks from mic.
-  void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus);
-  void OnDataCompletion();
-  void OnMicError(const scoped_refptr<dom::Event>& event);
-
-  // Callbacks from recognizer.
-  void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
+  void OnEventAvailable(const scoped_refptr<dom::Event>& event);
 
   base::WeakPtrFactory<SpeechRecognitionManager> weak_ptr_factory_;
   // We construct a WeakPtr upon SpeechRecognitionManager's construction in
@@ -88,11 +65,6 @@
   EventCallback event_callback_;
   scoped_ptr<SpeechRecognizer> recognizer_;
 
-  scoped_ptr<MicrophoneManager> microphone_manager_;
-
-  // Delegate of endpointer which is used for detecting sound energy.
-  EndPointerDelegate endpointer_delegate_;
-
   State state_;
 };
 
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc
index 9360688..0f3bec9 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,386 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Note to Cobalt porters: To use the v1 speech-api API, you must provide an API
-// key with v1 speech-api quota. The code is provided here, but not an API key.
-//
-// This is similar to how Chromium handles API keys:
-// https://www.chromium.org/developers/how-tos/api-keys
-//
-// The API key is provided by SbSystemGetProperty:
-// http://cobalt.foo/reference/starboard/modules/system.html#sbsystemgetproperty
-//
-// Talk with your Google representative about how to get speech-api quota.
-
 #include "cobalt/speech/speech_recognizer.h"
 
-#include "base/bind.h"
-#include "base/rand_util.h"
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
-#include "base/utf_string_conversions.h"
-#include "cobalt/base/language.h"
-#include "cobalt/loader/fetcher_factory.h"
-#include "cobalt/network/network_module.h"
-#include "cobalt/speech/google_streaming_api.pb.h"
-#include "cobalt/speech/microphone.h"
-#include "cobalt/speech/speech_configuration.h"
-#include "cobalt/speech/speech_recognition_error.h"
-#include "net/base/escape.h"
-#include "net/url_request/url_fetcher.h"
-
-#if defined(SB_USE_SB_MICROPHONE)
-#include "starboard/system.h"
-#endif  // defined(SB_USE_SB_MICROPHONE)
-
 namespace cobalt {
 namespace speech {
 
-namespace {
-const char kBaseStreamURL[] =
-    "https://www.google.com/speech-api/full-duplex/v1";
-const char kUp[] = "up";
-const char kDown[] = "down";
-const char kClient[] = "com.speech.tv";
+SpeechRecognizer::SpeechRecognizer(const EventCallback& event_callback)
+    : event_callback_(event_callback) {}
 
-GURL AppendPath(const GURL& url, const std::string& value) {
-  std::string path(url.path());
-
-  if (!path.empty()) path += "/";
-
-  path += net::EscapePath(value);
-  GURL::Replacements replacements;
-  replacements.SetPathStr(path);
-  return url.ReplaceComponents(replacements);
-}
-
-GURL AppendQueryParameter(const GURL& url, const std::string& new_query,
-                          const std::string& value) {
-  std::string query(url.query());
-
-  if (!query.empty()) query += "&";
-
-  query += net::EscapeQueryParamValue(new_query, true);
-
-  if (!value.empty()) {
-    query += "=" + net::EscapeQueryParamValue(value, true);
-  }
-
-  GURL::Replacements replacements;
-  replacements.SetQueryStr(query);
-  return url.ReplaceComponents(replacements);
-}
-
-SpeechRecognitionResultList::SpeechRecognitionResults
-ProcessProtoSuccessResults(const proto::SpeechRecognitionEvent& event) {
-  DCHECK_EQ(event.status(), proto::SpeechRecognitionEvent::STATUS_SUCCESS);
-
-  SpeechRecognitionResultList::SpeechRecognitionResults results;
-  for (int i = 0; i < event.result_size(); ++i) {
-    SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
-    const proto::SpeechRecognitionResult& proto_result = event.result(i);
-    for (int j = 0; j < proto_result.alternative_size(); ++j) {
-      const proto::SpeechRecognitionAlternative& proto_alternative =
-          proto_result.alternative(j);
-      float confidence = 0.0f;
-      if (proto_alternative.has_confidence()) {
-        confidence = proto_alternative.confidence();
-      } else if (proto_result.has_stability()) {
-        confidence = proto_result.stability();
-      }
-      scoped_refptr<SpeechRecognitionAlternative> alternative(
-          new SpeechRecognitionAlternative(proto_alternative.transcript(),
-                                           confidence));
-      alternatives.push_back(alternative);
-    }
-
-    bool final = proto_result.has_final() && proto_result.final();
-    scoped_refptr<SpeechRecognitionResult> recognition_result(
-        new SpeechRecognitionResult(alternatives, final));
-    results.push_back(recognition_result);
-  }
-  return results;
-}
-
-// TODO: Feed error messages when creating SpeechRecognitionError.
-void ProcessAndFireErrorEvent(
-    const proto::SpeechRecognitionEvent& event,
-    const SpeechRecognizer::EventCallback& event_callback) {
-  scoped_refptr<dom::Event> error_event;
-  switch (event.status()) {
-    case proto::SpeechRecognitionEvent::STATUS_SUCCESS:
-      NOTREACHED();
-      return;
-    case proto::SpeechRecognitionEvent::STATUS_NO_SPEECH:
-      error_event =
-          new SpeechRecognitionError(SpeechRecognitionError::kNoSpeech, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_ABORTED:
-      error_event =
-          new SpeechRecognitionError(SpeechRecognitionError::kAborted, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_AUDIO_CAPTURE:
-      error_event =
-          new SpeechRecognitionError(SpeechRecognitionError::kAudioCapture, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_NETWORK:
-      error_event =
-          new SpeechRecognitionError(SpeechRecognitionError::kNetwork, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_NOT_ALLOWED:
-      error_event =
-          new SpeechRecognitionError(SpeechRecognitionError::kNotAllowed, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_SERVICE_NOT_ALLOWED:
-      error_event = new SpeechRecognitionError(
-          SpeechRecognitionError::kServiceNotAllowed, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_BAD_GRAMMAR:
-      error_event =
-          new SpeechRecognitionError(SpeechRecognitionError::kBadGrammar, "");
-      break;
-    case proto::SpeechRecognitionEvent::STATUS_LANGUAGE_NOT_SUPPORTED:
-      error_event = new SpeechRecognitionError(
-          SpeechRecognitionError::kLanguageNotSupported, "");
-      break;
-  }
-
-  DCHECK(error_event);
-  event_callback.Run(error_event);
-}
-
-bool IsResponseCodeSuccess(int response_code) {
-  // NetFetcher only considers success to be if the network request
-  // was successful *and* we get a 2xx response back.
-  // TODO: 304s are unexpected since we don't enable the HTTP cache,
-  // meaning we don't add the If-Modified-Since header to our request.
-  // However, it's unclear what would happen if we did, so DCHECK.
-  DCHECK_NE(response_code, 304) << "Unsupported status code";
-  return response_code / 100 == 2;
-}
-
-}  // namespace
-
-SpeechRecognizer::SpeechRecognizer(network::NetworkModule* network_module,
-                                   const EventCallback& event_callback,
-                                   const URLFetcherCreator& fetcher_creator)
-    : network_module_(network_module),
-      started_(false),
-      event_callback_(event_callback),
-      fetcher_creator_(fetcher_creator),
-      thread_("speech_recognizer") {
-  thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
-}
-
-SpeechRecognizer::~SpeechRecognizer() {
-  Stop();
-  // Stopping the thread here to ensure that StopInternal has completed before
-  // we finish running the destructor.
-  thread_.Stop();
-}
-
-void SpeechRecognizer::Start(const SpeechRecognitionConfig& config,
-                             int sample_rate) {
-  // Called by the speech recognition manager thread.
-  thread_.message_loop()->PostTask(
-      FROM_HERE, base::Bind(&SpeechRecognizer::StartInternal,
-                            base::Unretained(this), config, sample_rate));
-}
-
-void SpeechRecognizer::Stop() {
-  // Called by the speech recognition manager thread.
-  thread_.message_loop()->PostTask(
-      FROM_HERE,
-      base::Bind(&SpeechRecognizer::StopInternal, base::Unretained(this)));
-}
-
-void SpeechRecognizer::RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus,
-                                      bool is_last_chunk) {
-  // Called by the speech recognition manager thread.
-  thread_.message_loop()->PostTask(
-      FROM_HERE, base::Bind(&SpeechRecognizer::UploadAudioDataInternal,
-                            base::Unretained(this), base::Passed(&audio_bus),
-                            is_last_chunk));
-}
-
-void SpeechRecognizer::OnURLFetchDownloadData(
-    const net::URLFetcher* source, scoped_ptr<std::string> download_data) {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
-  const net::URLRequestStatus& status = source->GetStatus();
-  const int response_code = source->GetResponseCode();
-
-  if (source == downstream_fetcher_.get()) {
-    if (status.is_success() && IsResponseCodeSuccess(response_code)) {
-      chunked_byte_buffer_.Append(*download_data);
-      while (chunked_byte_buffer_.HasChunks()) {
-        scoped_ptr<std::vector<uint8_t> > chunk =
-            chunked_byte_buffer_.PopChunk().Pass();
-
-        proto::SpeechRecognitionEvent event;
-        if (!event.ParseFromString(std::string(chunk->begin(), chunk->end()))) {
-          DLOG(WARNING) << "Parse proto string error.";
-          return;
-        }
-
-        if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
-          ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
-        } else {
-          ProcessAndFireErrorEvent(event, event_callback_);
-        }
-      }
-    } else {
-      event_callback_.Run(new SpeechRecognitionError(
-          SpeechRecognitionError::kNetwork, "Network response failure."));
-    }
-  }
-}
-
-void SpeechRecognizer::OnURLFetchComplete(const net::URLFetcher* source) {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-  UNREFERENCED_PARAMETER(source);
-  // no-op.
-}
-
-void SpeechRecognizer::StartInternal(const SpeechRecognitionConfig& config,
-                                     int sample_rate) {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
-  if (started_) {
-    // Recognizer is already started.
-    return;
-  }
-  started_ = true;
-
-  encoder_.reset(new AudioEncoderFlac(sample_rate));
-
-  // Required for streaming on both up and down connections.
-  std::string pair = base::Uint64ToString(base::RandUint64());
-
-  // Set up down stream first.
-  GURL down_url(kBaseStreamURL);
-  down_url = AppendPath(down_url, kDown);
-  down_url = AppendQueryParameter(down_url, "pair", pair);
-  // Use protobuffer as the output format.
-  down_url = AppendQueryParameter(down_url, "output", "pb");
-
-  downstream_fetcher_ =
-      fetcher_creator_.Run(down_url, net::URLFetcher::GET, this);
-  downstream_fetcher_->SetRequestContext(
-      network_module_->url_request_context_getter());
-  downstream_fetcher_->Start();
-
-  // Up stream.
-  GURL up_url(kBaseStreamURL);
-  up_url = AppendPath(up_url, kUp);
-  up_url = AppendQueryParameter(up_url, "client", kClient);
-  up_url = AppendQueryParameter(up_url, "pair", pair);
-  up_url = AppendQueryParameter(up_url, "output", "pb");
-
-  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.
-  if (!config.lang.empty()) {
-    up_url = AppendQueryParameter(up_url, "lang", config.lang);
-  } else {
-    up_url = AppendQueryParameter(up_url, "lang", base::GetSystemLanguage());
-  }
-
-  if (config.max_alternatives) {
-    up_url = AppendQueryParameter(up_url, "maxAlternatives",
-                                  base::UintToString(config.max_alternatives));
-  }
-
-  if (config.continuous) {
-    up_url = AppendQueryParameter(up_url, "continuous", "");
-  }
-  if (config.interim_results) {
-    up_url = AppendQueryParameter(up_url, "interim", "");
-  }
-
-  upstream_fetcher_ = fetcher_creator_.Run(up_url, net::URLFetcher::POST, this);
-  upstream_fetcher_->SetRequestContext(
-      network_module_->url_request_context_getter());
-  upstream_fetcher_->SetChunkedUpload(encoder_->GetMimeType());
-  upstream_fetcher_->Start();
-}
-
-void SpeechRecognizer::StopInternal() {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
-  if (!started_) {
-    // Recognizer is not started.
-    return;
-  }
-  started_ = false;
-
-  upstream_fetcher_.reset();
-  downstream_fetcher_.reset();
-  encoder_.reset();
-
-  // Clear the final results.
-  final_results_.clear();
-  // Clear any remaining audio data.
-  chunked_byte_buffer_.Clear();
-}
-
-void SpeechRecognizer::UploadAudioDataInternal(
-    scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-  DCHECK(audio_bus);
-
-  std::string encoded_audio_data;
-  if (encoder_) {
-    encoder_->Encode(audio_bus.get());
-    if (is_last_chunk) {
-      encoder_->Finish();
-    }
-    encoded_audio_data = encoder_->GetAndClearAvailableEncodedData();
-  }
-
-  if (upstream_fetcher_ && !encoded_audio_data.empty()) {
-    upstream_fetcher_->AppendChunkToUpload(encoded_audio_data, is_last_chunk);
-  }
-}
-
-void SpeechRecognizer::ProcessAndFireSuccessEvent(
-    const SpeechRecognitionResults& new_results) {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
-  SpeechRecognitionResults success_results;
-  size_t total_size = final_results_.size() + new_results.size();
-  success_results.reserve(total_size);
-  success_results = final_results_;
-  success_results.insert(success_results.end(), new_results.begin(),
-                         new_results.end());
-
-  size_t result_index = final_results_.size();
-  // Update final results list.
-  for (size_t i = 0; i < new_results.size(); ++i) {
-    if (new_results[i]->is_final()) {
-      final_results_.push_back(new_results[i]);
-    }
-  }
-
-  scoped_refptr<SpeechRecognitionResultList> recognition_list(
-      new SpeechRecognitionResultList(success_results));
-  scoped_refptr<SpeechRecognitionEvent> recognition_event(
-      new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
-                                 static_cast<uint32>(result_index),
-                                 recognition_list));
-  event_callback_.Run(recognition_event);
+void SpeechRecognizer::RunEventCallback(
+    const scoped_refptr<dom::Event>& event) {
+  event_callback_.Run(event);
 }
 
 }  // namespace speech
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h
index 339ed66..0622b7c 100644
--- a/src/cobalt/speech/speech_recognizer.h
+++ b/src/cobalt/speech/speech_recognizer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,97 +15,32 @@
 #ifndef COBALT_SPEECH_SPEECH_RECOGNIZER_H_
 #define COBALT_SPEECH_SPEECH_RECOGNIZER_H_
 
-#include <string>
-#include <vector>
-
-#include "base/threading/thread.h"
-#include "cobalt/network/network_module.h"
-#include "cobalt/speech/audio_encoder_flac.h"
+#include "base/callback.h"
+#include "cobalt/dom/event.h"
 #include "cobalt/speech/speech_recognition_config.h"
-#include "cobalt/speech/speech_recognition_event.h"
-#include "content/browser/speech/chunked_byte_buffer.h"
-#if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_audio_bus.h"
-#else  // defined(COBALT_MEDIA_SOURCE_2016)
-#include "media/base/shell_audio_bus.h"
-#endif  // defined(COBALT_MEDIA_SOURCE_2016)
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
 
 namespace cobalt {
 namespace speech {
 
-// Interacts with Google speech recognition service, and then parses recognition
-// results and forms speech recognition event.
-// It creates an upstream fetcher to upload the encoded audio and a downstream
-// fetcher to fetch the speech recognition result. The fetched speech
-// recognition result is parsed by JSON parser and a SpeechRecognitionEvent,
-// is formed based on the parsed result, would be sent to speech recognition
-// manager.
-class SpeechRecognizer : public net::URLFetcherDelegate {
+// An abstract class representing a speech recognizer and it is for a family of
+// classes to run event callback.
+class SpeechRecognizer {
  public:
-#if defined(COBALT_MEDIA_SOURCE_2016)
-  typedef media::ShellAudioBus ShellAudioBus;
-#else   // defined(COBALT_MEDIA_SOURCE_2016)
-  typedef ::media::ShellAudioBus ShellAudioBus;
-#endif  // defined(COBALT_MEDIA_SOURCE_2016)
   typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
-  typedef SpeechRecognitionResultList::SpeechRecognitionResults
-      SpeechRecognitionResults;
-  typedef base::Callback<scoped_ptr<net::URLFetcher>(
-      const GURL&, net::URLFetcher::RequestType, net::URLFetcherDelegate*)>
-      URLFetcherCreator;
 
-  SpeechRecognizer(network::NetworkModule* network_module,
-                   const EventCallback& event_callback,
-                   const URLFetcherCreator& fetcher_creator);
-  ~SpeechRecognizer() OVERRIDE;
+  explicit SpeechRecognizer(const EventCallback& event_callback);
 
-  // Multiple calls to Start/Stop are allowed, the implementation should take
-  // care of multiple calls.
-  // Start speech recognizer.
-  void Start(const SpeechRecognitionConfig& config, int sample_rate);
-  // Stop speech recognizer.
-  void Stop();
-  // An encoded audio data is available and ready to be recognized.
-  void RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk);
+  virtual ~SpeechRecognizer() {}
 
-  // net::URLFetcherDelegate interface
-  void OnURLFetchDownloadData(const net::URLFetcher* source,
-                              scoped_ptr<std::string> download_data) OVERRIDE;
-  void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
-  bool ShouldSendDownloadData() OVERRIDE { return true; }
-  void OnURLFetchUploadProgress(const net::URLFetcher* /*source*/,
-                                int64 /*current*/, int64 /*total*/) OVERRIDE {}
+  virtual void Start(const SpeechRecognitionConfig& config) = 0;
+  virtual void Stop() = 0;
+
+ protected:
+  void RunEventCallback(const scoped_refptr<dom::Event>& event);
 
  private:
-  void StartInternal(const SpeechRecognitionConfig& config, int sample_rate);
-  void StopInternal();
-  void UploadAudioDataInternal(scoped_ptr<ShellAudioBus> audio_bus,
-                               bool is_last_chunk);
-  void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
-
-  // This is used for creating fetchers.
-  network::NetworkModule* network_module_;
-  // Track the start/stop state of speech recognizer.
-  bool started_;
-
-  // Encoder for encoding raw audio data to flac codec.
-  scoped_ptr<AudioEncoderFlac> encoder_;
-  // Fetcher for posting the audio data.
-  scoped_ptr<net::URLFetcher> upstream_fetcher_;
-  // Fetcher for receiving the streaming results.
-  scoped_ptr<net::URLFetcher> downstream_fetcher_;
-  // Used to send speech recognition event.
-  const EventCallback event_callback_;
-  // Used to create url fetcher.
-  const URLFetcherCreator fetcher_creator_;
-  // Used for processing proto buffer data.
-  content::ChunkedByteBuffer chunked_byte_buffer_;
-  // Used for accumulating final results.
-  SpeechRecognitionResults final_results_;
-  // Speech recognizer is operating in its own thread.
-  base::Thread thread_;
+  // Callback for sending dom events if available.
+  EventCallback event_callback_;
 };
 
 }  // namespace speech
diff --git a/src/cobalt/speech/speech_synthesis.cc b/src/cobalt/speech/speech_synthesis.cc
index ca0573f..3fd8db6 100644
--- a/src/cobalt/speech/speech_synthesis.cc
+++ b/src/cobalt/speech/speech_synthesis.cc
@@ -65,7 +65,7 @@
 
 void SpeechSynthesis::DispatchErrorEvent(
     const scoped_refptr<SpeechSynthesisUtterance>& utterance,
-    SpeechSynthesisErrorEvent::SpeechErrorCode error_code) {
+    SpeechSynthesisErrorCode error_code) {
   utterance->DispatchErrorEvent(error_code);
   Cancel();
 }
@@ -88,16 +88,16 @@
   if (!utterance->lang().empty() &&
       utterance->lang() != navigator_->language()) {
     DispatchErrorEvent(utterance,
-                       SpeechSynthesisErrorEvent::kLanguageUnavailable);
+                       kSpeechSynthesisErrorCodeLanguageUnavailable);
     return;
   }
   if ((utterance->volume() != 1.0f) || (utterance->rate() != 1.0f) ||
       (utterance->pitch() != 1.0f)) {
-    DispatchErrorEvent(utterance, SpeechSynthesisErrorEvent::kInvalidArgument);
+    DispatchErrorEvent(utterance, kSpeechSynthesisErrorCodeInvalidArgument);
     return;
   }
 
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
   // DEPRECATED IN API VERSION 4
   std::string language =
       utterance->lang().empty() ? navigator_->language() : utterance->lang();
@@ -109,8 +109,7 @@
   utterance->DispatchStartEvent();
   utterance->DispatchEndEvent();
 #else
-  DispatchErrorEvent(utterance,
-                     SpeechSynthesisErrorEvent::kSynthesisUnavailable);
+  DispatchErrorEvent(utterance, kSpeechSynthesisErrorCodeSynthesisUnavailable);
 #endif
 }
 
diff --git a/src/cobalt/speech/speech_synthesis.h b/src/cobalt/speech/speech_synthesis.h
index e067d36..7fca443 100644
--- a/src/cobalt/speech/speech_synthesis.h
+++ b/src/cobalt/speech/speech_synthesis.h
@@ -72,7 +72,7 @@
 
   void DispatchErrorEvent(
       const scoped_refptr<SpeechSynthesisUtterance>& utterance,
-      SpeechSynthesisErrorEvent::SpeechErrorCode error_code);
+      SpeechSynthesisErrorCode error_code);
 
   bool paused_;
   typedef std::list<scoped_refptr<SpeechSynthesisUtterance> > UtterancesList;
diff --git a/src/cobalt/speech/speech_synthesis_error_code.idl b/src/cobalt/speech/speech_synthesis_error_code.idl
new file mode 100644
index 0000000..ecee5e3
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_error_code.idl
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+
+enum SpeechSynthesisErrorCode {
+  "canceled",
+  "interrupted",
+  "audio-busy",
+  "audio-hardware",
+  "network",
+  "synthesis-unavailable",
+  "synthesis-failed",
+  "language-unavailable",
+  "voice-unavailable",
+  "text-too-long",
+  "invalid-argument",
+};
diff --git a/src/cobalt/speech/speech_synthesis_error_event.h b/src/cobalt/speech/speech_synthesis_error_event.h
index 0e60750..052b018 100644
--- a/src/cobalt/speech/speech_synthesis_error_event.h
+++ b/src/cobalt/speech/speech_synthesis_error_event.h
@@ -18,6 +18,7 @@
 #include "base/basictypes.h"
 #include "cobalt/dom/event_target.h"
 #include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_synthesis_error_code.h"
 #include "cobalt/speech/speech_synthesis_event.h"
 
 namespace cobalt {
@@ -28,34 +29,20 @@
 //   https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
 class SpeechSynthesisErrorEvent : public SpeechSynthesisEvent {
  public:
-  enum SpeechErrorCode {
-    kCanceled,
-    kInterrupted,
-    kAudioBusy,
-    kAudioHardware,
-    kNetwork,
-    kSynthesisUnavailable,
-    kSynthesisFailed,
-    kLanguageUnavailable,
-    kVoiceUnavailable,
-    kTextTooLong,
-    kInvalidArgument
-  };
-
   explicit SpeechSynthesisErrorEvent(
-      SpeechErrorCode error_code,
+      SpeechSynthesisErrorCode error_code,
       const scoped_refptr<SpeechSynthesisUtterance>& utterance)
       : SpeechSynthesisEvent(base::Tokens::error(), utterance),
         error_code_(error_code) {}
 
-  SpeechErrorCode error() const { return error_code_; }
+  SpeechSynthesisErrorCode error() const { return error_code_; }
 
   DEFINE_WRAPPABLE_TYPE(SpeechSynthesisErrorEvent);
 
  private:
   ~SpeechSynthesisErrorEvent() OVERRIDE {}
 
-  const SpeechErrorCode error_code_;
+  const SpeechSynthesisErrorCode error_code_;
 
   DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisErrorEvent);
 };
diff --git a/src/cobalt/speech/speech_synthesis_error_event.idl b/src/cobalt/speech/speech_synthesis_error_event.idl
index 8cd554f..ff30d2f 100644
--- a/src/cobalt/speech/speech_synthesis_error_event.idl
+++ b/src/cobalt/speech/speech_synthesis_error_event.idl
@@ -14,20 +14,6 @@
 
 // https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
 
-enum SpeechErrorCode {
-  "canceled",
-  "interrupted",
-  "audio-busy",
-  "audio-hardware",
-  "network",
-  "synthesis-unavailable",
-  "synthesis-failed",
-  "language-unavailable",
-  "voice-unavailable",
-  "text-too-long",
-  "invalid-argument",
-};
-
 interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent {
-  readonly attribute SpeechErrorCode error;
+  readonly attribute SpeechSynthesisErrorCode error;
 };
diff --git a/src/cobalt/speech/speech_synthesis_utterance.cc b/src/cobalt/speech/speech_synthesis_utterance.cc
index ca96359..e6d67f5 100644
--- a/src/cobalt/speech/speech_synthesis_utterance.cc
+++ b/src/cobalt/speech/speech_synthesis_utterance.cc
@@ -55,7 +55,7 @@
 void SpeechSynthesisUtterance::DispatchErrorCancelledEvent() {
   if (pending_speak_) {
     SB_DLOG(INFO) << "Utterance has a pending Speak()";
-    DispatchErrorEvent(SpeechSynthesisErrorEvent::kCanceled);
+    DispatchErrorEvent(kSpeechSynthesisErrorCodeCanceled);
   }
 }
 
@@ -71,7 +71,7 @@
 }
 
 void SpeechSynthesisUtterance::DispatchErrorEvent(
-    SpeechSynthesisErrorEvent::SpeechErrorCode error_code) {
+    SpeechSynthesisErrorCode error_code) {
   pending_speak_ = false;
   DispatchEvent(new SpeechSynthesisErrorEvent(error_code, this));
 }
diff --git a/src/cobalt/speech/speech_synthesis_utterance.h b/src/cobalt/speech/speech_synthesis_utterance.h
index 9c476c3..8674e76 100644
--- a/src/cobalt/speech/speech_synthesis_utterance.h
+++ b/src/cobalt/speech/speech_synthesis_utterance.h
@@ -123,8 +123,7 @@
 
   void DispatchStartEvent();
   void DispatchEndEvent();
-  void DispatchErrorEvent(
-      SpeechSynthesisErrorEvent::SpeechErrorCode error_code);
+  void DispatchErrorEvent(SpeechSynthesisErrorCode error_code);
 
   DEFINE_WRAPPABLE_TYPE(SpeechSynthesisUtterance);
 
diff --git a/src/cobalt/speech/speech_synthesis_voice.idl b/src/cobalt/speech/speech_synthesis_voice.idl
index 472552e..915e959 100644
--- a/src/cobalt/speech/speech_synthesis_voice.idl
+++ b/src/cobalt/speech/speech_synthesis_voice.idl
@@ -20,7 +20,5 @@
   readonly attribute DOMString lang;
   readonly attribute boolean localService;
 
-  // Not supported by IDL compiler: Results in a member using a reserved
-  // keyword, IDL compiler doesn't support ImplementedAs.
-  // [ImplementedAs=attribute_default] readonly attribute boolean default;
+  [ImplementedAs=attribute_default] readonly attribute boolean default;
 };
diff --git a/src/cobalt/speech/starboard_speech_recognizer.cc b/src/cobalt/speech/starboard_speech_recognizer.cc
new file mode 100644
index 0000000..14fdfd5
--- /dev/null
+++ b/src/cobalt/speech/starboard_speech_recognizer.cc
@@ -0,0 +1,183 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/starboard_speech_recognizer.h"
+
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+
+#include "cobalt/base/tokens.h"
+#include "cobalt/speech/speech_recognition_error.h"
+#include "cobalt/speech/speech_recognition_event.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+
+using cobalt::speech::StarboardSpeechRecognizer;
+
+void OnSpeechDetected(void* context, bool detected) {
+  StarboardSpeechRecognizer* recognizer =
+      static_cast<StarboardSpeechRecognizer*>(context);
+  recognizer->OnRecognizerSpeechDetected(detected);
+}
+
+void OnError(void* context, SbSpeechRecognizerError error) {
+  StarboardSpeechRecognizer* recognizer =
+      static_cast<StarboardSpeechRecognizer*>(context);
+  recognizer->OnRecognizerError(error);
+}
+
+void OnResults(void* context, SbSpeechResult* results, int results_size,
+               bool is_final) {
+  StarboardSpeechRecognizer* recognizer =
+      static_cast<StarboardSpeechRecognizer*>(context);
+  recognizer->OnRecognizerResults(results, results_size, is_final);
+}
+
+}  // namespace
+
+StarboardSpeechRecognizer::StarboardSpeechRecognizer(
+    const EventCallback& event_callback)
+    : SpeechRecognizer(event_callback) {
+  SbSpeechRecognizerHandler handler = {&OnSpeechDetected, &OnError, &OnResults,
+                                       this};
+  speech_recognizer_ = SbSpeechRecognizerCreate(&handler);
+
+  if (!SbSpeechRecognizerIsValid(speech_recognizer_)) {
+    scoped_refptr<dom::Event> error_event(new SpeechRecognitionError(
+        kSpeechRecognitionErrorCodeServiceNotAllowed, ""));
+    RunEventCallback(error_event);
+  }
+}
+
+StarboardSpeechRecognizer::~StarboardSpeechRecognizer() {
+  if (SbSpeechRecognizerIsValid(speech_recognizer_)) {
+    SbSpeechRecognizerDestroy(speech_recognizer_);
+  }
+}
+
+void StarboardSpeechRecognizer::Start(const SpeechRecognitionConfig& config) {
+  SbSpeechConfiguration configuration = {
+      config.continuous, config.interim_results, config.max_alternatives};
+  if (SbSpeechRecognizerIsValid(speech_recognizer_)) {
+    SbSpeechRecognizerStart(speech_recognizer_, &configuration);
+  }
+}
+
+void StarboardSpeechRecognizer::Stop() {
+  if (SbSpeechRecognizerIsValid(speech_recognizer_)) {
+    SbSpeechRecognizerStop(speech_recognizer_);
+  }
+  // Clear the final results.
+  final_results_.clear();
+}
+
+void StarboardSpeechRecognizer::OnRecognizerSpeechDetected(bool detected) {
+  scoped_refptr<dom::Event> event(new dom::Event(
+      detected ? base::Tokens::soundstart() : base::Tokens::soundend()));
+  RunEventCallback(event);
+}
+
+void StarboardSpeechRecognizer::OnRecognizerError(
+    SbSpeechRecognizerError error) {
+  scoped_refptr<dom::Event> error_event;
+  switch (error) {
+    case kSbNoSpeechError:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeNoSpeech, "");
+      break;
+    case kSbAborted:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeAborted, "");
+      break;
+    case kSbAudioCaptureError:
+      error_event = new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeAudioCapture, "");
+      break;
+    case kSbNetworkError:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeNetwork, "");
+      break;
+    case kSbNotAllowed:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeNotAllowed, "");
+      break;
+    case kSbServiceNotAllowed:
+      error_event = new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeServiceNotAllowed, "");
+      break;
+    case kSbBadGrammar:
+      error_event =
+          new SpeechRecognitionError(kSpeechRecognitionErrorCodeBadGrammar, "");
+      break;
+    case kSbLanguageNotSupported:
+      error_event = new SpeechRecognitionError(
+          kSpeechRecognitionErrorCodeLanguageNotSupported, "");
+      break;
+  }
+  SB_DCHECK(error_event);
+  RunEventCallback(error_event);
+}
+
+void StarboardSpeechRecognizer::OnRecognizerResults(SbSpeechResult* results,
+                                                    int results_size,
+                                                    bool is_final) {
+  SpeechRecognitionResultList::SpeechRecognitionResults recognition_results;
+  SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
+  for (int i = 0; i < results_size; ++i) {
+    scoped_refptr<SpeechRecognitionAlternative> alternative(
+        new SpeechRecognitionAlternative(results[i].transcript,
+                                         results[i].confidence));
+    alternatives.push_back(alternative);
+    // Platform implementation allocates memory of |transcript|.
+    SbMemoryDeallocate(results[i].transcript);
+  }
+  scoped_refptr<SpeechRecognitionResult> recognition_result(
+      new SpeechRecognitionResult(alternatives, is_final));
+  recognition_results.push_back(recognition_result);
+
+  // Gather all results for the SpeechRecognitionEvent, including all final
+  // results we've previously accumulated, plus all (final or not) results that
+  // we just received.
+  SpeechRecognitionResults success_results;
+  size_t total_size = final_results_.size() + recognition_results.size();
+  success_results.reserve(total_size);
+  success_results = final_results_;
+  success_results.insert(success_results.end(), recognition_results.begin(),
+                         recognition_results.end());
+
+  size_t result_index = final_results_.size();
+  // Update final results list with final results that we just received, so we
+  // have them for the next event.
+  for (size_t i = 0; i < recognition_results.size(); ++i) {
+    if (recognition_results[i]->is_final()) {
+      final_results_.push_back(recognition_results[i]);
+    }
+  }
+
+  scoped_refptr<SpeechRecognitionResultList> recognition_list(
+      new SpeechRecognitionResultList(success_results));
+  scoped_refptr<SpeechRecognitionEvent> recognition_event(
+      new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
+                                 static_cast<uint32>(result_index),
+                                 recognition_list));
+  RunEventCallback(recognition_event);
+}
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // defined(SB_USE_SB_SPEECH_RECOGNIZER)
diff --git a/src/cobalt/speech/starboard_speech_recognizer.h b/src/cobalt/speech/starboard_speech_recognizer.h
new file mode 100644
index 0000000..d7a8281
--- /dev/null
+++ b/src/cobalt/speech/starboard_speech_recognizer.h
@@ -0,0 +1,57 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_STARBOARD_SPEECH_RECOGNIZER_H_
+#define COBALT_SPEECH_STARBOARD_SPEECH_RECOGNIZER_H_
+
+#include "cobalt/speech/speech_configuration.h"
+#include "cobalt/speech/speech_recognition_result_list.h"
+#include "cobalt/speech/speech_recognizer.h"
+#include "starboard/speech_recognizer.h"
+
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+
+namespace cobalt {
+namespace speech {
+
+// Controls |SbSpeechRecognizer| to access the device's recognition service and
+// receives the speech recognition results from there.
+class StarboardSpeechRecognizer : public SpeechRecognizer {
+ public:
+  typedef SpeechRecognitionResultList::SpeechRecognitionResults
+      SpeechRecognitionResults;
+
+  explicit StarboardSpeechRecognizer(const EventCallback& event_callback);
+  ~StarboardSpeechRecognizer();
+
+  void Start(const SpeechRecognitionConfig& config) SB_OVERRIDE;
+  void Stop() SB_OVERRIDE;
+
+  void OnRecognizerSpeechDetected(bool detected);
+  void OnRecognizerError(SbSpeechRecognizerError error);
+  void OnRecognizerResults(SbSpeechResult* results, int results_size,
+                           bool is_final);
+
+ private:
+  SbSpeechRecognizer speech_recognizer_;
+  // Used for accumulating final results.
+  SpeechRecognitionResults final_results_;
+};
+
+}  // namespace speech
+}  // namespace cobalt
+
+#endif  // defined(SB_USE_SB_SPEECH_RECOGNIZER)
+
+#endif  // COBALT_SPEECH_STARBOARD_SPEECH_RECOGNIZER_H_
diff --git a/src/cobalt/storage/savegame_thread.cc b/src/cobalt/storage/savegame_thread.cc
index 517ad4b..e659ec7 100644
--- a/src/cobalt/storage/savegame_thread.cc
+++ b/src/cobalt/storage/savegame_thread.cc
@@ -63,6 +63,9 @@
     // because we won't be able to reload it. Something must have
     // gone wrong, and it's far better to leave any existing readable
     // data that's persisted than it is to risk making it unreadable.
+    if (!on_flush_complete.is_null()) {
+      on_flush_complete.Run();
+    }
     return;
   }
   thread_->message_loop()->PostTask(
@@ -107,7 +110,6 @@
       const int kMaxConsecutiveFlushFailures = 2;
       DCHECK_LT(++num_consecutive_flush_failures_,
                 kMaxConsecutiveFlushFailures);
-      return;
     }
   }
 
diff --git a/src/cobalt/storage/upgrade/storage_upgrade_test.cc b/src/cobalt/storage/upgrade/storage_upgrade_test.cc
index efc22e3..56710a2 100644
--- a/src/cobalt/storage/upgrade/storage_upgrade_test.cc
+++ b/src/cobalt/storage/upgrade/storage_upgrade_test.cc
@@ -32,6 +32,13 @@
 
 namespace {
 
+using ::testing::StrictMock;
+
+class MockUpgradeReader : public UpgradeReader {
+ public:
+  MOCK_METHOD0(OnNullExpiration, void());
+};
+
 void ReadFileToString(const char* pathname, std::string* string_out) {
   EXPECT_TRUE(pathname);
   EXPECT_TRUE(string_out);
@@ -71,7 +78,7 @@
   const std::string domain = host.empty() ? "" : host.substr(host.find("."));
   const std::string path = "/";
   const base::Time creation = base::Time::Now();
-  const base::Time expiration = creation;
+  const base::Time expiration = base::Time();
   const bool http_only = false;
   ValidateCookie(cookie, url, name, value, domain, path, creation, expiration,
                  http_only);
@@ -91,8 +98,13 @@
   std::string file_contents;
   ReadFileToString("cobalt/storage/upgrade/testdata/minimal_cookie_v1.json",
                    &file_contents);
-  UpgradeReader upgrade_reader(file_contents.c_str(),
-                               static_cast<int>(file_contents.length()));
+  StrictMock<MockUpgradeReader> upgrade_reader;
+
+  // No expiration
+  EXPECT_CALL(upgrade_reader, OnNullExpiration());
+
+  upgrade_reader.Parse(file_contents.c_str(),
+                       static_cast<int>(file_contents.length()));
 
   // 1 cookie.
   EXPECT_EQ(upgrade_reader.GetNumCookies(), 1);
@@ -111,8 +123,9 @@
   ReadFileToString(
       "cobalt/storage/upgrade/testdata/minimal_local_storage_entry_v1.json",
       &file_contents);
-  UpgradeReader upgrade_reader(file_contents.c_str(),
-                               static_cast<int>(file_contents.length()));
+  UpgradeReader upgrade_reader;
+  upgrade_reader.Parse(file_contents.c_str(),
+                       static_cast<int>(file_contents.length()));
 
   // 0 cookies.
   EXPECT_EQ(upgrade_reader.GetNumCookies(), 0);
@@ -130,8 +143,9 @@
   std::string file_contents;
   ReadFileToString("cobalt/storage/upgrade/testdata/full_data_v1.json",
                    &file_contents);
-  UpgradeReader upgrade_reader(file_contents.c_str(),
-                               static_cast<int>(file_contents.length()));
+  UpgradeReader upgrade_reader;
+  upgrade_reader.Parse(file_contents.c_str(),
+                       static_cast<int>(file_contents.length()));
 
   // 2 cookies.
   EXPECT_EQ(upgrade_reader.GetNumCookies(), 2);
@@ -163,8 +177,9 @@
   std::string file_contents;
   ReadFileToString("cobalt/storage/upgrade/testdata/missing_fields_v1.json",
                    &file_contents);
-  UpgradeReader upgrade_reader(file_contents.c_str(),
-                               static_cast<int>(file_contents.length()));
+  UpgradeReader upgrade_reader;
+  upgrade_reader.Parse(file_contents.c_str(),
+                       static_cast<int>(file_contents.length()));
 
   // 1 cookie with missing fields, 2 local storage entries with missing fields,
   // 1 valid local storage entry.
@@ -181,8 +196,9 @@
   std::string file_contents;
   ReadFileToString("cobalt/storage/upgrade/testdata/malformed_v1.json",
                    &file_contents);
-  UpgradeReader upgrade_reader(file_contents.c_str(),
-                               static_cast<int>(file_contents.length()));
+  UpgradeReader upgrade_reader;
+  upgrade_reader.Parse(file_contents.c_str(),
+                       static_cast<int>(file_contents.length()));
 
   // No cookies or local storage entries available in malformed data.
   EXPECT_EQ(upgrade_reader.GetNumCookies(), 0);
@@ -195,8 +211,13 @@
   std::string file_contents;
   ReadFileToString("cobalt/storage/upgrade/testdata/extra_fields_v1.json",
                    &file_contents);
-  UpgradeReader upgrade_reader(file_contents.c_str(),
-                               static_cast<int>(file_contents.length()));
+  StrictMock<MockUpgradeReader> upgrade_reader;
+
+  // No expiration
+  EXPECT_CALL(upgrade_reader, OnNullExpiration());
+
+  upgrade_reader.Parse(file_contents.c_str(),
+                       static_cast<int>(file_contents.length()));
 
   // 1 cookie, extra fields should be ignored.
   EXPECT_EQ(upgrade_reader.GetNumCookies(), 1);
diff --git a/src/cobalt/storage/upgrade/upgrade_reader.cc b/src/cobalt/storage/upgrade/upgrade_reader.cc
index 25ceff4..6e5908a 100644
--- a/src/cobalt/storage/upgrade/upgrade_reader.cc
+++ b/src/cobalt/storage/upgrade/upgrade_reader.cc
@@ -16,6 +16,7 @@
 
 #include <string>
 
+#include "base/i18n/time_formatting.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
@@ -36,7 +37,8 @@
 const int kMaxUpgradeDataSize = 10 * 1024 * 1024;
 
 // Deseralize a time value encoded as a possibly empty ASCII decimal string.
-base::Time StringToTime(const std::string& time_as_string) {
+base::Time StringToTime(const std::string& time_as_string,
+                        const base::Time& default_time) {
   if (!time_as_string.empty()) {
     int64 time_as_int64;
     if (base::StringToInt64(time_as_string, &time_as_int64)) {
@@ -44,15 +46,15 @@
     }
   }
 
-  // Default value.
-  return base::Time::Now();
+  return default_time;
 }
 
 base::Time GetSerializedTime(const base::DictionaryValue* dictionary,
-                             const std::string& field_name) {
+                             const std::string& field_name,
+                             const base::Time& default_time) {
   std::string time_string;
   dictionary->GetString(field_name, &time_string);
-  return StringToTime(time_string);
+  return StringToTime(time_string, default_time);
 }
 
 // Get a list contained in a dictionary.
@@ -104,7 +106,7 @@
 
 }  // namespace
 
-UpgradeReader::UpgradeReader(const char* data, int size) {
+void UpgradeReader::Parse(const char* data, int size) {
   DCHECK(data);
   DCHECK_GE(size, kHeaderSize);
   DCHECK_LE(size, kMaxUpgradeDataSize);
@@ -168,9 +170,12 @@
   cookie->GetString("domain", &domain);
   std::string path = "/";
   cookie->GetString("path", &path);
-  base::Time creation = GetSerializedTime(cookie, "creation");
-  base::Time expiration = GetSerializedTime(cookie, "expiration");
-  base::Time last_access = GetSerializedTime(cookie, "last_access");
+  base::Time creation =
+      GetSerializedTime(cookie, "creation", base::Time::Now());
+  base::Time expiration =
+      GetSerializedTime(cookie, "expiration", base::Time());
+  base::Time last_access =
+      GetSerializedTime(cookie, "last_access", base::Time::Now());
   bool http_only = false;
   cookie->GetBoolean("http_only", &http_only);
 
@@ -179,6 +184,15 @@
   const std::string mac_algorithm;
   const bool secure = true;
 
+  if (expiration.is_null()) {
+    LOG(ERROR) << "\"" << name
+               << "\" cookie can not be upgraded due to null expiration.";
+    OnNullExpiration();
+  }
+  LOG_IF(ERROR, expiration < base::Time::UnixEpoch())
+      << "\"" << name << "\" upgrade cookie expiration is "
+      << base::TimeFormatFriendlyDateAndTime(expiration);
+
   cookies_.push_back(net::CanonicalCookie(
       gurl, name, value, domain, path, mac_key, mac_algorithm, creation,
       expiration, last_access, secure, http_only));
diff --git a/src/cobalt/storage/upgrade/upgrade_reader.h b/src/cobalt/storage/upgrade/upgrade_reader.h
index f59c7ba..a9961ad 100644
--- a/src/cobalt/storage/upgrade/upgrade_reader.h
+++ b/src/cobalt/storage/upgrade/upgrade_reader.h
@@ -18,6 +18,7 @@
 #include <string>
 #include <vector>
 
+#include "base/logging.h"
 #include "base/values.h"
 #include "net/cookies/canonical_cookie.h"
 
@@ -35,8 +36,11 @@
     std::string value;
   };
 
+  UpgradeReader() {}
+  virtual ~UpgradeReader() {}
+
   // Parse |data| in JSON-encoded legacy save data upgrade format.
-  UpgradeReader(const char* data, int size);
+  void Parse(const char* data, int size);
 
   // The number of valid cookies found in the parsed data. May be zero.
   int GetNumCookies() const;
@@ -70,6 +74,12 @@
   void AddLocalStorageEntryIfValid(
       const base::DictionaryValue* local_storage_entry);
 
+  // Alerts porters that expiration is required.
+  virtual void OnNullExpiration() {
+    NOTREACHED() << "Cookies must have a specified expiration, "
+                    "as there is no reasonable default.";
+  }
+
   std::vector<net::CanonicalCookie> cookies_;
   std::vector<LocalStorageEntry> local_storage_entries_;
 };
diff --git a/src/cobalt/test/document_loader.h b/src/cobalt/test/document_loader.h
index f97f4fb..df0ecdd 100644
--- a/src/cobalt/test/document_loader.h
+++ b/src/cobalt/test/document_loader.h
@@ -48,9 +48,9 @@
         css_parser_(css_parser::Parser::Create()),
         dom_parser_(new dom_parser::Parser()),
         resource_provider_stub_(new render_tree::ResourceProviderStub()),
-        loader_factory_(new loader::LoaderFactory(
-            &fetcher_factory_, resource_provider_stub_.get(),
-            base::kThreadPriority_Low)),
+        loader_factory_(new loader::LoaderFactory(&fetcher_factory_,
+                                                  resource_provider_stub_.get(),
+                                                  base::kThreadPriority_Low)),
         image_cache_(loader::image::CreateImageCache(
             "Test.ImageCache", 32U * 1024 * 1024, loader_factory_.get())),
         dom_stat_tracker_(new dom::DomStatTracker("IsDisplayedTest")),
@@ -59,7 +59,8 @@
             &fetcher_factory_, css_parser_.get(), dom_parser_.get(),
             NULL /* can_play_type_handler  */,
             NULL /* web_media_player_factory */, &script_runner_,
-            NULL /* media_source_registry */, &resource_provider_,
+            NULL /* script_value_factory */, NULL /* media_source_registry */,
+            &resource_provider_, NULL /* animated_image_tracker */,
             image_cache_.get(), NULL /* reduced_image_cache_capacity_manager */,
             NULL /* remote_font_cache */, NULL /* mesh_cache */,
             dom_stat_tracker_.get(), "" /* language */) {}
diff --git a/src/cobalt/test/empty_document.h b/src/cobalt/test/empty_document.h
index b6ffccf..9b6ca37 100644
--- a/src/cobalt/test/empty_document.h
+++ b/src/cobalt/test/empty_document.h
@@ -31,7 +31,7 @@
       : css_parser_(css_parser::Parser::Create()),
         dom_stat_tracker_(new dom::DomStatTracker("EmptyDocument")),
         html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               dom_stat_tracker_.get(), ""),
         document_(new dom::Document(&html_element_context_)) {}
 
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 9be98af..8cd1366 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -15,6 +15,6 @@
 #define COBALT_VERSION_H_
 
 // Cobalt release number.
-#define COBALT_VERSION "9"
+#define COBALT_VERSION "10"
 
 #endif  // COBALT_VERSION_H_
diff --git a/src/cobalt/web_animations/animation_effect_timing_read_only.idl b/src/cobalt/web_animations/animation_effect_timing_read_only.idl
index 789bbe5..550f269 100644
--- a/src/cobalt/web_animations/animation_effect_timing_read_only.idl
+++ b/src/cobalt/web_animations/animation_effect_timing_read_only.idl
@@ -14,17 +14,11 @@
 
 // https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-animationeffecttimingreadonly-interface
 
-// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-fillmode-enumeration
-enum FillMode { "none", "forwards", "backwards", "both" };
-
-// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-playbackdirection-enumeration
-enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
-
 interface AnimationEffectTimingReadOnly {
   readonly attribute double delay;
   readonly attribute double endDelay;
-  readonly attribute FillMode fill;
-  readonly attribute PlaybackDirection direction;
+  readonly attribute AnimationFillMode fill;
+  readonly attribute AnimationPlaybackDirection direction;
   readonly attribute double iterationStart;
   readonly attribute unrestricted double iterations;
   readonly attribute (unrestricted double or DOMString) duration;
diff --git a/src/cobalt/base/math.cc b/src/cobalt/web_animations/animation_fill_mode.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/web_animations/animation_fill_mode.idl
index d335495..6e71b7d 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/web_animations/animation_fill_mode.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,5 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-fillmode-enumeration
+enum AnimationFillMode { "none", "forwards", "backwards", "both" };
diff --git a/src/cobalt/base/math.cc b/src/cobalt/web_animations/animation_playback_direction.idl
similarity index 69%
copy from src/cobalt/base/math.cc
copy to src/cobalt/web_animations/animation_playback_direction.idl
index d335495..00a3506 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/web_animations/animation_playback_direction.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,5 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-playbackdirection-enumeration
+enum AnimationPlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
diff --git a/src/cobalt/webdriver/algorithms.cc b/src/cobalt/webdriver/algorithms.cc
index 4d4c59e..8fbce6e 100644
--- a/src/cobalt/webdriver/algorithms.cc
+++ b/src/cobalt/webdriver/algorithms.cc
@@ -71,8 +71,9 @@
   return strchr(kZeroWidthSpacesAndFeeds, c) != NULL;
 }
 
-bool IsInHeadElement(const dom::Element* element) {
-  if (element->tag_name() == dom::HTMLHeadElement::kTagName) {
+bool IsInHeadElement(dom::Element* element) {
+  if (element->AsHTMLElement() &&
+      element->AsHTMLElement()->AsHTMLHeadElement()) {
     return true;
   }
   scoped_refptr<dom::Element> parent = element->parent_element();
@@ -215,7 +216,7 @@
 void GetElementTextInternal(dom::Element* element,
                             std::vector<std::string>* lines) {
   // If the element is a: BR element: Push '' to lines and continue.
-  if (element->tag_name() == dom::HTMLBRElement::kTagName) {
+  if (element->AsHTMLElement() && element->AsHTMLElement()->AsHTMLBRElement()) {
     lines->push_back("");
     return;
   }
@@ -437,7 +438,8 @@
   // By convention, BODY element is always shown: BODY represents the document
   // and even if there's nothing rendered in there, user can always see there's
   // the document.
-  if (element->tag_name() == dom::HTMLBodyElement::kTagName) {
+  if (element->AsHTMLElement() &&
+      element->AsHTMLElement()->AsHTMLBodyElement()) {
     return true;
   }
 
diff --git a/src/cobalt/webdriver/get_element_text_test.cc b/src/cobalt/webdriver/get_element_text_test.cc
index cecd563..29d70f3 100644
--- a/src/cobalt/webdriver/get_element_text_test.cc
+++ b/src/cobalt/webdriver/get_element_text_test.cc
@@ -41,7 +41,7 @@
       : css_parser_(css_parser::Parser::Create()),
         dom_stat_tracker_(new dom::DomStatTracker("GetElementTextTest")),
         html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               dom_stat_tracker_.get(), "") {}
 
   void SetUp() OVERRIDE {
diff --git a/src/cobalt/webdriver/testing/stub_window.h b/src/cobalt/webdriver/testing/stub_window.h
index 9a7fdca..e2751dc 100644
--- a/src/cobalt/webdriver/testing/stub_window.h
+++ b/src/cobalt/webdriver/testing/stub_window.h
@@ -26,6 +26,7 @@
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
 #include "cobalt/network/network_module.h"
 #include "googleurl/src/gurl.h"
 
@@ -47,19 +48,21 @@
         url_("about:blank"),
         dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
     engine_ = script::JavaScriptEngine::CreateEngine();
-    global_environment_ = engine_->CreateGlobalEnvironment(
-        script::JavaScriptEngine::Options());
+    global_environment_ = engine_->CreateGlobalEnvironment();
     window_ = new dom::Window(
         1920, 1080, css_parser_.get(), dom_parser_.get(),
-        fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
+        fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
         &local_storage_database_, stub_media_module_.get(),
-        stub_media_module_.get(), NULL, NULL, NULL, dom_stat_tracker_.get(),
-        url_, "", "en-US", base::Callback<void(const GURL&)>(),
-        base::Bind(&StubErrorCallback), NULL, network_bridge::PostSender(),
+        stub_media_module_.get(), NULL, NULL, NULL, NULL,
+        dom_stat_tracker_.get(), url_, "", "en-US",
+        base::Callback<void(const GURL&)>(), base::Bind(&StubErrorCallback),
+        NULL, network_bridge::PostSender(),
         std::string() /* default security policy */, dom::kCspEnforcementEnable,
         base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
-        base::Closure() /* window_close */, NULL, NULL);
+        base::Closure() /* window_close */,
+        base::Closure() /* window_minimize */,
+        NULL, NULL, NULL);
     global_environment_->CreateGlobalObject(window_, &environment_settings_);
   }
 
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index 1614fb2..daec972 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -157,7 +157,11 @@
 
 }  // namespace
 
+#if SB_HAS(IPV6)
+const char WebDriverModule::kDefaultListenIp[] = "::";
+#else
 const char WebDriverModule::kDefaultListenIp[] = "0.0.0.0";
+#endif
 
 WebDriverModule::WebDriverModule(
     int server_port, const std::string& listen_ip,
diff --git a/src/cobalt/webdriver_benchmarks/c_val_names.py b/src/cobalt/webdriver_benchmarks/c_val_names.py
index 9e2a1aa..3256e48 100644
--- a/src/cobalt/webdriver_benchmarks/c_val_names.py
+++ b/src/cobalt/webdriver_benchmarks/c_val_names.py
@@ -13,10 +13,6 @@
   return "Count.MainWebModule.ImageCache.LoadingResources"
 
 
-def count_font_files_loaded():
-  return "Count.Font.FilesLoaded"
-
-
 def event_duration_dom_video_start_delay():
   return "Event.Duration.MainWebModule.DOM.VideoStartDelay"
 
diff --git a/src/cobalt/webdriver_benchmarks/tests/README.md b/src/cobalt/webdriver_benchmarks/tests/README.md
index 032c41c..752f1a0 100644
--- a/src/cobalt/webdriver_benchmarks/tests/README.md
+++ b/src/cobalt/webdriver_benchmarks/tests/README.md
@@ -1,26 +1,38 @@
-Cobalt Webdriver-driven Benchmarks
----------------------
+# 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 included within "all.py" will be run on the
-build system. Results can be recorded in the build results database.
+All tests in all of the files included within [all.py](all.py) 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.
+## Running the tests
 
-To make a new test:
+In most cases, you will want to run all tests, and you can do so by executing
+the script [all.py](all.py).  You can call `python all.py --help` to see a list
+of commandline parameters to call it with.  For example, to run tests on the
+raspi-2 QA build, you should run the following command:
+
+```
+python all.py -p raspi-2 -c qa -d $RASPI_ADDR
+```
+
+Where `RASPI_ADDR` is set to the IP of the target Raspberry Pi device.
+
+To run individual tests, simply execute the script directly. For all tests,
+platform configuration will be inferred from the environment if set. Otherwise,
+it must be specified via commandline parameters.
+
+## Creating a new test
 
  1. If appropriate, create a new file borrowing the boilerplate from
-    an existing simple file, such as "browse_horizontal.py".
+    an existing simple file, such as
+    [browse_horizontal.py](browse_horizontal.py).
 
- 2. Add the file name to the tests added within "all.py", causing it run
-    when "all.py" is run.
+ 2. Add the file name to the tests added within [all.py](all.py), causing it run
+    when [all.py](all.py) is run.
 
  3. If this file contains internal names or details, consider adding it
     to the "EXCLUDE.FILES" list.
@@ -29,4 +41,105 @@
     appropriate.
 
  5. Results must be added to the build results database schema. See
-    the internal "README-Updating-Result-Schema.md" file
+    the internal
+    [README-Updating-Result-Schema.md](README-Updating-Result-Schema.md) file.
+
+## Testing arbitrary loaders
+
+Unfortunately we haven't yet had the time to implement an easy way to adjust
+the test URL query parameters.  In order to adjust the query parameters to test,
+you should do the following:
+
+Open [../tv_testcase_util.py](../tv_testcase_util.py) and in the function
+`get_url()`, replace the line
+
+```
+query_dict = BASE_PARAMS.copy()
+```
+
+with
+
+```
+query_dict = {}
+```
+
+and then append the following else-clause to the `if query_params:` statement,
+
+```
+else:
+  query_dict = BASE_PARAMS.copy()
+```
+
+and then finally, near the top of the file, modify `BASE_PARAMS` to include all
+query parameters you would like to test.  For example,
+
+```
+BASE_PARAMS = {"loader": "airc"}
+```
+
+## Benchmark Results
+
+The results will be printed to stdout.  You should redirect output to a file
+if you would like to store the results.  Each line of the benchmark output
+prefixed with `webdriver_benchmark TEST_RESULT:` provides the result of one
+measurment.  Those lines have the following format:
+
+```
+webdriver_benchmark TEST_RESULT: result_name result_value
+```
+
+where `result_name` is the name of the result and `result_value` is a number
+providing the measured result for that metric.  For example,
+
+```
+webdriver_benchmark TEST RESULT: wbBrowseHorizontalDurLayoutBoxTreeUpdateUsedSizesUsPct50 3061.5
+```
+
+gives the 50th percentile of the duration Cobalt took to update the box tree's
+used sizes, on a horizontal scroll event, in microseconds.
+
+Note that most time-based measurements are in microseconds.
+
+### Interesting benchmarks
+
+Some particularly interesting benchmark results are:
+
+ - `wbStartupDurBlankToBrowseUs*`: Measures the startup time, until all images
+   finish loading.
+ - `wbBrowseToWatchDurVideoStartDelay*`: Measures the browse-to-watch time.
+ - `wbBrowseVerticalDurTotalUs*`: Measures the input latency (i.e. JavaScript
+   execution time + layout time) during vertical scroll events.
+ - `wbBrowseVerticalDurRasterizeAnimationsUs*`: Measures the time it takes to
+   render each frame of the animation triggered by a vertical scroll event.
+   The inverse of this number is the framerate.
+ - `wbBrowseHorizontalDurTotalUs*`: Same as `wbBrowseVerticalDurTotalUs*` except
+   for horizontal scroll events.
+ - `wbBrowseHorizontalDurRasterizeAnimationsUs*`: Same as
+   `wbBrowseVerticalDurRasterizeAnimationsUs*` except for horizontal scroll
+   events.
+
+In each case above, the `*` symbol can be one of either `Mean`, `Pct25`,
+`Pct50`, `Pct75` or `Pct95`.  For example, `wbStartupDurBlankToBrowseUsMean` or
+`wbStartupDurBlankToBrowseUsPct95` are both valid measurements.  The
+webdriver benchmarks runs its tests many times in order to obtain multiple
+samples, so you can drill into the data by exploring either the mean, or the
+various percentiles.
+
+### Filtering results
+
+The webdriver benchmarks output many metrics, but you may only be interested
+in a few.  You will have to manually filter only the metrics that you are
+interested in.  You can do so with `grep`, for example:
+
+```
+python all.py -p raspi-2 -c qa -d $RASPI_ADDR > results.txt
+echo "" > filtered_results.txt
+grep -o "wbStartupDurBlankToBrowseUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseToWatchDurVideoStartDelay.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseVerticalDurTotalUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseVerticalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurTotalUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
+cat filtered_results.txt
+```
+
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
index 3abcd5d..cb49624 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
@@ -26,7 +26,6 @@
 
     self.record_rasterize_animations = True
     self.record_video_start_delay = False
-    self.skip_font_file_load_events = True
 
 
 class EventRecorder(object):
@@ -40,9 +39,6 @@
   Both rasterize animations and video start delay data can potentially be
   recorded, depending on the |record_rasterize_animations| and
   |record_video_start_delay| option flags.
-
-  Additionally, events containing font file loads can be skipped if the
-  |skip_font_file_load_events| option flag is set to True.
   """
 
   def __init__(self, options):
@@ -57,12 +53,8 @@
     self.test = options.test
     self.event_name = options.event_name
     self.event_type = options.event_type
-    self.skip_font_file_load_events = options.skip_font_file_load_events
-
-    self.font_files_loaded_count = None
 
     self.render_tree_failure_count = 0
-    self.font_file_load_skip_count = 0
 
     # Each entry in the list contains a tuple with a key and value recorder.
     self.value_dictionary_recorders = []
@@ -151,30 +143,11 @@
 
   def on_start_event(self):
     """Handles logic related to the start of the event instance."""
-
-    # If the recorder is set to skip events with font file loads and the font
-    # file loaded count hasn't been initialized yet, then do that now.
-    if self.skip_font_file_load_events and self.font_files_loaded_count is None:
-      self.font_files_loaded_count = self.test.get_cval(
-          c_val_names.count_font_files_loaded())
+    pass
 
   def on_end_event(self):
     """Handles logic related to the end of the event instance."""
 
-    # If the recorder is set to skip events with font file loads and a font file
-    # loaded during the event, then its data is not collected. Log that it was
-    # skipped and return.
-    if self.skip_font_file_load_events:
-      current_font_files_loaded_count = self.test.get_cval(
-          c_val_names.count_font_files_loaded())
-      if self.font_files_loaded_count != current_font_files_loaded_count:
-        self.font_file_load_skip_count += 1
-        print("{} event skipped because a font file loaded during it! {} "
-              "events skipped.".format(self.event_name,
-                                       self.font_file_load_skip_count))
-        self.font_files_loaded_count = current_font_files_loaded_count
-        return
-
     value_dictionary = self.test.get_cval(
         c_val_names.event_value_dictionary(self.event_type))
 
diff --git a/src/cobalt/websocket/buffered_amount_tracker.cc b/src/cobalt/websocket/buffered_amount_tracker.cc
new file mode 100644
index 0000000..708efc0
--- /dev/null
+++ b/src/cobalt/websocket/buffered_amount_tracker.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/websocket/buffered_amount_tracker.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace websocket {
+
+std::size_t BufferedAmountTracker::Pop(const std::size_t number_bytes_to_pop) {
+  std::size_t size_left_pop = number_bytes_to_pop;
+  std::size_t payload_amount = 0;
+  while (!entries_.empty() && (size_left_pop > 0)) {
+    Entry& entry(entries_[0]);
+
+    std::size_t potential_payload_delta = 0;
+    if (entry.message_size_ > size_left_pop) {
+      potential_payload_delta = size_left_pop;
+      entry.message_size_ -= size_left_pop;
+    } else {  // entry.message_size <= size_left
+      potential_payload_delta += entry.message_size_;
+      entries_.pop_front();
+    }
+
+    if (entry.is_user_payload_) {
+      payload_amount += potential_payload_delta;
+    }
+
+    size_left_pop -= potential_payload_delta;
+  }
+
+  // std::min prevents an underflow due to a bug.
+  DCHECK_LE(payload_amount, total_payload_inflight_);
+
+  total_payload_inflight_ -= std::min(total_payload_inflight_, payload_amount);
+
+  // Sort of an overflow check...
+  DCHECK_LE(size_left_pop, number_bytes_to_pop);
+
+  return payload_amount;
+}
+
+}  // namespace websocket
+}  // namespace cobalt
diff --git a/src/cobalt/websocket/buffered_amount_tracker.h b/src/cobalt/websocket/buffered_amount_tracker.h
new file mode 100644
index 0000000..ea35084
--- /dev/null
+++ b/src/cobalt/websocket/buffered_amount_tracker.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_WEBSOCKET_BUFFERED_AMOUNT_TRACKER_H_
+#define COBALT_WEBSOCKET_BUFFERED_AMOUNT_TRACKER_H_
+
+#include <deque>
+
+namespace cobalt {
+namespace websocket {
+
+// This class is helper class for implementing the |bufferedAmount| attribute in
+// the Websockets API at:
+// https://www.w3.org/TR/websockets/#dom-websocket-bufferedamount.
+class BufferedAmountTracker {
+ public:
+  BufferedAmountTracker() : total_payload_inflight_(0) {}
+
+  // The payload in this context only applies to user messages.
+  // So even though a close message might have a "payload", for the purposes
+  // of the tracker, the |is_user_payload| will be false.
+  void Add(const bool is_user_payload, const std::size_t message_size) {
+    if (is_user_payload) {
+      total_payload_inflight_ += message_size;
+    }
+    entries_.push_back(Entry(is_user_payload, message_size));
+  }
+
+  // Returns the number of user payload bytes popped, these are bytes that were
+  // added with |is_user_payload| set to true.
+  std::size_t Pop(const std::size_t number_bytes_to_pop);
+
+  std::size_t GetTotalBytesInflight() const { return total_payload_inflight_; }
+
+ private:
+  struct Entry {
+    Entry(const bool is_user_payload, const std::size_t message_size)
+        : is_user_payload_(is_user_payload), message_size_(message_size) {}
+
+    bool is_user_payload_;
+    std::size_t message_size_;
+  };
+
+  typedef std::deque<Entry> Entries;
+
+  std::size_t total_payload_inflight_;
+  Entries entries_;
+};
+
+}  // namespace websocket
+}  // namespace cobalt
+
+#endif  // COBALT_WEBSOCKET_BUFFERED_AMOUNT_TRACKER_H_
diff --git a/src/cobalt/websocket/buffered_amount_tracker_test.cc b/src/cobalt/websocket/buffered_amount_tracker_test.cc
new file mode 100644
index 0000000..435dc53
--- /dev/null
+++ b/src/cobalt/websocket/buffered_amount_tracker_test.cc
@@ -0,0 +1,144 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/websocket/buffered_amount_tracker.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace websocket {
+
+class BufferedAmountTrackerTest : public ::testing::Test {
+ public:
+  BufferedAmountTracker tracker_;
+};
+
+TEST_F(BufferedAmountTrackerTest, Construct) {
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddNonPayload) {
+  tracker_.Add(false, 5);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddPayload) {
+  tracker_.Add(true, 5);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 5ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddCombination) {
+  tracker_.Add(true, 5);
+  tracker_.Add(false, 3);
+  tracker_.Add(true, 11);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 16ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddCombination2) {
+  tracker_.Add(false, 2);
+  tracker_.Add(false, 3);
+  tracker_.Add(true, 11);
+  tracker_.Add(true, 1);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 12ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddZero) {
+  tracker_.Add(true, 0);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+
+  tracker_.Add(false, 0);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopZero) {
+  std::size_t amount_poped = 0;
+  tracker_.Add(false, 5);
+  amount_poped = tracker_.Pop(0);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+  EXPECT_EQ(amount_poped, 0);
+
+  tracker_.Add(false, 0);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+  EXPECT_EQ(amount_poped, 0);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopPayload) {
+  std::size_t amount_poped = 0;
+  tracker_.Add(true, 5);
+  amount_poped = tracker_.Pop(4);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 1ul);
+  EXPECT_EQ(amount_poped, 4);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopNonPayload) {
+  std::size_t amount_poped = 0;
+  tracker_.Add(false, 5);
+  amount_poped = tracker_.Pop(3);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+  EXPECT_EQ(amount_poped, 0);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopCombined) {
+  std::size_t amount_poped = 0;
+  tracker_.Add(false, 5);
+  tracker_.Add(true, 3);
+  amount_poped = tracker_.Pop(7);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 1ul);
+  EXPECT_EQ(amount_poped, 2);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopOverflow) {
+  std::size_t amount_poped = 0;
+  tracker_.Add(false, 2);
+  tracker_.Add(true, 2);
+  amount_poped = tracker_.Pop(16);
+  EXPECT_EQ(amount_poped, 2);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, MultipleOperations) {
+  tracker_.Add(false, 2);
+  tracker_.Add(true, 3);
+  tracker_.Add(false, 1);
+  tracker_.Add(false, 6);
+  tracker_.Add(true, 14);
+  tracker_.Add(false, 3);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 17ul);
+
+  std::size_t amount_poped = 0;
+  tracker_.Add(false, 3);
+  amount_poped = tracker_.Pop(4);
+  EXPECT_EQ(amount_poped, 2);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 15ul);
+  tracker_.Add(false, 4);
+  amount_poped = tracker_.Pop(2);
+  EXPECT_EQ(amount_poped, 1);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 14ul);
+  tracker_.Add(true, 5);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 19ul);
+  amount_poped = tracker_.Pop(4);
+  EXPECT_EQ(amount_poped, 0);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 19ul);
+  tracker_.Add(false, 6);
+  amount_poped = tracker_.Pop(4);
+  EXPECT_EQ(amount_poped, 2);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 17ul);
+  tracker_.Add(true, 7);
+  amount_poped = tracker_.Pop(4);
+  EXPECT_EQ(amount_poped, 4);
+  EXPECT_EQ(tracker_.GetTotalBytesInflight(), 20ul);
+}
+
+}  // namespace websocket
+}  // namespace cobalt
diff --git a/src/cobalt/websocket/close_event.h b/src/cobalt/websocket/close_event.h
new file mode 100644
index 0000000..2a23cad
--- /dev/null
+++ b/src/cobalt/websocket/close_event.h
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef COBALT_WEBSOCKET_CLOSE_EVENT_H_
+#define COBALT_WEBSOCKET_CLOSE_EVENT_H_
+
+#include <string>
+
+#include "cobalt/base/tokens.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/websocket/close_event_init.h"
+#include "net/websockets/websocket_errors.h"
+
+namespace cobalt {
+namespace websocket {
+
+class CloseEvent : public dom::Event {
+ public:
+  explicit CloseEvent(const base::Token type)
+      : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
+  explicit CloseEvent(const std::string& type)
+      : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
+  CloseEvent(const base::Token type,
+             const cobalt::websocket::CloseEventInit& eventInitDict)
+      : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
+    InitializeFromCloseEventInit(eventInitDict);
+  }
+  CloseEvent(const std::string& type,
+             const cobalt::websocket::CloseEventInit& eventInitDict)
+      : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
+    InitializeFromCloseEventInit(eventInitDict);
+  }
+
+  // Readonly Attributes.
+  bool was_clean() const { return was_clean_; }
+  uint16 code() const { return code_; }
+  std::string reason() const { return reason_; }
+
+  DEFINE_WRAPPABLE_TYPE(CloseEvent)
+
+ private:
+  void InitializeFromCloseEventInit(const CloseEventInit& eventInitDict) {
+    if (eventInitDict.has_was_clean()) {
+      was_clean_ = eventInitDict.was_clean();
+    }
+    if (eventInitDict.has_code()) {
+      code_ = eventInitDict.code();
+    }
+    if (eventInitDict.has_reason()) {
+      reason_ = eventInitDict.reason();
+    }
+  }
+  bool was_clean_;
+  uint16 code_;
+  std::string reason_;
+
+  DISALLOW_COPY_AND_ASSIGN(CloseEvent);
+};
+
+}  // namespace websocket
+}  // namespace cobalt
+
+#endif  // COBALT_WEBSOCKET_CLOSE_EVENT_H_
diff --git a/src/cobalt/websocket/close_event.idl b/src/cobalt/websocket/close_event.idl
new file mode 100644
index 0000000..28023c1
--- /dev/null
+++ b/src/cobalt/websocket/close_event.idl
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
+// This software or document includes material copied from or derived from
+// The WebSocket API https://www.w3.org/TR/websockets/#event-definitions
+
+[Constructor(DOMString type, optional CloseEventInit eventInitDict)]
+interface CloseEvent : Event {
+  readonly attribute boolean wasClean;
+  readonly attribute unsigned short code;
+  readonly attribute DOMString reason;
+};
+
diff --git a/src/cobalt/websocket/close_event_init.idl b/src/cobalt/websocket/close_event_init.idl
new file mode 100644
index 0000000..ccd1237
--- /dev/null
+++ b/src/cobalt/websocket/close_event_init.idl
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
+// This software or document includes material copied from or derived from
+// The WebSocket API https://www.w3.org/TR/websockets/#event-definitions
+
+// From:
+// https://html.spec.whatwg.org/multipage/comms.html#the-closeevent-interfaces
+dictionary CloseEventInit : EventInit {
+  boolean wasClean = false;
+  unsigned short code = 0;
+  DOMString reason = "";
+};
diff --git a/src/cobalt/websocket/web_socket.cc b/src/cobalt/websocket/web_socket.cc
index 4c25991..fd291bf 100644
--- a/src/cobalt/websocket/web_socket.cc
+++ b/src/cobalt/websocket/web_socket.cc
@@ -21,8 +21,13 @@
 #include "base/logging.h"
 #include "base/string_number_conversions.h"
 #include "base/string_piece.h"
+#include "base/string_util.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/document.h"
 #include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/websocket/close_event.h"
 #include "googleurl/src/gurl.h"
 #include "googleurl/src/url_canon.h"
 #include "googleurl/src/url_constants.h"
@@ -33,6 +38,8 @@
 
 typedef uint16 SerializedCloseStatusCodeType;
 
+static const std::string kComma = ",";
+
 bool IsURLAbsolute(cobalt::dom::DOMSettings* dom_settings,
                    const std::string& url) {
   // This is a requirement for calling spec()
@@ -160,31 +167,23 @@
 WebSocket::WebSocket(script::EnvironmentSettings* settings,
                      const std::string& url,
                      script::ExceptionState* exception_state)
-    : buffered_amount_(0),
-      ready_state_(kConnecting),
-      binary_type_(dom::MessageEvent::kBlob),
-      is_secure_(false),
-      port_(-1),
-      settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)) {
+    : require_network_module_(true) {
   const std::vector<std::string> empty;
-  dom::DOMSettings* dom_settings =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings);
-  Initialize(dom_settings, url, empty, exception_state);
+  Initialize(settings, url, empty, exception_state);
 }
 
 WebSocket::WebSocket(script::EnvironmentSettings* settings,
                      const std::string& url,
                      const std::vector<std::string>& sub_protocols,
                      script::ExceptionState* exception_state)
-    : buffered_amount_(0),
-      ready_state_(kConnecting),
-      binary_type_(dom::MessageEvent::kBlob),
-      is_secure_(false),
-      port_(-1),
-      settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)) {
-  dom::DOMSettings* dom_settings =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings);
-  Initialize(dom_settings, url, sub_protocols, exception_state);
+    : require_network_module_(true) {
+  Initialize(settings, url, sub_protocols, exception_state);
+}
+
+WebSocket::~WebSocket() {
+  if (impl_) {
+    impl_->SetWebSocketEventDelegate(NULL);
+  }
 }
 
 std::string WebSocket::binary_type(script::ExceptionState* exception_state) {
@@ -198,19 +197,13 @@
 
 // Implements spec at https://www.w3.org/TR/websockets/#dom-websocket.
 WebSocket::WebSocket(script::EnvironmentSettings* settings,
-                     const std::string& url, const std::string& sub_protocol,
+                     const std::string& url,
+                     const std::string& sub_protocol_list,
                      script::ExceptionState* exception_state)
-    : buffered_amount_(0),
-      ready_state_(kConnecting),
-      binary_type_(dom::MessageEvent::kBlob),
-      is_secure_(false),
-      port_(-1),
-      settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)) {
+    : require_network_module_(true) {
   std::vector<std::string> sub_protocols;
-  sub_protocols.push_back(sub_protocol);
-  dom::DOMSettings* dom_settings =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings);
-  Initialize(dom_settings, url, sub_protocols, exception_state);
+  Tokenize(sub_protocol_list, kComma, &sub_protocols);
+  Initialize(settings, url, sub_protocols, exception_state);
 }
 
 void WebSocket::set_binary_type(const std::string& binary_type,
@@ -269,12 +262,14 @@
     return;
   }
 
-  switch (ready_state_) {
+  DLOG(INFO) << "Websocket close code " << code;
+
+  switch (ready_state()) {
     case kOpen:
     case kConnecting:
       DCHECK(impl_);
       impl_->Close(net::WebSocketError(code), reason);
-      ready_state_ = kClosing;
+      SetReadyState(kClosing);
       break;
     case kClosing:
     case kClosed:
@@ -291,7 +286,7 @@
   // Per Websockets API spec:
   // "If the readyState attribute is CONNECTING, it must throw an
   // InvalidStateError exception"
-  if (ready_state_ == kConnecting) {
+  if (ready_state() == kConnecting) {
     dom::DOMException::Raise(dom::DOMException::kInvalidStateErr,
                              "readyState is not in CONNECTING state",
                              exception_state);
@@ -402,10 +397,20 @@
   DLOG(INFO) << "Websockets selected subprotocol: [" << selected_subprotocol
              << "]";
   protocol_ = selected_subprotocol;
-  ready_state_ = kOpen;
+  SetReadyState(kOpen);
   this->DispatchEvent(new dom::Event(base::Tokens::open()));
 }
 
+void WebSocket::OnDisconnected(bool was_clean, uint16 code,
+                               const std::string& reason) {
+  SetReadyState(kClosed);
+  CloseEventInit close_event_init;
+  close_event_init.set_was_clean(was_clean);
+  close_event_init.set_code(code);
+  close_event_init.set_reason(reason);
+  this->DispatchEvent(new CloseEvent(base::Tokens::close(), close_event_init));
+}
+
 void WebSocket::OnReceivedData(bool is_text_frame,
                                scoped_refptr<net::IOBufferWithSize> data) {
   dom::MessageEvent::ResponseTypeCode response_type_code = binary_type_;
@@ -416,13 +421,20 @@
                                             response_type_code, data));
 }
 
-void WebSocket::Initialize(dom::DOMSettings* dom_settings,
+void WebSocket::Initialize(script::EnvironmentSettings* settings,
                            const std::string& url,
                            const std::vector<std::string>& sub_protocols,
                            script::ExceptionState* exception_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  buffered_amount_ = 0;
+  binary_type_ = dom::MessageEvent::kBlob;
+  is_secure_ = false;
+  port_ = -1;
+  preventing_gc_ = false;
+  SetReadyState(kConnecting);
 
-  if (!dom_settings) {
+  settings_ = base::polymorphic_downcast<dom::DOMSettings*>(settings);
+  if (!settings_) {
     dom::DOMException::Raise(dom::DOMException::kNone,
                              "Internal error: Unable to get DOM settings.",
                              exception_state);
@@ -430,7 +442,15 @@
     return;
   }
 
-  if (!dom_settings->base_url().is_valid()) {
+  if (require_network_module_ && !settings_->network_module()) {
+    dom::DOMException::Raise(dom::DOMException::kNone,
+                             "Internal error: Unable to get network module.",
+                             exception_state);
+    NOTREACHED() << "Unable to get network module.";
+    return;
+  }
+
+  if (!settings_->base_url().is_valid()) {
     dom::DOMException::Raise(
         dom::DOMException::kNone,
         "Internal error: base_url (the url of the entry script) must be valid.",
@@ -440,7 +460,7 @@
     // GetOrigin() can only be called on valid urls.
     // Since origin does not contain fragments, spec() is guaranteed
     // to return an ASCII encoded string.
-    entry_script_origin_ = dom_settings->base_url().GetOrigin().spec();
+    entry_script_origin_ = settings_->base_url().GetOrigin().spec();
   }
 
   // Per spec:
@@ -448,7 +468,7 @@
   // port, resource name, and secure. If this fails, throw a SyntaxError
   // exception and abort these steps. [WSP]"
 
-  resolved_url_ = dom_settings->base_url().Resolve(url);
+  resolved_url_ = settings_->base_url().Resolve(url);
   if (resolved_url_.is_empty()) {
     dom::DOMException::Raise(dom::DOMException::kSyntaxErr, "url is empty",
                              exception_state);
@@ -461,7 +481,7 @@
     return;
   }
 
-  bool is_absolute = IsURLAbsolute(dom_settings, url);
+  bool is_absolute = IsURLAbsolute(settings_, url);
 
   if (!is_absolute) {
     std::string error_message = "Only relative URLs are supported.  [" + url +
@@ -498,6 +518,13 @@
     return;
   }
 
+  dom::CspDelegate* csp = csp_delegate();
+  if (csp &&
+      !csp->CanLoad(dom::CspDelegate::kWebSocket, resolved_url_, false)) {
+    dom::DOMException::Raise(dom::DOMException::kSecurityErr, exception_state);
+    return;
+  }
+
   if (!net::IsPortAllowedByDefault(GetPort())) {
     std::string error_message = "Connecting to port " + GetPortAsString() +
                                 " using websockets is not allowed.";
@@ -527,22 +554,136 @@
   Connect(resolved_url_, sub_protocols);
 }
 
+dom::CspDelegate* WebSocket::csp_delegate() const {
+  DCHECK(settings_);
+  if (!settings_) {
+    return NULL;
+  }
+  if (settings_->window() && settings_->window()->document()) {
+    return settings_->window()->document()->csp_delegate();
+  } else {
+    return NULL;
+  }
+}
+
 void WebSocket::Connect(const GURL& url,
                         const std::vector<std::string>& sub_protocols) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(settings_);
-  DCHECK(settings_->network_module());
 
   GURL origin_gurl = settings_->base_url().GetOrigin();
   const std::string& origin = origin_gurl.possibly_invalid_spec();
 
-  impl_ = make_scoped_refptr<WebSocketImpl>(
-      new WebSocketImpl(settings_->network_module(), this));
+  impl_ =
+      make_scoped_refptr(new WebSocketImpl(settings_->network_module(), this));
 
-  UNREFERENCED_PARAMETER(origin);
-  UNREFERENCED_PARAMETER(url);
-  UNREFERENCED_PARAMETER(sub_protocols);
-  // impl_->Connect(origin, url, sub_protocols);
+  impl_->Connect(origin, url, sub_protocols);
+}
+
+WebSocket::WebSocket(script::EnvironmentSettings* settings,
+                     const std::string& url,
+                     script::ExceptionState* exception_state,
+                     const bool require_network_module)
+    : require_network_module_(require_network_module) {
+  const std::vector<std::string> empty;
+  Initialize(settings, url, empty, exception_state);
+}
+
+WebSocket::WebSocket(script::EnvironmentSettings* settings,
+                     const std::string& url, const std::string& sub_protocol,
+                     script::ExceptionState* exception_state,
+                     const bool require_network_module)
+    : require_network_module_(require_network_module) {
+  std::vector<std::string> sub_protocols;
+  sub_protocols.push_back(sub_protocol);
+  Initialize(settings, url, sub_protocols, exception_state);
+}
+
+WebSocket::WebSocket(script::EnvironmentSettings* settings,
+                     const std::string& url,
+                     const std::vector<std::string>& sub_protocols,
+                     script::ExceptionState* exception_state,
+                     const bool require_network_module)
+    : require_network_module_(require_network_module) {
+  Initialize(settings, url, sub_protocols, exception_state);
+}
+
+void WebSocket::PotentiallyAllowGarbageCollection() {
+  bool prevent_gc = false;
+  switch (ready_state()) {
+    case kOpen:
+      //  Per spec, "A WebSocket object whose readyState attribute's value was
+      //  set to OPEN (1) as of the last time the event loop started executing a
+      //  task must not be garbage collected if there are any event listeners
+      //  registered for message events, error, or close events..
+      //  A WebSocket object with an established connection that has data queued
+      //  to be transmitted to the network must not be garbage collected."
+      prevent_gc = HasOnMessageListener() || HasOnErrorListener() ||
+                   HasOnCloseListener() || HasOutstandingData();
+      break;
+    case kConnecting:
+      //  Per spec, "WebSocket object whose readyState attribute's value was set
+      //  to CONNECTING (0) as of the last time the event loop started executing
+      //  a task must not be garbage collected if there are any event listeners
+      //  registered for open events, message events, error events, or close
+      //  events."
+      prevent_gc = HasOnOpenListener() || HasOnMessageListener() ||
+                   HasOnErrorListener() || HasOnCloseListener() ||
+                   HasOutstandingData();
+      break;
+    case kClosing:
+      //  Per spec, "A WebSocket object whose readyState attribute's value was
+      //  set to CLOSING (2) as of the last time the event loop started
+      //  executing a task must not be garbage collected if there are any event
+      //  listeners registered for error or close events."
+      prevent_gc =
+          HasOnErrorListener() || HasOnCloseListener() || HasOutstandingData();
+      break;
+    case kClosed:
+      prevent_gc = false;
+      break;
+    default:
+      NOTREACHED() << "Invalid ready_state: " << ready_state();
+  }
+
+  if (prevent_gc != preventing_gc_) {
+    if (prevent_gc) {
+      PreventGarbageCollection();
+    } else {
+      AllowGarbageCollection();
+    }
+
+    // The above function calls should change |preventing_gc_|.
+    DCHECK_EQ(prevent_gc, preventing_gc_);
+  }
+}
+
+void WebSocket::PreventGarbageCollection() {
+  settings_->global_environment()->PreventGarbageCollection(
+      make_scoped_refptr(this));
+  DCHECK(!preventing_gc_);
+  preventing_gc_ = true;
+}
+
+void WebSocket::AllowGarbageCollection() {
+  DCHECK(preventing_gc_);
+
+  // Note: the fall through in this switch statement is on purpose.
+  switch (ready_state_) {
+    case kConnecting:
+      DCHECK(!HasOnOpenListener());
+    case kOpen:
+      DCHECK(!HasOnMessageListener());
+    case kClosing:
+      DCHECK(!HasOnErrorListener());
+      DCHECK(!HasOnCloseListener());
+    default:
+      break;
+  }
+
+  preventing_gc_ = false;
+  settings_->global_environment()->AllowGarbageCollection(
+      make_scoped_refptr(this));
 }
 
 }  // namespace websocket
diff --git a/src/cobalt/websocket/web_socket.h b/src/cobalt/websocket/web_socket.h
index b99990b..f81b0a1 100644
--- a/src/cobalt/websocket/web_socket.h
+++ b/src/cobalt/websocket/web_socket.h
@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "cobalt/base/compiler.h"
@@ -25,6 +26,7 @@
 #include "cobalt/dom/array_buffer.h"
 #include "cobalt/dom/array_buffer_view.h"
 #include "cobalt/dom/blob.h"
+#include "cobalt/dom/csp_delegate.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/event_target.h"
@@ -36,7 +38,7 @@
 namespace cobalt {
 namespace websocket {
 
-// This class represents a WebSocket.  It will abide by RFC 6455 "The WebSocket
+// This class represents a WebSocket.  It abides by RFC 6455 "The WebSocket
 // Protocol", and implements the The WebSocket API spec at
 // https://www.w3.org/TR/websockets/ (as of Jan 2017).
 class WebSocket : public dom::EventTarget, public WebsocketEventInterface {
@@ -56,6 +58,7 @@
   WebSocket(script::EnvironmentSettings* settings, const std::string& url,
             const std::vector<std::string>& sub_protocols,
             script::ExceptionState* exception_state);
+  ~WebSocket();
 
   // Readonly Attributes.
   uint32 buffered_amount() const { return buffered_amount_; }
@@ -93,6 +96,7 @@
 
   void set_onclose(const EventListenerScriptValue& event_listener) {
     SetAttributeEventListener(base::Tokens::close(), event_listener);
+    PotentiallyAllowGarbageCollection();
   }
 
   const EventListenerScriptValue* onerror() const {
@@ -101,6 +105,7 @@
 
   void set_onerror(const EventListenerScriptValue& event_listener) {
     SetAttributeEventListener(base::Tokens::error(), event_listener);
+    PotentiallyAllowGarbageCollection();
   }
 
   const EventListenerScriptValue* onmessage() const {
@@ -109,6 +114,7 @@
 
   void set_onmessage(const EventListenerScriptValue& event_listener) {
     SetAttributeEventListener(base::Tokens::message(), event_listener);
+    PotentiallyAllowGarbageCollection();
   }
 
   const EventListenerScriptValue* onopen() const {
@@ -117,6 +123,7 @@
 
   void set_onopen(const EventListenerScriptValue& event_listener) {
     SetAttributeEventListener(base::Tokens::open(), event_listener);
+    PotentiallyAllowGarbageCollection();
   }
 
   std::string GetHost() const { return resolved_url_.host(); }
@@ -125,16 +132,35 @@
   int GetPort() const { return resolved_url_.EffectiveIntPort(); }
   std::string GetPortAsString() const;
 
+  dom::CspDelegate* csp_delegate() const;
+
   DEFINE_WRAPPABLE_TYPE(WebSocket)
 
  private:
+  // The following constructors are used for testing only.
+  WebSocket(script::EnvironmentSettings* settings, const std::string& url,
+            script::ExceptionState* exception_state,
+            const bool require_network_module);
+
+  WebSocket(script::EnvironmentSettings* settings, const std::string& url,
+            const std::string& sub_protocol_list,
+            script::ExceptionState* exception_state,
+            const bool require_network_module);
+
+  WebSocket(script::EnvironmentSettings* settings, const std::string& url,
+            const std::vector<std::string>& sub_protocols,
+            script::ExceptionState* exception_state,
+            const bool require_network_module);
+
   void OnConnected(const std::string& selected_subprotocol) OVERRIDE;
 
-  void OnDisconnected() OVERRIDE {
-    this->DispatchEvent(new dom::Event(base::Tokens::close()));
-  }
+  void OnDisconnected(bool was_clean, uint16 code,
+                      const std::string& reason) OVERRIDE;
+
   void OnSentData(int amount_sent) OVERRIDE {
-    UNREFERENCED_PARAMETER(amount_sent);
+    DCHECK_GE(buffered_amount_, amount_sent);
+    buffered_amount_ -= amount_sent;
+    PotentiallyAllowGarbageCollection();
   }
   void OnReceivedData(bool is_text_frame,
                       scoped_refptr<net::IOBufferWithSize> data) OVERRIDE;
@@ -142,15 +168,36 @@
     this->DispatchEvent(new dom::Event(base::Tokens::error()));
   }
 
-  void Initialize(dom::DOMSettings* dom_settings, const std::string& url,
+  void Initialize(script::EnvironmentSettings* settings, const std::string& url,
                   const std::vector<std::string>& sub_protocols,
                   script::ExceptionState* exception_state);
 
   void Connect(const GURL& url, const std::vector<std::string>& sub_protocols);
 
+  void SetReadyState(const uint16 ready_state) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    ready_state_ = ready_state;
+    PotentiallyAllowGarbageCollection();
+  }
+
+  // TODO: Investigate if these are worth optimizing.
+  bool HasOnOpenListener() const { return (onopen() != NULL); }
+  bool HasOnMessageListener() const { return (onmessage() != NULL); }
+  bool HasOnErrorListener() const { return (onerror() != NULL); }
+  bool HasOnCloseListener() const { return (onclose() != NULL); }
+  bool HasOutstandingData() const { return (buffered_amount_ > 0); }
+
+  void PotentiallyAllowGarbageCollection();
+
   // Returns false if the check fails.
   bool CheckReadyState(script::ExceptionState* exception_state);
 
+  // Per https://www.w3.org/TR/websockets/#garbage-collection, prevent garbage
+  // collection of this object while connection is live and event listeners are
+  // registered.
+  void AllowGarbageCollection();
+  void PreventGarbageCollection();
+
   // https://www.w3.org/TR/websockets/#dom-websocket-bufferedamount
   int32 buffered_amount_;
   // https://www.w3.org/TR/websockets/#dom-websocket-readystate
@@ -172,10 +219,30 @@
   std::string resource_name_;  // The path of the URL.
   std::string entry_script_origin_;
 
+  bool require_network_module_;
   dom::DOMSettings* settings_;
   scoped_refptr<WebSocketImpl> impl_;
 
-  FRIEND_TEST(WebSocketTest, GoodOrigin);
+  bool preventing_gc_;
+
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, BadOrigin);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, GoodOrigin);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, TestInitialReadyState);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, SyntaxErrorWhenBadScheme);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseWsAndWssCorrectly);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, CheckSecure);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, SyntaxErrorWhenRelativeUrl);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, URLHasFragments);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, URLHasPort);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, URLHasNoPort);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseHost);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseResourceName);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseEmptyResourceName);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseResourceNameWithQuery);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, FailUnsecurePort);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, FailInvalidSubProtocols);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, SubProtocols);
+  FRIEND_TEST_ALL_PREFIXES(WebSocketTest, DuplicatedSubProtocols);
 
   DISALLOW_COPY_AND_ASSIGN(WebSocket);
 };
diff --git a/src/cobalt/websocket/web_socket.idl b/src/cobalt/websocket/web_socket.idl
index a968129..b0d2127 100644
--- a/src/cobalt/websocket/web_socket.idl
+++ b/src/cobalt/websocket/web_socket.idl
@@ -47,9 +47,12 @@
            attribute EventHandler onmessage;
   [RaisesException] attribute DOMString binaryType;
   [RaisesException] void send(DOMString data);
-  [RaisesException] void send(Blob data);
   [RaisesException] void send(ArrayBuffer data);
-  [RaisesException] void send(ArrayBufferView data);
+
+  // Blob is not currently supported in Cobalt.
+  // [RaisesException] void send(Blob data);
+  // ArrayBufferViews translation is not well supported in Cobalt.
+  // [RaisesException] void send(ArrayBufferView data);
 };
 
 typedef EventListener? EventHandler;
diff --git a/src/cobalt/websocket/web_socket_event_interface.h b/src/cobalt/websocket/web_socket_event_interface.h
index 9a16c23..2a786a4 100644
--- a/src/cobalt/websocket/web_socket_event_interface.h
+++ b/src/cobalt/websocket/web_socket_event_interface.h
@@ -27,7 +27,8 @@
  public:
   virtual ~WebsocketEventInterface() {}
   virtual void OnConnected(const std::string& selected_subprotocol) = 0;
-  virtual void OnDisconnected() = 0;
+  virtual void OnDisconnected(bool was_clean, uint16 code,
+                              const std::string& reason) = 0;
   virtual void OnSentData(int amount_sent) = 0;
   virtual void OnReceivedData(bool is_text_frame,
                               scoped_refptr<net::IOBufferWithSize> data) = 0;
diff --git a/src/cobalt/websocket/web_socket_impl.cc b/src/cobalt/websocket/web_socket_impl.cc
index 608e598..ed6995f 100644
--- a/src/cobalt/websocket/web_socket_impl.cc
+++ b/src/cobalt/websocket/web_socket_impl.cc
@@ -57,7 +57,7 @@
                              WebsocketEventInterface *delegate)
     : network_module_(network_module),
       delegate_(delegate),
-      handshake_helper_(network_module->GetUserAgent()),
+      handshake_helper_(network_module ? network_module->GetUserAgent() : ""),
       handshake_completed_(false) {
   DCHECK(delegate_);
   DCHECK(MessageLoop::current());
@@ -65,8 +65,17 @@
   owner_task_runner_ = MessageLoop::current()->message_loop_proxy();
 }
 
+void WebSocketImpl::SetWebSocketEventDelegate(
+    WebsocketEventInterface *delegate) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  delegate_ = delegate;
+}
+
 void WebSocketImpl::Connect(const std::string &origin, const GURL &url,
                             const std::vector<std::string> &sub_protocols) {
+  if (!network_module_) {
+    return;
+  }
   DCHECK(network_module_->url_request_context_getter());
   thread_checker_.CalledOnValidThread();
   origin_ = origin;
@@ -108,6 +117,7 @@
   job_created_event->Signal();  // Signal that this->job_ has been assigned.
 
   job_->set_context(context->GetURLRequestContext());
+  job_->set_network_task_runner(context->GetNetworkTaskRunner());
   job_->Connect();
 
   DCHECK_EQ(GetCurrentState(), net::WebSocketJob::CONNECTING);
@@ -116,25 +126,48 @@
 void WebSocketImpl::Close(const net::WebSocketError code,
                           const std::string &reason) {
   DCHECK(job_);
+  CloseInfo close_info(code, reason);
   delegate_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&WebSocketImpl::DoClose, this, code, reason));
+      FROM_HERE, base::Bind(&WebSocketImpl::DoClose, this, close_info));
 }
 
-void WebSocketImpl::DoClose(const net::WebSocketError code,
-                            const std::string &reason) {
+void WebSocketImpl::DoClose(const CloseInfo &close_info) {
   DCHECK(job_);
   DCHECK(delegate_task_runner_->BelongsToCurrentThread());
 
   net::WebSocketJob::State current_state = GetCurrentState();
-  if ((current_state != net::WebSocketJob::CLOSED) &&
-      (current_state != net::WebSocketJob::CLOSING)) {
-    // Write the close frame
-    std::string error_message;
-    if (!SendClose(code, reason, &error_message)) {
-      DLOG(ERROR) << "Error while sending websocket close: " << error_message;
+  switch (current_state) {
+    case net::WebSocketJob::CONNECTING:
+    case net::WebSocketJob::OPEN: {
+      // Write the close frame
+      std::string error_message;
+      if (!SendClose(close_info.code, close_info.reason, &error_message)) {
+        DLOG(ERROR) << "Error while sending websocket close: " << error_message;
+      }
+      net::WebSocketJob::State new_state;
+      if (peer_close_info_) {
+        // We have received a Close frame from the peer.
+        new_state = net::WebSocketJob::RECV_CLOSED;
+      } else {
+        // We have not received a Close frame from the peer.
+        new_state = net::WebSocketJob::SEND_CLOSED;
+      }
+      job_->SetState(new_state);
+      job_->Close();
+      break;
     }
+    case net::WebSocketJob::SEND_CLOSED:
+      if (peer_close_info_) {
+        // Closing handshake is now complete.
+        job_->SetState(net::WebSocketJob::CLOSE_WAIT);
+        job_->Close();
+      }
+    case net::WebSocketJob::CLOSE_WAIT:
+    case net::WebSocketJob::CLOSED:
+    case net::WebSocketJob::INITIALIZED:
+    case net::WebSocketJob::RECV_CLOSED:
+      break;
   }
-  job_->Close();
 }
 
 void WebSocketImpl::DoPong(const scoped_refptr<net::IOBufferWithSize> payload) {
@@ -143,8 +176,8 @@
 
   net::WebSocketJob::State current_state = GetCurrentState();
 
-  if ((current_state == net::WebSocketJob::CLOSED) ||
-      (current_state == net::WebSocketJob::CLOSING)) {
+  if (!((current_state == net::WebSocketJob::CONNECTING) ||
+        (current_state == net::WebSocketJob::OPEN))) {
     return;
   }
 
@@ -225,8 +258,6 @@
     return;
   }
 
-  DLOG(INFO) << "ReceivedData " << len << " bytes.";
-
   std::size_t payload_offset = 0;
   if (!ProcessHandshake(&payload_offset)) {
     return;  // Handshake is still in progress.
@@ -244,7 +275,6 @@
     DLOG(INFO) << "Error while decoding websocket: " << websocket_error;
     return;
   }
-  DLOG(INFO) << "Received " << frame_chunks.size() << " chunks.";
 
   bool protocol_violation_occured = false;
 
@@ -273,14 +303,6 @@
       }
     }
 
-    //    if (header) {
-    //      DLOG(INFO) << "Got a chunk " << header->opcode << " "
-    //                 << chunk->final_chunk;
-    //    } else {
-    //      DLOG(INFO) << "Got a chunk ? "
-    //                 << " " << chunk->final_chunk;
-    //    }
-
     // Note that |MoveInto| transfers ownership of the pointer.
     WebSocketFrameContainer::ErrorCode move_into_retval =
         current_frame_container_.Take(*iterator);
@@ -310,22 +332,57 @@
   protocol_violation_occured |= (iterator != frame_chunks.end());
 
   if (protocol_violation_occured) {
-    TrampolineClose();
+    CloseInfo close_info(net::kWebSocketErrorProtocolError);
+    TrampolineClose(close_info);
     frame_chunks.erase(iterator, frame_chunks.end());
   }
 
   frame_chunks.weak_clear();
 }
 
-void WebSocketImpl::TrampolineClose(const net::WebSocketError error_code) {
-  std::string empty;
+// The main reason to call TrampolineClose is to ensure messages that are posted
+// from this thread prior to this function call are processed before the
+// connection is closed.
+void WebSocketImpl::TrampolineClose(const CloseInfo &close_info) {
   base::Closure no_op_closure(base::Bind(&base::DoNothing));
+
   base::Closure do_close_closure(
-      base::Bind(&WebSocketImpl::DoClose, this, error_code, empty));
+      base::Bind(&WebSocketImpl::DoClose, this, close_info));
   owner_task_runner_->PostTaskAndReply(FROM_HERE, no_op_closure,
                                        do_close_closure);
 }
 
+void WebSocketImpl::OnWebSocketConnected(
+    const std::string &selected_subprotocol) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (delegate_) {
+    delegate_->OnConnected(selected_subprotocol);
+  }
+}
+
+void WebSocketImpl::OnWebSocketDisconnected(bool was_clean, uint16 code,
+                                            const std::string &reason) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (delegate_) {
+    delegate_->OnDisconnected(was_clean, code, reason);
+  }
+}
+
+void WebSocketImpl::OnWebSocketSentData(int amount_sent) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (delegate_) {
+    delegate_->OnSentData(amount_sent);
+  }
+}
+
+void WebSocketImpl::OnWebSocketReceivedData(
+    bool is_text_frame, scoped_refptr<net::IOBufferWithSize> data) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (delegate_) {
+    delegate_->OnReceivedData(is_text_frame, data);
+  }
+}
+
 void WebSocketImpl::ProcessCompleteMessage(
     const WebSocketMessageContainer &message_container) {
   if (message_container.GetCurrentPayloadSizeBytes() >
@@ -354,27 +411,32 @@
   if (is_text_message && buf && (buf->size() > 0)) {
     base::StringPiece payload_string_piece(buf->data(), buf->size());
     if (!IsStringUTF8(payload_string_piece)) {
-      TrampolineClose();
+      CloseInfo close_info(net::kWebSocketErrorProtocolError);
+      TrampolineClose(close_info);
       return;
     }
   }
 
   owner_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&WebsocketEventInterface::OnReceivedData,
-                            base::Unretained(delegate_), is_text_message, buf));
+      FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketReceivedData, this,
+                            is_text_message, buf));
 }
 
 void WebSocketImpl::ProcessControlMessage(
     const WebSocketMessageContainer &message_container) {
   if (message_container.empty()) {
-    DoClose(net::kWebSocketErrorInternalServerError, "Message has no frames.");
+    CloseInfo close_info(net::kWebSocketErrorInternalServerError,
+                         "Message has no frames.");
+    DoClose(close_info);
     NOTREACHED();
     return;
   }
 
   if (message_container.GetCurrentPayloadSizeBytes() >
       kMaxControlPayloadSizeInBytes) {
-    DoClose(net::kWebSocketErrorProtocolError, "Control Frame too large");
+    CloseInfo close_info(net::kWebSocketErrorProtocolError,
+                         "Control Frame too large");
+    DoClose(close_info);
     return;
   }
 
@@ -385,8 +447,9 @@
   if (number_of_frames > 0) {
     const net::WebSocketFrameHeader *header = frames.begin()->GetHeader();
     if ((number_of_frames > 1) || (header && !header->final)) {
-      DoClose(net::kWebSocketErrorProtocolError,
-              "Control messages must not be fragmented");
+      CloseInfo close_info(net::kWebSocketErrorProtocolError,
+                           "Control messages must not be fragmented");
+      DoClose(close_info);
       return;
     }
   }
@@ -414,7 +477,10 @@
     case net::WebSocketFrameHeader::kOpCodeBinary:
     default:
       NOTREACHED() << "Invalid case " << header_pointer->opcode;
-      DoClose(net::kWebSocketErrorInternalServerError, "Invalid op code.");
+
+      CloseInfo close_info(net::kWebSocketErrorInternalServerError,
+                           "Invalid op code.");
+      DoClose(close_info);
       break;
   }
 }
@@ -447,11 +513,15 @@
     const scoped_refptr<net::IOBufferWithSize> &close_data) {
   net::WebSocketError outgoing_status_code = net::kWebSocketNormalClosure;
   std::size_t payload_length = 0;
+  std::string close_reason;
   if (close_data) {
     DCHECK_EQ(header.payload_length, close_data->size());
     DCHECK(close_data->data());
     payload_length = close_data->size();
   }
+
+  peer_close_info_ = CloseInfo(net::kWebSocketErrorNoStatusReceived);
+
   if (payload_length == 0) {
     // default status code of normal is OK.
   } else if (payload_length < 2) {
@@ -473,44 +543,65 @@
       payload_length -= sizeof(status_code_on_wire);
 
       if (net::IsValidCloseStatusCode(status_code_on_wire)) {
-        DLOG(INFO) << "Received close status code in close "
-                   << outgoing_status_code;
+        DLOG(INFO) << "Websocket received close status code: "
+                   << status_code_on_wire;
+        peer_close_info_->code = net::WebSocketError(status_code_on_wire);
       } else {
-        DLOG(ERROR) << "Received invalid status code in close "
+        DLOG(ERROR) << "Websocket received invalid close status code: "
                     << outgoing_status_code;
         outgoing_status_code = net::kWebSocketErrorProtocolError;
       }
 
-      std::string close_reason(payload_pointer, payload_length);
-      if (IsStringUTF8(close_reason)) {
-        DLOG(INFO) << "Websocket close reason [" << close_reason << "]";
-      } else {
-        outgoing_status_code = net::kWebSocketErrorProtocolError;
+      close_reason.assign(payload_pointer, payload_length);
+      if (payload_length > 0) {
+        if (IsStringUTF8(close_reason)) {
+          DLOG(INFO) << "Websocket close reason [" << close_reason << "]";
+          peer_close_info_->reason = close_reason;
+        } else {
+          outgoing_status_code = net::kWebSocketErrorProtocolError;
+        }
       }
     }
   }
 
-  TrampolineClose(outgoing_status_code);
+  CloseInfo outgoing_close_info(outgoing_status_code, close_reason);
+  TrampolineClose(outgoing_close_info);
 }
 
 void WebSocketImpl::OnSentData(net::SocketStream *socket, int amount_sent) {
   UNREFERENCED_PARAMETER(socket);
-  UNREFERENCED_PARAMETER(amount_sent);
   DCHECK(delegate_task_runner_->BelongsToCurrentThread());
 
-  DLOG(INFO) << "Websocket Sent " << amount_sent << " bytes.";
+  std::size_t payload_sent = buffered_amount_tracker_.Pop(amount_sent);
+  DCHECK_GE(payload_sent, 0ul);
+  DCHECK_LE(payload_sent, static_cast<unsigned int>(kint32max));
+
   owner_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&WebsocketEventInterface::OnSentData,
-                            base::Unretained(delegate_), amount_sent));
+      FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketSentData, this,
+                            static_cast<int>(payload_sent)));
 }
 
 void WebSocketImpl::OnClose(net::SocketStream *socket) {
-  DLOG(INFO) << "Got a close yaar";
   UNREFERENCED_PARAMETER(socket);
   DCHECK(delegate_task_runner_->BelongsToCurrentThread());
+
+  bool close_was_clean = false;
+  net::WebSocketError close_code = net::kWebSocketErrorAbnormalClosure;
+  std::string close_reason;
+
+  if (peer_close_info_) {
+    close_was_clean = true;
+    close_code = peer_close_info_->code;
+    close_reason = peer_close_info_->reason;
+  }
+
+  DLOG(INFO) << "WebSocket is closing. was_clean[" << std::boolalpha
+             << close_was_clean << "] code[" << close_code << "] reason["
+             << close_reason << "]";
+
   owner_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&WebsocketEventInterface::OnDisconnected,
-                            base::Unretained(delegate_)));
+      FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketDisconnected, this,
+                            close_was_clean, close_code, close_reason));
 }
 
 // Currently only called in SocketStream::Finish(), so it is meant
@@ -536,14 +627,16 @@
   handshake_helper_.GenerateHandshakeRequest(
       connect_url_, origin_, desired_sub_protocols_, &header_string);
 
+  buffered_amount_tracker_.Add(false, header_string.size());
+
   job_->SendData(header_string.data(), static_cast<int>(header_string.size()));
 }
 
 void WebSocketImpl::OnHandshakeComplete(
     const std::string &selected_subprotocol) {
   owner_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&WebsocketEventInterface::OnConnected,
-                            base::Unretained(delegate_), selected_subprotocol));
+      FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketConnected, this,
+                            selected_subprotocol));
 }
 
 // Note that |payload_length| in |header| will define the payload length.
@@ -575,21 +668,28 @@
   DCHECK_EQ(application_payload_offset + payload_length, frame_size);
   if (payload_length != 0) {
     char *payload_offset = data_ptr.get() + application_payload_offset;
-    COMPILE_ASSERT(kMaxFramePayloadInBytes < kint32max,
-                   frame_payload_size_too_big);
+    COMPILE_ASSERT(
+        kMaxFramePayloadInBytes < static_cast<std::size_t>(kint32max),
+        frame_payload_size_too_big);
     SbMemoryCopy(payload_offset, data, static_cast<int>(payload_length));
     net::MaskWebSocketFramePayload(masking_key, 0, payload_offset,
                                    static_cast<int>(payload_length));
   }
 
+  int overhead_bytes = static_cast<int>(frame_size);
+  if ((header.opcode == net::WebSocketFrameHeader::kOpCodeText) ||
+      (header.opcode == net::WebSocketFrameHeader::kOpCodeBinary)) {
+    // Only consider text and binary frames as "payload".
+    overhead_bytes = application_payload_offset;
+  }
   if (delegate_task_runner_->BelongsToCurrentThread()) {
     // this behavior is not just an optimization, but required in case
     // we are closing the connection
-    SendFrame(data_ptr.Pass(), static_cast<int>(frame_size));
+    SendFrame(data_ptr.Pass(), static_cast<int>(frame_size), overhead_bytes);
   } else {
-    base::Closure do_send_closure(base::Bind(&WebSocketImpl::SendFrame, this,
-                                             base::Passed(data_ptr.Pass()),
-                                             static_cast<int>(frame_size)));
+    base::Closure do_send_closure(base::Bind(
+        &WebSocketImpl::SendFrame, this, base::Passed(data_ptr.Pass()),
+        static_cast<int>(frame_size), overhead_bytes));
     delegate_task_runner_->PostTask(FROM_HERE, do_send_closure);
   }
 
@@ -638,7 +738,8 @@
   } else {
     DLOG(ERROR) << "Handshake response is invalid: " << error_message;
     // Something is wrong, let's shutdown.
-    job_->Close();
+    CloseInfo close_info(net::kWebSocketErrorProtocolError);
+    DoClose(close_info);
   }
 
   return false;
@@ -672,6 +773,8 @@
   header.masked = true;
   header.payload_length = length;
 
+  *buffered_amount += length;
+
   return SendHelper(header, data, error_message);
 }
 
@@ -692,6 +795,8 @@
   header.masked = true;
   header.payload_length = length;
 
+  *buffered_amount += length;
+
   return SendHelper(header, data, error_message);
 }
 
@@ -763,10 +868,20 @@
   return SendHelper(header, payload.data(), error_message);
 }
 
-void WebSocketImpl::SendFrame(const scoped_array<char> data, const int length) {
+void WebSocketImpl::SendFrame(const scoped_array<char> data, const int length,
+                              const int overhead_bytes) {
   DCHECK(delegate_task_runner_->BelongsToCurrentThread());
+
   DCHECK(data);
   DCHECK_GE(length, 0);
+  DCHECK_GE(length, overhead_bytes);
+
+  buffered_amount_tracker_.Add(false, overhead_bytes);
+  int user_payload_bytes = length - overhead_bytes;
+  if (user_payload_bytes > 0) {
+    buffered_amount_tracker_.Add(true, user_payload_bytes);
+  }
+
   job_->SendData(data.get(), length);
 }
 
diff --git a/src/cobalt/websocket/web_socket_impl.h b/src/cobalt/websocket/web_socket_impl.h
index 812d4fb..2d89fc4 100644
--- a/src/cobalt/websocket/web_socket_impl.h
+++ b/src/cobalt/websocket/web_socket_impl.h
@@ -21,9 +21,11 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_vector.h"
+#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
 #include "cobalt/network/network_module.h"
+#include "cobalt/websocket/buffered_amount_tracker.h"
 #include "cobalt/websocket/web_socket_event_interface.h"
 #include "cobalt/websocket/web_socket_frame_container.h"
 #include "cobalt/websocket/web_socket_handshake_helper.h"
@@ -53,6 +55,8 @@
   explicit WebSocketImpl(cobalt::network::NetworkModule* network_module,
                          WebsocketEventInterface* delegate);
 
+  void SetWebSocketEventDelegate(WebsocketEventInterface* delegate);
+
   // These functions are meant to be called from the Web Module thread.
   void Connect(const std::string& origin, const GURL& url,
                const std::vector<std::string>& sub_protocols);
@@ -81,13 +85,23 @@
   void OnError(const net::SocketStream* socket, int error) OVERRIDE;
 
  private:
+  struct CloseInfo {
+    CloseInfo(const net::WebSocketError code, const std::string& reason)
+        : code(code), reason(reason) {}
+    explicit CloseInfo(const net::WebSocketError code) : code(code) {}
+
+    net::WebSocketError code;
+    std::string reason;
+  };
+
   void DoDetach(base::WaitableEvent* waitable_event);
-  void DoClose(const net::WebSocketError code, const std::string& reason);
+  void DoClose(const CloseInfo& close_info);
   void DoPong(const scoped_refptr<net::IOBufferWithSize> payload);
   void DoConnect(
       scoped_refptr<cobalt::network::URLRequestContextGetter> context,
       const GURL& url, base::WaitableEvent* job_created_event);
-  void SendFrame(const scoped_array<char> data, const int length);
+  void SendFrame(const scoped_array<char> data, const int length,
+                 const int overhead_bytes);
   void OnHandshakeComplete(const std::string& selected_subprotocol);
 
   void ProcessCompleteMessage(
@@ -114,8 +128,15 @@
   // Returns true if the handshake has been fully processed.
   bool ProcessHandshake(std::size_t* payload_offset);
 
-  void TrampolineClose(
-      const net::WebSocketError error_code = net::kWebSocketErrorProtocolError);
+  void TrampolineClose(const CloseInfo& close_info);
+
+  void OnWebSocketConnected(const std::string& selected_subprotocol);
+  void OnWebSocketDisconnected(bool was_clean, uint16 code,
+                               const std::string& reason);
+  void OnWebSocketSentData(int amount_sent);
+  void OnWebSocketReceivedData(bool is_text_frame,
+                               scoped_refptr<net::IOBufferWithSize> data);
+  void OnWebSocketError();
 
   base::ThreadChecker thread_checker_;
   net::WebSocketJob::State GetCurrentState() const;
@@ -132,6 +153,9 @@
   WebSocketFrameContainer current_frame_container_;
   WebSocketMessageContainer current_message_container_;
 
+  base::optional<CloseInfo> peer_close_info_;
+  BufferedAmountTracker buffered_amount_tracker_;
+
   scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner_;
 
diff --git a/src/cobalt/websocket/web_socket_test.cc b/src/cobalt/websocket/web_socket_test.cc
index 0595461..ebca33f 100644
--- a/src/cobalt/websocket/web_socket_test.cc
+++ b/src/cobalt/websocket/web_socket_test.cc
@@ -41,13 +41,12 @@
       : dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                          NULL, NULL),
         base_("https://example.com") {
-    this->set_network_module(&network_module_);
+    this->set_network_module(NULL);
   }
   const GURL& base_url() const OVERRIDE { return base_; }
 
   // public members, so that they're easier for testing.
   GURL base_;
-  cobalt::network::NetworkModule network_module_;
 };
 
 class WebSocketTest : public ::testing::Test {
@@ -73,7 +72,7 @@
       .WillOnce(SaveArg<0>(&exception));
 
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com", &exception_state_));
+      new WebSocket(settings(), "ws://example.com", &exception_state_, false));
 
   ASSERT_TRUE(exception.get());
   dom::DOMException& dom_exception(
@@ -100,8 +99,8 @@
     base::polymorphic_downcast<FakeSettings*>(settings())->base_ =
         GURL(new_base);
 
-    scoped_refptr<WebSocket> ws(
-        new WebSocket(settings(), "ws://example.com", &exception_state_));
+    scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
+                                              &exception_state_, false));
 
     ASSERT_FALSE(exception.get());
     EXPECT_EQ(ws->entry_script_origin_, test_case.expected_output);
@@ -111,7 +110,7 @@
 TEST_F(WebSocketTest, TestInitialReadyState) {
   scoped_ptr<FakeSettings> settings_;
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com", &exception_state_));
+      new WebSocket(settings(), "ws://example.com", &exception_state_, false));
   EXPECT_EQ(ws->ready_state(), WebSocket::kConnecting);
 }
 
@@ -121,8 +120,8 @@
   EXPECT_CALL(exception_state_, SetException(_))
       .WillOnce(SaveArg<0>(&exception));
 
-  scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "badscheme://example.com", &exception_state_));
+  scoped_refptr<WebSocket> ws(new WebSocket(
+      settings(), "badscheme://example.com", &exception_state_, false));
 
   dom::DOMException& dom_exception(
       *base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -137,22 +136,22 @@
 TEST_F(WebSocketTest, ParseWsAndWssCorrectly) {
   EXPECT_CALL(exception_state_, SetException(_)).Times(0);
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com/", &exception_state_));
+      new WebSocket(settings(), "ws://example.com/", &exception_state_, false));
 
   EXPECT_CALL(exception_state_, SetException(_)).Times(0);
   scoped_refptr<WebSocket> wss(
-      new WebSocket(settings(), "wss://example.com", &exception_state_));
+      new WebSocket(settings(), "wss://example.com", &exception_state_, false));
 }
 
 TEST_F(WebSocketTest, CheckSecure) {
   EXPECT_CALL(exception_state_, SetException(_)).Times(0);
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com/", &exception_state_));
+      new WebSocket(settings(), "ws://example.com/", &exception_state_, false));
   EXPECT_FALSE(ws->IsSecure());
 
   EXPECT_CALL(exception_state_, SetException(_)).Times(0);
   scoped_refptr<WebSocket> wss(
-      new WebSocket(settings(), "wss://example.com", &exception_state_));
+      new WebSocket(settings(), "wss://example.com", &exception_state_, false));
 
   EXPECT_TRUE(wss->IsSecure());
 }
@@ -165,7 +164,7 @@
       .WillOnce(SaveArg<0>(&exception));
 
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "relative_url", &exception_state_));
+      new WebSocket(settings(), "relative_url", &exception_state_, false));
 
   dom::DOMException& dom_exception(
       *base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -183,7 +182,7 @@
       .WillOnce(SaveArg<0>(&exception));
 
   scoped_refptr<WebSocket> ws(new WebSocket(
-      settings(), "wss://example.com/#fragment", &exception_state_));
+      settings(), "wss://example.com/#fragment", &exception_state_, false));
 
   dom::DOMException& dom_exception(
       *base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -195,8 +194,8 @@
 }
 
 TEST_F(WebSocketTest, URLHasPort) {
-  scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "wss://example.com:789", &exception_state_));
+  scoped_refptr<WebSocket> ws(new WebSocket(settings(), "wss://example.com:789",
+                                            &exception_state_, false));
 
   EXPECT_EQ(ws->GetPort(), 789);
   EXPECT_EQ(ws->GetPortAsString(), "789");
@@ -208,13 +207,13 @@
   // otherwise let port be 443.
 
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com", &exception_state_));
+      new WebSocket(settings(), "ws://example.com", &exception_state_, false));
 
   EXPECT_EQ(ws->GetPort(), 80);
   EXPECT_EQ(ws->GetPortAsString(), "80");
 
   scoped_refptr<WebSocket> wss(
-      new WebSocket(settings(), "wss://example.com", &exception_state_));
+      new WebSocket(settings(), "wss://example.com", &exception_state_, false));
 
   EXPECT_EQ(wss->GetPort(), 443);
   EXPECT_EQ(wss->GetPortAsString(), "443");
@@ -225,7 +224,7 @@
   // Let host be the value of the <host> component of url, converted to ASCII
   // lowercase.
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "wss://eXAmpLe.com", &exception_state_));
+      new WebSocket(settings(), "wss://eXAmpLe.com", &exception_state_, false));
 
   std::string host(ws->GetHost());
   EXPECT_EQ(host, "example.com");
@@ -237,7 +236,7 @@
   // empty) of
   // url.
   scoped_refptr<WebSocket> ws(new WebSocket(
-      settings(), "ws://eXAmpLe.com/resource_name", &exception_state_));
+      settings(), "ws://eXAmpLe.com/resource_name", &exception_state_, false));
 
   std::string resource_name(ws->GetResourceName());
   EXPECT_EQ(resource_name, "/resource_name");
@@ -248,7 +247,7 @@
   // If resource name is the empty string, set it to a single character U+002F
   // SOLIDUS (/).
   scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com", &exception_state_));
+      new WebSocket(settings(), "ws://example.com", &exception_state_, false));
 
   std::string resource_name(ws->GetResourceName());
   EXPECT_EQ(resource_name, "/");
@@ -261,7 +260,7 @@
   // component.
   scoped_refptr<WebSocket> ws(
       new WebSocket(settings(), "ws://example.com/resource_name?abc=xyz&j=3",
-                    &exception_state_));
+                    &exception_state_, false));
 
   std::string resource_name(ws->GetResourceName());
   EXPECT_EQ(resource_name, "/resource_name?abc=xyz&j=3");
@@ -273,8 +272,8 @@
   EXPECT_CALL(exception_state_, SetException(_))
       .WillOnce(SaveArg<0>(&exception));
 
-  scoped_refptr<WebSocket> ws(
-      new WebSocket(settings(), "ws://example.com:22", &exception_state_));
+  scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com:22",
+                                            &exception_state_, false));
 
   dom::DOMException& dom_exception(
       *base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -317,7 +316,7 @@
 
     scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
                                               test_case.sub_protocol,
-                                              &exception_state_));
+                                              &exception_state_, false));
 
     if (test_case.exception_thrown) {
       dom::DOMException& dom_exception(
@@ -335,8 +334,9 @@
     std::vector<std::string> sub_protocols;
     sub_protocols.push_back("chat.example.com");
     sub_protocols.push_back("bicker.example.com");
-    scoped_refptr<WebSocket> ws(new WebSocket(
-        settings(), "ws://example.com", sub_protocols, &exception_state_));
+    scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
+                                              sub_protocols, &exception_state_,
+                                              false));
 
     ASSERT_FALSE(exception.get());
   }
@@ -344,7 +344,8 @@
     scoped_refptr<script::ScriptException> exception;
     std::string sub_protocol("chat");
     scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
-                                              sub_protocol, &exception_state_));
+                                              sub_protocol, &exception_state_,
+                                              false));
 
     ASSERT_FALSE(exception.get());
   }
@@ -358,8 +359,8 @@
   std::vector<std::string> sub_protocols;
   sub_protocols.push_back("chat");
   sub_protocols.push_back("chat");
-  scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
-                                            sub_protocols, &exception_state_));
+  scoped_refptr<WebSocket> ws(new WebSocket(
+      settings(), "ws://example.com", sub_protocols, &exception_state_, false));
 
   ASSERT_TRUE(exception.get());
 
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index d970680..d13c46d 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -21,21 +21,25 @@
       'target_name': 'websocket',
       'type': 'static_library',
       'sources': [
+        'buffered_amount_tracker.cc',
+        'buffered_amount_tracker.h',
+        'close_event.h',
         'sec_web_socket_key.h',
         'web_socket.cc',
         'web_socket.h',
         'web_socket_event_interface.h',
-        'web_socket_frame_container.h',
         'web_socket_frame_container.cc',
-        'web_socket_handshake_helper.h',
+        'web_socket_frame_container.h',
         'web_socket_handshake_helper.cc',
-        'web_socket_impl.h',
+        'web_socket_handshake_helper.h',
         'web_socket_impl.cc',
-        'web_socket_message_container.h',
+        'web_socket_impl.h',
         'web_socket_message_container.cc',
+        'web_socket_message_container.h',
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
@@ -46,6 +50,7 @@
       'target_name': 'websocket_test',
       'type': '<(gtest_target_type)',
       'sources': [
+        'buffered_amount_tracker_test.cc',
         'web_socket_frame_container_test.cc',
         'web_socket_handshake_helper_test.cc',
         'web_socket_message_container_test.cc',
@@ -57,6 +62,10 @@
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
+
+        # TODO: Remove the dependency below, it works around the fact that
+        #       ScriptValueFactory has non-virtual method CreatePromise().
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
       ],
     },
 
@@ -73,3 +82,4 @@
     },
   ],
 }
+
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index 1fbd0c0..8d4d574 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -50,6 +50,10 @@
         '<(DEPTH)/testing/gtest.gyp:gtest',
         'xhr',
         'xhr_copy_test_data',
+
+        # TODO: Remove the dependency below, it works around the fact that
+        #       ScriptValueFactory has non-virtual method CreatePromise().
+        '<(DEPTH)/cobalt/script/engine.gyp:engine',
       ],
     },
     {
diff --git a/src/cobalt/xhr/xml_http_request_test.cc b/src/cobalt/xhr/xml_http_request_test.cc
index 77aa859..5863f7f 100644
--- a/src/cobalt/xhr/xml_http_request_test.cc
+++ b/src/cobalt/xhr/xml_http_request_test.cc
@@ -19,6 +19,7 @@
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/testing/mock_event_listener.h"
 #include "cobalt/dom/window.h"
+#include "cobalt/script/testing/fake_script_value.h"
 #include "cobalt/script/testing/mock_exception_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -30,8 +31,9 @@
 using ::testing::Return;
 using ::testing::SaveArg;
 using ::testing::StrictMock;
-using cobalt::dom::testing::FakeScriptValue;
+using cobalt::dom::EventListener;
 using cobalt::dom::testing::MockEventListener;
+using cobalt::script::testing::FakeScriptValue;
 using cobalt::script::testing::MockExceptionState;
 
 namespace cobalt {
@@ -151,7 +153,7 @@
 
 TEST_F(XhrTest, Open) {
   scoped_ptr<MockEventListener> listener = MockEventListener::Create();
-  FakeScriptValue script_object(listener.get());
+  FakeScriptValue<EventListener> script_object(listener.get());
   xhr_->set_onreadystatechange(script_object);
   EXPECT_CALL(
       *listener,
diff --git a/src/glimp/egl/display.cc b/src/glimp/egl/display.cc
index 0d02a74..c4d7608 100644
--- a/src/glimp/egl/display.cc
+++ b/src/glimp/egl/display.cc
@@ -309,5 +309,9 @@
   return true;
 }
 
+bool Display::SwapInterval(EGLint interval) {
+  return impl_->SetSwapInterval(interval);
+}
+
 }  // namespace egl
 }  // namespace glimp
diff --git a/src/glimp/egl/display.h b/src/glimp/egl/display.h
index a61469a..8fc432d 100644
--- a/src/glimp/egl/display.h
+++ b/src/glimp/egl/display.h
@@ -64,6 +64,7 @@
 
   bool MakeCurrent(EGLSurface draw, EGLSurface read, EGLContext ctx);
   bool SwapBuffers(EGLSurface surface);
+  bool SwapInterval(EGLint interval);
 
   DisplayImpl* impl() const { return impl_.get(); }
 
diff --git a/src/glimp/egl/display_impl.h b/src/glimp/egl/display_impl.h
index c430202..d98efb9 100644
--- a/src/glimp/egl/display_impl.h
+++ b/src/glimp/egl/display_impl.h
@@ -87,6 +87,11 @@
   virtual nb::scoped_ptr<gles::ContextImpl> CreateContext(const Config* config,
                                                           int gles_version) = 0;
 
+  // Sets the swap behavior for this display.  This will be called when
+  // eglSwapInterval() is called.  Returns true on success and false on failure.
+  //   https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglSwapInterval.xhtml
+  virtual bool SetSwapInterval(int interval) = 0;
+
  private:
 };
 
diff --git a/src/glimp/entry_points/egl.cc b/src/glimp/entry_points/egl.cc
index f1ecb62..da0ff5f 100644
--- a/src/glimp/entry_points/egl.cc
+++ b/src/glimp/entry_points/egl.cc
@@ -316,8 +316,12 @@
 
 EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval) {
   egl::ScopedEGLLock egl_lock;
-  SB_NOTIMPLEMENTED();
-  return false;
+  egl::Display* display = egl::GetDisplayOrSetError(dpy);
+  if (!display) {
+    return false;
+  }
+
+  return display->SwapInterval(interval);
 }
 
 EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api) {
diff --git a/src/glimp/gles/context.h b/src/glimp/gles/context.h
index 018a2f2..e0163cd 100644
--- a/src/glimp/gles/context.h
+++ b/src/glimp/gles/context.h
@@ -229,6 +229,15 @@
   // Called when eglReleaseTexImage() is called.
   bool ReleaseTextureFromEGLSurface(egl::Surface* surface);
 
+  // Utility functions for use by other modules.
+  nb::scoped_refptr<Texture> GetTexture(uint32_t id) {
+    return resource_manager_->GetTexture(id);
+  }
+
+  DrawStateDirtyFlags* GetDrawStateDirtyFlags() {
+    return &draw_state_dirty_flags_;
+  }
+
  private:
   void MakeCurrent(egl::Surface* draw, egl::Surface* read);
   void ReleaseContext();
diff --git a/src/glimp/stub/egl/display_impl.h b/src/glimp/stub/egl/display_impl.h
index f766cce..9dbf897 100644
--- a/src/glimp/stub/egl/display_impl.h
+++ b/src/glimp/stub/egl/display_impl.h
@@ -50,6 +50,8 @@
   nb::scoped_ptr<gles::ContextImpl> CreateContext(const Config* config,
                                                   int gles_version) SB_OVERRIDE;
 
+  bool SetSwapInterval(int interval) SB_OVERRIDE { return true; }
+
  private:
   void InitializeSupportedConfigs();
 
diff --git a/src/media/base/pipeline.h b/src/media/base/pipeline.h
index 4c3565b..d3d752a 100644
--- a/src/media/base/pipeline.h
+++ b/src/media/base/pipeline.h
@@ -105,8 +105,7 @@
                      const PipelineStatusCB& error_cb,
                      const PipelineStatusCB& seek_cb,
                      const BufferingStateCB& buffering_state_cb,
-                     const base::Closure& duration_change_cb,
-                     bool prefer_decode_to_texture) = 0;
+                     const base::Closure& duration_change_cb) = 0;
 
   // Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
   // teardown has completed.
@@ -181,8 +180,8 @@
   // Get the SetBoundsCB used to set the bounds of the video frame.
   virtual SetBoundsCB GetSetBoundsCB() { return SetBoundsCB(); }
 
-  // Returns whether the player is configured for outputting in punch out mode.
-  virtual bool IsPunchOutMode() { return false; }
+  // Updates the player's preference for decode-to-texture versus punch through.
+  virtual void SetDecodeToTextureOutputMode(bool /*enabled*/) {}
 };
 
 }  // namespace media
diff --git a/src/media/base/pipeline_impl.cc b/src/media/base/pipeline_impl.cc
index 6d3c5fa..aff759d 100644
--- a/src/media/base/pipeline_impl.cc
+++ b/src/media/base/pipeline_impl.cc
@@ -138,9 +138,7 @@
                          const PipelineStatusCB& error_cb,
                          const PipelineStatusCB& seek_cb,
                          const BufferingStateCB& buffering_state_cb,
-                         const base::Closure& duration_change_cb,
-                         bool prefer_decode_to_texture) {
-  UNREFERENCED_PARAMETER(prefer_decode_to_texture);
+                         const base::Closure& duration_change_cb) {
   DCHECK_EQ(collection->GetAudioDecoders()->size(), 1);
   DCHECK_EQ(collection->GetVideoDecoders()->size(), 1);
 
diff --git a/src/media/base/pipeline_impl.h b/src/media/base/pipeline_impl.h
index 358694e..8b1983f 100644
--- a/src/media/base/pipeline_impl.h
+++ b/src/media/base/pipeline_impl.h
@@ -124,8 +124,7 @@
              const PipelineStatusCB& error_cb,
              const PipelineStatusCB& seek_cb,
              const BufferingStateCB& buffering_state_cb,
-             const base::Closure& duration_change_cb,
-             bool prefer_decode_to_texture) OVERRIDE;
+             const base::Closure& duration_change_cb) OVERRIDE;
 
   // Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
   // teardown has completed.
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc
index 22d4460..044871c 100644
--- a/src/media/base/sbplayer_pipeline.cc
+++ b/src/media/base/sbplayer_pipeline.cc
@@ -20,6 +20,7 @@
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
+#include "base/optional.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/time.h"
@@ -61,7 +62,6 @@
   PipelineStatusCB seek_cb;
   Pipeline::BufferingStateCB buffering_state_cb;
   base::Closure duration_change_cb;
-  bool prefer_decode_to_texture;
 };
 
 // SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer
@@ -84,8 +84,7 @@
              const PipelineStatusCB& error_cb,
              const PipelineStatusCB& seek_cb,
              const BufferingStateCB& buffering_state_cb,
-             const base::Closure& duration_change_cb,
-             bool prefer_decode_to_texture) OVERRIDE;
+             const base::Closure& duration_change_cb) OVERRIDE;
 
   void Stop(const base::Closure& stop_cb) OVERRIDE;
   void Seek(TimeDelta time, const PipelineStatusCB& seek_cb);
@@ -106,8 +105,7 @@
   bool DidLoadingProgress() const OVERRIDE;
   PipelineStatistics GetStatistics() const OVERRIDE;
   SetBoundsCB GetSetBoundsCB() OVERRIDE;
-
-  bool IsPunchOutMode() OVERRIDE;
+  void SetDecodeToTextureOutputMode(bool preference) OVERRIDE;
 
  private:
   void StartTask(const StartTaskParameters& parameters);
@@ -197,7 +195,7 @@
   PipelineStatusCB error_cb_;
   BufferingStateCB buffering_state_cb_;
   base::Closure duration_change_cb_;
-  bool prefer_decode_to_texture_;
+  base::optional<bool> decode_to_texture_output_mode_;
 
   // Demuxer reference used for setting the preload value.
   scoped_refptr<Demuxer> demuxer_;
@@ -237,8 +235,7 @@
       audio_read_in_progress_(false),
       video_read_in_progress_(false),
       set_bounds_helper_(new SbPlayerSetBoundsHelper),
-      suspended_(false),
-      prefer_decode_to_texture_(false) {}
+      suspended_(false) {}
 
 SbPlayerPipeline::~SbPlayerPipeline() {
   DCHECK(!player_);
@@ -272,8 +269,7 @@
                              const PipelineStatusCB& error_cb,
                              const PipelineStatusCB& seek_cb,
                              const BufferingStateCB& buffering_state_cb,
-                             const base::Closure& duration_change_cb,
-                             bool prefer_decode_to_texture) {
+                             const base::Closure& duration_change_cb) {
   DCHECK(filter_collection);
 
   StartTaskParameters parameters;
@@ -284,7 +280,6 @@
   parameters.seek_cb = seek_cb;
   parameters.buffering_state_cb = buffering_state_cb;
   parameters.duration_change_cb = duration_change_cb;
-  parameters.prefer_decode_to_texture = prefer_decode_to_texture;
 
   message_loop_->PostTask(
       FROM_HERE, base::Bind(&SbPlayerPipeline::StartTask, this, parameters));
@@ -459,17 +454,15 @@
   return base::Bind(&SbPlayerSetBoundsHelper::SetBounds, set_bounds_helper_);
 }
 
-bool SbPlayerPipeline::IsPunchOutMode() {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-  base::AutoLock auto_lock(lock_);
-  if (player_) {
-    return player_->GetSbPlayerOutputMode() == kSbPlayerOutputModePunchOut;
-  } else {
-    return true;
+void SbPlayerPipeline::SetDecodeToTextureOutputMode(bool enabled) {
+  if (!message_loop_->BelongsToCurrentThread()) {
+    message_loop_->PostTask(
+        FROM_HERE, base::Bind(&SbPlayerPipeline::SetDecodeToTextureOutputMode,
+                              this, enabled));
+    return;
   }
-#else  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-  return true;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+
+  decode_to_texture_output_mode_ = enabled;
 }
 
 void SbPlayerPipeline::StartTask(const StartTaskParameters& parameters) {
@@ -487,7 +480,6 @@
   }
   buffering_state_cb_ = parameters.buffering_state_cb;
   duration_change_cb_ = parameters.duration_change_cb;
-  prefer_decode_to_texture_ = parameters.prefer_decode_to_texture;
 
   demuxer_->Initialize(
       this, BindToCurrentLoop(
@@ -576,7 +568,7 @@
     base::AutoLock auto_lock(lock_);
     player_.reset(new StarboardPlayer(
         message_loop_, audio_config, video_config, window_, drm_system, this,
-        set_bounds_helper_.get(), prefer_decode_to_texture_));
+        set_bounds_helper_.get(), *decode_to_texture_output_mode_));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
diff --git a/src/media/base/shell_data_source_reader.cc b/src/media/base/shell_data_source_reader.cc
index 07e1c8a..00e65f1 100644
--- a/src/media/base/shell_data_source_reader.cc
+++ b/src/media/base/shell_data_source_reader.cc
@@ -50,9 +50,15 @@
 
   int total_bytes_read = 0;
   while (size > 0 && !read_has_failed_) {
-    data_source_->Read(
-        position, size, data,
-        base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+    {
+      base::AutoLock auto_lock(lock_);
+      if (!data_source_) {
+        break;
+      }
+      data_source_->Read(
+          position, size, data,
+          base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+    }
 
     // wait for callback on read completion
     blocking_read_event_.Wait();
@@ -90,6 +96,8 @@
   if (data_source_) {
     // stop the data source, it can call the callback
     data_source_->Stop();
+
+    base::AutoLock auto_lock(lock_);
     data_source_ = NULL;
   }
   callback.Run();
@@ -103,7 +111,8 @@
 
 int64 ShellDataSourceReader::FileSize() {
   if (file_size_ == -1) {
-    if (!data_source_->GetSize(&file_size_)) {
+    base::AutoLock auto_lock(lock_);
+    if (data_source_ && !data_source_->GetSize(&file_size_)) {
       file_size_ = -1;
     }
   }
diff --git a/src/media/base/shell_data_source_reader.h b/src/media/base/shell_data_source_reader.h
index 01c52bd..9e50be7 100644
--- a/src/media/base/shell_data_source_reader.h
+++ b/src/media/base/shell_data_source_reader.h
@@ -22,6 +22,7 @@
 #include "base/message_loop.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "media/base/data_source.h"
 
@@ -59,6 +60,7 @@
   // blocking read callback
   virtual void BlockingReadCompleted(int bytes_read);
 
+  base::Lock lock_;
   DataSource* data_source_;
   base::WaitableEvent blocking_read_event_;
   int64 file_size_;
diff --git a/src/media/base/shell_media_platform.h b/src/media/base/shell_media_platform.h
index 9c49fce..86f2d79 100644
--- a/src/media/base/shell_media_platform.h
+++ b/src/media/base/shell_media_platform.h
@@ -28,6 +28,12 @@
 #include "media/base/shell_video_frame_provider.h"
 #include "starboard/decode_target.h"
 
+namespace cobalt {
+namespace render_tree {
+class ResourceProvider;
+}  // namespace render_tree
+}  // namespace cobalt
+
 namespace media {
 
 // This class is meant to be the single point to attach platform specific media
@@ -49,7 +55,8 @@
   // The following functions will be called when the application enters or
   // leaves suspending status.
   virtual void Suspend() {}
-  virtual void Resume() {}
+  virtual void Resume(
+      cobalt::render_tree::ResourceProvider* /*resource_provider*/) {}
 
   // Media stack buffer allocate/free functions currently only used by
   // ShellBufferFactory.
@@ -65,9 +72,12 @@
     return NULL;
   }
 
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+  virtual SbDecodeTargetGraphicsContextProvider*
+  GetSbDecodeTargetGraphicsContextProvider() { return NULL; }
+#elif SB_API_VERSION >= 3
   virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
 
   // Total number of video frames which are populating in the pipeline when
   // prerolling.
diff --git a/src/media/base/shell_video_frame_provider.cc b/src/media/base/shell_video_frame_provider.cc
index 104f64a..0cf2f98 100644
--- a/src/media/base/shell_video_frame_provider.cc
+++ b/src/media/base/shell_video_frame_provider.cc
@@ -17,6 +17,9 @@
 #include "media/base/shell_video_frame_provider.h"
 
 #include "base/logging.h"
+#if SB_API_VERSION >= 4
+#include "starboard/decode_target.h"
+#endif  // #if SB_API_VERSION >= 4
 
 namespace media {
 
@@ -99,6 +102,41 @@
   return current_frame_;
 }
 
+void ShellVideoFrameProvider::SetOutputMode(OutputMode output_mode) {
+  base::AutoLock auto_lock(frames_lock_);
+  output_mode_ = output_mode;
+}
+
+ShellVideoFrameProvider::OutputMode ShellVideoFrameProvider::GetOutputMode()
+    const {
+  base::AutoLock auto_lock(frames_lock_);
+  return output_mode_;
+}
+
+#if SB_API_VERSION >= 4
+
+void ShellVideoFrameProvider::SetGetCurrentSbDecodeTargetFunction(
+    GetCurrentSbDecodeTargetFunction function) {
+  base::AutoLock auto_lock(frames_lock_);
+  get_current_sb_decode_target_function_ = function;
+}
+
+void ShellVideoFrameProvider::ResetGetCurrentSbDecodeTargetFunction() {
+  base::AutoLock auto_lock(frames_lock_);
+  get_current_sb_decode_target_function_.Reset();
+}
+
+SbDecodeTarget ShellVideoFrameProvider::GetCurrentSbDecodeTarget() const {
+  base::AutoLock auto_lock(frames_lock_);
+  if (get_current_sb_decode_target_function_.is_null()) {
+    return kSbDecodeTargetInvalid;
+  } else {
+    return get_current_sb_decode_target_function_.Run();
+  }
+}
+
+#endif  // #if SB_API_VERSION >= 4
+
 void ShellVideoFrameProvider::AddFrame(const scoped_refptr<VideoFrame>& frame) {
   base::AutoLock auto_lock(frames_lock_);
   frames_.push_back(frame);
diff --git a/src/media/base/shell_video_frame_provider.h b/src/media/base/shell_video_frame_provider.h
index 66852c8..ae1e2d0 100644
--- a/src/media/base/shell_video_frame_provider.h
+++ b/src/media/base/shell_video_frame_provider.h
@@ -37,9 +37,9 @@
 class ShellVideoFrameProvider
     : public base::RefCountedThreadSafe<ShellVideoFrameProvider> {
  public:
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   enum OutputMode {
     kOutputModePunchOut,
@@ -69,10 +69,10 @@
   // returns NULL.
   const scoped_refptr<VideoFrame>& GetCurrentFrame();
 
-  void SetOutputMode(OutputMode output_mode) { output_mode_ = output_mode; }
-  OutputMode GetOutputMode() const { return output_mode_; }
+  void SetOutputMode(OutputMode output_mode);
+  OutputMode GetOutputMode() const;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   // For Starboard platforms that have a decode-to-texture player, we enable
   // this ShellVideoFrameProvider to act as a bridge for Cobalt code to query
   // for the current SbDecodeTarget.  In effect, we bypass all of
@@ -80,22 +80,12 @@
   // Starboard implementation to provide us with the current video frame when
   // needed.
   void SetGetCurrentSbDecodeTargetFunction(
-      GetCurrentSbDecodeTargetFunction function) {
-    get_current_sb_decode_target_function_ = function;
-  }
+      GetCurrentSbDecodeTargetFunction function);
 
-  void ResetGetCurrentSbDecodeTargetFunction() {
-    get_current_sb_decode_target_function_.Reset();
-  }
+  void ResetGetCurrentSbDecodeTargetFunction();
 
-  SbDecodeTarget GetCurrentSbDecodeTarget() const {
-    if (get_current_sb_decode_target_function_.is_null()) {
-      return kSbDecodeTargetInvalid;
-    } else {
-      return get_current_sb_decode_target_function_.Run();
-    }
-  }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  SbDecodeTarget GetCurrentSbDecodeTarget() const;
+#endif  // SB_API_VERSION >= 4
 
   void AddFrame(const scoped_refptr<VideoFrame>& frame);
   // Flush will clear all cached frames except the current frame. So the current
@@ -130,9 +120,9 @@
 #endif  // !defined(__LB_SHELL__FOR_RELEASE__)
 
   OutputMode output_mode_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   DISALLOW_COPY_AND_ASSIGN(ShellVideoFrameProvider);
 };
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index b74b39a..f8a838e 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -56,11 +56,11 @@
   audio_config_.CopyFrom(audio_config);
   video_config_.CopyFrom(video_config);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   output_mode_ = ComputeSbPlayerOutputMode(
       MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
       prefer_decode_to_texture);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
   CreatePlayer();
 
   message_loop->PostTask(
@@ -73,11 +73,13 @@
 
   set_bounds_helper_->SetPlayer(NULL);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+      ShellVideoFrameProvider::kOutputModeInvalid);
+#if SB_API_VERSION >= 4
   ShellMediaPlatform::Instance()
       ->GetVideoFrameProvider()
       ->ResetGetCurrentSbDecodeTargetFunction();
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   SbPlayerDestroy(player_);
 }
@@ -131,7 +133,7 @@
   video_info.frame_width = frame_width_;
   video_info.frame_height = frame_height_;
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   SbMediaColorMetadata sb_media_color_metadata =
       MediaToSbMediaColorMetadata(video_config_.webm_color_metadata());
   video_info.color_metadata = &sb_media_color_metadata;
@@ -139,27 +141,43 @@
   if (is_encrypted) {
     FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
   }
+#if SB_API_VERSION >= 4
+  const void* sample_buffers[] = {buffer->GetData()};
+  int sample_buffer_sizes[] = {buffer->GetDataSize()};
+  SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
+                      sample_buffers, sample_buffer_sizes, 1,
+                      TimeDeltaToSbMediaTime(buffer->GetTimestamp()),
+                      type == DemuxerStream::VIDEO ? &video_info : NULL,
+                      drm_info.subsample_count > 0 ? &drm_info : NULL);
+#else   // SB_API_VERSION >= 4
   SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
                       buffer->GetData(), buffer->GetDataSize(),
                       TimeDeltaToSbMediaTime(buffer->GetTimestamp()),
                       type == DemuxerStream::VIDEO ? &video_info : NULL,
                       drm_info.subsample_count > 0 ? &drm_info : NULL);
+#endif  // SB_API_VERSION >= 4
 }
 
 void StarboardPlayer::SetBounds(const gfx::Rect& rect) {
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION >= 4
+  const int kZIndex = 0;
+  SbPlayerSetBounds(player_, kZIndex, rect.x(), rect.y(), rect.width(),
+                    rect.height());
+#else   // SB_API_VERSION >= 4
   SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
+#endif  // SB_API_VERSION >= 4
 }
 
 void StarboardPlayer::PrepareForSeek() {
   DCHECK(message_loop_->BelongsToCurrentThread());
   ++ticket_;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, true);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.f);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
   seek_pending_ = true;
 }
 
@@ -185,11 +203,11 @@
   ++ticket_;
   SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
   seek_pending_ = false;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else  // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::SetVolume(float volume) {
@@ -211,11 +229,11 @@
   DCHECK(SbPlayerIsValid(player_));
 
   playback_rate_ = playback_rate;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, playback_rate == 0.0);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, playback_rate);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
@@ -265,11 +283,11 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
   SbPlayerSetPause(player_, true);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.0);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 
   base::AutoLock auto_lock(lock_);
 
@@ -306,6 +324,25 @@
   state_ = kResuming;
 }
 
+namespace {
+#if SB_API_VERSION >= 4
+ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
+    SbPlayerOutputMode output_mode) {
+  switch (output_mode) {
+    case kSbPlayerOutputModeDecodeToTexture:
+      return ShellVideoFrameProvider::kOutputModeDecodeToTexture;
+    case kSbPlayerOutputModePunchOut:
+      return ShellVideoFrameProvider::kOutputModePunchOut;
+    case kSbPlayerOutputModeInvalid:
+      return ShellVideoFrameProvider::kOutputModeInvalid;
+  }
+
+  NOTREACHED();
+  return ShellVideoFrameProvider::kOutputModeInvalid;
+}
+#endif  // #if SB_API_VERSION >= 4
+}  // namespace
+
 void StarboardPlayer::CreatePlayer() {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
@@ -331,25 +368,25 @@
   SbMediaVideoCodec video_codec =
       MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   player_ = SbPlayerCreate(
       window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
       &audio_header, &StarboardPlayer::DeallocateSampleCB,
       &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
       ,
-      output_mode_
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+      output_mode_,
+      ShellMediaPlatform::Instance()->GetSbDecodeTargetGraphicsContextProvider()
+#elif SB_API_VERSION >= 3
       ,
       ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
 #endif  // SB_API_VERSION >= 3
-      );
+          );
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     // If the player is setup to decode to texture, then provide Cobalt with
     // a method of querying that texture.
@@ -359,19 +396,23 @@
             base::Bind(&StarboardPlayer::GetCurrentSbDecodeTarget,
                        base::Unretained(this)));
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
+  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+      ToVideoFrameProviderOutputMode(output_mode_));
+#else   // SB_API_VERSION >= 4
+  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+      ShellVideoFrameProvider::kOutputModePunchOut);
+#endif  // SB_API_VERSION >= 4
   set_bounds_helper_->SetPlayer(this);
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 SbDecodeTarget StarboardPlayer::GetCurrentSbDecodeTarget() {
   return SbPlayerGetCurrentFrame(player_);
 }
 SbPlayerOutputMode StarboardPlayer::GetSbPlayerOutputMode() {
   return output_mode_;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 void StarboardPlayer::ClearDecoderBufferCache() {
   DCHECK(message_loop_->BelongsToCurrentThread());
@@ -446,11 +487,11 @@
     }
     SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
     SetVolume(volume_);
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
     SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else   // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else  // SB_API_VERSION < 4
     SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
     return;
   }
   host_->OnPlayerStatus(state);
@@ -505,7 +546,7 @@
                             helper->weak_this_, sample_buffer));
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 // static
 SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
     SbMediaVideoCodec codec,
@@ -520,7 +561,7 @@
     output_mode = kSbPlayerOutputModePunchOut;
   }
   if ((prefer_decode_to_texture || output_mode == kSbPlayerOutputModeInvalid) &&
-      SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, codec,
+      SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, codec,
                                   drm_system)) {
     output_mode = kSbPlayerOutputModeDecodeToTexture;
   }
@@ -528,6 +569,6 @@
 
   return output_mode;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace media
diff --git a/src/media/base/starboard_player.h b/src/media/base/starboard_player.h
index 5d9ef46..521065a 100644
--- a/src/media/base/starboard_player.h
+++ b/src/media/base/starboard_player.h
@@ -75,10 +75,10 @@
   void Suspend();
   void Resume();
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentSbDecodeTarget();
   SbPlayerOutputMode GetSbPlayerOutputMode();
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
   enum State {
@@ -119,14 +119,14 @@
                                  void* context,
                                  const void* sample_buffer);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   // Returns the output mode that should be used for a video with the given
   // specifications.
   static SbPlayerOutputMode ComputeSbPlayerOutputMode(
       SbMediaVideoCodec codec,
       SbDrmSystem drm_system,
       bool prefer_decode_to_texture);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   // The following variables are initialized in the ctor and never changed.
   const scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -158,10 +158,10 @@
   uint32 cached_video_frames_dropped_;
   base::TimeDelta preroll_timestamp_;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   // Keep track of the output mode we are supposed to output to.
   SbPlayerOutputMode output_mode_;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace media
diff --git a/src/media/base/starboard_utils.cc b/src/media/base/starboard_utils.cc
index 4f60ec9..c66d3a6 100644
--- a/src/media/base/starboard_utils.cc
+++ b/src/media/base/starboard_utils.cc
@@ -125,7 +125,7 @@
   }
 }
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 // Ensure that the enums in starboard/media.h match enums in gfx::ColorSpace.
 #define ENUM_EQ(a, b) \
   COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums)
@@ -276,6 +276,6 @@
 
   return sb_media_color_metadata;
 }
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace media
diff --git a/src/media/base/starboard_utils.h b/src/media/base/starboard_utils.h
index ef03e27..e5112d1 100644
--- a/src/media/base/starboard_utils.h
+++ b/src/media/base/starboard_utils.h
@@ -37,7 +37,7 @@
                        SbDrmSampleInfo* drm_info,
                        SbDrmSubSampleMapping* subsample_mapping);
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 SbMediaColorMetadata MediaToSbMediaColorMetadata(
     const WebMColorMetadata& webm_color_metadata);
 #endif
diff --git a/src/media/crypto/starboard_decryptor.cc b/src/media/crypto/starboard_decryptor.cc
index 48ee249..9ee01d0 100644
--- a/src/media/crypto/starboard_decryptor.cc
+++ b/src/media/crypto/starboard_decryptor.cc
@@ -53,8 +53,12 @@
     key_system_ = key_system;
   }
   DCHECK_EQ(key_system_, key_system);
-  SbDrmGenerateSessionUpdateRequest(drm_system_, type.c_str(), init_data,
-                                    init_data_length);
+  SbDrmGenerateSessionUpdateRequest(drm_system_,
+#if SB_API_VERSION >= 4
+                                    0,  // No need to distinguish between
+                                        // callbacks in EME 0.1b implementation.
+#endif                                  // SB_API_VERSION >= 4
+                                    type.c_str(), init_data, init_data_length);
   return true;
 }
 
@@ -156,6 +160,9 @@
 // static
 void StarboardDecryptor::KeyRequestFunc(SbDrmSystem drm_system,
                                         void* context,
+#if SB_API_VERSION >= 4
+                                        int /*ticket*/,
+#endif  // SB_API_VERSION >= 4
                                         const void* session_id,
                                         int session_id_size,
                                         const void* content,
diff --git a/src/media/crypto/starboard_decryptor.h b/src/media/crypto/starboard_decryptor.h
index c594013..a2ffc17 100644
--- a/src/media/crypto/starboard_decryptor.h
+++ b/src/media/crypto/starboard_decryptor.h
@@ -86,6 +86,9 @@
 
   static void KeyRequestFunc(SbDrmSystem drm_system,
                              void* context,
+#if SB_API_VERSION >= 4
+                             int ticket,
+#endif  // SB_API_VERSION >= 4
                              const void* session_id,
                              int session_id_size,
                              const void* content,
diff --git a/src/media/player/web_media_player_impl.cc b/src/media/player/web_media_player_impl.cc
index feb83a9..e1690a9 100644
--- a/src/media/player/web_media_player_impl.cc
+++ b/src/media/player/web_media_player_impl.cc
@@ -190,9 +190,6 @@
     video_frame_provider_->UnregisterMediaTimeAndSeekingStateCB(
         media_time_and_seeking_state_cb_);
     media_time_and_seeking_state_cb_.Reset();
-
-    video_frame_provider_->SetOutputMode(
-        ShellVideoFrameProvider::kOutputModeInvalid);
   }
 
 #if defined(__LB_ANDROID__)
@@ -968,10 +965,6 @@
 
   switch (buffering_state) {
     case Pipeline::kHaveMetadata:
-      video_frame_provider_->SetOutputMode(
-          (pipeline_->IsPunchOutMode() ?
-               ShellVideoFrameProvider::kOutputModePunchOut :
-               ShellVideoFrameProvider::kOutputModeDecodeToTexture));
       SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
       break;
     case Pipeline::kPrerollCompleted:
@@ -982,6 +975,7 @@
 
 void WebMediaPlayerImpl::OnDemuxerOpened() {
   DCHECK_EQ(main_loop_, MessageLoop::current());
+
   GetClient()->SourceOpened();
 }
 
@@ -1089,14 +1083,14 @@
                                         base::Unretained(decryptor_.get()));
   }
 
+  pipeline_->SetDecodeToTextureOutputMode(client_->PreferDecodeToTexture());
   pipeline_->Start(
       filter_collection_.Pass(), set_decryptor_ready_cb,
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
-      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
-      client_->PreferDecodeToTexture());
+      BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged));
 }
 
 void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
diff --git a/src/nb/allocator.h b/src/nb/allocator.h
index 75ac4ed..07ee8e0 100644
--- a/src/nb/allocator.h
+++ b/src/nb/allocator.h
@@ -61,6 +61,11 @@
   // Frees memory previously allocated via any call to Allocate().
   virtual void Free(void* memory) = 0;
 
+  // Frees memory with a size. By default it will delegate to Free().
+  virtual void FreeWithSize(void* memory, std::size_t /*size*/) {
+    Free(memory);
+  }
+
   // Returns the allocator's total capacity for allocations.  It will always
   // be true that GetSize() <= GetCapacity(), though it is possible for
   // capacity to grow and change over time.  It is also possible that due to,
diff --git a/src/nb/allocator_decorator.cc b/src/nb/allocator_decorator.cc
deleted file mode 100644
index 18578b1..0000000
--- a/src/nb/allocator_decorator.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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/allocator_decorator.h"
-
-#include "starboard/mutex.h"
-
-namespace nb {
-
-class AllocatorDecorator::Lock {
- public:
-  void Acquire() { mutex_.Acquire(); }
-  void Release() { mutex_.Release(); }
-
- private:
-  starboard::Mutex mutex_;
-};
-
-class AllocatorDecorator::ScopedLock {
- public:
-  ScopedLock(Lock* lock) : lock_(lock) {
-    if (lock_) {
-      lock_->Acquire();
-    }
-  }
-  ~ScopedLock() {
-    if (lock_) {
-      lock_->Release();
-    }
-  }
-
- private:
-  Lock* lock_;
-};
-
-AllocatorDecorator::AllocatorDecorator(scoped_ptr<Allocator> impl,
-                                       bool thread_safe)
-    : lock_(NULL), impl_(impl.Pass()) {
-  if (thread_safe) {
-    lock_ = new Lock;
-  }
-}
-
-AllocatorDecorator::~AllocatorDecorator() {
-  delete lock_;
-}
-
-void* AllocatorDecorator::Allocate(std::size_t size) {
-  ScopedLock scoped_lock(lock_);
-  return impl_->Allocate(size);
-}
-
-void* AllocatorDecorator::Allocate(std::size_t size, std::size_t alignment) {
-  ScopedLock scoped_lock(lock_);
-  return impl_->Allocate(size, alignment);
-}
-
-void* AllocatorDecorator::AllocateForAlignment(std::size_t* size,
-                                               std::size_t alignment) {
-  ScopedLock scoped_lock(lock_);
-  return impl_->AllocateForAlignment(size, alignment);
-}
-
-void AllocatorDecorator::Free(void* memory) {
-  ScopedLock scoped_lock(lock_);
-  impl_->Free(memory);
-}
-
-std::size_t AllocatorDecorator::GetCapacity() const {
-  ScopedLock scoped_lock(lock_);
-  return impl_->GetCapacity();
-}
-
-std::size_t AllocatorDecorator::GetAllocated() const {
-  ScopedLock scoped_lock(lock_);
-  return impl_->GetAllocated();
-}
-
-void AllocatorDecorator::PrintAllocations() const {
-  ScopedLock scoped_lock(lock_);
-  impl_->PrintAllocations();
-}
-
-}  // namespace nb
diff --git a/src/nb/allocator_decorator.h b/src/nb/allocator_decorator.h
deleted file mode 100644
index 78a8407..0000000
--- a/src/nb/allocator_decorator.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2014 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_ALLOCATOR_DECORATOR_H_
-#define NB_ALLOCATOR_DECORATOR_H_
-
-#include "nb/allocator.h"
-#include "nb/scoped_ptr.h"
-
-namespace nb {
-
-// Class that can be configured with an allocator behavior and threading
-// behavior.
-// Dispatches Allocate and Free calls to the internal implementation.
-// Threadsafe or not, depending on the LockTraits.
-// In non-release builds, provides a cval tracking heap size.
-class AllocatorDecorator : public Allocator {
- public:
-  AllocatorDecorator(scoped_ptr<Allocator> impl, bool thread_safe);
-  ~AllocatorDecorator();
-
-  void* Allocate(std::size_t size);
-  void* Allocate(std::size_t size, std::size_t alignment);
-  void* AllocateForAlignment(std::size_t* size, std::size_t alignment);
-  void Free(void* memory);
-  std::size_t GetCapacity() const;
-  std::size_t GetAllocated() const;
-  void PrintAllocations() const;
-
- private:
-  class Lock;
-  class ScopedLock;
-
-  Lock* lock_;
-  scoped_ptr<Allocator> impl_;
-};
-
-}  // namespace nb
-
-#endif  // NB_ALLOCATOR_DECORATOR_H_
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
index 4d601d6..653a47f 100644
--- a/src/nb/analytics/memory_tracker_helpers.h
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -23,16 +23,34 @@
 #include "nb/analytics/memory_tracker.h"
 #include "nb/atomic.h"
 #include "nb/simple_thread.h"
+#include "nb/std_allocator.h"
 #include "nb/thread_local_boolean.h"
 #include "nb/thread_local_pointer.h"
 #include "starboard/mutex.h"
 #include "starboard/thread.h"
 #include "starboard/types.h"
 #include "starboard/log.h"
+#include "starboard/memory.h"
 
 namespace nb {
 namespace analytics {
 
+// This is used to build the std allocators for the internal data structures so
+// that they don't attempt to report memory allocations, while reporting
+// memory allocations.
+class NoReportAllocator {
+ public:
+  NoReportAllocator() {}
+  NoReportAllocator(const NoReportAllocator&) {}
+  static void* Allocate(size_t n) {
+    return SbMemoryAllocateNoReport(n);
+  }
+  // Second argument can be used for accounting, but is otherwise optional.
+  static void Deallocate(void* ptr, size_t /* not used*/) {
+    SbMemoryDeallocateNoReport(ptr);
+  }
+};
+
 class AllocationGroup;
 class AllocationRecord;
 
@@ -73,7 +91,14 @@
   void GetAll(std::vector<const AllocationGroup*>* output) const;
 
  private:
-  typedef std::map<std::string, AllocationGroup*> Map;
+  // This map bypasses memory reporting of internal memory structures.
+  typedef std::map<
+      std::string,
+      AllocationGroup*,
+      std::less<std::string>,
+      nb::StdAllocator<
+          std::pair<const std::string, AllocationGroup*>,
+          NoReportAllocator> > Map;
   Map group_map_;
   AllocationGroup* unaccounted_group_;
   mutable starboard::Mutex mutex_;
@@ -131,11 +156,19 @@
   void Clear();
 
  private:
-  SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap);
-  typedef std::map<const void*, AllocationRecord> PointerMap;
+  // This map bypasses memory reporting of internal memory structures.
+  typedef std::map<
+      const void*,
+      AllocationRecord,
+      std::less<const void*>,  // required, when specifying allocator.
+      nb::StdAllocator<
+          std::pair<const void*, AllocationRecord>,
+          NoReportAllocator> > PointerMap;
 
   PointerMap pointer_map_;
   mutable starboard::Mutex mutex_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap);
 };
 
 // A per-pointer map of allocations to AllocRecords. This is a hybrid data
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h
index 002fa28..b45b1a3 100644
--- a/src/nb/analytics/memory_tracker_impl.h
+++ b/src/nb/analytics/memory_tracker_impl.h
@@ -71,6 +71,7 @@
   void RemoveGlobalTrackingHooks() SB_OVERRIDE {
     SbMemorySetReporter(NULL);
     NbSetMemoryScopeReporter(NULL);
+    global_hooks_installed_ = false;
   }
 
   bool AddMemoryTracking(const void* memory, size_t size) SB_OVERRIDE;
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc
index 26e3500..11f4f01 100644
--- a/src/nb/analytics/memory_tracker_impl_test.cc
+++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -46,8 +46,9 @@
 
 void* operator new(std::size_t size, CustomObject ignored) {
   SB_UNREFERENCED_PARAMETER(ignored);
-  // Volitile prevent optimization and elmination of operator new.
-  volatile void* ptr = SbMemoryAllocate(size);
+  // Volatile prevents optimization and elimination of operator new by the
+  // optimizing compiler.
+  volatile void* ptr = ::operator new(size);
   return const_cast<void*>(ptr);
 }
 
@@ -254,18 +255,27 @@
   static void SetUpTestCase() {
     if (!s_memory_tracker_) {
       s_memory_tracker_ = new MemoryTrackerImpl;
-      s_memory_tracker_enabled_ =
-          s_memory_tracker_->InstallGlobalTrackingHooks();
     }
+    // There are obligatory background threads for nb_test suite. This filter
+    // makes sure that they don't intercept this test.
+    s_memory_tracker_->SetThreadFilter(SbThreadGetId());
+    s_memory_tracker_enabled_ =
+        s_memory_tracker_->InstallGlobalTrackingHooks();
   }
-  static void TearDownTestCase() { SbMemorySetReporter(NULL); }
+  static void TearDownTestCase() {
+    s_memory_tracker_->RemoveGlobalTrackingHooks();
+    // Give time for threads to sync. We don't use locks on the reporter
+    // for performance reasons.
+    SbThreadSleep(250 * kSbTimeMillisecond);
+  }
 
   virtual void SetUp() {
     memory_tracker()->Clear();
-    memory_tracker()->SetThreadFilter(SbThreadGetId());
   }
 
-  virtual void TearDown() { memory_tracker()->Clear(); }
+  virtual void TearDown() {
+    memory_tracker()->Clear();
+  }
   static bool s_memory_tracker_enabled_;
 };
 bool MemoryTrackerImplTest::s_memory_tracker_enabled_ = false;
@@ -447,6 +457,7 @@
 
   EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_,
                         static_cast<void*>(group));
+  NbPopMemoryScope();
 }
 
 // Tests the expectation that the macro TRACK_MEMORY_SCOPE will capture the
diff --git a/src/nb/analytics/memory_tracker_test.cc b/src/nb/analytics/memory_tracker_test.cc
index 92cb8b3..8005802 100644
--- a/src/nb/analytics/memory_tracker_test.cc
+++ b/src/nb/analytics/memory_tracker_test.cc
@@ -102,6 +102,9 @@
   }
   static void TearDownTestCase() {
     s_memory_tracker_->RemoveGlobalTrackingHooks();
+    // Give time for all threads to sync up to the fact that the memory tracker
+    // has been removed.
+    SbThreadSleep(50 * kSbTimeMillisecond);
   }
   static bool s_memory_tracker_enabled_;
 };
diff --git a/src/nb/lexical_cast.h b/src/nb/lexical_cast.h
index 8b576c1..6ded009 100644
--- a/src/nb/lexical_cast.h
+++ b/src/nb/lexical_cast.h
@@ -46,7 +46,7 @@
 //   EXPECT_FALSE(ok);
 //   EXPECT_EQ(0, value);
 template <typename T>
-T lexical_cast(const char* s, bool* cast_ok = NULL) {
+inline T lexical_cast(const char* s, bool* cast_ok = NULL) {
   if (!s) {  // Handle NULL case of input string.
     if (cast_ok) {
       *cast_ok = false;
@@ -66,12 +66,17 @@
   return value;
 }
 
+template <typename T>
+inline T lexical_cast(const std::string& s, bool* cast_ok = NULL) {
+  return lexical_cast<T>(s.c_str(), cast_ok);
+}
+
 // int8_t and uint8_t will normally be interpreted as a char, which will
 // result in only the first character being parsed. This is obviously not
 // what we want. Therefore we provide specializations for lexical_cast for
 // these types.
 template <>
-int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) {
+inline int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) {
   int16_t value_i16 = lexical_cast<int16_t>(s, cast_ok);
   if (value_i16 < std::numeric_limits<int8_t>::min() ||
       value_i16 > std::numeric_limits<int8_t>::max()) {
@@ -84,7 +89,7 @@
 }
 
 template <typename SmallInt>
-SmallInt NarrowingLexicalCast(const char* s, bool* cast_ok) {
+inline SmallInt NarrowingLexicalCast(const char* s, bool* cast_ok) {
   int64_t value = lexical_cast<int64_t>(s, cast_ok);
   if ((value > std::numeric_limits<SmallInt>::max()) ||
       (value < std::numeric_limits<SmallInt>::min())) {
@@ -97,23 +102,23 @@
 }
 
 template <>
-uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
+inline uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
   return NarrowingLexicalCast<uint8_t>(s, cast_ok);
 }
 
 template <>
-uint16_t lexical_cast<uint16_t>(const char* s, bool* cast_ok) {
+inline uint16_t lexical_cast<uint16_t>(const char* s, bool* cast_ok) {
   return NarrowingLexicalCast<uint16_t>(s, cast_ok);
 }
 
 template <>
-uint32_t lexical_cast<uint32_t>(const char* s, bool* cast_ok) {
+inline uint32_t lexical_cast<uint32_t>(const char* s, bool* cast_ok) {
   return NarrowingLexicalCast<uint32_t>(s, cast_ok);
 }
 
 // uint64_t types will have a max value of int64_t. But this is acceptable.
 template <>
-uint64_t lexical_cast<uint64_t>(const char* s, bool* cast_ok) {
+inline uint64_t lexical_cast<uint64_t>(const char* s, bool* cast_ok) {
   int64_t val_i64 = lexical_cast<int64_t>(s, cast_ok);
   // Handle failure condition for negative values.
   if (val_i64 < 0) {
@@ -127,4 +132,4 @@
 
 }  // namespace nb
 
-#endif  // NB_LEXICAL_CAST_H_
\ No newline at end of file
+#endif  // NB_LEXICAL_CAST_H_
diff --git a/src/nb/lexical_cast_test.cc b/src/nb/lexical_cast_test.cc
index fb7e12d..88b56d5 100644
--- a/src/nb/lexical_cast_test.cc
+++ b/src/nb/lexical_cast_test.cc
@@ -189,5 +189,11 @@
   EXPECT_EQ(100000, value);  //

 }

 

+TEST(lexical_cast, StdString) {

+  bool cast_ok = false;

+  uint32_t value = lexical_cast<uint32_t>(std::string("100000"), &cast_ok);

+  EXPECT_EQ(100000, value);

+}

+

 }  // namespace

 }  // namespace nb

diff --git a/src/nb/memory_pool.cc b/src/nb/memory_pool.cc
index 4ae9a7e..6cdef60 100644
--- a/src/nb/memory_pool.cc
+++ b/src/nb/memory_pool.cc
@@ -22,15 +22,10 @@
 
 MemoryPool::MemoryPool(void* buffer,
                        std::size_t size,
-                       bool thread_safe,
                        bool verify_full_capacity,
                        std::size_t small_allocation_threshold)
     : no_free_allocator_(buffer, size),
-      reuse_allocator_(
-          scoped_ptr<Allocator>(new ReuseAllocator(&no_free_allocator_,
-                                                   size,
-                                                   small_allocation_threshold)),
-          thread_safe) {
+      reuse_allocator_(&no_free_allocator_, size, small_allocation_threshold) {
   SB_DCHECK(buffer);
   SB_DCHECK(size != 0U);
 
diff --git a/src/nb/memory_pool.h b/src/nb/memory_pool.h
index b81e893..8dff09e 100644
--- a/src/nb/memory_pool.h
+++ b/src/nb/memory_pool.h
@@ -18,7 +18,6 @@
 #define NB_MEMORY_POOL_H_
 
 #include "nb/allocator.h"
-#include "nb/allocator_decorator.h"
 #include "nb/fixed_no_free_allocator.h"
 #include "nb/reuse_allocator.h"
 
@@ -31,7 +30,6 @@
  public:
   MemoryPool(void* buffer,
              std::size_t size,
-             bool thread_safe,
              bool verify_full_capacity = false,
              std::size_t small_allocation_threshold = 0);
 
@@ -56,7 +54,7 @@
   // A reuse allocator that will be setup to fallback on the no free allocator
   // to expand its pool as memory is required for which there is no re-usable
   // space already.
-  AllocatorDecorator reuse_allocator_;
+  ReuseAllocator reuse_allocator_;
 };
 
 }  // namespace nb
diff --git a/src/nb/memory_scope.h b/src/nb/memory_scope.h
index 72e1ddc..5fd0034 100644
--- a/src/nb/memory_scope.h
+++ b/src/nb/memory_scope.h
@@ -58,13 +58,15 @@
   static NbMemoryScopeInfo memory_scope_handle_##LineNum =                \
       { NULL, Str, FileStr, LineNum, FuncStr, true };                     \
   NbPushMemoryScope(&memory_scope_handle_##LineNum);                      \
-  NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
+  NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;                  \
+  (void)pop_on_scope_end_##LineNum;
 
 #define TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
   NbMemoryScopeInfo memory_scope_handle_##LineNum = {                         \
       NULL, Str, FileStr, LineNum, FuncStr, false};                           \
   NbPushMemoryScope(&memory_scope_handle_##LineNum);                          \
-  NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
+  NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;                      \
+  (void)pop_on_scope_end_##LineNum;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 4c4b6c5..db80e43 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -24,8 +24,6 @@
         ['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
           'sources': [
             'allocator.h',
-            'allocator_decorator.cc',
-            'allocator_decorator.h',
             'analytics/memory_tracker.cc',
             'analytics/memory_tracker.h',
             'analytics/memory_tracker_impl.cc',
diff --git a/src/nb/reuse_allocator_benchmark.cc b/src/nb/reuse_allocator_benchmark.cc
index ac53c08..299b7f7 100644
--- a/src/nb/reuse_allocator_benchmark.cc
+++ b/src/nb/reuse_allocator_benchmark.cc
@@ -20,7 +20,6 @@
 #include <vector>
 
 #include "nb/allocator.h"
-#include "nb/allocator_decorator.h"
 #include "nb/fixed_no_free_allocator.h"
 #include "nb/reuse_allocator.h"
 #include "starboard/client_porting/wrap_main/wrap_main.h"
diff --git a/src/nb/std_allocator.h b/src/nb/std_allocator.h
index ce3a818..318a1b9 100644
--- a/src/nb/std_allocator.h
+++ b/src/nb/std_allocator.h
@@ -19,6 +19,8 @@
 
 #include <memory>
 
+#include "nb/allocator.h"
+#include "starboard/configuration.h"
 #include "starboard/types.h"
 
 namespace nb {
@@ -60,10 +62,11 @@
 
   // Constructor used for rebinding
   template <typename U, typename V>
-  StdAllocator(const StdAllocator<U, V>& x) {}
+  StdAllocator(const StdAllocator<U, V>&) {}
 
   pointer allocate(size_type n,
                    std::allocator<void>::const_pointer hint = NULL) {
+    SB_UNREFERENCED_PARAMETER(hint);
     void* ptr = AllocatorT::Allocate(n * sizeof(value_type));
     return static_cast<pointer>(ptr);
   }
@@ -77,6 +80,91 @@
   };
 };
 
+// A standard container compatible allocator that delegates allocations to a
+// custom allocator via a shared pointer. This differs from StdAllocator since
+// StdAllocator binds to static functions. This important difference allows
+// StdDynamicAllocator to keep memory accounting on a per-instance basis, but
+// otherwise is a tad slower, harder to instantiate and produces less readable
+// code.
+//
+// When in doubt, use StdAllocator over StdDynamicAllocator.
+//
+// Passed in nb::Allocator:
+//  Even though nb::Allocator has many functions for alignment, only the two
+//  are used within StdDynamicAllocator:
+//    void* nb::Allocator::Allocate() -and-
+//    void nb::FreeWithSize(void* ptr, size_t optional_size)
+//
+// Example of Allocator Definition:
+//  class MyAllocator : public SimpleAllocator {
+//   public:
+//    void* Allocate(size_t size) SB_OVERRIDE {
+//      return SbMemoryAllocate(size);
+//    }
+//
+//    // Second argument can be used for accounting, but is otherwise optional.
+//    void FreeWithSize(void* ptr, size_t optional_size) SB_OVERRIDE {
+//      SbMemoryDeallocate(ptr);
+//    }
+//
+//    // ... other functions
+//  };
+//
+// Example of std::vector<int>:
+//  typedef StdDynamicAllocator<int> IntAllocator;
+//  typedef std::vector<int, IntAllocator> IntVector;
+//
+//  MyAllocator my_allocator;
+//  // Note that IntVector is not default constructible!
+//  IntVector int_vector(IntAllocator(&my_allocator));
+//
+// Example of std::map<int, int>:
+//  // Note that maps require std::less() instance to be passed in whenever
+//  // a custom allocator is passed in.
+//  typedef std::map<int, int, std::less<int>, MyAllocator> IntMap;
+//  IntMap int_map(std::less<int>(), /* std::less<int> required pre-C++11 */
+//                 IntAllocator(&my_allocator));
+template <typename T>
+class StdDynamicAllocator : public std::allocator<T> {
+ public:
+  typedef typename std::allocator<T>::pointer pointer;
+  typedef typename std::allocator<T>::const_pointer const_pointer;
+  typedef typename std::allocator<T>::reference reference;
+  typedef typename std::allocator<T>::const_reference const_reference;
+  typedef typename std::allocator<T>::size_type size_type;
+  typedef typename std::allocator<T>::value_type value_type;
+  typedef typename std::allocator<T>::difference_type difference_type;
+
+  explicit StdDynamicAllocator(Allocator* allocator) : allocator_(allocator) {}
+  StdDynamicAllocator(StdDynamicAllocator& std_allocator)
+      : allocator_(std_allocator.allocator_) {}
+
+  // Constructor used for rebinding
+  template <typename U>
+  StdDynamicAllocator(const StdDynamicAllocator<U>& x)
+      : allocator_(x.allocator()) {}
+
+  pointer allocate(size_type n,
+                   std::allocator<void>::const_pointer hint = NULL) {
+    SB_UNREFERENCED_PARAMETER(hint);
+    void* ptr = allocator_->Allocate(n * sizeof(value_type));
+    return static_cast<pointer>(ptr);
+  }
+
+  void deallocate(pointer ptr, size_type n) {
+    allocator_->FreeWithSize(ptr, n * sizeof(value_type));
+  }
+  template <typename U>
+  struct rebind {
+    typedef StdDynamicAllocator<U> other;
+  };
+
+  Allocator* allocator() const { return allocator_; }
+
+ private:
+  Allocator* allocator_;
+};
+
 }  // namespace nb
 
 #endif  // NB_STD_ALLOCATOR_H_
diff --git a/src/nb/std_allocator_test.cc b/src/nb/std_allocator_test.cc
index 06d634e..a866e2b 100644
--- a/src/nb/std_allocator_test.cc
+++ b/src/nb/std_allocator_test.cc
@@ -102,5 +102,179 @@
   CountingAllocator::Reset();
 }
 
+class CountingDynamicAllocator : public nb::Allocator {
+ public:
+  CountingDynamicAllocator() : bytes_allocated_(0), num_allocations_(0) {}
+  void* Allocate(size_t size) SB_OVERRIDE {
+    bytes_allocated_ += size;
+    ++num_allocations_;
+    return SbMemoryAllocate(size);
+  }
+
+  virtual void* Allocate(std::size_t size, std::size_t alignment) SB_OVERRIDE {
+    EXPECT_TRUE(false) << "Unexpected that aligned version is called.";
+    return Allocate(size);
+  }
+
+  virtual void Free(void* memory) SB_OVERRIDE {
+    FreeWithSize(memory, 0);
+    ASSERT_TRUE(false) << "Unexpected that free without size "
+                          "version is called.";
+  }
+
+  void FreeWithSize(void* ptr, size_t optional_size) SB_OVERRIDE {
+    SbMemoryDeallocate(ptr);
+
+    bytes_allocated_ -= optional_size;
+    --num_allocations_;
+  }
+
+  std::size_t GetCapacity() const SB_OVERRIDE {
+    EXPECT_TRUE(false) << "Unexpected that GetCapacity().";
+    return 0;
+  }
+
+  std::size_t GetAllocated() const SB_OVERRIDE {
+    return static_cast<size_t>(bytes_allocated_);
+  }
+
+  void PrintAllocations() const SB_OVERRIDE {}
+
+  int64_t bytes_allocated_;
+  int64_t num_allocations_;
+};
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdDynamicAllocator, vector) {
+  typedef StdDynamicAllocator<int> IntAllocator;
+  typedef std::vector<int, IntAllocator> IntVector;
+
+  CountingDynamicAllocator counting_allocator;
+
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+  IntVector* int_vector = new IntVector(IntAllocator(&counting_allocator));
+  int_vector->push_back(0);
+
+  for (int i = 0; i < 10; ++i) {
+    int_vector->push_back(i);
+  }
+
+  // We aren't sure how much allocation is here, but we know it's more than 0.
+  EXPECT_LT(0, counting_allocator.bytes_allocated_);
+  EXPECT_LT(0, counting_allocator.num_allocations_);
+
+  delete int_vector;
+
+  // Expect that all allocations are destroyed now.
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdDynamicAllocator, map) {
+  typedef StdDynamicAllocator<int> IntAllocator;
+  typedef std::map<int, int, std::less<int>, IntAllocator> IntMap;
+
+  CountingDynamicAllocator counting_allocator;
+
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+  IntMap* int_map = new IntMap(std::less<int>(),  // required.
+                               IntAllocator(&counting_allocator));
+  for (int i = 0; i < 10; ++i) {
+    (*int_map)[i] = i;
+  }
+
+  // We aren't sure how much allocation is here, but we know it's more than 0.
+  EXPECT_LT(0, counting_allocator.bytes_allocated_);
+  EXPECT_LT(0, counting_allocator.num_allocations_);
+
+  delete int_map;
+
+  // Expect that all allocations are destroyed now.
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
+// Test the expectation that a copied vector will go through the allocator from
+// the first vector.
+TEST(StdDynamicAllocator, vector_copy) {
+  typedef StdDynamicAllocator<int> IntAllocator;
+  typedef std::vector<int, IntAllocator> IntVector;
+
+  CountingDynamicAllocator counting_allocator;
+
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+  IntVector* int_vector = new IntVector(IntAllocator(&counting_allocator));
+  int_vector->push_back(0);
+
+  for (int i = 0; i < 10; ++i) {
+    int_vector->push_back(i);
+  }
+
+  // We aren't sure how much allocation is here, but we know it's more than 0.
+  EXPECT_LT(0, counting_allocator.bytes_allocated_);
+  EXPECT_LT(0, counting_allocator.num_allocations_);
+
+  int64_t saved_bytes_allocated = counting_allocator.bytes_allocated_;
+  int64_t saved_num_allocations = counting_allocator.num_allocations_;
+
+  IntVector* int_vector_copy = new IntVector(*int_vector);
+  for (int i = 0; i < 10; ++i) {
+    int_vector_copy->push_back(i);
+  }
+
+  EXPECT_LT(saved_bytes_allocated, counting_allocator.bytes_allocated_);
+  EXPECT_LT(saved_num_allocations, counting_allocator.num_allocations_);
+
+  delete int_vector_copy;
+  delete int_vector;
+
+  // Expect that all allocations are destroyed now.
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdDynamicAllocator, map_copy) {
+  typedef StdDynamicAllocator<int> IntAllocator;
+  typedef std::map<int, int, std::less<int>, IntAllocator> IntMap;
+
+  CountingDynamicAllocator counting_allocator;
+
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+  IntMap* int_map = new IntMap(std::less<int>(),  // required.
+                               IntAllocator(&counting_allocator));
+  for (int i = 0; i < 10; ++i) {
+    (*int_map)[i] = i;
+  }
+
+  int64_t saved_bytes_allocated = counting_allocator.bytes_allocated_;
+  int64_t saved_num_allocations = counting_allocator.num_allocations_;
+
+  IntMap* int_map_copy = new IntMap(*int_map);
+
+  for (int i = 0; i < 10; ++i) {
+    (*int_map_copy)[i] = i;
+  }
+
+  EXPECT_LT(saved_bytes_allocated, counting_allocator.bytes_allocated_);
+  EXPECT_LT(saved_num_allocations, counting_allocator.num_allocations_);
+
+  delete int_map_copy;
+  delete int_map;
+
+  // Expect that all allocations are destroyed now.
+  EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+  EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
 }  // namespace
 }  // namespace nb
diff --git a/src/nb/thread_local_boolean.h b/src/nb/thread_local_boolean.h
index a70336d..d91a744 100644
--- a/src/nb/thread_local_boolean.h
+++ b/src/nb/thread_local_boolean.h
@@ -49,4 +49,4 @@
 
 }  // namespace nb.
 
-#endif  // NB_THREAD_LOCAL_POINTER_H_
\ No newline at end of file
+#endif  // NB_THREAD_LOCAL_POINTER_H_
diff --git a/src/nb/thread_local_pointer.h b/src/nb/thread_local_pointer.h
index 5c86208..75fea51 100644
--- a/src/nb/thread_local_pointer.h
+++ b/src/nb/thread_local_pointer.h
@@ -50,4 +50,4 @@
 
 }  // namespace nb.
 
-#endif  // NB_THREAD_LOCAL_POINTER_H_
\ No newline at end of file
+#endif  // NB_THREAD_LOCAL_POINTER_H_
diff --git a/src/net/base/address_list.cc b/src/net/base/address_list.cc
index 9fb4068..29c9946 100644
--- a/src/net/base/address_list.cc
+++ b/src/net/base/address_list.cc
@@ -5,6 +5,8 @@
 #include "net/base/address_list.h"
 
 #include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/values.h"
 #include "net/base/net_util.h"
@@ -58,6 +60,34 @@
 }
 
 #if defined(OS_STARBOARD)
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
+namespace {
+const char kResolveOnlyIpv6[] = "resolve_only_ipv6";
+const char kResolveOnlyIpv4[] = "resolve_only_ipv4";
+
+struct ResolveFilterFlags {
+  ResolveFilterFlags();
+
+  bool resolve_only_ipv6;
+  bool resolve_only_ipv4;
+};
+
+base::LazyInstance<ResolveFilterFlags>::Leaky g_resolve_filter_flags =
+    LAZY_INSTANCE_INITIALIZER;
+
+ResolveFilterFlags::ResolveFilterFlags() {
+  resolve_only_ipv6 =
+      CommandLine::ForCurrentProcess()->HasSwitch(kResolveOnlyIpv6);
+  resolve_only_ipv4 =
+      CommandLine::ForCurrentProcess()->HasSwitch(kResolveOnlyIpv4);
+  DCHECK(!(resolve_only_ipv6 && resolve_only_ipv4));
+}
+
+}  // namespace
+
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
 // static
 AddressList AddressList::CreateFromSbSocketResolution(
     const SbSocketResolution* resolution) {
@@ -65,6 +95,14 @@
   AddressList list;
 
   for (int i = 0; i < resolution->address_count; ++i) {
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+    SbSocketAddressType address_type = resolution->addresses[i].type;
+    ResolveFilterFlags& flags = g_resolve_filter_flags.Get();
+    if ((flags.resolve_only_ipv6 && address_type != kSbSocketAddressTypeIpv6) ||
+        (flags.resolve_only_ipv4 && address_type != kSbSocketAddressTypeIpv4)) {
+      continue;
+    }
+#endif  // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
     IPEndPoint end_point;
     if (end_point.FromSbSocketAddress(&resolution->addresses[i])) {
       list.push_back(end_point);
diff --git a/src/net/base/gzip_filter.cc b/src/net/base/gzip_filter.cc
index d4d72c3..bf0fb9b 100644
--- a/src/net/base/gzip_filter.cc
+++ b/src/net/base/gzip_filter.cc
@@ -4,6 +4,8 @@
 
 #include "net/base/gzip_filter.h"
 
+#include <algorithm>
+
 #if defined(USE_SYSTEM_ZLIB)
 #include <zlib.h>
 #else
diff --git a/src/net/base/gzip_header.cc b/src/net/base/gzip_header.cc
index 23a3dd3..5c4ef75 100644
--- a/src/net/base/gzip_header.cc
+++ b/src/net/base/gzip_header.cc
@@ -4,6 +4,8 @@
 
 #include "net/base/gzip_header.h"
 
+#include <algorithm>
+
 #if defined(USE_SYSTEM_ZLIB)
 #include <zlib.h>
 #else
diff --git a/src/net/base/net_util.cc b/src/net/base/net_util.cc
index b1473db..bfd3c72 100644
--- a/src/net/base/net_util.cc
+++ b/src/net/base/net_util.cc
@@ -2122,6 +2122,11 @@
     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
 }
 
+#if SB_HAS(IPV6)
+const unsigned char kIPv6Localhost[] = {0, 0, 0, 0, 0, 0, 0, 0,
+                                        0, 0, 0, 0, 0, 0, 0, 1};
+#endif
+
 IPAddressNumber ConvertIPv4NumberToIPv6Number(
     const IPAddressNumber& ipv4_number) {
   DCHECK(ipv4_number.size() == 4);
@@ -2260,6 +2265,13 @@
       host == "localhost6.localdomain6")
     return true;
 
+#if SB_HAS(IPV6)
+  IPAddressNumber ipv6_localhost;
+  ipv6_localhost.reserve(16);
+  ipv6_localhost.insert(ipv6_localhost.end(), kIPv6Localhost,
+                        kIPv6Localhost + arraysize(kIPv6Localhost));
+#endif
+
   IPAddressNumber ip_number;
   if (ParseIPLiteralToNumber(host, &ip_number)) {
     size_t size = ip_number.size();
@@ -2273,11 +2285,9 @@
         return IPNumberMatchesPrefix(ip_number, localhost_prefix, 8);
       }
 
-#if defined(IN6ADDR_ANY_INIT)
+#if SB_HAS(IPV6)
       case kIPv6AddressSize: {
-        struct in6_addr sin6_addr;
-        memcpy(&sin6_addr, &ip_number[0], kIPv6AddressSize);
-        return !!IN6_IS_ADDR_LOOPBACK(&sin6_addr);
+        return ip_number == ipv6_localhost;
       }
 #endif
 
diff --git a/src/net/base/tcp_listen_socket.cc b/src/net/base/tcp_listen_socket.cc
index 2a955cc..b0db082 100644
--- a/src/net/base/tcp_listen_socket.cc
+++ b/src/net/base/tcp_listen_socket.cc
@@ -66,18 +66,29 @@
 
 SocketDescriptor TCPListenSocket::CreateAndBind(const string& ip, int port) {
 #if defined(OS_STARBOARD)
+  base::optional<IPEndPoint> endpoint = ToIPEndPoint(ip, port);
+  if (!endpoint) {
+    return kSbSocketInvalid;
+  }
+  SbSocketAddressType socket_address_type;
+  switch (endpoint->GetFamily()) {
+    case ADDRESS_FAMILY_IPV4:
+      socket_address_type = kSbSocketAddressTypeIpv4;
+      break;
+    case ADDRESS_FAMILY_IPV6:
+      socket_address_type = kSbSocketAddressTypeIpv6;
+      break;
+    default:
+      return kSbSocketInvalid;
+  }
+
   SocketDescriptor socket =
-      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
+      SbSocketCreate(socket_address_type, kSbSocketProtocolTcp);
   if (SbSocketIsValid(socket)) {
     SbSocketSetReuseAddress(socket, true);
-    base::optional<IPEndPoint> endpoint = ToIPEndPoint(ip, port);
 
     SbSocketAddress sb_address;
-    bool groovy = endpoint ? true : false;
-    if (groovy) {
-      groovy &= endpoint->ToSbSocketAddress(&sb_address);
-    }
-
+    bool groovy = endpoint->ToSbSocketAddress(&sb_address);
     if (groovy) {
       groovy &= (SbSocketBind(socket, &sb_address) == kSbSocketOk);
     }
diff --git a/src/net/dial/dial_http_server.cc b/src/net/dial/dial_http_server.cc
index c4150c7..a1a44c4 100644
--- a/src/net/dial/dial_http_server.cc
+++ b/src/net/dial/dial_http_server.cc
@@ -77,11 +77,37 @@
   int ret = http_server_->GetLocalAddress(addr);
 
 #if defined(OS_STARBOARD)
+
+#if SB_API_VERSION >= 4
+  if (ret != 0) {
+    return ERR_FAILED;
+  }
+
+  SbSocketAddress local_ip = {0};
+
   // Now get the IPAddress of the network card.
+  SbSocketAddress destination = {0};
+  SbSocketAddress netmask = {0};
+
+  // Dial only works with IPv4.
+  destination.type = kSbSocketAddressTypeIpv4;
+  if (!SbSocketGetInterfaceAddress(&destination, &local_ip, NULL)) {
+    return ERR_FAILED;
+  }
+  local_ip.port = addr->port();
+
+  if (addr->FromSbSocketAddress(&local_ip)) {
+    return OK;
+  }
+
+  return ERR_FAILED;
+#else
   SbSocketAddress address;
   ret |= SbSocketGetLocalInterfaceAddress(&address) ? 0 : -1;
   address.port = addr->port();
   return (ret == 0 && addr->FromSbSocketAddress(&address)) ? OK : ERR_FAILED;
+#endif  // SB_API_VERSION >= 4
+
 #else
   // Now get the IPAddress of the network card.
   SockaddrStorage sock_addr;
diff --git a/src/net/dial/dial_system_config_starboard.cc b/src/net/dial/dial_system_config_starboard.cc
index f25e9e6..c1f7add 100644
--- a/src/net/dial/dial_system_config_starboard.cc
+++ b/src/net/dial/dial_system_config_starboard.cc
@@ -21,30 +21,44 @@
 // static
 std::string DialSystemConfig::GetFriendlyName() {
   char buffer[kMaxNameSize];
-  SbSystemGetProperty(kSbSystemPropertyFriendlyName, buffer, sizeof(buffer));
-  return std::string(buffer);
+  if (SbSystemGetProperty(kSbSystemPropertyFriendlyName, buffer,
+                          sizeof(buffer))) {
+    return std::string(buffer);
+  } else {
+    return std::string();
+  }
 }
 
 // static
 std::string DialSystemConfig::GetManufacturerName() {
   char buffer[kMaxNameSize];
-  SbSystemGetProperty(kSbSystemPropertyManufacturerName, buffer,
-                      sizeof(buffer));
-  return std::string(buffer);
+  if (SbSystemGetProperty(kSbSystemPropertyManufacturerName, buffer,
+                          sizeof(buffer))) {
+    return std::string(buffer);
+  } else {
+    return std::string();
+  }
 }
 
 // static
 std::string DialSystemConfig::GetModelName() {
   char buffer[kMaxNameSize];
-  SbSystemGetProperty(kSbSystemPropertyModelName, buffer, sizeof(buffer));
-  return std::string(buffer);
+  if (SbSystemGetProperty(kSbSystemPropertyModelName, buffer, sizeof(buffer))) {
+    return std::string(buffer);
+  } else {
+    return std::string();
+  }
 }
 
 // static
 std::string DialSystemConfig::GeneratePlatformUuid() {
   char buffer[kMaxNameSize];
-  SbSystemGetProperty(kSbSystemPropertyPlatformUuid, buffer, sizeof(buffer));
-  return std::string(buffer);
+  if (SbSystemGetProperty(kSbSystemPropertyPlatformUuid, buffer,
+                          sizeof(buffer))) {
+    return std::string(buffer);
+  } else {
+    return std::string();
+  }
 }
 
 }  // namespace net
diff --git a/src/net/dial/dial_udp_server.cc b/src/net/dial/dial_udp_server.cc
index bfeac10..53a336a 100644
--- a/src/net/dial/dial_udp_server.cc
+++ b/src/net/dial/dial_udp_server.cc
@@ -4,7 +4,11 @@
 
 #include "net/dial/dial_udp_server.h"
 
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/poem/inet_poem.h"
+#else
 #include <arpa/inet.h>
+#endif
 #include <utility>
 #include <vector>
 
diff --git a/src/net/dns/address_sorter_posix.cc b/src/net/dns/address_sorter_posix.cc
index 51ef35e..548745f 100644
--- a/src/net/dns/address_sorter_posix.cc
+++ b/src/net/dns/address_sorter_posix.cc
@@ -4,7 +4,11 @@
 
 #include "net/dns/address_sorter_posix.h"
 
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/poem/inet_poem.h"
+#else
 #include <netinet/in.h>
+#endif
 
 #if defined(OS_MACOSX) || defined(OS_BSD)
 #include <sys/socket.h>  // Must be included before ifaddrs.h.
@@ -425,4 +429,3 @@
 }
 
 }  // namespace net
-
diff --git a/src/net/quic/congestion_control/cubic.cc b/src/net/quic/congestion_control/cubic.cc
index 6ea46bd..b5e8cdf 100644
--- a/src/net/quic/congestion_control/cubic.cc
+++ b/src/net/quic/congestion_control/cubic.cc
@@ -4,6 +4,8 @@
 
 #include "net/quic/congestion_control/cubic.h"
 
+#include <algorithm>
+
 #include "base/basictypes.h"
 #include "base/logging.h"
 #include "base/time.h"
diff --git a/src/net/server/http_server.cc b/src/net/server/http_server.cc
index 4c1ed8e..80372dc 100644
--- a/src/net/server/http_server.cc
+++ b/src/net/server/http_server.cc
@@ -180,7 +180,18 @@
       base::StringToSizeT(
           base::StringPiece(request.GetHeaderValue("Content-Length")),
           &content_length);
-      if (content_length > 0 && pos < len) {
+
+      if (pos > connection->recv_data_.size()) {
+        NOTREACHED() << "pos should not be outside of recv_data_";
+        break;
+      }
+
+      size_t payload_size = connection->recv_data_.size() - pos;
+      if ((content_length > 0) && (content_length > payload_size)) {
+        break;
+      }
+
+      if (content_length > 0 && pos < (connection->recv_data_.size())) {
         request.data = connection->recv_data_.substr(pos);
       }
       pos += request.data.length();
diff --git a/src/net/socket_stream/socket_stream_job.h b/src/net/socket_stream/socket_stream_job.h
index 7c97aab..d3e9512 100644
--- a/src/net/socket_stream/socket_stream_job.h
+++ b/src/net/socket_stream/socket_stream_job.h
@@ -55,6 +55,14 @@
   void set_context(const URLRequestContext* context) {
     socket_->set_context(context);
   }
+  void set_network_task_runner(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+    network_task_runner_ = task_runner;
+  }
+
+  base::SingleThreadTaskRunner* network_task_runner() {
+    return network_task_runner_.get();
+  }
 
   virtual void Connect();
 
@@ -79,6 +87,7 @@
   virtual ~SocketStreamJob();
 
   scoped_refptr<SocketStream> socket_;
+  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(SocketStreamJob);
 };
diff --git a/src/net/udp/udp_listen_socket.cc b/src/net/udp/udp_listen_socket.cc
index 82fdfd9..56d2c63 100644
--- a/src/net/udp/udp_listen_socket.cc
+++ b/src/net/udp/udp_listen_socket.cc
@@ -16,6 +16,8 @@
 
 #include "net/udp/udp_listen_socket.h"
 
+#include <algorithm>
+
 #if defined(OS_POSIX)
 #include <errno.h>
 #include <sys/types.h>
diff --git a/src/net/url_request/url_request_throttler_entry.cc b/src/net/url_request/url_request_throttler_entry.cc
index 0899ed4..0207f83 100644
--- a/src/net/url_request/url_request_throttler_entry.cc
+++ b/src/net/url_request/url_request_throttler_entry.cc
@@ -4,6 +4,7 @@
 
 #include "net/url_request/url_request_throttler_entry.h"
 
+#include <algorithm>
 #include <cmath>
 
 #include "base/logging.h"
diff --git a/src/net/url_request/view_cache_helper.cc b/src/net/url_request/view_cache_helper.cc
index d468075..16e1a46 100644
--- a/src/net/url_request/view_cache_helper.cc
+++ b/src/net/url_request/view_cache_helper.cc
@@ -4,6 +4,8 @@
 
 #include "net/url_request/view_cache_helper.h"
 
+#include <algorithm>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/stringprintf.h"
diff --git a/src/net/websockets/websocket_job.cc b/src/net/websockets/websocket_job.cc
index 71e49b5..f2a9b3e 100644
--- a/src/net/websockets/websocket_job.cc
+++ b/src/net/websockets/websocket_job.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/lazy_instance.h"
 #include "base/string_tokenizer.h"
+#include "base/synchronization/waitable_event.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_log.h"
@@ -27,6 +28,12 @@
 
 namespace {
 
+// RFC 6455 (https://tools.ietf.org/html/rfc6455#section-7.1.1) advises
+// to use 2 * Maximum Segment Life to wait for the close on the tcp connection.
+// However, Chromium team has found that some servers wait for the client to
+// close the connection, and this leads to unnecessary long wait times.
+const int kClosingHandshakeTimeoutSeconds = 60;
+
 // lower-case header names.
 const char* const kCookieHeaders[] = {
   "cookie", "cookie2"
@@ -54,6 +61,22 @@
 static base::LazyInstance<WebSocketJobInitSingleton> g_websocket_job_init =
     LAZY_INSTANCE_INITIALIZER;
 
+bool IsWebSocketAlive(net::WebSocketJob::State state) {
+  switch (state) {
+    case net::WebSocketJob::OPEN:
+    case net::WebSocketJob::SEND_CLOSED:
+    case net::WebSocketJob::RECV_CLOSED:
+    case net::WebSocketJob::CLOSE_WAIT:
+      return true;
+    case net::WebSocketJob::INITIALIZED:
+    case net::WebSocketJob::CONNECTING:
+    case net::WebSocketJob::CLOSED:
+      return false;
+  }
+
+  return false;
+}
+
 }  // anonymous namespace
 
 namespace net {
@@ -72,6 +95,8 @@
       started_to_send_handshake_request_(false),
       handshake_request_sent_(0),
       response_cookies_save_index_(0),
+      closing_handshake_timeout_(
+          base::TimeDelta::FromSeconds(kClosingHandshakeTimeoutSeconds)),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_for_send_pending_(this)) {
 }
@@ -80,6 +105,24 @@
   DCHECK_EQ(CLOSED, state_);
   DCHECK(!delegate_);
   DCHECK(!socket_.get());
+
+  base::WaitableEvent timer_stop_event(false, false);
+
+  if (network_task_runner()) {
+    network_task_runner()->PostTask(
+        FROM_HERE, base::Bind(&WebSocketJob::StopTimer, base::Unretained(this),
+                              &timer_stop_event));
+    timer_stop_event.Wait();
+  }
+}
+
+void WebSocketJob::StopTimer(base::WaitableEvent* timer_stop_event) {
+  if (close_timer_) {
+    close_timer_.reset();
+  }
+  if (timer_stop_event) {
+    timer_stop_event->Signal();
+  }
 }
 
 void WebSocketJob::Connect() {
@@ -97,8 +140,7 @@
     case CONNECTING:
       return SendHandshakeRequest(data, len);
 
-    case OPEN:
-      {
+    case OPEN: {
         scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(len);
         SbMemoryCopy(buffer->data(), data, len);
         if (current_send_buffer_ || !send_buffer_queue_.empty()) {
@@ -108,26 +150,50 @@
         current_send_buffer_ = new DrainableIOBuffer(buffer.get(), len);
         return SendDataInternal(current_send_buffer_->data(),
                                 current_send_buffer_->BytesRemaining());
+        break;
       }
 
-    case CLOSING:
+      case CLOSE_WAIT:
+      case SEND_CLOSED:
+      case RECV_CLOSED:
     case CLOSED:
       return false;
   }
   return false;
 }
 
+void WebSocketJob::CloseTimeout() {
+  DLOG(INFO) << "Close timed out";
+  // DCHECK to make sure we've either:
+  // 1. Sent the close message, but timed out waiting to received a response
+  // 2. Closing handshake is complete, but the server has not closed the
+  // connection.
+  DCHECK((state_ == SEND_CLOSED) || (state_ == CLOSE_WAIT));
+  state_ = CLOSED;
+  CloseInternal();
+}
+
 void WebSocketJob::Close() {
   if (state_ == CLOSED)
     return;
 
-  state_ = CLOSING;
   if (current_send_buffer_) {
     // Will close in SendPending.
     return;
   }
-  state_ = CLOSED;
-  CloseInternal();
+
+  WaitForSocketClose();
+}
+
+void WebSocketJob::DelayedClose() {
+  if (!close_timer_) {
+    DLOG(INFO) << "Delaying close";
+    close_timer_ = make_scoped_ptr<base::OneShotTimer<WebSocketJob> >(
+        new base::OneShotTimer<WebSocketJob>());
+    close_timer_->Start(
+        FROM_HERE, closing_handshake_timeout_,
+        base::Bind(&WebSocketJob::CloseTimeout, base::Unretained(this)));
+  }
 }
 
 void WebSocketJob::RestartWithAuth(const AuthCredentials& credentials) {
@@ -194,7 +260,7 @@
     return;
   }
   if (delegate_) {
-    DCHECK(state_ == OPEN || state_ == CLOSING);
+    DCHECK(IsWebSocketAlive(state_));
     if (!current_send_buffer_) {
       VLOG(1) << "OnSentData current_send_buffer=NULL amount_sent="
               << amount_sent;
@@ -228,13 +294,16 @@
     OnReceivedHandshakeResponse(socket, data, len);
     return;
   }
-  DCHECK(state_ == OPEN || state_ == CLOSING);
+  DCHECK(IsWebSocketAlive(state_));
   if (delegate_ && len > 0)
     delegate_->OnReceivedData(socket, data, len);
 }
 
 void WebSocketJob::OnClose(SocketStream* socket) {
   state_ = CLOSED;
+  if (close_timer_) {
+    close_timer_.reset();
+  }
   WebSocketThrottle::GetInstance()->RemoveFromQueue(this);
   WebSocketThrottle::GetInstance()->WakeupSocketIfNecessary();
 
@@ -333,11 +402,12 @@
   if (handshake_request_sent_ >= handshake_request_->raw_length()) {
     // handshake request has been sent.
     // notify original size of handshake request to delegate.
+    std::size_t original_length = handshake_request_->original_length();
+    handshake_request_.reset();
     if (delegate_)
       delegate_->OnSentData(
           socket,
-          handshake_request_->original_length());
-    handshake_request_.reset();
+          original_length);
   }
 }
 
@@ -494,6 +564,27 @@
     socket_->Close();
 }
 
+void WebSocketJob::WaitForSocketClose() {
+  switch (state_) {
+    case CLOSE_WAIT:
+    case CLOSED:
+    case OPEN:
+      break;
+    case INITIALIZED:
+    case CONNECTING:
+      NOTREACHED();
+      break;
+    case SEND_CLOSED:
+    // Close frame has been sent, so wait for peer to send a close reponse,
+    // and a socket close.
+    case RECV_CLOSED:
+      // Our reponse to a close frame has gone out (since |send_buffer_queue_|
+      // is empty.  So wait for the peer to close the connection;
+      DelayedClose();
+      break;
+  }
+}
+
 void WebSocketJob::SendPending() {
   if (current_send_buffer_)
     return;
@@ -501,8 +592,7 @@
   // Current buffer has been sent. Try next if any.
   if (send_buffer_queue_.empty()) {
     // No more data to send.
-    if (state_ == CLOSING)
-      CloseInternal();
+    WaitForSocketClose();
     return;
   }
 
diff --git a/src/net/websockets/websocket_job.h b/src/net/websockets/websocket_job.h
index 99a9c93..32c5539 100644
--- a/src/net/websockets/websocket_job.h
+++ b/src/net/websockets/websocket_job.h
@@ -9,7 +9,10 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "base/timer.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
 #include "net/socket_stream/socket_stream_job.h"
@@ -38,8 +41,14 @@
     INITIALIZED = -1,
     CONNECTING = 0,
     OPEN = 1,
-    CLOSING = 2,
-    CLOSED = 3,
+    SEND_CLOSED = 2,  // A Close frame has been sent but not received.
+    RECV_CLOSED = 3,  // Used briefly between receiving a Close frame and
+                      // sending the response. Once the response is sent, the
+                      // state changes to CLOSED.
+    CLOSE_WAIT = 4,   // The Closing Handshake has completed, but the remote
+                      // server has not yet closed the connection.
+    CLOSED = 5,       // The Closing Handshake has completed and the connection
+                      // has been closed; or the connection is failed.
   };
 
   explicit WebSocketJob(SocketStream::Delegate* delegate);
@@ -74,6 +83,11 @@
     return handshake_response_.get();
   }
 
+  void SetState(const State new_state) {
+    DCHECK_GE(new_state, state_);  // states should only move forward.
+    state_ = new_state;
+  }
+
  private:
   friend class WebSocketThrottle;
   virtual ~WebSocketJob();
@@ -102,6 +116,11 @@
   bool SendDataInternal(const char* data, int length);
   void CloseInternal();
   void SendPending();
+  void CloseTimeout();
+  void StopTimer(base::WaitableEvent* timer_stop_event);
+
+  void WaitForSocketClose();
+  void DelayedClose();
 
   SocketStream::Delegate* delegate_;
   State state_;
@@ -124,6 +143,9 @@
 
   std::string challenge_;
 
+  base::TimeDelta closing_handshake_timeout_;
+  scoped_ptr<base::OneShotTimer<WebSocketJob> > close_timer_;
+
   base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_;
   base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_for_send_pending_;
 
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index 836ad1f..fbddb4a 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -7,17 +7,132 @@
 version previous to it.
 
 ## Version 4
-(This is a temporary template, as this version is not actually released yet.)
 
-**Introduce feature X**
+### Decode-to-Texture Player Output Mode
+Feature introducing support for decode-to-texture player output mode, and
+runtime player output mode selection and detection.
+In `starboard/configuration.h`,
+  * `SB_IS_PLAYER_PUNCHED_OUT`, `SB_IS_PLAYER_PRODUCING_TEXTURE`, and
+    `SB_IS_PLAYER_COMPOSITED` now no longer need to be defined (and should not
+    be defined) by platforms.  Instead, these capabilities are detected at
+    runtime via `SbPlayerOutputModeSupported()`.
+In `starboard/player.h`,
+  * The enum `SbPlayerOutputMode` is introduced.
+  * `SbPlayerOutputModeSupported()` is introduced to let applications query
+    for player output mode support.
+  * `SbPlayerCreate()` now takes an additional parameter that specifies the
+    desired output mode.
+  * The punch out specific function `SbPlayerSetBounds()` must now be
+    defined on all platforms, even if they don't support punch out (in which
+    case they can implement a stub).
+  * The function `SbPlayerGetCompositionHandle()` is removed.
+  * The function `SbPlayerGetTextureId()` is replaced by the new
+    `SbPlayerGetCurrentFrame()`, which returns a `SbDecodeTarget`.
+In `starboard/decode_target.h`,
+  * All get methods (`SbDecodeTargetGetPlane()` and `SbDecodeTargetGetFormat()`,
+    `SbDecodeTargetIsOpaque()`) are now replaced with `SbDecodeTargetGetInfo()`.
+  * The `SbDecodeTargetInfo` structure is introduced and is the return value
+    type of `SbDecodeTargetGetInfo()`.
+  * `SbDecdodeTargetCreate()` is now responsible for creating all its internal
+    planes, and so its `planes` parameter is replaced by `width` and
+    `height` parameters.
+  * The GLES2 version of `SbDecdodeTargetCreate()` has its EGL types
+    (`EGLDisplay`, `EGLContext`) replaced by `void*` types, so that
+    `decode_target.h` can avoid #including EGL/GLES2 headers.
+  * `SbDecodeTargetDestroy()` is renamed to `SbDecodeTargetRelease()`.
+In `starboard/player.h`, `starboard/image.h` and `starboard/decode_target.h`,
+  * Replace `SbDecodeTargetProvider` with
+    `SbDecodeTargetGraphicsContextProvider`.
+  * Instead of restricting Starboard implementations to only be able to run
+    `SbDecodeTarget` creation and destruction code on the application's
+    renderer's thread with the application's renderer's `EGLContext` current,
+    Starboard implementations can now run arbitrary code on the application's
+    renderer's thread with its `EGLContext` current.
+  * Remove `SbDecodeTargetCreate()`, `SbDecodeTarget` creation is now an
+    implementation detail to be dealt with in other Starboard API functions
+    that create `SbDecodeTargets`, like `SbImageDecode()` or `SbPlayerCreate()`.
 
-In starboard/foo.h,
+### Playback Rate
+Support for setting the playback rate on an SbPlayer.  This allows for control
+of the playback speed of video at runtime.
 
-  * Introduce SbFooBar().
-  * Remove SbFooDom().
+### Floating Point Input Vector
+Change `input.h`'s `SbInputVector` structure to contain float members instead of
+ints.
 
-**Introduce feature Y**
+### Delete SbUserApplicationTokenResults
+Deleted the vestigal struct `SbUserApplicationTokenResults` from `user.h`.
 
-In starboard/hello.h,
+### Storage Options for Encoded Audio/Video Data
+Enables the SbPlayer implementation to provide instructions to its user on
+how to store audio/video data.  Encoded audio/video data is cached once being
+demuxed and may occupy a significant amount of memory.  Enabling this feature
+allows the SbPlayer implementation to have better control on where encoded
+audio/video data is stored.
 
-  * Introduce SbHelloWorld().
+### Unified implementation of `SbMediaCanPlayMimeAndKeySystem()`
+Use a unified implementation of `SbMediaCanPlayMimeAndKeySystem()` based on
+`SbMediaIsSupported()`, `SbMediaIsAudioSupported()`, and
+`SbMediaIsVideoSupported()`.
+
+### Introduce `ticket` parameter to `SbDrmGenerateSessionUpdateRequest()`
+Introduce `ticket` parameter to `SbDrmGenerateSessionUpdateRequest()`
+and `SbDrmSessionUpdateRequestFunc` to allow distinguishing between callbacks
+from multiple concurrent calls.
+
+### Introduce `SbSocketGetInterfaceAddress()`
+`SbSocketGetInterfaceAddress()` is introduced to let applications find out
+which source IP address and the associated netmask will be used to connect to
+the destination. This is very important for multi-homed devices, and for
+certain conditions in IPv6.
+
+### Introduce `starboard/cryptography.h`
+In newly-introduced `starboard/cryptography.h`,
+  * Optional support for accelerated cryptography, which can, in
+    particular, be used for accelerating SSL.
+
+### Introduce z-index parameter to `SbPlayerSetBounds()`
+Allow `SbPlayerSetBounds` to use an extra parameter to indicate the z-index of
+the video so multiple overlapping videos can be rendered.
+
+### Media source buffer settings removed from `configuration.h`
+Media source buffer settings in Starboard.
+
+### Introduce `starboard/accessibility.h`
+In particular, the functions `SbAccessibilityGetDisplaySettings()` and
+`SbAccessibilityGetTextToSpeechSettings()` have now been introduced.
+
+Additionally, a new Starboard event, `kSbEventTypeAccessiblitySettingsChanged`,
+has been defined in `starboard/event.h`.
+
+### HDR decode support
+In `starboard/media.h`, `SbMediaColorMetadata` is now defined and it contains
+HDR metadata. The field `SbMediaColorMetadata color_metadata` is now added to
+`SbMediaVideoSampleInfo`.
+
+### Add `kSbSystemDeviceTypeAndroidTV` to `starboard/system.h`
+A new device type, `kSbSystemDeviceTypeAndroidTV`, is added to
+starboard/system.h.
+
+### Deprecate `SbSpeechSynthesisSetLanguage()`
+SbSpeechSynthesisSetLanguage() has been deprecated.
+
+### Request State Change Support
+Added `SbSystemRequestPause()`, `SbSystemRequestUnpause()`,
+`SbSystemRequestSuspend()`.
+
+`SbSystemRequestSuspend()` in particular can be hooked into a platform's "hide"
+or "minimize" window functionality.
+
+### Font Directory Path Support
+Added `kSbSystemPathFontDirectory` and `kSbSystemPathFontConfigurationDirectory`
+which can be optionally specified for platforms that want to provide system
+fonts to Starboard applications. The font and font configuration formats
+supported are application-specific.
+
+### Add `SB_NORETURN` to `starboard/configuration.h`.
+Added attribute macro `SB_NORETURN` to allow functions to be marked as noreturn.
+
+### Mark `SbSystemBreakIntoDebugger` `SB_NORETURN`.
+Add `SB_NORETURN` to declaration of `SbSystemBreakIntoDebugger`, to allow it to
+be used in a manner similar to `abort`.
diff --git a/src/starboard/accessibility.h b/src/starboard/accessibility.h
index b274da0..3e63bf1 100644
--- a/src/starboard/accessibility.h
+++ b/src/starboard/accessibility.h
@@ -26,7 +26,7 @@
 extern "C" {
 #endif
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 
 // A group of settings related to text-to-speech functionality, for platforms
 // that expose system settings for text-to-speech.
@@ -65,7 +65,7 @@
 SB_EXPORT bool SbAccessibilityGetDisplaySettings(
     SbAccessibilityDisplaySettings* out_settings);
 
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index 243c4ea..447958b 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -182,27 +182,27 @@
 static SB_C_FORCE_INLINE void
 SbAtomicNoBarrier_StorePtr(volatile SbAtomicPtr* ptr, SbAtomicPtr value) {
 #if SB_HAS(64_BIT_POINTERS)
-  return SbAtomicNoBarrier_Store64(ptr, value);
+  SbAtomicNoBarrier_Store64(ptr, value);
 #else
-  return SbAtomicNoBarrier_Store(ptr, value);
+  SbAtomicNoBarrier_Store(ptr, value);
 #endif
 }
 
 static SB_C_FORCE_INLINE void
 SbAtomicAcquire_StorePtr(volatile SbAtomicPtr* ptr, SbAtomicPtr value) {
 #if SB_HAS(64_BIT_POINTERS)
-  return SbAtomicAcquire_Store64(ptr, value);
+  SbAtomicAcquire_Store64(ptr, value);
 #else
-  return SbAtomicAcquire_Store(ptr, value);
+  SbAtomicAcquire_Store(ptr, value);
 #endif
 }
 
 static SB_C_FORCE_INLINE void
 SbAtomicRelease_StorePtr(volatile SbAtomicPtr* ptr, SbAtomicPtr value) {
 #if SB_HAS(64_BIT_POINTERS)
-  return SbAtomicRelease_Store64(ptr, value);
+  SbAtomicRelease_Store64(ptr, value);
 #else
-  return SbAtomicRelease_Store(ptr, value);
+  SbAtomicRelease_Store(ptr, value);
 #endif
 }
 
diff --git a/src/starboard/client_porting/wrap_main/wrap_main.h b/src/starboard/client_porting/wrap_main/wrap_main.h
index 7b51a6e..a12e779 100644
--- a/src/starboard/client_porting/wrap_main/wrap_main.h
+++ b/src/starboard/client_porting/wrap_main/wrap_main.h
@@ -20,7 +20,6 @@
 #ifndef STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
 #define STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
 
-#if defined(STARBOARD)
 #include "starboard/event.h"
 #include "starboard/system.h"
 
@@ -48,38 +47,10 @@
 }  // namespace client_porting
 }  // namespace starboard
 
-#if defined(_WIN32)
-// Today there is no Starboard win32. Make sure those who create it know
-// the _CrtSet* functions below should be moved to starboard win32 main.
-#error For starboard win32, please move _CrtSet* to main
-#endif
 #define STARBOARD_WRAP_SIMPLE_MAIN(main_function)                \
   void SbEventHandle(const SbEvent* event) {                     \
     ::starboard::client_porting::wrap_main::SimpleEventHandler<  \
         main_function>(event);                                   \
   }
 
-#else
-#if defined(_WIN32)
-#include <windows.h>
-
-// TODO this case should be removed when win32 is starboardized
-#define STARBOARD_WRAP_SIMPLE_MAIN(main_function)                             \
-  int main(int argc, char** argv) {                                           \
-    if (!IsDebuggerPresent()) {                                               \
-      _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); \
-      _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);  \
-      _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);   \
-      _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);                    \
-    }                                                                         \
-    return main_function(argc, argv);                                         \
-  }
-#else  // defined(_WIN32)
-#define STARBOARD_WRAP_SIMPLE_MAIN(main_function) \
-  int main(int argc, char** argv) {               \
-    return main_function(argc, argv);             \
-  }
-#endif
-
-#endif  // STARBOARD
 #endif  // STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
diff --git a/src/starboard/common/common.cc b/src/starboard/common/common.cc
index 86c63c2..1200a32 100644
--- a/src/starboard/common/common.cc
+++ b/src/starboard/common/common.cc
@@ -16,6 +16,8 @@
 
 // This audit is here so it is only displayed once every build.
 #if SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION
-#pragma message "Your platform's SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION."
-#pragma message "You are implementing the experimental SB API at your own risk!"
+#pragma message( \
+    "Your platform's SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION.")
+#pragma message( \
+    "You are implementing the experimental SB API at your own risk!")
 #endif
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index 228f48f..bfe17b0 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -25,10 +25,10 @@
       },
       'sources': [
         'common.cc',
-        'decode_target_provider.cc',
         'flat_map.h',
         'memory.cc',
         'move.h',
+        'new.cc',
         'ref_counted.cc',
         'ref_counted.h',
         'reset_and_return.h',
diff --git a/src/starboard/common/decode_target_provider.cc b/src/starboard/common/decode_target_provider.cc
deleted file mode 100644
index 673d091..0000000
--- a/src/starboard/common/decode_target_provider.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// 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/decode_target.h"
-#include "starboard/log.h"
-#include "starboard/mutex.h"
-
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
-
-namespace {
-struct Registry {
-  SbMutex mutex;
-  SbDecodeTargetProvider* provider;
-  void* context;
-};
-
-Registry g_registry = {SB_MUTEX_INITIALIZER};
-}  // namespace
-
-bool SbDecodeTargetRegisterProvider(SbDecodeTargetProvider* provider,
-                                    void* context) {
-  SbMutexAcquire(&(g_registry.mutex));
-  if (g_registry.provider || g_registry.context) {
-    SB_DLOG(WARNING) << __FUNCTION__ << ": "
-                     << "Registering (P" << provider << ", C" << context
-                     << ") while "
-                     << "(P" << g_registry.provider << ", C"
-                     << g_registry.context << ") is already registered.";
-  }
-  g_registry.provider = provider;
-  g_registry.context = context;
-  SbMutexRelease(&(g_registry.mutex));
-  return true;
-}
-
-void SbDecodeTargetUnregisterProvider(SbDecodeTargetProvider* provider,
-                                      void* context) {
-  SbMutexAcquire(&(g_registry.mutex));
-  if (g_registry.provider == provider && g_registry.context == context) {
-    g_registry.provider = NULL;
-    g_registry.context = NULL;
-  } else {
-    SB_DLOG(WARNING) << __FUNCTION__ << ": "
-                     << "Unregistering (P" << provider << ", C" << context
-                     << ") while "
-                     << "(P" << g_registry.provider << ", C"
-                     << g_registry.context << ") is what is registered.";
-  }
-  SbMutexRelease(&(g_registry.mutex));
-}
-
-SbDecodeTarget SbDecodeTargetAcquireFromProvider(SbDecodeTargetFormat format,
-                                                 int width,
-                                                 int height) {
-  SbDecodeTargetProvider* provider = NULL;
-  void* context = NULL;
-  SbMutexAcquire(&(g_registry.mutex));
-  provider = g_registry.provider;
-  context = g_registry.context;
-  SbMutexRelease(&(g_registry.mutex));
-  if (!provider) {
-    SB_DLOG(ERROR) << __FUNCTION__ << ": "
-                   << "No registered provider.";
-    return kSbDecodeTargetInvalid;
-  }
-
-  return provider->acquire(context, format, width, height);
-}
-
-void SbDecodeTargetReleaseToProvider(SbDecodeTarget decode_target) {
-  SbDecodeTargetProvider* provider = NULL;
-  void* context = NULL;
-  SbMutexAcquire(&(g_registry.mutex));
-  provider = g_registry.provider;
-  context = g_registry.context;
-  SbMutexRelease(&(g_registry.mutex));
-  if (!provider) {
-    SB_DLOG(ERROR) << __FUNCTION__ << ": "
-                   << "No registered provider.";
-    return;
-  }
-
-  provider->release(context, decode_target);
-}
-
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
diff --git a/src/starboard/common/locked_ptr.h b/src/starboard/common/locked_ptr.h
new file mode 100644
index 0000000..6f308a0
--- /dev/null
+++ b/src/starboard/common/locked_ptr.h
@@ -0,0 +1,104 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_COMMON_LOCKED_PTR_H_
+#define STARBOARD_COMMON_LOCKED_PTR_H_
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+
+#ifdef __cplusplus
+namespace starboard {
+
+// This class adds thread-safety to any class.  Note when using LockedPtr<T>,
+// the caller should use the returned object directly as a pointer without
+// holding on it.  This is enforced by making the ctors of Impl private.
+template <typename Type>
+class LockedPtr {
+ public:
+  template <typename ImplType>
+  class Impl {
+   public:
+    ~Impl() {
+      if (mutex_) {
+        mutex_->Release();
+      }
+    }
+
+    ImplType* operator->() { return ptr_; }
+
+   private:
+    friend class LockedPtr<Type>;
+
+    Impl(Mutex* mutex, ImplType* ptr) : mutex_(mutex), ptr_(ptr) {
+      SB_DCHECK(mutex);
+      SB_DCHECK(ptr);
+
+      mutex_->Acquire();
+    }
+
+    // The copy ctor transfers the ownership.  So only the last one holds the
+    // lock.  This is safe as long as the user won't hold a reference to the
+    // returned object.
+    Impl(Impl& that) {
+      mutex_ = that.mutex_;
+      ptr_ = that.ptr_;
+
+      that.mutex_ = NULL;
+      that.ptr_ = NULL;
+    }
+
+    Mutex* mutex_;
+    ImplType* ptr_;
+  };
+
+  LockedPtr() : ptr_(NULL) {}
+  explicit LockedPtr(scoped_ptr<Type> ptr) : ptr_(ptr) {
+    SB_DCHECK(ptr != NULL);
+  }
+
+  // This function is solely used to set the pointer to a valid value when it is
+  // not convenient to construct the object directly.  So it checks if the
+  // existing |ptr_| is NULL.
+  void set(scoped_ptr<Type> ptr) {
+    starboard::ScopedLock scoped_lock(mutex_);
+    SB_DCHECK(ptr_ == NULL);
+    ptr_ = ptr.Pass();
+  }
+
+  bool is_valid() const {
+    starboard::ScopedLock scoped_lock(mutex_);
+    return ptr_ != NULL;
+  }
+
+  Impl<Type> operator->() {
+    Impl<Type> impl(&mutex_, ptr_.get());
+    return impl;
+  }
+
+  Impl<const Type> operator->() const {
+    Impl<const Type> impl(&mutex_, ptr_.get());
+    return impl;
+  }
+
+ private:
+  mutable Mutex mutex_;
+  scoped_ptr<Type> ptr_;
+};
+
+}  // namespace starboard
+#endif  // __cplusplus
+
+#endif  // STARBOARD_COMMON_LOCKED_PTR_H_
diff --git a/src/starboard/common/new.cc b/src/starboard/common/new.cc
new file mode 100644
index 0000000..7e08ed0
--- /dev/null
+++ b/src/starboard/common/new.cc
@@ -0,0 +1,34 @@
+// 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.
+
+// TODO: I believe this code will have to be linked into all DLLs on
+// Windows. I think Starboard can do this through GYP when the time comes.
+
+#include "starboard/memory.h"
+
+void* operator new(size_t size) {
+  return SbMemoryAllocate(size);
+}
+
+void operator delete(void* pointer) {
+  SbMemoryDeallocate(pointer);
+}
+
+void* operator new[](size_t size) {
+  return SbMemoryAllocate(size);
+}
+
+void operator delete[](void* pointer) {
+  SbMemoryDeallocate(pointer);
+}
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index f9efb66..a549295 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -39,12 +39,12 @@
 
 // The maximum API version allowed by this version of the Starboard headers,
 // inclusive.
-#define SB_MAXIMUM_API_VERSION 4
+#define SB_MAXIMUM_API_VERSION 5
 
 // The API version that is currently open for changes, and therefore is not
 // stable or frozen. Production-oriented ports should avoid declaring that they
 // implement the experimental Starboard API version.
-#define SB_EXPERIMENTAL_API_VERSION 4
+#define SB_EXPERIMENTAL_API_VERSION 5
 
 // --- Experimental Feature Defines ------------------------------------------
 
@@ -52,49 +52,20 @@
 // starboard/CHANGELOG.md when released.  Thus, you can find examples of the
 // format your feature comments should take by checking that file.
 
-// Feature introducing support for decode-to-texture player output mode, and
-// runtime player output mode selection and detection.
-// In starboard/configuration.h,
-//   * SB_IS_PLAYER_PUNCHED_OUT, SB_IS_PLAYER_PRODUCING_TEXTURE, and
-//     SB_IS_PLAYER_COMPOSITED now no longer need to be defined (and should not
-//     be defined) by platforms.  Instead, these capabilities are detected at
-//     runtime via SbPlayerOutputModeSupported().
-// In starboard/player.h,
-//   * The enum SbPlayerOutputMode is introduced.
-//   * SbPlayerOutputModeSupported() is introduced to let applications query
-//     for player output mode support.
-//   * SbPlayerCreate() now takes an additional parameter that specifies the
-//     desired output mode.
-//   * The punch out specific function SbPlayerSetBounds() must now be
-//     defined on all platforms, even if they don't support punch out (in which
-//     case they can implement a stub).
-//   * The function SbPlayerGetCompositionHandle() is removed.
-//   * The function SbPlayerGetTextureId() is replaced by the new
-//     SbPlayerGetCurrentFrame(), which returns a SbDecodeTarget.
-// In starboard/decode_target.h,
-//   * All get methods (SbDecodeTargetGetPlane() and SbDecodeTargetGetFormat(),
-//     SbDecodeTargetIsOpaque()) are now replaced with SbDecodeTargetGetInfo().
-//   * The SbDecodeTargetInfo structure is introduced and is the return value
-//     type of SbDecodeTargetGetInfo().
-//   * SbDecdodeTargetCreate() is now responsible for creating all its internal
-//     planes, and so its |planes| parameter is replaced by |width| and
-//     |height| parameters.
-//   * The GLES2 version of SbDecdodeTargetCreate() has its EGL types
-//     (EGLDisplay, EGLContext) replaced by void* types, so that decode_target.h
-//     can avoid #including EGL/GLES2 headers.
-//   * SbDecodeTargetDestroy() is renamed to SbDecodeTargetRelease().
-#define SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION SB_EXPERIMENTAL_API_VERSION
+// EXAMPLE:
+//   // Introduce new experimental feature.
+//   //   Add a function, `SbMyNewFeature()` to `starboard/feature.h` which
+//   //   exposes functionality for my new feature.
+//   #define SB_MY_EXPERIMENTAL_FEATURE VERSION SB_EXPERIMENTAL_API_VERSION
 
-// Support for setting the playback rate on an SbPlayer.  This allows for
-// control of the playback speed of video at runtime.
-#define SB_PLAYER_SET_PLAYBACK_RATE_VERSION SB_EXPERIMENTAL_API_VERSION
+// Adds kSbSystemPropertyUserAgentAuxField to the SbSystemPropertyId
+// enum to allow platform-specific  User-Agent suffix.
+#define SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION SB_EXPERIMENTAL_API_VERSION
 
-// Change input.h's SbInputVector structure to contain float members instead of
-// ints.
-#define SB_INPUT_FLOATING_POINT_INPUT_VECTOR_VERSION SB_EXPERIMENTAL_API_VERSION
-
-// Deleted the vestigal struct SbUserApplicationTokenResults from user.h.
-#define SB_DELETE_USER_APPLICATION_TOKEN_VERSION SB_EXPERIMENTAL_API_VERSION
+// Introduce 'starboard/speech_recognizer.h'
+// This newly-introduced 'starboard/speech_recognizer.h' adds the on-device
+// speech recognizer feature.
+#define SB_SPEECH_RECOGNIZER_API_VERSION SB_EXPERIMENTAL_API_VERSION
 
 // --- Common Detected Features ----------------------------------------------
 
@@ -107,18 +78,20 @@
 // --- Common Helper Macros --------------------------------------------------
 
 // Determines a compile-time capability of the system.
-#define SB_CAN(SB_FEATURE) (defined(SB_CAN_##SB_FEATURE) && SB_CAN_##SB_FEATURE)
+#define SB_CAN(SB_FEATURE) \
+  ((defined SB_CAN_##SB_FEATURE) && SB_CAN_##SB_FEATURE)
 
 // Determines at compile-time whether this platform has a standard feature or
 // header available.
-#define SB_HAS(SB_FEATURE) (defined(SB_HAS_##SB_FEATURE) && SB_HAS_##SB_FEATURE)
+#define SB_HAS(SB_FEATURE) \
+  ((defined SB_HAS_##SB_FEATURE) && SB_HAS_##SB_FEATURE)
 
 // Determines at compile-time an inherent aspect of this platform.
-#define SB_IS(SB_FEATURE) (defined(SB_IS_##SB_FEATURE) && SB_IS_##SB_FEATURE)
+#define SB_IS(SB_FEATURE) ((defined SB_IS_##SB_FEATURE) && SB_IS_##SB_FEATURE)
 
 // Determines at compile-time whether this platform has a quirk.
 #define SB_HAS_QUIRK(SB_FEATURE) \
-  (defined(SB_HAS_QUIRK_##SB_FEATURE) && SB_HAS_QUIRK_##SB_FEATURE)
+  ((defined SB_HAS_QUIRK_##SB_FEATURE) && SB_HAS_QUIRK_##SB_FEATURE)
 
 // Determines at compile-time if this platform implements a given Starboard API
 // version number (or above).
@@ -195,7 +168,7 @@
 #define SB_RESTRICT
 #endif  // COMPILER
 #else   // __cplusplus
-#define SB_RESTRICT restrict
+#define SB_RESTRICT __restrict
 #endif  // __cplusplus
 #endif  // SB_RESTRICT
 
@@ -311,6 +284,21 @@
 #define SB_DEPRECATED_EXTERNAL(FUNC) SB_DEPRECATED(FUNC)
 #endif
 
+#if SB_API_VERSION >= 4
+// Macro to annotate a function as noreturn, which signals to the compiler
+// that the function cannot return.
+#if !defined(SB_NORETURN)
+#if SB_IS(COMPILER_GCC)
+#define SB_NORETURN __attribute__((__noreturn__))
+#elif SB_IS(COMPILER_MSVC)
+#define SB_NORETURN __declspec(noreturn)
+#else
+// Empty definition for other compilers.
+#define SB_NORETURN
+#endif
+#endif  // SB_NORETURN
+#endif  // SB_API_VERSION >= 4
+
 // --- Configuration Audits --------------------------------------------------
 
 #if !defined(SB_API_VERSION)
@@ -448,7 +436,7 @@
 #error "Your platform must define SB_HAS_TIME_THREAD_NOW in API 3 or later."
 #endif
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 #if SB_HAS(PLAYER)
 #if !SB_IS(PLAYER_COMPOSITED) && !SB_IS(PLAYER_PUNCHED_OUT) && \
     !SB_IS(PLAYER_PRODUCING_TEXTURE)
@@ -467,12 +455,12 @@
 #error "Your player can't have a composition method if it doesn't exist."
 #endif
 #endif  // SB_HAS(PLAYER)
-#else   // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION < 4
 #if defined(SB_IS_PLAYER_COMPOSITITED) || defined(SB_IS_PLAYER_PUNCHED_OUT) || \
     defined(SB_IS_PLAYER_PRODUCING_TEXTURE)
 #error "New versions of Starboard specify player output mode at runtime."
 #endif
-#endif  // // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // // SB_API_VERSION < 4
 
 #if (SB_HAS(MANY_CORES) && (SB_HAS(1_CORE) || SB_HAS(2_CORES) ||    \
                             SB_HAS(4_CORES) || SB_HAS(6_CORES))) || \
@@ -513,6 +501,63 @@
 #error "Your platform must define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER."
 #endif
 
+#if SB_API_VERSION >= 4
+
+#if !defined(SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND)
+#error \
+    "Your platform must define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND."
+#endif  // !defined(SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND)
+
+#if !defined(SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND)
+#error \
+    "Your platform must define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND."
+#endif  // !defined(SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND)
+
+#endif  // SB_API_VERSION >= 4
+
+#if SB_API_VERSION >= 4
+
+#if defined(SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT)
+#error "SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT is deprecated."
+#error "Use gyp variable |cobalt_media_buffer_non_video_budget| instead."
+#endif  // defined(SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT)
+
+#if defined(SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT)
+#error "SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT is deprecated."
+#error "Use gyp variable |cobalt_media_buffer_video_budget_1080p| instead."
+#error "Use gyp variable |cobalt_media_buffer_video_budget_4k| instead."
+#endif  // defined(SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT)
+
+#if defined(SB_MEDIA_MAIN_BUFFER_BUDGET)
+#error "SB_MEDIA_MAIN_BUFFER_BUDGET is deprecated."
+#endif  // defined(SB_MEDIA_MAIN_BUFFER_BUDGET)
+
+#if defined(SB_MEDIA_GPU_BUFFER_BUDGET)
+#error "SB_MEDIA_GPU_BUFFER_BUDGET is deprecated."
+#endif  // defined(SB_MEDIA_GPU_BUFFER_BUDGET)
+
+#if COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET <= 0
+#error "cobalt_media_buffer_non_video_budget has to be greater than 0."
+#endif  // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET < 0
+
+#if COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P <= 0
+#error "cobalt_media_buffer_video_budget_1080p has to be greater than 0."
+#endif  // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P < 0
+
+#if COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K < COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
+#error cobalt_media_buffer_video_budget_4k has to be greater than or equal to \
+           cobalt_media_buffer_video_budget_1080p.
+#endif  // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K <
+        //     COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
+
+#endif  // SB_API_VERSION >= 4
+
+#if SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if !defined(SB_HAS_SPEECH_RECOGNIZER)
+#error "Your platform must define SB_HAS_SPEECH_RECOGNIZER."
+#endif  // !defined(SB_HAS_SPEECH_RECOGNIZER)
+#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+
 // --- Derived Configuration -------------------------------------------------
 
 // Whether the current platform is little endian.
diff --git a/src/starboard/creator/ci20directfb/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp
index d897ef9..e7ca37e 100644
--- a/src/starboard/creator/ci20directfb/starboard_platform.gyp
+++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -303,8 +303,17 @@
         '<(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_pause.cc',
         '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_transform.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',
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index 8105b97..f412dec 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -266,8 +266,17 @@
         '<(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_pause.cc',
         '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_transform.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',
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h
index 23a5afe..1a587d9 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/creator/shared/configuration_public.h
@@ -122,6 +122,9 @@
 // Whether the current platform has microphone supported.
 #define SB_HAS_MICROPHONE 0
 
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
 // Whether the current platform has speech synthesis.
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
@@ -328,7 +331,7 @@
 // supported composition methods below.
 #define SB_HAS_PLAYER 1
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 // Specifies whether this platform's player will produce an OpenGL texture that
 // the client must draw every frame with its graphics rendering. It may be that
 // we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -347,43 +350,7 @@
 // this case, changing the video bounds must be tightly synchronized between the
 // player and the graphics plane.
 #define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// Specifies the maximum amount of memory used by audio buffers of media source
-// before triggering a garbage collection.  A large value will cause more memory
-// being used by audio buffers but will also make JavaScript app less likely to
-// re-download audio data.  Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT (3U * 1024U * 1024U)
-
-// Specifies the maximum amount of memory used by video buffers of media source
-// before triggering a garbage collection.  A large value will cause more memory
-// being used by video buffers but will also make JavaScript app less likely to
-// re-download video data.  Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT (16U * 1024U * 1024U)
-
-// Specifies how much memory to reserve up-front for the main media buffer
-// (usually resides inside the CPU memory) used by media source and demuxers.
-// The main media buffer can work in one of the following two ways:
-// 1. If GPU buffer is used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is non-zero), the
-//    main buffer will be used as a cache so a media buffer will be copied from
-//    GPU memory to main memory before sending to the decoder for further
-//    processing.  In this case this macro should be set to a value that is
-//    large enough to hold all media buffers being decoded.
-// 2. If GPU buffer is not used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is zero) all
-//    media buffers will reside in the main memory buffer.  In this case the
-//    macro should be set to a value that is greater than the sum of the above
-//    source buffer stream memory limits with extra room to take account of
-//    fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
-
-// Specifies how much GPU memory to reserve up-front for media source buffers.
-// This should only be set to non-zero on system with limited CPU memory and
-// excess GPU memory so the app can store media buffer in GPU memory.
-// SB_MEDIA_MAIN_BUFFER_BUDGET has to be set to a non-zero value to avoid
-// media buffers being decoded when being stored in GPU.
-#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif  // SB_API_VERSION < 4
 
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
diff --git a/src/starboard/cryptography.h b/src/starboard/cryptography.h
new file mode 100644
index 0000000..e9e2cab
--- /dev/null
+++ b/src/starboard/cryptography.h
@@ -0,0 +1,213 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Module Overview: Starboard Cryptography module
+//
+// Hardware-accelerated cryptography. Platforms should **only** implement this
+// when there are **hardware-accelerated** cryptography facilities. Applications
+// must fall back to platform-independent CPU-based algorithms if the cipher
+// algorithm isn't supported in hardware.
+//
+// # Tips for Porters
+//
+// You should implement cipher algorithms in this descending order of priority
+// to maximize usage for SSL.
+//
+//   1. GCM - The preferred block cipher mode for OpenSSL, mainly due to speed.
+//   2. CBC - If GCM is disabled, then SSL will use the CBC stream cipher.
+//      Normally this is less desirable because GCM is faster, but if CBC is
+//      hardware-accelerated, it is likely to be better than software GCM. CBC
+//      is also considered by some to be more secure.
+//   3. CTR - This can be used internally with GCM, as long as the CTR
+//      implementation only uses the last 4 bytes of the IV for the counter.
+//      (i.e. 96-bit IV, 32-bit counter)
+//   4. ECB - This is for if you only have core AES block encryption. It can be
+//      used with any of the other cipher block modes to accelerate the core AES
+//      algorithm if none of the streaming modes can be accelerated.
+//
+// Further reading on GCM vs CBC vs CTR:
+// https://crypto.stackexchange.com/questions/10775/practical-disadvantages-of-gcm-mode-encryption
+
+#ifndef STARBOARD_CRYPTOGRAPHY_H_
+#define STARBOARD_CRYPTOGRAPHY_H_
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#if SB_API_VERSION >= 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// String literal for the AES symmetric block cipher.
+// https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+#define kSbCryptographyAlgorithmAes "aes"
+
+// The direction of a cryptographic transformation.
+typedef enum SbCryptographyDirection {
+  // Cryptographic transformations that encode/encrypt data into a
+  // target format.
+  kSbCryptographyDirectionEncode,
+
+  // Cryptographic transformations that decode/decrypt data into
+  // its original form.
+  kSbCryptographyDirectionDecode,
+} SbCryptographyDirection;
+
+// The method of chaining encrypted blocks in a sequence.
+// https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+typedef enum SbCryptographyBlockCipherMode {
+  // Cipher Block Chaining mode.
+  kSbCryptographyBlockCipherModeCbc,
+
+  // Cipher Feedback mode.
+  kSbCryptographyBlockCipherModeCfb,
+
+  // Counter mode: A nonce is combined with an increasing counter.
+  kSbCryptographyBlockCipherModeCtr,
+
+  // Electronic Code Book mode: No chaining.
+  kSbCryptographyBlockCipherModeEcb,
+
+  // Output Feedback mode.
+  kSbCryptographyBlockCipherModeOfb,
+
+  // Galois/Counter Mode.
+  kSbCryptographyBlockCipherModeGcm,
+} SbCryptographyBlockCipherMode;
+
+// Private structure representing a cryptographic transformer
+// configuration and state.
+typedef struct SbCryptographyTransformerPrivate
+    SbCryptographyTransformerPrivate;
+
+// A handle to a cryptographic transformer.
+typedef SbCryptographyTransformerPrivate* SbCryptographyTransformer;
+
+// Well-defined value for an invalid transformer.
+#define kSbCryptographyInvalidTransformer ((SbCryptographyTransformer)NULL)
+
+// Returns whether the given transformer handle is valid.
+static SB_C_INLINE bool SbCryptographyIsTransformerValid(
+    SbCryptographyTransformer transformer) {
+  return transformer != kSbCryptographyInvalidTransformer;
+}
+
+// Creates an SbCryptographyTransformer with the given initialization
+// data. It can then be used to transform a series of data blocks.
+// Returns kSbCryptographyInvalidTransformer if the algorithm isn't
+// supported, or if the parameters are not compatible with the
+// algorithm.
+//
+// An SbCryptographyTransformer contains all state to start decrypting
+// a sequence of cipher blocks according to the cipher block mode. It
+// is not thread-safe, but implementations must allow different
+// SbCryptographyTransformer instances to operate on different threads.
+//
+// All parameters must not be assumed to live longer than the call to
+// this function. They must be copied by the implementation to be
+// retained.
+//
+// This function determines success mainly based on whether the combination of
+// |algorithm|, |direction|, |block_size_bits|, and |mode| is supported and
+// whether all the sizes passed in are sufficient for the selected
+// parameters. In particular, this function cannot verify that the key and IV
+// used were correct for the ciphertext, were it to be used in the decode
+// direction. The caller must make that verification.
+//
+// For example, to decrypt AES-128-CTR:
+//   SbCryptographyCreateTransformer(kSbCryptographyAlgorithmAes, 128,
+//                                   kSbCryptographyDirectionDecode,
+//                                   kSbCryptographyBlockCipherModeCtr,
+//                                   ...);
+//
+// |algorithm|: A string that represents the cipher algorithm.
+// |block_size_bits|: The block size variant of the algorithm to use, in bits.
+// |direction|: The direction in which to transform the data.
+// |mode|: The block cipher mode to use.
+// |initialization_vector|: The Initialization Vector (IV) to use. May be NULL
+// for block cipher modes that don't use it, or don't set it at init time.
+// |initialization_vector_size|: The size, in bytes, of the IV.
+// |key|: The key to use for this transformation.
+// |key_size|: The size, in bytes, of the key.
+SB_EXPORT SbCryptographyTransformer
+SbCryptographyCreateTransformer(const char* algorithm,
+                                int block_size_bits,
+                                SbCryptographyDirection direction,
+                                SbCryptographyBlockCipherMode mode,
+                                const void* initialization_vector,
+                                int initialization_vector_size,
+                                const void* key,
+                                int key_size);
+
+// Destroys the given |transformer| instance.
+SB_EXPORT void SbCryptographyDestroyTransformer(
+    SbCryptographyTransformer transformer);
+
+// Transforms one or more |block_size_bits|-sized blocks of |in_data|, with the
+// given |transformer|, placing the result in |out_data|. Returns the number of
+// bytes that were written to |out_data|, unless there was an error, in which
+// case it will return a negative number.
+//
+// |transformer|: A transformer initialized with an algorithm, IV, cipherkey,
+// and so on.
+// |in_data|: The data to be transformed.
+// |in_data_size|: The size of the data to be transformed, in bytes. Must be a
+// multiple of the transformer's |block-size_bits|, or an error will be
+// returned.
+// |out_data|: A buffer where the transformed data should be placed. Must have
+// at least capacity for |in_data_size| bytes. May point to the same memory as
+// |in_data|.
+SB_EXPORT int SbCryptographyTransform(
+    SbCryptographyTransformer transformer,
+    const void* in_data,
+    int in_data_size,
+    void* out_data);
+
+// Sets the initialization vector (IV) for a transformer, replacing the
+// internally-set IV. The block cipher mode algorithm will update the IV
+// appropriately after every block, so this is not necessary unless the stream
+// is discontiguous in some way. This happens with AES-GCM in TLS.
+SB_EXPORT void SbCryptographySetInitializationVector(
+    SbCryptographyTransformer transformer,
+    const void* initialization_vector,
+    int initialization_vector_size);
+
+// Sets additional authenticated data (AAD) for a transformer, for chaining
+// modes that support it (GCM). Returns whether the data was successfully
+// set. This can fail if the chaining mode doesn't support AAD, if the
+// parameters are invalid, or if the internal state is invalid for setting AAD.
+SB_EXPORT bool SbCryptographySetAuthenticatedData(
+    SbCryptographyTransformer transformer,
+    const void* data,
+    int data_size);
+
+// Calculates the authenticator tag for a transformer and places up to
+// |out_tag_size| bytes of it in |out_tag|. Returns whether it was able to get
+// the tag, which mainly has to do with whether it is compatible with the
+// current block cipher mode.
+SB_EXPORT bool SbCryptographyGetTag(
+    SbCryptographyTransformer transformer,
+    void* out_tag,
+    int out_tag_size);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // SB_API_VERSION >= 4
+
+#endif  // STARBOARD_CRYPTOGRAPHY_H_
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index 48198f5..20e1f70 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -34,26 +34,29 @@
 // an error. Each decoder provides a way to check if a given
 // SbDecodeTargetFormat is supported by that decoder.
 //
-// #### SbDecodeTargetProvider
+// #### SbDecodeTargetGraphicsContextProvider
 //
 // Some components may need to acquire SbDecodeTargets compatible with a certain
 // rendering context, which may need to be created on a particular thread. The
-// SbDecodeTargetProvider is a way for the primary rendering context to provide
-// an interface that can create SbDecodeTargets on a designated thread.
+// SbDecodeTargetGraphicsContextProvider are passed in to the Starboard
+// implementation from the application and provide information about the
+// rendering context that will be used to render the SbDecodeTarget objects.
+// For GLES renderers, it also provides functionality to enable the Starboard
+// implementation to run arbitrary code on the application's renderer thread
+// with the renderer's EGLContext held current.  This may be useful if your
+// SbDecodeTarget creation code needs to execute GLES commands like, for
+// example, glGenTextures().
 //
 // The primary usage is likely to be the the SbPlayer implementation on some
 // platforms.
 //
 // #### SbDecodeTarget Example
 //
-// Let's say there's an image decoder for .foo files:
+// Let's say that we are an application and we would like to use the interface
+// defined in starboard/image.h to decode an imaginary "image/foo" image type.
 //
-//     bool SbImageDecodeFooSupportsFormat(SbDecodeTargetFormat format);
-//     SbDecodeTarget SbImageDecodeFoo(void* data, int data_size,
-//                                     SbDecodeTargetFormat format);
-//
-// First, the client should enumerate which SbDecodeTargetFormats are supported
-// by that decoder.
+// First, the application should enumerate which SbDecodeTargetFormats are
+// supported by that decoder.
 //
 //     SbDecodeTargetFormat kPreferredFormats[] = {
 //         kSbDecodeTargetFormat3PlaneYUVI420,
@@ -63,19 +66,20 @@
 //
 //     SbDecodeTargetFormat format = kSbDecodeTargetFormatInvalid;
 //     for (int i = 0; i < SB_ARRAY_SIZE_INT(kPreferredFormats); ++i) {
-//       if (SbImageDecodeFooSupportsFormat(kPreferredFormats[i])) {
+//       if (SbImageIsDecodeSupported("image/foo", kPreferredFormats[i])) {
 //         format = kPreferredFormats[i];
 //         break;
 //       }
 //     }
 //
-// Now that the client has a format, it can create a decode target that it
+// Now that the application 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.
 //
-//     SbDecodeTarget target = SbImageDecodeFoo(encoded_foo_data,
-//                                              encoded_foo_data_size, format);
+//     SbDecodeTarget target = SbImageDecode(
+//         context_provider, encoded_foo_data, encoded_foo_data_size,
+//         "image/foo", format);
 //
 //     // If the decode works, you can get the texture out and render it.
 //     SbDecodeTargetInfo info;
@@ -96,10 +100,10 @@
 #if SB_HAS(BLITTER)
 #include "starboard/blitter.h"
 #elif SB_HAS(GLES2)  // SB_HAS(BLITTER)
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
 #endif  // SB_HAS(BLITTER)
 
 #ifdef __cplusplus
@@ -162,6 +166,63 @@
   kSbDecodeTargetPlaneV = 2,
 } SbDecodeTargetPlane;
 
+#if SB_API_VERSION >= 4
+#if SB_HAS(GLES2)
+struct SbDecodeTargetGraphicsContextProvider;
+
+// Signature for a Starboard implementaion function that is to be run by a
+// SbDecodeTargetGlesContextRunner callback.
+typedef void (*SbDecodeTargetGlesContextRunnerTarget)(
+    void* gles_context_runner_target_context);
+
+// Signature for a function provided by the application to the Starboard
+// implementation that will let the Starboard implementation run arbitrary code
+// on the application's renderer thread with the application's EGLContext held
+// current.
+typedef void (*SbDecodeTargetGlesContextRunner)(
+    struct SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+    SbDecodeTargetGlesContextRunnerTarget target_function,
+    void* target_function_context);
+#endif  // SB_HAS(GLES2)
+
+// In general, the SbDecodeTargetGraphicsContextProvider structure provides
+// information about the graphics context that will be used to render
+// SbDecodeTargets.  Some Starboard implementations may need to have references
+// to some graphics objects when creating/destroying resources used by
+// SbDecodeTarget.  References to SbDecodeTargetGraphicsContextProvider objects
+// should be provided to all Starboard functions that might create
+// SbDecodeTargets (e.g. SbImageDecode()).
+typedef struct SbDecodeTargetGraphicsContextProvider {
+#if SB_HAS(BLITTER)
+  // The SbBlitterDevice object that will be used to render any produced
+  // SbDecodeTargets.
+  SbBlitterDevice device;
+#elif SB_HAS(GLES2)
+  // A reference to the EGLDisplay object that hosts the EGLContext that will
+  // be used to render any produced SbDecodeTargets.  Note that it has the
+  // type |void*| in order to avoid #including the EGL header files here.
+  void* egl_display;
+  // The EGLContext object that will be used to render any produced
+  // SbDecodeTargets.  Note that it has the
+  // type |void*| in order to avoid #including the EGL header files here.
+  void* egl_context;
+
+  // The |gles_context_runner| function pointer is passed in from the
+  // application into the Starboard implementation, and can be invoked by the
+  // Starboard implementation to allow running arbitrary code on the renderer's
+  // thread with the EGLContext above held current.
+  SbDecodeTargetGlesContextRunner gles_context_runner;
+
+  // Context data that is to be passed in to |gles_context_runner| when it is
+  // invoked.
+  void* gles_context_runner_context;
+#else  // SB_HAS(BLITTER)
+  // Some compilers complain about empty structures, this is to appease them.
+  char dummy;
+#endif  // SB_HAS(BLITTER)
+} SbDecodeTargetGraphicsContextProvider;
+
+#else   // SB_API_VERSION >= 4
 // A function that can produce an SbDecodeTarget of the given |format|, |width|,
 // and |height|.
 typedef SbDecodeTarget (*SbDecodeTargetAcquireFunction)(
@@ -186,8 +247,9 @@
   // |context| will be passed into every call to |acquire| and |release|.
   void* context;
 } SbDecodeTargetProvider;
+#endif  // SB_API_VERSION >= 4
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
 // Defines a rectangular content region within a SbDecodeTargetInfoPlane
 // structure.
@@ -256,7 +318,7 @@
   SbDecodeTargetInfoPlane planes[3];
 } SbDecodeTargetInfo;
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 // --- Constants -------------------------------------------------------------
 
@@ -276,9 +338,9 @@
   return format != kSbDecodeTargetFormatInvalid;
 }
 
-#if SB_HAS(BLITTER)
+#if SB_API_VERSION < 4
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_HAS(BLITTER)
 
 // Creates a new SbBlitter-compatible SbDecodeTarget from one or more |planes|
 // created from |display|.
@@ -295,25 +357,8 @@
 SB_EXPORT SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
                                                   SbDecodeTargetPlane plane);
 
-#else  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// Creates a new SbBlitter-compatible SbDecodeTarget and all internal
-// surfaces/planes associated with it, created from |device|.
-//
-// |device|: The device from which the internal surfaces should be created on.
-// |format|: The format of the decode target being created.
-// |width|: The width of the decode target being created.
-// |height|: The height of the decode target being created.
-SB_EXPORT SbDecodeTarget SbDecodeTargetCreate(SbBlitterDevice device,
-                                              SbDecodeTargetFormat format,
-                                              int width,
-                                              int height);
-
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
 #elif SB_HAS(GLES2)  // SB_HAS(BLITTER)
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
 // 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.
@@ -335,39 +380,6 @@
 SB_EXPORT GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
                                         SbDecodeTargetPlane plane);
 
-#else  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// SBCHANGELOG: SbDecodeTargetCreate() is now responsible for creating its
-//              internal planes/textures/surfaces, instead of expecting them
-//              to be externally created and passed into it.  This allows the
-//              platform to customize the process of plane creation.  This
-//              change applies to both Blitter API and GLES platforms.
-
-// Creates a new EGL/GLES2-compatible SbDecodeTarget including all internal
-// textures/planes associated with it, which will be owned by |context|, created
-// from |display|. Must be called from a thread where |context| is current.
-// Returns kSbDecodeTargetInvalid on failure.
-//
-// void* is used in place of the EGL types so as to avoid including EGL headers,
-// which may transitively include unexpected platform-specific headers. You must
-// call reinterpret_cast<void*>(my_egl_context).
-//
-// |display|: The platform-specific graphics display (e.g. EGLDisplay) being
-// targeted.
-// |context|: The platform-specific graphics context (e.g. EGLContext) that owns
-// the provided planes, or NULL if a context is not required.
-// |format|: The format of the decode target being created, which implies how
-// many |planes| are expected, and what format they are expected to be in.
-// |width|: The width of the decode target being created.
-// |height|: The height of the decode target being created.
-SB_EXPORT SbDecodeTarget SbDecodeTargetCreate(void* display,
-                                              void* context,
-                                              SbDecodeTargetFormat format,
-                                              int width,
-                                              int height);
-
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
 #else  // SB_HAS(BLITTER)
 
 // Stub function for when graphics aren't enabled.  Always creates
@@ -381,8 +393,6 @@
 
 #endif  // SB_HAS(BLITTER)
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
 // Destroys the given SbDecodeTarget and all its associated surfaces.
 SB_EXPORT void SbDecodeTargetDestroy(SbDecodeTarget decode_target);
 
@@ -400,7 +410,24 @@
 // not have alpha values, then this function should return |true|.
 SB_EXPORT bool SbDecodeTargetIsOpaque(SbDecodeTarget decode_target);
 
-#else  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+// 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) {
+  return provider->acquire(provider->context, format, width, height);
+}
+
+// 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);
+}
+
+#else  // SB_API_VERSION < 4
 
 // SBCHANGELOG: Rename SbDecodeTargetDestroy() to SbDecodeTargetRelease() to
 //              more accurately reflect its potential semantics as a reference
@@ -422,25 +449,38 @@
 SB_EXPORT bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
                                      SbDecodeTargetInfo* out_info);
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// 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) {
-  return provider->acquire(provider->context, format, width, height);
+#if SB_HAS(GLES2)
+// Inline convenience function to run an arbitrary
+// SbDecodeTargetGlesContextRunnerTarget function through a
+// SbDecodeTargetGraphicsContextProvider.  This is intended to be called by
+// Starboard implementations, if it is necessary.
+static SB_C_INLINE void SbDecodeTargetRunInGlesContext(
+    SbDecodeTargetGraphicsContextProvider* provider,
+    SbDecodeTargetGlesContextRunnerTarget target,
+    void* target_context) {
+  provider->gles_context_runner(provider, target, target_context);
 }
 
-// Inline convenience function to release |decode_target| back to |provider|.
-static SB_C_INLINE void SbDecodeTargetReleaseToProvider(
-    SbDecodeTargetProvider* provider,
+// This function is just an implementation detail of
+// SbDecodeTargetReleaseInGlesContext() and should not be called directly.
+static SB_C_INLINE void PrivateDecodeTargetReleaser(void* context) {
+  SbDecodeTarget decode_target = (SbDecodeTarget)context;
+  SbDecodeTargetRelease(decode_target);
+}
+
+// Helper function that is possibly useful to Starboard implementations that
+// will release a decode target on the thread with the GLES context current.
+static SB_C_INLINE void SbDecodeTargetReleaseInGlesContext(
+    SbDecodeTargetGraphicsContextProvider* provider,
     SbDecodeTarget decode_target) {
-  provider->release(provider->context, decode_target);
+  SbDecodeTargetRunInGlesContext(provider, &PrivateDecodeTargetReleaser,
+                                 (void*)decode_target);
 }
 
+#endif  // SB_HAS(GLES2)
+
+#endif  // SB_API_VERSION < 4
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/starboard/doc/howto_decode_to_texture.md b/src/starboard/doc/howto_decode_to_texture.md
new file mode 100644
index 0000000..093deb4
--- /dev/null
+++ b/src/starboard/doc/howto_decode_to_texture.md
@@ -0,0 +1,137 @@
+# **HOWTO:** Decode to Texture
+
+Starboard declares the interfaces necessary to allow applications to query for
+video frames from the media player, and have them returned as texture objects
+(e.g. GLES textures).  This is useful if the application would like to apply
+a geometrical transformation to the rendered video, in order to support 360
+spherical video playback for example.  Additionally, if a Starboard platform
+implementation does not support punch-through video playback, then
+applications can choose to use decode-to-texture instead.
+
+## API Overview
+
+Decode-to-texture support involves multiple Starboard API functions spanning
+both the [`starboard/player.h`](../player.h) and
+[`starboard/decode_target.h`](../decode_target.h) Starboard interface header
+files.  Support for decode-to-texture began in version 4 of the Starboard
+API.
+
+In particular, the following function implementations require consideration
+for decode-to-texture support:
+
+From [`starboard/player.h`](../player.h),
+
+* `SbPlayerCreate()`
+* `SbPlayerOutputModeSupported()`
+* `SbPlayerGetCurrentFrame()`
+
+From [`starboard/decode_target.h`](../decode_target.h),
+
+* `SbDecodeTargetCreate()`
+* `SbDecodeTargetRelease()`
+* `SbDecodeTargetGetInfo()`
+
+Note that it is possible that you may not need to use the
+`SbDecodeTargetGraphicsContextProvider` parameter of SbPlayerCreate().  More on
+this later.
+
+## Example Application Usage Pattern
+
+We now describe an example, and typical, sequence of steps that an
+application will take when it wishes to make use of decode-to-texture
+support.
+
+![Decode-to-texture sequence diagram](resources/decode_to_texture_sequence.png)
+
+1. An application with the desire to make use of decode-to-texture will first
+   call `SbPlayerOutputModeSupported()`, passing in
+   `kSbPlayerOutputModeDecodeToTexture` for its `output_mode` parameter.  If
+   the function returns false, the application learns that decode-to-texture
+   is not supported by the platform and it will not continue with a
+   decode-to-texture flow.
+
+2. If `SbPlayerOutputModeSupported()` returns true, the application will call
+   `SbPlayerCreate()`, passing in `kSbPlayerOutputModeDecodeToTexture` for
+   the `output_mode` parameter, and also providing a valid `provider`
+   parameter (more on this later).  At this point, the Starboard platform is
+   expected to have created a player with the decode-to-texture output mode.
+
+3. Once the player is started and playback has begun, the application's
+   renderer thread (this may be a different thread than the one that called
+   `SbPlayerCreate()`) will repeatedly and frequently call
+   `SbPlayerGetCurrentFrame()`.  Since this function will be called from the
+   application's renderer thread, it should be thread-safe.  If the platform
+   uses a GLES renderer, it is guaranteed that this function will be called
+   with the GLES renderer context set as current.  This function is expected
+   to return the video frame that is to be displayed at the time the function
+   is called as a `SbDecodeTarget` object.  The `SbPlayerGetCurrentFrame()`
+   will be called at the renderer's frequency, i.e. the application render
+   loop's frame rate.  If the application's frame rate is higher than the
+   video's frame rate, then the same video frame will sometimes be returned
+   in consecutive calls to `SbPlayerGetCurrentFrame()`.  If the video's frame
+   rate is higher than the application's (this should be rare), then some
+   video frames will never be returned by calls to
+   `SbPlayerGetCurrentFrame()`; in other words, video frames will be
+   dropped.
+
+4. Once the application has acquired a valid SbDecodeTarget object through a
+   call to `SbPlayerGetCurrentFrame()`, it will call
+   `SbDecodeTargetGetInfo()` on it to extract information about the opaque
+   `SbDecodeTarget` object.  The `SbDecodeTargetGetInfo()` function fills
+   out a `SbDecodeTargetInfo` structure which contains information about the
+   decoded frame and, most importantly, a reference to a GLES texture ID on
+   GLES platforms, or a reference to a `SbBlitterSurface` object on
+   Starboard Blitter API platforms.  The application can then use this
+   texture/surface handle to render the video frame as it wishes.
+
+5. When the application is finished using the `SbDecodeTarget` that it has
+   aquired through the `SbPlayerGetCurrentFrame()` function, it will call
+   `SbDecodeTargetRelease()` on it.  The Starboard platform implementation
+   should ensure that the `SbDecodeTarget` object returned by
+   `SbPlayerGetCurrentFrame()` remains valid until the corresponding call to
+   `SbDecodeTargetRelease()` is made.  A call to `SbDecodeTargetRelease()`
+   will be made to match each call to `SbPlayerGetCurrentFrame()`.
+
+## The `SbDecodeTargetGraphicsContextProvider` object
+
+It is completely possible that a platform's Starboard implementation can
+properly implement decode-to-texture support without dealing with the
+`SbDecodeTargetGraphicsContextProvider` object (passed in to
+`SbPlayerCreate()`).  The `SbDecodeTargetGraphicsContextProvider` reference
+gives platforms references to the graphics objects that will later be used to
+render the decoded frames.  For example, on Blitter API platforms, a reference
+to the `SbBlitterDevice` object will be a mamber of
+`SbDecodeTargetGraphicsContextProvider`.  For EGL platforms, a `EGLDisplay` and
+`EGLContext` will be available, but additionally a
+`SbDecodeTargetGlesContextRunner` function pointer will be provided that will
+allow you to run arbitrary code on the renderer thread with the `EGLContext`
+held current.  This may be useful if your `SbDecodeTarget` creation code will
+required making GLES calls (e.g. `glGenTextures()`) in which a `EGLContext` must
+be held current.
+
+## Performance Considerations
+
+The decode-to-texture Starboard API is specifically designed to allow
+Starboard implementations to have the player decode directly to a texture,
+so that the application can then reference and render with that texture
+without at any point performing a pixel copy.  The
+decode-to-texture path can therefore be highly performant.
+
+It is still recommended however that platforms support the punch-through
+player mode if possible.  When using the decode-to-texture player output
+mode, the video may be rendered within the application's render loop, which
+means that non-video-related time complexity in the application's render
+loop can affect video playback's apparent frame rate, potentially resulting in
+dropped frames.  The platform can likely configure punch-through video to
+refresh on its own loop, decoupling it from the application render loop.
+
+## Implementation Strategies
+
+### Working with "push" players
+
+If your player implementation is setup with a "push" framework where
+frames are pushed out as soon as they are decoded, then you will need
+to cache those frames (along with their timestamps) so that they can be
+passed on to the application when `SbPlayerGetCurrentFrame()` is called.
+This same strategy applies if the player pushes frames only when they are meant
+to be rendered.
\ No newline at end of file
diff --git a/src/starboard/doc/resources/decode_to_texture_sequence.png b/src/starboard/doc/resources/decode_to_texture_sequence.png
new file mode 100644
index 0000000..b2b4cb4
--- /dev/null
+++ b/src/starboard/doc/resources/decode_to_texture_sequence.png
Binary files differ
diff --git a/src/starboard/doc/resources/decode_to_texture_sequence.txt b/src/starboard/doc/resources/decode_to_texture_sequence.txt
new file mode 100644
index 0000000..5118f0b
--- /dev/null
+++ b/src/starboard/doc/resources/decode_to_texture_sequence.txt
@@ -0,0 +1,14 @@
+participant Application (e.g. Cobalt) as a [fillcolor="#ffd0d0"]
+participant Starboard as s [fillcolor="#d0d0ff"]
+
+a->s: SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, ...)
+s-->a: returns bool
+Note over a: If SbPlayerOutputModeSupported()\nreturns true... [fillcolor="white"]
+a->s: SbPlayerCreate(..., kSbPlayerOutputModeDecodeToTexture, ...)
+Note over a: Start of render loop [fillcolor="#ffffd0"]
+a->s: SbPlayerGetCurrentFrame()
+s-->a: returns SbDecodeTarget
+Note over a: Extracts GLES texture(s) from the\nSbDecodeTarget object and\nrenders a scene with them. [fillcolor="white"]
+a->s: SbDecodeTargetRelease()
+Note over a: Goto: Start of render loop [fillcolor="#ffffd0"]
+
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index f7f4544..dad7d24 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -78,10 +78,18 @@
 // A callback that will receive generated session update request when requested
 // from a SbDrmSystem. |drm_system| will be the DRM system that
 // SbDrmGenerateSessionUpdateRequest() was called on. |context| will be the same
-// context that was passed into the call to SbDrmCreateSystem(). |session_id|
-// can be NULL if there was an error generating the request.
+// context that was passed into the call to SbDrmCreateSystem().
+#if SB_API_VERSION >= 4
+// |ticket| will be the same ticket that was passed
+// to SbDrmGenerateSessionUpdateRequest() or |kSbDrmTicketInvalid| if
+// the update request was generated by the DRM system.
+#endif  // SB_API_VERSION >= 4
+// |session_id| can be NULL if there was an error generating the request.
 typedef void (*SbDrmSessionUpdateRequestFunc)(SbDrmSystem drm_system,
                                               void* context,
+#if SB_API_VERSION >= 4
+                                              int ticket,
+#endif  // SB_API_VERSION >= 4
                                               const void* session_id,
                                               int session_id_size,
                                               const void* content,
@@ -91,10 +99,16 @@
 // A callback for notifications that a session has been added, and subsequent
 // encrypted samples are actively ready to be decoded. |drm_system| will be the
 // DRM system that SbDrmUpdateSession() was called on. |context| will be the
-// same context passed into that call to SbDrmCreateSystem(). |succeeded| is
-// whether the session was successfully updated or not.
+// same context passed into that call to SbDrmCreateSystem().
+#if SB_API_VERSION >= 4
+// |ticket| will be the same ticket that was passed to SbDrmUpdateSession().
+#endif  // SB_API_VERSION >= 4
+// |succeeded| is whether the session was successfully updated or not.
 typedef void (*SbDrmSessionUpdatedFunc)(SbDrmSystem drm_system,
                                         void* context,
+#if SB_API_VERSION >= 4
+                                        int ticket,
+#endif  // SB_API_VERSION >= 4
                                         const void* session_id,
                                         int session_id_size,
                                         bool succeeded);
@@ -104,6 +118,11 @@
 // An invalid SbDrmSystem.
 #define kSbDrmSystemInvalid ((SbDrmSystem)NULL)
 
+#if SB_API_VERSION >= 4
+// A ticket for callback invocations initiated by the DRM system.
+#define kSbDrmTicketInvalid kSbInvalidInt
+#endif  // SB_API_VERSION >= 4
+
 // --- Functions -------------------------------------------------------------
 
 // Indicates whether |drm_system| is a valid SbDrmSystem.
@@ -111,6 +130,13 @@
   return drm != kSbDrmSystemInvalid;
 }
 
+#if SB_API_VERSION >= 4
+// Indicates whether |ticket| is a valid ticket.
+static SB_C_FORCE_INLINE bool SbDrmTicketIsValid(int ticket) {
+  return ticket != kSbDrmTicketInvalid;
+}
+#endif  // SB_API_VERSION >= 4
+
 // Creates a new DRM system that can be used when constructing an SbPlayer
 // or an SbDecoder.
 //
@@ -146,9 +172,8 @@
 // |SbDrmCreateSystem|) and a populated request, or it sends NULL |session_id|
 // if an error occurred.
 //
-// |drm_system|'s |context| may be used to distinguish callbacks from
-// multiple concurrent calls to SbDrmGenerateSessionUpdateRequest(), and/or
-// to route callbacks back to an object instance.
+// |drm_system|'s |context| may be used to route callbacks back to an object
+// instance.
 //
 // Callbacks may be called either from the current thread before this function
 // returns or from another thread.
@@ -156,22 +181,38 @@
 // |drm_system|: The DRM system that defines the key system used for the
 // session update request payload as well as the callback function that is
 // called as a result of the function being called.
+#if SB_API_VERSION >= 4
+// |ticket|: The opaque ID that allows to distinguish callbacks from
+// multiple concurrent calls to SbDrmGenerateSessionUpdateRequest(),
+// which will be passed to |update_request_callback| as-is. It is
+// the responsibility of the caller to establish ticket uniqueness, issuing
+// multiple request with the same ticket may result in undefined behavior.
+// Value |kSbDrmTicketInvalid| must not be used.
+#endif  // SB_API_VERSION >= 4
 // |type|: The case-sensitive type of the session update request payload.
 // |initialization_data|: The data for which the session update request payload
 // is created.
 // |initialization_data_size|: The size of the session update request payload.
 SB_EXPORT void SbDrmGenerateSessionUpdateRequest(
     SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+    int ticket,
+#endif  // SB_API_VERSION >= 4
     const char* type,
     const void* initialization_data,
     int initialization_data_size);
 
 // Update session with |key|, in |drm_system|'s key system, from the license
-// server response. |request| must be the corresponding returned request from
-// SbDrmGenerateSessionUpdateRequest(). Calls |session_updated_callback| with
-// |context| and whether the update succeeded. |context| may be used to
-// distinguish callbacks from multiple concurrent calls to SbDrmUpdateSession(),
-// and/or to route callbacks back to an object instance.
+// server response. Calls |session_updated_callback| with |context| and whether
+// the update succeeded. |context| may be used to route callbacks back to
+// an object instance.
+#if SB_API_VERSION >= 4
+// |ticket| is the opaque ID that allows to distinguish callbacks from
+// multiple concurrent calls to SbDrmUpdateSession(), which will be passed
+// to |session_updated_callback| as-is. It is the responsibility of the caller
+// to establish ticket uniqueness, issuing multiple calls with the same ticket
+// may result in undefined behavior.
+#endif  // SB_API_VERSION >= 4
 //
 // Once the session is successfully updated, an SbPlayer or SbDecoder associated
 // with that DRM key system will be able to decrypt encrypted samples.
@@ -179,6 +220,9 @@
 // |drm_system|'s |session_updated_callback| may called either from the
 // current thread before this function returns or from another thread.
 SB_EXPORT void SbDrmUpdateSession(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+                                  int ticket,
+#endif  // SB_API_VERSION >= 4
                                   const void* key,
                                   int key_size,
                                   const void* session_id,
diff --git a/src/starboard/event.h b/src/starboard/event.h
index f7d9a68..9ab88c7 100644
--- a/src/starboard/event.h
+++ b/src/starboard/event.h
@@ -152,7 +152,7 @@
   // directly. The data type is an internally-defined structure.
   kSbEventTypeScheduled,
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   // The platform's accessibility settings have changed. The application should
   // query the accessibility settings using the appropriate APIs to get the
   // new settings.
diff --git a/src/starboard/examples/window/main.cc b/src/starboard/examples/window/main.cc
index cea9a13..fb47cdb 100644
--- a/src/starboard/examples/window/main.cc
+++ b/src/starboard/examples/window/main.cc
@@ -13,6 +13,8 @@
 // limitations under the License.
 
 #include <iomanip>
+#include <set>
+#include <sstream>
 
 #include "starboard/event.h"
 #include "starboard/input.h"
@@ -20,33 +22,109 @@
 #include "starboard/system.h"
 #include "starboard/window.h"
 
+namespace {
+// Helper set to keep track of which keys are currently pressed.
+typedef std::set<SbKey> KeySet;
+KeySet s_is_pressed;
+}  // namespace
+
+SbWindow g_window;
+
 void SbEventHandle(const SbEvent* event) {
   switch (event->type) {
     case kSbEventTypeStart: {
-      SB_DLOG(INFO) << __FUNCTION__ << ": START";
+      SB_LOG(INFO) << "START";
       SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
-      SbWindow window = SbWindowCreate(NULL);
-      SB_CHECK(SbWindowIsValid(window));
+      g_window = SbWindowCreate(NULL);
+      SB_CHECK(SbWindowIsValid(g_window));
+
+#if SB_API_VERSION >= 4
+      SB_LOG(INFO) << "    F1 - Pause";
+      SB_LOG(INFO) << "    F2 - Unpause";
+      SB_LOG(INFO) << "    F3 - Suspend";
+#endif  // SB_API_VERSION >= 4
+      SB_LOG(INFO) << "    F5 - Stop";
       break;
     }
     case kSbEventTypeInput: {
       SbInputData* data = static_cast<SbInputData*>(event->data);
-      SB_DLOG(INFO) << __FUNCTION__ << ": INPUT: type=" << data->type
-                    << ", window=" << data->window
-                    << ", device_type=" << data->device_type
-                    << ", device_id=" << data->device_id
-                    << ", key=0x" << std::hex << data->key
-                    << ", character=" << data->character
-                    << ", modifiers=0x" << std::hex << data->key_modifiers
-                    << ", location=" << std::dec << data->key_location
-                    << ", position="
-                    << "[ " << data->position.x << " , " << data->position.y
-                    << " ]";
+
+      SB_LOG(INFO) << "INPUT: type=" << data->type
+                   << ", window=" << data->window
+                   << ", device_type=" << data->device_type
+                   << ", device_id=" << data->device_id
+                   << ", key=0x" << std::hex << data->key
+                   << ", character=" << data->character
+                   << ", modifiers=0x" << std::hex << data->key_modifiers
+                   << ", location=" << std::dec << data->key_location
+                   << ", position="
+                   << "[ " << data->position.x << " , " << data->position.y
+                   << " ]";
+
+      // Track which keys are currently pressed, from our perspective outside
+      // of Starboard.  Print out the current state after each key event.
+      if (data->type == kSbInputEventTypePress ||
+          data->type == kSbInputEventTypeUnpress) {
+        if (data->type == kSbInputEventTypePress) {
+          s_is_pressed.insert(data->key);
+        } else {
+          s_is_pressed.erase(data->key);
+        }
+        if (!s_is_pressed.empty()) {
+          std::stringstream keys;
+          keys << "Keys currently pressed:";
+          for (KeySet::const_iterator iter = s_is_pressed.begin();
+               iter != s_is_pressed.end(); ++iter) {
+            keys << " " << std::hex << *iter;
+          }
+          SB_LOG(INFO) << keys.str();
+        }
+      }
+
+      switch (data->key) {
+#if SB_API_VERSION >= 4
+        case kSbKeyF1:
+          SbSystemRequestPause();
+          break;
+        case kSbKeyF2:
+          SbSystemRequestUnpause();
+          break;
+        case kSbKeyF3:
+          SbSystemRequestSuspend();
+          break;
+#endif  // SB_API_VERSION >= 4
+        case kSbKeyF5:
+          SbSystemRequestStop(0);
+          break;
+        default:
+          // Do nothing.
+          break;
+      }
+      break;
+    }
+    case kSbEventTypePause: {
+      SB_LOG(INFO) << "PAUSE";
+      break;
+    }
+    case kSbEventTypeResume: {
+      SB_LOG(INFO) << "RESUME";
+      break;
+    }
+    case kSbEventTypeStop: {
+      SB_LOG(INFO) << "STOP";
+      SbWindowDestroy(g_window);
+      break;
+    }
+    case kSbEventTypeSuspend: {
+      SB_LOG(INFO) << "SUSPEND";
+      break;
+    }
+    case kSbEventTypeUnpause: {
+      SB_LOG(INFO) << "UNPAUSE";
       break;
     }
     default:
-      SB_DLOG(INFO) << __FUNCTION__ << ": Event Type " << event->type
-                    << " not handled.";
+      SB_LOG(INFO) << "Event Type " << event->type << " not handled.";
       break;
   }
 }
diff --git a/src/starboard/image.h b/src/starboard/image.h
index 5ff6e3f..82d93be 100644
--- a/src/starboard/image.h
+++ b/src/starboard/image.h
@@ -87,11 +87,16 @@
 // 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);
+SB_EXPORT SbDecodeTarget SbImageDecode(
+#if SB_API_VERSION >= 4
+    SbDecodeTargetGraphicsContextProvider* context_provider,
+#else
+    SbDecodeTargetProvider* provider,
+#endif
+    void* data,
+    int data_size,
+    const char* mime_type,
+    SbDecodeTargetFormat format);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/starboard/input.h b/src/starboard/input.h
index aa43c6f..10c919a 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -117,7 +117,7 @@
 } SbInputEventType;
 
 // A 2-dimensional vector used to represent points and motion vectors.
-#if SB_API_VERSION >= SB_INPUT_FLOATING_POINT_INPUT_VECTOR_VERSION
+#if SB_API_VERSION >= 4
 typedef struct SbInputVector {
   float x;
   float y;
diff --git a/src/starboard/linux/shared/compiler_flags.gypi b/src/starboard/linux/shared/compiler_flags.gypi
index 0218e92..931779b 100644
--- a/src/starboard/linux/shared/compiler_flags.gypi
+++ b/src/starboard/linux/shared/compiler_flags.gypi
@@ -42,6 +42,9 @@
     ],
     'conditions': [
       ['clang==1', {
+        'linker_flags': [
+          '-fuse-ld=lld',
+        ],
         'common_clang_flags': [
           '-Werror',
           '-fcolor-diagnostics',
@@ -50,8 +53,7 @@
           # Warn for implicit type conversions that may change a value.
           '-Wconversion',
           '-Wno-c++11-compat',
-          # This (rightfully) complains about 'override', which we use
-          # heavily.
+          # This complains about 'override', which we use heavily.
           '-Wno-c++11-extensions',
           # Warns on switches on enums that cover all enum values but
           # also contain a default: branch. Chrome is full of that.
@@ -98,11 +100,7 @@
       '-std=c99',
     ],
     'cflags_cc': [
-      # Limit to gnu++98. This allows Linux to be a canary build for any
-      # C++11 features that are not supported on some platforms' compilers.
-      # We do allow ourselves GNU extensions, which are assumed to exist
-      # by Chromium code.
-      '-std=gnu++98',
+      '-std=gnu++11',
     ],
     'target_conditions': [
       ['cobalt_code==1', {
@@ -143,6 +141,13 @@
         'defines': [
           'ADDRESS_SANITIZER',
         ],
+        'conditions': [
+          ['asan_symbolizer_path!=""', {
+            'defines': [
+              'ASAN_SYMBOLIZER_PATH="<@(asan_symbolizer_path)"',
+            ],
+          }],
+        ],
       }],
       ['use_tsan==1', {
         'cflags': [
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index 632e4e1..b4bed14 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 2
+#define SB_API_VERSION 4
 #endif
 
 // --- System Header Configuration -------------------------------------------
@@ -59,6 +59,9 @@
 // Whether the current platform has microphone supported.
 #define SB_HAS_MICROPHONE 0
 
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
 // Whether the current platform has speech synthesis.
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
@@ -261,7 +264,7 @@
 // supported composition methods below.
 #define SB_HAS_PLAYER 1
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 // Specifies whether this platform's player will produce an OpenGL texture that
 // the client must draw every frame with its graphics rendering. It may be that
 // we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -280,7 +283,9 @@
 // this case, changing the video bounds must be tightly synchronized between the
 // player and the graphics plane.
 #define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
+
+#if SB_API_VERSION < 4
 
 // Specifies the maximum amount of memory used by audio buffers of media source
 // before triggering a garbage collection.  A large value will cause more memory
@@ -309,7 +314,7 @@
 //    macro should be set to a value that is greater than the sum of the above
 //    source buffer stream memory limits with extra room to take account of
 //    fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
+#define SB_MEDIA_MAIN_BUFFER_BUDGET (24U * 1024U * 1024U)
 
 // Specifies how much GPU memory to reserve up-front for media source buffers.
 // This should only be set to non-zero on system with limited CPU memory and
@@ -318,6 +323,22 @@
 // media buffers being decoded when being stored in GPU.
 #define SB_MEDIA_GPU_BUFFER_BUDGET 0U
 
+#endif  // SB_API_VERSION < 4
+
+#if SB_API_VERSION >= 4
+
+// The maximum audio bitrate the platform can decode.  The following value
+// equals to 5M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
+
+// The maximum video bitrate the platform can decode.  The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
+
+#endif  // SB_API_VERSION >= 4
+
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi
index f053843..01ca539 100644
--- a/src/starboard/linux/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -24,6 +24,10 @@
     'in_app_dial%': 1,
     'gl_type%': 'system_gles3',
 
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'cobalt_media_source_2016': 1,
+
     'platform_libraries': [
       '-lasound',
       '-lavcodec',
@@ -40,12 +44,22 @@
       '-U__linux__',
     ],
 
+    # Using an inner scope for 'variables' so that it can be made a default
+    # (and so overridden elsewhere), but yet still used immediately in this
+    # file.
+    'variables': {
+      'use_dlmalloc_allocator%': 0,
+    },
+
     'conditions': [
-      ['use_asan==0', {
+      ['use_dlmalloc_allocator==1 and use_asan==0', {
         'linker_flags': [
-          # We don't wrap these symbols, but this ensures that they aren't
-          # linked in. We do have to allow them to linked in when using ASAN, as
-          # it needs to use its own version of these allocators in the Starboard
+          # If we're not using the system allocator (e.g. we are using dlmalloc
+          # and ASAN is inactive) then we should never be making any calls to
+          # malloc() or free().  The following linker flags ensure that they
+          # are not linked in because we don't actually implement the wrapped
+          # version of them. We do link them in when using ASAN, as it needs to
+          # use its own version of these allocators in the Starboard
           # implementation.
           '-Wl,--wrap=malloc',
           '-Wl,--wrap=calloc',
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index d2cb2e8..3750233 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -28,12 +28,7 @@
       '<(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',
@@ -88,6 +83,7 @@
       '<(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_interface_address.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',
@@ -214,11 +210,17 @@
       '<(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/codec_util.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
       '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
+      '<(DEPTH)/starboard/shared/starboard/media/media_util.h',
       '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
       '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
       '<(DEPTH)/starboard/shared/starboard/new.cc',
@@ -265,14 +267,29 @@
       '<(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_pause.cc',
       '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
       '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+      '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
+      '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+      '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+      '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+      '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+      '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+      '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+      '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+      '<(DEPTH)/starboard/shared/stub/decode_target_release.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/image_decode.cc',
+      '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
       '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
       '<(DEPTH)/starboard/shared/stub/microphone_close.cc',
       '<(DEPTH)/starboard/shared/stub/microphone_create.cc',
@@ -287,6 +304,25 @@
       '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
       '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
     ],
+    'conditions': [
+      ['use_dlmalloc_allocator==1', {
+        'starboard_platform_sources': [
+          '<(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_reallocate_unchecked.cc',
+        ],
+      }, {
+        'starboard_platform_sources': [
+          '<(DEPTH)/starboard/shared/iso/memory_allocate_unchecked.cc',
+          '<(DEPTH)/starboard/shared/iso/memory_free.cc',
+          '<(DEPTH)/starboard/shared/iso/memory_reallocate_unchecked.cc',
+          '<(DEPTH)/starboard/shared/posix/memory_allocate_aligned_unchecked.cc',
+          '<(DEPTH)/starboard/shared/posix/memory_free_aligned.cc',
+        ],
+      }]
+    ],
     'starboard_platform_dependencies': [
       '<(DEPTH)/starboard/common/common.gyp:common',
       '<(DEPTH)/starboard/linux/shared/starboard_base_symbolize.gyp:starboard_base_symbolize',
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index b06571d..6621841 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -21,6 +21,7 @@
 #include <cstring>
 
 #include "starboard/directory.h"
+#include "starboard/log.h"
 #include "starboard/string.h"
 
 namespace {
@@ -58,7 +59,8 @@
     return false;
   }
 
-  char* last_slash = strrchr(out_path, '/');
+  char* last_slash =
+      const_cast<char *>(SbStringFindLastCharacter(out_path, '/'));
   if (!last_slash) {
     return false;
   }
@@ -73,7 +75,7 @@
     return false;
   }
 
-  const int kPathSize = PATH_MAX;
+  const int kPathSize = SB_FILE_MAX_PATH;
   char path[kPathSize];
   path[0] = '\0';
 
@@ -127,9 +129,13 @@
                              path_size);
     case kSbSystemPathExecutableFile:
       return GetExecutablePath(out_path, path_size);
+
+    default:
+      SB_NOTIMPLEMENTED();
+      return false;
   }
 
-  int length = strlen(path);
+  int length = SbStringGetLength(path);
   if (length < 1 || length > path_size) {
     return false;
   }
diff --git a/src/starboard/linux/x64directfb/future/configuration_public.h b/src/starboard/linux/x64directfb/future/configuration_public.h
index 2851606..ec08c92 100644
--- a/src/starboard/linux/x64directfb/future/configuration_public.h
+++ b/src/starboard/linux/x64directfb/future/configuration_public.h
@@ -18,9 +18,6 @@
 #ifndef STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
 #define STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
 
-// Include the X64DIRECTFB Linux configuration.
-#include "starboard/linux/x64directfb/configuration_public.h"
-
 // The API version implemented by this platform.
 #undef SB_API_VERSION
 #define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
@@ -29,4 +26,7 @@
 #undef SB_IS_PLAYER_COMPOSITED
 #undef SB_IS_PLAYER_PUNCHED_OUT
 
+// Include the X64DIRECTFB Linux configuration.
+#include "starboard/linux/x64directfb/configuration_public.h"
+
 #endif  // STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.gypi b/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
index fd813f6..44b920d 100644
--- a/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
+++ b/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
@@ -13,12 +13,6 @@
 # limitations under the License.
 
 {
-  'variables': {
-    # This should have a default value in cobalt/base.gypi. See the comment
-    # there for acceptable values for this variable.
-    'cobalt_media_source_2016': 1,
-  },
-
   'target_defaults': {
     'default_configuration': 'linux-x64directfb-future_debug',
     'configurations': {
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform.gyp b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
index 9a7b68e..6fe0639 100644
--- a/src/starboard/linux/x64directfb/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
@@ -17,19 +17,7 @@
       'target_name': 'starboard_platform',
       'product_name': 'starboard_platform_future',
       'type': 'static_library',
-      'sources': [
-        '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
-        '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_create_blitter.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_blitter.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_size.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
-        '<(DEPTH)/starboard/shared/stub/image_decode.cc',
-        '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
-      ],
+      'sources': [],
       'defines': [
         # This must be defined when building Starboard, and must not when
         # building Starboard client code.
diff --git a/src/starboard/linux/x64directfb/sanitizer_options.cc b/src/starboard/linux/x64directfb/sanitizer_options.cc
index b5f72cd..6205ec9 100644
--- a/src/starboard/linux/x64directfb/sanitizer_options.cc
+++ b/src/starboard/linux/x64directfb/sanitizer_options.cc
@@ -38,7 +38,13 @@
 SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
   return
       // DirectFB leaks __strdup data at initialization time.
-      "leak:__strdup\n"
+      "leak:__strdup\n";
 }
 
+#if defined(ASAN_SYMBOLIZER_PATH)
+extern "C" const char *__asan_default_options() {
+  return "external_symbolizer_path=" ASAN_SYMBOLIZER_PATH;
+}
+#endif
+
 #endif  // defined(ADDRESS_SANITIZER)
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 1f3cb42..67a704c 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -32,7 +32,7 @@
 
   def __init__(self, platform, asan_enabled_by_default=True):
     super(PlatformConfig, self).__init__(
-        platform, asan_enabled_by_default, goma_supports_compiler=False)
+        platform, goma_supports_compiler=False)
 
   def GetEnvironmentVariables(self):
     env_variables = {
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h
index d76ffff..b1cff4c 100644
--- a/src/starboard/linux/x64x11/configuration_public.h
+++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -22,11 +22,6 @@
 #ifndef STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
 #define STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
 
-// The API version implemented by this platform.
-#if !defined(SB_API_VERSION)
-#define SB_API_VERSION 2
-#endif
-
 // --- Architecture Configuration --------------------------------------------
 
 // Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
diff --git a/src/cobalt/base/math.cc b/src/starboard/linux/x64x11/directgles/atomic_public.h
similarity index 65%
copy from src/cobalt/base/math.cc
copy to src/starboard/linux/x64x11/directgles/atomic_public.h
index d335495..7bd6e3b 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/linux/x64x11/directgles/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_LINUX_X64X11_DIRECTGLES_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_DIRECTGLES_ATOMIC_PUBLIC_H_
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_DIRECTGLES_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/directgles/configuration_public.h b/src/starboard/linux/x64x11/directgles/configuration_public.h
new file mode 100644
index 0000000..0158690
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/configuration_public.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Desktop X86 Linux configured for the future
+// starboard API.
+
+#ifndef STARBOARD_LINUX_X64X11_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+
+// This is not a released configuration, so it should implement the
+// experimental API version to validate trunk's viability.
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Include the X64X11 Linux configuration.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_DIRECTGLES_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi b/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi
new file mode 100644
index 0000000..66e6d55
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.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.
+
+{
+  'variables': {
+    # Use the direct-to-GLES rasterizer.
+    # This rasterizer falls back to the hardware skia rasterizer in certain
+    # situations.
+    # NOTE: This rasterizer requires a 16-bit depth buffer and a full frame
+    # scratch surface (without depth buffer).
+    'rasterizer_type': 'direct-gles',
+
+    # Accommodate the direct-to-GLES rasterizer's additional memory overhead
+    # by reducing the image cache size. This is only an issue when additional
+    # memory is required for video frames, so shrink the image cache size
+    # only during video playback.
+    'image_cache_capacity_multiplier_when_playing_video': '0.5f',
+
+    # This dictates how much GPU memory will be used for the offscreen render
+    # target atlases. One will be used as a cache and the other used as a
+    # working scratch. It is recommended to allot enough memory for two
+    # atlases that are roughly a quarter of the framebuffer. (The render
+    # target atlases use power-of-2 dimenions.)
+    'surface_cache_size_in_bytes': 2 * (1024 * 512 * 4),
+
+    # The rasterizer does not benefit much from rendering only the dirty
+    # region. Disable this option since it costs GPU time.
+    'render_dirty_region_only': 0,
+  },
+  'target_defaults': {
+    'default_configuration': 'linux-x64x11-directgles_debug',
+    'configurations': {
+      'linux-x64x11-directgles_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'linux-x64x11-directgles_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'linux-x64x11-directgles_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'linux-x64x11-directgles_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '../gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.py b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
new file mode 100644
index 0000000..4fc899f
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
@@ -0,0 +1,34 @@
+# 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.
+"""Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Linux platform configuration.
+sys.path.append(
+    os.path.realpath(
+        os.path.join(
+            os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
+# pylint: disable=import-self,g-import-not-at-top
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+  try:
+    return gyp_configuration.PlatformConfig('linux-x64x11-directgles')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
diff --git a/src/starboard/linux/x64x11/directgles/starboard_platform.gyp b/src/starboard/linux/x64x11/directgles/starboard_platform.gyp
new file mode 100644
index 0000000..9a450cc
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/starboard_platform.gyp
@@ -0,0 +1,36 @@
+# 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': [
+    '../starboard_platform.gypi'
+  ],
+  'targets': [
+    {
+      'target_name': 'starboard_platform',
+      'product_name': 'starboard_platform_future',
+      'type': 'static_library',
+      'sources': [
+        '<@(starboard_platform_sources)',
+      ],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<@(starboard_platform_dependencies)',
+      ],
+    },
+  ],
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/linux/x64x11/directgles/thread_types_public.h
similarity index 64%
copy from src/cobalt/base/math.cc
copy to src/starboard/linux/x64x11/directgles/thread_types_public.h
index d335495..4809a57 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/linux/x64x11/directgles/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_LINUX_X64X11_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.gypi b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
index d7f3af6..0cb12cf 100644
--- a/src/starboard/linux/x64x11/future/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
@@ -13,12 +13,6 @@
 # limitations under the License.
 
 {
-  'variables': {
-    # This should have a default value in cobalt/base.gypi. See the comment
-    # there for acceptable values for this variable.
-    'cobalt_media_source_2016': 1,
-  },
-
   'target_defaults': {
     'default_configuration': 'linux-x64x11-future_debug',
     'configurations': {
diff --git a/src/starboard/linux/x64x11/future/starboard_platform.gyp b/src/starboard/linux/x64x11/future/starboard_platform.gyp
index 391210a..9a450cc 100644
--- a/src/starboard/linux/x64x11/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/future/starboard_platform.gyp
@@ -22,17 +22,6 @@
       'type': 'static_library',
       'sources': [
         '<@(starboard_platform_sources)',
-        '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
-        '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_egl.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_get_size.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
-        '<(DEPTH)/starboard/shared/stub/decode_target_release.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
diff --git a/src/starboard/linux/x64x11/sanitizer_options.cc b/src/starboard/linux/x64x11/sanitizer_options.cc
index 2ac2410..e8a4aea 100644
--- a/src/starboard/linux/x64x11/sanitizer_options.cc
+++ b/src/starboard/linux/x64x11/sanitizer_options.cc
@@ -36,7 +36,15 @@
 // http://clang.llvm.org/docs/AddressSanitizer.html#issue-suppression
 // http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc
 SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
-  return "leak:egl_gallium.so\n";
+  return
+      "leak:egl_gallium.so\n"
+      "leak:libspeechd.so\n";
 }
 
+#if defined(ASAN_SYMBOLIZER_PATH)
+extern "C" const char *__asan_default_options() {
+  return "external_symbolizer_path=" ASAN_SYMBOLIZER_PATH;
+}
+#endif
+
 #endif  // defined(ADDRESS_SANITIZER)
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 1b11b8f..3982d15 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -131,7 +131,7 @@
   kSbMediaAudioFrameStorageTypePlanar,
 } SbMediaAudioFrameStorageType;
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 // SMPTE 2086 mastering data
 //   http://ieeexplore.ieee.org/document/7291707/
 // This standard specifies the metadata items to specify the color
@@ -384,7 +384,7 @@
   // completed as (0, 0, 0, 1).
   float custom_primary_matrix[12];
 } SbMediaColorMetadata;
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 // The set of information required by the decoder or player for each video
 // sample.
@@ -403,7 +403,7 @@
   // key frames, but may change on any key frame.
   int frame_height;
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   // HDR metadata common for HDR10 and WebM/VP9-based HDR formats as
   // well as the Color Space, and Color elements: MatrixCoefficients,
   // BitsPerChannel, ChromaSubsamplingHorz, ChromaSubsamplingVert,
@@ -495,6 +495,8 @@
                                   SbMediaAudioCodec audio_codec,
                                   const char* key_system);
 
+#if SB_API_VERSION < 4
+
 // 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
@@ -523,6 +525,8 @@
 SB_EXPORT bool SbMediaIsAudioSupported(SbMediaVideoCodec audio_codec,
                                        int64_t bitrate);
 
+#endif  // SB_API_VERSION < 4
+
 // Returns information about whether the playback of the specific media
 // described by |mime| and encrypted using |key_system| can be played.
 //
@@ -531,12 +535,13 @@
 //
 // |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.
+//   like "codecs", "channels", etc.  Note that the "codecs" parameter may
+//   contain more than one codec, delimited by comma.
 // |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.
+//   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.
 SB_EXPORT SbMediaSupportType
 SbMediaCanPlayMimeAndKeySystem(const char* mime, const char* key_system);
 
diff --git a/src/starboard/nplb/accessibility_get_setting_test.cc b/src/starboard/nplb/accessibility_get_setting_test.cc
index 3ffaa18..ca0662f 100644
--- a/src/starboard/nplb/accessibility_get_setting_test.cc
+++ b/src/starboard/nplb/accessibility_get_setting_test.cc
@@ -19,7 +19,7 @@
 namespace nplb {
 namespace {
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
 
 TEST(SbAccessibilityGetSettingTest, CanCallGetTextToSpeechSettings) {
   SbAccessibilityTextToSpeechSettings settings = {0};
diff --git a/src/starboard/nplb/cryptography_create_transformer_test.cc b/src/starboard/nplb/cryptography_create_transformer_test.cc
new file mode 100644
index 0000000..241d6cf
--- /dev/null
+++ b/src/starboard/nplb/cryptography_create_transformer_test.cc
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/cryptography.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 4
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+const int kBlockSizeBits = 128;
+const int kBlockSizeBytes = kBlockSizeBits / 8;
+
+TEST(SbCryptographyCreateTransformer, SunnyDay) {
+  char initialization_vector[kBlockSizeBytes] = {0};
+  char key[kBlockSizeBytes] = {0};
+
+  // Try to create a transformer for the most likely algorithm to be supported:
+  // AES-128-CBC
+  SbCryptographyTransformer transformer = SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes, kBlockSizeBits,
+      kSbCryptographyDirectionDecode, kSbCryptographyBlockCipherModeCbc,
+      initialization_vector, kBlockSizeBytes, key, kBlockSizeBytes);
+
+  if (!SbCryptographyIsTransformerValid(transformer)) {
+    // Test over if there's no implementation.
+    return;
+  }
+
+  // TODO: Validate implementation.
+  SbCryptographyDestroyTransformer(transformer);
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/cryptography_transform_gcm_test.cc b/src/starboard/nplb/cryptography_transform_gcm_test.cc
new file mode 100644
index 0000000..819b63d
--- /dev/null
+++ b/src/starboard/nplb/cryptography_transform_gcm_test.cc
@@ -0,0 +1,466 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This test is adapted from BoringSSL's crypto/fipsmodule/modes/gcm_test.cc
+
+#include "starboard/cryptography.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 4
+
+using ::testing::NotNull;
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+const int kBlockSizeBits = 128;
+const int kBlockSizeBytes = kBlockSizeBits / 8;
+
+struct test_case {
+  const char *key;
+  const char *plaintext;
+  const char *additional_data;
+  const char *nonce;
+  const char *ciphertext;
+  const char *tag;
+};
+
+const struct test_case test_cases[] = {
+  {
+    "00000000000000000000000000000000",
+    NULL,
+    NULL,
+    "000000000000000000000000",
+    NULL,
+    "58e2fccefa7e3061367f1d57a4e7455a",
+  },
+  {
+    "00000000000000000000000000000000",
+    "00000000000000000000000000000000",
+    NULL,
+    "000000000000000000000000",
+    "0388dace60b6a392f328c2b971b2fe78",
+    "ab6e47d42cec13bdf53a67b21257bddf",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+    NULL,
+    "cafebabefacedbaddecaf888",
+    "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+    "4d5c2af327cd64a62cf35abd2ba6fab4",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbaddecaf888",
+    "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+    "5bc94fbc3221a5db94fae95ae7121a47",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+    "3612d2e79e3b0785561be14aaca2fccb",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+    "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+    "619cc5aefffe0bfa462af43c1699d050",
+  },
+  {
+    "000000000000000000000000000000000000000000000000",
+    NULL,
+    NULL,
+    "000000000000000000000000",
+    NULL,
+    "cd33b28ac773f74ba00ed1f312572435",
+  },
+  {
+    "000000000000000000000000000000000000000000000000",
+    "00000000000000000000000000000000",
+    NULL,
+    "000000000000000000000000",
+    "98e7247c07f0fe411c267e4384b0f600",
+    "2ff58d80033927ab8ef4d4587514f0fb",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+    NULL,
+    "cafebabefacedbaddecaf888",
+    "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+    "9924a7c8587336bfb118024db8674a14",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbaddecaf888",
+    "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+    "2519498e80f1478f37ba55bd6d27618c",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+    "65dcc57fcf623a24094fcca40d3533f8",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+    "65dcc57fcf623a24094fcca40d3533f8",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+    "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+    "dcf566ff291c25bbb8568fc3d376a6d9",
+  },
+  {
+    "0000000000000000000000000000000000000000000000000000000000000000",
+    NULL,
+    NULL,
+    "000000000000000000000000",
+    NULL,
+    "530f8afbc74536b9a963b4f1c4cb738b",
+  },
+  {
+    "0000000000000000000000000000000000000000000000000000000000000000",
+    "00000000000000000000000000000000",
+    NULL,
+    "000000000000000000000000",
+    "cea7403d4d606b6e074ec5d3baf39d18",
+    "d0d1c8a799996bf0265b98b5d48ab919",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+    NULL,
+    "cafebabefacedbaddecaf888",
+    "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+    "b094dac5d93471bdec1a502270e3cc6c",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbaddecaf888",
+    "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+    "76fc6ece0f4e1768cddf8853bb2d551b",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "cafebabefacedbad",
+    "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+    "3a337dbf46a792c45e454913fe2ea8f2",
+  },
+  {
+    "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+    "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+    "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+    "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+    "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+  },
+  {
+    "00000000000000000000000000000000",
+    NULL,
+    "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+    "000000000000000000000000",
+    NULL,
+    "5fea793a2d6f974d37e68e0cb8ff9492",
+  },
+  {
+    "00000000000000000000000000000000",
+    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+    NULL,
+    /* This nonce results in 0xfff in counter LSB. */
+    "ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+    "56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606872ca10dee15b3249b1a1b958f23134c4bccb7d03200bce420a2f8eb66dcf3644d1423c1b5699003c13ecef4bf38a3b60eedc34033bac1902783dc6d89e2e774188a439c7ebcc0672dbda4ddcfb2794613b0be41315ef778708a70ee7d75165c",
+    "8b307f6b33286d0ab026a9ed3fe1e85f",
+  },
+};
+
+int from_hex(uint8_t *out, char in) {
+  if (in >= '0' && in <= '9') {
+    *out = in - '0';
+    return 1;
+  }
+  if (in >= 'a' && in <= 'f') {
+    *out = in - 'a' + 10;
+    return 1;
+  }
+  if (in >= 'A' && in <= 'F') {
+    *out = in - 'A' + 10;
+    return 1;
+  }
+
+  return 0;
+}
+
+void decode_hex(scoped_array<uint8_t> *out, int *out_len, const char *in,
+                int test_num, const char *description) {
+  if (in == NULL) {
+    out->reset();
+    *out_len = 0;
+    return;
+  }
+
+  size_t len = SbStringGetLength(in);
+  if (len & 1) {
+    ADD_FAILURE() << description << ": Odd length.";
+    return;
+  }
+
+  scoped_array<uint8_t> buf(new uint8_t[len / 2]);
+  if (!buf) {
+    ADD_FAILURE() << description << ": Memory fail.";
+    return;
+  }
+
+  for (size_t i = 0; i < len; i += 2) {
+    uint8_t v, v2;
+    bool result = from_hex(&v, in[i]) && from_hex(&v2, in[i + 1]);
+    if (!result) {
+      ADD_FAILURE() << description << ": Invalid character at " << i << ".";
+      continue;
+    }
+
+    buf[i / 2] = (v << 4) | v2;
+  }
+
+  *out = buf.Pass();
+  *out_len = len / 2;
+}
+
+std::string hexdump(const void *in, int len) {
+  const uint8_t* data = reinterpret_cast<const uint8_t*>(in);
+
+  std::string result;
+  for (size_t i = 0; i < len; i++) {
+    char hex[3] = {0};
+    SbStringFormatF(hex, 3, "%02x", data[i]);
+    result += hex;
+  }
+
+  return result;
+}
+
+class Gcm
+    : public ::testing::TestWithParam<int> {
+ public:
+  int GetTestCase() { return GetParam(); }
+};
+
+TEST_P(Gcm, TestCase) {
+  int test_num = GetTestCase();
+  const struct test_case *test = &test_cases[test_num];
+
+  int key_len;
+  scoped_array<uint8_t> key;
+  int plaintext_len;
+  scoped_array<uint8_t> plaintext;
+  int additional_data_len;
+  scoped_array<uint8_t> additional_data;
+  int nonce_len;
+  scoped_array<uint8_t> nonce;
+  int ciphertext_len;
+  scoped_array<uint8_t> ciphertext;
+  int tag_len;
+  scoped_array<uint8_t> tag;
+  scoped_array<uint8_t> out;
+
+  decode_hex(&key, &key_len, test->key, test_num, "key");
+  decode_hex(&plaintext, &plaintext_len, test->plaintext, test_num,
+             "plaintext");
+  decode_hex(&additional_data, &additional_data_len,
+             test->additional_data, test_num, "additional_data");
+  decode_hex(&nonce, &nonce_len, test->nonce, test_num, "nonce");
+  decode_hex(&ciphertext, &ciphertext_len, test->ciphertext, test_num,
+             "ciphertext");
+  decode_hex(&tag, &tag_len, test->tag, test_num, "tag");
+
+  if (plaintext_len != ciphertext_len) {
+    FAIL() << "Plaintext and ciphertext have differing lengths.";
+  }
+
+  if (key_len != 16 && key_len != 24 && key_len != 32) {
+    FAIL() << "Bad key length.";
+  }
+
+  if (tag_len != 16) {
+    FAIL() << "Bad tag length.";
+  }
+
+  out.reset(new uint8_t[plaintext_len]);
+  if (plaintext_len != 0 && out == NULL) {
+    FAIL() << "Out of memory.";
+  }
+
+  // Test encryption.
+
+  // Try to create a transformer for GCM.
+  SbCryptographyTransformer encrypter = SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes, kBlockSizeBits,
+      kSbCryptographyDirectionEncode, kSbCryptographyBlockCipherModeGcm,
+      NULL, 0, key.get(), key_len);
+
+  if (!SbCryptographyIsTransformerValid(encrypter)) {
+    // Test over (but no failure) if there's no implementation.
+    return;
+  }
+
+  SbCryptographySetInitializationVector(encrypter, nonce.get(), nonce_len);
+  SbMemorySet(out.get(), 0, plaintext_len);
+  if (additional_data) {
+    SbCryptographySetAuthenticatedData(encrypter, additional_data.get(),
+                                       additional_data_len);
+  }
+
+  if (plaintext) {
+    EXPECT_EQ(plaintext_len,
+              SbCryptographyTransform(encrypter, plaintext.get(), plaintext_len,
+                                      out.get()));
+  }
+
+  scoped_array<uint8_t> actual_tag(new uint8_t[tag_len]);
+  SbMemorySet(actual_tag.get(), 0, tag_len);
+  SbCryptographyGetTag(encrypter, actual_tag.get(), tag_len);
+  if (tag) {
+    EXPECT_STREQ(hexdump(tag.get(), tag_len).c_str(),
+                 hexdump(actual_tag.get(), tag_len).c_str());
+  }
+
+  if (ciphertext) {
+    EXPECT_STREQ(hexdump(ciphertext.get(), plaintext_len).c_str(),
+                 hexdump(out.get(), plaintext_len).c_str());
+  }
+
+  SbCryptographyDestroyTransformer(encrypter);
+
+  // Test decryption.
+
+  SbCryptographyTransformer decrypter = SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes, kBlockSizeBits,
+      kSbCryptographyDirectionDecode, kSbCryptographyBlockCipherModeGcm,
+      NULL, 0, key.get(), key_len);
+  ASSERT_THAT(decrypter, NotNull());
+
+  SbCryptographySetInitializationVector(decrypter, nonce.get(), nonce_len);
+  SbMemorySet(out.get(), 0, plaintext_len);
+  if (additional_data) {
+    SbCryptographySetAuthenticatedData(decrypter, additional_data.get(),
+                                       additional_data_len);
+  }
+
+  if (ciphertext) {
+    EXPECT_EQ(plaintext_len,
+              SbCryptographyTransform(decrypter, ciphertext.get(),
+                                      plaintext_len, out.get()));
+  }
+
+  actual_tag.reset(new uint8_t[tag_len]);
+  SbMemorySet(actual_tag.get(), 0, tag_len);
+  SbCryptographyGetTag(decrypter, actual_tag.get(), tag_len);
+  if (tag) {
+    EXPECT_STREQ(hexdump(tag.get(), tag_len).c_str(),
+                 hexdump(actual_tag.get(), tag_len).c_str());
+  }
+
+  if (plaintext) {
+    EXPECT_STREQ(hexdump(plaintext.get(), plaintext_len).c_str(),
+                 hexdump(out.get(), plaintext_len).c_str());
+  }
+  SbCryptographyDestroyTransformer(decrypter);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    SbCryptographyTransform,
+    Gcm,
+    ::testing::Range(0, SB_ARRAY_SIZE_INT(test_cases)));
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/cryptography_transform_test.cc b/src/starboard/nplb/cryptography_transform_test.cc
new file mode 100644
index 0000000..ae916a7
--- /dev/null
+++ b/src/starboard/nplb/cryptography_transform_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/cryptography.h"
+
+#include "starboard/log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 4
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+const int kBlockSizeBits = 128;
+const int kBlockSizeBytes = kBlockSizeBits / 8;
+
+TEST(SbCryptographyTransform, SunnyDay) {
+  char initialization_vector[kBlockSizeBytes + 1] = "0123456789ABCDEF";
+  char key[kBlockSizeBytes + 1] = "RijndaelRijndael";
+  const char kClearText[] =
+      "This test text is designed to be a multiple of "
+      "128 bits, huzzah.";
+
+  // Try to create a transformer for the most likely algorithm to be supported:
+  // AES-128-CBC
+  SbCryptographyTransformer transformer = SbCryptographyCreateTransformer(
+      kSbCryptographyAlgorithmAes, kBlockSizeBits,
+      kSbCryptographyDirectionEncode, kSbCryptographyBlockCipherModeCbc,
+      initialization_vector, kBlockSizeBytes, key, kBlockSizeBytes);
+
+  if (!SbCryptographyIsTransformerValid(transformer)) {
+    // Test over if there's no implementation.
+    return;
+  }
+
+  const int kInputSize = SbStringGetLength(kClearText);
+  const int kBufferSize = sizeof(kClearText);
+  char* cipher_text = new char[kBufferSize];
+  SbMemorySet(cipher_text, 0, kBufferSize);
+  int count = SbCryptographyTransform(transformer, kClearText, kInputSize,
+                                      cipher_text);
+  EXPECT_EQ(kInputSize, count);
+  EXPECT_NE(0, SbStringCompare(kClearText, cipher_text, kBufferSize));
+
+  SbCryptographyTransformer decode_transformer =
+      SbCryptographyCreateTransformer(
+          kSbCryptographyAlgorithmAes, kBlockSizeBits,
+          kSbCryptographyDirectionDecode, kSbCryptographyBlockCipherModeCbc,
+          initialization_vector, kBlockSizeBytes, key, kBlockSizeBytes);
+
+  ASSERT_TRUE(SbCryptographyIsTransformerValid(decode_transformer));
+
+  char* decrypted_text = new char[kBufferSize];
+  SbMemorySet(decrypted_text, 0, kBufferSize);
+  count =
+      SbCryptographyTransform(decode_transformer, cipher_text, kInputSize,
+                              decrypted_text);
+
+  EXPECT_EQ(kInputSize, count);
+  EXPECT_EQ(kInputSize, SbStringGetLength(decrypted_text));
+  EXPECT_STREQ(kClearText, decrypted_text);
+
+  delete[] decrypted_text;
+  delete[] cipher_text;
+  SbCryptographyDestroyTransformer(decode_transformer);
+  SbCryptographyDestroyTransformer(transformer);
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/decode_target_create_test.cc b/src/starboard/nplb/decode_target_create_test.cc
index 33235e6..d6edb3a 100644
--- a/src/starboard/nplb/decode_target_create_test.cc
+++ b/src/starboard/nplb/decode_target_create_test.cc
@@ -19,7 +19,7 @@
 // which will define None to be 0L, which conflicts with gtest.
 #include "starboard/decode_target.h"  // NOLINT(build/include_order)
 
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_API_VERSION < 4 && SB_HAS(GRAPHICS)
 
 #if SB_HAS(BLITTER)
 #include "starboard/blitter.h"
@@ -42,7 +42,7 @@
 
   ASSERT_TRUE(SbBlitterSetRenderTarget(env.context(), env.render_target()));
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget target = SbDecodeTargetCreate(
       env.device(), kSbDecodeTargetFormat1PlaneRGBA, kWidth, kHeight);
   if (SbDecodeTargetIsValid(target)) {
@@ -59,7 +59,7 @@
         SbBlitterIsSurfaceValid(info.planes[kSbDecodeTargetPlaneRGBA].surface));
   }
   SbDecodeTargetRelease(target);
-#else   // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION >= 4
   SbBlitterSurface surface =
       CreateArbitraryRenderTargetSurface(env.device(), kWidth, kHeight);
 
@@ -72,7 +72,7 @@
   }
   SbDecodeTargetDestroy(target);
   EXPECT_TRUE(SbBlitterDestroySurface(surface));
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 }
 #elif SB_HAS(GLES2)  // SB_HAS(BLITTER)
 // clang-format off
@@ -175,7 +175,7 @@
   SbWindow window_;
 };
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 TEST_F(SbDecodeTargetTest, SunnyDayCreate) {
   const int kTextureWidth = 256;
   const int kTextureHeight = 256;
@@ -200,7 +200,7 @@
     SbDecodeTargetRelease(target);
   }
 }
-#else   // #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // #if SB_API_VERSION >= 4
 TEST_F(SbDecodeTargetTest, SunnyDayCreate) {
   // Generate a texture to put in the SbDecodeTarget.
   const int kTextureWidth = 256;
@@ -224,7 +224,7 @@
   }
   glDeleteTextures(1, &texture_handle);
 }
-#endif  // #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // #if SB_API_VERSION >= 4
 
 #else  // SB_HAS(BLITTER)
 
@@ -244,4 +244,6 @@
 }  // namespace nplb
 }  // namespace starboard
 
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif  // SB_API_VERSION >= 3 && \
+        // SB_API_VERSION < 4 && \
+        // SB_HAS(GRAPHICS)
diff --git a/src/starboard/nplb/decode_target_provider_test.cc b/src/starboard/nplb/decode_target_provider_test.cc
index 78fc485..1da7057 100644
--- a/src/starboard/nplb/decode_target_provider_test.cc
+++ b/src/starboard/nplb/decode_target_provider_test.cc
@@ -18,10 +18,12 @@
 // which will define None to be 0L, which conflicts with gtest.
 #include "starboard/decode_target.h"  // NOLINT(build/include_order)
 
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
-
 namespace starboard {
 namespace nplb {
+
+#if SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_API_VERSION < 4
+
 namespace {
 
 struct ProviderContext {
@@ -50,11 +52,11 @@
     bool valid = SbDecodeTargetIsValid(target);
     EXPECT_FALSE(valid);
     if (valid) {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
       SbDecodeTargetRelease(target);
-#else   // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION >= 4
       SbDecodeTargetDestroy(target);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
     }
   }
 }
@@ -152,7 +154,10 @@
 }
 
 }  // namespace
+
+#endif  // SB_API_VERSION >= 3 && \
+        // SB_API_VERSION < 4
+#endif  // SB_HAS(GRAPHICS)
+
 }  // namespace nplb
 }  // namespace starboard
-
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
diff --git a/src/starboard/nplb/include_all.c b/src/starboard/nplb/include_all.c
index 69ce553..2b8a50d 100644
--- a/src/starboard/nplb/include_all.c
+++ b/src/starboard/nplb/include_all.c
@@ -22,6 +22,7 @@
 #include "starboard/character.h"
 #include "starboard/condition_variable.h"
 #include "starboard/configuration.h"
+#include "starboard/cryptography.h"
 #include "starboard/decode_target.h"
 #include "starboard/directory.h"
 #include "starboard/double.h"
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 43576cd..92c4f2d 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -81,6 +81,9 @@
         'condition_variable_wait_test.cc',
         'condition_variable_wait_timed_test.cc',
         'configuration_test.cc',
+        'cryptography_create_transformer_test.cc',
+        'cryptography_transform_test.cc',
+        'cryptography_transform_gcm_test.cc',
         'decode_target_create_test.cc',
         'decode_target_provider_test.cc',
         'directory_can_open_test.cc',
@@ -120,9 +123,9 @@
         'memory_copy_test.cc',
         'memory_deallocate_aligned_test.cc',
         'memory_deallocate_test.cc',
+        'memory_find_byte_test.cc',
         'memory_get_stack_bounds_test.cc',
         'memory_is_zero_test.cc',
-        'memory_find_byte_test.cc',
         'memory_map_test.cc',
         'memory_move_test.cc',
         'memory_reallocate_test.cc',
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index f4247c1..3bff176 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/blitter.h"
 #include "starboard/decode_target.h"
 #include "starboard/player.h"
 #include "starboard/window.h"
@@ -22,7 +23,18 @@
 namespace starboard {
 namespace nplb {
 
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+#if SB_HAS(GLES2)
+void GlesContextRunner(
+    SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+    SbDecodeTargetGlesContextRunnerTarget target_function,
+    void* target_function_context) {
+  SB_UNREFERENCED_PARAMETER(graphics_context_provider);
+  SB_UNREFERENCED_PARAMETER(target_function);
+  SB_UNREFERENCED_PARAMETER(target_function_context);
+}
+#endif  // SB_HAS(GLES2)
+#elif SB_API_VERSION >= 3
 SbDecodeTarget SbDecodeTargetAcquireStub(void* /*context*/,
                                          SbDecodeTargetFormat /*format*/,
                                          int /*width*/,
@@ -55,7 +67,7 @@
   SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
   SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
                                        kSbPlayerOutputModePunchOut};
 
@@ -64,9 +76,21 @@
     if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
       continue;
     }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+    SbDecodeTargetGraphicsContextProvider
+        decode_target_graphics_context_provider;
+#if SB_HAS(BLITTER)
+    decode_target_graphics_context_provider.device = kSbBlitterInvalidDevice;
+#elif SB_HAS(GLES2)
+    decode_target_graphics_context_provider.egl_display = NULL;
+    decode_target_graphics_context_provider.egl_context = NULL;
+    decode_target_graphics_context_provider.gles_context_runner =
+        &GlesContextRunner;
+    decode_target_graphics_context_provider.gles_context_runner_context = NULL;
+#endif  // SB_HAS(BLITTER)
+#elif SB_API_VERSION >= 3
     SbDecodeTargetProvider decode_target_provider;
     decode_target_provider.acquire = &SbDecodeTargetAcquireStub;
     decode_target_provider.release = &SbDecodeTargetReleaseStub;
@@ -77,29 +101,30 @@
         SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
                        SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid,
                        &audio_header, NULL, NULL, NULL, NULL
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
                        ,
-                       output_mode
-#endif
-#if SB_API_VERSION >= 3
+                       output_mode,
+                       &decode_target_graphics_context_provider
+#elif SB_API_VERSION >= 3
                        ,
                        &decode_target_provider
 #endif
                        );  // NOLINT
     EXPECT_TRUE(SbPlayerIsValid(player));
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
     if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
       SbDecodeTarget current_frame = SbPlayerGetCurrentFrame(player);
     }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
     SbPlayerDestroy(player);
-    SbWindowDestroy(window);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
+
+  SbWindowDestroy(window);
 }
 
 }  // namespace nplb
diff --git a/src/starboard/nplb/player_output_mode_supported_test.cc b/src/starboard/nplb/player_output_mode_supported_test.cc
index 4336204..ffffb84 100644
--- a/src/starboard/nplb/player_output_mode_supported_test.cc
+++ b/src/starboard/nplb/player_output_mode_supported_test.cc
@@ -17,7 +17,7 @@
 #include "starboard/window.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if SB_HAS(PLAYER) && SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_HAS(PLAYER) && SB_API_VERSION >= 4
 
 namespace starboard {
 namespace nplb {
@@ -45,4 +45,4 @@
 }  // namespace starboard
 
 #endif  // SB_HAS(PLAYER) && \
-           SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+           SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/semaphore_test.cc b/src/starboard/nplb/semaphore_test.cc
index 709ab67..05de364 100644
--- a/src/starboard/nplb/semaphore_test.cc
+++ b/src/starboard/nplb/semaphore_test.cc
@@ -95,20 +95,37 @@
               kSbTimeMillisecond * 10);  // Error threshold
 }
 
+double IsDoubleNear(double first, double second, double diff_threshold) {
+  double diff = first - second;
+  if (diff < 0) {
+    diff = -diff;
+  }
+  return diff < diff_threshold;
+}
+
 TEST(Semaphore, ThreadTakesWait_TimeExpires) {
-  SbTime wait_time = kSbTimeMillisecond * 20;
-  ThreadTakesWaitSemaphore thread(wait_time);
-  thread.Start();
+  const int attempts = 20;  // Retest up to 20 times.
+  bool passed = false;
 
-  SbThreadSleep(wait_time * 2);
-  thread.semaphore_.Put();
+  const SbTime kTimeThreshold = kSbTimeMillisecond * 5;
 
-  thread.Join();
+  for (int i = 0; i < attempts; ++i) {
+    SbTime wait_time = kSbTimeMillisecond * 20;
+    ThreadTakesWaitSemaphore thread(wait_time);
+    thread.Start();
 
-  EXPECT_FALSE(thread.result_signaled_);
-  EXPECT_NEAR(thread.result_wait_time_,
-              wait_time,
-              kSbTimeMillisecond * 5);  // Error threshold
+    SbThreadSleep(wait_time * 2);
+    thread.semaphore_.Put();
+
+    thread.Join();
+    EXPECT_FALSE(thread.result_signaled_);
+
+    if (IsDoubleNear(wait_time, thread.result_wait_time_, kTimeThreshold)) {
+      return;  // Test passed.
+    }
+  }
+
+  EXPECT_TRUE(false) << "Thread waited, but time exceeded expectations.";
 }
 
 class ThreadPutsSemaphore : public AbstractTestThread {
diff --git a/src/starboard/nplb/socket_accept_test.cc b/src/starboard/nplb/socket_accept_test.cc
index f9e9b9e..9c3357c 100644
--- a/src/starboard/nplb/socket_accept_test.cc
+++ b/src/starboard/nplb/socket_accept_test.cc
@@ -24,13 +24,17 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketAcceptTest, RainyDayNoConnection) {
+class SbSocketAcceptTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_P(SbSocketAcceptTest, RainyDayNoConnection) {
   // Set up a socket to listen.
   SbSocket server_socket =
-      CreateListeningTcpIpv4Socket(GetPortNumberForTests());
-  if (!SbSocketIsValid(server_socket)) {
-    return;
-  }
+      CreateListeningTcpSocket(GetAddressType(), GetPortNumberForTests());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Don't create a socket to connect to it.
 
@@ -43,12 +47,10 @@
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketAcceptTest, RainyDayNotBound) {
+TEST_P(SbSocketAcceptTest, RainyDayNotBound) {
   // Set up a socket, but don't Bind or Listen.
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  if (!SbSocketIsValid(server_socket)) {
-    return;
-  }
+  SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Accept should result in an error.
   EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
@@ -57,12 +59,11 @@
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketAcceptTest, RainyDayNotListening) {
+TEST_P(SbSocketAcceptTest, RainyDayNotListening) {
   // Set up a socket, but don't Bind or Listen.
-  SbSocket server_socket = CreateBoundTcpIpv4Socket(GetPortNumberForTests());
-  if (!SbSocketIsValid(server_socket)) {
-    return;
-  }
+  SbSocket server_socket =
+      CreateBoundTcpSocket(GetAddressType(), GetPortNumberForTests());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Accept should result in an error.
   EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
@@ -71,6 +72,17 @@
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketAcceptTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketAcceptTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_bind_test.cc b/src/starboard/nplb/socket_bind_test.cc
index 81cb4e8..04b9c5e 100644
--- a/src/starboard/nplb/socket_bind_test.cc
+++ b/src/starboard/nplb/socket_bind_test.cc
@@ -15,6 +15,8 @@
 // The basic Sunny Day test is a subset of other Sunny Day tests, so it is not
 // repeated here.
 
+#include <utility>
+
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,62 +25,84 @@
 namespace nplb {
 namespace {
 
+class SbSocketBindTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketResolveFilter> > {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam().first; }
+  SbSocketResolveFilter GetFilterType() { return GetParam().second; }
+};
+
+#if SB_HAS(IPV6)
+class PairSbSocketBindTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+#endif
+
 // This is to use NULL in asserts, which otherwise complain about long
 // vs. pointer type.
 const void* kNull = NULL;
 
-TEST(SbSocketBindTest, RainyDayNullSocket) {
-  SbSocketAddress address = GetIpv4Unspecified(GetPortNumberForTests());
+TEST_P(SbSocketBindTest, RainyDayNullSocket) {
+  SbSocketAddress address =
+      GetUnspecifiedAddress(GetAddressType(), GetPortNumberForTests());
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(kSbSocketInvalid, &address));
 }
 
-TEST(SbSocketBindTest, RainyDayNullAddress) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketBindTest, RainyDayNullAddress) {
+  SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Binding with a NULL address should fail.
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(server_socket, NULL));
 
   // Even though that failed, binding the same socket now with 0.0.0.0:2048
   // should work.
-  SbSocketAddress address = GetIpv4Unspecified(GetPortNumberForTests());
+  SbSocketAddress address =
+      GetUnspecifiedAddress(GetAddressType(), GetPortNumberForTests());
   EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketBindTest, RainyDayNullNull) {
+TEST_F(SbSocketBindTest, RainyDayNullNull) {
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(kSbSocketInvalid, NULL));
 }
 
 #if SB_HAS(IPV6)
-TEST(SbSocketBindTest, RainyDayWrongAddressType) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(PairSbSocketBindTest, RainyDayWrongAddressType) {
+  SbSocket server_socket = CreateServerTcpSocket(GetServerAddressType());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Binding with the wrong address type should fail.
-  SbSocketAddress address = GetIpv6Unspecified(GetPortNumberForTests());
+  SbSocketAddress address =
+      GetUnspecifiedAddress(GetClientAddressType(), GetPortNumberForTests());
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(server_socket, &address));
 
-  // Even though that failed, binding the same socket now with 0.0.0.0:2048
-  // should work.
-  address = GetIpv4Unspecified(GetPortNumberForTests());
+  // Even though that failed, binding the same socket now with the server
+  // address type should work.
+  address =
+      GetUnspecifiedAddress(GetServerAddressType(), GetPortNumberForTests());
   EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 #endif
 
-TEST(SbSocketBindTest, RainyDayBadInterface) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketBindTest, RainyDayBadInterface) {
+  SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Binding with an interface that doesn't exist on this device should fail, so
   // let's find an address of a well-known public website that we shouldn't be
   // able to bind to.
   const char* kTestHostName = "www.yahoo.com";
   SbSocketResolution* resolution =
-      SbSocketResolve(kTestHostName, kSbSocketResolveFilterIpv4);
+      SbSocketResolve(kTestHostName, GetFilterType());
   ASSERT_NE(kNull, resolution);
   EXPECT_LT(0, resolution->address_count);
   EXPECT_EQ(kSbSocketErrorFailed,
@@ -88,17 +112,42 @@
   SbSocketFreeResolution(resolution);
 }
 
-TEST(SbSocketBindTest, SunnyDayLocalInterface) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
-
+TEST_F(SbSocketBindTest, SunnyDayLocalInterface) {
   SbSocketAddress address = {0};
+#if SB_API_VERSION < 4
   EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&address));
+#else
+  EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &address, NULL));
+#endif  // SB_API_VERSION < 4
+  SbSocket server_socket = CreateServerTcpSocket(address.type);
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
+
   EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    SbSocketBindTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketResolveFilterIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketResolveFilterIpv6)));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketBindTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    SbSocketBindTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketResolveFilterIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_clear_last_error_test.cc b/src/starboard/nplb/socket_clear_last_error_test.cc
index dd54d13..bce6015 100644
--- a/src/starboard/nplb/socket_clear_last_error_test.cc
+++ b/src/starboard/nplb/socket_clear_last_error_test.cc
@@ -25,7 +25,7 @@
 
 TEST(SbSocketClearLastErrorTest, SunnyDay) {
   // Set up a socket, but don't Bind or Listen.
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
+  SbSocket server_socket = CreateServerTcpSocket(kSbSocketAddressTypeIpv4);
   ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Accept on the unbound socket should result in an error.
diff --git a/src/starboard/nplb/socket_connect_test.cc b/src/starboard/nplb/socket_connect_test.cc
index 64b8dba..34378cf 100644
--- a/src/starboard/nplb/socket_connect_test.cc
+++ b/src/starboard/nplb/socket_connect_test.cc
@@ -23,22 +23,39 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketConnectTest, RainyDayNullSocket) {
-  SbSocketAddress address = GetIpv4Unspecified(2048);
+class SbSocketConnectTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_P(SbSocketConnectTest, RainyDayNullSocket) {
+  SbSocketAddress address = GetUnspecifiedAddress(GetAddressType(), 2048);
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(kSbSocketInvalid, &address));
 }
 
-TEST(SbSocketConnectTest, RainyDayNullAddress) {
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+TEST_P(SbSocketConnectTest, RainyDayNullAddress) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(socket, NULL));
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
-TEST(SbSocketConnectTest, RainyDayNullNull) {
+TEST_F(SbSocketConnectTest, RainyDayNullNull) {
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(kSbSocketInvalid, NULL));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketConnectTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketConnectTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_create_test.cc b/src/starboard/nplb/socket_create_test.cc
index fea779a..d379f26 100644
--- a/src/starboard/nplb/socket_create_test.cc
+++ b/src/starboard/nplb/socket_create_test.cc
@@ -19,59 +19,59 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketCreateTest, TcpIpv4) {
-  SbSocket socket =
-      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
-  EXPECT_TRUE(SbSocketIsValid(socket));
+class SbSocketCreateTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketCreateTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketProtocol> > {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam().first; }
+  SbSocketProtocol GetProtocol() { return GetParam().second; }
+};
+
+TEST_P(PairSbSocketCreateTest, Create) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), GetProtocol());
+#if !SB_HAS(IPV6)
+  // It is allowed for a platform not to support IPv6 sockets, but we use this
+  // test to at least exercise the code path.
+  if (kSbSocketAddressTypeIpv6 == GetAddressType()) {
+    if (SbSocketIsValid(socket)) {
+      EXPECT_TRUE(SbSocketDestroy(socket));
+    }
+    return;
+  }
+#endif
+  if (kSbSocketProtocolUdp == GetProtocol()) {
+    // It is allowed for a platform not to support UDP sockets, but we use this
+    // test to at least exercise the code path.
+    if (SbSocketIsValid(socket)) {
+      EXPECT_TRUE(SbSocketDestroy(socket));
+    }
+    return;
+  }
+  ASSERT_TRUE(SbSocketIsValid(socket));
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
-TEST(SbSocketCreateTest, TcpIpv6) {
-  // It is allowed for a platform not to support IPv6 sockets, but we use this
-  // test to at least exercise the code path.
-  SbSocket socket =
-      SbSocketCreate(kSbSocketAddressTypeIpv6, kSbSocketProtocolTcp);
-  if (SbSocketIsValid(socket)) {
-    EXPECT_TRUE(SbSocketDestroy(socket));
-  }
-}
-
-TEST(SbSocketCreateTest, UdpIpv4) {
-  // It is allowed for a platform not to support UDP sockets, but we use this
-  // test to at least exercise the code path.
-  SbSocket socket =
-      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
-  if (SbSocketIsValid(socket)) {
-    EXPECT_TRUE(SbSocketDestroy(socket));
-  }
-}
-
-TEST(SbSocketCreateTest, UdpIpv6) {
-  // It is allowed for a platform not to support UDP and/or IPv6 sockets, but we
-  // use this test to at least exercise the code path.
-  SbSocket socket =
-      SbSocketCreate(kSbSocketAddressTypeIpv6, kSbSocketProtocolUdp);
-  if (SbSocketIsValid(socket)) {
-    EXPECT_TRUE(SbSocketDestroy(socket));
-  }
-}
-
-TEST(SbSocketCreateTest, ATonOfTcpIpv4) {
+TEST_P(SbSocketCreateTest, ATonOfTcp) {
   const int kATon = 4096;
   for (int i = 0; i < kATon; ++i) {
-    SbSocket socket =
-        SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
-    EXPECT_TRUE(SbSocketIsValid(socket));
+    SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+    ASSERT_TRUE(SbSocketIsValid(socket));
     EXPECT_TRUE(SbSocketDestroy(socket));
   }
 }
 
-TEST(SbSocketCreateTest, ManyTcpIpv4AtOnce) {
+TEST_P(SbSocketCreateTest, ManyTcpAtOnce) {
   const int kMany = 128;
   SbSocket sockets[kMany] = {0};
   for (int i = 0; i < kMany; ++i) {
-    sockets[i] = SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
-    EXPECT_TRUE(SbSocketIsValid(sockets[i]));
+    sockets[i] = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+    ASSERT_TRUE(SbSocketIsValid(sockets[i]));
   }
 
   for (int i = 0; i < kMany; ++i) {
@@ -79,6 +79,26 @@
   }
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketCreateTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketCreateTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
+INSTANTIATE_TEST_CASE_P(
+    SbSocketTypes,
+    PairSbSocketCreateTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp),
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolTcp),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolUdp)));
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_get_local_address_test.cc b/src/starboard/nplb/socket_get_local_address_test.cc
index 112f9a4..2021446 100644
--- a/src/starboard/nplb/socket_get_local_address_test.cc
+++ b/src/starboard/nplb/socket_get_local_address_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <utility>
+
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,40 +22,54 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketGetLocalAddressTest, RainyDayInvalidSocket) {
+class SbSocketGetLocalAddressTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketGetLocalAddressTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
+TEST_F(SbSocketGetLocalAddressTest, RainyDayInvalidSocket) {
   SbSocketAddress address = {0};
   EXPECT_FALSE(SbSocketGetLocalAddress(kSbSocketInvalid, &address));
 }
 
-TEST(SbSocketGetLocalAddressTest, RainyDayNullAddress) {
-  SbSocket server_socket = CreateBoundTcpIpv4Socket(0);
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketGetLocalAddressTest, RainyDayNullAddress) {
+  SbSocket server_socket = CreateBoundTcpSocket(GetAddressType(), 0);
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
   EXPECT_FALSE(SbSocketGetLocalAddress(server_socket, NULL));
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketGetLocalAddressTest, RainyDayNullNull) {
+TEST_F(SbSocketGetLocalAddressTest, RainyDayNullNull) {
   EXPECT_FALSE(SbSocketGetLocalAddress(kSbSocketInvalid, NULL));
 }
 
-TEST(SbSocketGetLocalAddressTest, SunnyDayUnbound) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
+TEST_P(SbSocketGetLocalAddressTest, SunnyDayUnbound) {
+  SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
   SbSocketAddress address = {0};
   EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
-  EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
+  EXPECT_EQ(GetAddressType(), address.type);
   EXPECT_EQ(0, address.port);
   EXPECT_TRUE(IsUnspecified(&address));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketGetLocalAddressTest, SunnyDayBoundUnspecified) {
-  SbSocket server_socket = CreateBoundTcpIpv4Socket(0);
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketGetLocalAddressTest, SunnyDayBoundUnspecified) {
+  SbSocket server_socket = CreateBoundTcpSocket(GetAddressType(), 0);
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   SbSocketAddress address = {0};
   EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
-  EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
+  EXPECT_EQ(GetAddressType(), address.type);
   EXPECT_NE(0, address.port);
 
   // We bound to an unspecified address, so it should come back that way.
@@ -62,31 +78,31 @@
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketGetLocalAddressTest, SunnyDayBoundSpecified) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_F(SbSocketGetLocalAddressTest, SunnyDayBoundSpecified) {
+  SbSocketAddress interface_address = {0};
+#if SB_API_VERSION < 4
+  EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&interface_address));
+#else
+  EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &interface_address, NULL));
+#endif
+  SbSocket server_socket = CreateServerTcpSocket(interface_address.type);
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
-  {
-    SbSocketAddress address = {0};
-    EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&address));
-    EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
-  }
+  EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &interface_address));
 
-  SbSocketAddress address = {0};
-  EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
-  EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
-  EXPECT_NE(0, address.port);
-  EXPECT_FALSE(IsUnspecified(&address));
+  SbSocketAddress local_address = {0};
+  EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &local_address));
+  EXPECT_NE(0, local_address.port);
+  EXPECT_FALSE(IsUnspecified(&local_address));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
-TEST(SbSocketGetLocalAddressTest, SunnyDayConnected) {
+TEST_P(PairSbSocketGetLocalAddressTest, SunnyDayConnected) {
   const int kPort = GetPortNumberForTests();
-  ConnectedTrio trio = CreateAndConnect(kPort, kSocketTimeout);
-  if (!SbSocketIsValid(trio.server_socket)) {
-    return;
-  }
+  ConnectedTrio trio = CreateAndConnect(
+      GetServerAddressType(), GetClientAddressType(), kPort, kSocketTimeout);
+  ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
 
   SbSocketAddress address = {0};
   EXPECT_TRUE(SbSocketGetLocalAddress(trio.listen_socket, &address));
@@ -108,6 +124,29 @@
   EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketGetLocalAddressTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketGetLocalAddressTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketGetLocalAddressTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketGetLocalAddressTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_get_local_interface_address_test.cc b/src/starboard/nplb/socket_get_local_interface_address_test.cc
index 1051c24..566aad3 100644
--- a/src/starboard/nplb/socket_get_local_interface_address_test.cc
+++ b/src/starboard/nplb/socket_get_local_interface_address_test.cc
@@ -20,21 +20,45 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketGetLocalInterfaceTest, SunnyDay) {
+class SbSocketGetLocalInterfaceAddressTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_F(SbSocketGetLocalInterfaceAddressTest, SunnyDay) {
   SbSocketAddress address;
   // Initialize to something invalid.
   SbMemorySet(&address, 0xFE, sizeof(address));
+#if SB_API_VERSION < 4
   EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&address));
+#else
+  EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &address, NULL));
+#endif  // SB_API_VERSION < 4
   EXPECT_EQ(0, address.port);
-  EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
   EXPECT_FALSE(IsUnspecified(&address));
   EXPECT_FALSE(IsLocalhost(&address));
 }
 
-TEST(SbSocketGetLocalInterfaceTest, RainyDayNull) {
+TEST_F(SbSocketGetLocalInterfaceAddressTest, RainyDayNull) {
+#if SB_API_VERSION < 4
   EXPECT_FALSE(SbSocketGetLocalInterfaceAddress(NULL));
+#else
+  EXPECT_FALSE(SbSocketGetInterfaceAddress(NULL, NULL, NULL));
+#endif  // SB_API_VERSION < 4
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketGetLocalInterfaceAddressTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketGetLocalInterfaceAddressTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_helpers.cc b/src/starboard/nplb/socket_helpers.cc
index 6f9b35a..c78a21a 100644
--- a/src/starboard/nplb/socket_helpers.cc
+++ b/src/starboard/nplb/socket_helpers.cc
@@ -30,7 +30,7 @@
 
 void InitializePortNumberForTests() {
   // Create a listening socket. Let the system choose a port for us.
-  SbSocket socket = CreateListeningTcpIpv4Socket(0);
+  SbSocket socket = CreateListeningTcpSocket(kSbSocketAddressTypeIpv4, 0);
   SB_DCHECK(socket != kSbSocketInvalid);
 
   // Query which port this socket was bound to and save it to valid_port_number.
@@ -68,8 +68,7 @@
 
 bool IsLocalhost(const SbSocketAddress* address) {
   if (address->type == kSbSocketAddressTypeIpv4) {
-    return (address->address[0] == 127 && address->address[1] == 0 &&
-            address->address[2] == 0 && address->address[3] == 1);
+    return address->address[0] == 127;
   }
 
   if (address->type == kSbSocketAddressTypeIpv6) {
@@ -84,35 +83,34 @@
   return false;
 }
 
-SbSocketAddress GetIpv4Localhost(int port) {
-  SbSocketAddress address = GetIpv4Unspecified(port);
-  address.address[0] = 127;
-  address.address[3] = 1;
+SbSocketAddress GetLocalhostAddress(SbSocketAddressType address_type,
+                                    int port) {
+  SbSocketAddress address = GetUnspecifiedAddress(address_type, port);
+  switch (address_type) {
+    case kSbSocketAddressTypeIpv4: {
+      address.address[0] = 127;
+      address.address[3] = 1;
+      return address;
+    }
+    case kSbSocketAddressTypeIpv6: {
+      address.address[15] = 1;
+      return address;
+    }
+  }
+  ADD_FAILURE() << "GetLocalhostAddress for unknown address type";
   return address;
 }
 
-SbSocketAddress GetIpv4Unspecified(int port) {
+SbSocketAddress GetUnspecifiedAddress(SbSocketAddressType address_type,
+                                      int port) {
   SbSocketAddress address = {0};
-  address.type = kSbSocketAddressTypeIpv4;
+  address.type = address_type;
   address.port = port;
   return address;
 }
 
-SbSocketAddress GetIpv6Localhost(int port) {
-  SbSocketAddress address = GetIpv6Unspecified(port);
-  address.address[15] = 1;
-  return address;
-}
-
-SbSocketAddress GetIpv6Unspecified(int port) {
-  SbSocketAddress address = {0};
-  address.type = kSbSocketAddressTypeIpv6;
-  address.port = port;
-  return address;
-}
-
-SbSocket CreateServerTcpIpv4Socket() {
-  SbSocket server_socket = CreateTcpIpv4Socket();
+SbSocket CreateServerTcpSocket(SbSocketAddressType address_type) {
+  SbSocket server_socket = SbSocketCreate(address_type, kSbSocketProtocolTcp);
   if (!SbSocketIsValid(server_socket)) {
     ADD_FAILURE() << "SbSocketCreate failed";
     return kSbSocketInvalid;
@@ -127,13 +125,13 @@
   return server_socket;
 }
 
-SbSocket CreateBoundTcpIpv4Socket(int port) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
+SbSocket CreateBoundTcpSocket(SbSocketAddressType address_type, int port) {
+  SbSocket server_socket = CreateServerTcpSocket(address_type);
   if (!SbSocketIsValid(server_socket)) {
     return kSbSocketInvalid;
   }
 
-  SbSocketAddress address = GetIpv4Unspecified(port);
+  SbSocketAddress address = GetUnspecifiedAddress(address_type, port);
   SbSocketError result = SbSocketBind(server_socket, &address);
   if (result != kSbSocketOk) {
     ADD_FAILURE() << "SbSocketBind to " << port << " failed: " << result;
@@ -144,8 +142,8 @@
   return server_socket;
 }
 
-SbSocket CreateListeningTcpIpv4Socket(int port) {
-  SbSocket server_socket = CreateBoundTcpIpv4Socket(port);
+SbSocket CreateListeningTcpSocket(SbSocketAddressType address_type, int port) {
+  SbSocket server_socket = CreateBoundTcpSocket(address_type, port);
   if (!SbSocketIsValid(server_socket)) {
     return kSbSocketInvalid;
   }
@@ -160,15 +158,16 @@
   return server_socket;
 }
 
-SbSocket CreateConnectingTcpIpv4Socket(int port) {
-  SbSocket client_socket = CreateTcpIpv4Socket();
+namespace {
+SbSocket CreateConnectingTcpSocket(SbSocketAddressType address_type, int port) {
+  SbSocket client_socket = SbSocketCreate(address_type, kSbSocketProtocolTcp);
   if (!SbSocketIsValid(client_socket)) {
     ADD_FAILURE() << "SbSocketCreate failed";
     return kSbSocketInvalid;
   }
 
   // Connect to localhost:<port>.
-  SbSocketAddress address = GetIpv4Localhost(port);
+  SbSocketAddress address = GetLocalhostAddress(address_type, port);
 
   // This connect will probably return pending, but we'll assume it will connect
   // eventually.
@@ -181,6 +180,7 @@
 
   return client_socket;
 }
+}  // namespace
 
 SbSocket AcceptBySpinning(SbSocket server_socket, SbTime timeout) {
   SbTimeMonotonic start = SbTimeGetMonotonicNow();
@@ -260,16 +260,19 @@
   return true;
 }
 
-ConnectedTrio CreateAndConnect(int port, SbTime timeout) {
-  // Set up a listening socket.
-  SbSocket listen_socket = CreateListeningTcpIpv4Socket(port);
+ConnectedTrio CreateAndConnect(SbSocketAddressType server_address_type,
+                               SbSocketAddressType client_address_type,
+                               int port,
+                               SbTime timeout) {
+  // Verify the listening socket.
+  SbSocket listen_socket = CreateListeningTcpSocket(server_address_type, port);
   if (!SbSocketIsValid(listen_socket)) {
     ADD_FAILURE() << "Could not create listen socket.";
     return ConnectedTrio();
   }
 
-  // Create a new socket to connect to the listening socket.
-  SbSocket client_socket = CreateConnectingTcpIpv4Socket(port);
+  // Verify the socket to connect to the listening socket.
+  SbSocket client_socket = CreateConnectingTcpSocket(client_address_type, port);
   if (!SbSocketIsValid(client_socket)) {
     ADD_FAILURE() << "Could not create client socket.";
     EXPECT_TRUE(SbSocketDestroy(listen_socket));
diff --git a/src/starboard/nplb/socket_helpers.h b/src/starboard/nplb/socket_helpers.h
index a01c02d..8cb8922 100644
--- a/src/starboard/nplb/socket_helpers.h
+++ b/src/starboard/nplb/socket_helpers.h
@@ -25,16 +25,6 @@
 
 const SbTime kSocketTimeout = kSbTimeSecond / 5;
 
-// Creates a plain TCP/IPv4 socket.
-static inline SbSocket CreateTcpIpv4Socket() {
-  return SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
-}
-
-// Creates a plain UDP/IPv4 socket.
-static inline SbSocket CreateUdpIpv4Socket() {
-  return SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
-}
-
 // Returns true if the given address is the unspecified address (all zeros),
 // supporting both address types.
 bool IsUnspecified(const SbSocketAddress* address);
@@ -47,29 +37,21 @@
 // This will always return the same port number.
 int GetPortNumberForTests();
 
-// Returns an IPv4 localhost address with the given port.
-SbSocketAddress GetIpv4Localhost(int port);
+// Returns an IP localhost address with the given port.
+SbSocketAddress GetLocalhostAddress(SbSocketAddressType address_type, int port);
 
-// Returns an IPv4 unspecified address with the given port.
-SbSocketAddress GetIpv4Unspecified(int port);
+// Returns an IP unspecified address with the given port.
+SbSocketAddress GetUnspecifiedAddress(SbSocketAddressType address_type,
+                                      int port);
 
-// Returns an IPv6 localhost address with the given port.
-SbSocketAddress GetIpv6Localhost(int port);
+// Creates a TCP/IP server socket (sets Reuse Address option).
+SbSocket CreateServerTcpSocket(SbSocketAddressType address_type);
 
-// Returns an IPv6 unspecified address with the given port.
-SbSocketAddress GetIpv6Unspecified(int port);
+// Creates a TCP/IP socket bound to all interfaces on the given port.
+SbSocket CreateBoundTcpSocket(SbSocketAddressType address_type, int port);
 
-// Creates a TCP/IPv4 server socket (sets Reuse Address option).
-SbSocket CreateServerTcpIpv4Socket();
-
-// Creates a TCP/IPv4 socket bound to all interfaces on the given port.
-SbSocket CreateBoundTcpIpv4Socket(int port);
-
-// Creates a TCP/IPv4 socket listening on all interfaces on the given port.
-SbSocket CreateListeningTcpIpv4Socket(int port);
-
-// Creates a TCP/IPv4 socket connecting to the given port on localhost.
-SbSocket CreateConnectingTcpIpv4Socket(int port);
+// Creates a TCP/IP socket listening on all interfaces on the given port.
+SbSocket CreateListeningTcpSocket(SbSocketAddressType address_type, int port);
 
 // Tries to accept a new connection from the given listening socket by checking,
 // yielding, and retrying for up to timeout. Returns kSbSocketInvalid if no
@@ -104,10 +86,13 @@
   SbSocket server_socket;
 } ConnectedTrio;
 
-// Creates and returns 3 sockets, a connected client and server, and a listener
-// on the given port. If anything fails, adds a failure and returns three
-// invalid sockets.
-ConnectedTrio CreateAndConnect(int port, SbTime timeout);
+// Creates and returns 3 TCP/IP sockets, a connected client and server, and a
+// listener on the given port. If anything fails, adds a failure and returns
+// three invalid sockets.
+ConnectedTrio CreateAndConnect(SbSocketAddressType server_address_type,
+                               SbSocketAddressType client_address_type,
+                               int port,
+                               SbTime timeout);
 
 // Waits on the given waiter, and returns the elapsed time.
 SbTimeMonotonic TimedWait(SbSocketWaiter waiter);
diff --git a/src/starboard/nplb/socket_is_connected_and_idle_test.cc b/src/starboard/nplb/socket_is_connected_and_idle_test.cc
index 74a082c..29b047c 100644
--- a/src/starboard/nplb/socket_is_connected_and_idle_test.cc
+++ b/src/starboard/nplb/socket_is_connected_and_idle_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <utility>
+
 #include "starboard/log.h"
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
@@ -22,6 +24,20 @@
 namespace nplb {
 namespace {
 
+class SbSocketIsConnectedAndIdleTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketIsConnectedAndIdleTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
 bool IsNonIdleWithin(SbSocket socket, SbTimeMonotonic timeout) {
   SbTimeMonotonic deadline = SbTimeGetMonotonicNow() + timeout;
   while (SbTimeGetMonotonicNow() < deadline) {
@@ -32,16 +48,15 @@
   return false;
 }
 
-TEST(SbSocketIsConnectedAndIdleTest, RainyDayInvalidSocket) {
+TEST_F(SbSocketIsConnectedAndIdleTest, RainyDayInvalidSocket) {
   EXPECT_FALSE(SbSocketIsConnectedAndIdle(kSbSocketInvalid));
 }
 
-TEST(SbSocketIsConnectedAndIdleTest, SunnyDay) {
+TEST_P(PairSbSocketIsConnectedAndIdleTest, SunnyDay) {
   ConnectedTrio trio =
-      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
-  if (!SbSocketIsValid(trio.server_socket)) {
-    return;
-  }
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
+  ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
 
   EXPECT_FALSE(SbSocketIsConnectedAndIdle(trio.listen_socket));
   EXPECT_TRUE(SbSocketIsConnectedAndIdle(trio.server_socket));
@@ -63,28 +78,44 @@
   EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
 }
 
-TEST(SbSocketIsConnectedAndIdleTest, SunnyDayNotConnected) {
-  SbSocket socket = CreateTcpIpv4Socket();
-  if (!SbSocketIsValid(socket)) {
-    return;
-  }
-
+TEST_P(SbSocketIsConnectedAndIdleTest, SunnyDayNotConnected) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
   EXPECT_TRUE(IsNonIdleWithin(socket, kSocketTimeout));
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
-TEST(SbSocketIsConnectedAndIdleTest, SunnyDayListeningNotConnected) {
+TEST_P(SbSocketIsConnectedAndIdleTest, SunnyDayListeningNotConnected) {
   SbSocket server_socket =
-      CreateListeningTcpIpv4Socket(GetPortNumberForTests());
-  if (!SbSocketIsValid(server_socket)) {
-    return;
-  }
-
+      CreateListeningTcpSocket(GetAddressType(), GetPortNumberForTests());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
   EXPECT_FALSE(SbSocketIsConnectedAndIdle(server_socket));
-
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketIsConnectedAndIdleTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketIsConnectedAndIdleTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketIsConnectedAndIdleTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketIsConnectedAndIdleTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_is_connected_test.cc b/src/starboard/nplb/socket_is_connected_test.cc
index e69fc4d..6bdf082 100644
--- a/src/starboard/nplb/socket_is_connected_test.cc
+++ b/src/starboard/nplb/socket_is_connected_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <utility>
+
 #include "starboard/log.h"
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
@@ -21,17 +23,29 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketIsConnectedTest, RainyDayInvalidSocket) {
+class SbSocketIsConnectedTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketIsConnectedTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
+TEST_F(SbSocketIsConnectedTest, RainyDayInvalidSocket) {
   EXPECT_FALSE(SbSocketIsConnected(kSbSocketInvalid));
 }
 
-TEST(SbSocketIsConnectedTest, SunnyDay) {
+TEST_P(PairSbSocketIsConnectedTest, SunnyDay) {
   ConnectedTrio trio =
-      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
-  if (!SbSocketIsValid(trio.server_socket)) {
-    return;
-  }
-
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
+  ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
   EXPECT_FALSE(SbSocketIsConnected(trio.listen_socket));
   EXPECT_TRUE(SbSocketIsConnected(trio.server_socket));
   EXPECT_TRUE(SbSocketIsConnected(trio.client_socket));
@@ -48,29 +62,44 @@
   EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
 }
 
-TEST(SbSocketIsConnectedTest, SunnyDayNotConnected) {
-  SbSocket socket = CreateTcpIpv4Socket();
-  if (!SbSocketIsValid(socket)) {
-    return;
-  }
-
+TEST_P(SbSocketIsConnectedTest, SunnyDayNotConnected) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
   EXPECT_FALSE(SbSocketIsConnected(socket));
-
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
-TEST(SbSocketIsConnectedTest, SunnyDayListeningNotConnected) {
+TEST_P(SbSocketIsConnectedTest, SunnyDayListeningNotConnected) {
   SbSocket server_socket =
-      CreateListeningTcpIpv4Socket(GetPortNumberForTests());
-  if (!SbSocketIsValid(server_socket)) {
-    return;
-  }
-
+      CreateListeningTcpSocket(GetAddressType(), GetPortNumberForTests());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
   EXPECT_FALSE(SbSocketIsConnected(server_socket));
-
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketIsConnectedTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketIsConnectedTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketIsConnectedTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketIsConnectedTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_join_multicast_group_test.cc b/src/starboard/nplb/socket_join_multicast_group_test.cc
index 115abbf..0ad788c 100644
--- a/src/starboard/nplb/socket_join_multicast_group_test.cc
+++ b/src/starboard/nplb/socket_join_multicast_group_test.cc
@@ -22,12 +22,14 @@
 namespace {
 
 SbSocket CreateMulticastSocket(const SbSocketAddress& address) {
-  SbSocket socket = CreateUdpIpv4Socket();
+  SbSocket socket =
+      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
   EXPECT_TRUE(SbSocketIsValid(socket));
   EXPECT_TRUE(SbSocketSetReuseAddress(socket, true));
 
   // It will choose a port for me.
-  SbSocketAddress bind_address = GetIpv4Unspecified(0);
+  SbSocketAddress bind_address =
+      GetUnspecifiedAddress(kSbSocketAddressTypeIpv4, 0);
   EXPECT_EQ(kSbSocketOk, SbSocketBind(socket, &bind_address));
 
   EXPECT_TRUE(SbSocketJoinMulticastGroup(socket, &address));
@@ -35,7 +37,8 @@
 }
 
 SbSocket CreateSendSocket() {
-  SbSocket socket = CreateUdpIpv4Socket();
+  SbSocket socket =
+      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
   EXPECT_TRUE(SbSocketIsValid(socket));
   EXPECT_TRUE(SbSocketSetReuseAddress(socket, true));
   return socket;
@@ -45,7 +48,7 @@
   // "224.0.2.0" is an unassigned ad-hoc multicast address. Hopefully no one is
   // spamming it on the local network.
   // http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
-  SbSocketAddress address = GetIpv4Unspecified(0);
+  SbSocketAddress address = GetUnspecifiedAddress(kSbSocketAddressTypeIpv4, 0);
   address.address[0] = 224;
   address.address[2] = 2;
 
@@ -96,13 +99,14 @@
 }
 
 TEST(SbSocketJoinMulticastGroupTest, RainyDayInvalidSocket) {
-  SbSocketAddress address = GetIpv4Unspecified(0);
+  SbSocketAddress address = GetUnspecifiedAddress(kSbSocketAddressTypeIpv4, 0);
   EXPECT_FALSE(SbSocketJoinMulticastGroup(kSbSocketInvalid, &address));
 }
 
 TEST(SbSocketJoinMulticastGroupTest, RainyDayInvalidAddress) {
-  SbSocket socket = CreateUdpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket =
+      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
   EXPECT_FALSE(SbSocketJoinMulticastGroup(socket, NULL));
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
diff --git a/src/starboard/nplb/socket_listen_test.cc b/src/starboard/nplb/socket_listen_test.cc
index 9a403a7..aaeabab 100644
--- a/src/starboard/nplb/socket_listen_test.cc
+++ b/src/starboard/nplb/socket_listen_test.cc
@@ -23,13 +23,19 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketListenTest, RainyDayInvalid) {
+class SbSocketListenTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_F(SbSocketListenTest, RainyDayInvalid) {
   EXPECT_EQ(kSbSocketErrorFailed, SbSocketListen(kSbSocketInvalid));
 }
 
-TEST(SbSocketListenTest, SunnyDayUnbound) {
-  SbSocket server_socket = CreateServerTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketListenTest, SunnyDayUnbound) {
+  SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+  ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   EXPECT_EQ(kSbSocketOk, SbSocketListen(server_socket));
 
@@ -37,13 +43,24 @@
   // all local interfaces.
   SbSocketAddress address = {0};
   EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
-  EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
+  EXPECT_EQ(GetAddressType(), address.type);
   EXPECT_NE(0, address.port);
   EXPECT_TRUE(IsUnspecified(&address));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketListenTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketListenTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_receive_from_test.cc b/src/starboard/nplb/socket_receive_from_test.cc
index 5cd8c43..f4dba24 100644
--- a/src/starboard/nplb/socket_receive_from_test.cc
+++ b/src/starboard/nplb/socket_receive_from_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <utility>
+
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
 #include "starboard/time.h"
@@ -21,6 +23,14 @@
 namespace nplb {
 namespace {
 
+class PairSbSocketReceiveFromTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
 // Transfers data between the two connected local sockets, spinning until |size|
 // has been transfered, or an error occurs.
 int Transfer(SbSocket receive_socket,
@@ -59,15 +69,14 @@
   return size;
 }
 
-TEST(SbSocketReceiveFromTest, SunnyDay) {
+TEST_P(PairSbSocketReceiveFromTest, SunnyDay) {
   const int kBufSize = 256 * 1024;
   const int kSockBufSize = kBufSize / 8;
 
   ConnectedTrio trio =
-      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
-  if (!SbSocketIsValid(trio.server_socket)) {
-    return;
-  }
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
+  ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
 
   // Let's set the buffers small to create partial reads and writes.
   SbSocketSetReceiveBufferSize(trio.client_socket, kSockBufSize);
@@ -116,6 +125,22 @@
   EXPECT_EQ(-1, result);
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketReceiveFromTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketReceiveFromTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_resolve_test.cc b/src/starboard/nplb/socket_resolve_test.cc
index b088a0d..ebca117 100644
--- a/src/starboard/nplb/socket_resolve_test.cc
+++ b/src/starboard/nplb/socket_resolve_test.cc
@@ -12,8 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/nplb/socket_helpers.h"
+#include <utility>
+
 #include "starboard/log.h"
+#include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
 #include "starboard/string.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,6 +24,14 @@
 namespace nplb {
 namespace {
 
+class SbSocketResolveTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketResolveFilter, SbSocketAddressType> > {
+ public:
+  SbSocketResolveFilter GetResolveFilter() { return GetParam().first; }
+  SbSocketAddressType GetAddressType() { return GetParam().second; }
+};
+
 // This is to use NULL in asserts, which otherwise complain about long
 // vs. pointer type.
 const void* kNull = NULL;
@@ -31,7 +41,7 @@
 
 #define LOG_ADDRESS(i) "In returned address #" << (i)
 
-TEST(SbSocketResolveTest, SunnyDay) {
+TEST_F(SbSocketResolveTest, SunnyDay) {
   SbSocketResolution* resolution = SbSocketResolve(kTestHostName, 0);
   ASSERT_NE(kNull, resolution);
   EXPECT_LT(0, resolution->address_count);
@@ -47,35 +57,20 @@
   SbSocketFreeResolution(resolution);
 }
 
-TEST(SbSocketResolveTest, SunnyDayIpv4) {
+TEST_P(SbSocketResolveTest, SunnyDayFiltered) {
   SbSocketResolution* resolution =
-      SbSocketResolve(kTestHostName, kSbSocketResolveFilterIpv4);
+      SbSocketResolve(kTestHostName, GetResolveFilter());
   ASSERT_NE(kNull, resolution);
   EXPECT_LT(0, resolution->address_count);
   EXPECT_NE(kNull, resolution->addresses);
   for (int i = 0; i < resolution->address_count; ++i) {
     const SbSocketAddress& address = resolution->addresses[i];
-    EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type) << LOG_ADDRESS(i);
+    EXPECT_EQ(GetAddressType(), address.type) << LOG_ADDRESS(i);
   }
   SbSocketFreeResolution(resolution);
 }
 
-#if SB_HAS(IPV6)
-TEST(SbSocketResolveTest, SunnyDayIpv6) {
-  SbSocketResolution* resolution =
-      SbSocketResolve(kTestHostName, kSbSocketResolveFilterIpv6);
-  ASSERT_NE(kNull, resolution);
-  EXPECT_LT(0, resolution->address_count);
-  EXPECT_NE(kNull, resolution->addresses);
-  for (int i = 0; i < resolution->address_count; ++i) {
-    const SbSocketAddress& address = resolution->addresses[i];
-    EXPECT_EQ(kSbSocketAddressTypeIpv6, address.type) << LOG_ADDRESS(i);
-  }
-  SbSocketFreeResolution(resolution);
-}
-#endif
-
-TEST(SbSocketResolveTest, IgnoreExtraBits) {
+TEST_F(SbSocketResolveTest, IgnoreExtraBits) {
   // Even with this extra bit set, the resolution should come out the same.
   SbSocketResolution* resolution1 = SbSocketResolve(kTestHostName, 1 << 14);
   SbSocketResolution* resolution2 = SbSocketResolve(kTestHostName, 0);
@@ -89,11 +84,26 @@
   SbSocketFreeResolution(resolution2);
 }
 
-TEST(SbSocketResolveTest, RainyDayNullHostname) {
+TEST_F(SbSocketResolveTest, RainyDayNullHostname) {
   SbSocketResolution* resolution = SbSocketResolve(NULL, 0);
   ASSERT_EQ(kNull, resolution);
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    SbSocketResolveTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketResolveFilterIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketResolveFilterIpv6, kSbSocketAddressTypeIpv6)));
+#else
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    SbSocketResolveTest,
+    ::testing::Values(std::make_pair(kSbSocketResolveFilterIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_send_to_test.cc b/src/starboard/nplb/socket_send_to_test.cc
index 9db1d36..fae82a2 100644
--- a/src/starboard/nplb/socket_send_to_test.cc
+++ b/src/starboard/nplb/socket_send_to_test.cc
@@ -15,6 +15,8 @@
 // SendTo is largely tested with ReceiveFrom, so look there for more invovled
 // tests.
 
+#include <utility>
+
 #include "starboard/memory.h"
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
@@ -26,6 +28,14 @@
 namespace nplb {
 namespace {
 
+class PairSbSocketSendToTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
 // Thread entry point to continuously write to a socket that is expected to
 // be closed on another thread.
 void* SendToServerSocketEntryPoint(void* trio_as_void_ptr) {
@@ -58,9 +68,10 @@
   EXPECT_EQ(-1, result);
 }
 
-TEST(SbSocketSendToTest, RainyDaySendToClosedSocket) {
+TEST_P(PairSbSocketSendToTest, RainyDaySendToClosedSocket) {
   ConnectedTrio trio =
-      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
   EXPECT_NE(trio.client_socket, kSbSocketInvalid);
   EXPECT_NE(trio.server_socket, kSbSocketInvalid);
   EXPECT_NE(trio.listen_socket, kSbSocketInvalid);
@@ -87,6 +98,21 @@
   EXPECT_TRUE(SbSocketDestroy(trio.server_socket));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketSendToTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketSendToTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_set_options_test.cc b/src/starboard/nplb/socket_set_options_test.cc
index 4e075d9..a6557a8 100644
--- a/src/starboard/nplb/socket_set_options_test.cc
+++ b/src/starboard/nplb/socket_set_options_test.cc
@@ -21,8 +21,14 @@
 namespace nplb {
 namespace {
 
-TEST(SbSocketSetOptionsTest, TryThemAll) {
-  SbSocket socket = CreateTcpIpv4Socket();
+class SbSocketSetOptionsTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_P(SbSocketSetOptionsTest, TryThemAll) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
 
   EXPECT_TRUE(SbSocketSetBroadcast(socket, true));
   EXPECT_TRUE(SbSocketSetReuseAddress(socket, true));
@@ -40,6 +46,17 @@
 
 // TODO: Come up with some way to test the effects of these options.
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketSetOptionsTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketSetOptionsTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_add_test.cc b/src/starboard/nplb/socket_waiter_add_test.cc
index 25b3c3d..c17a4a1 100644
--- a/src/starboard/nplb/socket_waiter_add_test.cc
+++ b/src/starboard/nplb/socket_waiter_add_test.cc
@@ -21,17 +21,23 @@
 namespace nplb {
 namespace {
 
+class SbSocketWaiterAddTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
 void NoOpSocketWaiterCallback(SbSocketWaiter waiter,
                               SbSocket socket,
                               void* context,
                               int ready_interests) {}
 
-TEST(SbSocketWaiterAddTest, SunnyDay) {
+TEST_P(SbSocketWaiterAddTest, SunnyDay) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_TRUE(SbSocketWaiterAdd(
       waiter, socket, NULL, &NoOpSocketWaiterCallback,
@@ -41,12 +47,12 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterAddTest, SunnyDayPersistent) {
+TEST_P(SbSocketWaiterAddTest, SunnyDayPersistent) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_TRUE(SbSocketWaiterAdd(
       waiter, socket, NULL, &NoOpSocketWaiterCallback,
@@ -56,15 +62,15 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterAddTest, SunnyDayMany) {
+TEST_P(SbSocketWaiterAddTest, SunnyDayMany) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
   const int kMany = SB_FILE_MAX_OPEN;
   SbSocket sockets[kMany] = {0};
   for (int i = 0; i < kMany; ++i) {
-    sockets[i] = SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
-    EXPECT_TRUE(SbSocketIsValid(sockets[i]));
+    sockets[i] = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+    ASSERT_TRUE(SbSocketIsValid(sockets[i]));
     EXPECT_TRUE(SbSocketWaiterAdd(
         waiter, sockets[i], NULL, &NoOpSocketWaiterCallback,
         kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite, false));
@@ -77,12 +83,12 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterAddTest, RainyDayAddToSameWaiter) {
+TEST_P(SbSocketWaiterAddTest, RainyDayAddToSameWaiter) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   // First add should succeed.
   EXPECT_TRUE(SbSocketWaiterAdd(
@@ -111,15 +117,15 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterAddTest, RainyDayAddToOtherWaiter) {
+TEST_P(SbSocketWaiterAddTest, RainyDayAddToOtherWaiter) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
   SbSocketWaiter waiter2 = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter2));
 
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   // The first add should succeed.
   EXPECT_TRUE(SbSocketWaiterAdd(
@@ -149,7 +155,7 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter2));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidSocket) {
+TEST_F(SbSocketWaiterAddTest, RainyDayInvalidSocket) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
@@ -160,9 +166,9 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidWaiter) {
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+TEST_P(SbSocketWaiterAddTest, RainyDayInvalidWaiter) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_FALSE(SbSocketWaiterAdd(
       kSbSocketWaiterInvalid, socket, NULL, &NoOpSocketWaiterCallback,
@@ -171,11 +177,11 @@
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayNoCallback) {
+TEST_P(SbSocketWaiterAddTest, RainyDayNoCallback) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_FALSE(SbSocketWaiterAdd(
       waiter, socket, NULL, NULL,
@@ -185,11 +191,11 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayNoInterest) {
+TEST_P(SbSocketWaiterAddTest, RainyDayNoInterest) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_FALSE(SbSocketWaiterAdd(waiter, socket, NULL,
                                  &NoOpSocketWaiterCallback,
@@ -199,6 +205,17 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterAddTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterAddTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_remove_test.cc b/src/starboard/nplb/socket_waiter_remove_test.cc
index 5407cc4..6bb7a67 100644
--- a/src/starboard/nplb/socket_waiter_remove_test.cc
+++ b/src/starboard/nplb/socket_waiter_remove_test.cc
@@ -21,12 +21,18 @@
 namespace nplb {
 namespace {
 
+class SbSocketWaiterRemoveTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
 void NoOpSocketWaiterCallback(SbSocketWaiter waiter,
                               SbSocket socket,
                               void* context,
                               int ready_interests) {}
 
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidSocket) {
+TEST_F(SbSocketWaiterRemoveTest, RainyDayInvalidSocket) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
@@ -35,20 +41,20 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidWaiter) {
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+TEST_P(SbSocketWaiterRemoveTest, RainyDayInvalidWaiter) {
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_FALSE(SbSocketWaiterRemove(kSbSocketWaiterInvalid, socket));
 
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayNotAdded) {
+TEST_P(SbSocketWaiterRemoveTest, RainyDayNotAdded) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_FALSE(SbSocketWaiterRemove(waiter, socket));
 
@@ -56,11 +62,11 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterRemoveTest, RainyDayAlreadyRemoved) {
+TEST_P(SbSocketWaiterRemoveTest, RainyDayAlreadyRemoved) {
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
-  SbSocket socket = CreateTcpIpv4Socket();
-  EXPECT_TRUE(SbSocketIsValid(socket));
+  SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
 
   EXPECT_TRUE(SbSocketWaiterAdd(
       waiter, socket, NULL, &NoOpSocketWaiterCallback,
@@ -72,6 +78,17 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterRemoveTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterRemoveTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_wait_test.cc b/src/starboard/nplb/socket_waiter_wait_test.cc
index 8b67583..d70734a 100644
--- a/src/starboard/nplb/socket_waiter_wait_test.cc
+++ b/src/starboard/nplb/socket_waiter_wait_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <utility>
+
 #include "starboard/log.h"
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/nplb/thread_helpers.h"
@@ -25,6 +27,20 @@
 namespace nplb {
 namespace {
 
+class SbSocketWaiterWaitTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketWaiterWaitTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
 struct CallbackValues {
   int count;
   SbSocketWaiter waiter;
@@ -55,18 +71,16 @@
   ADD_FAILURE() << __FUNCTION__ << " was called.";
 }
 
-TEST(SbSocketWaiterWaitTest, SunnyDay) {
+TEST_P(PairSbSocketWaiterWaitTest, SunnyDay) {
   const int kBufSize = 1024;
 
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
   ConnectedTrio trio =
-      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
-  if (!SbSocketIsValid(trio.server_socket)) {
-    ADD_FAILURE();
-    return;
-  }
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
+  ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
 
   // The client socket should be ready to write right away, but not read until
   // it gets some data.
@@ -198,12 +212,14 @@
 // This test ensures that if the socket gets written to while it is not being
 // waited on, and inside a callback, it will become ready immediately the next
 // time it is waited on.
-TEST(SbSocketWaiterWaitTest, SunnyDayAlreadyReady) {
+TEST_P(PairSbSocketWaiterWaitTest, SunnyDayAlreadyReady) {
   AlreadyReadyContext context;
   context.waiter = SbSocketWaiterCreate();
   ASSERT_TRUE(SbSocketWaiterIsValid(context.waiter));
 
-  context.trio = CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
+  context.trio =
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
   ASSERT_TRUE(SbSocketIsValid(context.trio.server_socket));
 
   EXPECT_TRUE(SbSocketWaiterAdd(context.waiter, context.trio.client_socket,
@@ -221,10 +237,33 @@
   EXPECT_TRUE(SbThreadJoin(thread, NULL));
 }
 
-TEST(SbSocketWaiterWaitTest, RainyDayInvalidWaiter) {
+TEST_F(SbSocketWaiterWaitTest, RainyDayInvalidWaiter) {
   WaitShouldNotBlock(kSbSocketWaiterInvalid);
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterWaitTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketWaiterWaitTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterWaitTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketWaiterWaitTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_wait_timed_test.cc b/src/starboard/nplb/socket_waiter_wait_timed_test.cc
index 0166743..ec18c3e 100644
--- a/src/starboard/nplb/socket_waiter_wait_timed_test.cc
+++ b/src/starboard/nplb/socket_waiter_wait_timed_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <utility>
+
 #include "starboard/log.h"
 #include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
@@ -23,6 +25,20 @@
 namespace nplb {
 namespace {
 
+class SbSocketWaiterWaitTimedTest
+    : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+  SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketWaiterWaitTimedTest
+    : public ::testing::TestWithParam<
+          std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+  SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+  SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
 struct CallbackValues {
   int count;
   SbSocketWaiter waiter;
@@ -46,18 +62,16 @@
   SbSocketWaiterWakeUp(waiter);
 }
 
-TEST(SbSocketWaiterWaitTimedTest, SunnyDay) {
+TEST_P(PairSbSocketWaiterWaitTimedTest, SunnyDay) {
   const int kBufSize = 1024;
 
   SbSocketWaiter waiter = SbSocketWaiterCreate();
   EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
 
   ConnectedTrio trio =
-      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
-  if (!SbSocketIsValid(trio.server_socket)) {
-    ADD_FAILURE();
-    return;
-  }
+      CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+                       GetPortNumberForTests(), kSocketTimeout);
+  ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
 
   // The client socket should be ready to write right away, but not read until
   // it gets some data.
@@ -107,10 +121,33 @@
   EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
 }
 
-TEST(SbSocketWaiterWaitTimedTest, RainyDayInvalidWaiter) {
+TEST_F(SbSocketWaiterWaitTimedTest, RainyDayInvalidWaiter) {
   TimedWaitShouldNotBlock(kSbSocketWaiterInvalid, kSocketTimeout);
 }
 
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterWaitTimedTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4,
+                                          kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketWaiterWaitTimedTest,
+    ::testing::Values(
+        std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+        std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+                        SbSocketWaiterWaitTimedTest,
+                        ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+    SbSocketAddressTypes,
+    PairSbSocketWaiterWaitTimedTest,
+    ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+                                     kSbSocketAddressTypeIpv4)));
+#endif
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/system_get_path_test.cc b/src/starboard/nplb/system_get_path_test.cc
index fea6b9e..2e9ddf0 100644
--- a/src/starboard/nplb/system_get_path_test.cc
+++ b/src/starboard/nplb/system_get_path_test.cc
@@ -75,6 +75,10 @@
   BasicTest(kSbSystemPathSourceDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathTempDirectory, false, false, __LINE__);
   BasicTest(kSbSystemPathTestOutputDirectory, false, false, __LINE__);
+#if SB_API_VERSION >= 4
+  BasicTest(kSbSystemPathFontDirectory, false, false, __LINE__);
+  BasicTest(kSbSystemPathFontConfigurationDirectory, false, false, __LINE__);
+#endif  // SB_API_VERSION >= 4
 }
 
 }  // namespace
diff --git a/src/starboard/player.h b/src/starboard/player.h
index 69074a1..de12f42 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -79,13 +79,13 @@
   kSbPlayerStateError,
 } SbPlayerState;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 typedef enum SbPlayerOutputMode {
   // Requests for SbPlayer to produce an OpenGL texture that the client must
   // draw every frame with its graphics rendering. It may be that we get a
   // texture handle, but cannot perform operations like glReadPixels on it if it
   // is DRM-protected, or it may not support DRM-protected content at all.  When
-  // this output mode is provided to SbCreatePlayer(), the application will be
+  // this output mode is provided to SbPlayerCreate(), the application will be
   // able to pull frames via calls to SbPlayerGetCurrentFrame().
   kSbPlayerOutputModeDecodeToTexture,
 
@@ -100,7 +100,7 @@
   // An invalid output mode.
   kSbPlayerOutputModeInvalid,
 } SbPlayerOutputMode;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 // Information about the current media playback state.
 typedef struct SbPlayerInfo {
@@ -138,7 +138,7 @@
   // the player.
   int corrupted_video_frames;
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   // The rate of playback.  The video is played back in a speed that is
   // proportional to this.  By default it is 1.0 which indicates that the
   // playback is at normal speed.  When it is greater than one, the video is
@@ -146,7 +146,7 @@
   // is played in a slower than normal speed.  Negative speeds are not
   // supported.
   double playback_rate;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 } SbPlayerInfo;
 
 // An opaque handle to an implementation-private structure representing a
@@ -278,9 +278,10 @@
 //
 // |provider|: Only present in Starboard version 3 and up.  If not |NULL|,
 //   then when output_mode == kSbPlayerOutputModeDecodeToTexture, the player MAY
-//   use the provider to create SbDecodeTargets. A provider could also
-//   potentially be required by the player, in which case, if the provider is
-//   not given, the player will fail by returning kSbPlayerInvalid.
+//   use the provider to create SbDecodeTargets on the renderer thread. A
+//   provider may not always be needed by the player, but if it is needed, and
+//   the provider is not given, the player will fail by returning
+//   kSbPlayerInvalid.
 SB_EXPORT SbPlayer SbPlayerCreate(
     SbWindow window,
     SbMediaVideoCodec video_codec,
@@ -292,24 +293,24 @@
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
     void* context
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
     ,
-    SbPlayerOutputMode output_mode
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+    SbPlayerOutputMode output_mode,
+    SbDecodeTargetGraphicsContextProvider* context_provider
+#elif SB_API_VERSION >= 3
     ,
     SbDecodeTargetProvider* provider
-#endif  // SB_API_VERSION >= 3
+#endif
     );  // NOLINT
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 // Returns true if the given player output mode is supported by the platform.
 // If this function returns true, it is okay to call SbPlayerCreate() with
 // the given |output_mode|.
 SB_EXPORT bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode,
                                            SbMediaVideoCodec codec,
                                            SbDrmSystem drm_system);
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
 
 // Destroys |player|, freeing all associated resources. Each callback must
 // receive one more callback to say that the player was destroyed. Callbacks
@@ -352,6 +353,54 @@
                             SbMediaTime seek_to_pts,
                             int ticket);
 
+#if SB_API_VERSION >= 4
+
+// Writes a single sample of the given media type to |player|'s input stream.
+// Its data may be passed in via more than one buffers.  The lifetime of
+// |sample_buffers|, |sample_buffer_sizes|, |video_sample_info|, and
+// |sample_drm_info| (as well as member |subsample_mapping| contained inside it)
+// are not guaranteed past the call to SbPlayerWriteSample. That means that
+// before returning, the implementation must synchronously copy any information
+// it wants to retain from those structures.
+//
+// |player|: The player to which the sample is written.
+// |sample_type|: The type of sample being written. See the |SbMediaType|
+//   enum in media.h.
+// |sample_buffers|: A pointer to an array of buffers with
+//   |number_of_sample_buffers| elements that hold the data for this sample. The
+//   buffers are expected to be a portion of a bytestream of the codec type that
+//   the player was created with. The buffers should contain a sequence of whole
+//   NAL Units for video, or a complete audio frame.  |sample_buffers| cannot be
+//   assumed to live past the call into SbPlayerWriteSample(), so it must be
+//   copied if its content will be used after SbPlayerWriteSample() returns.
+// |sample_buffer_sizes|: A pointer to an array of sizes with
+//   |number_of_sample_buffers| elements.  Each of them specify the number of
+//   bytes in the corresponding buffer contained in |sample_buffers|.  None of
+//   them can be 0.  |sample_buffer_sizes| cannot be assumed to live past the
+//   call into SbPlayerWriteSample(), so it must be copied if its content will
+//   be used after SbPlayerWriteSample() returns.
+// |number_of_sample_buffers|: Specify the number of elements contained inside
+//   |sample_buffers| and |sample_buffer_sizes|.  It has to be at least one, or
+//   the call will be ignored.
+// |sample_pts|: The timestamp of the sample in 90KHz ticks (PTS). Note that
+//   samples MAY be written "slightly" out of order.
+// |video_sample_info|: Information about a video sample. This value is
+//   required if |sample_type| is |kSbMediaTypeVideo|. Otherwise, it must be
+//   |NULL|.
+// |sample_drm_info|: The DRM system for the media sample. This value is
+//   required for encrypted samples. Otherwise, it must be |NULL|.
+SB_EXPORT void SbPlayerWriteSample(
+    SbPlayer player,
+    SbMediaType sample_type,
+    const void** sample_buffers,
+    int* sample_buffer_sizes,
+    int number_of_sample_buffers,
+    SbMediaTime sample_pts,
+    const SbMediaVideoSampleInfo* video_sample_info,
+    const SbDrmSampleInfo* sample_drm_info);
+
+#else  // SB_API_VERSION >= 4
+
 // Writes a sample of the given media type to |player|'s input stream. The
 // lifetime of |video_sample_info| and |sample_drm_info| (as well as member
 // |subsample_mapping| contained inside it) are not guaranteed past the call
@@ -383,6 +432,8 @@
     const SbMediaVideoSampleInfo* video_sample_info,
     const SbDrmSampleInfo* sample_drm_info);
 
+#endif  // SB_API_VERSION >= 4
+
 // Writes a marker to |player|'s input stream of |stream_type| indicating that
 // there are no more samples for that media type for the remainder of this
 // media stream. This marker is invalidated, along with the rest of the stream's
@@ -393,8 +444,7 @@
 SB_EXPORT void SbPlayerWriteEndOfStream(SbPlayer player,
                                         SbMediaType stream_type);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 // Sets the player bounds to the given graphics plane coordinates. The changes
 // do not take effect until the next graphics frame buffer swap. The default
 // bounds for a player is the full screen.  This function is only relevant when
@@ -408,26 +458,31 @@
 // with such frequent calls.
 //
 // |player|: The player that is being resized.
+// |z_index|: The z-index of the player.  When the bounds of multiple players
+//            are overlapped, the one with larger z-index will be rendered on
+//            top of the ones with smaller z-index.
 // |x|: The x-coordinate of the upper-left corner of the player.
 // |y|: The y-coordinate of the upper-left corner of the player.
 // |width|: The width of the player, in pixels.
 // |height|: The height of the player, in pixels.
 SB_EXPORT void SbPlayerSetBounds(SbPlayer player,
+#if SB_API_VERSION >= 4
+                                 int z_index,
+#endif  // SB_API_VERSION >= 4
                                  int x,
                                  int y,
                                  int width,
                                  int height);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-           SB_IS(PLAYER_PUNCHED_OUT)
+#endif  // SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
 
 // Pauses or unpauses the |player|. If the |player|'s state is
 // |kPlayerStatePrerolling|, this function sets the initial pause state for
 // the current seek target.
 SB_EXPORT void SbPlayerSetPause(SbPlayer player, bool pause);
 
-#else  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else  // SB_API_VERSION < 4
 
 // Set the playback rate of the |player|.  |rate| is default to 1.0 which
 // indicates the playback is at its original speed.  A |rate| greater than one
@@ -441,7 +496,7 @@
 // |playback_rate| is negative or if it is too high to support.
 SB_EXPORT bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate);
 
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
 
 // Sets the player's volume.
 //
@@ -459,7 +514,7 @@
 // |out_player_info|: The information retrieved for the player.
 SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info);
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 #if SB_IS(PLAYER_COMPOSITED)
 // Gets a handle that represents the player's video output, for the purpose of
 // composing with |SbCompositor|, which is currently undefined.
@@ -468,9 +523,9 @@
 SB_EXPORT SbPlayerCompositionHandle
 SbPlayerGetCompositionHandle(SbPlayer player);
 #endif  // SB_IS(PLAYER_COMPOSITED)
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 #if SB_IS(PLAYER_PRODUCING_TEXTURE)
 // Gets an OpenGL texture ID that points to the player's video output frame at
 // the time it was called. This function can be called once, and the texture ID
@@ -479,9 +534,9 @@
 // |player|: The player for which the texture ID is retrieved.
 SB_EXPORT uint32_t SbPlayerGetTextureId(SbPlayer player);
 #endif  // SB_IS(PLAYER_PRODUCING_TEXTURE)
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 // Given a player created with the kSbPlayerOutputModeDecodeToTexture
 // output mode, it will return a SbDecodeTarget representing the current frame
 // to be rasterized.  On GLES systems, this function must be called on a
@@ -490,7 +545,7 @@
 // |player| object that was created with an output mode other than
 // kSbPlayerOutputModeDecodeToTexture, kSbDecodeTargetInvalid is returned.
 SB_EXPORT SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer player);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/starboard/raspi/1/gyp_configuration.gypi b/src/starboard/raspi/1/gyp_configuration.gypi
index 2aa6431..f76edaf 100644
--- a/src/starboard/raspi/1/gyp_configuration.gypi
+++ b/src/starboard/raspi/1/gyp_configuration.gypi
@@ -14,24 +14,6 @@
 
 {
   'variables': {
-    'target_arch': 'arm',
-    'target_os': 'linux',
-
-    'in_app_dial%': 0,
-    'sysroot%': '/',
-    'gl_type': 'system_gles2',
-
-    # VideoCore's tiled renderer will do a render for every tile of a render
-    # target even if only part of that target was rendered to.  Since the
-    # scratch surface cache is designed to choose large offscreen surfaces so
-    # that they can be maximally reused, it is not a very good fit for a tiled
-    # renderer.
-    'scratch_surface_cache_size_in_bytes' : 0,
-
-    # This should have a default value in cobalt/base.gypi. See the comment
-    # there for acceptable values for this variable.
-    'javascript_engine': 'mozjs',
-    'cobalt_enable_jit': 0,
     'cobalt_minimum_frame_time_in_milliseconds': '33',
 
     # RasPi 1 is ARMv6
@@ -39,134 +21,16 @@
     'armv7': 0,
     'arm_neon': 0,
 
-    # Define platform specific compiler and linker flags.
-    # Refer to base.gypi for a list of all available variables.
-    'compiler_flags_host': [
-      '-O2',
-    ],
     'compiler_flags': [
-      # We'll pretend not to be Linux, but Starboard instead.
-      '-U__linux__',
-
-      # Force char to be signed.
-      '-fsigned-char',
-
-      # Suppress some warnings that will be hard to fix.
-      '-Wno-unused-local-typedefs',
-      '-Wno-unused-result',
-      '-Wno-deprecated-declarations',
-      '-Wno-missing-field-initializers',
-      '-Wno-comment',  # Talk to my lawyer.
-      '-Wno-narrowing',
-      '-Wno-unknown-pragmas',
-      '-Wno-type-limits',  # TODO: We should actually look into these.
-
-      # Specify the sysroot with all your include dependencies.
-      '--sysroot=<(sysroot)',
-
       # Optimize for Raspberry Pi 1 chips.
       '-march=armv6zk',
       '-mcpu=arm1176jzf-s',
       '-mfloat-abi=hard',
       '-mfpu=vfp',
-
-      # This is a quirk of Raspbian, these are required to include any
-      # GL-related headers.
-      '-I<(sysroot)/opt/vc/include',
-      '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
-      '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
-    ],
-    'linker_flags': [
-      '--sysroot=<(sysroot)',
-      # This is a quirk of Raspbian, these are required to link any GL-related
-      # libraries.
-      '-L<(sysroot)/opt/vc/lib',
-      '-Wl,-rpath=<(sysroot)/opt/vc/lib',
-
-      # We don't wrap these symbols, but this ensures that they aren't
-      # linked in.
-      '-Wl,--wrap=malloc',
-      '-Wl,--wrap=calloc',
-      '-Wl,--wrap=realloc',
-      '-Wl,--wrap=memalign',
-      '-Wl,--wrap=reallocalign',
-      '-Wl,--wrap=free',
-      '-Wl,--wrap=strdup',
-      '-Wl,--wrap=malloc_usable_size',
-      '-Wl,--wrap=malloc_stats_fast',
-      '-Wl,--wrap=__cxa_demangle',
-    ],
-    'compiler_flags_debug': [
-      '-O0',
-    ],
-    'compiler_flags_cc_debug': [
-      '-frtti',
-    ],
-    'compiler_flags_devel': [
-      '-O2',
-    ],
-    'compiler_flags_cc_devel': [
-      '-frtti',
-    ],
-    'compiler_flags_qa': [
-      '-O2',
-    ],
-    'compiler_flags_cc_qa': [
-      '-fno-rtti',
-    ],
-    'compiler_flags_gold': [
-      '-O2',
-    ],
-    'compiler_flags_cc_gold': [
-      '-fno-rtti',
-    ],
-    'platform_libraries': [
-      '-lasound',
-      '-lavcodec',
-      '-lavformat',
-      '-lavresample',
-      '-lavutil',
-      '-lEGL',
-      '-lGLESv2',
-      '-lpthread',
-      '-lpulse',
-      '-lrt',
-      '-lopenmaxil',
-      '-lbcm_host',
-      '-lvcos',
-      '-lvchiq_arm',
-    ],
-    'conditions': [
-      ['cobalt_fastbuild==0', {
-        'compiler_flags_debug': [
-          '-g',
-        ],
-        'compiler_flags_devel': [
-          '-g',
-        ],
-        'compiler_flags_qa': [
-        ],
-        'compiler_flags_gold': [
-        ],
-      }],
     ],
   },
 
   'target_defaults': {
-    'defines': [
-      # Cobalt on Linux flag
-      'COBALT_LINUX',
-      '__STDC_FORMAT_MACROS', # so that we get PRI*
-      # Enable GNU extensions to get prototypes like ffsl.
-      '_GNU_SOURCE=1',
-    ],
-    'cflags_c': [
-      '-std=c11',
-    ],
-    'cflags_cc': [
-      '-std=gnu++11',
-      '-Wno-literal-suffix',
-    ],
     'default_configuration': 'raspi-1_debug',
     'configurations': {
       'raspi-1_debug': {
@@ -182,21 +46,9 @@
         'inherit_from': ['gold_base'],
       },
     }, # end of configurations
-    'target_conditions': [
-      ['cobalt_code==1', {
-        'cflags': [
-          '-Wall',
-          '-Wextra',
-          '-Wunreachable-code',
-        ],
-      },{
-        'cflags': [
-          # Do not warn about unused function params.
-          '-Wno-unused-parameter',
-          # Do not warn for implicit type conversions that may change a value.
-          '-Wno-conversion',
-        ],
-      }],
-    ],
-  }, # end of target_defaults
+  },
+
+  'includes': [
+    '../shared/gyp_configuration.gypi',
+  ],
 }
diff --git a/src/starboard/raspi/2/architecture.gypi b/src/starboard/raspi/2/architecture.gypi
new file mode 100644
index 0000000..a216f12
--- /dev/null
+++ b/src/starboard/raspi/2/architecture.gypi
@@ -0,0 +1,33 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    # RasPi 2 is ARMv7
+    'arm_version': 7,
+    'armv7': 1,
+    'arm_neon': 0, # Disable neon until it shows measurable
+                   # benefit under cobalt benchmarks.
+    'arm_float_abi': 'hard',
+
+    'compiler_flags': [
+      # Optimize for Raspberry Pi 2 chips.
+      '-march=armv7-a',
+      '-mtune=cortex-a8',
+      '-mfloat-abi=hard',
+      #'-mfpu=neon-vfpv4',  # Disable neon until it shows measurable
+                            # benefit under cobalt benchmarks.
+    ],
+  },
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/raspi/2/directgles/atomic_public.h
similarity index 67%
copy from src/cobalt/base/math.cc
copy to src/starboard/raspi/2/directgles/atomic_public.h
index d335495..d8c1445 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/raspi/2/directgles/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_RASPI_2_DIRECTGLES_ATOMIC_PUBLIC_H_
+#define STARBOARD_RASPI_2_DIRECTGLES_ATOMIC_PUBLIC_H_
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/raspi/2/atomic_public.h"
+
+#endif  // STARBOARD_RASPI_2_DIRECTGLES_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/raspi/2/directgles/configuration_public.h b/src/starboard/raspi/2/directgles/configuration_public.h
new file mode 100644
index 0000000..92b76a8
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/configuration_public.h
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Raspberry PI 2 Raspbian.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_RASPI_2_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_RASPI_2_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/raspi/2/configuration_public.h"
+
+#endif  // STARBOARD_RASPI_2_DIRECTGLES_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/2/directgles/gyp_configuration.gypi b/src/starboard/raspi/2/directgles/gyp_configuration.gypi
new file mode 100644
index 0000000..fbac27e
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/gyp_configuration.gypi
@@ -0,0 +1,64 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    # Use the direct-to-GLES rasterizer.
+    # This rasterizer falls back to the hardware skia rasterizer in certain
+    # situations.
+    # NOTE: This rasterizer requires a 16-bit depth buffer and a full frame
+    # scratch surface (without depth buffer).
+    'rasterizer_type': 'direct-gles',
+
+    # Accommodate the direct-to-GLES rasterizer's additional memory overhead
+    # by reducing the image cache size. This is only an issue when additional
+    # memory is required for video frames, so shrink the image cache size
+    # only during video playback.
+    'image_cache_capacity_multiplier_when_playing_video': '0.5f',
+
+    # This dictates how much GPU memory will be used for the offscreen render
+    # target atlases. One will be used as a cache and the other used as a
+    # working scratch. It is recommended to allot enough memory for two
+    # atlases that are roughly a quarter of the framebuffer. (The render
+    # target atlases use power-of-2 dimenions.)
+    'surface_cache_size_in_bytes': 2 * (1024 * 512 * 4),
+
+    # The rasterizer does not benefit much from rendering only the dirty
+    # region. Disable this option since it costs GPU time.
+    'render_dirty_region_only': 0,
+  },
+
+  'target_defaults': {
+    'default_configuration': 'raspi-2-directgles_debug',
+    'configurations': {
+      'raspi-2-directgles_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'raspi-2-directgles_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'raspi-2-directgles_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'raspi-2-directgles_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '../architecture.gypi',
+    '../../shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/raspi/2/directgles/gyp_configuration.py b/src/starboard/raspi/2/directgles/gyp_configuration.py
new file mode 100644
index 0000000..172b1d2
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/gyp_configuration.py
@@ -0,0 +1,32 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, os.path.join(_SCRIPT_DIR, '../..'))
+
+# pylint: disable=g-import-not-at-top
+from shared.gyp_configuration import RaspiPlatformConfig
+
+
+def CreatePlatformConfig():
+  try:
+    return RaspiPlatformConfig('raspi-2-directgles')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
diff --git a/src/starboard/raspi/2/directgles/starboard_platform.gyp b/src/starboard/raspi/2/directgles/starboard_platform.gyp
new file mode 100644
index 0000000..a304ac6
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/starboard_platform.gyp
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../../shared/starboard_platform.gypi',
+  ],
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/raspi/2/directgles/thread_types_public.h
similarity index 65%
copy from src/cobalt/base/math.cc
copy to src/starboard/raspi/2/directgles/thread_types_public.h
index d335495..8e76ba5 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/raspi/2/directgles/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_RASPI_2_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_RASPI_2_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/raspi/2/thread_types_public.h"
+
+#endif  // STARBOARD_RASPI_2_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/raspi/2/gyp_configuration.gypi b/src/starboard/raspi/2/gyp_configuration.gypi
index ac73a07..dca287b 100644
--- a/src/starboard/raspi/2/gyp_configuration.gypi
+++ b/src/starboard/raspi/2/gyp_configuration.gypi
@@ -13,163 +13,7 @@
 # limitations under the License.
 
 {
-  'variables': {
-    'target_arch': 'arm',
-    'target_os': 'linux',
-
-    'in_app_dial%': 0,
-    'sysroot%': '/',
-    'gl_type': 'system_gles2',
-
-    # VideoCore's tiled renderer will do a render for every tile of a render
-    # target even if only part of that target was rendered to.  Since the
-    # scratch surface cache is designed to choose large offscreen surfaces so
-    # that they can be maximally reused, it is not a very good fit for a tiled
-    # renderer.
-    'scratch_surface_cache_size_in_bytes' : 0,
-
-    # This should have a default value in cobalt/base.gypi. See the comment
-    # there for acceptable values for this variable.
-    'javascript_engine': 'mozjs',
-    'cobalt_enable_jit': 0,
-
-    # RasPi 2 is ARMv7
-    'arm_version': 7,
-    'armv7': 1,
-    'arm_neon': 0, # Disable neon until it shows measurable
-                   # benefit under cobalt benchmarks.
-    'arm_float_abi': 'hard',
-
-    # Define platform specific compiler and linker flags.
-    # Refer to base.gypi for a list of all available variables.
-    'compiler_flags_host': [
-      '-O2',
-    ],
-    'compiler_flags': [
-      # We'll pretend not to be Linux, but Starboard instead.
-      '-U__linux__',
-
-      # Force char to be signed.
-      '-fsigned-char',
-
-      # Suppress some warnings that will be hard to fix.
-      '-Wno-unused-local-typedefs',
-      '-Wno-unused-result',
-      '-Wno-deprecated-declarations',
-      '-Wno-missing-field-initializers',
-      '-Wno-comment',  # Talk to my lawyer.
-      '-Wno-narrowing',
-      '-Wno-unknown-pragmas',
-      '-Wno-type-limits',  # TODO: We should actually look into these.
-
-      # Specify the sysroot with all your include dependencies.
-      '--sysroot=<(sysroot)',
-
-      # Optimize for Raspberry Pi 2 chips.
-      '-march=armv7-a',
-      '-mtune=cortex-a8',
-      '-mfloat-abi=hard',
-      #'-mfpu=neon-vfpv4',  # Disable neon until it shows measurable
-                            # benefit under cobalt benchmarks.
-
-      # This is a quirk of Raspbian, these are required to include any
-      # GL-related headers.
-      '-I<(sysroot)/usr/local/include',
-      '-I<(sysroot)/opt/vc/include',
-      '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
-      '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
-    ],
-    'linker_flags': [
-      '--sysroot=<(sysroot)',
-      # This is a quirk of Raspbian, these are required to link any GL-related
-      # libraries.
-      '-L<(sysroot)/opt/vc/lib',
-      '-Wl,-rpath=<(sysroot)/opt/vc/lib',
-
-      # We don't wrap these symbols, but this ensures that they aren't
-      # linked in.
-      '-Wl,--wrap=malloc',
-      '-Wl,--wrap=calloc',
-      '-Wl,--wrap=realloc',
-      '-Wl,--wrap=memalign',
-      '-Wl,--wrap=reallocalign',
-      '-Wl,--wrap=free',
-      '-Wl,--wrap=strdup',
-      '-Wl,--wrap=malloc_usable_size',
-      '-Wl,--wrap=malloc_stats_fast',
-      '-Wl,--wrap=__cxa_demangle',
-    ],
-    'compiler_flags_debug': [
-      '-O0',
-    ],
-    'compiler_flags_cc_debug': [
-      '-frtti',
-    ],
-    'compiler_flags_devel': [
-      '-O2',
-    ],
-    'compiler_flags_cc_devel': [
-      '-frtti',
-    ],
-    'compiler_flags_qa': [
-      '-O2',
-    ],
-    'compiler_flags_cc_qa': [
-      '-fno-rtti',
-    ],
-    'compiler_flags_gold': [
-      '-O2',
-    ],
-    'compiler_flags_cc_gold': [
-      '-fno-rtti',
-    ],
-    'platform_libraries': [
-      '-lasound',
-      '-lavcodec',
-      '-lavformat',
-      '-lavresample',
-      '-lavutil',
-      '-lEGL',
-      '-lGLESv2',
-      '-lpthread',
-      '-lpulse',
-      '-lrt',
-      '-lopenmaxil',
-      '-lbcm_host',
-      '-lvcos',
-      '-lvchiq_arm',
-    ],
-    'conditions': [
-      ['cobalt_fastbuild==0', {
-        'compiler_flags_debug': [
-          '-g',
-        ],
-        'compiler_flags_devel': [
-          '-g',
-        ],
-        'compiler_flags_qa': [
-        ],
-        'compiler_flags_gold': [
-        ],
-      }],
-    ],
-  },
-
   'target_defaults': {
-    'defines': [
-      # Cobalt on Linux flag
-      'COBALT_LINUX',
-      '__STDC_FORMAT_MACROS', # so that we get PRI*
-      # Enable GNU extensions to get prototypes like ffsl.
-      '_GNU_SOURCE=1',
-    ],
-    'cflags_c': [
-      '-std=c11',
-    ],
-    'cflags_cc': [
-      '-std=gnu++11',
-      '-Wno-literal-suffix',
-    ],
     'default_configuration': 'raspi-2_debug',
     'configurations': {
       'raspi-2_debug': {
@@ -185,21 +29,10 @@
         'inherit_from': ['gold_base'],
       },
     }, # end of configurations
-    'target_conditions': [
-      ['cobalt_code==1', {
-        'cflags': [
-          '-Wall',
-          '-Wextra',
-          '-Wunreachable-code',
-        ],
-      },{
-        'cflags': [
-          # Do not warn about unused function params.
-          '-Wno-unused-parameter',
-          # Do not warn for implicit type conversions that may change a value.
-          '-Wno-conversion',
-        ],
-      }],
-    ],
-  }, # end of target_defaults
+  },
+
+  'includes': [
+    'architecture.gypi',
+    '../shared/gyp_configuration.gypi',
+  ],
 }
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 39bb1c7..f89c9a5 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -18,7 +18,7 @@
 #define STARBOARD_RASPI_SHARED_CONFIGURATION_PUBLIC_H_
 
 // The API version implemented by this platform.
-#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+#define SB_API_VERSION 4
 
 // --- System Header Configuration -------------------------------------------
 
@@ -52,6 +52,9 @@
 // Whether the current platform has microphone supported.
 #define SB_HAS_MICROPHONE 0
 
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
 // Whether the current platform has speech synthesis.
 #define SB_HAS_SPEECH_SYNTHESIS 0
 
@@ -260,7 +263,7 @@
 // supported composition methods below.
 #define SB_HAS_PLAYER 1
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 // Specifies whether this platform's player will produce an OpenGL texture that
 // the client must draw every frame with its graphics rendering. It may be that
 // we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -279,7 +282,9 @@
 // this case, changing the video bounds must be tightly synchronized between the
 // player and the graphics plane.
 #define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
+
+#if SB_API_VERSION < 4
 
 // Specifies the maximum amount of memory used by audio buffers of media source
 // before triggering a garbage collection.  A large value will cause more memory
@@ -308,7 +313,7 @@
 //    macro should be set to a value that is greater than the sum of the above
 //    source buffer stream memory limits with extra room to take account of
 //    fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
+#define SB_MEDIA_MAIN_BUFFER_BUDGET (24U * 1024U * 1024U)
 
 // Specifies how much GPU memory to reserve up-front for media source buffers.
 // This should only be set to non-zero on system with limited CPU memory and
@@ -317,6 +322,18 @@
 // media buffers being decoded when being stored in GPU.
 #define SB_MEDIA_GPU_BUFFER_BUDGET 0U
 
+#endif  // SB_API_VERSION < 4
+
+// The maximum audio bitrate the platform can decode.  The following value
+// equals to 5M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
+
+// The maximum video bitrate the platform can decode.  The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
+
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
new file mode 100644
index 0000000..96dca33
--- /dev/null
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -0,0 +1,180 @@
+# 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': {
+    'target_arch': 'arm',
+    'target_os': 'linux',
+
+    'in_app_dial%': 0,
+    'sysroot%': '/',
+    'gl_type': 'system_gles2',
+
+    # VideoCore's tiled renderer will do a render for every tile of a render
+    # target even if only part of that target was rendered to.  Since the
+    # scratch surface cache is designed to choose large offscreen surfaces so
+    # that they can be maximally reused, it is not a very good fit for a tiled
+    # renderer.
+    'scratch_surface_cache_size_in_bytes' : 0,
+
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 0,
+    'cobalt_media_source_2016': 1,
+
+    # This atlas size works better than the auto-mem setting.
+    'skia_glyph_atlas_width%': '2048',
+    'skia_glyph_atlas_height%': '2048',
+
+    # Define platform specific compiler and linker flags.
+    # Refer to base.gypi for a list of all available variables.
+    'compiler_flags_host': [
+      '-O2',
+    ],
+    'compiler_flags': [
+      # We'll pretend not to be Linux, but Starboard instead.
+      '-U__linux__',
+
+      # Force char to be signed.
+      '-fsigned-char',
+
+      # Suppress some warnings that will be hard to fix.
+      '-Wno-unused-local-typedefs',
+      '-Wno-unused-result',
+      '-Wno-deprecated-declarations',
+      '-Wno-missing-field-initializers',
+      '-Wno-comment',  # Talk to my lawyer.
+      '-Wno-narrowing',
+      '-Wno-unknown-pragmas',
+      '-Wno-type-limits',  # TODO: We should actually look into these.
+
+      # Specify the sysroot with all your include dependencies.
+      '--sysroot=<(sysroot)',
+
+      # This is a quirk of Raspbian, these are required to include any
+      # GL-related headers.
+      '-I<(sysroot)/opt/vc/include',
+      '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
+      '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
+    ],
+    'linker_flags': [
+      '--sysroot=<(sysroot)',
+      # This is a quirk of Raspbian, these are required to link any GL-related
+      # libraries.
+      '-L<(sysroot)/opt/vc/lib',
+      '-Wl,-rpath=<(sysroot)/opt/vc/lib',
+
+      # We don't wrap these symbols, but this ensures that they aren't
+      # linked in.
+      '-Wl,--wrap=malloc',
+      '-Wl,--wrap=calloc',
+      '-Wl,--wrap=realloc',
+      '-Wl,--wrap=memalign',
+      '-Wl,--wrap=reallocalign',
+      '-Wl,--wrap=free',
+      '-Wl,--wrap=strdup',
+      '-Wl,--wrap=malloc_usable_size',
+      '-Wl,--wrap=malloc_stats_fast',
+      '-Wl,--wrap=__cxa_demangle',
+    ],
+    'compiler_flags_debug': [
+      '-O0',
+    ],
+    'compiler_flags_cc_debug': [
+      '-frtti',
+    ],
+    'compiler_flags_devel': [
+      '-O2',
+    ],
+    'compiler_flags_cc_devel': [
+      '-frtti',
+    ],
+    'compiler_flags_qa': [
+      '-O2',
+    ],
+    'compiler_flags_cc_qa': [
+      '-fno-rtti',
+    ],
+    'compiler_flags_gold': [
+      '-O2',
+    ],
+    'compiler_flags_cc_gold': [
+      '-fno-rtti',
+    ],
+    'platform_libraries': [
+      '-lasound',
+      '-lavcodec',
+      '-lavformat',
+      '-lavresample',
+      '-lavutil',
+      '-lEGL',
+      '-lGLESv2',
+      '-lpthread',
+      '-lpulse',
+      '-lrt',
+      '-lopenmaxil',
+      '-lbcm_host',
+      '-lvcos',
+      '-lvchiq_arm',
+    ],
+    'conditions': [
+      ['cobalt_fastbuild==0', {
+        'compiler_flags_debug': [
+          '-g',
+        ],
+        'compiler_flags_devel': [
+          '-g',
+        ],
+        'compiler_flags_qa': [
+        ],
+        'compiler_flags_gold': [
+        ],
+      }],
+    ],
+  },
+
+  'target_defaults': {
+    'defines': [
+      # Cobalt on Linux flag
+      'COBALT_LINUX',
+      '__STDC_FORMAT_MACROS', # so that we get PRI*
+      # Enable GNU extensions to get prototypes like ffsl.
+      '_GNU_SOURCE=1',
+    ],
+    'cflags_c': [
+      '-std=c11',
+    ],
+    'cflags_cc': [
+      '-std=gnu++11',
+      '-Wno-literal-suffix',
+    ],
+    'target_conditions': [
+      ['cobalt_code==1', {
+        'cflags': [
+          '-Wall',
+          '-Wextra',
+          '-Wunreachable-code',
+        ],
+      },{
+        'cflags': [
+          # Do not warn about unused function params.
+          '-Wno-unused-parameter',
+          # Do not warn for implicit type conversions that may change a value.
+          '-Wno-conversion',
+        ],
+      }],
+    ],
+  }, # end of target_defaults
+}
diff --git a/src/starboard/raspi/shared/open_max/decode_target_create.cc b/src/starboard/raspi/shared/open_max/decode_target_create.cc
index 76c34f3..41667c8 100644
--- a/src/starboard/raspi/shared/open_max/decode_target_create.cc
+++ b/src/starboard/raspi/shared/open_max/decode_target_create.cc
@@ -12,23 +12,34 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/decode_target.h"
+#include "starboard/raspi/shared/open_max/decode_target_create.h"
 
 #include <GLES2/gl2.h>
 
+#include "starboard/decode_target.h"
 #include "starboard/log.h"
 #include "starboard/raspi/shared/open_max/decode_target_internal.h"
 #include "starboard/shared/gles/gl_call.h"
 
-SbDecodeTarget SbDecodeTargetCreate(EGLDisplay display,
-                                    EGLContext context,
-                                    SbDecodeTargetFormat format,
-                                    int width,
-                                    int height) {
-  SB_DCHECK(format == kSbDecodeTargetFormat1PlaneRGBA);
-  if (format != kSbDecodeTargetFormat1PlaneRGBA) {
-    return kSbDecodeTargetInvalid;
-  }
+namespace starboard {
+namespace raspi {
+namespace shared {
+namespace open_max {
+
+namespace {
+
+struct CreateParams {
+  int width;
+  int height;
+  SbDecodeTargetFormat format;
+  EGLDisplay egl_display;
+  EGLContext egl_context;
+
+  SbDecodeTarget decode_target_out;
+};
+
+void CreateWithContextRunner(void* context) {
+  CreateParams* params = static_cast<CreateParams*>(context);
 
   GLuint texture;
   GL_CALL(glGenTextures(1, &texture));
@@ -38,32 +49,62 @@
   GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
   GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
 
-  GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
-                       GL_UNSIGNED_BYTE, NULL));
+  GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, params->width, params->height,
+                       0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
 
   SbDecodeTargetPrivate* target = new SbDecodeTargetPrivate;
-  target->display = display;
-  target->info.format = format;
+  target->display = params->egl_display;
+  target->info.format = params->format;
   // Data to be set by the decoder.
   target->info.is_opaque = false;
-  target->info.width = width;
-  target->info.height = height;
+  target->info.width = params->width;
+  target->info.height = params->height;
   target->info.planes[0].texture = texture;
   target->info.planes[0].gl_texture_target = GL_TEXTURE_2D;
-  target->info.planes[0].width = width;
-  target->info.planes[0].height = height;
+  target->info.planes[0].width = params->width;
+  target->info.planes[0].height = params->height;
   target->info.planes[0].content_region.left = 0;
   target->info.planes[0].content_region.top = 0;
-  target->info.planes[0].content_region.right = width;
-  target->info.planes[0].content_region.bottom = height;
+  target->info.planes[0].content_region.right = params->width;
+  target->info.planes[0].content_region.bottom = params->height;
 
-  target->images[0] =
-      eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
-                        (EGLClientBuffer)target->info.planes[0].texture, NULL);
+  target->images[0] = eglCreateImageKHR(
+      params->egl_display, params->egl_context, EGL_GL_TEXTURE_2D_KHR,
+      (EGLClientBuffer)target->info.planes[0].texture, NULL);
   SB_DCHECK(eglGetError() == EGL_SUCCESS);
   SB_DCHECK(target->images[0] != EGL_NO_IMAGE_KHR);
 
   GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
 
-  return target;
+  params->decode_target_out = target;
 }
+}  // namespace
+
+SbDecodeTarget DecodeTargetCreate(
+    SbDecodeTargetGraphicsContextProvider* provider,
+    SbDecodeTargetFormat format,
+    int width,
+    int height) {
+  SB_DCHECK(format == kSbDecodeTargetFormat1PlaneRGBA);
+  if (format != kSbDecodeTargetFormat1PlaneRGBA) {
+    return kSbDecodeTargetInvalid;
+  }
+
+  CreateParams params;
+  params.width = width;
+  params.height = height;
+  params.format = format;
+  params.egl_display = provider->egl_display;
+  params.egl_context = provider->egl_context;
+  params.decode_target_out = kSbDecodeTargetInvalid;
+
+  SbDecodeTargetRunInGlesContext(
+      provider, &CreateWithContextRunner, static_cast<void*>(&params));
+
+  return params.decode_target_out;
+}
+
+}  // namespace open_max
+}  // namespace shared
+}  // namespace raspi
+}  // namespace starboard
diff --git a/src/starboard/raspi/shared/open_max/decode_target_create.h b/src/starboard/raspi/shared/open_max/decode_target_create.h
new file mode 100644
index 0000000..ef582b3
--- /dev/null
+++ b/src/starboard/raspi/shared/open_max/decode_target_create.h
@@ -0,0 +1,36 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_RASPI_SHARED_OPEN_MAX_DECODE_TARGET_CREATE_H_
+#define STARBOARD_RASPI_SHARED_OPEN_MAX_DECODE_TARGET_CREATE_H_
+
+#include "starboard/decode_target.h"
+
+namespace starboard {
+namespace raspi {
+namespace shared {
+namespace open_max {
+
+SbDecodeTarget DecodeTargetCreate(
+    SbDecodeTargetGraphicsContextProvider* provider,
+    SbDecodeTargetFormat format,
+    int width,
+    int height);
+
+}  // namespace open_max
+}  // namespace shared
+}  // namespace raspi
+}  // namespace starboard
+
+#endif  // STARBOARD_RASPI_SHARED_OPEN_MAX_DECODE_TARGET_CREATE_H_
diff --git a/src/starboard/raspi/shared/open_max/image_decode.cc b/src/starboard/raspi/shared/open_max/image_decode.cc
index 30b551a..43948dd 100644
--- a/src/starboard/raspi/shared/open_max/image_decode.cc
+++ b/src/starboard/raspi/shared/open_max/image_decode.cc
@@ -23,7 +23,7 @@
 starboard::Mutex decode_lock_;
 }
 
-SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider,
+SbDecodeTarget SbImageDecode(SbDecodeTargetGraphicsContextProvider* provider,
                              void* data,
                              int data_size,
                              const char* mime_type,
diff --git a/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc b/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
index d9a98ff..6d559ce 100644
--- a/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
+++ b/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
@@ -20,8 +20,17 @@
 
 bool SbImageIsDecodeSupported(const char* mime_type,
                               SbDecodeTargetFormat format) {
+  // Unfortunately, while openmax image decoding is implemented and supported,
+  // there is a very sporadic (i.e. around every 1 in 200 image decodes) crash
+  // bug that will go off.  This may be due to a threading issue somewhere,
+  // but it is not clear right now.  Temporarily, we report that we do not
+  // support Starboard image decoding.
+#if 1
+  return false;
+#else
   bool type_supported =
       OMX_IMAGE_CodingMax !=
       open_max::OpenMaxImageDecodeComponent::GetCompressionFormat(mime_type);
   return type_supported && format == kSbDecodeTargetFormat1PlaneRGBA;
+#endif
 }
diff --git a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
index f2dfe44..a915af2 100644
--- a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
+++ b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
@@ -18,6 +18,7 @@
 
 #include "starboard/configuration.h"
 #include "starboard/memory.h"
+#include "starboard/raspi/shared/open_max/decode_target_create.h"
 #include "starboard/raspi/shared/open_max/decode_target_internal.h"
 #include "starboard/thread.h"
 
@@ -46,7 +47,7 @@
 OpenMaxImageDecodeComponent::OpenMaxImageDecodeComponent()
     : OpenMaxComponent("OMX.broadcom.image_decode"),
       state_(kStateIdle),
-      target_provider_(NULL),
+      graphics_context_provider_(NULL),
       target_(NULL),
       input_format_(OMX_IMAGE_CodingMax) {}
 
@@ -62,14 +63,14 @@
 }
 
 SbDecodeTarget OpenMaxImageDecodeComponent::Decode(
-    SbDecodeTargetProvider* provider,
+    SbDecodeTargetGraphicsContextProvider* provider,
     const char* mime_type,
     SbDecodeTargetFormat output_format,
     const void* data,
     int data_size) {
   SB_DCHECK(target_ == NULL);
 
-  target_provider_ = provider;
+  graphics_context_provider_ = provider;
   SetInputFormat(mime_type, output_format);
 
   // Start the component and enable the input port.
@@ -117,8 +118,8 @@
 void OpenMaxImageDecodeComponent::SetOutputFormat(OMX_COLOR_FORMATTYPE format,
                                                   int width,
                                                   int height) {
-  target_ = SbDecodeTargetAcquireFromProvider(
-      target_provider_, kSbDecodeTargetFormat1PlaneRGBA, width, height);
+  target_ = DecodeTargetCreate(graphics_context_provider_,
+                               kSbDecodeTargetFormat1PlaneRGBA, width, height);
   target_->info.is_opaque =
       (input_format_ == OMX_IMAGE_CodingPNG) ? false : true;
   render_component_.SetOutputImage(target_->images[0]);
diff --git a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
index a12458b..c99f02b 100644
--- a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
+++ b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
@@ -35,7 +35,7 @@
   static OMX_IMAGE_CODINGTYPE GetCompressionFormat(const char* mime_type);
 
   // Decode the given data to a decode target.
-  SbDecodeTarget Decode(SbDecodeTargetProvider* provider,
+  SbDecodeTarget Decode(SbDecodeTargetGraphicsContextProvider* provider,
                         const char* mime_type,
                         SbDecodeTargetFormat output_format,
                         const void* data,
@@ -59,7 +59,7 @@
 
   OpenMaxEglRenderComponent render_component_;
   State state_;
-  SbDecodeTargetProvider* target_provider_;
+  SbDecodeTargetGraphicsContextProvider* graphics_context_provider_;
   SbDecodeTargetPrivate* target_;
   OMX_IMAGE_CODINGTYPE input_format_;
 };
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.h b/src/starboard/raspi/shared/open_max/video_decoder.h
index 454f33c..fa950fe 100644
--- a/src/starboard/raspi/shared/open_max/video_decoder.h
+++ b/src/starboard/raspi/shared/open_max/video_decoder.h
@@ -35,7 +35,7 @@
 namespace open_max {
 
 class VideoDecoder
-    : public starboard::shared::starboard::player::filter::VideoDecoder {
+    : public starboard::shared::starboard::player::filter::HostedVideoDecoder {
  public:
   typedef starboard::shared::starboard::player::InputBuffer InputBuffer;
   typedef starboard::shared::starboard::player::VideoFrame VideoFrame;
diff --git a/src/starboard/raspi/shared/player_components_impl.cc b/src/starboard/raspi/shared/player_components_impl.cc
index bd3a737..5644922 100644
--- a/src/starboard/raspi/shared/player_components_impl.cc
+++ b/src/starboard/raspi/shared/player_components_impl.cc
@@ -47,8 +47,8 @@
                             scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
                             audio_parameters.audio_header);
 
-  VideoRendererImpl* video_renderer =
-      new VideoRendererImpl(scoped_ptr<VideoDecoder>(video_decoder).Pass());
+  VideoRendererImpl* video_renderer = new VideoRendererImpl(
+      scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
 
   return scoped_ptr<PlayerComponents>(
       new PlayerComponents(audio_renderer, video_renderer));
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index ffb22bf..65951fd 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -37,6 +37,7 @@
         '<(DEPTH)/starboard/raspi/shared/dispmanx_util.h',
         '<(DEPTH)/starboard/raspi/shared/main.cc',
         '<(DEPTH)/starboard/raspi/shared/open_max/decode_target_create.cc',
+        '<(DEPTH)/starboard/raspi/shared/open_max/decode_target_create.h',
         '<(DEPTH)/starboard/raspi/shared/open_max/decode_target_get_info.cc',
         '<(DEPTH)/starboard/raspi/shared/open_max/decode_target_internal.h',
         '<(DEPTH)/starboard/raspi/shared/open_max/decode_target_release.cc',
@@ -130,6 +131,7 @@
         '<(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_interface_address.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',
@@ -256,9 +258,17 @@
         '<(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/codec_util.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
         '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
+        '<(DEPTH)/starboard/shared/starboard/media/media_util.h',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
         '<(DEPTH)/starboard/shared/starboard/new.cc',
@@ -304,18 +314,25 @@
         '<(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_pause.cc',
         '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
         '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_transform.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_get_audio_configuration.cc',
-        '<(DEPTH)/starboard/shared/stub/media_get_audio_output_count.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',
diff --git a/src/starboard/raspi/shared/thread_create_priority.cc b/src/starboard/raspi/shared/thread_create_priority.cc
index b36cd1f..451c85d 100644
--- a/src/starboard/raspi/shared/thread_create_priority.cc
+++ b/src/starboard/raspi/shared/thread_create_priority.cc
@@ -15,6 +15,7 @@
 #include "starboard/shared/pthread/thread_create_priority.h"
 
 #include <sched.h>
+#include <sys/resource.h>
 
 #include "starboard/log.h"
 
@@ -23,47 +24,89 @@
 namespace pthread {
 
 #if SB_HAS(THREAD_PRIORITY_SUPPORT)
-void ThreadSetPriority(SbThreadPriority priority) {
-  // Note that use of sched_setscheduler() has been found to be more reliably
-  // supported than pthread_setschedparam(), so we are using that.
+// Note that use of sched_setscheduler() has been found to be more reliably
+// supported than pthread_setschedparam(), so we are using that.
 
+void SetIdleScheduler() {
+  struct sched_param thread_sched_param;
+  thread_sched_param.sched_priority = 0;
+  int result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param);
+  if (result != 0) {
+    SB_NOTREACHED();
+  }
+}
+
+void SetOtherScheduler() {
+  struct sched_param thread_sched_param;
+  thread_sched_param.sched_priority = 0;
+  int result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
+  if (result != 0) {
+    SB_NOTREACHED();
+  }
+}
+
+// Here |priority| is a number between >= 0, where the higher the number, the
+// higher the priority.  The actual real time priority setting will be:
+//
+//     std::min(sched_get_priority_min(SCHED_RR) + priority,
+//              sched_get_priority_max(SCHED_RR))
+//
+// If the desired priority is not supported on this platform, we fall back to
+// SCHED_OTHER.
+void SetRoundRobinScheduler(int priority) {
+  // First determine what the system has setup for the min/max priorities.
+  int min_priority = sched_get_priority_min(SCHED_RR);
+  int max_priority = sched_get_priority_max(SCHED_RR);
+
+  struct rlimit rlimit_rtprio;
+  getrlimit(RLIMIT_RTPRIO, &rlimit_rtprio);
+
+  if (rlimit_rtprio.rlim_cur < min_priority) {
+    SB_LOG(WARNING) << "Unable to set real time round-robin thread priority "
+                    << "because `ulimit -r` is too low ("
+                    << rlimit_rtprio.rlim_cur << " < " << min_priority << ").";
+
+    // Fallback to SCHED_OTHER.
+    SetOtherScheduler();
+  } else {
+    struct sched_param thread_sched_param;
+    thread_sched_param.sched_priority =
+        std::min(min_priority + priority, max_priority);
+    int result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+    if (result != 0) {
+      SB_NOTREACHED();
+    }
+  }
+}
+
+void ThreadSetPriority(SbThreadPriority priority) {
   // Use different schedulers according to priority. This is preferred over
   // using SCHED_RR for all threads because the scheduler time slice is too
   // high (defaults to 100ms) for the desired threading behavior.
-  struct sched_param thread_sched_param;
-  int result = 0;
-
-  thread_sched_param.sched_priority = 0;
   switch (priority) {
     case kSbThreadPriorityLowest:
     case kSbThreadPriorityLow:
-      result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param);
+      SetIdleScheduler();
       break;
     case kSbThreadNoPriority:
     case kSbThreadPriorityNormal:
-      result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
+      SetOtherScheduler();
       break;
     case kSbThreadPriorityHigh:
-      thread_sched_param.sched_priority = 1;
-      result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+      SetRoundRobinScheduler(0);
       break;
     case kSbThreadPriorityHighest:
-      thread_sched_param.sched_priority = 2;
-      result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+      SetRoundRobinScheduler(1);
       break;
     case kSbThreadPriorityRealTime:
-      thread_sched_param.sched_priority = 3;
-      result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+      SetRoundRobinScheduler(2);
       break;
     default:
       SB_NOTREACHED();
       break;
   }
-
-  if (result != 0) {
-    SB_NOTREACHED();
-  }
 }
+
 #endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
 
 }  // namespace pthread
diff --git a/src/starboard/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
index 62306c0..25688a5 100644
--- a/src/starboard/shared/alsa/alsa_audio_sink_type.cc
+++ b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
@@ -92,12 +92,12 @@
 
   bool IsType(Type* type) SB_OVERRIDE { return type_ == type; }
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate) SB_OVERRIDE {
     ScopedLock lock(mutex_);
     playback_rate_ = playback_rate;
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
   bool is_valid() { return playback_handle_ != NULL; }
 
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
index f0c4d34..8951eaf 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -64,16 +64,29 @@
   av_free(context);
 }
 
+AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) {
+  switch (audio_codec) {
+    case kSbMediaAudioCodecAac:
+      return AV_CODEC_ID_AAC;
+    case kSbMediaAudioCodecOpus:
+      return AV_CODEC_ID_OPUS;
+    default:
+      return AV_CODEC_ID_NONE;
+  }
+}
+
 }  // namespace
 
 AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
                            const SbMediaAudioHeader& audio_header)
-    : sample_type_(GetSupportedSampleType()),
+    : audio_codec_(audio_codec),
+      sample_type_(GetSupportedSampleType()),
       codec_context_(NULL),
       av_frame_(NULL),
       stream_ended_(false),
       audio_header_(audio_header) {
-  SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
+  SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE)
+      << "Unsupported audio codec " << audio_codec;
 
   InitializeCodec();
 }
@@ -118,7 +131,7 @@
         codec_context_->channels * av_frame_->nb_samples *
             (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
     if (codec_context_->sample_fmt == codec_context_->request_sample_fmt) {
-      SbMemoryCopy(decoded_audio->buffer(), av_frame_->extended_data,
+      SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data,
                    decoded_audio->size());
     } else {
       ConvertSamples(codec_context_->sample_fmt,
@@ -177,7 +190,7 @@
   }
 
   codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
-  codec_context_->codec_id = AV_CODEC_ID_AAC;
+  codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_);
   // Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
   if (sample_type_ == kSbMediaAudioSampleTypeInt16) {
     codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
@@ -199,7 +212,7 @@
     return;
   }
 
-  int rv = avcodec_open2(codec_context_, codec, NULL);
+  int rv = OpenCodec(codec_context_, codec);
   if (rv < 0) {
     SB_LOG(ERROR) << "Unable to open codec";
     TeardownCodec();
@@ -215,7 +228,7 @@
 
 void AudioDecoder::TeardownCodec() {
   if (codec_context_) {
-    avcodec_close(codec_context_);
+    CloseCodec(codec_context_);
     av_free(codec_context_);
     codec_context_ = NULL;
   }
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index 4d6ba4c..a74b0b3 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -49,6 +49,7 @@
   void InitializeCodec();
   void TeardownCodec();
 
+  SbMediaAudioCodec audio_codec_;
   SbMediaAudioSampleType sample_type_;
   AVCodecContext* codec_context_;
   AVFrame* av_frame_;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/shared/ffmpeg/ffmpeg_common.cc
index b19a10c..b3fd3b6 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.cc
@@ -15,6 +15,7 @@
 #include "starboard/shared/ffmpeg/ffmpeg_common.h"
 
 #include "starboard/log.h"
+#include "starboard/mutex.h"
 #include "starboard/once.h"
 
 namespace starboard {
@@ -24,6 +25,7 @@
 namespace {
 
 SbOnceControl ffmpeg_initialization_once = SB_ONCE_INITIALIZER;
+SbMutex codec_mutex = SB_MUTEX_INITIALIZER;
 
 }  // namespace
 
@@ -32,6 +34,19 @@
   SB_DCHECK(initialized);
 }
 
+int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec) {
+  SbMutexAcquire(&codec_mutex);
+  int result = avcodec_open2(codec_context, codec, NULL);
+  SbMutexRelease(&codec_mutex);
+  return result;
+}
+
+void CloseCodec(AVCodecContext* codec_context) {
+  SbMutexAcquire(&codec_mutex);
+  avcodec_close(codec_context);
+  SbMutexRelease(&codec_mutex);
+}
+
 }  // namespace ffmpeg
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.h b/src/starboard/shared/ffmpeg/ffmpeg_common.h
index 4ba3573..4203e91 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -33,6 +33,14 @@
 
 void InitializeFfmpeg();
 
+// In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not
+// synchronized internally so it is the responsibility of its user to ensure
+// that these calls don't overlap.  The following functions acquires a lock
+// internally before calling avcodec_open2() and avcodec_close() to enforce
+// this.
+int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec);
+void CloseCodec(AVCodecContext* codec_context);
+
 }  // namespace ffmpeg
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index e26dc9c..2d4e659 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -265,7 +265,7 @@
     return;
   }
 
-  int rv = avcodec_open2(codec_context_, codec, NULL);
+  int rv = OpenCodec(codec_context_, codec);
   if (rv < 0) {
     SB_LOG(ERROR) << "Unable to open codec";
     TeardownCodec();
@@ -281,7 +281,7 @@
 
 void VideoDecoder::TeardownCodec() {
   if (codec_context_) {
-    avcodec_close(codec_context_);
+    CloseCodec(codec_context_);
     av_free(codec_context_);
     codec_context_ = NULL;
   }
@@ -297,7 +297,7 @@
 namespace player {
 namespace filter {
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 // static
 bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
                                        SbMediaVideoCodec codec,
@@ -307,7 +307,7 @@
 
   return output_mode == kSbPlayerOutputModePunchOut;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace filter
 }  // namespace player
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
index 6fcc2f1..03639ed 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -29,7 +29,7 @@
 namespace shared {
 namespace ffmpeg {
 
-class VideoDecoder : public starboard::player::filter::VideoDecoder {
+class VideoDecoder : public starboard::player::filter::HostedVideoDecoder {
  public:
   typedef starboard::player::InputBuffer InputBuffer;
   typedef starboard::player::VideoFrame VideoFrame;
diff --git a/src/starboard/shared/linux/socket_get_interface_address.cc b/src/starboard/shared/linux/socket_get_interface_address.cc
new file mode 100644
index 0000000..752d898
--- /dev/null
+++ b/src/starboard/shared/linux/socket_get_interface_address.cc
@@ -0,0 +1,382 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#if SB_API_VERSION >= 4
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <linux/if.h>
+#include <linux/if_addr.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "net/base/net_util.h"
+#include "starboard/byte_swap.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/posix/socket_internal.h"
+
+namespace sbposix = starboard::shared::posix;
+
+namespace {
+
+bool IsAnyAddress(const SbSocketAddress& address) {
+  switch (address.type) {
+    case kSbSocketAddressTypeIpv4:
+      return (address.address[0] == 0 && address.address[1] == 0 &&
+              address.address[2] == 0 && address.address[3] == 0);
+#if SB_HAS(IPV6)
+    case kSbSocketAddressTypeIpv6: {
+      bool found_nonzero = false;
+      for (std::size_t i = 0; i != net::kIPv6AddressSize; ++i) {
+        found_nonzero |= (address.address[i] != 0);
+      }
+      return !found_nonzero;
+    }
+#endif
+    default:
+      SB_NOTREACHED() << "Invalid address type " << address.type;
+      break;
+  }
+
+  return false;
+}
+
+template <typename T, int source_size>
+void CopyIntoObjectFromArray(T* out_destination,
+                             const unsigned char(&source)[source_size]) {
+  SB_COMPILE_ASSERT(sizeof(T) <= source_size, destination_is_too_small);
+  SbMemoryCopy(out_destination, source, sizeof(T));
+}
+
+bool GetPotentialMatch(const sockaddr* input_addr,
+                       const in_addr** out_interface_addr) {
+  if (!input_addr || input_addr->sa_family != AF_INET) {
+    *out_interface_addr = NULL;
+    return false;
+  }
+
+  const sockaddr_in* v4input_addr =
+      reinterpret_cast<const sockaddr_in*>(input_addr);
+  *out_interface_addr = &(v4input_addr->sin_addr);
+  return true;
+}
+
+bool GetPotentialMatch(const sockaddr* input_addr,
+                       const in6_addr** out_interface_addr) {
+  if (!input_addr || input_addr->sa_family != AF_INET6) {
+    *out_interface_addr = NULL;
+    return false;
+  }
+
+  const sockaddr_in6* v6input_addr =
+      reinterpret_cast<const sockaddr_in6*>(input_addr);
+  *out_interface_addr = &(v6input_addr->sin6_addr);
+  return true;
+}
+
+template <typename in_addr_type>
+bool GetNetmaskForInterfaceAddress(const SbSocketAddress& interface_address,
+                                   SbSocketAddress* out_netmask) {
+  SB_DCHECK(interface_address.type == kSbSocketAddressTypeIpv4);
+  struct ifaddrs* interface_addrs = NULL;
+
+  int retval = getifaddrs(&interface_addrs);
+  if (retval != 0) {
+    return false;
+  }
+
+  in_addr_type to_match;
+  CopyIntoObjectFromArray(&to_match, interface_address.address);
+
+  bool found_netmask = false;
+  for (struct ifaddrs* interface = interface_addrs; interface != NULL;
+       interface = interface->ifa_next) {
+    if (!(IFF_UP & interface->ifa_flags) ||
+        (IFF_LOOPBACK & interface->ifa_flags)) {
+      continue;
+    }
+
+    const in_addr_type* potential_match;
+    if (!GetPotentialMatch(interface->ifa_addr, &potential_match))
+      continue;
+
+    if (SbMemoryCompare(&to_match, potential_match, sizeof(in_addr_type)) !=
+        0) {
+      continue;
+    }
+
+    sbposix::SockAddr sock_addr;
+    sock_addr.FromSockaddr(interface->ifa_addr);
+    if (sock_addr.ToSbSocketAddress(out_netmask)) {
+      found_netmask = true;
+      break;
+    }
+  }
+
+  freeifaddrs(interface_addrs);
+
+  return found_netmask;
+}
+
+bool GetNetMaskForIPv4InterfaceAddress(const SbSocketAddress& interface_address,
+                                       SbSocketAddress* out_netmask) {
+  return GetNetmaskForInterfaceAddress<in_addr>(interface_address, out_netmask);
+}
+
+#if SB_HAS(IPV6)
+bool GetNetMaskForIPv6InterfaceAddress(const SbSocketAddress& interface_address,
+                                       SbSocketAddress* out_netmask) {
+  return GetNetmaskForInterfaceAddress<in6_addr>(interface_address,
+                                                 out_netmask);
+}
+#endif
+
+bool GetNetMaskForInterfaceAddress(const SbSocketAddress& interface_address,
+                                   SbSocketAddress* out_netmask) {
+  SB_DCHECK(out_netmask);
+
+  switch (interface_address.type) {
+    case kSbSocketAddressTypeIpv4:
+      return GetNetMaskForIPv4InterfaceAddress(interface_address, out_netmask);
+#if SB_HAS(IPV6)
+    case kSbSocketAddressTypeIpv6:
+      return GetNetMaskForIPv6InterfaceAddress(interface_address, out_netmask);
+#endif
+    default:
+      SB_NOTREACHED() << "Invalid address type " << interface_address.type;
+      break;
+  }
+
+  return false;
+}
+
+bool FindIPv4InterfaceIP(SbSocketAddress* out_interface_ip,
+                         SbSocketAddress* out_netmask) {
+  if (out_interface_ip == NULL) {
+    SB_NOTREACHED() << "out_interface_ip must be specified";
+    return false;
+  }
+  struct ifaddrs* interface_addrs = NULL;
+
+  int retval = getifaddrs(&interface_addrs);
+  if (retval != 0) {
+    return false;
+  }
+
+  bool success = false;
+  for (struct ifaddrs* interface = interface_addrs; interface != NULL;
+       interface = interface->ifa_next) {
+    if (!(IFF_UP & interface->ifa_flags) ||
+        (IFF_LOOPBACK & interface->ifa_flags)) {
+      continue;
+    }
+
+    const struct sockaddr* addr = interface->ifa_addr;
+    const struct sockaddr* netmask = interface->ifa_netmask;
+    if (!addr || !netmask || (addr->sa_family != AF_INET)) {
+      // IPv4 addresses only.
+      continue;
+    }
+
+    sbposix::SockAddr sock_addr;
+    sock_addr.FromSockaddr(addr);
+    if (sock_addr.ToSbSocketAddress(out_interface_ip)) {
+      if (out_netmask) {
+        sbposix::SockAddr netmask_addr;
+        netmask_addr.FromSockaddr(netmask);
+        if (!netmask_addr.ToSbSocketAddress(out_netmask)) {
+          continue;
+        }
+      }
+
+      success = true;
+      break;
+    }
+  }
+
+  freeifaddrs(interface_addrs);
+
+  return success;
+}
+
+#if SB_HAS(IPV6)
+bool IsUniqueLocalAddress(const unsigned char ip[16]) {
+  // Unique Local Addresses are in fd08::/8.
+  return ip[0] == 0xfd && ip[1] == 0x08;
+}
+
+bool FindIPv6InterfaceIP(SbSocketAddress* out_interface_ip,
+                         SbSocketAddress* out_netmask) {
+  if (!out_interface_ip) {
+    SB_NOTREACHED() << "out_interface_ip must be specified";
+    return false;
+  }
+  struct ifaddrs* interface_addrs = NULL;
+
+  int retval = getifaddrs(&interface_addrs);
+  if (retval != 0) {
+    return false;
+  }
+
+  int max_scope_interface_value = -1;
+
+  bool ip_found = false;
+  SbSocketAddress temp_interface_ip;
+  SbSocketAddress temp_netmask;
+
+  for (struct ifaddrs* interface = interface_addrs; interface != NULL;
+       interface = interface->ifa_next) {
+    if (!(IFF_UP & interface->ifa_flags) ||
+        (IFF_LOOPBACK & interface->ifa_flags)) {
+      continue;
+    }
+
+    const struct sockaddr* addr = interface->ifa_addr;
+    const struct sockaddr* netmask = interface->ifa_netmask;
+    if (!addr || !netmask || addr->sa_family != AF_INET6) {
+      // IPv6 addresses only.
+      continue;
+    }
+
+    const in6_addr* potential_match;
+    if (!GetPotentialMatch(interface->ifa_addr, &potential_match))
+      continue;
+
+    // Check the IP for loopback again, just in case flags were incorrect.
+    if (IN6_IS_ADDR_LOOPBACK(potential_match) ||
+        IN6_IS_ADDR_LINKLOCAL(potential_match)) {
+      continue;
+    }
+
+    const sockaddr_in6* v6addr =
+        reinterpret_cast<const sockaddr_in6*>(interface->ifa_addr);
+    if (!v6addr) {
+      continue;
+    }
+
+    int current_interface_scope = v6addr->sin6_scope_id;
+
+    if (IsUniqueLocalAddress(v6addr->sin6_addr.s6_addr)) {
+      // ULAs have global scope, but not globally routable.  So prefer
+      // non ULA addresses with global scope by adjusting their "scope"
+      current_interface_scope -= 1;
+    }
+
+    if (current_interface_scope <= max_scope_interface_value) {
+      continue;
+    }
+    max_scope_interface_value = current_interface_scope;
+
+    sbposix::SockAddr sock_addr;
+    sock_addr.FromSockaddr(addr);
+    if (sock_addr.ToSbSocketAddress(&temp_interface_ip)) {
+      if (netmask) {
+        sbposix::SockAddr netmask_addr;
+        netmask_addr.FromSockaddr(netmask);
+        if (!netmask_addr.ToSbSocketAddress(&temp_netmask)) {
+          continue;
+        }
+      }
+
+      ip_found = true;
+    }
+  }
+
+  freeifaddrs(interface_addrs);
+
+  if (!ip_found) {
+    return false;
+  }
+
+  SbMemoryCopy(out_interface_ip, &temp_interface_ip, sizeof(SbSocketAddress));
+  if (out_netmask != NULL) {
+    SbMemoryCopy(out_netmask, &temp_netmask, sizeof(SbSocketAddress));
+  }
+
+  return true;
+}
+#endif
+
+bool FindInterfaceIP(const SbSocketAddressType type,
+                     SbSocketAddress* out_interface_ip,
+                     SbSocketAddress* out_netmask) {
+  switch (type) {
+    case kSbSocketAddressTypeIpv4:
+      return FindIPv4InterfaceIP(out_interface_ip, out_netmask);
+#if SB_HAS(IPV6)
+    case kSbSocketAddressTypeIpv6:
+      return FindIPv6InterfaceIP(out_interface_ip, out_netmask);
+#endif
+    default:
+      SB_NOTREACHED() << "Invalid socket address type " << type;
+  }
+
+  return false;
+}
+
+bool FindSourceAddressForDestination(const SbSocketAddress& destination,
+                                     SbSocketAddress* out_source_address) {
+  SbSocket socket = SbSocketCreate(destination.type, kSbSocketProtocolUdp);
+  if (!SbSocketIsValid(socket)) {
+    return false;
+  }
+
+  SbSocketError connect_retval = SbSocketConnect(socket, &destination);
+  if (connect_retval != kSbSocketOk) {
+    bool socket_destroyed = SbSocketDestroy(socket);
+    SB_DCHECK(socket_destroyed);
+    return false;
+  }
+
+  bool success = SbSocketGetLocalAddress(socket, out_source_address);
+  bool socket_destroyed = SbSocketDestroy(socket);
+  SB_DCHECK(socket_destroyed);
+  return success;
+}
+
+}  // namespace
+
+bool SbSocketGetInterfaceAddress(const SbSocketAddress* const destination,
+                                 SbSocketAddress* out_source_address,
+                                 SbSocketAddress* out_netmask) {
+  if (!out_source_address) {
+    return false;
+  }
+
+  if (destination == NULL) {
+#if SB_HAS(IPV6)
+    // Return either a v4 or a v6 address.  Per spec.
+    return (FindIPv4InterfaceIP(out_source_address, out_netmask) ||
+            FindIPv6InterfaceIP(out_source_address, out_netmask));
+#else
+    return FindIPv4InterfaceIP(out_source_address, out_netmask);
+#endif
+
+  } else if (IsAnyAddress(*destination)) {
+    return FindInterfaceIP(destination->type, out_source_address, out_netmask);
+  } else {
+    return (FindSourceAddressForDestination(*destination, out_source_address) &&
+            GetNetMaskForInterfaceAddress(*out_source_address, out_netmask));
+  }
+
+  return false;
+}
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/linux/socket_get_local_interface_address.cc b/src/starboard/shared/linux/socket_get_local_interface_address.cc
index 1c95514..61ef792 100644
--- a/src/starboard/shared/linux/socket_get_local_interface_address.cc
+++ b/src/starboard/shared/linux/socket_get_local_interface_address.cc
@@ -14,6 +14,8 @@
 
 #include "starboard/socket.h"
 
+#if SB_API_VERSION < 4
+
 #include <arpa/inet.h>
 #include <ifaddrs.h>
 
@@ -67,3 +69,5 @@
   freeifaddrs(ifaddr);
   return false;
 }
+
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/posix/socket_bind.cc b/src/starboard/shared/posix/socket_bind.cc
index 15db625..9cea55f 100644
--- a/src/starboard/shared/posix/socket_bind.cc
+++ b/src/starboard/shared/posix/socket_bind.cc
@@ -18,6 +18,7 @@
 #include <sys/socket.h>
 
 #include "starboard/log.h"
+#include "starboard/memory.h"
 #include "starboard/shared/posix/handle_eintr.h"
 #include "starboard/shared/posix/socket_internal.h"
 
@@ -45,6 +46,20 @@
     return (socket->error = sbposix::TranslateSocketErrno(EAFNOSUPPORT));
   }
 
+#if SB_HAS(IPV6)
+  // When binding to the IPV6 any address, ensure that the IPV6_V6ONLY flag is
+  // off to allow incoming IPV4 connections on the same socket.
+  // See https://www.ietf.org/rfc/rfc3493.txt for details.
+  if (local_address && (local_address->type == kSbSocketAddressTypeIpv6) &&
+      SbMemoryIsZero(local_address->address, 16)) {
+    if (!sbposix::SetBooleanSocketOption(socket, IPPROTO_IPV6, IPV6_V6ONLY,
+                                         "IPV6_V6ONLY", false)) {
+      // Silently ignore errors, assume the default behavior is as expected.
+      socket->error = kSbSocketOk;
+    }
+  }
+#endif
+
   int result = HANDLE_EINTR(
       bind(socket->socket_fd, sock_addr.sockaddr(), sock_addr.length));
   if (result != 0) {
diff --git a/src/starboard/shared/posix/socket_resolve.cc b/src/starboard/shared/posix/socket_resolve.cc
index f36ed0c..dd305d2 100644
--- a/src/starboard/shared/posix/socket_resolve.cc
+++ b/src/starboard/shared/posix/socket_resolve.cc
@@ -57,11 +57,13 @@
 
   // Translate all the sockaddrs.
   sbposix::SockAddr* sock_addrs = new sbposix::SockAddr[address_count];
+  bool* parsed = new bool[address_count];
   int index = 0;
   int skip = 0;
   for (const struct addrinfo *i = ai; i != NULL; i = i->ai_next, ++index) {
     // Skip over any addresses we can't parse.
-    if (!sock_addrs[index].FromSockaddr(i->ai_addr)) {
+    parsed[index] = sock_addrs[index].FromSockaddr(i->ai_addr);
+    if (!parsed[index]) {
       ++skip;
     }
   }
@@ -71,11 +73,13 @@
 
   int result_index = 0;
   for (int i = 0; i < address_count; ++i) {
-    if (sock_addrs[i].ToSbSocketAddress(&result->addresses[result_index])) {
+    if (parsed[i] &&
+        sock_addrs[i].ToSbSocketAddress(&result->addresses[result_index])) {
       ++result_index;
     }
   }
 
+  delete[] parsed;
   delete[] sock_addrs;
   freeaddrinfo(ai);
   return result;
diff --git a/src/starboard/shared/speechd/speech_synthesis_set_language.cc b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
index 6943fb7..fe083f7 100644
--- a/src/starboard/shared/speechd/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
@@ -14,7 +14,7 @@
 
 #include "starboard/speech_synthesis.h"
 
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
 // DEPRECATED IN API VERSION 4
 
 #include "starboard/shared/speechd/speechd_internal.h"
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index c3a12dd..1402d12 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -128,9 +128,7 @@
   CancelTimedEvent(id);
 }
 
-#if SB_HAS(PLAYER) &&                                             \
-    (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-     SB_IS(PLAYER_PUNCHED_OUT))
+#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
 
 void Application::HandleFrame(SbPlayer player,
                               const scoped_refptr<VideoFrame>& frame,
@@ -140,9 +138,7 @@
                               int height) {
   AcceptFrame(player, frame, x, y, width, height);
 }
-#endif  // SB_HAS(PLAYER) && \
-           (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-            SB_IS(PLAYER_PUNCHED_OUT))
+#endif  // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
 
 void Application::SetStartLink(const char* start_link) {
   SbMemoryDeallocate(start_link_);
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 51f5a84..2469a6e 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -207,9 +207,7 @@
   // external thread.
   void Cancel(SbEventId id);
 
-#if SB_HAS(PLAYER) &&                                             \
-    (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-     SB_IS(PLAYER_PUNCHED_OUT))
+#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
   // Handles receiving a new video frame of |player| from the media system. Only
   // used when the application needs to composite video frames with punch-out
   // video manually (should be rare). Will be called from an external thread.
@@ -219,9 +217,7 @@
                    int y,
                    int width,
                    int height);
-#endif  // SB_HAS(PLAYER) && \
-           (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-            SB_IS(PLAYER_PUNCHED_OUT))
+#endif  // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
 
   // Registers a |callback| function that will be called when |Teardown| is
   // called.
@@ -249,9 +245,7 @@
   // processed the Resume event.
   virtual void OnResume() {}
 
-#if SB_HAS(PLAYER) &&                                             \
-    (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-     SB_IS(PLAYER_PUNCHED_OUT))
+#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
   // Subclasses may override this method to accept video frames from the media
   // system. Will be called from an external thread.
   virtual void AcceptFrame(SbPlayer player,
@@ -260,9 +254,7 @@
                            int y,
                            int width,
                            int height) {}
-#endif  // SB_HAS(PLAYER) && \
-           (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-            SB_IS(PLAYER_PUNCHED_OUT))
+#endif  // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
 
   // Blocks until the next event is available. Subclasses must implement this
   // method to provide events for the platform. Gives ownership to the caller.
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
index 10b6b68..cf40ad5 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
@@ -39,9 +39,9 @@
   };
 
   virtual ~SbAudioSinkPrivate() {}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   virtual void SetPlaybackRate(double playback_rate) = 0;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   virtual bool IsType(Type* type) = 0;
 
   // The following two functions will be called during application startup and
diff --git a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
index 2fef267..a34477e 100644
--- a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
+++ b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
@@ -37,11 +37,11 @@
   ~StubAudioSink() SB_OVERRIDE;
 
   bool IsType(Type* type) SB_OVERRIDE { return type_ == type; }
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate) SB_OVERRIDE {
     SB_NOTIMPLEMENTED();
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
   static void* ThreadEntryPoint(void* context);
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
new file mode 100644
index 0000000..63b5a66
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
@@ -0,0 +1,112 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+#include "starboard/string.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_KEY;
+using starboard::shared::starboard::cryptography::AES_gcm128_init;
+using starboard::shared::starboard::cryptography::Algorithm;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+SbCryptographyTransformer SbCryptographyCreateTransformer(
+    const char* algorithm,
+    int block_size_bits,
+    SbCryptographyDirection direction,
+    SbCryptographyBlockCipherMode mode,
+    const void* initialization_vector,
+    int initialization_vector_size,
+    const void* key,
+    int key_size) {
+  if (SbStringCompareAll(algorithm, kSbCryptographyAlgorithmAes) != 0) {
+    SB_DLOG(WARNING) << "Unsupported algorithm: " << algorithm;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  if (block_size_bits != 128) {
+    SB_DLOG(WARNING) << "Unsupported block size: " << block_size_bits;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  // TODO: Support 64-bit IV with CTR mode.
+  if ((mode == kSbCryptographyBlockCipherModeGcm &&
+       initialization_vector_size != 0) ||
+      (mode != kSbCryptographyBlockCipherModeGcm &&
+       initialization_vector_size != block_size_bits / 8)) {
+    SB_DLOG(WARNING) << "Unsupported initialization_vector_size: "
+                     << initialization_vector_size;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  if (key_size != 16 && key_size != 24 && key_size != 32) {
+    SB_DLOG(WARNING) << "Unsupported key_size: " << key_size;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  Algorithm combined_algorithm;
+  if (mode == kSbCryptographyBlockCipherModeCbc) {
+    combined_algorithm = kAlgorithmAes128Cbc;
+  } else if (mode == kSbCryptographyBlockCipherModeCtr) {
+    combined_algorithm = kAlgorithmAes128Ctr;
+  } else if (mode == kSbCryptographyBlockCipherModeGcm) {
+    combined_algorithm = kAlgorithmAes128Gcm;
+  } else {
+    SB_DLOG(WARNING) << "Unsupported block cipher mode: " << mode;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  AES_KEY aeskey = {0};
+  int result = -1;
+  if (direction == kSbCryptographyDirectionDecode &&
+      mode != kSbCryptographyBlockCipherModeCtr &&
+      mode != kSbCryptographyBlockCipherModeGcm) {
+    result = AES_set_decrypt_key(key, key_size * 8, &aeskey);
+  } else {
+    result = AES_set_encrypt_key(key, key_size * 8, &aeskey);
+  }
+
+  if (result != 0) {
+    SB_DLOG(WARNING) << "Error setting key: " << result;
+    return kSbCryptographyInvalidTransformer;
+  }
+
+  SbCryptographyTransformer transformer =
+      new SbCryptographyTransformerPrivate();
+  SbMemorySet(transformer, 0, sizeof(transformer));
+  transformer->key = aeskey;
+  transformer->algorithm = combined_algorithm;
+  transformer->direction = direction;
+  if (initialization_vector_size) {
+    SbMemoryCopy(transformer->ivec, initialization_vector,
+                 initialization_vector_size);
+  }
+
+  if (transformer->algorithm == kAlgorithmAes128Gcm) {
+    AES_gcm128_init(&transformer->gcm_context, &transformer->key,
+                    direction == kSbCryptographyDirectionEncode ? 1 : 0);
+  }
+
+  return transformer;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
new file mode 100644
index 0000000..fede3c3
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+void SbCryptographyDestroyTransformer(SbCryptographyTransformer transformer) {
+  if (!transformer) {
+    return;
+  }
+
+  delete transformer;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc b/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
new file mode 100644
index 0000000..ee09892
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_gcm128_tag;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+bool SbCryptographyGetTag(
+    SbCryptographyTransformer transformer,
+    void* out_tag,
+    int out_tag_size) {
+  if (transformer->algorithm != kAlgorithmAes128Gcm) {
+    SB_DLOG(ERROR) << "Trying to get tag on non-GCM transformer.";
+    return false;
+  }
+
+  AES_gcm128_tag(&transformer->gcm_context, out_tag, out_tag_size);
+  return true;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_internal.h b/src/starboard/shared/starboard/cryptography/cryptography_internal.h
new file mode 100644
index 0000000..526e50a
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_internal.h
@@ -0,0 +1,51 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
+
+#include "starboard/cryptography.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace cryptography {
+
+// The modes supported by this software-only implementation.
+enum Algorithm {
+  kAlgorithmAes128Cbc,
+  kAlgorithmAes128Ctr,
+  kAlgorithmAes128Ecb,
+  kAlgorithmAes128Gcm,
+};
+
+}  // namespace cryptography
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+struct SbCryptographyTransformerPrivate {
+  starboard::shared::starboard::cryptography::Algorithm algorithm;
+  SbCryptographyDirection direction;
+  starboard::shared::starboard::cryptography::GCM128_CONTEXT gcm_context;
+  starboard::shared::starboard::cryptography::AES_KEY key;
+  uint8_t ivec[SB_AES_BLOCK_SIZE];
+  uint8_t ecount_buf[SB_AES_BLOCK_SIZE];
+  uint32_t counter;
+};
+
+#endif  // STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
new file mode 100644
index 0000000..6178bd6
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_gcm128_aad;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+bool SbCryptographySetAuthenticatedData(
+    SbCryptographyTransformer transformer,
+    const void* data,
+    int data_size) {
+  if (transformer->algorithm != kAlgorithmAes128Gcm) {
+    SB_DLOG(ERROR) << "Trying to set AAD on non-GCM transformer.";
+    return false;
+  }
+
+  int result = AES_gcm128_aad(&transformer->gcm_context, data, data_size);
+  return result == 1;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
new file mode 100644
index 0000000..0eb09a9
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_gcm128_setiv;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+void SbCryptographySetInitializationVector(
+    SbCryptographyTransformer transformer,
+    const void* initialization_vector,
+    int initialization_vector_size) {
+  if (transformer->algorithm != kAlgorithmAes128Gcm) {
+    SB_DLOG(ERROR) << "Trying to set initialization vector on non-GCM "
+                   << "transformer.";
+    return;
+  }
+
+  AES_gcm128_setiv(&transformer->gcm_context, &transformer->key,
+                   initialization_vector, initialization_vector_size);
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_transform.cc b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
new file mode 100644
index 0000000..0981a63
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+int SbCryptographyTransform(SbCryptographyTransformer transformer,
+                            const void* in_data,
+                            int in_data_size,
+                            void* out_data) {
+  if (!SbCryptographyIsTransformerValid(transformer) || !in_data || !out_data) {
+    return -1;
+  }
+
+  if (in_data_size == 0) {
+    return 0;
+  }
+
+  if (transformer->algorithm ==
+      starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc) {
+    int enc = transformer->direction == kSbCryptographyDirectionEncode
+                  ? SB_AES_ENCRYPT
+                  : SB_AES_DECRYPT;
+    starboard::shared::starboard::cryptography::AES_cbc_encrypt(
+        in_data, out_data, in_data_size, &(transformer->key), transformer->ivec,
+        enc);
+  } else if (transformer->algorithm ==
+             starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr) {
+    starboard::shared::starboard::cryptography::AES_ctr128_encrypt(
+        in_data, out_data, in_data_size, &(transformer->key), transformer->ivec,
+        transformer->ecount_buf, &transformer->counter);
+  } else if (transformer->algorithm ==
+             starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm) {
+    if (transformer->direction == kSbCryptographyDirectionEncode) {
+      starboard::shared::starboard::cryptography::AES_gcm128_encrypt(
+          &transformer->gcm_context, &transformer->key, in_data, out_data,
+          in_data_size);
+    } else if (transformer->direction == kSbCryptographyDirectionDecode) {
+      starboard::shared::starboard::cryptography::AES_gcm128_decrypt(
+          &transformer->gcm_context, &transformer->key, in_data, out_data,
+          in_data_size);
+    } else {
+      SB_NOTREACHED();
+    }
+  }
+
+  return in_data_size;
+}
diff --git a/src/starboard/shared/starboard/cryptography/software_aes.cc b/src/starboard/shared/starboard/cryptography/software_aes.cc
new file mode 100644
index 0000000..c9a3453
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/software_aes.cc
@@ -0,0 +1,1570 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#include "starboard/byte_swap.h"
+#include "starboard/memory.h"
+#include "starboard/types.h"
+
+#pragma message "software_aes.cc is not meant for production use! Instead, use "
+#pragma message "starboard/shared/stub/cryptography_* if you don't have "
+#pragma message "hardware-accelerated AES support."
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace cryptography {
+
+namespace {
+inline uint32_t GETU32(const void *in) {
+  uint32_t value;
+  SbMemoryCopy(&value, in, sizeof(value));
+  return SbByteSwapU32(value);
+}
+
+inline void PUTU32(void *out, uint32_t value) {
+  value = SbByteSwapU32(value);
+  SbMemoryCopy(out, &value, sizeof(value));
+}
+
+inline uint32_t GETU32_aligned(const void *in) {
+  const char *alias = static_cast<const char*>(in);
+  return SbByteSwapU32(*reinterpret_cast<const uint32_t*>(alias));
+}
+
+inline void PUTU32_aligned(void *in, uint32_t value) {
+  char *alias = static_cast<char*>(in);
+  *reinterpret_cast<uint32_t*>(alias) = SbByteSwapU32(value);
+}
+
+/* Te0[x] = S [x].[02, 01, 01, 03];
+ * Te1[x] = S [x].[03, 02, 01, 01];
+ * Te2[x] = S [x].[01, 03, 02, 01];
+ * Te3[x] = S [x].[01, 01, 03, 02];
+ *
+ * Td0[x] = Si[x].[0e, 09, 0d, 0b];
+ * Td1[x] = Si[x].[0b, 0e, 09, 0d];
+ * Td2[x] = Si[x].[0d, 0b, 0e, 09];
+ * Td3[x] = Si[x].[09, 0d, 0b, 0e];
+ * Td4[x] = Si[x].[01]; */
+static const uint32_t Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU,
+    0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U,
+    0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U,
+    0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU,
+    0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U,
+    0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU,
+    0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U,
+    0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U,
+    0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU,
+    0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU,
+    0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U,
+    0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U,
+    0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U,
+    0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U,
+    0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U,
+    0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU,
+    0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U,
+    0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U,
+    0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U,
+    0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U,
+    0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU,
+    0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U,
+    0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU,
+    0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U,
+    0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU,
+    0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU,
+    0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U,
+    0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U,
+    0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU,
+    0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU,
+    0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU,
+    0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U,
+    0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U,
+    0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U,
+    0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U,
+    0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U,
+    0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU,
+    0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U,
+    0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U,
+    0x2c16163aU,
+};
+static const uint32_t Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U,
+    0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U,
+    0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU,
+    0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU,
+    0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U,
+    0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U,
+    0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U,
+    0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U,
+    0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U,
+    0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U,
+    0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU,
+    0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U,
+    0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U,
+    0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU,
+    0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U,
+    0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U,
+    0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU,
+    0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U,
+    0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U,
+    0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU,
+    0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U,
+    0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U,
+    0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU,
+    0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U,
+    0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U,
+    0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U,
+    0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U,
+    0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU,
+    0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU,
+    0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U,
+    0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU,
+    0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU,
+    0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U,
+    0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U,
+    0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U,
+    0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U,
+    0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU,
+    0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U,
+    0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU,
+    0x3a2c1616U,
+};
+static const uint32_t Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U,
+    0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U,
+    0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU,
+    0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU,
+    0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U,
+    0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U,
+    0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U,
+    0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U,
+    0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U,
+    0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U,
+    0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU,
+    0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U,
+    0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U,
+    0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU,
+    0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U,
+    0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U,
+    0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU,
+    0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U,
+    0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U,
+    0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU,
+    0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U,
+    0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U,
+    0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU,
+    0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U,
+    0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U,
+    0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U,
+    0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U,
+    0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU,
+    0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU,
+    0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U,
+    0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU,
+    0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU,
+    0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U,
+    0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U,
+    0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U,
+    0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U,
+    0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU,
+    0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U,
+    0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU,
+    0x163a2c16U,
+};
+static const uint32_t Te3[256] = {
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU,
+    0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U,
+    0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU,
+    0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U,
+    0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U,
+    0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU,
+    0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U,
+    0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U,
+    0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU,
+    0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U,
+    0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU,
+    0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U,
+    0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U,
+    0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU,
+    0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U,
+    0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU,
+    0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U,
+    0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U,
+    0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U,
+    0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U,
+    0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U,
+    0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U,
+    0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U,
+    0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU,
+    0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U,
+    0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU,
+    0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U,
+    0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U,
+    0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U,
+    0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU,
+    0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U,
+    0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU,
+    0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U,
+    0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U,
+    0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U,
+    0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U,
+    0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U,
+    0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U,
+    0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU,
+    0x16163a2cU,
+};
+static const uint32_t Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU,
+    0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U,
+    0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U,
+    0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U,
+    0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U,
+    0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU,
+    0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U,
+    0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U,
+    0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U,
+    0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU,
+    0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U,
+    0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U,
+    0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U,
+    0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U,
+    0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U,
+    0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU,
+    0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U,
+    0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U,
+    0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU,
+    0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU,
+    0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U,
+    0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU,
+    0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U,
+    0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU,
+    0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU,
+    0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU,
+    0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU,
+    0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U,
+    0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU,
+    0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U,
+    0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU,
+    0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU,
+    0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U,
+    0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU,
+    0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U,
+    0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U,
+    0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U,
+    0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU,
+    0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U,
+    0xd0b85742U,
+};
+static const uint32_t Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU,
+    0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU,
+    0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U,
+    0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU,
+    0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U,
+    0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU,
+    0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U,
+    0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU,
+    0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU,
+    0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU,
+    0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU,
+    0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U,
+    0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU,
+    0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U,
+    0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU,
+    0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU,
+    0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U,
+    0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U,
+    0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU,
+    0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U,
+    0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU,
+    0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U,
+    0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U,
+    0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U,
+    0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U,
+    0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U,
+    0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U,
+    0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U,
+    0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U,
+    0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U,
+    0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U,
+    0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U,
+    0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU,
+    0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U,
+    0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U,
+    0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU,
+    0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U,
+    0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U,
+    0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU,
+    0x42d0b857U,
+};
+static const uint32_t Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU,
+    0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U,
+    0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U,
+    0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU,
+    0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U,
+    0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U,
+    0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U,
+    0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U,
+    0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU,
+    0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU,
+    0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U,
+    0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU,
+    0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U,
+    0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU,
+    0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U,
+    0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU,
+    0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU,
+    0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU,
+    0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U,
+    0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U,
+    0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U,
+    0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU,
+    0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U,
+    0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U,
+    0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U,
+    0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U,
+    0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U,
+    0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U,
+    0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU,
+    0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU,
+    0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U,
+    0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU,
+    0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU,
+    0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U,
+    0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U,
+    0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U,
+    0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU,
+    0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU,
+    0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU,
+    0x5742d0b8U,
+};
+static const uint32_t Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU,
+    0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU,
+    0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U,
+    0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U,
+    0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U,
+    0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U,
+    0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U,
+    0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU,
+    0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U,
+    0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU,
+    0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U,
+    0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U,
+    0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U,
+    0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U,
+    0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U,
+    0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU,
+    0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U,
+    0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U,
+    0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU,
+    0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU,
+    0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U,
+    0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U,
+    0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU,
+    0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U,
+    0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U,
+    0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U,
+    0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU,
+    0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU,
+    0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU,
+    0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU,
+    0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U,
+    0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U,
+    0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U,
+    0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU,
+    0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U,
+    0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U,
+    0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U,
+    0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U,
+    0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U,
+    0xb85742d0U,
+};
+static const uint8_t Td4[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U,
+    0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU,
+    0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, 0x54U,
+    0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU,
+    0x42U, 0xfaU, 0xc3U, 0x4eU, 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U,
+    0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, 0x72U, 0xf8U,
+    0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU,
+    0x65U, 0xb6U, 0x92U, 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, 0x90U, 0xd8U, 0xabU,
+    0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U,
+    0x45U, 0x06U, 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U,
+    0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, 0x3aU, 0x91U, 0x11U, 0x41U,
+    0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U,
+    0x73U, 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U,
+    0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU,
+    0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U,
+    0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U,
+    0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, 0x60U,
+    0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU,
+    0x93U, 0xc9U, 0x9cU, 0xefU, 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U,
+    0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, 0x17U, 0x2bU,
+    0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U,
+    0x21U, 0x0cU, 0x7dU,
+};
+static const uint32_t rcon[] = {
+    0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+    0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
+    /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+inline void ctr128_inc(void* counter_pointer) {
+  uint8_t* counter = reinterpret_cast<uint8_t*>(counter_pointer);
+  uint32_t n = 16;
+  uint8_t c;
+
+  do {
+    --n;
+    c = counter[n];
+    ++c;
+    counter[n] = c;
+    if (c)
+      return;
+  } while (n);
+}
+
+inline void ctr128_inc_aligned(void* counter_pointer) {
+  uint8_t* counter = reinterpret_cast<uint8_t*>(counter_pointer);
+  size_t *data, c, n;
+  const union {
+    uint32_t one;
+    char little;
+  } is_endian = {1};
+
+  if (is_endian.little) {
+    ctr128_inc(counter);
+    return;
+  }
+
+  data = reinterpret_cast<size_t*>(counter_pointer);
+  n = 16 / sizeof(size_t);
+  do {
+    --n;
+    c = data[n];
+    ++c;
+    data[n] = c;
+    if (c)
+      return;
+  } while (n);
+}
+
+/*
+ * The input encrypted as though 128bit counter mode is being used.  The
+ * extra state information to record how much of the 128bit block we have
+ * used is contained in *num, and the encrypted counter is kept in
+ * ecount_buf.  Both *num and ecount_buf must be initialised with zeros
+ * before the first call to CRYPTO_ctr128_encrypt(). This algorithm assumes
+ * that the counter is in the x lower bits of the IV (ivec), and that the
+ * application has full control over overflow and the rest of the IV.  This
+ * implementation takes NO responsability for checking that the counter
+ * doesn't overflow into the rest of the IV when incremented.
+ */
+void CRYPTO_ctr128_encrypt(const void* in_pointer,
+                           void* out_pointer,
+                           size_t len,
+                           const AES_KEY* key,
+                           unsigned char ivec[16],
+                           unsigned char ecount_buf[16],
+                           unsigned int* num,
+                           block128_f block) {
+  const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+  uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+
+  uint32_t n;
+  size_t l = 0;
+
+  SB_DCHECK(in && out && key && ecount_buf && num);
+  SB_DCHECK(*num < 16);
+
+  n = *num;
+
+  if (16 % sizeof(size_t) == 0) { /* always true actually */
+    do {
+      while (n && len) {
+        *(out++) = *(in++) ^ ecount_buf[n];
+        --len;
+        n = (n + 1) % 16;
+      }
+
+      while (len >= 16) {
+        (*block)(ivec, ecount_buf, key);
+        ctr128_inc_aligned(ivec);
+        for (; n < 16; n += sizeof(size_t))
+          *reinterpret_cast<size_t*>(out + n) =
+              *reinterpret_cast<const size_t*>(in + n) ^
+              *reinterpret_cast<size_t*>(ecount_buf + n);
+        len -= 16;
+        out += 16;
+        in += 16;
+        n = 0;
+      }
+      if (len) {
+        (*block)(ivec, ecount_buf, key);
+        ctr128_inc_aligned(ivec);
+        while (len--) {
+          out[n] = in[n] ^ ecount_buf[n];
+          ++n;
+        }
+      }
+      *num = n;
+      return;
+    } while (0);
+  }
+
+  /* the rest would be commonly eliminated by x86* compiler */
+  while (l < len) {
+    if (n == 0) {
+      (*block)(ivec, ecount_buf, key);
+      ctr128_inc(ivec);
+    }
+    out[l] = in[l] ^ ecount_buf[n];
+    ++l;
+    n = (n + 1) % 16;
+  }
+
+  *num = n;
+}
+
+void CRYPTO_cbc128_encrypt(const void* in_pointer,
+                           void* out_pointer,
+                           size_t len,
+                           const AES_KEY* key,
+                           unsigned char ivec[16],
+                           block128_f block) {
+  const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+  uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+  size_t n;
+  const unsigned char* iv = ivec;
+
+  SB_DCHECK(in && out && key && ivec);
+
+  while (len >= 16) {
+    for (n = 0; n < 16; n += sizeof(size_t))
+      *reinterpret_cast<size_t*>(out + n) =
+          *reinterpret_cast<const size_t*>(in + n) ^
+          *reinterpret_cast<const size_t*>(iv + n);
+    (*block)(out, out, key);
+    iv = out;
+    len -= 16;
+    in += 16;
+    out += 16;
+  }
+
+  while (len) {
+    for (n = 0; n < 16 && n < len; ++n)
+      out[n] = in[n] ^ iv[n];
+    for (; n < 16; ++n)
+      out[n] = iv[n];
+    (*block)(out, out, key);
+    iv = out;
+    if (len <= 16)
+      break;
+    len -= 16;
+    in += 16;
+    out += 16;
+  }
+  SbMemoryCopy(ivec, iv, 16);
+}
+
+void CRYPTO_cbc128_decrypt(const void* in_pointer,
+                           void* out_pointer,
+                           size_t len,
+                           const AES_KEY* key,
+                           unsigned char ivec[16],
+                           block128_f block) {
+  const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+  uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+  size_t n;
+  union {
+    size_t t[16 / sizeof(size_t)];
+    unsigned char c[16];
+  } tmp;
+
+  SB_DCHECK(in && out && key && ivec);
+
+  if (in != out) {
+    const unsigned char* iv = ivec;
+
+    if (16 % sizeof(size_t) == 0) { /* always true */
+      while (len >= 16) {
+        size_t* out_t = reinterpret_cast<size_t*>(out);
+        const size_t* iv_t = reinterpret_cast<const size_t*>(iv);
+
+        (*block)(in, out, key);
+        for (n = 0; n < 16 / sizeof(size_t); n++)
+          out_t[n] ^= iv_t[n];
+        iv = in;
+        len -= 16;
+        in += 16;
+        out += 16;
+      }
+    }
+    SbMemoryCopy(ivec, iv, 16);
+  } else {
+    if (16 % sizeof(size_t) == 0) { /* always true */
+      while (len >= 16) {
+        size_t c, *out_t = reinterpret_cast<size_t *>(out);
+        size_t* ivec_t = reinterpret_cast<size_t*>(ivec);
+        const size_t* in_t = reinterpret_cast<const size_t*>(in);
+
+        (*block)(in, tmp.c, key);
+        for (n = 0; n < 16 / sizeof(size_t); n++) {
+          c = in_t[n];
+          out_t[n] = tmp.t[n] ^ ivec_t[n];
+          ivec_t[n] = c;
+        }
+        len -= 16;
+        in += 16;
+        out += 16;
+      }
+    }
+  }
+
+  while (len) {
+    unsigned char c;
+    (*block)(in, tmp.c, key);
+    for (n = 0; n < 16 && n < len; ++n) {
+      c = in[n];
+      out[n] = tmp.c[n] ^ ivec[n];
+      ivec[n] = c;
+    }
+    if (len <= 16) {
+      for (; n < 16; ++n)
+        ivec[n] = in[n];
+      break;
+    }
+    len -= 16;
+    in += 16;
+    out += 16;
+  }
+}
+}  // namespace
+
+int AES_set_encrypt_key(const void* key_pointer,
+                        unsigned bits,
+                        AES_KEY* aeskey) {
+  const uint8_t* key = reinterpret_cast<const uint8_t*>(key_pointer);
+  uint32_t* rk;
+  int i = 0;
+  uint32_t temp;
+  if (!key || !aeskey) {
+    return -1;
+  }
+  switch (bits) {
+    case 128:
+      aeskey->rounds = 10;
+      break;
+    case 192:
+      aeskey->rounds = 12;
+      break;
+    case 256:
+      aeskey->rounds = 14;
+      break;
+    default:
+      return -2;
+  }
+  rk = aeskey->rd_key;
+  rk[0] = GETU32(key);
+  rk[1] = GETU32(key + 4);
+  rk[2] = GETU32(key + 8);
+  rk[3] = GETU32(key + 12);
+  if (bits == 128) {
+    while (1) {
+      temp = rk[3];
+      rk[4] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+              (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+              (Te0[(temp)&0xff] & 0x0000ff00) ^
+              (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+      rk[5] = rk[1] ^ rk[4];
+      rk[6] = rk[2] ^ rk[5];
+      rk[7] = rk[3] ^ rk[6];
+      if (++i == 10) {
+        return 0;
+      }
+      rk += 4;
+    }
+  }
+  rk[4] = GETU32(key + 16);
+  rk[5] = GETU32(key + 20);
+  if (bits == 192) {
+    while (1) {
+      temp = rk[5];
+      rk[6] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+              (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+              (Te0[(temp)&0xff] & 0x0000ff00) ^
+              (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+      rk[7] = rk[1] ^ rk[6];
+      rk[8] = rk[2] ^ rk[7];
+      rk[9] = rk[3] ^ rk[8];
+      if (++i == 8) {
+        return 0;
+      }
+      rk[10] = rk[4] ^ rk[9];
+      rk[11] = rk[5] ^ rk[10];
+      rk += 6;
+    }
+  }
+  rk[6] = GETU32(key + 24);
+  rk[7] = GETU32(key + 28);
+  if (bits == 256) {
+    while (1) {
+      temp = rk[7];
+      rk[8] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+              (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+              (Te0[(temp)&0xff] & 0x0000ff00) ^
+              (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+      rk[9] = rk[1] ^ rk[8];
+      rk[10] = rk[2] ^ rk[9];
+      rk[11] = rk[3] ^ rk[10];
+      if (++i == 7) {
+        return 0;
+      }
+      temp = rk[11];
+      rk[12] = rk[4] ^ (Te2[(temp >> 24)] & 0xff000000) ^
+               (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^
+               (Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^
+               (Te1[(temp)&0xff] & 0x000000ff);
+      rk[13] = rk[5] ^ rk[12];
+      rk[14] = rk[6] ^ rk[13];
+      rk[15] = rk[7] ^ rk[14];
+      rk += 8;
+    }
+  }
+  return 0;
+}
+
+int AES_set_decrypt_key(const void* key_pointer,
+                        unsigned bits,
+                        AES_KEY* aeskey) {
+  const uint8_t* key = reinterpret_cast<const uint8_t*>(key_pointer);
+  uint32_t* rk;
+  int i, j, status;
+  uint32_t temp;
+  /* first, start with an encryption schedule */
+  status = AES_set_encrypt_key(key, bits, aeskey);
+  if (status < 0) {
+    return status;
+  }
+  rk = aeskey->rd_key;
+  /* invert the order of the round keys: */
+  for (i = 0, j = 4 * aeskey->rounds; i < j; i += 4, j -= 4) {
+    temp = rk[i];
+    rk[i] = rk[j];
+    rk[j] = temp;
+    temp = rk[i + 1];
+    rk[i + 1] = rk[j + 1];
+    rk[j + 1] = temp;
+    temp = rk[i + 2];
+    rk[i + 2] = rk[j + 2];
+    rk[j + 2] = temp;
+    temp = rk[i + 3];
+    rk[i + 3] = rk[j + 3];
+    rk[j + 3] = temp;
+  }
+  /* apply the inverse MixColumn transform to all round keys but the first and
+   * the last: */
+  for (i = 1; i < static_cast<int>(aeskey->rounds); i++) {
+    rk += 4;
+    rk[0] =
+        Td0[Te1[(rk[0] >> 24)] & 0xff] ^ Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[0] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[0]) & 0xff] & 0xff];
+    rk[1] =
+        Td0[Te1[(rk[1] >> 24)] & 0xff] ^ Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[1] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[1]) & 0xff] & 0xff];
+    rk[2] =
+        Td0[Te1[(rk[2] >> 24)] & 0xff] ^ Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[2] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[2]) & 0xff] & 0xff];
+    rk[3] =
+        Td0[Te1[(rk[3] >> 24)] & 0xff] ^ Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^
+        Td2[Te1[(rk[3] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[3]) & 0xff] & 0xff];
+  }
+  return 0;
+}
+
+void AES_encrypt(const void* in_pointer,
+                 void* out_pointer,
+                 const AES_KEY* key) {
+  const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+  uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+  const uint32_t* rk;
+  uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+  int r;
+  SB_DCHECK(in && out && key);
+  rk = key->rd_key;
+  /* map byte array block to cipher state
+   * and add initial round key: */
+  s0 = GETU32(in) ^ rk[0];
+  s1 = GETU32(in + 4) ^ rk[1];
+  s2 = GETU32(in + 8) ^ rk[2];
+  s3 = GETU32(in + 12) ^ rk[3];
+  /*
+   * Nr - 1 full rounds:
+   */
+  r = key->rounds >> 1;
+  for (;;) {
+    t0 = Te0[(s0 >> 24)] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+         Te3[(s3)&0xff] ^ rk[4];
+    t1 = Te0[(s1 >> 24)] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+         Te3[(s0)&0xff] ^ rk[5];
+    t2 = Te0[(s2 >> 24)] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+         Te3[(s1)&0xff] ^ rk[6];
+    t3 = Te0[(s3 >> 24)] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+         Te3[(s2)&0xff] ^ rk[7];
+    rk += 8;
+    if (--r == 0) {
+      break;
+    }
+    s0 = Te0[(t0 >> 24)] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+         Te3[(t3)&0xff] ^ rk[0];
+    s1 = Te0[(t1 >> 24)] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+         Te3[(t0)&0xff] ^ rk[1];
+    s2 = Te0[(t2 >> 24)] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+         Te3[(t1)&0xff] ^ rk[2];
+    s3 = Te0[(t3 >> 24)] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+         Te3[(t2)&0xff] ^ rk[3];
+  }
+
+  /*  apply last round and map cipher state to byte array block: */
+  s0 = (Te2[(t0 >> 24)] & 0xff000000) ^ (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t3)&0xff] & 0x000000ff) ^
+       rk[0];
+  PUTU32(out, s0);
+  s1 = (Te2[(t1 >> 24)] & 0xff000000) ^ (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t0)&0xff] & 0x000000ff) ^
+       rk[1];
+  PUTU32(out + 4, s1);
+  s2 = (Te2[(t2 >> 24)] & 0xff000000) ^ (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t1)&0xff] & 0x000000ff) ^
+       rk[2];
+  PUTU32(out + 8, s2);
+  s3 = (Te2[(t3 >> 24)] & 0xff000000) ^ (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+       (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t2)&0xff] & 0x000000ff) ^
+       rk[3];
+  PUTU32(out + 12, s3);
+}
+
+void AES_decrypt(const void* in_pointer,
+                 void* out_pointer,
+                 const AES_KEY* key) {
+  const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+  uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+  const uint32_t* rk;
+  uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+  int r;
+  SB_DCHECK(in && out && key);
+  rk = key->rd_key;
+
+  /* map byte array block to cipher state
+   * and add initial round key: */
+  s0 = GETU32(in) ^ rk[0];
+  s1 = GETU32(in + 4) ^ rk[1];
+  s2 = GETU32(in + 8) ^ rk[2];
+  s3 = GETU32(in + 12) ^ rk[3];
+
+  /*
+   * Nr - 1 full rounds:
+   */
+  r = key->rounds >> 1;
+  for (;;) {
+    t0 = Td0[(s0 >> 24)] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+         Td3[(s1)&0xff] ^ rk[4];
+    t1 = Td0[(s1 >> 24)] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+         Td3[(s2)&0xff] ^ rk[5];
+    t2 = Td0[(s2 >> 24)] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+         Td3[(s3)&0xff] ^ rk[6];
+    t3 = Td0[(s3 >> 24)] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+         Td3[(s0)&0xff] ^ rk[7];
+    rk += 8;
+    if (--r == 0) {
+      break;
+    }
+    s0 = Td0[(t0 >> 24)] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+         Td3[(t1)&0xff] ^ rk[0];
+    s1 = Td0[(t1 >> 24)] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+         Td3[(t2)&0xff] ^ rk[1];
+    s2 = Td0[(t2 >> 24)] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+         Td3[(t3)&0xff] ^ rk[2];
+    s3 = Td0[(t3 >> 24)] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+         Td3[(t0)&0xff] ^ rk[3];
+  }
+
+  /* apply last round and
+   * map cipher state to byte array block: */
+  s0 = ((uint32_t)Td4[(t0 >> 24)] << 24) ^
+       ((uint32_t)Td4[(t3 >> 16) & 0xff] << 16) ^
+       ((uint32_t)Td4[(t2 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t1)&0xff]) ^
+       rk[0];
+  PUTU32(out, s0);
+  s1 = ((uint32_t)Td4[(t1 >> 24)] << 24) ^
+       ((uint32_t)Td4[(t0 >> 16) & 0xff] << 16) ^
+       ((uint32_t)Td4[(t3 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t2)&0xff]) ^
+       rk[1];
+  PUTU32(out + 4, s1);
+  s2 = ((uint32_t)Td4[(t2 >> 24)] << 24) ^
+       ((uint32_t)Td4[(t1 >> 16) & 0xff] << 16) ^
+       ((uint32_t)Td4[(t0 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t3)&0xff]) ^
+       rk[2];
+  PUTU32(out + 8, s2);
+  s3 = ((uint32_t)Td4[(t3 >> 24)] << 24) ^
+       ((uint32_t)Td4[(t2 >> 16) & 0xff] << 16) ^
+       ((uint32_t)Td4[(t1 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t0)&0xff]) ^
+       rk[3];
+  PUTU32(out + 12, s3);
+}
+
+void AES_ctr128_encrypt(const void* in,
+                        void* out,
+                        size_t len,
+                        const AES_KEY* key,
+                        uint8_t ivec[SB_AES_BLOCK_SIZE],
+                        uint8_t ecount_buf[SB_AES_BLOCK_SIZE],
+                        unsigned int* num) {
+  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num, AES_encrypt);
+}
+
+void AES_cbc_encrypt(const void* in,
+                     void* out,
+                     size_t len,
+                     const AES_KEY* key,
+                     uint8_t* ivec,
+                     const int enc) {
+  if (enc) {
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, AES_encrypt);
+  } else {
+    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, AES_decrypt);
+  }
+}
+
+// --- GCM -------------------------------------------------------------------
+
+#define GCM_MUL(ctx, Xi) gcm_gmult_4bit((ctx)->Xi.u, (ctx)->Htable)
+#define PACK(s) ((size_t)(s) << (sizeof(size_t) * 8 - 16))
+#define REDUCE1BIT(V)                                                 \
+  do {                                                                \
+    if (sizeof(size_t) == 8) {                                        \
+      uint64_t T = UINT64_C(0xe100000000000000) & (0 - ((V).lo & 1)); \
+      (V).lo = ((V).hi << 63) | ((V).lo >> 1);                        \
+      (V).hi = ((V).hi >> 1) ^ T;                                     \
+    } else {                                                          \
+      uint32_t T = 0xe1000000U & (0 - (uint32_t)((V).lo & 1));        \
+      (V).lo = ((V).hi << 63) | ((V).lo >> 1);                        \
+      (V).hi = ((V).hi >> 1) ^ ((uint64_t)T << 32);                   \
+    }                                                                 \
+  } while (0)
+
+namespace {
+const size_t rem_4bit[16] = {
+    PACK(0x0000), PACK(0x1C20), PACK(0x3840), PACK(0x2460),
+    PACK(0x7080), PACK(0x6CA0), PACK(0x48C0), PACK(0x54E0),
+    PACK(0xE100), PACK(0xFD20), PACK(0xD940), PACK(0xC560),
+    PACK(0x9180), PACK(0x8DA0), PACK(0xA9C0), PACK(0xB5E0)};
+
+static void gcm_init_4bit(u128 Htable[16], uint64_t H[2]) {
+  u128 V;
+
+  Htable[0].hi = 0;
+  Htable[0].lo = 0;
+  V.hi = H[0];
+  V.lo = H[1];
+
+  Htable[8] = V;
+  REDUCE1BIT(V);
+  Htable[4] = V;
+  REDUCE1BIT(V);
+  Htable[2] = V;
+  REDUCE1BIT(V);
+  Htable[1] = V;
+  Htable[3].hi = V.hi ^ Htable[2].hi, Htable[3].lo = V.lo ^ Htable[2].lo;
+  V = Htable[4];
+  Htable[5].hi = V.hi ^ Htable[1].hi, Htable[5].lo = V.lo ^ Htable[1].lo;
+  Htable[6].hi = V.hi ^ Htable[2].hi, Htable[6].lo = V.lo ^ Htable[2].lo;
+  Htable[7].hi = V.hi ^ Htable[3].hi, Htable[7].lo = V.lo ^ Htable[3].lo;
+  V = Htable[8];
+  Htable[9].hi = V.hi ^ Htable[1].hi, Htable[9].lo = V.lo ^ Htable[1].lo;
+  Htable[10].hi = V.hi ^ Htable[2].hi, Htable[10].lo = V.lo ^ Htable[2].lo;
+  Htable[11].hi = V.hi ^ Htable[3].hi, Htable[11].lo = V.lo ^ Htable[3].lo;
+  Htable[12].hi = V.hi ^ Htable[4].hi, Htable[12].lo = V.lo ^ Htable[4].lo;
+  Htable[13].hi = V.hi ^ Htable[5].hi, Htable[13].lo = V.lo ^ Htable[5].lo;
+  Htable[14].hi = V.hi ^ Htable[6].hi, Htable[14].lo = V.lo ^ Htable[6].lo;
+  Htable[15].hi = V.hi ^ Htable[7].hi, Htable[15].lo = V.lo ^ Htable[7].lo;
+}
+
+void gcm_gmult_4bit(uint64_t Xi[2], const u128 Htable[16]) {
+  u128 Z;
+  int cnt = 15;
+  size_t rem, nlo, nhi;
+
+  nlo = ((const uint8_t *)Xi)[15];
+  nhi = nlo >> 4;
+  nlo &= 0xf;
+
+  Z.hi = Htable[nlo].hi;
+  Z.lo = Htable[nlo].lo;
+
+  while (1) {
+    rem = (size_t)Z.lo & 0xf;
+    Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+    Z.hi = (Z.hi >> 4);
+    if (sizeof(size_t) == 8) {
+      Z.hi ^= rem_4bit[rem];
+    } else {
+      Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+    }
+
+    Z.hi ^= Htable[nhi].hi;
+    Z.lo ^= Htable[nhi].lo;
+
+    if (--cnt < 0) {
+      break;
+    }
+
+    nlo = ((const uint8_t *)Xi)[cnt];
+    nhi = nlo >> 4;
+    nlo &= 0xf;
+
+    rem = (size_t)Z.lo & 0xf;
+    Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+    Z.hi = (Z.hi >> 4);
+    if (sizeof(size_t) == 8) {
+      Z.hi ^= rem_4bit[rem];
+    } else {
+      Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+    }
+
+    Z.hi ^= Htable[nlo].hi;
+    Z.lo ^= Htable[nlo].lo;
+  }
+
+  Xi[0] = SbByteSwapU64(Z.hi);
+  Xi[1] = SbByteSwapU64(Z.lo);
+}
+
+/* Streamed gcm_mult_4bit, see CRYPTO_gcm128_[en|de]crypt for
+ * details... Compiler-generated code doesn't seem to give any
+ * performance improvement, at least not on x86[_64]. It's here
+ * mostly as reference and a placeholder for possible future
+ * non-trivial optimization[s]... */
+void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16],
+                    const uint8_t *inp, size_t len) {
+  u128 Z;
+  int cnt;
+  size_t rem, nlo, nhi;
+
+  do {
+    cnt = 15;
+    nlo = ((const uint8_t *)Xi)[15];
+    nlo ^= inp[15];
+    nhi = nlo >> 4;
+    nlo &= 0xf;
+
+    Z.hi = Htable[nlo].hi;
+    Z.lo = Htable[nlo].lo;
+
+    while (1) {
+      rem = (size_t)Z.lo & 0xf;
+      Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+      Z.hi = (Z.hi >> 4);
+      if (sizeof(size_t) == 8) {
+        Z.hi ^= rem_4bit[rem];
+      } else {
+        Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+      }
+
+      Z.hi ^= Htable[nhi].hi;
+      Z.lo ^= Htable[nhi].lo;
+
+      if (--cnt < 0) {
+        break;
+      }
+
+      nlo = ((const uint8_t *)Xi)[cnt];
+      nlo ^= inp[cnt];
+      nhi = nlo >> 4;
+      nlo &= 0xf;
+
+      rem = (size_t)Z.lo & 0xf;
+      Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+      Z.hi = (Z.hi >> 4);
+      if (sizeof(size_t) == 8) {
+        Z.hi ^= rem_4bit[rem];
+      } else {
+        Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+      }
+
+      Z.hi ^= Htable[nlo].hi;
+      Z.lo ^= Htable[nlo].lo;
+    }
+
+    Xi[0] = SbByteSwapU64(Z.hi);
+    Xi[1] = SbByteSwapU64(Z.lo);
+  } while (inp += 16, len -= 16);
+}
+}  // namespace
+
+void CRYPTO_ghash_init(gmult_func *out_mult, ghash_func *out_hash,
+                       u128 *out_key, u128 out_table[16],
+                       int *out_is_avx,
+                       const uint8_t *gcm_key) {
+  *out_is_avx = 0;
+
+  union {
+    uint64_t u[2];
+    uint8_t c[16];
+  } H;
+
+  SbMemoryCopy(H.c, gcm_key, 16);
+
+  /* H is stored in host byte order */
+  H.u[0] = SbByteSwapU64(H.u[0]);
+  H.u[1] = SbByteSwapU64(H.u[1]);
+
+  SbMemoryCopy(out_key, H.c, 16);
+
+  gcm_init_4bit(out_table, H.u);
+  *out_mult = gcm_gmult_4bit;
+  *out_hash = gcm_ghash_4bit;
+}
+
+void AES_gcm128_init(GCM128_CONTEXT *ctx, const AES_KEY *aes_key, int enc) {
+  SbMemorySet(ctx, 0, sizeof(*ctx));
+  ctx->block = AES_encrypt;
+
+  uint8_t gcm_key[16];
+  SbMemorySet(gcm_key, 0, sizeof(gcm_key));
+  (*ctx->block)(gcm_key, gcm_key, aes_key);
+
+  int is_avx;
+  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, &ctx->H, ctx->Htable, &is_avx,
+                    gcm_key);
+}
+
+void AES_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
+                      const void *iv_void, size_t len) {
+  unsigned int ctr;
+  const uint8_t *iv = static_cast<const uint8_t*>(iv_void);
+
+  ctx->Yi.u[0] = 0;
+  ctx->Yi.u[1] = 0;
+  ctx->Xi.u[0] = 0;
+  ctx->Xi.u[1] = 0;
+  ctx->len.u[0] = 0; /* AAD length */
+  ctx->len.u[1] = 0; /* message length */
+  ctx->ares = 0;
+  ctx->mres = 0;
+
+  if (len == 12) {
+    SbMemoryCopy(ctx->Yi.c, iv, 12);
+    ctx->Yi.c[15] = 1;
+    ctr = 1;
+  } else {
+    uint64_t len0 = len;
+
+    while (len >= 16) {
+      for (size_t i = 0; i < 16; ++i) {
+        ctx->Yi.c[i] ^= iv[i];
+      }
+      GCM_MUL(ctx, Yi);
+      iv += 16;
+      len -= 16;
+    }
+    if (len) {
+      for (size_t i = 0; i < len; ++i) {
+        ctx->Yi.c[i] ^= iv[i];
+      }
+      GCM_MUL(ctx, Yi);
+    }
+    len0 <<= 3;
+    ctx->Yi.u[1] ^= SbByteSwapU64(len0);
+
+    GCM_MUL(ctx, Yi);
+    ctr = GETU32_aligned(ctx->Yi.c + 12);
+  }
+
+  (*ctx->block)(ctx->Yi.c, ctx->EK0.c, key);
+  ++ctr;
+  PUTU32_aligned(ctx->Yi.c + 12, ctr);
+}
+
+int AES_gcm128_aad(GCM128_CONTEXT *ctx, const void *aad_void, size_t len) {
+  unsigned int n;
+  uint64_t alen = ctx->len.u[0];
+  const uint8_t *aad = static_cast<const uint8_t*>(aad_void);
+
+  if (ctx->len.u[1]) {
+    return 0;
+  }
+
+  alen += len;
+  if (alen > (UINT64_C(1) << 61) || (sizeof(len) == 8 && alen < len)) {
+    return 0;
+  }
+  ctx->len.u[0] = alen;
+
+  n = ctx->ares;
+  if (n) {
+    while (n && len) {
+      ctx->Xi.c[n] ^= *(aad++);
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->ares = n;
+      return 1;
+    }
+  }
+
+  /* Process a whole number of blocks. */
+  while (len >= 16) {
+    for (size_t i = 0; i < 16; ++i) {
+      ctx->Xi.c[i] ^= aad[i];
+    }
+    GCM_MUL(ctx, Xi);
+    aad += 16;
+    len -= 16;
+  }
+
+  /* Process the remainder. */
+  if (len != 0) {
+    n = (unsigned int)len;
+    for (size_t i = 0; i < len; ++i) {
+      ctx->Xi.c[i] ^= aad[i];
+    }
+  }
+
+  ctx->ares = n;
+  return 1;
+}
+
+int AES_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+                       const void *in_void, void *out_void, size_t len) {
+  unsigned int n, ctr;
+  uint64_t mlen = ctx->len.u[1];
+  block128_f block = ctx->block;
+  const uint8_t *in = static_cast<const uint8_t*>(in_void);
+  uint8_t *out = static_cast<uint8_t*>(out_void);
+
+  mlen += len;
+  if (mlen > ((UINT64_C(1) << 36) - 32) ||
+      (sizeof(len) == 8 && mlen < len)) {
+    return 0;
+  }
+  ctx->len.u[1] = mlen;
+
+  if (ctx->ares) {
+    /* First call to encrypt finalizes GHASH(AAD) */
+    GCM_MUL(ctx, Xi);
+    ctx->ares = 0;
+  }
+
+  ctr = GETU32_aligned(ctx->Yi.c + 12);
+
+  n = ctx->mres;
+  if (n) {
+    while (n && len) {
+      ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->mres = n;
+      return 1;
+    }
+  }
+
+  while (len >= 16) {
+    size_t *out_t = reinterpret_cast<size_t*>(out);
+    const size_t *in_t = reinterpret_cast<const size_t*>(in);
+
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
+    for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
+      ctx->Xi.t[i] ^= out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+    }
+    GCM_MUL(ctx, Xi);
+    out += 16;
+    in += 16;
+    len -= 16;
+  }
+
+  if (len) {
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
+    while (len--) {
+      ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
+      ++n;
+    }
+  }
+
+  ctx->mres = n;
+  return 1;
+}
+
+int AES_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+                       const void *in_void, void *out_void, size_t len) {
+  unsigned int n, ctr;
+  uint64_t mlen = ctx->len.u[1];
+  block128_f block = ctx->block;
+  const uint8_t *in = static_cast<const uint8_t*>(in_void);
+  uint8_t *out = static_cast<uint8_t*>(out_void);
+
+  mlen += len;
+  if (mlen > ((UINT64_C(1) << 36) - 32) ||
+      (sizeof(len) == 8 && mlen < len)) {
+    return 0;
+  }
+  ctx->len.u[1] = mlen;
+
+  if (ctx->ares) {
+    /* First call to decrypt finalizes GHASH(AAD) */
+    GCM_MUL(ctx, Xi);
+    ctx->ares = 0;
+  }
+
+  ctr = GETU32_aligned(ctx->Yi.c + 12);
+
+  n = ctx->mres;
+  if (n) {
+    while (n && len) {
+      uint8_t c = *(in++);
+      *(out++) = c ^ ctx->EKi.c[n];
+      ctx->Xi.c[n] ^= c;
+      --len;
+      n = (n + 1) % 16;
+    }
+    if (n == 0) {
+      GCM_MUL(ctx, Xi);
+    } else {
+      ctx->mres = n;
+      return 1;
+    }
+  }
+
+  while (len >= 16) {
+    size_t *out_t = reinterpret_cast<size_t*>(out);
+    const size_t *in_t = reinterpret_cast<const size_t*>(in);
+
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
+    for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
+      size_t c = in_t[i];
+      out_t[i] = c ^ ctx->EKi.t[i];
+      ctx->Xi.t[i] ^= c;
+    }
+    GCM_MUL(ctx, Xi);
+    out += 16;
+    in += 16;
+    len -= 16;
+  }
+
+  if (len) {
+    (*block)(ctx->Yi.c, ctx->EKi.c, key);
+    ++ctr;
+    PUTU32_aligned(ctx->Yi.c + 12, ctr);
+    while (len--) {
+      uint8_t c = in[n];
+      ctx->Xi.c[n] ^= c;
+      out[n] = c ^ ctx->EKi.c[n];
+      ++n;
+    }
+  }
+
+  ctx->mres = n;
+  return 1;
+}
+
+void AES_gcm128_tag(GCM128_CONTEXT *ctx, void *tag, size_t len) {
+  uint64_t alen = ctx->len.u[0] << 3;
+  uint64_t clen = ctx->len.u[1] << 3;
+
+  if (ctx->mres || ctx->ares) {
+    GCM_MUL(ctx, Xi);
+  }
+
+  alen = SbByteSwapU64(alen);
+  clen = SbByteSwapU64(clen);
+
+  ctx->Xi.u[0] ^= alen;
+  ctx->Xi.u[1] ^= clen;
+  GCM_MUL(ctx, Xi);
+
+  ctx->Xi.u[0] ^= ctx->EK0.u[0];
+  ctx->Xi.u[1] ^= ctx->EK0.u[1];
+
+  SbMemoryCopy(tag, ctx->Xi.c,
+               len <= sizeof(ctx->Xi.c) ? len : sizeof(ctx->Xi.c));
+}
+
+}  // namespace cryptography
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/cryptography/software_aes.h b/src/starboard/shared/starboard/cryptography/software_aes.h
new file mode 100644
index 0000000..0087995
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/software_aes.h
@@ -0,0 +1,215 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006,2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
+#define STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace cryptography {
+
+/* Raw AES functions. */
+#define SB_AES_ENCRYPT 1
+#define SB_AES_DECRYPT 0
+
+/* AES_MAXNR is the maximum number of AES rounds. */
+#define SB_AES_MAXNR 14
+#define SB_AES_BLOCK_SIZE 16
+
+/* aes_key_st should be an opaque type, but EVP requires that the size be
+ * known. */
+struct aes_key_st {
+  uint32_t rd_key[4 * (SB_AES_MAXNR + 1)];
+  unsigned rounds;
+};
+
+typedef struct aes_key_st AES_KEY;
+
+typedef void (*block128_f)(const void* in, void* out, const AES_KEY* key);
+
+/* AES_set_encrypt_key configures |aeskey| to encrypt with the |bits|-bit key,
+ * |key|.
+ *
+ * WARNING: unlike other OpenSSL functions, this returns zero on success and a
+ * negative number on error. */
+int AES_set_encrypt_key(const void* key, unsigned bits, AES_KEY* aeskey);
+
+/* AES_set_decrypt_key configures |aeskey| to decrypt with the |bits|-bit key,
+ * |key|.
+ *
+ * WARNING: unlike other OpenSSL functions, this returns zero on success and a
+ * negative number on error. */
+int AES_set_decrypt_key(const void* key, unsigned bits, AES_KEY* aeskey);
+
+/* AES_encrypt encrypts a single block from |in| to |out| with |key|. The |in|
+ * and |out| pointers may overlap. */
+void AES_encrypt(const void* in, void* out, const AES_KEY* key);
+
+/* AES_decrypt decrypts a single block from |in| to |out| with |key|. The |in|
+ * and |out| pointers may overlap. */
+void AES_decrypt(const void* in, void* out, const AES_KEY* key);
+
+/* Block cipher modes. */
+/* AES_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode) |len|
+ * bytes from |in| to |out|. The |num| parameter must be set to zero on the
+ * first call and |ivec| will be incremented. */
+void AES_ctr128_encrypt(const void* in,
+                        void* out,
+                        size_t len,
+                        const AES_KEY* key,
+                        uint8_t ivec[SB_AES_BLOCK_SIZE],
+                        uint8_t ecount_buf[SB_AES_BLOCK_SIZE],
+                        uint32_t* num);
+
+/* AES_cbc_encrypt encrypts (or decrypts, if |enc| == |AES_DECRYPT|) |len|
+ * bytes from |in| to |out|. The length must be a multiple of the block size. */
+void AES_cbc_encrypt(const void* in,
+                     void* out,
+                     size_t len,
+                     const AES_KEY* key,
+                     uint8_t* ivec,
+                     const int enc);
+
+/* GCM */
+
+typedef struct {
+  uint64_t hi;
+  uint64_t lo;
+} u128;
+
+/* gmult_func multiplies |Xi| by the GCM key and writes the result back to
+ * |Xi|. */
+typedef void (*gmult_func)(uint64_t Xi[2], const u128 Htable[16]);
+
+/* ghash_func repeatedly multiplies |Xi| by the GCM key and adds in blocks from
+ * |inp|. The result is written back to |Xi| and the |len| argument must be a
+ * multiple of 16. */
+typedef void (*ghash_func)(uint64_t Xi[2], const u128 Htable[16],
+                           const uint8_t *inp, size_t len);
+
+/* This differs from upstream's |gcm128_context| in that it does not have the
+ * |key| pointer, in order to make it |memcpy|-friendly. Rather the key is
+ * passed into each call that needs it. */
+struct gcm128_context {
+  /* Following 6 names follow names in GCM specification */
+  union {
+    uint64_t u[2];
+    uint32_t d[4];
+    uint8_t c[16];
+    size_t t[16 / sizeof(size_t)];
+  } Yi, EKi, EK0, len, Xi;
+  /* Note that the order of |Xi|, |H| and |Htable| is fixed by the MOVBE-based,
+   * x86-64, GHASH assembly. */
+  u128 H;
+  u128 Htable[16];
+  gmult_func gmult;
+  ghash_func ghash;
+  unsigned int mres, ares;
+  block128_f block;
+};
+
+/* This API differs from the upstream API slightly. The |GCM128_CONTEXT| does
+ * not have a |key| pointer that points to the key as upstream's version does.
+ * Instead, every function takes a |key| parameter. This way |GCM128_CONTEXT|
+ * can be safely copied. */
+typedef struct gcm128_context GCM128_CONTEXT;
+
+/* AES_gcm128_init initialises |ctx| to use the given key. */
+void AES_gcm128_init(GCM128_CONTEXT *ctx, const AES_KEY *key, int enc);
+
+/* AES_gcm128_setiv sets the IV (nonce) for |ctx|. The |key| must be the same
+ * key that was passed to |AES_gcm128_init|. */
+void AES_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
+                      const void *iv, size_t iv_len);
+
+/* AES_gcm128_aad sets the authenticated data for an instance of GCM.  This must
+ * be called before and data is encrypted. It returns one on success and zero
+ * otherwise. */
+int AES_gcm128_aad(GCM128_CONTEXT *ctx, const void *aad, size_t len);
+
+/* AES_gcm128_encrypt encrypts |len| bytes from |in| to |out|. The |key| must be
+ * the same key that was passed to |AES_gcm128_init|. It returns one on success
+ * and zero otherwise. */
+int AES_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+                       const void *in, void *out, size_t len);
+
+/* AES_gcm128_decrypt decrypts |len| bytes from |in| to |out|. The |key| must be
+ * the same key that was passed to |AES_gcm128_init|. It returns one on success
+ * and zero otherwise. */
+int AES_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+                       const void *in, void *out, size_t len);
+
+/* AES_gcm128_tag calculates the authenticator and copies it into |tag|.  The
+ * minimum of |len| and 16 bytes are copied into |tag|. */
+void AES_gcm128_tag(GCM128_CONTEXT *ctx, void *tag, size_t len);
+
+}  // namespace cryptography
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
diff --git a/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc b/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc
index 3333f83..efedc7e 100644
--- a/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc
+++ b/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc
@@ -18,14 +18,27 @@
 #include "starboard/shared/starboard/drm/drm_system_internal.h"
 
 void SbDrmGenerateSessionUpdateRequest(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+                                       int ticket,
+#endif  // SB_API_VERSION >= 4
                                        const char* type,
                                        const void* initialization_data,
                                        int initialization_data_size) {
   if (!SbDrmSystemIsValid(drm_system)) {
-    SB_DLOG(WARNING) << "Invalid drm system";
+    SB_DLOG(ERROR) << "Invalid DRM system.";
     return;
   }
 
-  drm_system->GenerateSessionUpdateRequest(type, initialization_data,
-                                           initialization_data_size);
+#if SB_API_VERSION >= 4
+  if (ticket == kSbDrmTicketInvalid) {
+    SB_DLOG(ERROR) << "Ticket must be specified.";
+    return;
+  }
+#endif  // SB_API_VERSION >= 4
+
+  drm_system->GenerateSessionUpdateRequest(
+#if SB_API_VERSION >= 4
+      ticket,
+#endif  // SB_API_VERSION >= 4
+      type, initialization_data, initialization_data_size);
 }
diff --git a/src/starboard/shared/starboard/drm/drm_system_internal.h b/src/starboard/shared/starboard/drm/drm_system_internal.h
index 73e9da8..a392fe1 100644
--- a/src/starboard/shared/starboard/drm/drm_system_internal.h
+++ b/src/starboard/shared/starboard/drm/drm_system_internal.h
@@ -26,13 +26,21 @@
 
   virtual ~SbDrmSystemPrivate() {}
 
-  virtual void GenerateSessionUpdateRequest(const char* type,
-                                            const void* initialization_data,
-                                            int initialization_data_size) = 0;
-  virtual void UpdateSession(const void* key,
-                             int key_size,
-                             const void* session_id,
-                             int session_id_size) = 0;
+  virtual void GenerateSessionUpdateRequest(
+#if SB_API_VERSION >= 4
+      int ticket,
+#endif  // SB_API_VERSION >= 4
+      const char* type,
+      const void* initialization_data,
+      int initialization_data_size) = 0;
+  virtual void UpdateSession(
+#if SB_API_VERSION >= 4
+      int ticket,
+#endif  // SB_API_VERSION >= 4
+      const void* key,
+      int key_size,
+      const void* session_id,
+      int session_id_size) = 0;
   virtual void CloseSession(const void* session_id, int session_id_size) = 0;
 
   virtual DecryptStatus Decrypt(InputBuffer* buffer) = 0;
diff --git a/src/starboard/shared/starboard/drm/drm_update_session.cc b/src/starboard/shared/starboard/drm/drm_update_session.cc
index 287096e..57862d5 100644
--- a/src/starboard/shared/starboard/drm/drm_update_session.cc
+++ b/src/starboard/shared/starboard/drm/drm_update_session.cc
@@ -18,6 +18,9 @@
 #include "starboard/shared/starboard/drm/drm_system_internal.h"
 
 void SbDrmUpdateSession(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+                        int ticket,
+#endif  // SB_API_VERSION >= 4
                         const void* key,
                         int key_size,
                         const void* session_id,
@@ -27,5 +30,9 @@
     return;
   }
 
-  drm_system->UpdateSession(key, key_size, session_id, session_id_size);
+  drm_system->UpdateSession(
+#if SB_API_VERSION >= 4
+      ticket,
+#endif  // SB_API_VERSION >= 4
+      key, key_size, session_id, session_id_size);
 }
diff --git a/src/starboard/shared/starboard/media/codec_util.cc b/src/starboard/shared/starboard/media/codec_util.cc
new file mode 100644
index 0000000..9ec76dc
--- /dev/null
+++ b/src/starboard/shared/starboard/media/codec_util.cc
@@ -0,0 +1,149 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/codec_util.h"
+
+#include "starboard/character.h"
+#include "starboard/log.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+int hex_to_int(char hex) {
+  if (hex >= '0' && hex <= '9') {
+    return hex - '0';
+  }
+  if (hex >= 'A' && hex <= 'F') {
+    return hex - 'A' + 10;
+  }
+  if (hex >= 'a' && hex <= 'f') {
+    return hex - 'a' + 10;
+  }
+  SB_NOTREACHED();
+  return 0;
+}
+
+}  // namespace
+
+SbMediaAudioCodec GetAudioCodecFromString(const char* codec) {
+  if (SbStringCompare(codec, "mp4a.40.", 8) == 0) {
+    return kSbMediaAudioCodecAac;
+  }
+  if (SbStringCompare(codec, "opus", 4) == 0) {
+    return kSbMediaAudioCodecOpus;
+  }
+  if (SbStringCompare(codec, "vorbis", 6) == 0) {
+    return kSbMediaAudioCodecVorbis;
+  }
+  return kSbMediaAudioCodecNone;
+}
+
+SbMediaVideoCodec GetVideoCodecFromString(const char* codec) {
+  if (SbStringCompare(codec, "avc1.", 5) == 0 ||
+      SbStringCompare(codec, "avc3.", 5) == 0) {
+    return kSbMediaVideoCodecH264;
+  }
+  if (SbStringCompare(codec, "hev1.", 5) == 0 ||
+      SbStringCompare(codec, "hvc1.", 5) == 0) {
+    return kSbMediaVideoCodecH265;
+  }
+  if (SbStringCompare(codec, "vp8", 3) == 0) {
+    return kSbMediaVideoCodecVp8;
+  }
+  if (SbStringCompare(codec, "vp9", 3) == 0) {
+    return kSbMediaVideoCodecVp9;
+  }
+  return kSbMediaVideoCodecNone;
+}
+
+bool ParseH264Info(const char* codec, int* width, int* height, int* fps) {
+  SB_DCHECK(width);
+  SB_DCHECK(height);
+  SB_DCHECK(fps);
+
+  if (GetVideoCodecFromString(codec) != kSbMediaVideoCodecH264) {
+    return false;
+  }
+
+  if (SbStringGetLength(codec) != 11 || !SbCharacterIsHexDigit(codec[9]) ||
+      !SbCharacterIsHexDigit(codec[10])) {
+    return false;
+  }
+
+  // According to https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Levels, the
+  // only thing that determinates resolution and fps is level.  Profile and
+  // constraint set only affect bitrate and features.
+  //
+  // Note that a level can map to more than one resolution/fps combinations.
+  // So the returned resolution/fps is hand-picked to best fit for common cases.
+  // For example, level 4.2 indicates 1280 x 720 at 145.1 fps or 1920 x 1080 at
+  // 64.0 fps or 2048 x 1080 @ 60.9 fps.  In this case, the function returns
+  // 1920 x 1080 at 60 fps.
+  int level = hex_to_int(codec[9]) * 16 + hex_to_int(codec[10]);
+
+  // Level 3.1 is the minimum to support 720p at 30 fps.  We assume all devices
+  // can support it.
+  if (level <= 31) {
+    *width = 1280;
+    *height = 720;
+    *fps = 30;
+    return true;
+  } else if (level <= 32) {
+    *width = 1280;
+    *height = 720;
+    *fps = 60;
+    return true;
+  } else if (level <= 40) {
+    *width = 1920;
+    *height = 1080;
+    *fps = 30;
+    return true;
+  } else if (level <= 41) {
+    *width = 1920;
+    *height = 1080;
+    *fps = 30;
+    return true;
+  } else if (level <= 42) {
+    *width = 1920;
+    *height = 1080;
+    *fps = 60;
+    return true;
+  } else if (level <= 50) {
+    *width = 2560;
+    *height = 1440;
+    *fps = 30;
+    return true;
+  } else if (level <= 51) {
+    *width = 3840;
+    *height = 2160;
+    *fps = 30;
+    return true;
+  }
+
+  // Level >= 52
+  *width = 3840;
+  *height = 2160;
+  *fps = 60;
+  return true;
+}
+
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/media/codec_util.h b/src/starboard/shared/starboard/media/codec_util.h
new file mode 100644
index 0000000..144ff03
--- /dev/null
+++ b/src/starboard/shared/starboard/media/codec_util.h
@@ -0,0 +1,42 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+SbMediaAudioCodec GetAudioCodecFromString(const char* codec);
+SbMediaVideoCodec GetVideoCodecFromString(const char* codec);
+
+// This function parses an h264 codec in the form of {avc1|avc3}.PPCCLL as
+// specificed by https://tools.ietf.org/html/rfc6381#section-3.3.
+//
+// Note that the leading codec is not necessarily to be "avc1" or "avc3" per
+// spec but this function only parses "avc1" and "avc3".  This function returns
+// false when |codec| doesn't contain a valid codec string.
+bool ParseH264Info(const char* codec, int* width, int* height, int* fps);
+
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
diff --git a/src/starboard/shared/starboard/media/interleaved_sinc_resampler.cc b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.cc
new file mode 100644
index 0000000..d4df9ff
--- /dev/null
+++ b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2015 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.
+//
+// Input buffer layout, dividing the total buffer into regions (r0_ - r5_):
+//
+// |----------------|-----------------------------------------|----------------|
+//
+//                                   kBlockSize + kKernelSize / 2
+//                   <--------------------------------------------------------->
+//                                              r0_
+//
+//  kKernelSize / 2   kKernelSize / 2         kKernelSize / 2   kKernelSize / 2
+// <---------------> <--------------->       <---------------> <--------------->
+//        r1_               r2_                     r3_               r4_
+//
+//                                                     kBlockSize
+//                                     <--------------------------------------->
+//                                                        r5_
+//
+// The algorithm:
+//
+// 1) Consume input frames into r0_ (r1_ is zero-initialized).
+// 2) Position kernel centered at start of r0_ (r2_) and generate output frames
+//    until kernel is centered at start of r4_ or we've finished generating all
+//    the output frames.
+// 3) Copy r3_ to r1_ and r4_ to r2_.
+// 4) Consume input frames into r5_ (zero-pad if we run out of input).
+// 5) Goto (2) until all of input is consumed.
+//
+// Note: we're glossing over how the sub-sample handling works with
+// |virtual_source_idx_|, etc.
+
+#include "media/base/interleaved_sinc_resampler.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace media {
+
+namespace {
+
+// The kernel size can be adjusted for quality (higher is better) at the
+// expense of performance.  Must be a multiple of 32.
+const int kKernelSize = 32;
+
+// The number of destination frames generated per processing pass.  Affects
+// how often and for how much InterleavedSincResampler calls back for input.
+// Must be greater than kKernelSize.
+const int kBlockSize = 512;
+
+// The kernel offset count is used for interpolation and is the number of
+// sub-sample kernel shifts.  Can be adjusted for quality (higher is better)
+// at the expense of allocating more memory.
+const int kKernelOffsetCount = 32;
+const int kKernelStorageSize = kKernelSize * (kKernelOffsetCount + 1);
+
+// The size (in samples) of the internal buffer used by the resampler.
+const int kBufferSize = kBlockSize + kKernelSize;
+
+// The maximum numbers of buffer can be queued.
+const int kMaximumPendingBuffers = 8;
+
+}  // namespace
+
+InterleavedSincResampler::InterleavedSincResampler(double io_sample_rate_ratio,
+                                                   int channel_count)
+    : io_sample_rate_ratio_(io_sample_rate_ratio),
+      virtual_source_idx_(0),
+      buffer_primed_(false),
+      channel_count_(channel_count),
+      frame_size_in_bytes_(sizeof(float) * channel_count_),
+      // Create buffers with a 16-byte alignment for possible optimizations.
+      kernel_storage_(static_cast<float*>(
+          base::AlignedAlloc(sizeof(float) * kKernelStorageSize, 16))),
+      input_buffer_(static_cast<float*>(
+          base::AlignedAlloc(frame_size_in_bytes_ * kBufferSize, 16))),
+      offset_in_frames_(0),
+      frames_resampled_(0),
+      frames_queued_(0),
+      // Setup various region pointers in the buffer (see diagram above).
+      r0_(input_buffer_.get() + kKernelSize / 2 * channel_count_),
+      r1_(input_buffer_.get()),
+      r2_(r0_),
+      r3_(r0_ + (kBlockSize - kKernelSize / 2) * channel_count_),
+      r4_(r0_ + kBlockSize * channel_count_),
+      r5_(r0_ + kKernelSize / 2 * channel_count_) {
+  // Ensure kKernelSize is a multiple of 32 for easy SSE optimizations; causes
+  // r0_ and r5_ (used for input) to always be 16-byte aligned by virtue of
+  // input_buffer_ being 16-byte aligned.
+  DCHECK_EQ(kKernelSize % 32, 0) << "kKernelSize must be a multiple of 32!";
+  DCHECK_GT(kBlockSize, kKernelSize)
+      << "kBlockSize must be greater than kKernelSize!";
+  // Basic sanity checks to ensure buffer regions are laid out correctly:
+  // r0_ and r2_ should always be the same position.
+  DCHECK_EQ(r0_, r2_);
+  // r1_ at the beginning of the buffer.
+  DCHECK_EQ(r1_, input_buffer_.get());
+  // r1_ left of r2_, r2_ left of r5_ and r1_, r2_ size correct.
+  DCHECK_EQ(r2_ - r1_, r5_ - r2_);
+  // r3_ left of r4_, r5_ left of r0_ and r3_ size correct.
+  DCHECK_EQ(r4_ - r3_, r5_ - r0_);
+  // r3_, r4_ size correct and r4_ at the end of the buffer.
+  DCHECK_EQ(r4_ + (r4_ - r3_), r1_ + kBufferSize * channel_count_);
+  // r5_ size correct and at the end of the buffer.
+  DCHECK_EQ(r5_ + kBlockSize * channel_count_,
+            r1_ + kBufferSize * channel_count_);
+
+  memset(kernel_storage_.get(), 0,
+         sizeof(*kernel_storage_.get()) * kKernelStorageSize);
+  memset(input_buffer_.get(), 0, frame_size_in_bytes_ * kBufferSize);
+
+  InitializeKernel();
+}
+
+void InterleavedSincResampler::InitializeKernel() {
+  // Blackman window parameters.
+  static const double kAlpha = 0.16;
+  static const double kA0 = 0.5 * (1.0 - kAlpha);
+  static const double kA1 = 0.5;
+  static const double kA2 = 0.5 * kAlpha;
+
+  // |sinc_scale_factor| is basically the normalized cutoff frequency of the
+  // low-pass filter.
+  double sinc_scale_factor =
+      io_sample_rate_ratio_ > 1.0 ? 1.0 / io_sample_rate_ratio_ : 1.0;
+
+  // The sinc function is an idealized brick-wall filter, but since we're
+  // windowing it the transition from pass to stop does not happen right away.
+  // So we should adjust the low pass filter cutoff slightly downward to avoid
+  // some aliasing at the very high-end.
+  // TODO(crogers): this value is empirical and to be more exact should vary
+  // depending on kKernelSize.
+  sinc_scale_factor *= 0.9;
+
+  // Generates a set of windowed sinc() kernels.
+  // We generate a range of sub-sample offsets from 0.0 to 1.0.
+  for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
+    double subsample_offset =
+        static_cast<double>(offset_idx) / kKernelOffsetCount;
+
+    for (int i = 0; i < kKernelSize; ++i) {
+      // Compute the sinc with offset.
+      double s =
+          sinc_scale_factor * M_PI * (i - kKernelSize / 2 - subsample_offset);
+      double sinc = (!s ? 1.0 : sin(s) / s) * sinc_scale_factor;
+
+      // Compute Blackman window, matching the offset of the sinc().
+      double x = (i - subsample_offset) / kKernelSize;
+      double window =
+          kA0 - kA1 * cos(2.0 * M_PI * x) + kA2 * cos(4.0 * M_PI * x);
+
+      // Window the sinc() function and store at the correct offset.
+      kernel_storage_.get()[i + offset_idx * kKernelSize] = sinc * window;
+    }
+  }
+}
+
+void InterleavedSincResampler::QueueBuffer(
+    const scoped_refptr<Buffer>& buffer) {
+  DCHECK(buffer);
+  DCHECK(CanQueueBuffer());
+
+  if (!pending_buffers_.empty() && pending_buffers_.back()->IsEndOfStream()) {
+    DCHECK(buffer->IsEndOfStream());
+    return;
+  }
+
+  if (!buffer->IsEndOfStream()) {
+    frames_queued_ += buffer->GetDataSize() / frame_size_in_bytes_;
+  }
+
+  pending_buffers_.push(buffer);
+}
+
+bool InterleavedSincResampler::Resample(float* destination, int frames) {
+  if (!HasEnoughData(frames)) {
+    return false;
+  }
+
+  int remaining_frames = frames;
+
+  // Step (1) -- Prime the input buffer at the start of the input stream.
+  if (!buffer_primed_) {
+    Read(r0_, kBlockSize + kKernelSize / 2);
+    buffer_primed_ = true;
+  }
+
+  // Step (2) -- Resample!
+  while (remaining_frames) {
+    while (virtual_source_idx_ < kBlockSize) {
+      // |virtual_source_idx_| lies in between two kernel offsets so figure out
+      // what they are.
+      int source_idx = static_cast<int>(virtual_source_idx_);
+      double subsample_remainder = virtual_source_idx_ - source_idx;
+
+      double virtual_offset_idx = subsample_remainder * kKernelOffsetCount;
+      int offset_idx = static_cast<int>(virtual_offset_idx);
+
+      // We'll compute "convolutions" for the two kernels which straddle
+      // |virtual_source_idx_|.
+      float* k1 = kernel_storage_.get() + offset_idx * kKernelSize;
+      float* k2 = k1 + kKernelSize;
+
+      // Initialize input pointer based on quantized |virtual_source_idx_|.
+      float* input_ptr = r1_ + source_idx * channel_count_;
+
+      // Figure out how much to weight each kernel's "convolution".
+      double kernel_interpolation_factor = virtual_offset_idx - offset_idx;
+      for (int i = 0; i < channel_count_; ++i) {
+        *destination++ =
+            Convolve(input_ptr + i, k1, k2, kernel_interpolation_factor);
+      }
+
+      // Advance the virtual index.
+      virtual_source_idx_ += io_sample_rate_ratio_;
+
+      if (!--remaining_frames) {
+        frames_resampled_ += frames;
+        return true;
+      }
+    }
+
+    // Wrap back around to the start.
+    virtual_source_idx_ -= kBlockSize;
+
+    // Step (3) Copy r3_ to r1_ and r4_ to r2_.
+    // This wraps the last input frames back to the start of the buffer.
+    memcpy(r1_, r3_, frame_size_in_bytes_ * (kKernelSize / 2));
+    memcpy(r2_, r4_, frame_size_in_bytes_ * (kKernelSize / 2));
+
+    // Step (4)
+    // Refresh the buffer with more input.
+    Read(r5_, kBlockSize);
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+void InterleavedSincResampler::Flush() {
+  virtual_source_idx_ = 0;
+  buffer_primed_ = false;
+  memset(input_buffer_.get(), 0, frame_size_in_bytes_ * kBufferSize);
+  while (!pending_buffers_.empty()) {
+    pending_buffers_.pop();
+  }
+  offset_in_frames_ = 0;
+  frames_resampled_ = 0;
+  frames_queued_ = 0;
+}
+
+bool InterleavedSincResampler::CanQueueBuffer() const {
+  if (pending_buffers_.empty()) {
+    return true;
+  }
+  if (pending_buffers_.back()->IsEndOfStream()) {
+    return false;
+  }
+  return pending_buffers_.size() < kMaximumPendingBuffers;
+}
+
+bool InterleavedSincResampler::ReachedEOS() const {
+  if (pending_buffers_.empty() || !pending_buffers_.back()->IsEndOfStream()) {
+    return false;
+  }
+  return frames_resampled_ * io_sample_rate_ratio_ >= frames_queued_;
+}
+
+bool InterleavedSincResampler::HasEnoughData(int frames_to_resample) const {
+  // Always return true if EOS is seen, as in this case we will just fill 0.
+  if (!pending_buffers_.empty() && pending_buffers_.back()->IsEndOfStream()) {
+    return true;
+  }
+
+  // We have to decrease frames_queued_ down as the Read()s are always done in
+  // blocks of kBlockSize or kBufferSize. We have to ensure that there is buffer
+  // for an extra Read().
+  return (frames_resampled_ + frames_to_resample) * io_sample_rate_ratio_ <
+         frames_queued_ - kBufferSize;
+}
+
+void InterleavedSincResampler::Read(float* destination, int frames) {
+  while (frames > 0 && !pending_buffers_.empty()) {
+    scoped_refptr<Buffer> buffer = pending_buffers_.front();
+    if (buffer->IsEndOfStream()) {
+      // Zero fill the buffer after EOS has reached.
+      memset(destination, 0, frame_size_in_bytes_ * frames);
+      return;
+    }
+    // Copy the data over.
+    int frames_in_buffer = buffer->GetDataSize() / frame_size_in_bytes_;
+    int frames_to_copy = std::min(frames_in_buffer - offset_in_frames_, frames);
+    const uint8* source = buffer->GetData();
+    source += frame_size_in_bytes_ * offset_in_frames_;
+    memcpy(destination, source, frame_size_in_bytes_ * frames_to_copy);
+    offset_in_frames_ += frames_to_copy;
+    // Pop the first buffer if all its content has been read.
+    if (offset_in_frames_ == frames_in_buffer) {
+      offset_in_frames_ = 0;
+      pending_buffers_.pop();
+    }
+    frames -= frames_to_copy;
+    destination += frames_to_copy * channel_count_;
+  }
+
+  // Read should always be satisfied as otherwise Resample should return false
+  // to the caller directly.
+  DCHECK_EQ(frames, 0);
+}
+
+float InterleavedSincResampler::Convolve(const float* input_ptr,
+                                         const float* k1,
+                                         const float* k2,
+                                         double kernel_interpolation_factor) {
+  float sum1 = 0;
+  float sum2 = 0;
+
+  // Generate a single output sample.  Unrolling this loop hurt performance in
+  // local testing.
+  int n = kKernelSize;
+  while (n--) {
+    sum1 += *input_ptr * *k1++;
+    sum2 += *input_ptr * *k2++;
+    input_ptr += channel_count_;
+  }
+
+  // Linearly interpolate the two "convolutions".
+  return (1.0 - kernel_interpolation_factor) * sum1 +
+         kernel_interpolation_factor * sum2;
+}
+
+}  // namespace media
diff --git a/src/starboard/shared/starboard/media/interleaved_sinc_resampler.h b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.h
new file mode 100644
index 0000000..edfdada
--- /dev/null
+++ b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2015 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 MEDIA_BASE_INTERLEAVED_SINC_RESAMPLER_H_
+#define MEDIA_BASE_INTERLEAVED_SINC_RESAMPLER_H_
+
+#include <queue>
+
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/buffers.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// InterleavedSincResampler is a high-quality interleaved multi-channel sample
+//-rate converter operating on samples in float. It uses the same algorithm as
+// SincResampler. Unlike SincResampler, it works in push mode instead of pull
+// mode.
+class MEDIA_EXPORT InterleavedSincResampler {
+ public:
+  // |io_sample_rate_ratio| is the ratio of input / output sample rates.
+  // |channel_count| is the number of channels in the interleaved audio stream.
+  InterleavedSincResampler(double io_sample_rate_ratio, int channel_count);
+
+  // Append a buffer to the queue. The samples in the buffer has to be floats.
+  void QueueBuffer(const scoped_refptr<Buffer>& buffer);
+
+  // Resample |frames| of data from enqueued buffers.  Return false if no sample
+  // is read.  Return true if all requested samples have been written into
+  // |destination|.  It will never do a partial read.  After the stream reaches
+  // the end, the function will fill the rest of buffer with 0.
+  bool Resample(float* destination, int frames);
+
+  // Flush all buffered data and reset internal indices.
+  void Flush();
+
+  // Return false if we shouldn't queue more buffers to the resampler.
+  bool CanQueueBuffer() const;
+
+  // Returning true when we start to return zero filled data because of EOS.
+  bool ReachedEOS() const;
+
+ private:
+  void InitializeKernel();
+  bool HasEnoughData(int frames_to_resample) const;
+  void Read(float* destination, int frames);
+
+  float Convolve(const float* input_ptr,
+                 const float* k1,
+                 const float* k2,
+                 double kernel_interpolation_factor);
+
+  // The ratio of input / output sample rates.
+  double io_sample_rate_ratio_;
+
+  // An index on the source input buffer with sub-sample precision.  It must be
+  // double precision to avoid drift.
+  double virtual_source_idx_;
+
+  // The buffer is primed once at the very beginning of processing.
+  bool buffer_primed_;
+
+  // Number of audio channels.
+  int channel_count_;
+
+  // The size of bytes for an audio frame.
+  const int frame_size_in_bytes_;
+
+  // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize.
+  // The kernel offsets are sub-sample shifts of a windowed sinc shifted from
+  // 0.0 to 1.0 sample.
+  scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> kernel_storage_;
+
+  // Data from the source is copied into this buffer for each processing pass.
+  scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_buffer_;
+
+  // A queue of buffers to be resampled.
+  std::queue<scoped_refptr<Buffer> > pending_buffers_;
+
+  // The current offset to read when reading from the first pending buffer.
+  int offset_in_frames_;
+
+  // The following two variables are used to calculate EOS and in HasEnoughData.
+  int frames_resampled_;
+  int frames_queued_;
+
+  // Pointers to the various regions inside |input_buffer_|.  See the diagram at
+  // the top of the .cc file for more information.
+  float* const r0_;
+  float* const r1_;
+  float* const r2_;
+  float* const r3_;
+  float* const r4_;
+  float* const r5_;
+
+  DISALLOW_COPY_AND_ASSIGN(InterleavedSincResampler);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_INTERLEAVED_SINC_RESAMPLER_H_
diff --git a/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc b/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
index 50e64a0..390abe4 100644
--- a/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
+++ b/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
@@ -19,6 +19,8 @@
 #include "starboard/shared/starboard/media/mime_type.h"
 #include "starboard/string.h"
 
+#if SB_API_VERSION < 4
+
 using starboard::shared::starboard::media::MimeType;
 
 namespace {
@@ -86,3 +88,213 @@
 #endif  // SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
   return kSbMediaSupportTypeNotSupported;
 }
+
+#else  // SB_API_VERSION < 4
+
+#include "starboard/shared/starboard/media/codec_util.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/media/media_util.h"
+
+using starboard::shared::starboard::media::GetAudioCodecFromString;
+using starboard::shared::starboard::media::GetVideoCodecFromString;
+using starboard::shared::starboard::media::IsAudioOutputSupported;
+using starboard::shared::starboard::media::MimeType;
+using starboard::shared::starboard::media::ParseH264Info;
+
+namespace {
+
+const int64_t kDefaultBitRate = 0;
+const int64_t kDefaultAudioChannels = 2;
+
+// A progressive video contains both audio and video data.  The JS app uses
+// canPlayType() like "canPlayType(video/mp4; codecs="avc1.42001E, mp4a.40.2",)"
+// to query for support on both the audio and video codecs, which is being
+// forwarded to here.
+//
+// Note that canPlayType() doesn't support extra parameters like width, height
+// and channels.
+SbMediaSupportType CanPlayProgressiveVideo(const MimeType& mime_type) {
+  const std::vector<std::string>& codecs = mime_type.GetCodecs();
+
+  SB_DCHECK(codecs.size() == 2) << codecs.size();
+
+  bool has_audio_codec = false;
+  bool has_video_codec = false;
+  for (size_t i = 0; i < codecs.size(); ++i) {
+    SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codecs[i].c_str());
+    if (audio_codec != kSbMediaAudioCodecNone) {
+      if (!SbMediaIsAudioSupported(audio_codec, kDefaultBitRate)) {
+        return kSbMediaSupportTypeNotSupported;
+      }
+      has_audio_codec = true;
+      continue;
+    }
+    SbMediaVideoCodec video_codec = GetVideoCodecFromString(codecs[i].c_str());
+    if (video_codec == kSbMediaVideoCodecNone) {
+      return kSbMediaSupportTypeNotSupported;
+    }
+
+    has_video_codec = true;
+
+    int width = 0;
+    int height = 0;
+    int fps = 0;
+
+    if (video_codec == kSbMediaVideoCodecH264) {
+      if (!ParseH264Info(codecs[i].c_str(), &width, &height, &fps)) {
+        return kSbMediaSupportTypeNotSupported;
+      }
+    }
+    if (!SbMediaIsVideoSupported(video_codec, width, height, kDefaultBitRate,
+                                 fps)) {
+      return kSbMediaSupportTypeNotSupported;
+    }
+  }
+
+  if (has_audio_codec && has_video_codec) {
+    return kSbMediaSupportTypeProbably;
+  }
+
+  return kSbMediaSupportTypeNotSupported;
+}
+
+// Calls to isTypeSupported() are redirected to this function.  Following are
+// some example inputs:
+//   isTypeSupported(video/webm; codecs="vp9")
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=640)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=99999)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=360)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=99999)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=30)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=9999)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=300000)
+//   isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=2000000000)
+//   isTypeSupported(audio/mp4; codecs="mp4a.40.2")
+//   isTypeSupported(audio/webm; codecs="vorbis")
+//   isTypeSupported(video/webm; codecs="vp9")
+//   isTypeSupported(video/webm; codecs="vp9")
+//   isTypeSupported(audio/webm; codecs="opus")
+//   isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=2)
+//   isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=99)
+SbMediaSupportType CanPlayMimeAndKeySystem(const MimeType& mime_type,
+                                           const char* key_system) {
+  SB_DCHECK(mime_type.is_valid());
+
+  const std::vector<std::string>& codecs = mime_type.GetCodecs();
+
+  // Pre-filter for |key_system|.
+  if (SbStringGetLength(key_system) != 0) {
+    if (!SbMediaIsSupported(kSbMediaVideoCodecNone, kSbMediaAudioCodecNone,
+                            key_system)) {
+      return kSbMediaSupportTypeNotSupported;
+    }
+  }
+
+  if (codecs.size() == 0) {
+    // When there is no codecs listed, returns |kSbMediaSupportTypeMaybe| and
+    // reject unsupported formats when query again with valid "codecs".
+    return kSbMediaSupportTypeMaybe;
+  }
+
+  if (codecs.size() > 2) {
+    return kSbMediaSupportTypeNotSupported;
+  }
+
+  if (codecs.size() == 2) {
+    SB_DCHECK(SbStringGetLength(key_system) == 0);
+    return CanPlayProgressiveVideo(mime_type);
+  }
+
+  SB_DCHECK(codecs.size() == 1);
+
+  if (mime_type.type() == "audio") {
+    SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codecs[0].c_str());
+    if (audio_codec == kSbMediaAudioCodecNone) {
+      return kSbMediaSupportTypeNotSupported;
+    }
+
+    if (SbStringGetLength(key_system) != 0) {
+      if (!SbMediaIsSupported(kSbMediaVideoCodecNone, audio_codec,
+                              key_system)) {
+        return kSbMediaSupportTypeNotSupported;
+      }
+    }
+
+    int channels =
+        mime_type.GetParamIntValue("channels", kDefaultAudioChannels);
+    if (!IsAudioOutputSupported(kSbMediaAudioCodingTypePcm, channels)) {
+      return kSbMediaSupportTypeNotSupported;
+    }
+
+    int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
+
+    if (SbMediaIsAudioSupported(audio_codec, bitrate)) {
+      return kSbMediaSupportTypeProbably;
+    }
+
+    return kSbMediaSupportTypeNotSupported;
+  }
+
+  if (mime_type.type() == "video") {
+    SbMediaVideoCodec video_codec = GetVideoCodecFromString(codecs[0].c_str());
+    if (video_codec == kSbMediaVideoCodecNone) {
+      return kSbMediaSupportTypeNotSupported;
+    }
+
+    if (SbStringGetLength(key_system) != 0) {
+      if (!SbMediaIsSupported(video_codec, kSbMediaAudioCodecNone,
+                              key_system)) {
+        return kSbMediaSupportTypeNotSupported;
+      }
+    }
+
+    int width = 0;
+    int height = 0;
+    int fps = 0;
+
+    if (video_codec == kSbMediaVideoCodecH264) {
+      if (!ParseH264Info(codecs[0].c_str(), &width, &height, &fps)) {
+        return kSbMediaSupportTypeNotSupported;
+      }
+    }
+
+    width = mime_type.GetParamIntValue("width", width);
+    height = mime_type.GetParamIntValue("height", height);
+    fps = mime_type.GetParamIntValue("framerate", fps);
+
+    int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
+
+    if (SbMediaIsVideoSupported(video_codec, width, height, bitrate, fps)) {
+      return kSbMediaSupportTypeProbably;
+    }
+
+    return kSbMediaSupportTypeNotSupported;
+  }
+
+  return kSbMediaSupportTypeNotSupported;
+}
+
+}  // namespace
+
+SbMediaSupportType SbMediaCanPlayMimeAndKeySystem(const char* mime,
+                                                  const char* key_system) {
+  if (mime == NULL) {
+    SB_DLOG(WARNING) << "mime cannot be NULL";
+    return kSbMediaSupportTypeNotSupported;
+  }
+
+  if (key_system == NULL) {
+    SB_DLOG(WARNING) << "key_system cannot be NULL";
+    return kSbMediaSupportTypeNotSupported;
+  }
+
+  MimeType mime_type(mime);
+  if (!mime_type.is_valid()) {
+    SB_DLOG(WARNING) << mime << " is not a valid mime type";
+    return kSbMediaSupportTypeNotSupported;
+  }
+
+  return CanPlayMimeAndKeySystem(mime_type, key_system);
+}
+
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
new file mode 100644
index 0000000..e09901d
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+                                       int64_t bitrate) {
+  if (audio_codec != kSbMediaAudioCodecAac &&
+      audio_codec != kSbMediaAudioCodecOpus) {
+    return false;
+  }
+  return bitrate <= SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
+}
diff --git a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
new file mode 100644
index 0000000..996b4d4
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+                                       int64_t bitrate) {
+  return audio_codec == kSbMediaAudioCodecAac &&
+         bitrate <= SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
+}
diff --git a/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc
new file mode 100644
index 0000000..a820091
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+                                       int frame_width,
+                                       int frame_height,
+                                       int64_t bitrate,
+                                       int fps) {
+  return video_codec == kSbMediaVideoCodecH264 && frame_width <= 1920 &&
+         frame_height <= 1080 &&
+         bitrate <= SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND && fps <= 60;
+}
diff --git a/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc
new file mode 100644
index 0000000..637e835
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+                                       int frame_width,
+                                       int frame_height,
+                                       int64_t bitrate,
+                                       int fps) {
+  return video_codec == kSbMediaVideoCodecH264 && frame_width <= 1920 &&
+         frame_height <= 1080 &&
+         bitrate <= SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND && fps <= 30;
+}
diff --git a/src/starboard/shared/starboard/media/media_support_internal.h b/src/starboard/shared/starboard/media/media_support_internal.h
new file mode 100644
index 0000000..8ad5b21
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_support_internal.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+#if SB_API_VERSION < 4
+#error "Incorrect SB_API_VERSION to include media_support_internal.h"
+#endif  // SB_API_VERSION < 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 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|.
+//
+// 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);
+
+// 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(SbMediaAudioCodec audio_codec,
+                                       int64_t bitrate);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
new file mode 100644
index 0000000..b6fd9d7
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_util.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) {
+  int count = SbMediaGetAudioOutputCount();
+
+  for (int output_index = 0; output_index < count; ++output_index) {
+    SbMediaAudioConfiguration configuration;
+    if (!SbMediaGetAudioConfiguration(output_index, &configuration)) {
+      continue;
+    }
+
+    if (configuration.coding_type == coding_type &&
+        configuration.number_of_channels >= channels) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/media/media_util.h b/src/starboard/shared/starboard/media/media_util.h
new file mode 100644
index 0000000..293a34e
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_util.h
@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels);
+
+}  // namespace media
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
diff --git a/src/starboard/shared/starboard/media/mime_type.cc b/src/starboard/shared/starboard/media/mime_type.cc
index 50da87a..4986d87 100644
--- a/src/starboard/shared/starboard/media/mime_type.cc
+++ b/src/starboard/shared/starboard/media/mime_type.cc
@@ -136,12 +136,16 @@
   is_valid_ = true;
 }
 
-std::vector<std::string> MimeType::GetCodecs() const {
+const std::vector<std::string>& MimeType::GetCodecs() const {
+  if (!codecs_.empty()) {
+    return codecs_;
+  }
   int codecs_index = GetParamIndexByName("codecs");
   if (codecs_index != 0) {
-    return std::vector<std::string>();
+    return codecs_;
   }
-  return SplitAndTrim(params_[0].value, ',');
+  codecs_ = SplitAndTrim(params_[0].value, ',');
+  return codecs_;
 }
 
 int MimeType::GetParamCount() const {
diff --git a/src/starboard/shared/starboard/media/mime_type.h b/src/starboard/shared/starboard/media/mime_type.h
index 21ac4ef..fc3a2cd 100644
--- a/src/starboard/shared/starboard/media/mime_type.h
+++ b/src/starboard/shared/starboard/media/mime_type.h
@@ -59,7 +59,7 @@
   const std::string& type() const { return type_; }
   const std::string& subtype() const { return subtype_; }
 
-  std::vector<std::string> GetCodecs() const;
+  const std::vector<std::string>& GetCodecs() const;
 
   int GetParamCount() const;
   ParamType GetParamType(int index) const;
@@ -92,6 +92,7 @@
   std::string type_;
   std::string subtype_;
   Params params_;
+  mutable std::vector<std::string> codecs_;
 };
 
 }  // namespace media
diff --git a/src/starboard/shared/starboard/new.cc b/src/starboard/shared/starboard/new.cc
index 7e08ed0..478a74c 100644
--- a/src/starboard/shared/starboard/new.cc
+++ b/src/starboard/shared/starboard/new.cc
@@ -12,23 +12,4 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// TODO: I believe this code will have to be linked into all DLLs on
-// Windows. I think Starboard can do this through GYP when the time comes.
-
-#include "starboard/memory.h"
-
-void* operator new(size_t size) {
-  return SbMemoryAllocate(size);
-}
-
-void operator delete(void* pointer) {
-  SbMemoryDeallocate(pointer);
-}
-
-void* operator new[](size_t size) {
-  return SbMemoryAllocate(size);
-}
-
-void operator delete[](void* pointer) {
-  SbMemoryDeallocate(pointer);
-}
+// TODO: Remove this file. The code has been moved to starboard/common/new.cc
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index ac98df9..13b9db4 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -136,7 +136,7 @@
   paused_ = true;
 }
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
 void AudioRendererImpl::SetPlaybackRate(double playback_rate) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
@@ -146,7 +146,7 @@
     audio_sink_->SetPlaybackRate(playback_rate);
   }
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
 void AudioRendererImpl::Seek(SbMediaTime seek_to_pts) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
@@ -308,9 +308,9 @@
         reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
         kMaxCachedFrames, &AudioRendererImpl::UpdateSourceStatusFunc,
         &AudioRendererImpl::ConsumeFramesFunc, this);
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
     audio_sink_->SetPlaybackRate(playback_rate_);
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   }
 }
 
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index 78dff81..70915f6 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -51,9 +51,9 @@
 
   void Play() SB_OVERRIDE;
   void Pause() SB_OVERRIDE;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate) SB_OVERRIDE;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   void Seek(SbMediaTime seek_to_pts) SB_OVERRIDE;
 
   bool IsEndOfStreamWritten() const SB_OVERRIDE {
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index abae295..816c959 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -36,9 +36,9 @@
   virtual void WriteEndOfStream() = 0;
   virtual void Play() = 0;
   virtual void Pause() = 0;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   virtual void SetPlaybackRate(double playback_rate) = 0;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   virtual void Seek(SbMediaTime seek_to_pts) = 0;
   virtual bool IsEndOfStreamWritten() const = 0;
   virtual bool IsEndOfStreamPlayed() const = 0;
diff --git a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
index 2830d2b..e5c1529 100644
--- a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
@@ -50,8 +50,8 @@
       new AudioRendererImpl(audio_parameters.job_queue,
                             scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
                             audio_parameters.audio_header);
-  VideoRendererImpl* video_renderer =
-      new VideoRendererImpl(scoped_ptr<VideoDecoder>(video_decoder).Pass());
+  VideoRendererImpl* video_renderer = new VideoRendererImpl(
+      scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
 
   return scoped_ptr<PlayerComponents>(
       new PlayerComponents(audio_renderer, video_renderer));
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index b27bb8c..9e0bbe4 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -42,14 +42,14 @@
     SbMediaAudioCodec audio_codec,
     SbDrmSystem drm_system,
     const SbMediaAudioHeader& audio_header
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
     ,
-    SbPlayerOutputMode output_mode
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+    SbPlayerOutputMode output_mode,
+    SbDecodeTargetGraphicsContextProvider* provider
+#elif SB_API_VERSION >= 3
     ,
     SbDecodeTargetProvider* provider
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
     )
     : player_worker_(NULL),
       job_queue_(NULL),
@@ -62,14 +62,14 @@
       drm_system_(drm_system),
       audio_header_(audio_header),
       paused_(false)
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
       ,
-      output_mode_(output_mode)
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+      output_mode_(output_mode),
+      decode_target_graphics_context_provider_(provider)
+#elif SB_API_VERSION >= 3
       ,
       decode_target_provider_(provider)
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
 {
   bounds_ = PlayerWorker::Bounds();
 }
@@ -106,14 +106,13 @@
     video_codec_,
     drm_system_,
     job_queue_
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
     ,
     output_mode_,
-    decode_target_provider_
-#endif    // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-  };      // NOLINT(whitespace/parens)
+    decode_target_graphics_context_provider_
+#endif  // SB_API_VERSION >= 4
+  };    // NOLINT(whitespace/parens)
 
-  ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
   scoped_ptr<PlayerComponents> media_components =
       PlayerComponents::Create(audio_parameters, video_parameters);
   if (!media_components) {
@@ -121,6 +120,7 @@
   }
   SB_DCHECK(media_components->is_valid());
 
+  ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
   media_components->GetRenderers(&audio_renderer_, &video_renderer_);
 
   update_closure_ = Bind(&FilterBasedPlayerWorkerHandler::Update, this);
@@ -233,14 +233,14 @@
   return true;
 }
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
 bool FilterBasedPlayerWorkerHandler::SetPlaybackRate(double playback_rate) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
   audio_renderer_->SetPlaybackRate(playback_rate);
   return true;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
 bool FilterBasedPlayerWorkerHandler::SetBounds(
     const PlayerWorker::Bounds& bounds) {
@@ -281,9 +281,9 @@
     player_worker_->UpdateDroppedVideoFrames(
         video_renderer_->GetDroppedFrames());
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
     if (output_mode_ == kSbPlayerOutputModePunchOut)
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
     {
       shared::starboard::Application::Get()->HandleFrame(
           player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
@@ -299,14 +299,21 @@
   job_queue_->Remove(update_closure_);
 
   audio_renderer_.reset();
-  {
-    ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
-    video_renderer_.reset();
-  }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  scoped_ptr<VideoRenderer> video_renderer;
+  {
+    // Set |video_renderer_| to null with the lock, but we actually destroy
+    // it outside of the lock.  This is because the VideoRenderer destructor
+    // may post a task to destroy the SbDecodeTarget to the same thread that
+    // might call GetCurrentDecodeTarget(), which would try to take this lock.
+    ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
+    video_renderer = video_renderer_.Pass();
+  }
+  video_renderer.reset();
+
+#if SB_API_VERSION >= 4
   if (output_mode_ == kSbPlayerOutputModePunchOut)
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
   {
     // Clear the video frame as we terminate.
     shared::starboard::Application::Get()->HandleFrame(
@@ -314,7 +321,7 @@
   }
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 SbDecodeTarget FilterBasedPlayerWorkerHandler::GetCurrentDecodeTarget() {
   ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
   if (video_renderer_) {
@@ -323,7 +330,7 @@
     return kSbDecodeTargetInvalid;
   }
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace filter
 }  // namespace player
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index b0bf86a..3024054 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -42,14 +42,14 @@
                                  SbMediaAudioCodec audio_codec,
                                  SbDrmSystem drm_system,
                                  const SbMediaAudioHeader& audio_header
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
                                  ,
-                                 SbPlayerOutputMode output_mode
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+                                 SbPlayerOutputMode output_mode,
+                                 SbDecodeTargetGraphicsContextProvider* provider
+#elif SB_API_VERSION >= 3
                                  ,
                                  SbDecodeTargetProvider* provider
-#endif  // SB_API_VERSION >= 3
+#endif                               // SB_API_VERSION >= 4
                                  );  // NOLINT(whitespace/parens)
 
  private:
@@ -63,17 +63,17 @@
   bool WriteSample(InputBuffer input_buffer, bool* written) SB_OVERRIDE;
   bool WriteEndOfStream(SbMediaType sample_type) SB_OVERRIDE;
   bool SetPause(bool pause) SB_OVERRIDE;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   bool SetPlaybackRate(double playback_rate) SB_OVERRIDE;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   bool SetBounds(const PlayerWorker::Bounds& bounds) SB_OVERRIDE;
   void Stop() SB_OVERRIDE;
 
   void Update();
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   PlayerWorker* player_worker_;
   JobQueue* job_queue_;
@@ -104,12 +104,13 @@
   // accesses are happening from the same thread.
   ::starboard::Mutex video_renderer_existence_mutex_;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbPlayerOutputMode output_mode_;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+  SbDecodeTargetGraphicsContextProvider*
+      decode_target_graphics_context_provider_;
+#elif SB_API_VERSION >= 3
   SbDecodeTargetProvider* decode_target_provider_;
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index d883751..cf2a60d 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -38,10 +38,11 @@
   SbMediaVideoCodec video_codec;
   SbDrmSystem drm_system;
   JobQueue* job_queue;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbPlayerOutputMode output_mode;
-  SbDecodeTargetProvider* decode_target_provider;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  SbDecodeTargetGraphicsContextProvider*
+      decode_target_graphics_context_provider;
+#endif  // SB_API_VERSION >= 4
 };
 
 // All the platform specific components that a
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
index 91e09cb..a5cddd7 100644
--- a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
@@ -14,18 +14,155 @@
 
 #include "starboard/shared/starboard/player/filter/player_components.h"
 
+#include <queue>
+
+#include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h"
+
 namespace starboard {
 namespace shared {
 namespace starboard {
 namespace player {
 namespace filter {
 
+namespace {
+
+SbMediaAudioSampleType GetSupportedSampleType() {
+  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+    return kSbMediaAudioSampleTypeFloat32;
+  }
+  SB_DCHECK(
+      SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16));
+  return kSbMediaAudioSampleTypeInt16;
+}
+
+}  // namespace
+
+class StubAudioDecoder : public AudioDecoder {
+ public:
+  explicit StubAudioDecoder(const SbMediaAudioHeader& audio_header)
+      : sample_type_(GetSupportedSampleType()), audio_header_(audio_header) {}
+  void Decode(const InputBuffer& input_buffer) SB_OVERRIDE {
+    // Values to represent what kind of dummy audio to fill the decoded audio
+    // we produce with.
+    enum FillType {
+      kSilence,
+      kWave,
+    };
+    // Can be set locally to fill with different types.
+    const FillType fill_type = kSilence;
+
+    if (last_input_buffer_.is_valid()) {
+      SbMediaTime diff = input_buffer.pts() - last_input_buffer_.pts();
+      size_t sample_size =
+          GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4;
+      size_t size = diff * GetSamplesPerSecond() * sample_size *
+                    audio_header_.number_of_channels / kSbMediaTimeSecond;
+      decoded_audios_.push(new DecodedAudio(input_buffer.pts(), size));
+
+      if (fill_type == kSilence) {
+        SbMemorySet(decoded_audios_.back()->buffer(), 0, size);
+      } else {
+        SB_DCHECK(fill_type == kWave);
+        for (int i = 0; i < size / sample_size; ++i) {
+          if (sample_size == 2) {
+            *(reinterpret_cast<int16_t*>(decoded_audios_.back()->buffer()) +
+              i) = i;
+          } else {
+            SB_DCHECK(sample_size == 4);
+            *(reinterpret_cast<float*>(decoded_audios_.back()->buffer()) + i) =
+                ((i % 1024) - 512) / 512.0f;
+          }
+        }
+      }
+    }
+    last_input_buffer_ = input_buffer;
+  }
+  void WriteEndOfStream() SB_OVERRIDE {
+    if (last_input_buffer_.is_valid()) {
+      // There won't be a next pts, so just guess that the decoded size is
+      // 4 times the encoded size.
+      decoded_audios_.push(new DecodedAudio(last_input_buffer_.pts(),
+                                            4 * last_input_buffer_.size()));
+    }
+    decoded_audios_.push(new DecodedAudio());
+  }
+  scoped_refptr<DecodedAudio> Read() SB_OVERRIDE {
+    scoped_refptr<DecodedAudio> result;
+    if (!decoded_audios_.empty()) {
+      result = decoded_audios_.front();
+      decoded_audios_.pop();
+    }
+    return result;
+  }
+  void Reset() SB_OVERRIDE {
+    while (!decoded_audios_.empty()) {
+      decoded_audios_.pop();
+    }
+    last_input_buffer_ = InputBuffer();
+  }
+  SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE {
+    return sample_type_;
+  }
+  int GetSamplesPerSecond() const SB_OVERRIDE {
+    return audio_header_.samples_per_second;
+  }
+
+ private:
+  SbMediaAudioSampleType sample_type_;
+  SbMediaAudioHeader audio_header_;
+  std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
+  InputBuffer last_input_buffer_;
+};
+
+class StubVideoDecoder : public HostedVideoDecoder {
+ public:
+  StubVideoDecoder() : host_(NULL) {}
+  void WriteInputBuffer(const InputBuffer& input_buffer) SB_OVERRIDE {
+    SB_DCHECK(host_ != NULL);
+    host_->OnDecoderStatusUpdate(
+        kNeedMoreInput, VideoFrame::CreateEmptyFrame(input_buffer.pts()));
+  }
+  void WriteEndOfStream() SB_OVERRIDE {
+    SB_DCHECK(host_ != NULL);
+    host_->OnDecoderStatusUpdate(kBufferFull, VideoFrame::CreateEOSFrame());
+  }
+  void Reset() SB_OVERRIDE {}
+  void SetHost(Host* host) {
+    SB_DCHECK(host != NULL);
+    SB_DCHECK(host_ == NULL);
+    host_ = host;
+  }
+
+ private:
+  Host* host_;
+};
+
+#if SB_API_VERSION >= 4
+// static
+bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
+                                       SbMediaVideoCodec codec,
+                                       SbDrmSystem drm_system) {
+  return output_mode == kSbPlayerOutputModePunchOut;
+}
+#endif  // SB_API_VERSION >= 4
+
 // static
 scoped_ptr<PlayerComponents> PlayerComponents::Create(
     const AudioParameters& audio_parameters,
     const VideoParameters& video_parameters) {
-  // TODO: Implement stubs that do something.
-  return scoped_ptr<PlayerComponents>(NULL);
+  StubAudioDecoder* audio_decoder =
+      new StubAudioDecoder(audio_parameters.audio_header);
+  StubVideoDecoder* video_decoder = new StubVideoDecoder();
+  AudioRendererImpl* audio_renderer =
+      new AudioRendererImpl(audio_parameters.job_queue,
+                            scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
+                            audio_parameters.audio_header);
+  VideoRendererImpl* video_renderer = new VideoRendererImpl(
+      scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
+
+  return scoped_ptr<PlayerComponents>(
+      new PlayerComponents(audio_renderer, video_renderer));
 }
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
index bd5b5f0..246ee6c 100644
--- a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
@@ -32,6 +32,45 @@
  public:
   enum Status { kNeedMoreInput, kBufferFull, kFatalError };
 
+  virtual ~VideoDecoder() {}
+
+  // Send encoded video frame stored in |input_buffer| to decode.
+  virtual void WriteInputBuffer(const InputBuffer& input_buffer) = 0;
+  // Note that there won't be more input data unless Reset() is called.
+  // OnDecoderStatusUpdate will still be called on Host during flushing until
+  // the |frame| is an EOS frame.
+  virtual void WriteEndOfStream() = 0;
+  // Clear any cached buffer of the codec and reset the state of the codec.
+  // This function will be called during seek to ensure that there is no left
+  // over data from previous buffers.  No DecoderStatusFunc call will be made
+  // after this function returns unless WriteInputFrame() or WriteEndOfStream()
+  // is called again.
+  virtual void Reset() = 0;
+
+#if SB_API_VERSION >= 3
+  // May be called from an arbitrary thread (e.g. a renderer thread).
+  virtual SbDecodeTarget GetCurrentDecodeTarget() {
+    return kSbDecodeTargetInvalid;
+  }
+#endif  // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+  // Individual implementations must implement this function to indicate which
+  // output modes they support.
+  static bool OutputModeSupported(SbPlayerOutputMode output_mode,
+                                  SbMediaVideoCodec codec,
+                                  SbDrmSystem drm_system);
+#endif  // SB_API_VERSION >= 3
+};
+
+// An extended |VideoDecoder| that is capable of providing |VideoFrame|s to
+// |Host| that owns it.  If the platform's video decoder implementation can
+// satisfy this interface, then the default video renderer implementation may
+// be used.  If not, then the platform will also likely require a video
+// renderer implementation that is somehow capable of receiving video frames
+// (through |VideoFrame| or possibly some other means).
+class HostedVideoDecoder : public VideoDecoder {
+ public:
   class Host {
    public:
     // |frame| can contain a decoded frame or be NULL when |status| is not
@@ -47,43 +86,7 @@
     ~Host() {}
   };
 
-  virtual ~VideoDecoder() {}
-
   virtual void SetHost(Host* host) = 0;
-
-  // Send encoded video frame stored in |input_buffer| to decode.
-  virtual void WriteInputBuffer(const InputBuffer& input_buffer) = 0;
-  // Note that there won't be more input data unless Reset() is called.
-  // OnDecoderStatusUpdate will still be called on Host during flushing until
-  // the |frame| is an EOS frame.
-  virtual void WriteEndOfStream() = 0;
-  // Clear any cached buffer of the codec and reset the state of the codec.
-  // This function will be called during seek to ensure that there is no left
-  // over data from previous buffers.  No DecoderStatusFunc call will be made
-  // after this function returns unless WriteInputFrame() or WriteEndOfStream()
-  // is called again.
-  virtual void Reset() = 0;
-
-  // In certain cases, a decoder may benefit from knowing the current time of
-  // the audio decoder that it is following.  This optional function provides
-  // the renderer that owns the decoder an opportunity to provide this
-  // information to the decoder.
-  virtual void SetCurrentTime(SbMediaTime current_time) {}
-
-#if SB_API_VERSION >= 3
-  // May be called from an arbitrary thread (e.g. a renderer thread).
-  virtual SbDecodeTarget GetCurrentDecodeTarget() {
-    return kSbDecodeTargetInvalid;
-  }
-#endif  // SB_API_VERSION >= 3
-
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-  // Individual implementations must implement this function to indicate which
-  // output modes they support.
-  static bool OutputModeSupported(SbPlayerOutputMode output_mode,
-                                  SbMediaVideoCodec codec,
-                                  SbDrmSystem drm_system);
-#endif  // SB_API_VERSION >= 3
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
index 6c09f90..810f5d2 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
@@ -22,7 +22,7 @@
 namespace player {
 namespace filter {
 
-VideoRendererImpl::VideoRendererImpl(scoped_ptr<VideoDecoder> decoder)
+VideoRendererImpl::VideoRendererImpl(scoped_ptr<HostedVideoDecoder> decoder)
     : seeking_(false),
       seeking_to_pts_(0),
       end_of_stream_written_(false),
@@ -81,8 +81,6 @@
     SbMediaTime media_time) {
   SB_DCHECK(thread_checker_.CalledOnValidThread());
 
-  decoder_->SetCurrentTime(media_time);
-
   if (frames_.empty()) {
     return last_displayed_frame_;
   }
@@ -142,7 +140,7 @@
   need_more_input_ = (status == VideoDecoder::kNeedMoreInput);
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 SbDecodeTarget VideoRendererImpl::GetCurrentDecodeTarget() {
   if (decoder_) {
     return decoder_->GetCurrentDecodeTarget();
@@ -150,7 +148,7 @@
     return kSbDecodeTargetInvalid;
   }
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace filter
 }  // namespace player
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
index c954ef0..e56051d 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
@@ -35,10 +35,12 @@
 namespace filter {
 
 // A default implementation of |VideoRenderer| that only depends on the
-// |VideoDecoder| interface, rather than a platform specific implementation.
-class VideoRendererImpl : public VideoRenderer {
+// |HostedVideoDecoder| interface, rather than a platform specific
+// implementation.
+class VideoRendererImpl : public VideoRenderer,
+                          private HostedVideoDecoder::Host {
  public:
-  explicit VideoRendererImpl(scoped_ptr<VideoDecoder> decoder);
+  explicit VideoRendererImpl(scoped_ptr<HostedVideoDecoder> decoder);
 
   int GetDroppedFrames() const SB_OVERRIDE { return dropped_frames_; }
 
@@ -56,9 +58,9 @@
   bool CanAcceptMoreData() const SB_OVERRIDE;
   bool IsSeekingInProgress() const SB_OVERRIDE;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
   typedef std::list<scoped_refptr<VideoFrame> > Frames;
@@ -94,7 +96,7 @@
   bool need_more_input_;
   int dropped_frames_;
 
-  scoped_ptr<VideoDecoder> decoder_;
+  scoped_ptr<HostedVideoDecoder> decoder_;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
index a409eb9..df3beef 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -31,7 +31,7 @@
 namespace player {
 namespace filter {
 
-class VideoRenderer : protected VideoDecoder::Host {
+class VideoRenderer {
  public:
   virtual ~VideoRenderer() {}
 
@@ -44,9 +44,9 @@
   virtual bool IsEndOfStreamPlayed() const = 0;
   virtual bool CanAcceptMoreData() const = 0;
   virtual bool IsSeekingInProgress() const = 0;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   virtual SbDecodeTarget GetCurrentDecodeTarget() = 0;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index 5b0e64c..5c83020 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -17,6 +17,9 @@
 #include "starboard/configuration.h"
 #include "starboard/decode_target.h"
 #include "starboard/log.h"
+#if SB_API_VERSION >= 4
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#endif  // SB_API_VERSION >= 4
 #include "starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 #include "starboard/shared/starboard/player/player_worker.h"
@@ -35,24 +38,36 @@
                         SbPlayerDecoderStatusFunc decoder_status_func,
                         SbPlayerStatusFunc player_status_func,
                         void* context
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
                         ,
-                        SbPlayerOutputMode output_mode
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+                        SbPlayerOutputMode output_mode,
+                        SbDecodeTargetGraphicsContextProvider* provider
+#elif SB_API_VERSION >= 3
                         ,
                         SbDecodeTargetProvider* provider
-#endif  // SB_API_VERSION >= 3
+#endif  // SB_API_VERSION >= 4
                         ) {
   SB_UNREFERENCED_PARAMETER(window);
 
-  if (audio_codec != kSbMediaAudioCodecAac) {
+#if SB_API_VERSION >= 4
+  const int64_t kDefaultBitRate = 0;
+  if (!SbMediaIsAudioSupported(audio_codec, kDefaultBitRate)) {
     SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
     return kSbPlayerInvalid;
   }
 
-  if (!audio_header) {
-    SB_LOG(ERROR) << "SbPlayerCreate() requires a non-NULL SbMediaAudioHeader";
+  const int kDefaultFrameWidth = 0;
+  const int kDefaultFrameHeight = 0;
+  const int kDefaultFrameRate = 0;
+  if (!SbMediaIsVideoSupported(video_codec, kDefaultFrameWidth,
+                               kDefaultFrameHeight, kDefaultBitRate,
+                               kDefaultFrameRate)) {
+    SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
+    return kSbPlayerInvalid;
+  }
+#else   // SB_API_VERSION >= 4
+  if (audio_codec != kSbMediaAudioCodecAac) {
+    SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
     return kSbPlayerInvalid;
   }
 
@@ -60,15 +75,21 @@
     SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
     return kSbPlayerInvalid;
   }
+#endif  // SB_API_VERSION >= 4
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+  if (!audio_header) {
+    SB_LOG(ERROR) << "SbPlayerCreate() requires a non-NULL SbMediaAudioHeader";
+    return kSbPlayerInvalid;
+  }
+
+#if SB_API_VERSION >= 4
   if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) {
     SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
     return kSbPlayerInvalid;
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   starboard::scoped_ptr<PlayerWorker::Handler> handler(
       new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
                                          *audio_header, output_mode, provider));
@@ -76,11 +97,11 @@
   starboard::scoped_ptr<PlayerWorker::Handler> handler(
       new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
                                          *audio_header, provider));
-#else   // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION >= 4
   starboard::scoped_ptr<PlayerWorker::Handler> handler(
       new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
                                          *audio_header));
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   return new SbPlayerPrivate(duration_pts, sample_deallocate_func,
                              decoder_status_func, player_status_func, context,
diff --git a/src/starboard/shared/starboard/player/player_get_current_frame.cc b/src/starboard/shared/starboard/player/player_get_current_frame.cc
index 6fca433..47eb30f 100644
--- a/src/starboard/shared/starboard/player/player_get_current_frame.cc
+++ b/src/starboard/shared/starboard/player/player_get_current_frame.cc
@@ -18,7 +18,7 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
 SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer player) {
   if (!SbPlayerIsValid(player)) {
@@ -29,4 +29,4 @@
   return player->GetCurrentDecodeTarget();
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index b3c104f..a64fed3 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -44,9 +44,9 @@
       frame_width_(0),
       frame_height_(0),
       is_paused_(true),
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
       playback_rate_(1.0),
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
       volume_(1.0),
       total_video_frames_(0),
       dropped_video_frames_(0),
@@ -114,21 +114,21 @@
   out_player_info->total_video_frames = total_video_frames_;
   out_player_info->dropped_video_frames = dropped_video_frames_;
   out_player_info->corrupted_video_frames = 0;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   out_player_info->playback_rate = playback_rate_;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 }
 
 void SbPlayerPrivate::SetPause(bool pause) {
   worker_->SetPause(pause);
 }
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
 void SbPlayerPrivate::SetPlaybackRate(double playback_rate) {
   playback_rate_ = playback_rate;
   worker_->SetPlaybackRate(playback_rate);
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
 void SbPlayerPrivate::SetVolume(double volume) {
   SB_NOTIMPLEMENTED();
@@ -148,8 +148,8 @@
   dropped_video_frames_ = dropped_video_frames;
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 SbDecodeTarget SbPlayerPrivate::GetCurrentDecodeTarget() {
   return worker_->GetCurrentDecodeTarget();
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index b0c8d27..d04d0f9 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -49,14 +49,14 @@
 
   void GetInfo(SbPlayerInfo* out_player_info);
   void SetPause(bool pause);
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate);
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   void SetVolume(double volume);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentDecodeTarget();
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
   // PlayerWorker::Host methods.
@@ -74,9 +74,9 @@
   int frame_width_;
   int frame_height_;
   bool is_paused_;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   double playback_rate_;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   double volume_;
   int total_video_frames_;
   int dropped_video_frames_;
diff --git a/src/starboard/shared/starboard/player/player_output_mode_supported.cc b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
index 4889ddf..4d34cce 100644
--- a/src/starboard/shared/starboard/player/player_output_mode_supported.cc
+++ b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
@@ -18,7 +18,7 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
 bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode,
                                  SbMediaVideoCodec codec,
@@ -27,4 +27,4 @@
       OutputModeSupported(output_mode, codec, drm_system);
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_set_bounds.cc b/src/starboard/shared/starboard/player/player_set_bounds.cc
index a2275ea..6974370 100644
--- a/src/starboard/shared/starboard/player/player_set_bounds.cc
+++ b/src/starboard/shared/starboard/player/player_set_bounds.cc
@@ -17,10 +17,16 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 
-void SbPlayerSetBounds(SbPlayer player, int x, int y, int width, int height) {
+void SbPlayerSetBounds(SbPlayer player,
+#if SB_API_VERSION >= 4
+                       int /*z_index*/,
+#endif  // SB_API_VERSION >= 4
+                       int x,
+                       int y,
+                       int width,
+                       int height) {
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
     return;
@@ -28,5 +34,5 @@
   player->SetBounds(x, y, width, height);
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif  // SB_API_VERSION >= 4 || \
            SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/starboard/player/player_set_pause.cc b/src/starboard/shared/starboard/player/player_set_pause.cc
index 7ce922b..ae5cd95 100644
--- a/src/starboard/shared/starboard/player/player_set_pause.cc
+++ b/src/starboard/shared/starboard/player/player_set_pause.cc
@@ -17,7 +17,7 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
 
 void SbPlayerSetPause(SbPlayer player, bool pause) {
   if (!SbPlayerIsValid(player)) {
@@ -27,4 +27,4 @@
   player->SetPause(pause);
 }
 
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/starboard/player/player_set_playback_rate.cc b/src/starboard/shared/starboard/player/player_set_playback_rate.cc
index 60abbc6..7928293 100644
--- a/src/starboard/shared/starboard/player/player_set_playback_rate.cc
+++ b/src/starboard/shared/starboard/player/player_set_playback_rate.cc
@@ -17,7 +17,7 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
 
 bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate) {
   if (!SbPlayerIsValid(player)) {
@@ -33,4 +33,4 @@
   return true;
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index a5fe4d5..5e9f302 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -233,15 +233,14 @@
   }
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 void PlayerWorker::DoSetBounds(Bounds bounds) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
   if (!handler_->SetBounds(bounds)) {
     UpdatePlayerState(kSbPlayerStateError);
   }
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif  // SB_API_VERSION >= 4 || \
            SB_IS(PLAYER_PUNCHED_OUT)
 
 void PlayerWorker::DoSetPause(bool pause) {
@@ -252,7 +251,7 @@
   }
 }
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
 void PlayerWorker::DoSetPlaybackRate(double playback_rate) {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
@@ -260,7 +259,7 @@
     UpdatePlayerState(kSbPlayerStateError);
   }
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
 void PlayerWorker::DoStop() {
   SB_DCHECK(job_queue_->BelongsToCurrentThread());
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 18149c9..4599be9 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -78,9 +78,9 @@
     virtual bool WriteSample(InputBuffer input_buffer, bool* written) = 0;
     virtual bool WriteEndOfStream(SbMediaType sample_type) = 0;
     virtual bool SetPause(bool pause) = 0;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
     virtual bool SetPlaybackRate(double playback_rate) = 0;
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
     virtual bool SetBounds(const Bounds& bounds) = 0;
 
     // Once this function returns, all processing on the Handler and related
@@ -88,9 +88,9 @@
     // after and is no longer safe to access.
     virtual void Stop() = 0;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
     virtual SbDecodeTarget GetCurrentDecodeTarget() = 0;
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
   };
 
   PlayerWorker(Host* host,
@@ -116,19 +116,18 @@
         Bind(&PlayerWorker::DoWriteEndOfStream, this, sample_type));
   }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
   void SetBounds(Bounds bounds) {
     job_queue_->Schedule(Bind(&PlayerWorker::DoSetBounds, this, bounds));
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif  // SB_API_VERSION >= 4 || \
            SB_IS(PLAYER_PUNCHED_OUT)
 
   void SetPause(bool pause) {
     job_queue_->Schedule(Bind(&PlayerWorker::DoSetPause, this, pause));
   }
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   void SetPlaybackRate(double playback_rate) {
     // Arbitrary values to give the playback rate a valid range.  A particular
     // implementation may have stricter or looser requirement, or even only
@@ -145,17 +144,17 @@
     job_queue_->Schedule(
         Bind(&PlayerWorker::DoSetPlaybackRate, this, playback_rate));
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
 
   void UpdateDroppedVideoFrames(int dropped_video_frames) {
     host_->UpdateDroppedVideoFrames(dropped_video_frames);
   }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentDecodeTarget() {
     return handler_->GetCurrentDecodeTarget();
   }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
  private:
   void UpdateMediaTime(SbMediaTime time);
@@ -170,15 +169,14 @@
   void DoWriteSample(InputBuffer input_buffer);
   void DoWritePendingSamples();
   void DoWriteEndOfStream(SbMediaType sample_type);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
   void DoSetBounds(Bounds bounds);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif  // SB_API_VERSION >= 4 || \
            SB_IS(PLAYER_PUNCHED_OUT)
   void DoSetPause(bool pause);
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
   void DoSetPlaybackRate(double rate);
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
   void DoStop();
 
   void UpdateDecoderState(SbMediaType type, SbPlayerDecoderState state);
diff --git a/src/starboard/shared/starboard/player/player_write_sample.cc b/src/starboard/shared/starboard/player/player_write_sample.cc
index d796f37..477e434 100644
--- a/src/starboard/shared/starboard/player/player_write_sample.cc
+++ b/src/starboard/shared/starboard/player/player_write_sample.cc
@@ -17,6 +17,43 @@
 #include "starboard/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
+#if SB_API_VERSION >= 4
+
+void SbPlayerWriteSample(SbPlayer player,
+                         SbMediaType sample_type,
+                         const void** sample_buffers,
+                         int* sample_buffer_sizes,
+                         int number_of_sample_buffers,
+                         SbMediaTime sample_pts,
+                         const SbMediaVideoSampleInfo* video_sample_info,
+                         const SbDrmSampleInfo* sample_drm_info) {
+  if (!SbPlayerIsValid(player)) {
+    SB_DLOG(WARNING) << "player is invalid.";
+    return;
+  }
+
+  if (number_of_sample_buffers != 1) {
+    SB_DLOG(WARNING) << "SbPlayerWriteSample() doesn't support"
+                     << " |number_of_sample_buffers| other than one.";
+    return;
+  }
+
+  if (sample_buffers == NULL) {
+    SB_DLOG(WARNING) << "|sample_buffers| cannot be NULL";
+    return;
+  }
+
+  if (sample_buffer_sizes == NULL) {
+    SB_DLOG(WARNING) << "|sample_buffer_sizes| cannot be NULL";
+    return;
+  }
+
+  player->WriteSample(sample_type, sample_buffers[0], sample_buffer_sizes[0],
+                      sample_pts, video_sample_info, sample_drm_info);
+}
+
+#else  // SB_API_VERSION >= 4
+
 void SbPlayerWriteSample(SbPlayer player,
                          SbMediaType sample_type,
                          const void* sample_buffer,
@@ -32,3 +69,5 @@
   player->WriteSample(sample_type, sample_buffer, sample_buffer_size,
                       sample_pts, video_sample_info, sample_drm_info);
 }
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/video_frame_internal.cc b/src/starboard/shared/starboard/player/video_frame_internal.cc
index 69818c1..c000c26 100644
--- a/src/starboard/shared/starboard/player/video_frame_internal.cc
+++ b/src/starboard/shared/starboard/player/video_frame_internal.cc
@@ -231,8 +231,7 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::CreateEmptyFrame(SbMediaTime pts) {
-  VideoFrame* frame = new VideoFrame;
-  frame->format_ = kYV12;
+  VideoFrame* frame = new VideoFrame();
   frame->pts_ = pts;
   return frame;
 }
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/starboard/system_request_pause.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/starboard/system_request_pause.cc
index d335495..98feeb4 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/starboard/system_request_pause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/shared/starboard/application.h"
+
+#if SB_API_VERSION < 4
+#error "SbSystemRequestPause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestPause() {
+  starboard::shared::starboard::Application::Get()->Pause(NULL, NULL);
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/starboard/system_request_suspend.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/starboard/system_request_suspend.cc
index d335495..779ea2d 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/starboard/system_request_suspend.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/shared/starboard/application.h"
+
+#if SB_API_VERSION < 4
+#error "SbSystemRequestSuspend requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestSuspend() {
+  starboard::shared::starboard::Application::Get()->Suspend(NULL, NULL);
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/starboard/system_request_unpause.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/starboard/system_request_unpause.cc
index d335495..c9baab5 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/starboard/system_request_unpause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/shared/starboard/application.h"
+
+#if SB_API_VERSION < 4
+#error "SbSystemRequestUnpause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestUnpause() {
+  starboard::shared::starboard::Application::Get()->Unpause(NULL, NULL);
+}
diff --git a/src/starboard/shared/starboard/thread_local_storage_internal.cc b/src/starboard/shared/starboard/thread_local_storage_internal.cc
index 895d218..df7dd39 100644
--- a/src/starboard/shared/starboard/thread_local_storage_internal.cc
+++ b/src/starboard/shared/starboard/thread_local_storage_internal.cc
@@ -21,6 +21,7 @@
 #include <utility>
 #include <vector>
 
+#include "starboard/common/flat_map.h"
 #include "starboard/log.h"
 #include "starboard/memory.h"
 #include "starboard/once.h"
@@ -77,6 +78,107 @@
   };
 };
 
+// A map of ThreadId -> int. This map is highly concurrent and allows
+// access of elements without much contention.
+class ConcurrentThreadIdMap {
+ public:
+  ConcurrentThreadIdMap() {
+    // Prime number reduces collisions.
+    static const size_t kNumBuckets = 101;
+    map_vector_.resize(kNumBuckets);
+
+    for (size_t i = 0; i < map_vector_.size(); ++i) {
+      void* memory_block = SbMemoryAllocateNoReport(sizeof(LockedMap));
+      map_vector_[i] = new (memory_block) LockedMap;
+    }
+  }
+
+  ~ConcurrentThreadIdMap() {
+    for (size_t i = 0; i < map_vector_.size(); ++i) {
+      LockedMap* obj = map_vector_[i];
+      obj->~LockedMap();
+      SbMemoryDeallocateNoReport(obj);
+    }
+  }
+
+  bool GetIfExists(SbThreadId key, int* value) const {
+    const LockedMap& map = GetBucket(key);
+    ScopedLock lock(map.mutex_);
+    ThreadIdMap::const_iterator it = map.map_.find(key);
+    if (it != map.map_.end()) {
+      *value = it->second;
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  void Insert(SbThreadId key, int value) {
+    LockedMap& map = GetBucket(key);
+    ScopedLock lock(map.mutex_);
+    map.map_[key] = value;
+  }
+
+  void Erase(SbThreadId key) {
+    LockedMap& map = GetBucket(key);
+    ScopedLock lock(map.mutex_);
+    map.map_.erase(key);
+  }
+
+ private:
+  typedef std::map<SbThreadId,
+                   int,
+                   std::less<SbThreadId>,
+                   RawAllocator<std::pair<const SbThreadId, int> > >
+      ThreadIdMap;
+
+  struct LockedMap {
+    Mutex mutex_;
+    ThreadIdMap map_;
+  };
+
+  // Simple hashing function for 32 bit numbers.
+  // Based off of Jenkins hash found at this url:
+  // https://gist.github.com/badboy/6267743#file-inthash-md
+  static size_t Hash(SbThreadId id) {
+    static const uint32_t kMagicNum1 = 0x7ed55d16;
+    static const uint32_t kMagicNum2 = 0xc761c23c;
+    static const uint32_t kMagicNum3 = 0x165667b1;
+    static const uint32_t kMagicNum4 = 0xd3a2646c;
+    static const uint32_t kMagicNum5 = 0xfd7046c5;
+    static const uint32_t kMagicNum6 = 0xfd7046c5;
+
+    static const uint32_t kMagicShift1 = 12;
+    static const uint32_t kMagicShift2 = 19;
+    static const uint32_t kMagicShift3 = 5;
+    static const uint32_t kMagicShift4 = 9;
+    static const uint32_t kMagicShift5 = 3;
+    static const uint32_t kMagicShift6 = 16;
+
+    uint32_t key = static_cast<uint32_t>(id);
+    key = (key + kMagicNum1) + (key << kMagicShift1);
+    key = (key ^ kMagicNum2) ^ (key >> kMagicShift2);
+    key = (key + kMagicNum3) + (key << kMagicShift3);
+    key = (key + kMagicNum4) ^ (key << kMagicShift4);
+    key = (key + kMagicNum5) + (key << kMagicShift5);
+    key = (key ^ kMagicNum6) ^ (key >> kMagicShift6);
+    return static_cast<size_t>(key);
+  }
+
+  const LockedMap& GetBucket(SbThreadId key) const {
+    size_t bucket_index = Hash(key) % map_vector_.size();
+    return *map_vector_[bucket_index];
+  }
+
+  LockedMap& GetBucket(SbThreadId key) {
+    size_t bucket_index = Hash(key) % map_vector_.size();
+    return *map_vector_[bucket_index];
+  }
+
+  typedef std::vector<LockedMap*, RawAllocator<LockedMap*> > MapVector;
+  MapVector map_vector_;
+};
+
 }  // namespace
 
 struct TLSKeyManager::InternalData {
@@ -84,11 +186,7 @@
   // any platform using this TLSKeyManager will crash during memory reporting.
   typedef std::vector<KeyRecord, RawAllocator<KeyRecord> > VectorKeyRecord;
   typedef std::vector<int, RawAllocator<int> > VectorInt;
-  typedef std::map<SbThreadId,
-                   int,
-                   std::less<SbThreadId>,
-                   RawAllocator<std::pair<const SbThreadId, int> > >
-      ThreadIdMap;
+  typedef ConcurrentThreadIdMap ThreadIdMap;
 
   // Overrides new/delete for InternalData to bypass memory reporting.
   static void* operator new(size_t n) { return SbMemoryAllocateNoReport(n); }
@@ -119,7 +217,11 @@
 }
 
 SbThreadLocalKey TLSKeyManager::CreateKey(SbThreadLocalDestructor destructor) {
-  SbThreadLocalKey key = new SbThreadLocalKeyPrivate();
+  // Allocate key and bypass the the normal allocator. Otherwise there
+  // could be a re-entrant loop that kills the process.
+  void* memory_block =
+      SbMemoryAllocateNoReport(sizeof(SbThreadLocalKeyPrivate));
+  SbThreadLocalKey key = new (memory_block) SbThreadLocalKeyPrivate();
 
   ScopedLock lock(mutex_);
   key->index = GetUnusedKeyIndex();
@@ -143,7 +245,8 @@
   SB_DCHECK(IsKeyActive(key));
   data_->key_table_[key->index].valid = false;
 
-  delete key;
+  key.~SbThreadLocalKey();
+  SbMemoryDeallocateNoReport(key);
 }
 
 bool TLSKeyManager::SetLocalValue(SbThreadLocalKey key, void* value) {
@@ -151,13 +254,15 @@
     return false;
   }
 
+  int current_thread_id = GetCurrentThreadId();
+
   ScopedLock lock(mutex_);
 
   if (!IsKeyActive(key)) {
     return false;
   }
 
-  data_->key_table_[key->index].values[GetCurrentThreadId()] = value;
+  data_->key_table_[key->index].values[current_thread_id] = value;
 
   return true;
 }
@@ -167,20 +272,22 @@
     return NULL;
   }
 
+  int current_thread_id = GetCurrentThreadId();
+
   ScopedLock lock(mutex_);
 
   if (!IsKeyActive(key)) {
     return NULL;
   }
 
-  return data_->key_table_[key->index].values[GetCurrentThreadId()];
+  return data_->key_table_[key->index].values[current_thread_id];
 }
 
 void TLSKeyManager::InitializeTLSForThread() {
-  ScopedLock lock(mutex_);
-
   int current_thread_id = GetCurrentThreadId();
 
+  ScopedLock lock(mutex_);
+
   const size_t table_size = data_->key_table_.size();
   for (int i = 0; i < table_size; ++i) {
     KeyRecord* key_record = &data_->key_table_[i];
@@ -191,10 +298,10 @@
 }
 
 void TLSKeyManager::ShutdownTLSForThread() {
-  ScopedLock lock(mutex_);
-
   int current_thread_id = GetCurrentThreadId();
 
+  ScopedLock lock(mutex_);
+
   // Apply the destructors multiple times (4 is the minimum value
   // according to the specifications).  This is necessary if one of
   // the destructors adds new values to the key map.
@@ -220,7 +327,7 @@
     }
   }
 
-  data_->thread_id_map_.erase(SbThreadGetId());
+  data_->thread_id_map_.Erase(SbThreadGetId());
   data_->available_thread_ids_.push_back(current_thread_id);
 }
 
@@ -243,17 +350,20 @@
 }
 
 int TLSKeyManager::GetCurrentThreadId() {
-  InternalData::ThreadIdMap::const_iterator found =
-      data_->thread_id_map_.find(SbThreadGetId());
-  if (found != data_->thread_id_map_.end()) {
-    return found->second;
+  const SbThreadId thread_id = SbThreadGetId();
+
+  int value = -1;
+  if (data_->thread_id_map_.GetIfExists(thread_id, &value)) {
+    return value;
   }
 
+  ScopedLock lock(mutex_);
+
   SB_DCHECK(!data_->available_thread_ids_.empty());
   int thread_tls_id = data_->available_thread_ids_.back();
   data_->available_thread_ids_.pop_back();
-  data_->thread_id_map_.insert(std::make_pair(SbThreadGetId(), thread_tls_id));
 
+  data_->thread_id_map_.Insert(thread_id, thread_tls_id);
   return thread_tls_id;
 }
 
diff --git a/src/starboard/shared/stub/cryptography_create_transformer.cc b/src/starboard/shared/stub/cryptography_create_transformer.cc
new file mode 100644
index 0000000..502a006
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_create_transformer.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+SbCryptographyTransformer SbCryptographyCreateTransformer(
+    const char* algorithm,
+    int block_size_bits,
+    SbCryptographyDirection direction,
+    SbCryptographyBlockCipherMode mode,
+    const void* initialization_vector,
+    int initialization_vector_size,
+    const void* key,
+    int key_size) {
+  SB_UNREFERENCED_PARAMETER(algorithm);
+  SB_UNREFERENCED_PARAMETER(block_size_bits);
+  SB_UNREFERENCED_PARAMETER(direction);
+  SB_UNREFERENCED_PARAMETER(mode);
+  SB_UNREFERENCED_PARAMETER(initialization_vector);
+  SB_UNREFERENCED_PARAMETER(initialization_vector_size);
+  SB_UNREFERENCED_PARAMETER(key);
+  SB_UNREFERENCED_PARAMETER(key_size);
+  return kSbCryptographyInvalidTransformer;
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/cryptography_destroy_transformer.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/cryptography_destroy_transformer.cc
index d335495..0547106 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/cryptography_destroy_transformer.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+void SbCryptographyDestroyTransformer(SbCryptographyTransformer transformer) {
+  SB_UNREFERENCED_PARAMETER(transformer);
+}
diff --git a/src/starboard/shared/stub/cryptography_get_tag.cc b/src/starboard/shared/stub/cryptography_get_tag.cc
new file mode 100644
index 0000000..5ca1cbf
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_get_tag.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+bool SbCryptographyGetTag(
+    SbCryptographyTransformer transformer,
+    void* out_tag,
+    int out_tag_size) {
+  SB_UNREFERENCED_PARAMETER(transformer);
+  SB_UNREFERENCED_PARAMETER(out_tag);
+  SB_UNREFERENCED_PARAMETER(out_tag_size);
+  return false;
+}
diff --git a/src/starboard/shared/stub/cryptography_set_authenticated_data.cc b/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
new file mode 100644
index 0000000..93968dd
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+bool SbCryptographySetAuthenticatedData(
+    SbCryptographyTransformer transformer,
+    const void* data,
+    int data_size) {
+  SB_UNREFERENCED_PARAMETER(transformer);
+  SB_UNREFERENCED_PARAMETER(data);
+  SB_UNREFERENCED_PARAMETER(data_size);
+  return false;
+}
diff --git a/src/starboard/shared/stub/cryptography_set_initialization_vector.cc b/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
new file mode 100644
index 0000000..a070c26
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+void SbCryptographySetInitializationVector(
+    SbCryptographyTransformer transformer,
+    const void* initialization_vector,
+    int initialization_vector_size) {
+  SB_UNREFERENCED_PARAMETER(transformer);
+  SB_UNREFERENCED_PARAMETER(initialization_vector);
+  SB_UNREFERENCED_PARAMETER(initialization_vector_size);
+}
diff --git a/src/starboard/shared/stub/cryptography_transform.cc b/src/starboard/shared/stub/cryptography_transform.cc
new file mode 100644
index 0000000..4488a1f
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_transform.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+int SbCryptographyTransform(SbCryptographyTransformer transformer,
+                            const void* in_data,
+                            int in_data_size,
+                            void* out_data) {
+  SB_UNREFERENCED_PARAMETER(transformer);
+  SB_UNREFERENCED_PARAMETER(in_data);
+  SB_UNREFERENCED_PARAMETER(in_data_size);
+  SB_UNREFERENCED_PARAMETER(out_data);
+  return 0;
+}
diff --git a/src/starboard/shared/stub/decode_target_create_blitter.cc b/src/starboard/shared/stub/decode_target_create_blitter.cc
index 0982fe7..5d807a7 100644
--- a/src/starboard/shared/stub/decode_target_create_blitter.cc
+++ b/src/starboard/shared/stub/decode_target_create_blitter.cc
@@ -14,14 +14,14 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 
 SbDecodeTarget SbDecodeTargetCreate(SbDecodeTargetFormat /*format*/,
                                     SbBlitterSurface* /*planes*/) {
   return kSbDecodeTargetInvalid;
 }
 
-#else  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else  // SB_API_VERSION < 4
 
 SbDecodeTarget SbDecodeTargetCreate(SbBlitterDevice device,
                                     SbDecodeTargetFormat format,
@@ -30,4 +30,4 @@
   return kSbDecodeTargetInvalid;
 }
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_create_egl.cc b/src/starboard/shared/stub/decode_target_create_egl.cc
index 17c33c5..82f151f 100644
--- a/src/starboard/shared/stub/decode_target_create_egl.cc
+++ b/src/starboard/shared/stub/decode_target_create_egl.cc
@@ -14,14 +14,14 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 SbDecodeTarget SbDecodeTargetCreate(EGLDisplay /*display*/,
                                     EGLContext /*context*/,
                                     SbDecodeTargetFormat /*format*/,
                                     GLuint* /*planes*/) {
   return kSbDecodeTargetInvalid;
 }
-#else   // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else   // SB_API_VERSION < 4
 SbDecodeTarget SbDecodeTargetCreate(void* /*display*/,
                                     void* /*context*/,
                                     SbDecodeTargetFormat /*format*/,
@@ -29,4 +29,4 @@
                                     int /*height*/) {
   return kSbDecodeTargetInvalid;
 }
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_format.cc b/src/starboard/shared/stub/decode_target_get_format.cc
index a64143a..28532f3 100644
--- a/src/starboard/shared/stub/decode_target_get_format.cc
+++ b/src/starboard/shared/stub/decode_target_get_format.cc
@@ -14,10 +14,10 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 
 SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget /*decode_target*/) {
   return kSbDecodeTargetFormatInvalid;
 }
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_info.cc b/src/starboard/shared/stub/decode_target_get_info.cc
index b37d7e8..c8fe44c 100644
--- a/src/starboard/shared/stub/decode_target_get_info.cc
+++ b/src/starboard/shared/stub/decode_target_get_info.cc
@@ -14,11 +14,11 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
 bool SbDecodeTargetGetInfo(SbDecodeTarget /*decode_target*/,
                            SbDecodeTargetInfo* /*out_info*/) {
   return false;
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/decode_target_get_plane_blitter.cc b/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
index 70ecf95..eac0538 100644
--- a/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
+++ b/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
@@ -14,11 +14,13 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 
+#if SB_HAS(BLITTER)
 SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget /*decode_target*/,
                                         SbDecodeTargetPlane /*plane*/) {
   return kSbBlitterInvalidSurface;
 }
+#endif  // SB_HAS(BLITTER)
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_plane_egl.cc b/src/starboard/shared/stub/decode_target_get_plane_egl.cc
index 0d01b57..dec9300 100644
--- a/src/starboard/shared/stub/decode_target_get_plane_egl.cc
+++ b/src/starboard/shared/stub/decode_target_get_plane_egl.cc
@@ -14,11 +14,13 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 
+#if SB_HAS(GLES2)
 GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
                               SbDecodeTargetPlane plane) {
   return 0;
 }
+#endif  // SB_HAS(GLES2)
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_size.cc b/src/starboard/shared/stub/decode_target_get_size.cc
index 9e82f71..09ee7a5 100644
--- a/src/starboard/shared/stub/decode_target_get_size.cc
+++ b/src/starboard/shared/stub/decode_target_get_size.cc
@@ -14,7 +14,7 @@
 
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 
 void SbDecodeTargetGetSize(SbDecodeTarget decode_target,
                            int* out_width,
@@ -24,4 +24,4 @@
   SB_UNREFERENCED_PARAMETER(out_height);
 }
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_is_opaque.cc b/src/starboard/shared/stub/decode_target_is_opaque.cc
index 5cb3596..3759b36 100644
--- a/src/starboard/shared/stub/decode_target_is_opaque.cc
+++ b/src/starboard/shared/stub/decode_target_is_opaque.cc
@@ -15,7 +15,7 @@
 #include "starboard/configuration.h"
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 
 #if !(SB_VERSION(3) && SB_HAS(GRAPHICS))
 #error "SbDecodeTargetIsOpaque requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
@@ -25,4 +25,4 @@
   return false;
 }
 
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/drm_generate_session_update_request.cc b/src/starboard/shared/stub/drm_generate_session_update_request.cc
index 2837255..5d14858 100644
--- a/src/starboard/shared/stub/drm_generate_session_update_request.cc
+++ b/src/starboard/shared/stub/drm_generate_session_update_request.cc
@@ -15,6 +15,9 @@
 #include "starboard/drm.h"
 
 void SbDrmGenerateSessionUpdateRequest(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+                                       int ticket,
+#endif  // SB_API_VERSION >= 4
                                        const char* type,
                                        const void* initialization_data,
                                        int initialization_data_size) {
diff --git a/src/starboard/shared/stub/drm_update_session.cc b/src/starboard/shared/stub/drm_update_session.cc
index 67a9d44..7f0d11f 100644
--- a/src/starboard/shared/stub/drm_update_session.cc
+++ b/src/starboard/shared/stub/drm_update_session.cc
@@ -15,6 +15,9 @@
 #include "starboard/drm.h"
 
 void SbDrmUpdateSession(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+                        int ticket,
+#endif  // SB_API_VERSION >= 4
                         const void* key,
                         int key_size,
                         const void* session_id,
diff --git a/src/starboard/shared/stub/image_decode.cc b/src/starboard/shared/stub/image_decode.cc
index ca1ff97..3072b96 100644
--- a/src/starboard/shared/stub/image_decode.cc
+++ b/src/starboard/shared/stub/image_decode.cc
@@ -19,6 +19,15 @@
 #error "SbImageDecode requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
 #endif
 
+#if SB_API_VERSION >= 4
+SbDecodeTarget SbImageDecode(SbDecodeTargetGraphicsContextProvider* provider,
+                             void* data,
+                             int data_size,
+                             const char* mime_type,
+                             SbDecodeTargetFormat format) {
+  return kSbDecodeTargetInvalid;
+}
+#else   // SB_API_VERSION >= 4
 SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider,
                              void* data,
                              int data_size,
@@ -26,3 +35,4 @@
                              SbDecodeTargetFormat format) {
   return kSbDecodeTargetInvalid;
 }
+#endif  // SB_API_VERSION >= 4
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/media_is_audio_supported.cc
similarity index 64%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/media_is_audio_supported.cc
index d335495..32d7b61 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/media_is_audio_supported.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec /*audio_codec*/,
+                                       int64_t /*bitrate*/) {
+  return false;
+}
diff --git a/src/starboard/shared/stub/media_is_video_supported.cc b/src/starboard/shared/stub/media_is_video_supported.cc
new file mode 100644
index 0000000..ebe95da
--- /dev/null
+++ b/src/starboard/shared/stub/media_is_video_supported.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec /*video_codec*/,
+                                       int /*frame_width*/,
+                                       int /*frame_height*/,
+                                       int64_t /*bitrate*/,
+                                       int /*fps*/) {
+  return false;
+}
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc
index de2ad10..7d5eb89 100644
--- a/src/starboard/shared/stub/player_create.cc
+++ b/src/starboard/shared/stub/player_create.cc
@@ -28,11 +28,11 @@
                         SbPlayerDecoderStatusFunc /*decoder_status_func*/,
                         SbPlayerStatusFunc /*player_status_func*/,
                         void* /*context*/
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
                         ,
-                        SbPlayerOutputMode output_mode
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_VERSION(3)
+                        SbPlayerOutputMode output_mode,
+                        SbDecodeTargetGraphicsContextProvider* /*provider*/
+#elif SB_VERSION(3)
                         ,
                         SbDecodeTargetProvider* /*provider*/
 #endif
diff --git a/src/starboard/shared/stub/player_get_current_frame.cc b/src/starboard/shared/stub/player_get_current_frame.cc
index ffec4f0..917e0f3 100644
--- a/src/starboard/shared/stub/player_get_current_frame.cc
+++ b/src/starboard/shared/stub/player_get_current_frame.cc
@@ -14,10 +14,10 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
 SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer /*player*/) {
   return kSbDecodeTargetInvalid;
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/player_output_mode_supported.cc b/src/starboard/shared/stub/player_output_mode_supported.cc
index 1ac4b8c..437d5e6 100644
--- a/src/starboard/shared/stub/player_output_mode_supported.cc
+++ b/src/starboard/shared/stub/player_output_mode_supported.cc
@@ -14,7 +14,7 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 
 bool SbPlayerOutputModeSupported(SbPlayerOutputMode /*output_mode*/,
                                  SbMediaVideoCodec /*codec*/,
@@ -22,4 +22,4 @@
   return false;
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/player_set_bounds.cc b/src/starboard/shared/stub/player_set_bounds.cc
index a1deeca..5fbee67 100644
--- a/src/starboard/shared/stub/player_set_bounds.cc
+++ b/src/starboard/shared/stub/player_set_bounds.cc
@@ -14,14 +14,16 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 
 void SbPlayerSetBounds(SbPlayer /*player*/,
+#if SB_API_VERSION >= 4
+                       int /*z_index*/,
+#endif  // SB_API_VERSION >= 4
                        int /*x*/,
                        int /*y*/,
                        int /*width*/,
-                       int /*height*/) {}
+                       int /*height*/) {
+}
 
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-           SB_IS(PLAYER_PUNCHED_OUT)
+#endif  // SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/stub/player_set_pause.cc b/src/starboard/shared/stub/player_set_pause.cc
index 496624c..c2f4d60 100644
--- a/src/starboard/shared/stub/player_set_pause.cc
+++ b/src/starboard/shared/stub/player_set_pause.cc
@@ -14,8 +14,8 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
 
 void SbPlayerSetPause(SbPlayer /*player*/, bool /*pause*/) {}
 
-#endif  // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/player_set_playback_rate.cc b/src/starboard/shared/stub/player_set_playback_rate.cc
index d9fc358..c9103aa 100644
--- a/src/starboard/shared/stub/player_set_playback_rate.cc
+++ b/src/starboard/shared/stub/player_set_playback_rate.cc
@@ -14,10 +14,10 @@
 
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
 
 bool SbPlayerSetPlaybackRate(SbPlayer /*player*/, double /*playback_rate*/) {
   return false;
 }
 
-#endif  // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/player_write_sample.cc b/src/starboard/shared/stub/player_write_sample.cc
index b1fd839..1fd2d52 100644
--- a/src/starboard/shared/stub/player_write_sample.cc
+++ b/src/starboard/shared/stub/player_write_sample.cc
@@ -14,6 +14,19 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION >= 4
+
+void SbPlayerWriteSample(SbPlayer /*player*/,
+                         SbMediaType /*sample_type*/,
+                         const void** /*sample_buffers*/,
+                         int* /*sample_buffer_sizes*/,
+                         int /*number_of_sample_buffers*/,
+                         SbMediaTime /*sample_pts*/,
+                         const SbMediaVideoSampleInfo* /*video_sample_info*/,
+                         const SbDrmSampleInfo* /*sample_drm_info*/) {}
+
+#else  // SB_API_VERSION >= 4
+
 void SbPlayerWriteSample(SbPlayer /*player*/,
                          SbMediaType /*sample_type*/,
                          const void* /*sample_buffer*/,
@@ -21,3 +34,5 @@
                          SbMediaTime /*sample_pts*/,
                          const SbMediaVideoSampleInfo* /*video_sample_info*/,
                          const SbDrmSampleInfo* /*sample_drm_info*/) {}
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/socket_get_interface_address.cc b/src/starboard/shared/stub/socket_get_interface_address.cc
new file mode 100644
index 0000000..c31cd84
--- /dev/null
+++ b/src/starboard/shared/stub/socket_get_interface_address.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#if SB_API_VERSION >= 4
+
+bool SbSocketGetInterfaceAddress(const SbSocketAddress* const /*destination*/,
+                                 SbSocketAddress* /*out_source_address*/,
+                                 SbSocketAddress* /*out_netmask*/) {
+  return false;
+}
+
+#endif  // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/socket_get_local_interface_address.cc b/src/starboard/shared/stub/socket_get_local_interface_address.cc
index 91cc212..17e4ab5 100644
--- a/src/starboard/shared/stub/socket_get_local_interface_address.cc
+++ b/src/starboard/shared/stub/socket_get_local_interface_address.cc
@@ -14,6 +14,10 @@
 
 #include "starboard/socket.h"
 
+#if SB_API_VERSION < 4
+
 bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* /*out_address*/) {
   return false;
 }
+
+#endif  // SB_API_VERSION < 4
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/speech_recognizer_cancel.cc
similarity index 60%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/speech_recognizer_cancel.cc
index d335495..a7c263a 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/speech_recognizer_cancel.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/speech_recognizer.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+void SbSpeechRecognizerCancel(SbSpeechRecognizer /*recognizer*/) {}
+
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/starboard/shared/stub/speech_recognizer_create.cc b/src/starboard/shared/stub/speech_recognizer_create.cc
new file mode 100644
index 0000000..5acdf3a
--- /dev/null
+++ b/src/starboard/shared/stub/speech_recognizer_create.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/speech_recognizer.h"
+
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+SbSpeechRecognizer SbSpeechRecognizerCreate(
+    const SbSpeechRecognizerHandler* /*handler*/) {
+  return kSbSpeechRecognizerInvalid;
+}
+
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/speech_recognizer_destroy.cc
similarity index 60%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/speech_recognizer_destroy.cc
index d335495..090417c 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/speech_recognizer_destroy.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/speech_recognizer.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+void SbSpeechRecognizerDestroy(SbSpeechRecognizer /*recognizer*/) {}
+
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/starboard/shared/stub/speech_recognizer_start.cc b/src/starboard/shared/stub/speech_recognizer_start.cc
new file mode 100644
index 0000000..fc2d43d
--- /dev/null
+++ b/src/starboard/shared/stub/speech_recognizer_start.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/speech_recognizer.h"
+
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+bool SbSpeechRecognizerStart(SbSpeechRecognizer /*recognizer*/,
+                             const SbSpeechConfiguration* /*configuration*/) {
+  return false;
+}
+
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/speech_recognizer_stop.cc
similarity index 60%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/speech_recognizer_stop.cc
index d335495..b799661 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/speech_recognizer_stop.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/speech_recognizer.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+void SbSpeechRecognizerStop(SbSpeechRecognizer /*recognizer*/) {}
+
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/starboard/shared/stub/speech_synthesis_set_language.cc b/src/starboard/shared/stub/speech_synthesis_set_language.cc
index 8e7d758..6cc1b33 100644
--- a/src/starboard/shared/stub/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/stub/speech_synthesis_set_language.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
 
 #include "starboard/speech_synthesis.h"
 
diff --git a/src/starboard/shared/stub/system_break_into_debugger.cc b/src/starboard/shared/stub/system_break_into_debugger.cc
index 773e271..59d081e 100644
--- a/src/starboard/shared/stub/system_break_into_debugger.cc
+++ b/src/starboard/shared/stub/system_break_into_debugger.cc
@@ -15,4 +15,5 @@
 #include "starboard/system.h"
 
 void SbSystemBreakIntoDebugger() {
+  __builtin_unreachable();
 }
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/system_request_pause.cc
similarity index 71%
rename from src/cobalt/base/math.cc
rename to src/starboard/shared/stub/system_request_pause.cc
index d335495..618cfb8 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/system_request_pause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbSystemRequestPause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestPause() {
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/system_request_suspend.cc
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/system_request_suspend.cc
index d335495..f9e28b0 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/system_request_suspend.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbSystemRequestSuspend requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestSuspend() {
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/system_request_unpause.cc
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/system_request_unpause.cc
index d335495..b3820dc 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/system_request_unpause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
 
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif  // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbSystemRequestUnpause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestUnpause() {
+}
diff --git a/src/starboard/shared/x11/window_get_platform_handle.cc b/src/starboard/shared/x11/window_get_platform_handle.cc
index f7af64f..267c9bd 100644
--- a/src/starboard/shared/x11/window_get_platform_handle.cc
+++ b/src/starboard/shared/x11/window_get_platform_handle.cc
@@ -23,8 +23,7 @@
   }
 
   Window handle = None;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
   handle = window->gl_window;
 #else
   handle = window->window;
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc
index 3294543..8235d61 100644
--- a/src/starboard/shared/x11/window_internal.cc
+++ b/src/starboard/shared/x11/window_internal.cc
@@ -26,11 +26,10 @@
 #include "starboard/log.h"
 #include "starboard/player.h"
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 #include <X11/extensions/Xcomposite.h>
 #include <X11/extensions/Xrender.h>
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif  // SB_API_VERSION >= 4 ||
         // SB_IS(PLAYER_PUNCHED_OUT)
 
 namespace {
@@ -38,22 +37,20 @@
 const int kWindowWidth = 1920;
 const int kWindowHeight = 1080;
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 bool HasNeededExtensionsForPunchedOutVideo(Display* display) {
   int dummy;
   return (XRenderQueryExtension(display, &dummy, &dummy) &&
           XCompositeQueryExtension(display, &dummy, &dummy));
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif  // SB_API_VERSION >= 4 ||
         // SB_IS(PLAYER_PUNCHED_OUT)
 }  // namespace
 
 SbWindowPrivate::SbWindowPrivate(Display* display,
                                  const SbWindowOptions* options)
     : window(None)
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
       ,
       window_picture(None),
       composition_pixmap(None),
@@ -65,7 +62,7 @@
       video_picture(None),
       gl_window(None),
       gl_picture(None)
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif  // SB_API_VERSION >= 4 ||
       // SB_IS(PLAYER_PUNCHED_OUT)
       ,
       display(display) {
@@ -107,8 +104,7 @@
   Atom wm_delete = XInternAtom(display, "WM_DELETE_WINDOW", True);
   XSetWMProtocols(display, window, &wm_delete, 1);
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
   SB_CHECK(HasNeededExtensionsForPunchedOutVideo(display));
   gl_window = XCreateSimpleWindow(display, window, 0, 0, width, height, 0,
                                   WhitePixel(display, DefaultScreen(display)),
@@ -132,15 +128,14 @@
       XRenderFindVisualFormat(display, x_visual_info.visual);
   window_picture = XRenderCreatePicture(display, window, pict_format, 0, NULL);
   SB_CHECK(window_picture != None);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif  // SB_API_VERSION >= 4 ||
         // SB_IS(PLAYER_PUNCHED_OUT)
 
   XMapWindow(display, window);
 }
 
 SbWindowPrivate::~SbWindowPrivate() {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
   if (composition_pixmap != None) {
     XFreePixmap(display, composition_pixmap);
     XRenderFreePicture(display, composition_picture);
@@ -153,13 +148,12 @@
   XRenderFreePicture(display, gl_picture);
   XDestroyWindow(display, gl_window);
   XRenderFreePicture(display, window_picture);
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif  // SB_API_VERSION >= 4 || \
            SB_IS(PLAYER_PUNCHED_OUT)
   XDestroyWindow(display, window);
 }
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
-    SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 void SbWindowPrivate::Composite(
     int bounds_x,
     int bounds_y,
@@ -264,21 +258,21 @@
 
     int dest_x = bounds_x + (bounds_width - video_width) / 2;
     int dest_y = bounds_y + (bounds_height - video_height) / 2;
-    XRenderComposite(display, PictOpSrc, video_picture, NULL,
+    XRenderComposite(display, PictOpSrc, video_picture, None,
                      composition_picture, 0, 0, 0, 0, dest_x, dest_y,
                      video_width, video_height);
   }
 
   // Composite (with blending) the GL output on top of the composition pixmap
   // that already has the current video frame if video is playing.
-  XRenderComposite(display, PictOpOver, gl_picture, NULL, composition_picture,
+  XRenderComposite(display, PictOpOver, gl_picture, None, composition_picture,
                    0, 0, 0, 0, 0, 0, width, height);
 
   // Now that we have a fully-composited frame in composition_pixmap, render it
   // to the window, which acts as our front buffer.
-  XRenderComposite(display, PictOpSrc, composition_picture, NULL,
+  XRenderComposite(display, PictOpSrc, composition_picture, None,
                    window_picture, 0, 0, 0, 0, 0, 0, width, height);
   XSynchronize(display, False);
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif  // SB_API_VERSION >= 4 || \
            SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/x11/window_internal.h b/src/starboard/shared/x11/window_internal.h
index fd170fd..3c96af9 100644
--- a/src/starboard/shared/x11/window_internal.h
+++ b/src/starboard/shared/x11/window_internal.h
@@ -22,9 +22,9 @@
 #include "starboard/shared/starboard/player/video_frame_internal.h"
 #include "starboard/window.h"
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION || SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
 #include <X11/extensions/Xrender.h>
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION ||
+#endif  // SB_API_VERSION >= 4 ||
         // SB_IS(PLAYER_PUNCHED_OUT)
 
 struct SbWindowPrivate {
@@ -33,7 +33,7 @@
 
   Window window;
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION || SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
   typedef ::starboard::shared::starboard::player::VideoFrame VideoFrame;
 
   // Composites graphics and the given video frame video for this window. In
@@ -80,7 +80,7 @@
 
   // A cached XRender Picture wrapper for |gl_window|.
   Picture gl_picture;
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION ||
+#endif  // SB_API_VERSION >= 4 ||
         // SB_IS(PLAYER_PUNCHED_OUT)
 
   // The display that this window was created from.
diff --git a/src/starboard/socket.h b/src/starboard/socket.h
index d2893ba..a9f403c 100644
--- a/src/starboard/socket.h
+++ b/src/starboard/socket.h
@@ -227,6 +227,57 @@
 SB_EXPORT bool SbSocketGetLocalAddress(SbSocket socket,
                                        SbSocketAddress* out_address);
 
+#if SB_API_VERSION >= 4
+// Gets the source address and the netmask that would be used to connect to the
+// destination.  The netmask parameter is optional, and only populated if a
+// non-NULL parameter is passed in.  To determine which source IP will be used,
+// the kernel takes into account the protocol, routes, destination
+// ip, etc.  The subnet mask, aka netmask, is used to find the routing prefix.
+// In IPv6, this should be derived from the prefix value.
+//
+// Returns whether it was possible to determine the source address and the
+// netmask (if non-NULL value is passed) to be used to connect to the
+// destination.  This function could fail if the destination is not reachable,
+// if it an invalid address, etc.
+//
+// |destination|: The destination IP to be connected to.  If IP addresses is not
+// 0.0.0.0 or ::, then temporary addresses may be returned.
+//
+// If the destination address is 0.0.0.0, and its |type| is
+// |kSbSocketAddressTypeIpv4|, then any IPv4 local interface that is up and not
+// a loopback interface is a valid return value.
+//
+// If the destination address is ::, and its |type| is
+// |kSbSocketAddressTypeIpv6| then any IPv6 local interface that is up and not
+// loopback or a link-local IP is a valid return value.  However, in the case of
+// IPv6, the address with the biggest scope must be returned.  E.g., a globally
+// scoped and routable IP is prefered over a unique local address (ULA).  Also,
+// the IP address that is returned must be permanent.
+//
+// If destination address is NULL, then any IP address that is valid for
+// |destination| set to 0.0.0.0 (IPv4) or :: (IPv6) can be returned.
+//
+// |out_source_address|: This function places the address of the local interface
+// in this output variable.
+// |out_netmask|: This parameter is optional.  If a non-NULL value is passed in,
+// this function places the netmask associated with the source address in this
+// output variable.
+SB_EXPORT bool SbSocketGetInterfaceAddress(
+    const SbSocketAddress* const destination,
+    SbSocketAddress* out_source_address,
+    SbSocketAddress* out_netmask);
+
+#else
+
+// Gets the address of the local IPv4 network interface. The return value
+// indicates whether the address was retrieved successfully.
+//
+// |out_address|: The retrieved address. The address does not include loopback
+//   (or IPv6) addresses.
+SB_EXPORT bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* out_address);
+
+#endif  // SB_API_VERSION >= 4
+
 // Reads up to |data_size| bytes from |socket| into |out_data| and places the
 // source address of the packet in |out_source| if out_source is not NULL.
 // Returns the number of bytes read, or a negative number if there is an error,
@@ -355,13 +406,6 @@
 SB_EXPORT bool SbSocketJoinMulticastGroup(SbSocket socket,
                                           const SbSocketAddress* address);
 
-// Gets the address of the local IPv4 network interface. The return value
-// indicates whether the address was retrieved successfully.
-//
-// |out_address|: The retrieved address. The address does not include loopback
-//   (or IPv6) addresses.
-SB_EXPORT bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* out_address);
-
 // Synchronously resolves |hostname| into the returned SbSocketResolution,
 // which must be freed with SbSocketFreeResolution. The function returns
 // |NULL| if it is unable to resolve |hostname|.
@@ -391,11 +435,28 @@
   if (address.type == kSbSocketAddressTypeIpv6) {
     os << std::hex << "[";
     const uint16_t* fields = reinterpret_cast<const uint16_t*>(address.address);
-    for (int i = 0; i < 8; ++i) {
+    int i = 0;
+    while (fields[i] == 0) {
+      if (i == 0) {
+        os << ":";
+      }
+      ++i;
+      if (i == 8) {
+        os << ":";
+      }
+    }
+    for (; i < 8; ++i) {
       if (i != 0) {
         os << ":";
       }
+#if SB_IS(LITTLE_ENDIAN)
+      const uint8_t* fields8 = reinterpret_cast<const uint8_t*>(&fields[i]);
+      const uint16_t value = static_cast<uint16_t>(fields8[0]) * 256 |
+                             static_cast<uint16_t>(fields8[1]);
+      os << value;
+#else
       os << fields[i];
+#endif
     }
     os << "]" << std::dec;
   } else {
diff --git a/src/starboard/speech_recognizer.h b/src/starboard/speech_recognizer.h
new file mode 100644
index 0000000..48af10c
--- /dev/null
+++ b/src/starboard/speech_recognizer.h
@@ -0,0 +1,201 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Module Overview: Starboard speech recognizer module
+//
+// Defines a streaming speech recognizer API. It provides access to the platform
+// speech recognition service.
+//
+// Note that there can be only one speech recognizer. Attempting to create a
+// second speech recognizer without destroying the first one will result in
+// undefined behavior.
+//
+// |SbSpeechRecognizerCreate|, |SbSpeechRecognizerStart|,
+// |SbSpeechRecognizerStop|, |SbSpeechRecognizerCancel| and
+// |SbSpeechRecognizerDestroy| should be called from a single thread. Callbacks
+// defined in |SbSpeechRecognizerHandler| will happen on another thread, so
+// calls back into the SbSpeechRecognizer from the callback thread are
+// disallowed.
+
+#ifndef STARBOARD_SPEECH_RECOGNIZER_H_
+#define STARBOARD_SPEECH_RECOGNIZER_H_
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+    SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// An opaque handle to an implementation-private structure that represents a
+// speech recognizer.
+typedef struct SbSpeechRecognizerPrivate* SbSpeechRecognizer;
+
+// Well-defined value for an invalid speech recognizer handle.
+#define kSbSpeechRecognizerInvalid ((SbSpeechRecognizer)NULL)
+
+// Indicates whether the given speech recognizer is valid.
+static SB_C_INLINE bool SbSpeechRecognizerIsValid(
+    SbSpeechRecognizer recognizer) {
+  return recognizer != kSbSpeechRecognizerInvalid;
+}
+
+// Indicates what has gone wrong with the recognition.
+typedef enum SbSpeechRecognizerError {
+  // No speech was detected. Speech timed out.
+  kSbNoSpeechError,
+  // Speech input was aborted somehow.
+  kSbAborted,
+  // Audio capture failed.
+  kSbAudioCaptureError,
+  // Some network communication that was required to complete the recognition
+  // failed.
+  kSbNetworkError,
+  // The implementation is not allowing any speech input to occur for reasons of
+  // security, privacy or user preference.
+  kSbNotAllowed,
+  // The implementation is not allowing the application requested speech
+  // service, but would allow some speech service, to be used either because the
+  // implementation doesn't support the selected one or because of reasons of
+  // security, privacy or user preference.
+  kSbServiceNotAllowed,
+  // There was an error in the speech recognition grammar or semantic tags, or
+  // the grammar format or semantic tag format is supported.
+  kSbBadGrammar,
+  // The language was not supported.
+  kSbLanguageNotSupported,
+} SbSpeechRecognizerError;
+
+// The recognition response that is received from the recognizer.
+typedef struct SbSpeechResult {
+  // The raw words that the user spoke.
+  char* transcript;
+  // A numeric estimate between 0 and 1 of how confident the recognition system
+  // is that the recognition is correct. A higher number means the system is
+  // more confident. NaN represents an unavailable confidence score.
+  float confidence;
+} SbSpeechResult;
+
+typedef struct SbSpeechConfiguration {
+  // When the continuous value is set to false, the implementation MUST
+  // return no more than one final result in response to starting recognition.
+  // When the continuous attribute is set to true, the implementation MUST
+  // return zero or more final results representing multiple consecutive
+  // recognitions in response to starting recognition. This attribute setting
+  // does not affect interim results.
+  bool continuous;
+  // Controls whether interim results are returned. When set to true, interim
+  // results SHOULD be returned. When set to false, interim results MUST NOT be
+  // returned. This value setting does not affect final results.
+  bool interim_results;
+  // This sets the maximum number of SbSpeechResult in
+  // |SbSpeechRecognizerOnResults| callback.
+  int max_alternatives;
+} SbSpeechConfiguration;
+
+// A function to notify that the user has started to speak or stops speaking.
+// |detected|: true if the user has started to speak, and false if the user
+// stops speaking.
+typedef void (*SbSpeechRecognizerSpeechDetectedFunction)(void* context,
+                                                         bool detected);
+
+// A function to notify that a speech recognition error occurred.
+// |error|: The occurred speech recognition error.
+typedef void (*SbSpeechRecognizerErrorFunction)(void* context,
+                                                SbSpeechRecognizerError error);
+
+// A function to notify that the recognition results are ready.
+// |results|: the list of recognition results.
+// |results_size|: the number of |results|.
+// |is_final|: indicates if the |results| is final.
+typedef void (*SbSpeechRecognizerResultsFunction)(void* context,
+                                                  SbSpeechResult* results,
+                                                  int results_size,
+                                                  bool is_final);
+
+// Allows receiving notifications from the device when recognition related
+// events occur.
+//
+// The void* context is passed to every function.
+struct SbSpeechRecognizerHandler {
+  // Function to notify the beginning/end of the speech.
+  SbSpeechRecognizerSpeechDetectedFunction on_speech_detected;
+
+  // Function to notify the speech error.
+  SbSpeechRecognizerErrorFunction on_error;
+
+  // Function to notify that the recognition results are available.
+  SbSpeechRecognizerResultsFunction on_results;
+
+  // This is passed to handler functions as first argument.
+  void* context;
+};
+
+// Creates a speech recognizer with a speech recognizer handler.
+//
+// If the system has a speech recognition service available, this function
+// returns the newly created handle.
+//
+// If no speech recognition service is available on the device, this function
+// returns |kSbSpeechRecognizerInvalid|.
+//
+// |SbSpeechRecognizerCreate| does not expect the passed
+// SbSpeechRecognizerHandler structure to live after |SbSpeechRecognizerCreate|
+// is called, so the implementation must copy the contents if necessary.
+SB_EXPORT SbSpeechRecognizer
+SbSpeechRecognizerCreate(const SbSpeechRecognizerHandler* handler);
+
+// Starts listening to audio and recognizing speech with the specified speech
+// configuration. If |SbSpeechRecognizerStart| is called on an already
+// started speech recognizer, the implementation MUST ignore the call and return
+// false.
+//
+// Returns whether the speech recognizer is started successfully.
+SB_EXPORT bool SbSpeechRecognizerStart(
+    SbSpeechRecognizer recognizer,
+    const SbSpeechConfiguration* configuration);
+
+// Stops listening to audio and returns a result using just the audio that it
+// has already received. Once |SbSpeechRecognizerStop| is called, the
+// implementation MUST NOT collect additional audio and MUST NOT continue to
+// listen to the user. This is important for privacy reasons. If
+// |SbSpeechRecognizerStop| is called on a speech recognizer which is already
+// stopped or being stopped, the implementation MUST ignore the call.
+SB_EXPORT void SbSpeechRecognizerStop(SbSpeechRecognizer recognizer);
+
+// Cancels speech recognition. The speech recognizer stops listening to
+// audio and does not return any information. When |SbSpeechRecognizerCancel| is
+// called, the implementation MUST NOT collect additional audio, MUST NOT
+// continue to listen to the user, and MUST stop recognizing. This is important
+// for privacy reasons. If |SbSpeechRecognizerCancel| is called on a speech
+// recognizer which is already stopped or cancelled, the implementation MUST
+// ignore the call.
+SB_EXPORT void SbSpeechRecognizerCancel(SbSpeechRecognizer recognizer);
+
+// Destroys the given speech recognizer. If the speech recognizer is in the
+// started state, it is first stopped and then destroyed.
+SB_EXPORT void SbSpeechRecognizerDestroy(SbSpeechRecognizer recognizer);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+        // SB_SPEECH_RECOGNIZER_API_VERSION
+
+#endif  // STARBOARD_SPEECH_RECOGNIZER_H_
diff --git a/src/starboard/speech_synthesis.h b/src/starboard/speech_synthesis.h
index 6bc568e..fe0de16 100644
--- a/src/starboard/speech_synthesis.h
+++ b/src/starboard/speech_synthesis.h
@@ -35,7 +35,7 @@
 extern "C" {
 #endif
 
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
 // DEPRECATED IN API VERSION 4
 // Must be called before any other function in this module,
 // or subsequent calls are allowed to fail silently.
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index 1b60f71..edf093a 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -134,6 +134,9 @@
 // Whether the current platform has microphone supported.
 #define SB_HAS_MICROPHONE 1
 
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 1
+
 // Whether the current platform has speech synthesis.
 #define SB_HAS_SPEECH_SYNTHESIS 1
 
@@ -350,7 +353,7 @@
 // supported composition methods below.
 #define SB_HAS_PLAYER 1
 
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
 // Specifies whether this platform's player will produce an OpenGL texture that
 // the client must draw every frame with its graphics rendering. It may be that
 // we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -369,7 +372,7 @@
 // this case, changing the video bounds must be tightly synchronized between the
 // player and the graphics plane.
 #define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif  // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION < 4
 
 // After a seek is triggerred, the default behavior is to append video frames
 // from the last key frame before the seek time and append audio frames from the
@@ -384,41 +387,19 @@
 // not available should define the following quirk.
 #undef SB_HAS_QUIRK_NO_FFS
 
-// Specifies the maximum amount of memory used by audio buffers of media source
-// before triggering a garbage collection.  A large value will cause more memory
-// being used by audio buffers but will also make JavaScript app less likely to
-// re-download audio data.  Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT (3U * 1024U * 1024U)
+#if SB_API_VERSION >= 4
 
-// Specifies the maximum amount of memory used by video buffers of media source
-// before triggering a garbage collection.  A large value will cause more memory
-// being used by video buffers but will also make JavaScript app less likely to
-// re-download video data.  Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT (16U * 1024U * 1024U)
+// The maximum audio bitrate the platform can decode.  The following value
+// equals to 5M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
 
-// Specifies how much memory to reserve up-front for the main media buffer
-// (usually resides inside the CPU memory) used by media source and demuxers.
-// The main media buffer can work in one of the following two ways:
-// 1. If GPU buffer is used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is non-zero), the
-//    main buffer will be used as a cache so a media buffer will be copied from
-//    GPU memory to main memory before sending to the decoder for further
-//    processing.  In this case this macro should be set to a value that is
-//    large enough to hold all media buffers being decoded.
-// 2. If GPU buffer is not used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is zero) all
-//    media buffers will reside in the main memory buffer.  In this case the
-//    macro should be set to a value that is greater than the sum of the above
-//    source buffer stream memory limits with extra room to take account of
-//    fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
+// The maximum video bitrate the platform can decode.  The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
 
-// Specifies how much GPU memory to reserve up-front for media source buffers.
-// This should only be set to non-zero on system with limited CPU memory and
-// excess GPU memory so the app can store media buffer in GPU memory.
-// SB_MEDIA_MAIN_BUFFER_BUDGET has to be set to a non-zero value to avoid
-// media buffers being decoded when being stored in GPU.
-#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif  // SB_API_VERSION >= 4
 
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi
index 01b6680..3a62ad7 100644
--- a/src/starboard/stub/gyp_configuration.gypi
+++ b/src/starboard/stub/gyp_configuration.gypi
@@ -26,6 +26,8 @@
     # there for acceptable values for this variable.
     'javascript_engine': 'mozjs',
 
+    'cobalt_media_source_2016': 1,
+
     # Define platform specific compiler and linker flags.
     # Refer to base.gypi for a list of all available variables.
     'compiler_flags_host': [
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp
index ccdd05b..e2ab6dc 100644
--- a/src/starboard/stub/starboard_platform.gyp
+++ b/src/starboard/stub/starboard_platform.gyp
@@ -49,6 +49,12 @@
         '<(DEPTH)/starboard/shared/stub/condition_variable_signal.cc',
         '<(DEPTH)/starboard/shared/stub/condition_variable_wait.cc',
         '<(DEPTH)/starboard/shared/stub/condition_variable_wait_timed.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+        '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
         '<(DEPTH)/starboard/shared/stub/directory_can_open.cc',
         '<(DEPTH)/starboard/shared/stub/directory_close.cc',
         '<(DEPTH)/starboard/shared/stub/directory_create.cc',
@@ -87,8 +93,10 @@
         '<(DEPTH)/starboard/shared/stub/media_can_play_mime_and_key_system.cc',
         '<(DEPTH)/starboard/shared/stub/media_get_audio_configuration.cc',
         '<(DEPTH)/starboard/shared/stub/media_get_audio_output_count.cc',
+        '<(DEPTH)/starboard/shared/stub/media_is_audio_supported.cc',
         '<(DEPTH)/starboard/shared/stub/media_is_output_protected.cc',
         '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
+        '<(DEPTH)/starboard/shared/stub/media_is_video_supported.cc',
         '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
         '<(DEPTH)/starboard/shared/stub/memory_allocate_aligned_unchecked.cc',
         '<(DEPTH)/starboard/shared/stub/memory_allocate_unchecked.cc',
@@ -137,6 +145,7 @@
         '<(DEPTH)/starboard/shared/stub/socket_destroy.cc',
         '<(DEPTH)/starboard/shared/stub/socket_free_resolution.cc',
         '<(DEPTH)/starboard/shared/stub/socket_get_last_error.cc',
+        '<(DEPTH)/starboard/shared/stub/socket_get_interface_address.cc',
         '<(DEPTH)/starboard/shared/stub/socket_get_local_address.cc',
         '<(DEPTH)/starboard/shared/stub/socket_get_local_interface_address.cc',
         '<(DEPTH)/starboard/shared/stub/socket_is_connected.cc',
@@ -160,6 +169,11 @@
         '<(DEPTH)/starboard/shared/stub/socket_waiter_wait.cc',
         '<(DEPTH)/starboard/shared/stub/socket_waiter_wait_timed.cc',
         '<(DEPTH)/starboard/shared/stub/socket_waiter_wake_up.cc',
+        '<(DEPTH)/starboard/shared/stub/speech_recognizer_cancel.cc',
+        '<(DEPTH)/starboard/shared/stub/speech_recognizer_create.cc',
+        '<(DEPTH)/starboard/shared/stub/speech_recognizer_destroy.cc',
+        '<(DEPTH)/starboard/shared/stub/speech_recognizer_start.cc',
+        '<(DEPTH)/starboard/shared/stub/speech_recognizer_stop.cc',
         '<(DEPTH)/starboard/shared/stub/speech_synthesis_cancel.cc',
         '<(DEPTH)/starboard/shared/stub/speech_synthesis_set_language.cc',
         '<(DEPTH)/starboard/shared/stub/speech_synthesis_speak.cc',
@@ -214,7 +228,10 @@
         '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
         '<(DEPTH)/starboard/shared/stub/system_is_debugger_attached.cc',
         '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
+        '<(DEPTH)/starboard/shared/stub/system_request_pause.cc',
         '<(DEPTH)/starboard/shared/stub/system_request_stop.cc',
+        '<(DEPTH)/starboard/shared/stub/system_request_suspend.cc',
+        '<(DEPTH)/starboard/shared/stub/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/stub/system_sort.cc',
         '<(DEPTH)/starboard/shared/stub/system_symbolize.cc',
         '<(DEPTH)/starboard/shared/stub/thread_create.cc',
diff --git a/src/starboard/system.h b/src/starboard/system.h
index 8532ad8..a56d034 100644
--- a/src/starboard/system.h
+++ b/src/starboard/system.h
@@ -45,6 +45,18 @@
   // screenshots) can be written into.
   kSbSystemPathDebugOutputDirectory,
 
+#if SB_API_VERSION >= 4
+  // Path to a directory where system font files can be found. Should only be
+  // specified on platforms that provide fonts usable by Starboard applications.
+  kSbSystemPathFontDirectory,
+
+  // Path to a directory where system font configuration metadata can be
+  // found. May be the same directory as |kSbSystemPathFontDirectory|, but not
+  // necessarily. Should only be specified on platforms that provide fonts
+  // usable by Starboard applications.
+  kSbSystemPathFontConfigurationDirectory,
+#endif  // SB_API_VERSION >= 4
+
   // Path to the directory containing the root of the source tree.
   kSbSystemPathSourceDirectory,
 
@@ -104,6 +116,10 @@
   // Speech APIs and generate a Speech API key.
   kSbSystemPropertySpeechApiKey,
 #endif  // SB_VERSION(2)
+#if SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+  // A field that, if available, is appended to the user agent
+  kSbSystemPropertyUserAgentAuxField,
+#endif  // SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
 } SbSystemPropertyId;
 
 // Enumeration of device types.
@@ -129,10 +145,10 @@
   // Desktop PC.
   kSbSystemDeviceTypeDesktopPC,
 
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
   // An Android TV Device.
   kSbSystemDeviceTypeAndroidTV,
-#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
   // Unknown device.
   kSbSystemDeviceTypeUnknown,
@@ -261,6 +277,9 @@
 
 // Breaks the current program into the debugger, if a debugger is attached.
 // If a debugger is not attached, this function aborts the program.
+#if SB_API_VERSION >= 4
+SB_NORETURN
+#endif
 SB_EXPORT void SbSystemBreakIntoDebugger();
 
 // Attempts to determine whether the current program is running inside or
@@ -431,6 +450,43 @@
                                  char* out_buffer,
                                  int buffer_size);
 
+#if SB_API_VERSION >= 4
+// Requests that the application move into the Paused state at the next
+// convenient point. This should roughly correspond to "unfocused application"
+// in a traditional window manager, where the application may be partially
+// visible.
+//
+// This function eventually causes a |kSbEventTypePause| event to be dispatched
+// to the application. Before the |kSbEventTypePause| event is dispatched, some
+// work may continue to be done, and unrelated system events may be dispatched.
+SB_EXPORT void SbSystemRequestPause();
+
+// Requests that the application move into the Started state at the next
+// convenient point. This should roughly correspond to a "focused application"
+// in a traditional window manager, where the application is fully visible and
+// the primary receiver of input events.
+//
+// This function eventually causes a |kSbEventTypeUnpause| event to be
+// dispatched to the application. Before |kSbEventTypeUnpause| is dispatched,
+// some work may continue to be done, and unrelated system events may be
+// dispatched.
+SB_EXPORT void SbSystemRequestUnpause();
+
+// Requests that the application move into the Suspended state at the next
+// convenient point. This should roughly correspond to "minimization" in a
+// traditional window manager, where the application is no longer visible.
+//
+// This function eventually causes a |kSbEventTypeSuspend| event to be
+// dispatched to the application. Before the |kSbEventTypeSuspend| event is
+// dispatched, some work may continue to be done, and unrelated system events
+// may be dispatched.
+//
+// In the Suspended state, the application will be resident, but probably not
+// running. The expectation is that an external system event will bring the
+// application out of the Suspended state.
+SB_EXPORT void SbSystemRequestSuspend();
+#endif  // SB_API_VERSION >= 4
+
 // Requests that the application be terminated gracefully at the next
 // convenient point. In the meantime, some work may continue to be done, and
 // unrelated system events may be dispatched. This function eventually causes
diff --git a/src/starboard/tizen/armv7l/configuration_public.h b/src/starboard/tizen/armv7l/configuration_public.h
index 9b1c14a..f27972c 100644
--- a/src/starboard/tizen/armv7l/configuration_public.h
+++ b/src/starboard/tizen/armv7l/configuration_public.h
@@ -119,6 +119,8 @@
 // player and the graphics plane.
 #define SB_IS_PLAYER_PUNCHED_OUT 1
 
+#if SB_API_VERSION < 4
+
 // Specifies the maximum amount of memory used by audio buffers of media source
 // before triggering a garbage collection.  A large value will cause more memory
 // being used by audio buffers but will also make JavaScript app less likely to
@@ -155,6 +157,8 @@
 // media buffers being decoded when being stored in GPU.
 #define SB_MEDIA_GPU_BUFFER_BUDGET 0U
 
+#endif  // SB_API_VERSION < 4
+
 // Specifies whether this platform has webm/vp9 support.  This should be set to
 // non-zero on platforms with webm/vp9 support.
 #define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
diff --git a/src/starboard/tizen/armv7l/starboard_common.gyp b/src/starboard/tizen/armv7l/starboard_common.gyp
index faa178c..6da8396 100644
--- a/src/starboard/tizen/armv7l/starboard_common.gyp
+++ b/src/starboard/tizen/armv7l/starboard_common.gyp
@@ -204,7 +204,10 @@
         '<(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_pause.cc',
         '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+        '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
         '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
index effc33e..02aaa9e 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -297,7 +297,7 @@
 namespace player {
 namespace filter {
 
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
 // static
 bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
                                        SbMediaVideoCodec codec,
@@ -307,7 +307,7 @@
 
   return output_mode == kSbPlayerOutputModePunchOut;
 }
-#endif  // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif  // SB_API_VERSION >= 4
 
 }  // namespace filter
 }  // namespace player
diff --git a/src/starboard/tizen/shared/system_get_path.cc b/src/starboard/tizen/shared/system_get_path.cc
index f3f7c6d..b20b051 100644
--- a/src/starboard/tizen/shared/system_get_path.cc
+++ b/src/starboard/tizen/shared/system_get_path.cc
@@ -153,6 +153,10 @@
                              path_size);
     case kSbSystemPathExecutableFile:
       return GetExecutablePath(out_path, path_size);
+
+    default:
+      SB_NOTIMPLEMENTED();
+      return false;
   }
 
   int length = strlen(path);
diff --git a/src/starboard/types.h b/src/starboard/types.h
index 04a1263..64a27c0 100644
--- a/src/starboard/types.h
+++ b/src/starboard/types.h
@@ -128,6 +128,10 @@
 #define LONG_MAX INT_MAX
 #endif
 
+#if !defined(ULONG_MAX)
+#define ULONG_MAX UINT_MAX
+#endif
+
 #else  // SB_HAS(32_BIT_LONG)
 
 SB_COMPILE_ASSERT(sizeof(long) == sizeof(int64_t),  // NOLINT[runtime/int]
@@ -140,6 +144,10 @@
 #define LONG_MAX SB_INT64_C(0x7FFFFFFFFFFFFFFF)
 #endif
 
+#if !defined(ULONG_MAX)
+#define ULONG_MAX SB_INT64_C(0xFFFFFFFFFFFFFFFF)
+#endif
+
 #endif  // SB_HAS(32_BIT_LONG)
 
 #if defined(_MSC_VER)
diff --git a/src/starboard/user.h b/src/starboard/user.h
index b12a764..6e66ed4 100644
--- a/src/starboard/user.h
+++ b/src/starboard/user.h
@@ -56,7 +56,7 @@
   kSbUserPropertyUserId,
 } SbUserPropertyId;
 
-#if SB_API_VERSION < SB_DELETE_USER_APPLICATION_TOKEN_VERSION
+#if SB_API_VERSION < 4
 #if SB_HAS(USER_APPLICATION_LINKING_SUPPORT)
 // Information about an application-specific authorization token.
 typedef struct SbUserApplicationTokenResults {
diff --git a/src/testing/gtest.gyp b/src/testing/gtest.gyp
index 7b6821f..2b125f1 100644
--- a/src/testing/gtest.gyp
+++ b/src/testing/gtest.gyp
@@ -173,10 +173,7 @@
                   'GTEST_USE_OWN_TR1_TUPLE=1',
                 ],
               }],
-              ['target_arch=="xb1" or target_arch=="xb360"', {
-                'gtest_defines' : [
-                  '_VARIADIC_MAX=10',
-                ],
+              ['target_os=="win"', {
                 'gtest_defines!' : [
                   'GTEST_USE_OWN_TR1_TUPLE=1',
                 ],
diff --git a/src/testing/gtest/src/gtest.cc b/src/testing/gtest/src/gtest.cc
index 9268208..550b156 100644
--- a/src/testing/gtest/src/gtest.cc
+++ b/src/testing/gtest/src/gtest.cc
@@ -47,6 +47,7 @@
 #else
 #include <math.h>
 #include <stdarg.h>
+#include "starboard/system.h"
 #endif
 
 #include <algorithm>
@@ -58,7 +59,11 @@
 #include <sstream>
 #include <vector>
 
-#if GTEST_OS_LINUX
+#if GTEST_OS_STARBOARD
+
+// Starboard does not require any additional includes.
+
+#elif GTEST_OS_LINUX
 
 // TODO(kenton@google.com): Use autoconf to detect availability of
 // gettimeofday().
@@ -4181,6 +4186,9 @@
       // when a failure happens and both the --gtest_break_on_failure and
       // the --gtest_catch_exceptions flags are specified.
       DebugBreak();
+
+#elif GTEST_OS_STARBOARD
+      SbSystemBreakIntoDebugger();
 #else
       // Dereference NULL through a volatile pointer to prevent the compiler
       // from removing. We use this rather than abort() or __builtin_trap() for
diff --git a/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py b/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py
index f64a7f9..6799f9a 100755
--- a/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py
+++ b/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py
@@ -173,7 +173,7 @@
             'full_paths': [],
             'include_paths': [],
         })
-        self.enumerations = set()
+        self.enumerations = {}
         self.union_types = set()
         self.typedefs = {}
         self.callback_functions = {}
@@ -229,12 +229,16 @@
                 'component_dir': idl_filename_to_component(idl_filename),
                 'full_path': os.path.realpath(idl_filename),
             }
-        # Check enum duplication.
-        for enum_name in definitions.enumerations.keys():
-            for defined_enum in self.enumerations:
-                if defined_enum.name == enum_name:
-                    raise Exception('Enumeration %s has multiple definitions' % enum_name)
-        self.enumerations.update(definitions.enumerations.values())
+
+        # Include more information about the enum for Cobalt.
+        for enum_name, values in definitions.enumerations.iteritems():
+            # Check enum duplication.
+            if self.enumerations.has_key(enum_name):
+                raise Exception('Enumeration %s has multiple definitions' % enum_name)
+            self.enumerations[enum_name] = {
+                'values': values,
+                'full_path': os.path.realpath(idl_filename),
+            }
 
         if definitions.interfaces:
             definition = next(definitions.interfaces.itervalues())
@@ -342,8 +346,7 @@
         """Returns component wide information as a dict."""
         return {
             'callback_functions': self.callback_functions,
-            'enumerations': dict((enum.name, enum.values)
-                                 for enum in self.enumerations),
+            'enumerations': self.enumerations,
             'typedefs': self.typedefs,
             'union_types': self.union_types,
         }
diff --git a/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py b/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py
index 5a032ed..1a8a7b0 100644
--- a/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py
+++ b/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py
@@ -159,7 +159,9 @@
     # Sort so order consistent, so can compare output from run to run.
     for dependency_idl_filename in sorted(dependency_idl_filenames):
         dependency_definitions = reader.read_idl_file(dependency_idl_filename)
-        dependency_component = idl_filename_to_component(dependency_idl_filename)
+        # For Cobalt, always use the component where |definitions| is located
+        # so interfaces will get merged together.
+        dependency_component = component
 
         dependency_interface = next(dependency_definitions.interfaces.itervalues())
         dependency_interface_basename, _ = os.path.splitext(os.path.basename(dependency_idl_filename))
diff --git a/src/third_party/dlmalloc/dlmalloc.gyp b/src/third_party/dlmalloc/dlmalloc.gyp
index ee777e0..64c0743 100644
--- a/src/third_party/dlmalloc/dlmalloc.gyp
+++ b/src/third_party/dlmalloc/dlmalloc.gyp
@@ -13,7 +13,7 @@
       'include_dirs': [
       ],
       'conditions': [
-        ['target_arch=="xb1" or target_arch=="xb360"', {
+        ['target_os=="win"', {
           # Compile dlmalloc.c as C++ on MSVC.
           'msvs_settings': {
             'VCCLCompilerTool': {
diff --git a/src/third_party/icu/source/common/putil.cpp b/src/third_party/icu/source/common/putil.cpp
index 7743792..8bed6aa 100644
--- a/src/third_party/icu/source/common/putil.cpp
+++ b/src/third_party/icu/source/common/putil.cpp
@@ -129,7 +129,7 @@
  */
 #include <time.h>
 
-#if !U_PLATFORM_USES_ONLY_WIN32_API
+#if !U_PLATFORM_USES_ONLY_WIN32_API && !defined(STARBOARD)
 #include <sys/time.h>
 #endif
 
diff --git a/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp b/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp
index ce7acc6..6248c5d 100644
--- a/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp
@@ -83,6 +83,8 @@
 
 #endif // defined(MOZ_HAVE_RDTSC)
 
+#if defined(JS_TRACE_LOGGING)
+
 class AutoTraceLoggerThreadStateLock
 {
   TraceLoggerThreadState* logging;
@@ -1029,3 +1031,5 @@
 
     return *this;
 }
+
+#endif  // defined(JS_TRACE_LOGGING)
diff --git a/src/third_party/mozjs-45/js/src/vm/TraceLogging.h b/src/third_party/mozjs-45/js/src/vm/TraceLogging.h
index 270478c..d09609c 100644
--- a/src/third_party/mozjs-45/js/src/vm/TraceLogging.h
+++ b/src/third_party/mozjs-45/js/src/vm/TraceLogging.h
@@ -111,7 +111,11 @@
         return !!payload_;
     }
 
+#if defined(JS_TRACE_LOGGING)
     TraceLoggerEvent& operator=(const TraceLoggerEvent& other);
+#else
+    TraceLoggerEvent& operator=(const TraceLoggerEvent& other) { return *this; }
+#endif
     TraceLoggerEvent(const TraceLoggerEvent& event) = delete;
 };
 
diff --git a/src/third_party/mozjs-45/mozjs-45.gyp b/src/third_party/mozjs-45/mozjs-45.gyp
index a3d1941..d082045 100644
--- a/src/third_party/mozjs-45/mozjs-45.gyp
+++ b/src/third_party/mozjs-45/mozjs-45.gyp
@@ -61,6 +61,7 @@
       'type': 'static_library',
       'cflags': [
         '-Wno-invalid-offsetof',
+        '-Wno-undefined-var-template',
         '-Wno-unused-function',
         '-include <(DEPTH)/third_party/mozjs-45/cobalt_config/include/js-confdefs.h',
       ],
@@ -78,6 +79,7 @@
         ],
         'cflags': [
           '-Wno-invalid-offsetof',
+          '-Wno-undefined-var-template',
           '-Wno-unused-function',
           '-include <(DEPTH)/third_party/mozjs-45/cobalt_config/include/js-confdefs.h',
         ],
@@ -99,6 +101,11 @@
               'JS_NUNBOX32=1',
             ],
           }],
+          ['cobalt_config != "gold"', {
+            'defines': [
+              'JS_TRACE_LOGGING=1',
+            ],
+          }],
         ],
       },
       'include_dirs': [
diff --git a/src/third_party/openssl/openssl/crypto/des/des_locl.h b/src/third_party/openssl/openssl/crypto/des/des_locl.h
index 3ce8268..5833de0 100644
--- a/src/third_party/openssl/openssl/crypto/des/des_locl.h
+++ b/src/third_party/openssl/openssl/crypto/des/des_locl.h
@@ -77,7 +77,7 @@
 #  if !defined(OPENSSL_SYS_VMS) || defined(__DECC)
 #   ifdef OPENSSL_UNISTD
 #    include OPENSSL_UNISTD
-#   else
+#   elif !defined(OPENSSL_SYS_STARBOARD)
 #    include <unistd.h>
 #   endif
 #   include <math.h>
diff --git a/src/third_party/openssl/openssl/crypto/evp/e_aes.c b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
index dfa60ce..c81005f 100644
--- a/src/third_party/openssl/openssl/crypto/evp/e_aes.c
+++ b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
@@ -64,6 +64,10 @@
 #  include "modes_lcl.h"
 #  include <openssl/rand.h>
 
+#if defined(OPENSSL_SYS_STARBOARD)
+# include "starboard/cryptography.h"
+#endif
+
 typedef struct {
     AES_KEY ks;
     block128_f block;
@@ -108,6 +112,235 @@
 
 #  define MAXBITCHUNK     ((size_t)1<<(sizeof(size_t)*8-4))
 
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+static bool sb_translate_mode(int flags, SbCryptographyBlockCipherMode *mode) {
+  switch (flags & EVP_CIPH_MODE) {
+    case EVP_CIPH_CBC_MODE:
+      *mode = kSbCryptographyBlockCipherModeCbc;
+      return true;
+    case EVP_CIPH_CFB_MODE:
+      *mode = kSbCryptographyBlockCipherModeCfb;
+      return true;
+    case EVP_CIPH_CTR_MODE:
+      *mode = kSbCryptographyBlockCipherModeCtr;
+      return true;
+    case EVP_CIPH_ECB_MODE:
+      *mode = kSbCryptographyBlockCipherModeEcb;
+      return true;
+    case EVP_CIPH_OFB_MODE:
+      *mode = kSbCryptographyBlockCipherModeOfb;
+      return true;
+    default:
+      break;
+  }
+
+  return false;
+}
+
+static inline bool sb_has_stream(EVP_CIPHER_CTX *context) {
+  return SbCryptographyIsTransformerValid(context->stream_transformer);
+}
+
+static inline bool sb_gcm_has_stream(GCM128_CONTEXT *context) {
+  return SbCryptographyIsTransformerValid(context->gcm_transformer);
+}
+
+static int sb_init_key(EVP_CIPHER_CTX* context,
+                       const unsigned char* key,
+                       const unsigned char* initialization_vector,
+                       int encrypt) {
+  SbCryptographyDirection direction =
+      (encrypt ? kSbCryptographyDirectionEncode :
+       kSbCryptographyDirectionDecode);
+
+  SbCryptographyBlockCipherMode mode;
+  if (!sb_translate_mode(context->cipher->flags, &mode)) {
+    return 0;
+  }
+
+  context->stream_transformer =
+      SbCryptographyCreateTransformer(
+          kSbCryptographyAlgorithmAes,
+          context->cipher->block_size * 8,
+          direction,
+          mode,
+          initialization_vector,
+          context->cipher->iv_len,
+          key,
+          context->cipher->key_len);
+
+  return sb_has_stream(context) ? 1 : 0;
+}
+
+static inline int sb_cipher(EVP_CIPHER_CTX *context,
+                            unsigned char *out,
+                            const unsigned char *in,
+                            size_t len) {
+  if (!sb_has_stream(context)) {
+    return 0;
+  }
+
+  int result = SbCryptographyTransform(context->stream_transformer,
+                                       in,
+                                       len,
+                                       out);
+  return (result == len);
+}
+
+static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key, int key_length,
+                        int encrypt) {
+  SbCryptographyDirection direction =
+      (encrypt ? kSbCryptographyDirectionEncode :
+       kSbCryptographyDirectionDecode);
+  context->gcm_transformer =
+      SbCryptographyCreateTransformer(
+          kSbCryptographyAlgorithmAes,
+          128,
+          direction,
+          kSbCryptographyBlockCipherModeGcm,
+          NULL,
+          0,
+          key,
+          key_length);
+  return sb_gcm_has_stream(context);
+}
+
+static inline void sb_gcm_setiv(GCM128_CONTEXT *context,
+                                const unsigned char *initialization_vector,
+                                size_t initialization_vector_size) {
+  if (sb_gcm_has_stream(context)) {
+    SbCryptographySetInitializationVector(context->gcm_transformer,
+                                          initialization_vector,
+                                          initialization_vector_size);
+    return;
+  }
+
+  CRYPTO_gcm128_setiv(context, initialization_vector,
+                      initialization_vector_size);
+}
+
+static inline int sb_gcm_aad(GCM128_CONTEXT *context,
+                             const unsigned char *data,
+                             size_t data_size) {
+  if (sb_gcm_has_stream(context)) {
+    return SbCryptographySetAuthenticatedData(context->gcm_transformer,
+                                              data, data_size) ? 0 : -1;
+  }
+
+  return CRYPTO_gcm128_aad(context, data, data_size);
+}
+
+static inline int sb_gcm_encrypt(GCM128_CONTEXT *context,
+                                 const unsigned char *in, unsigned char *out,
+                                 size_t len) {
+  if (sb_gcm_has_stream(context)) {
+    int result =
+        SbCryptographyTransform(context->gcm_transformer, in, len, out);
+    return result == len ? 0 : -1;
+  }
+
+  return CRYPTO_gcm128_encrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_decrypt(GCM128_CONTEXT *context,
+                                 const unsigned char *in, unsigned char *out,
+                                 size_t len) {
+  if (sb_gcm_has_stream(context)) {
+    int result =
+        SbCryptographyTransform(context->gcm_transformer, in, len, out);
+    return result == len ? 0 : -1;
+  }
+
+  return CRYPTO_gcm128_decrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_finish(GCM128_CONTEXT *context,
+                                const unsigned char *tag, size_t len) {
+  if (sb_gcm_has_stream(context)) {
+    unsigned char actual_tag[16];
+    SbCryptographyGetTag(context->gcm_transformer, actual_tag, 16);
+    return SbMemoryCompare(tag, actual_tag, len) == 0 ? 0 : -1;
+  }
+
+  return CRYPTO_gcm128_finish(context, tag, len);
+}
+
+static inline void sb_gcm_tag(GCM128_CONTEXT *context,
+                              unsigned char *tag, size_t len) {
+  if (sb_gcm_has_stream(context)) {
+    SbCryptographyGetTag(context->gcm_transformer, tag, len);
+    return;
+  }
+
+  CRYPTO_gcm128_tag(context, tag, len);
+}
+
+#define SB_TRY_INIT(context, key, iv, encrypt)  \
+  if (sb_init_key(context, key, iv, encrypt)) { \
+    return 1;                                   \
+  }
+
+#define SB_TRY_CIPHER(context, out, in, len) \
+  if (sb_cipher(context, out, in, len)) {    \
+    return 1;                                \
+  }
+
+#else  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+
+static inline bool sb_has_stream(EVP_CIPHER_CTX *context) {
+  return false;
+}
+
+static inline bool sb_gcm_has_stream(GCM128_CONTEXT *context) {
+  return false;
+}
+
+static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key, int key_length,
+                        int encrypt) {
+  return false;
+}
+
+static inline void sb_gcm_setiv(GCM128_CONTEXT *context,
+                                const unsigned char *initialization_vector,
+                                size_t initialization_vector_size) {
+  CRYPTO_gcm128_setiv(context, initialization_vector,
+                      initialization_vector_size);
+}
+
+static inline int sb_gcm_aad(GCM128_CONTEXT *context,
+                             const unsigned char *data,
+                             size_t data_size) {
+  return CRYPTO_gcm128_aad(context, data, data_size);
+}
+
+static inline int sb_gcm_encrypt(GCM128_CONTEXT *context,
+                                 const unsigned char *in, unsigned char *out,
+                                 size_t len) {
+  return CRYPTO_gcm128_encrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_decrypt(GCM128_CONTEXT *context,
+                                 const unsigned char *in, unsigned char *out,
+                                 size_t len) {
+  return CRYPTO_gcm128_decrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_finish(GCM128_CONTEXT *context,
+                                const unsigned char *tag, size_t len) {
+  return CRYPTO_gcm128_finish(context, tag, len);
+}
+
+static inline void sb_gcm_tag(GCM128_CONTEXT *context, unsigned char *tag,
+                              size_t len) {
+  CRYPTO_gcm128_tag(context, tag, len);
+}
+
+#define SB_TRY_INIT(context, key, iv, encrypt)
+
+#define SB_TRY_CIPHER(context, out, in, len)
+
+#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+
 #  ifdef VPAES_ASM
 int vpaes_set_encrypt_key(const unsigned char *userKey, int bits,
                           AES_KEY *key);
@@ -479,6 +712,8 @@
     int ret, mode;
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_INIT(ctx, key, iv, enc);
+
     mode = ctx->cipher->flags & EVP_CIPH_MODE;
     if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
         && !enc)
@@ -542,6 +777,8 @@
 {
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     if (dat->stream.cbc)
         (*dat->stream.cbc) (in, out, len, &dat->ks, ctx->iv, ctx->encrypt);
     else if (ctx->encrypt)
@@ -559,6 +796,8 @@
     size_t i;
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     if (len < bl)
         return 1;
 
@@ -573,6 +812,8 @@
 {
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     CRYPTO_ofb128_encrypt(in, out, len, &dat->ks,
                           ctx->iv, &ctx->num, dat->block);
     return 1;
@@ -583,6 +824,8 @@
 {
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     CRYPTO_cfb128_encrypt(in, out, len, &dat->ks,
                           ctx->iv, &ctx->num, ctx->encrypt, dat->block);
     return 1;
@@ -593,6 +836,8 @@
 {
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     CRYPTO_cfb128_8_encrypt(in, out, len, &dat->ks,
                             ctx->iv, &ctx->num, ctx->encrypt, dat->block);
     return 1;
@@ -603,6 +848,8 @@
 {
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     if (ctx->flags & EVP_CIPH_FLAG_LENGTH_BITS) {
         CRYPTO_cfb128_1_encrypt(in, out, len, &dat->ks,
                                 ctx->iv, &ctx->num, ctx->encrypt, dat->block);
@@ -627,6 +874,8 @@
     unsigned int num = ctx->num;
     EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
 
+    SB_TRY_CIPHER(ctx, out, in, len);
+
     if (dat->stream.ctr)
         CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks,
                                     ctx->iv, ctx->buf, &num, dat->stream.ctr);
@@ -644,6 +893,11 @@
 static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
 {
     EVP_AES_GCM_CTX *gctx = c->cipher_data;
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+    SbCryptographyDestroyTransformer(gctx->gcm.gcm_transformer);
+    SbCryptographyDestroyTransformer(gctx->gcm.ctr_transformer);
+    SbCryptographyDestroyTransformer(gctx->gcm.ecb_transformer);
+#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
     OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
     if (gctx->iv != c->iv)
         OPENSSL_free(gctx->iv);
@@ -669,6 +923,7 @@
 static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
 {
     EVP_AES_GCM_CTX *gctx = c->cipher_data;
+
     switch (type) {
     case EVP_CTRL_INIT:
         gctx->key_set = 0;
@@ -678,6 +933,12 @@
         gctx->taglen = -1;
         gctx->iv_gen = 0;
         gctx->tls_aad_len = -1;
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+        gctx->ctr = NULL;
+        gctx->gcm.gcm_transformer = kSbCryptographyInvalidTransformer;
+        gctx->gcm.ctr_transformer = kSbCryptographyInvalidTransformer;
+        gctx->gcm.ecb_transformer = kSbCryptographyInvalidTransformer;
+#endif
         return 1;
 
     case EVP_CTRL_GCM_SET_IVLEN:
@@ -735,7 +996,7 @@
     case EVP_CTRL_GCM_IV_GEN:
         if (gctx->iv_gen == 0 || gctx->key_set == 0)
             return 0;
-        CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+        sb_gcm_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
         if (arg <= 0 || arg > gctx->ivlen)
             arg = gctx->ivlen;
         OPENSSL_port_memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
@@ -751,7 +1012,7 @@
         if (gctx->iv_gen == 0 || gctx->key_set == 0 || c->encrypt)
             return 0;
         OPENSSL_port_memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
-        CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+        sb_gcm_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
         gctx->iv_set = 1;
         return 1;
 
@@ -810,32 +1071,38 @@
         do {
 #  ifdef BSAES_CAPABLE
             if (BSAES_CAPABLE) {
-                AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
-                CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
-                                   (block128_f) AES_encrypt);
-                gctx->ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+                if (!sb_gcm_init(&gctx->gcm, key, ctx->key_len, enc)) {
+                    AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
+                    CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                                       (block128_f) AES_encrypt);
+                    gctx->ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+                }
                 break;
             } else
 #  endif
 #  ifdef VPAES_CAPABLE
             if (VPAES_CAPABLE) {
-                vpaes_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
-                CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
-                                   (block128_f) vpaes_encrypt);
-                gctx->ctr = NULL;
+                if (!sb_gcm_init(&gctx->gcm, key, ctx->key_len, enc)) {
+                    vpaes_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
+                    CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                                       (block128_f) vpaes_encrypt);
+                    gctx->ctr = NULL;
+                }
                 break;
             } else
 #  endif
                 (void)0;        /* terminate potentially open 'else' */
 
-            AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
-            CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
-                               (block128_f) AES_encrypt);
+            if (!sb_gcm_init(&gctx->gcm, key, ctx->key_len, enc)) {
+                AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
+                CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+                                   (block128_f) AES_encrypt);
 #  ifdef AES_CTR_ASM
-            gctx->ctr = (ctr128_f) AES_ctr32_encrypt;
+                gctx->ctr = (ctr128_f) AES_ctr32_encrypt;
 #  else
-            gctx->ctr = NULL;
+                gctx->ctr = NULL;
 #  endif
+            }
         } while (0);
 
         /*
@@ -844,14 +1111,14 @@
         if (iv == NULL && gctx->iv_set)
             iv = gctx->iv;
         if (iv) {
-            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+            sb_gcm_setiv(&gctx->gcm, iv, gctx->ivlen);
             gctx->iv_set = 1;
         }
         gctx->key_set = 1;
     } else {
         /* If key set use IV, otherwise copy */
         if (gctx->key_set)
-            CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+            sb_gcm_setiv(&gctx->gcm, iv, gctx->ivlen);
         else
             OPENSSL_port_memcpy(gctx->iv, iv, gctx->ivlen);
         gctx->iv_set = 1;
@@ -885,7 +1152,7 @@
                             EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
         goto err;
     /* Use saved AAD */
-    if (CRYPTO_gcm128_aad(&gctx->gcm, ctx->buf, gctx->tls_aad_len))
+    if (sb_gcm_aad(&gctx->gcm, ctx->buf, gctx->tls_aad_len))
         goto err;
     /* Fix buffer and length to point to payload */
     in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
@@ -898,12 +1165,12 @@
                                             in, out, len, gctx->ctr))
                 goto err;
         } else {
-            if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, len))
+            if (sb_gcm_encrypt(&gctx->gcm, in, out, len))
                 goto err;
         }
         out += len;
         /* Finally write tag */
-        CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
+        sb_gcm_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
         rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
     } else {
         /* Decrypt */
@@ -912,11 +1179,11 @@
                                             in, out, len, gctx->ctr))
                 goto err;
         } else {
-            if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, len))
+            if (sb_gcm_decrypt(&gctx->gcm, in, out, len))
                 goto err;
         }
         /* Retrieve tag */
-        CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, EVP_GCM_TLS_TAG_LEN);
+        sb_gcm_tag(&gctx->gcm, ctx->buf, EVP_GCM_TLS_TAG_LEN);
         /* If tag mismatch wipe buffer */
         if (CRYPTO_memcmp(ctx->buf, in + len, EVP_GCM_TLS_TAG_LEN)) {
             OPENSSL_cleanse(out, len);
@@ -946,7 +1213,7 @@
         return -1;
     if (in) {
         if (out == NULL) {
-            if (CRYPTO_gcm128_aad(&gctx->gcm, in, len))
+            if (sb_gcm_aad(&gctx->gcm, in, len))
                 return -1;
         } else if (ctx->encrypt) {
             if (gctx->ctr) {
@@ -954,7 +1221,7 @@
                                                 in, out, len, gctx->ctr))
                     return -1;
             } else {
-                if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, len))
+                if (sb_gcm_encrypt(&gctx->gcm, in, out, len))
                     return -1;
             }
         } else {
@@ -963,7 +1230,7 @@
                                                 in, out, len, gctx->ctr))
                     return -1;
             } else {
-                if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, len))
+                if (sb_gcm_decrypt(&gctx->gcm, in, out, len))
                     return -1;
             }
         }
@@ -972,12 +1239,12 @@
         if (!ctx->encrypt) {
             if (gctx->taglen < 0)
                 return -1;
-            if (CRYPTO_gcm128_finish(&gctx->gcm, ctx->buf, gctx->taglen) != 0)
+            if (sb_gcm_finish(&gctx->gcm, ctx->buf, gctx->taglen) != 0)
                 return -1;
             gctx->iv_set = 0;
             return 0;
         }
-        CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, 16);
+        sb_gcm_tag(&gctx->gcm, ctx->buf, 16);
         gctx->taglen = 16;
         /* Don't reuse the IV */
         gctx->iv_set = 0;
diff --git a/src/third_party/openssl/openssl/crypto/evp/evp.h b/src/third_party/openssl/openssl/crypto/evp/evp.h
index 6cf98ac..6e50fa7 100644
--- a/src/third_party/openssl/openssl/crypto/evp/evp.h
+++ b/src/third_party/openssl/openssl/crypto/evp/evp.h
@@ -75,6 +75,10 @@
 #  include <openssl/bio.h>
 # endif
 
+#if defined(OPENSSL_SYS_STARBOARD)
+#  include "starboard/cryptography.h"
+#endif  // defined(STARBOARD)
+
 /*-
 #define EVP_RC2_KEY_SIZE                16
 #define EVP_RC4_KEY_SIZE                16
@@ -432,6 +436,15 @@
     int buf_len;                /* number we have left */
     unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */
     unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+    // A handle to a transformer for the full stream cipher. Intermediate state
+    // is kept internally to the transformer, so fields like |iv| and |num| are
+    // not used.
+    //
+    // This is not used for GCM. The GCM transformer is inside the GCM context,
+    // which is stored as the |cipher_data| for the GCM cipher.
+    SbCryptographyTransformer stream_transformer;
+#endif  // defined(STARBOARD) && SB_API_VERSION >= 4
     unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */
     int num;                    /* used by cfb/ofb/ctr mode */
     void *app_data;             /* application stuff */
diff --git a/src/third_party/openssl/openssl/crypto/evp/evp_enc.c b/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
index 5cd2037..3021e52 100644
--- a/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
+++ b/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
@@ -72,6 +72,10 @@
 #endif
 #include "evp_locl.h"
 
+#if defined(OPENSSL_SYS_STARBOARD)
+#include "starboard/cryptography.h"
+#endif  // defined(OPENSSL_SYS_STARBOARD)
+
 #ifdef OPENSSL_FIPS
 # define M_do_cipher(ctx, out, in, inl) FIPS_cipher(ctx, out, in, inl)
 #else
@@ -544,6 +548,9 @@
 
 int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c)
 {
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+    SbCryptographyDestroyTransformer(c->stream_transformer);
+#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
 #ifndef OPENSSL_FIPS
     if (c->cipher != NULL) {
         if (c->cipher->cleanup && !c->cipher->cleanup(c))
diff --git a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
index 296849b..54febc9 100644
--- a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
+++ b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
@@ -6,6 +6,11 @@
  */
 
 #include <openssl/modes.h>
+#include <openssl/opensslconf.h>
+
+#if defined(OPENSSL_SYS_STARBOARD)
+#include "starboard/cryptography.h"
+#endif
 
 #if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
 typedef __int64 i64;
@@ -115,6 +120,23 @@
     unsigned int mres, ares;
     block128_f block;
     void *key;
+
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+    // A handle to a HW-accelerated transformer for the GCM block cipher
+    // mode. If valid, then this is used instead of any other state in the
+    // context.
+    SbCryptographyTransformer gcm_transformer;
+
+    // A handle to a HW-accelerated transformer for the CTR block cipher
+    // mode. If valid, then the software GCM code will wrap the hardware CTR
+    // stream.
+    SbCryptographyTransformer ctr_transformer;
+
+    // A handle to a HW-accelerated transformer for the ECB block cipher
+    // mode. If valid, then the software GCM code will wrap the hardware ECB
+    // block cipher.
+    SbCryptographyTransformer ecb_transformer;
+#endif
 };
 
 struct xts128_context {
diff --git a/src/third_party/openssl/openssl/ssl/dtls1.h b/src/third_party/openssl/openssl/ssl/dtls1.h
index 8a6ad50..950cffb 100644
--- a/src/third_party/openssl/openssl/ssl/dtls1.h
+++ b/src/third_party/openssl/openssl/ssl/dtls1.h
@@ -60,6 +60,8 @@
 #ifndef HEADER_DTLS1_H
 # define HEADER_DTLS1_H
 
+#include "build/build_config.h"
+
 # include <openssl/buffer.h>
 # include <openssl/pqueue.h>
 # ifdef OPENSSL_SYS_VMS
@@ -74,7 +76,7 @@
 # else
 #  if defined(OPENSSL_SYS_VXWORKS)
 #   include <sys/times.h>
-#  else
+#  elif !defined(OS_STARBOARD)
 #   include <sys/time.h>
 #  endif
 # endif
diff --git a/src/third_party/skia/gyp/gpu.gypi b/src/third_party/skia/gyp/gpu.gypi
index eaaf5d0..e5827cb 100644
--- a/src/third_party/skia/gyp/gpu.gypi
+++ b/src/third_party/skia/gyp/gpu.gypi
@@ -7,8 +7,6 @@
 #
 {
   'defines': [
-    'COBALT_SKIA_GLYPH_ATLAS_WIDTH=<(skia_glyph_atlas_width)',
-    'COBALT_SKIA_GLYPH_ATLAS_HEIGHT=<(skia_glyph_atlas_height)',
   ],
   'variables': {
     'skgpu_sources': [
diff --git a/src/third_party/skia/include/core/SkMath.h b/src/third_party/skia/include/core/SkMath.h
index 014f014..625f5c7 100644
--- a/src/third_party/skia/include/core/SkMath.h
+++ b/src/third_party/skia/include/core/SkMath.h
@@ -10,6 +10,8 @@
 #ifndef SkMath_DEFINED
 #define SkMath_DEFINED
 
+#include "build/build_config.h"
+
 #include "SkTypes.h"
 
 // 64bit -> 32bit utilities
@@ -72,7 +74,7 @@
 int SkCLZ_portable(uint32_t);
 
 #ifndef SkCLZ
-    #if defined(_MSC_VER) && _MSC_VER >= 1400
+    #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(OS_STARBOARD)
         #include <intrin.h>
 
         static inline int SkCLZ(uint32_t mask) {
diff --git a/src/third_party/skia/include/gpu/GrContext.h b/src/third_party/skia/include/gpu/GrContext.h
index 45cd599..8450152 100644
--- a/src/third_party/skia/include/gpu/GrContext.h
+++ b/src/third_party/skia/include/gpu/GrContext.h
@@ -62,7 +62,9 @@
     /**
      * Creates a GrContext for a backend context.
      */
-    static GrContext* Create(GrBackend, GrBackendContext, const Options* opts = NULL);
+    static GrContext* Create(GrBackend, GrBackendContext,
+                             int atlas_tex_width, int atlas_tex_height,
+                             const Options* opts = NULL);
 
     virtual ~GrContext();
 
@@ -1029,9 +1031,14 @@
 
     int                             fMaxTextureSizeOverride;
 
+    // The size of the glyph atlas texture.
+    int atlas_texture_width;
+    int atlas_texture_height;
+
     const Options                   fOptions;
 
-    GrContext(const Options&); // init must be called after the constructor.
+    // init must be called after the constructor.
+    GrContext(int atlas_tex_width, int atlas_tex_height, const Options&);
     bool init(GrBackend, GrBackendContext);
 
     void setupDrawBuffer();
diff --git a/src/third_party/skia/src/gpu/GrContext.cpp b/src/third_party/skia/src/gpu/GrContext.cpp
index ad1b4c4..32a530e 100755
--- a/src/third_party/skia/src/gpu/GrContext.cpp
+++ b/src/third_party/skia/src/gpu/GrContext.cpp
@@ -86,12 +86,14 @@
 };
 
 GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext,
+                             int atlas_tex_width, int atlas_tex_height,
                              const Options* opts) {
-    GrContext* context;
-    if (NULL == opts) {
-        context = SkNEW_ARGS(GrContext, (Options()));
-    } else {
-        context = SkNEW_ARGS(GrContext, (*opts));
+
+    GrContext* context;

+    if (NULL == opts) {

+        context = SkNEW_ARGS(GrContext, (atlas_tex_width, atlas_tex_height, Options()));

+    } else {

+        context = SkNEW_ARGS(GrContext, (atlas_tex_width, atlas_tex_height, *opts));

     }
 
     if (context->init(backend, backendContext)) {
@@ -102,7 +104,11 @@
     }
 }
 
-GrContext::GrContext(const Options& opts) : fOptions(opts) {
+GrContext::GrContext(int atlas_tex_width, int atlas_tex_height,
+                     const Options& opts) :
+      atlas_texture_width(atlas_tex_width),
+      atlas_texture_height(atlas_tex_height),
+      fOptions(opts) {
     fDrawState = NULL;
     fGpu = NULL;
     fClip = NULL;
@@ -121,7 +127,8 @@
     fMaxTextureSizeOverride = 1 << 20;
 }
 
-bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
+bool GrContext::init(GrBackend backend,
+                     GrBackendContext backendContext) {
     SkASSERT(NULL == fGpu);
 
     fGpu = GrGpu::Create(backend, backendContext, this);
@@ -137,7 +144,9 @@
     fResourceCache->setOverbudgetCallback(OverbudgetCB, this);
     fResourceCache2 = SkNEW(GrResourceCache2);
 
-    fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
+    fFontCache = SkNEW_ARGS(GrFontCache, (atlas_texture_width,
+                                          atlas_texture_height,
+                                          fGpu));
 
     fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this)));
 
diff --git a/src/third_party/skia/src/gpu/GrTextStrike.cpp b/src/third_party/skia/src/gpu/GrTextStrike.cpp
index ef1bf64..74952e6 100644
--- a/src/third_party/skia/src/gpu/GrTextStrike.cpp
+++ b/src/third_party/skia/src/gpu/GrTextStrike.cpp
@@ -17,11 +17,6 @@
 
 #if defined(COBALT)
 
-// On Cobalt we would like to avoid re-rasterizing glyphs as much as possible,
-// so increase the default atlas size.
-#define GR_ATLAS_TEXTURE_WIDTH COBALT_SKIA_GLYPH_ATLAS_WIDTH
-#define GR_ATLAS_TEXTURE_HEIGHT COBALT_SKIA_GLYPH_ATLAS_HEIGHT
-
 // On Cobalt, not being able to fit glyphs into the atlas is a big penalty,
 // since its software rendering is not optimized.  Increase the plot size
 // to allow it to accommodate larger glyphs and avoid this situation as
@@ -31,23 +26,22 @@
 
 #else
 
-#define GR_ATLAS_TEXTURE_WIDTH 1024
-#define GR_ATLAS_TEXTURE_HEIGHT 2048
-
 #define GR_PLOT_WIDTH  256
 #define GR_PLOT_HEIGHT 256
 
 #endif  // defined(COBALT)
 
-#define GR_NUM_PLOTS_X   (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
-#define GR_NUM_PLOTS_Y   (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
-
 #define FONT_CACHE_STATS 0
 #if FONT_CACHE_STATS
 static int g_PurgeCount = 0;
 #endif
 
-GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
+GrFontCache::GrFontCache(int atlas_texture_width,
+                         int atlas_texture_height,
+                         GrGpu* gpu)
+    : atlas_texture_width_(atlas_texture_width),
+      atlas_texture_height_(atlas_texture_height),
+      fGpu(gpu) {
     gpu->ref();
     for (int i = 0; i < kAtlasCount; ++i) {
         fAtlases[i] = NULL;
@@ -101,12 +95,12 @@
     GrPixelConfig config = mask_format_to_pixel_config(format);
     int atlasIndex = mask_format_to_atlas_index(format);
     if (NULL == fAtlases[atlasIndex]) {
-        SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
-                                            GR_ATLAS_TEXTURE_HEIGHT);
+        SkISize textureSize = SkISize::Make(atlas_texture_width_,
+                                            atlas_texture_height_);
         fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags,
                                                     textureSize,
-                                                    GR_NUM_PLOTS_X,
-                                                    GR_NUM_PLOTS_Y,
+                                                    num_plots_x(),
+                                                    num_plots_y(),
                                                     true));
     }
     GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
@@ -365,3 +359,12 @@
     glyph->fPlot = plot;
     return true;
 }
+
+
+int GrFontCache::num_plots_x() const {
+  return (atlas_texture_width_ / GR_PLOT_WIDTH);
+}
+
+int GrFontCache::num_plots_y() const {
+  return (atlas_texture_height_ / GR_PLOT_HEIGHT);
+}
diff --git a/src/third_party/skia/src/gpu/GrTextStrike.h b/src/third_party/skia/src/gpu/GrTextStrike.h
index 401bd73..06a21df 100644
--- a/src/third_party/skia/src/gpu/GrTextStrike.h
+++ b/src/third_party/skia/src/gpu/GrTextStrike.h
@@ -80,7 +80,9 @@
 
 class GrFontCache {
 public:
-    GrFontCache(GrGpu*);
+    GrFontCache(int atlas_texture_width,
+                int atlas_texture_height,
+                GrGpu*);
     ~GrFontCache();
 
     inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField);
@@ -133,6 +135,12 @@
     GrTextStrike* generateStrike(GrFontScaler*);
     inline void detachStrikeFromList(GrTextStrike*);
     void purgeStrike(GrTextStrike* strike);
+
+    int num_plots_x() const;
+    int num_plots_y() const;
+
+    int atlas_texture_width_;
+    int atlas_texture_height_;
 };
 
 #endif
diff --git a/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h b/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h
index f093dba..ad25d3e 100644
--- a/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h
+++ b/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h
@@ -13,8 +13,11 @@
 #include "SkScalerContext.h"
 #include "SkTypeface.h"
 
-#include <ft2build.h>
-#include FT_FREETYPE_H
+// These are forward declared to avoid pimpl but also hide the FreeType implementation.
+typedef struct FT_LibraryRec_* FT_Library;
+typedef struct FT_FaceRec_* FT_Face;
+typedef struct FT_StreamRec_* FT_Stream;
+typedef signed long FT_Pos;
 
 #ifdef SK_DEBUG
     #define SkASSERT_CONTINUE(pred)                                                         \
diff --git a/src/third_party/super_fast_hash/super_fast_hash.cc b/src/third_party/super_fast_hash/super_fast_hash.cc
index 6618791..ecd1177 100644
--- a/src/third_party/super_fast_hash/super_fast_hash.cc
+++ b/src/third_party/super_fast_hash/super_fast_hash.cc
@@ -85,4 +85,4 @@
   hash += hash >> 6;
 
   return hash;
-}
\ No newline at end of file
+}
diff --git a/src/tools/clang/scripts/update.py b/src/tools/clang/scripts/update.py
index 617d863..ddd9098 100755
--- a/src/tools/clang/scripts/update.py
+++ b/src/tools/clang/scripts/update.py
@@ -405,12 +405,70 @@
            % (version_out, VERSION))
     sys.exit(1)
 
+# Returns true if there is no stdout/stderr
+# Or if there is a stdout/stderr and the user types "Y" or "y".
+def AskIfWantToWipeOutFolder():
+  if sys.stdin.isatty() and (sys.stdout.isatty() or sys.stderr.isatty()):
+    # Prompt the user, if there's a TTY.
+    reply = raw_input('clang was updated. You need to do a clean build. Want me'
+                      ' (update.py) to clean out your out/ folder? [yN]');
+    if reply == "y" or reply == "Y":
+      return True
+
+    return False
+
+  return True
+
+
+# Clobber the out/ folder for configs that use clang.
+def ClobberOutFolderForClang():
+  should_wipe_out_folder = None
+
+  directory_name_of_current_file = os.path.dirname(os.path.abspath(__file__))
+  out_dir_with_dots = os.path.join(directory_name_of_current_file, '..', '..',
+                                   '..', 'out')
+  out_dir = os.path.abspath(out_dir_with_dots)
+
+  if not os.path.exists(out_dir):
+    return
+
+  out_subdirs = [os.path.join(out_dir, d)
+                 for d in os.listdir(out_dir)]
+  for build_folder in filter(os.path.isdir, out_subdirs):
+    if not os.path.exists(os.path.join(build_folder, 'build.ninja')):
+      continue
+
+    package_version_filename = os.path.join(build_folder, 'cr_build_revision')
+    package_version = None
+    try:
+      with open(package_version_filename, 'r') as f:
+        package_version = f.read()
+    except IOError:
+      # File does not exist
+      pass
+
+    if package_version == PACKAGE_VERSION:
+      continue
+
+    if should_wipe_out_folder is None:
+      should_wipe_out_folder = AskIfWantToWipeOutFolder()
+
+    if should_wipe_out_folder is False:
+      break
+
+    RunCommand(['ninja', '-C', build_folder, '-t', 'clean'])
+
+    with open(package_version_filename, 'w') as f:
+      f.write(PACKAGE_VERSION)
+
 
 def UpdateClang(args):
 
   # Required for LTO, which is used when is_official_build = true.
   need_gold_plugin = sys.platform.startswith('linux')
 
+  ClobberOutFolderForClang()
+
   if ReadStampFile(
       STAMP_FILE) == PACKAGE_VERSION and not args.force_local_build:
     if not need_gold_plugin or os.path.exists(
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 7c01386..f05c6a9 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -576,11 +576,7 @@
       return '%s %s: %s' % (verb, self.name, fallback)
 
   def IsCygwinRule(self, action):
-    if platform.system() == 'Linux':
-      return False
-    elif self.flavor == 'win':
-      return self.msvs_settings.IsRuleRunUnderCygwin(action)
-    elif self.flavor in ['ps3', 'xb1', 'ps4'] :
+    if self.flavor in ['ps3', 'ps4']:
       return str(action.get('msvs_cygwin_shell', 1)) != '0'
     return False
 
@@ -1675,7 +1671,7 @@
   if flavor in ['win', 'xb1']:
     master_ninja.variable('ld', ld)
     master_ninja.variable('idl', 'midl.exe')
-    master_ninja.variable('ar', 'lib.exe')
+    master_ninja.variable('ar', os.environ.get('AR', 'ar'))
     master_ninja.variable('rc', 'rc.exe')
     master_ninja.variable('asm', 'ml.exe')
     master_ninja.variable('mt', 'mt.exe')
diff --git a/src/tools/gyp/pylib/gyp/msvs_emulation.py b/src/tools/gyp/pylib/gyp/msvs_emulation.py
index 4f75aa3..cf81dc9 100755
--- a/src/tools/gyp/pylib/gyp/msvs_emulation.py
+++ b/src/tools/gyp/pylib/gyp/msvs_emulation.py
@@ -618,7 +618,7 @@
     def midl(name, default=None):
       return self.ConvertVSMacros(midl_get(name, default=default),
                                   config=config)
-    if config.startswith('XB1'):
+    if config.startswith('xb1'):
       tlb = ''
       header = midl('HeaderFileName', default='${root}.h')
       dlldata = ''
@@ -634,7 +634,7 @@
       # Note that .tlb is not included in the outputs as it is not always
       # generated depending on the content of the input idl file.
       outdir = midl('OutputDirectory', default='')
-    if config.startswith('XB1'):
+    if config.startswith('xb1'):
       output = [header]
     else:
       output = [header, dlldata, iid, proxy]
@@ -643,9 +643,9 @@
                  ('dlldata', dlldata),
                  ('iid', iid),
                  ('proxy', proxy)]
-    if config.startswith('XB1'):
-      metadata_dir = '"%s%s"' % (os.environ.get('DurangoXDK'),
-          'adk\\references\\commonconfiguration\\neutral')
+    if config.startswith('xb1'):
+      metadata_dir = '"%s%s"' % ('C:\\Program Files (x86)\\Windows Kits\\10\\',
+          'UnionMetadata')
       flags = ['/env', 'x64', '/W1', '/char', 'signed', '/enum_class',
                '/metadata_dir', metadata_dir, '/notlb', '/winrt']
     else:
@@ -829,17 +829,13 @@
           'lbshell', 'build', 'platforms', 'DurangoVars.cmd'),
       'ADK'
   ]
-  # Using cygwin python so there is a bit of wrapping done to get the
-  # dos environment via set:
+  # Get the dos environment via set:
   # Use cmd /c to execute under native windows command
   args_cmd  = 'cmd /c '
-  # Convert the cygwin path i.e. /cygdrive/c .. to C:\ ..
-  args_bat  = '\"`cygpath -d \'' + vs_args[0] + '\'` '
-  # Create a list of the remaining arguments to the bat file
-  args_args = ' '.join(vs_args[1:])
-  args_set  = ' && set\"'
+  args_set  = '\"set\"'
 
-  args = args_cmd + args_bat + args_args + args_set
+  args = args_cmd + args_set
+
   popen = subprocess.Popen(
       args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
   variables, _ = popen.communicate()