Import Cobalt 6.15306

Change-Id: I401726588e8df7b26ef2af62f94a4ab270663b94
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index 4edc13f..d0875f2 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -153,6 +153,10 @@
 
   // Load channel count.
   number_of_channels_ = load_uint16_little_endian(data + offset + 2);
+  if (number_of_channels_ == 0) {
+    DLOG(ERROR) << "No channel on WAV.";
+    return false;
+  }
 
   // Load sample rate.
   sample_rate_ =
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index 19652ec..29ad3e2 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -49,7 +49,6 @@
         'poller.h',
         'polymorphic_downcast.h',
         'polymorphic_equatable.h',
-        'sanitizer_options.cc',
         'source_location.h',
         'stop_watch.cc',
         'stop_watch.h',
diff --git a/src/cobalt/base/sanitizer_options.cc b/src/cobalt/base/sanitizer_options.cc
deleted file mode 100644
index b5311a5..0000000
--- a/src/cobalt/base/sanitizer_options.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(ADDRESS_SANITIZER)
-
-// Functions returning default options are declared weak in the tools' runtime
-// libraries. To make the linker pick the strong replacements for those
-// functions from this module, we explicitly force its inclusion by passing
-// -Wl,-u_sanitizer_options_link_helper
-extern "C" void _sanitizer_options_link_helper() { }
-
-// The callbacks we define here will be called from the sanitizer runtime, but
-// aren't referenced from the executable. We must ensure that those
-// callbacks are not sanitizer-instrumented, and that they aren't stripped by
-// the linker.
-#define SANITIZER_HOOK_ATTRIBUTE          \
-  extern "C"                              \
-  __attribute__((no_sanitize_address))    \
-  __attribute__((no_sanitize_memory))     \
-  __attribute__((no_sanitize_thread))     \
-  __attribute__((visibility("default")))  \
-  __attribute__((used))
-
-extern "C" char kLSanDefaultSuppressions[];
-
-char kLSanDefaultSuppressions[] =
-    // 16-byte leak from a call to calloc() in this library.
-    "leak:egl_gallium.so\n";
-
-SANITIZER_HOOK_ATTRIBUTE const char *__lsan_default_suppressions() {
-  return kLSanDefaultSuppressions;
-}
-
-#endif  // defined(ADDRESS_SANITIZER)
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 170907c..ffded5e 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -51,6 +51,8 @@
 #include "lbshell/src/lb_memory_pages.h"
 #endif  // defined(__LB_SHELL__)
 #if defined(OS_STARBOARD)
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_impl.h"
 #include "starboard/configuration.h"
 #include "starboard/log.h"
 #endif  // defined(OS_STARBOARD)
@@ -110,6 +112,19 @@
   }
   return webdriver_port;
 }
+
+std::string GetWebDriverListenIp() {
+  // The default port on which the webdriver server should listen for incoming
+  // connections.
+  std::string webdriver_listen_ip =
+      webdriver::WebDriverModule::kDefaultListenIp;
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kWebDriverListenIp)) {
+    webdriver_listen_ip =
+        command_line->GetSwitchValueASCII(switches::kWebDriverListenIp);
+  }
+  return webdriver_listen_ip;
+}
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 #endif  // ENABLE_WEBDRIVER
 
@@ -297,6 +312,14 @@
       start_time_(base::TimeTicks::Now()),
       stats_update_timer_(true, true),
       lite_stats_update_timer_(true, true) {
+  // Check to see if a timed_trace has been set, indicating that we should
+  // begin a timed trace upon startup.
+  base::TimeDelta trace_duration = GetTimedTraceDuration();
+  if (trace_duration != base::TimeDelta()) {
+    trace_event::TraceToFileForDuration(
+        FilePath(FILE_PATH_LITERAL("timed_trace.json")), trace_duration);
+  }
+
   DCHECK(MessageLoop::current());
   DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
 
@@ -316,14 +339,6 @@
       base::Bind(&Application::UpdatePeriodicLiteStats,
                  base::Unretained(this)));
 
-  // Check to see if a timed_trace has been set, indicating that we should
-  // begin a timed trace upon startup.
-  base::TimeDelta trace_duration = GetTimedTraceDuration();
-  if (trace_duration != base::TimeDelta()) {
-    trace_event::TraceToFileForDuration(
-        FilePath(FILE_PATH_LITERAL("timed_trace.json")), trace_duration);
-  }
-
   // Get the initial URL.
   GURL initial_url = GetInitialURL();
   DLOG(INFO) << "Initial URL: " << initial_url;
@@ -419,6 +434,20 @@
     DLOG(INFO) << "Use ShellRawVideoDecoderStub";
     options.media_module_options.use_video_decoder_stub = true;
   }
+  if (command_line->HasSwitch(switches::kMemoryTracker)) {
+#if defined(OS_STARBOARD)
+    using nb::analytics::MemoryTrackerPrintThread;
+    using nb::analytics::MemoryTracker;
+
+    DLOG(INFO) << "Using MemoryTracking";
+    MemoryTracker* memory_tracker = MemoryTracker::Get();
+    memory_tracker->InstallGlobalTrackingHooks();
+    memory_tracker_print_thread_ = CreateDebugPrintThread(memory_tracker);
+#else
+    DLOG(INFO)
+        << "Memory tracker is not enabled on non-starboard builds.";
+#endif
+  }
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
   base::optional<math::Size> viewport_size;
@@ -483,10 +512,10 @@
 #if defined(ENABLE_WEBDRIVER)
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   if (command_line->HasSwitch(switches::kEnableWebDriver)) {
-    int webdriver_port = GetWebDriverPort();
     web_driver_module_.reset(new webdriver::WebDriverModule(
-        webdriver_port, base::Bind(&BrowserModule::CreateSessionDriver,
-                                   base::Unretained(browser_module_.get())),
+        GetWebDriverPort(), GetWebDriverListenIp(),
+        base::Bind(&BrowserModule::CreateSessionDriver,
+                   base::Unretained(browser_module_.get())),
         base::Bind(&BrowserModule::RequestScreenshotToBuffer,
                    base::Unretained(browser_module_.get())),
         base::Bind(&BrowserModule::SetProxy,
@@ -521,6 +550,13 @@
 }
 
 Application::~Application() {
+#if defined(OS_STARBOARD)
+  // explicitly reset here because the destruction of the object is complex
+  // and involves a thread join. If this were to hang the app then having
+  // the destruction at this point gives a real file-line number and a place
+  // for the debugger to land.
+  memory_tracker_print_thread_.reset(NULL);
+#endif
   // Unregister event callbacks.
   event_dispatcher_.RemoveEventCallback(account::AccountEvent::TypeId(),
                                         account_event_callback_);
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index da9f8b1..a118a1c 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -33,6 +33,11 @@
 #include "cobalt/debug/debug_web_server.h"
 #endif
 
+#if defined(OS_STARBOARD)
+#include "nb/analytics/memory_tracker.h"
+#include "nb/scoped_ptr.h"
+#endif
+
 namespace cobalt {
 namespace browser {
 
@@ -173,6 +178,13 @@
 
   base::Timer stats_update_timer_;
   base::Timer lite_stats_update_timer_;
+
+#if defined(OS_STARBOARD)
+  // This thread (when active) will print out memory statistics of the engine.
+  // It is activated by the command line -memory_tracker.
+  nb::scoped_ptr<nb::analytics::MemoryTrackerPrintThread>
+      memory_tracker_print_thread_;
+#endif
 };
 
 // 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 1115bbd..3572dbb 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -75,6 +75,7 @@
         '<(DEPTH)/cobalt/webdriver/webdriver.gyp:webdriver',
         '<(DEPTH)/cobalt/xhr/xhr.gyp:xhr',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+        '<(DEPTH)/nb/nb.gyp:nb',
         'browser_bindings.gyp:bindings',
         'screen_shot_writer',
       ],
@@ -82,11 +83,6 @@
         ['enable_about_scheme == 1', {
           'defines': [ 'ENABLE_ABOUT_SCHEME' ],
         }],
-        ['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
-          'dependencies': [
-            '<(DEPTH)/nb/nb.gyp:nb',
-          ],
-        }],
       ],
     },
 
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 0911d57..e725572 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -37,6 +37,7 @@
 #include "cobalt/dom/keycode.h"
 #include "cobalt/h5vcc/h5vcc.h"
 #include "cobalt/input/input_device_manager_fuzzer.h"
+#include "nb/memory_scope.h"
 
 namespace cobalt {
 namespace browser {
@@ -135,10 +136,6 @@
           renderer_module_.pipeline()->GetResourceProvider())),
       array_buffer_cache_(new dom::ArrayBuffer::Cache(3 * 1024 * 1024)),
 #endif  // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
-      media_module_(media::MediaModule::Create(
-          system_window, renderer_module_.render_target()->GetSize(),
-          renderer_module_.pipeline()->GetResourceProvider(),
-          options.media_module_options)),
       network_module_(&storage_manager_, system_window->event_dispatcher(),
                       options.network_module_options),
       render_tree_combiner_(&renderer_module_,
@@ -171,6 +168,15 @@
       has_resumed_(true, false),
       will_quit_(false),
       suspended_(false) {
+  // All allocations for media will be tracked by "Media" memory scope.
+  {
+    TRACK_MEMORY_SCOPE("Media");
+    media_module_ = (media::MediaModule::Create(
+        system_window, renderer_module_.render_target()->GetSize(),
+        renderer_module_.pipeline()->GetResourceProvider(),
+        options.media_module_options));
+  }
+
   // Setup our main web module to have the H5VCC API injected into it.
   DCHECK(!ContainsKey(web_module_options_.injected_window_attributes, "h5vcc"));
   h5vcc::H5vcc::Settings h5vcc_settings;
@@ -258,6 +264,15 @@
                        renderer_module_.pipeline()->GetResourceProvider(),
                        kLayoutMaxRefreshFrequencyInHz));
 
+#if defined(STARBOARD)
+#if SB_HAS(1_CORE)
+  // Wait until the splash screen is ready before loading the main web module.
+  // This prevents starvation of the splash screen module and decoding of the
+  // splash screen image(s).
+  splash_screen_->WaitUntilReady();
+#endif
+#endif
+
   // Create new WebModule.
 #if !defined(COBALT_FORCE_CSP)
   web_module_options_.csp_insecure_allowed_token =
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index a2a3d97..0ccc88c 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -30,14 +30,17 @@
         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) {
+    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";
 
   web_module_.reset(new WebModule(
-      options.url, render_tree_produced_callback,
+      options.url,
+      base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
       base::Bind(&SplashScreen::OnError, base::Unretained(this)),
-      base::Closure(), /* window_close_callback */
+      base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
       &stub_media_module_, network_module, window_dimensions, resource_provider,
       layout_refresh_rate, web_module_options));
 }
@@ -47,5 +50,19 @@
   web_module_->Resume(resource_provider);
 }
 
+void SplashScreen::WaitUntilReady() {
+  is_ready_.Wait();
+}
+
+void SplashScreen::OnRenderTreeProduced(
+    const browser::WebModule::LayoutResults& layout_results) {
+  is_ready_.Signal();
+  render_tree_produced_callback_.Run(layout_results);
+}
+
+void SplashScreen::OnWindowClosed() {
+  is_ready_.Signal();
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index a105fe4..7888fa8 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -21,6 +21,7 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/media/media_module_stub.h"
 #include "googleurl/src/gurl.h"
@@ -48,13 +49,28 @@
   void Suspend();
   void Resume(render_tree::ResourceProvider* resource_provider);
 
+  // Block the caller until the splash screen is ready to be rendered.
+  void WaitUntilReady();
+
  private:
+  void OnRenderTreeProduced(
+      const browser::WebModule::LayoutResults& layout_results);
+
   void OnError(const GURL& /* url */, const std::string& error) {
+    is_ready_.Signal();
     LOG(ERROR) << error;
   }
 
+  void OnWindowClosed();
+
   media::MediaModuleStub stub_media_module_;
   scoped_ptr<WebModule> web_module_;
+
+  WebModule::OnRenderTreeProducedCallback render_tree_produced_callback_;
+
+  // Signalled once the splash screen has produced its first render tree or
+  // an error occurred.
+  base::WaitableEvent is_ready_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/stack_size_constants.h b/src/cobalt/browser/stack_size_constants.h
new file mode 100644
index 0000000..d700f4b
--- /dev/null
+++ b/src/cobalt/browser/stack_size_constants.h
@@ -0,0 +1,35 @@
+/*
+ * 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_STACK_SIZE_CONSTANTS_H_
+#define COBALT_BROWSER_STACK_SIZE_CONSTANTS_H_
+
+#include "cobalt/base/address_sanitizer.h"
+
+namespace cobalt {
+namespace browser {
+#if defined(COBALT_BUILD_TYPE_DEBUG)
+  // Non-optimized builds require a bigger stack size.
+  const size_t kBaseStackSize = 2 * 1024 * 1024;
+#else
+  const size_t kBaseStackSize = 256 * 1024;
+#endif
+  const size_t kWebModuleStackSize =
+      kBaseStackSize + base::kAsanAdditionalStackSize;
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_STACK_SIZE_CONSTANTS_H_
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 6332a9d..91e0e6d 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -101,6 +101,13 @@
 // Port that the WebDriver server should be listening on.
 const char kWebDriverPort[] = "webdriver_port";
 
+// IP that the WebDriver server should be listening on.
+// (INADDR_ANY if unspecified).
+const char kWebDriverListenIp[] = "webdriver_listen_ip";
+
+// Enables memory tracking by installing the memory tracker on startup.
+const char kMemoryTracker[] = "memory_tracker";
+
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
 // Determines the capacity of the image cache which manages image surfaces
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index a9b3d5f..7ef6a28 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -44,6 +44,8 @@
 extern const char kVideoContainerSizeOverride[];
 extern const char kVideoDecoderStub[];
 extern const char kWebDriverPort[];
+extern const char kWebDriverListenIp[];
+extern const char kMemoryTracker[];
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
 extern const char kImageCacheSizeInBytes[];
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index c21d48b..af93505 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -26,14 +26,19 @@
 #include "cobalt/base/c_val.h"
 #include "cobalt/base/poller.h"
 #include "cobalt/base/tokens.h"
+#include "cobalt/browser/stack_size_constants.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/browser/web_module_stat_tracker.h"
+#include "cobalt/css_parser/parser.h"
 #include "cobalt/debug/debug_server_module.h"
 #include "cobalt/dom/blob.h"
 #include "cobalt/dom/csp_delegate_factory.h"
+#include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/storage.h"
 #include "cobalt/dom/url.h"
+#include "cobalt/dom_parser/parser.h"
 #include "cobalt/h5vcc/h5vcc.h"
+#include "cobalt/script/javascript_engine.h"
 #include "cobalt/storage/storage_manager.h"
 
 namespace cobalt {
@@ -695,7 +700,8 @@
   // Start the dedicated thread and create the internal implementation
   // object on that thread.
   thread_.StartWithOptions(
-      base::Thread::Options(MessageLoop::TYPE_DEFAULT, kWebModuleStackSize));
+      base::Thread::Options(MessageLoop::TYPE_DEFAULT,
+        cobalt::browser::kWebModuleStackSize));
   DCHECK(message_loop());
 
   message_loop()->PostTask(
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index ba8bf30..34f38b2 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -198,15 +198,6 @@
   // can only be called if we have previously suspended the WebModule.
   void Resume(render_tree::ResourceProvider* resource_provider);
 
-#if defined(COBALT_BUILD_TYPE_DEBUG)
-  // Non-optimized builds require a bigger stack size.
-  static const size_t kBaseStackSize = 2 * 1024 * 1024;
-#else
-  static const size_t kBaseStackSize = 256 * 1024;
-#endif
-  static const size_t kWebModuleStackSize =
-      kBaseStackSize + base::kAsanAdditionalStackSize;
-
  private:
   // Data required to construct a WebModule, initialized in the constructor and
   // passed to |Initialize|.
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 260d24f..1cb6aab 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-14838
\ No newline at end of file
+15306
\ No newline at end of file
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index 030de0f..9ef3934 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -208,7 +208,7 @@
         'cobalt_fastbuild': os.environ.get('LB_FASTBUILD', 0),
         'cobalt_version': gyp_utils.GetBuildNumber(),
         'host_os': _GetHostOS(),
-        'CC': os.environ.get('CC', ''),
+        'CC_HOST': os.environ.get('CC_HOST', os.environ.get('CC','')),
     }
     if self.platform_config.IsStarboard():
       variables['OS'] = 'starboard'
diff --git a/src/cobalt/css_parser/position_parse_structures.cc b/src/cobalt/css_parser/position_parse_structures.cc
index a3f3422..50c13f3 100644
--- a/src/cobalt/css_parser/position_parse_structures.cc
+++ b/src/cobalt/css_parser/position_parse_structures.cc
@@ -119,7 +119,7 @@
           break;
         }
         default:
-          NOTREACHED();
+          // Position value couldn't take more than 4 elements.
           return false;
       }
       break;
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 4ffeaa6..71ab518 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -258,6 +258,7 @@
         '<(DEPTH)/cobalt/storage/storage.gyp:storage',
         '<(DEPTH)/cobalt/web_animations/web_animations.gyp:web_animations',
         '<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+        '<(DEPTH)/nb/nb.gyp:nb',
       ],
     },
 
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc
index fd85733..7a4802c 100644
--- a/src/cobalt/dom/html_link_element.cc
+++ b/src/cobalt/dom/html_link_element.cc
@@ -26,6 +26,7 @@
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/html_element_context.h"
 #include "googleurl/src/gurl.h"
+#include "nb/memory_scope.h"
 
 namespace cobalt {
 namespace dom {
@@ -54,6 +55,7 @@
 // Algorithm for Obtain:
 //   https://www.w3.org/TR/html5/document-metadata.html#concept-link-obtain
 void HTMLLinkElement::Obtain() {
+  TRACK_MEMORY_SCOPE("DOM");
   TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::Obtain()");
   // Custom, not in any spec.
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -107,6 +109,7 @@
 }
 
 void HTMLLinkElement::OnLoadingDone(const std::string& content) {
+  TRACK_MEMORY_SCOPE("DOM");
   DCHECK(thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::OnLoadingDone()");
 
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index a2851ee..762a0c3 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -320,9 +320,9 @@
   }
 }
 
-WebMediaPlayer::ReadyState HTMLMediaElement::ready_state() const {
+uint16_t HTMLMediaElement::ready_state() const {
   MLOG() << ready_state_;
-  return ready_state_;
+  return static_cast<uint16_t>(ready_state_);
 }
 
 bool HTMLMediaElement::seeking() const {
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index 1d9dfc2..90bfa7c 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -95,7 +95,7 @@
     kHaveEnoughData = WebMediaPlayer::kReadyStateHaveEnoughData,
   };
 
-  WebMediaPlayer::ReadyState ready_state() const;
+  uint16_t ready_state() const;
   bool seeking() const;
 
   // Playback state
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index e4b5178..5584557 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -32,6 +32,7 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/script_runner.h"
 #include "googleurl/src/gurl.h"
+#include "nb/memory_scope.h"
 
 namespace cobalt {
 namespace dom {
@@ -111,6 +112,7 @@
 // Algorithm for Prepare:
 //   https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script
 void HTMLScriptElement::Prepare() {
+  TRACK_MEMORY_SCOPE("DOM");
   // Custom, not in any spec.
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(MessageLoop::current());
@@ -495,6 +497,7 @@
 void HTMLScriptElement::Execute(const std::string& content,
                                 const base::SourceLocation& script_location,
                                 bool is_external) {
+  TRACK_MEMORY_SCOPE("DOM");
   // If should_execute_ is set to false for whatever reason, then we
   // immediately exit.
   // When inserted using the document.write() method, script elements execute
diff --git a/src/cobalt/dom/local_storage_database.cc b/src/cobalt/dom/local_storage_database.cc
index e5f2d11..6b7a4cc 100644
--- a/src/cobalt/dom/local_storage_database.cc
+++ b/src/cobalt/dom/local_storage_database.cc
@@ -19,6 +19,7 @@
 #include "base/debug/trace_event.h"
 #include "cobalt/dom/storage_area.h"
 #include "cobalt/storage/storage_manager.h"
+#include "nb/memory_scope.h"
 #include "sql/statement.h"
 
 namespace cobalt {
@@ -30,6 +31,7 @@
 const int kLatestLocalStorageSchemaVersion = 1;
 
 void SqlInit(storage::SqlContext* sql_context) {
+  TRACK_MEMORY_SCOPE("Storage");
   TRACE_EVENT0("cobalt::storage", "LocalStorage::SqlInit()");
   sql::Connection* conn = sql_context->sql_connection();
   int schema_version;
@@ -74,6 +76,7 @@
 void SqlReadValues(const std::string& id,
                    const LocalStorageDatabase::ReadCompletionCallback& callback,
                    storage::SqlContext* sql_context) {
+  TRACK_MEMORY_SCOPE("Storage");
   scoped_ptr<StorageArea::StorageMap> values(new StorageArea::StorageMap);
   sql::Connection* conn = sql_context->sql_connection();
   sql::Statement get_values(conn->GetCachedStatement(
@@ -91,6 +94,7 @@
 
 void SqlWrite(const std::string& id, const std::string& key,
               const std::string& value, storage::SqlContext* sql_context) {
+  TRACK_MEMORY_SCOPE("Storage");
   sql::Connection* conn = sql_context->sql_connection();
   sql::Statement write_statement(conn->GetCachedStatement(
       SQL_FROM_HERE,
@@ -105,6 +109,7 @@
 
 void SqlDelete(const std::string& id, const std::string& key,
                storage::SqlContext* sql_context) {
+  TRACK_MEMORY_SCOPE("Storage");
   sql::Connection* conn = sql_context->sql_connection();
   sql::Statement delete_statement(
       conn->GetCachedStatement(SQL_FROM_HERE,
@@ -117,6 +122,7 @@
 }
 
 void SqlClear(const std::string& id, storage::SqlContext* sql_context) {
+  TRACK_MEMORY_SCOPE("Storage");
   sql::Connection* conn = sql_context->sql_connection();
   sql::Statement clear_statement(
       conn->GetCachedStatement(SQL_FROM_HERE,
@@ -142,12 +148,14 @@
 
 void LocalStorageDatabase::ReadAll(const std::string& id,
                                    const ReadCompletionCallback& callback) {
+  TRACK_MEMORY_SCOPE("Storage");
   Init();
   storage_->GetSqlContext(base::Bind(&SqlReadValues, id, callback));
 }
 
 void LocalStorageDatabase::Write(const std::string& id, const std::string& key,
                                  const std::string& value) {
+  TRACK_MEMORY_SCOPE("Storage");
   Init();
   storage_->GetSqlContext(base::Bind(&SqlWrite, id, key, value));
   storage_->Flush();
diff --git a/src/cobalt/dom/typed_array.h b/src/cobalt/dom/typed_array.h
index 266e002..e901a70 100644
--- a/src/cobalt/dom/typed_array.h
+++ b/src/cobalt/dom/typed_array.h
@@ -17,14 +17,16 @@
 #ifndef COBALT_DOM_TYPED_ARRAY_H_
 #define COBALT_DOM_TYPED_ARRAY_H_
 
-#include <memory.h>  // for memcpy
-
 #include "base/logging.h"
 #include "base/stringprintf.h"
 #include "cobalt/dom/array_buffer_view.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/exception_state.h"
 
+#if defined(STARBOARD)
+#include "starboard/memory.h"
+#endif
+
 namespace cobalt {
 namespace dom {
 
@@ -50,7 +52,11 @@
              uint32 length)
       : ArrayBufferView(new ArrayBuffer(settings, length * kBytesPerElement)) {
     DCHECK_EQ(this->length(), length);
+#if defined(STARBOARD)
+    SbMemoryCopy(this->data(), data, length * kBytesPerElement);
+#else
     memcpy(this->data(), data, length * kBytesPerElement);
+#endif
   }
 
   // Creates a new TypedArray and copies the elements of 'other' into this.
@@ -123,8 +129,13 @@
     }
     uint32 source_offset = 0;
     while (source_offset < source->length()) {
+#if defined(STARBOARD)
+      SbMemoryCopy(data() + offset, source->data() + source_offset,
+             sizeof(ElementType));
+#else
       memcpy(data() + offset, source->data() + source_offset,
              sizeof(ElementType));
+#endif
       ++offset;
       ++source_offset;
     }
@@ -138,14 +149,22 @@
   // Write a single element of the array.
   void Set(uint32 index, ElementType val) {
     if (index < length()) {
+#if defined(STARBOARD)
+      SbMemoryCopy(data() + index, &val, sizeof(ElementType));
+#else
       memcpy(data() + index, &val, sizeof(ElementType));
+#endif
     }
   }
 
   ElementType Get(uint32 index) const {
     if (index < length()) {
       ElementType val;
+#if defined(STARBOARD)
+      SbMemoryCopy(&val, data() + index, sizeof(ElementType));
+#else
       memcpy(&val, data() + index, sizeof(ElementType));
+#endif
       return val;
     } else {
       // TODO: an out of bounds index should return undefined.
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index c2e63c0..18dccb1 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -92,7 +92,7 @@
   scoped_refptr<render_tree::animations::AnimateNode> animate_node =
       new render_tree::animations::AnimateNode(layout_results.render_tree);
   scoped_refptr<render_tree::Node> animated_tree =
-      animate_node->Apply(layout_results.layout_time);
+      animate_node->Apply(layout_results.layout_time).animated;
 
   bool results =
       pixel_tester.TestTree(animated_tree, GetParam().base_file_path);
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle-expected.png
new file mode 100644
index 0000000..39205b9
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle.html
new file mode 100644
index 0000000..4972e3d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    div {
+      background: linear-gradient(to left, yellow 20%, purple 70%);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-expected.png
new file mode 100644
index 0000000..c2bdcce
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png
new file mode 100644
index 0000000..c2bdcce
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient.html
new file mode 100644
index 0000000..03019b7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    div {
+      background: linear-gradient(to left, yellow, purple, green);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient-expected.png
new file mode 100644
index 0000000..ac7cae8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient.html
new file mode 100644
index 0000000..36cdbcb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    div {
+      background: linear-gradient(to bottom, yellow, transparent);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient-expected.png
new file mode 100644
index 0000000..996008f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient.html
new file mode 100644
index 0000000..b161dbd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    div {
+      background: linear-gradient(to left, yellow, transparent);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient-expected.png
new file mode 100644
index 0000000..c96bfe7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient.html
new file mode 100644
index 0000000..e56d490
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    div {
+      background: linear-gradient(to right, yellow, transparent);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient-expected.png
new file mode 100644
index 0000000..e4ec68c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient.html
new file mode 100644
index 0000000..683c1da
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <style>
+    div {
+      background: linear-gradient(to top, yellow, transparent);
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt
index d2988a2..6abfa12 100644
--- a/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt
@@ -1,9 +1,15 @@
 4-1-2-corner-to-corner-linear-gradient
+4-1-2-gradient-in-middle
 4-1-2-linear-gradient-with-same-stop-positions
 4-1-2-linear-gradient-with-transparency
+4-1-2-multistop-one-dimensional-gradient
 4-1-2-simple-yellow-to-blue-arbitrary-angle-linear-gradient
 4-1-2-simple-yellow-to-blue-vertical-linear-gradient
 4-1-2-three-color-linear-gradient
+4-1-2-to-bottom-linear-gradient
+4-1-2-to-left-linear-gradient
+4-1-2-to-right-linear-gradient
+4-1-2-to-top-linear-gradient
 4-2-4-closest-side-radial-gradient
 4-2-4-radial-gradient-non-centered
 4-2-4-radial-gradient-with-transparency
diff --git a/src/cobalt/loader/file_fetcher.cc b/src/cobalt/loader/file_fetcher.cc
index 1cbe61f..83ee7a6 100644
--- a/src/cobalt/loader/file_fetcher.cc
+++ b/src/cobalt/loader/file_fetcher.cc
@@ -40,7 +40,7 @@
 
   // Ensure the request does not attempt to navigate outside the whitelisted
   // directory.
-  if (file_path_.ReferencesParent()) {
+  if (file_path_.ReferencesParent() || file_path_.IsAbsolute()) {
     handler->OnError(this, PlatformFileErrorToString(
                                base::PLATFORM_FILE_ERROR_ACCESS_DENIED));
     return;
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index 2f4ce23..1c3e4e6 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -47,6 +47,13 @@
 template <typename CacheType>
 class ResourceCache;
 
+enum CallbackType {
+  kOnLoadingSuccessCallbackType,
+  kOnLoadingFailureCallbackType,
+  kOnLoadingErrorCallbackType,
+  kCallbackTypeCount,
+};
+
 //////////////////////////////////////////////////////////////////////////
 // CachedResource - Declarations
 //////////////////////////////////////////////////////////////////////////
@@ -121,17 +128,11 @@
  private:
   friend class base::RefCountedThreadSafe<CachedResource>;
   friend class OnLoadedCallbackHandler;
+  friend class ResourceCache<CacheType>;
 
   typedef std::list<base::Closure> CallbackList;
   typedef std::list<base::Closure>::iterator CallbackListIterator;
 
-  enum CallbackType {
-    kOnLoadingSuccessCallbackType,
-    kOnLoadingFailureCallbackType,
-    kOnLoadingErrorCallbackType,
-    kCallbackTypeCount,
-  };
-
   ~CachedResource();
 
   // Callbacks for decoders.
@@ -274,10 +275,8 @@
   resource_ = resource;
 
   loader_.reset();
-  resource_cache_->NotifyResourceSuccessfullyLoaded(this);
-  // To avoid the last reference of this object get deleted in the callbacks.
-  scoped_refptr<CachedResource<CacheType> > holder(this);
-  RunCallbacks(kOnLoadingSuccessCallbackType);
+  resource_cache_->NotifyResourceLoadingComplete(this,
+                                                 kOnLoadingSuccessCallbackType);
 }
 
 template <typename CacheType>
@@ -287,9 +286,8 @@
   LOG(WARNING) << "Warning while loading '" << url_ << "': " << message;
 
   loader_.reset();
-  // To avoid the last reference of this object get deleted in the callbacks.
-  scoped_refptr<CachedResource<CacheType> > holder(this);
-  RunCallbacks(kOnLoadingFailureCallbackType);
+  resource_cache_->NotifyResourceLoadingComplete(this,
+                                                 kOnLoadingFailureCallbackType);
 }
 
 template <typename CacheType>
@@ -299,9 +297,8 @@
   LOG(ERROR) << "Error while loading '" << url_ << "': " << error;
 
   loader_.reset();
-  // To avoid the last reference of this object get deleted in the callbacks.
-  scoped_refptr<CachedResource<CacheType> > holder(this);
-  RunCallbacks(kOnLoadingErrorCallbackType);
+  resource_cache_->NotifyResourceLoadingComplete(this,
+                                                 kOnLoadingErrorCallbackType);
 }
 
 template <typename CacheType>
@@ -328,7 +325,7 @@
 void CachedResource<CacheType>::RunCallbacks(CallbackType type) {
   DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
 
-  // To avoid the list gets altered in the callbacks.
+  // To avoid the list getting altered in the callbacks.
   CallbackList callback_list = callback_lists[type];
   CallbackListIterator callback_iter;
   for (callback_iter = callback_list.begin();
@@ -391,6 +388,15 @@
   typedef
       typename CachedResourceType::CreateLoaderFunction CreateLoaderFunction;
 
+  struct ResourceCallbackInfo {
+    ResourceCallbackInfo(CachedResourceType* cached_resource,
+                         CallbackType callback_type)
+        : cached_resource(cached_resource), callback_type(callback_type) {}
+
+    CachedResourceType* cached_resource;
+    CallbackType callback_type;
+  };
+
   ResourceCache(const std::string& name, uint32 cache_capacity,
                 const CreateLoaderFunction& create_loader_function);
 
@@ -421,12 +427,17 @@
   typedef base::hash_map<std::string, CachedResourceType*> CachedResourceMap;
   typedef typename CachedResourceMap::iterator CachedResourceMapIterator;
 
+  typedef base::hash_set<std::string> ResourceSet;
+  typedef base::linked_hash_map<std::string, ResourceCallbackInfo>
+      ResourceCallbackMap;
+
   typedef base::linked_hash_map<std::string, scoped_refptr<ResourceType> >
       ResourceMap;
   typedef typename ResourceMap::iterator ResourceMapIterator;
 
-  // Called by CachedResource objects after they are successfully loaded.
-  void NotifyResourceSuccessfullyLoaded(CachedResourceType* cached_resource);
+  // Called by CachedResource objects after they finish loading.
+  void NotifyResourceLoadingComplete(CachedResourceType* cached_resource,
+                                     CallbackType callback_type);
 
   // Called by the destructor of CachedResource to remove CachedResource from
   // |cached_resource_map_| and either immediately free the resource from memory
@@ -434,10 +445,24 @@
   // cache is over its memory limit.
   void NotifyResourceDestroyed(CachedResourceType* cached_resource);
 
+  // Reclaims memory from unreferenced cache objects until total cache memory
+  // is reduced to |bytes_to_reclaim_down_to|. In the case where the desired
+  // memory cannot be freed, pending callbacks are processed (potentially
+  // enabling additional resources to be reclaimed), and memory reclamation is
+  // attempted again.
+  void ReclaimMemoryAndMaybeProcessPendingCallbacks(
+      uint32 bytes_to_reclaim_down_to);
   // Releases unreferenced cache objects until our total cache memory usage is
   // less than or equal to |bytes_to_reclaim_down_to|, or until there are no
   // more unreferenced cache objects to release.
-  void ReclaimMemory(uint32 bytes_to_reclaim_down_to);
+  void ReclaimMemory(uint32 bytes_to_reclaim_down_to, bool log_warning_if_over);
+
+  // Calls ProcessPendingCallbacks() if
+  // |callback_blocking_loading_resource_set_| is empty.
+  void ProcessPendingCallbacksIfUnblocked();
+  // Processes all pending callbacks regardless of the state of
+  // |callback_blocking_loading_resource_set_|.
+  void ProcessPendingCallbacks();
 
   // The name of this resource cache object, useful while debugging.
   const std::string name_;
@@ -448,6 +473,28 @@
 
   csp::SecurityCallback security_callback_;
 
+  // The resource cache attempts to batch callbacks as much as possible to try
+  // to ensure that events triggered by the callbacks occur together. It
+  // accomplishes this by waiting for all active loads to complete before
+  // processing any of their callbacks. However, to ensure that callbacks are
+  // processed in a timely manner as well, active loads are placed into two
+  // buckets: callback blocking and non-callback blocking. While no callbacks
+  // are pending, all active loads are added as callback blocking. As soon as
+  // a callback is pending, any additional load requests are added as
+  // non-callback blocking. As soon as all of the callback blocking loads are
+  // finished, the pending callbacks are processed, the non-callback blocking
+  // loads become callback blocking loads, and the process repeats itself.
+
+  // Currently loading resources that block any pending callbacks from running.
+  ResourceSet callback_blocking_loading_resource_set_;
+  // Currently loading resources that do not block the pending callbacks from
+  // running. After pending callbacks run, these become blocking.
+  ResourceSet non_callback_blocking_loading_resource_set_;
+  // Resources that have completed loading and have callbacks pending.
+  ResourceCallbackMap pending_callback_map_;
+  // Whether or not ProcessPendingCallbacks() is running.
+  bool is_processing_pending_callbacks_;
+
   // |cached_resource_map_| stores the cached resources that are currently
   // referenced.
   CachedResourceMap cached_resource_map_;
@@ -476,6 +523,7 @@
     : name_(name),
       cache_capacity_(cache_capacity),
       create_loader_function_(create_loader_function),
+      is_processing_pending_callbacks_(false),
       size_in_bytes_(base::StringPrintf("%s.Size", name_.c_str()), 0,
                      "Total number of bytes currently used by the cache."),
       capacity_in_bytes_(base::StringPrintf("%s.Capacity", name_.c_str()),
@@ -512,8 +560,21 @@
     return cached_resource;
   }
 
-  // If the resource doesn't exist, create a cached resource and fetch the
-  // resource based on the url.
+  // If we reach this point, then the resource doesn't exist yet.
+
+  // Add the resource to a loading set. If no current resources have pending
+  // callbacks, then this resource will block callbacks until it is decoded.
+  // However, if there are resources with pending callbacks, then the decoding
+  // of this resource won't block the callbacks from occurring. This ensures
+  // that a steady stream of new resources won't prevent callbacks from ever
+  // occurring.
+  if (pending_callback_map_.empty()) {
+    callback_blocking_loading_resource_set_.insert(url.spec());
+  } else {
+    non_callback_blocking_loading_resource_set_.insert(url.spec());
+  }
+
+  // Create the cached resource and fetch its resource based on the url.
   scoped_refptr<CachedResourceType> cached_resource(new CachedResourceType(
       url, security_callback_, create_loader_function_, this));
   cached_resource_map_.insert(
@@ -526,55 +587,106 @@
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
   cache_capacity_ = capacity;
   capacity_in_bytes_ = capacity;
-  ReclaimMemory(cache_capacity_);
+  ReclaimMemoryAndMaybeProcessPendingCallbacks(cache_capacity_);
 }
 
 template <typename CacheType>
 void ResourceCache<CacheType>::Purge() {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
-  ReclaimMemory(0);
+  ReclaimMemoryAndMaybeProcessPendingCallbacks(0);
 }
 
 template <typename CacheType>
-void ResourceCache<CacheType>::NotifyResourceSuccessfullyLoaded(
-    CachedResourceType* cached_resource) {
+void ResourceCache<CacheType>::NotifyResourceLoadingComplete(
+    CachedResourceType* cached_resource, CallbackType callback_type) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+  const std::string& url = cached_resource->url().spec();
 
   if (cached_resource->TryGetResource()) {
     size_in_bytes_ +=
         CacheType::GetEstimatedSizeInBytes(cached_resource->TryGetResource());
-    if (size_in_bytes_ > cache_capacity_) {
-      ReclaimMemory(cache_capacity_);
-    }
   }
+
+  // Remove the resource from its loading set. It should exist in exactly one
+  // of the loading sets.
+  if (callback_blocking_loading_resource_set_.erase(url)) {
+    DCHECK(non_callback_blocking_loading_resource_set_.find(url) ==
+           non_callback_blocking_loading_resource_set_.end());
+  } else if (!non_callback_blocking_loading_resource_set_.erase(url)) {
+    DCHECK(false);
+  }
+
+  // Add a callback for the resource that just finished loading to the pending
+  // callbacks.
+  pending_callback_map_.insert(std::make_pair(
+      url, ResourceCallbackInfo(cached_resource, callback_type)));
+
+  ProcessPendingCallbacksIfUnblocked();
+  ReclaimMemoryAndMaybeProcessPendingCallbacks(cache_capacity_);
 }
 
 template <typename CacheType>
 void ResourceCache<CacheType>::NotifyResourceDestroyed(
     CachedResourceType* cached_resource) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+  const std::string& url = cached_resource->url().spec();
 
-  std::string url = cached_resource->url().spec();
   cached_resource_map_.erase(url);
 
   DCHECK(unreference_cached_resource_map_.find(url) ==
          unreference_cached_resource_map_.end());
+
   // Check to see if this was a loaded resource.
   if (cached_resource->TryGetResource()) {
     // Add it into the unreferenced cached resource map, so that it will be
     // retained while memory is available for it in the cache.
     unreference_cached_resource_map_.insert(
         std::make_pair(url, cached_resource->TryGetResource()));
-    // Try to reclaim some memory.
-    ReclaimMemory(cache_capacity_);
+  }
+
+  // Remove the resource from any loading or pending container that it is in.
+  // It should never exist in more than one of the containers.
+  if (callback_blocking_loading_resource_set_.erase(url)) {
+    DCHECK(non_callback_blocking_loading_resource_set_.find(url) ==
+           non_callback_blocking_loading_resource_set_.end());
+    DCHECK(pending_callback_map_.find(url) == pending_callback_map_.end());
+  } else if (non_callback_blocking_loading_resource_set_.erase(url)) {
+    DCHECK(pending_callback_map_.find(url) == pending_callback_map_.end());
+  } else {
+    pending_callback_map_.erase(url);
+  }
+
+  // Only process pending callbacks and attempt to reclaim memory if
+  // NotifyResourceDestroyed() wasn't called from within
+  // ProcessPendingCallbacks(). This prevents recursion and redundant
+  // processing.
+  if (!is_processing_pending_callbacks_) {
+    ProcessPendingCallbacksIfUnblocked();
+    ReclaimMemory(cache_capacity_, true /*log_warning_if_over*/);
   }
 }
 
 template <typename CacheType>
-void ResourceCache<CacheType>::ReclaimMemory(uint32 bytes_to_reclaim_down_to) {
+void ResourceCache<CacheType>::ReclaimMemoryAndMaybeProcessPendingCallbacks(
+    uint32 bytes_to_reclaim_down_to) {
+  ReclaimMemory(bytes_to_reclaim_down_to, false /*log_warning_if_over*/);
+  // If the current size of the cache is still greater than
+  // |bytes_to_reclaim_down_to| after reclaiming memory, then process any
+  // pending callbacks and try again. References to the cached resources are
+  // potentially being held until the callbacks run, so processing them may
+  // enable more memory to be reclaimed.
+  if (size_in_bytes_ > bytes_to_reclaim_down_to) {
+    ProcessPendingCallbacks();
+    ReclaimMemory(bytes_to_reclaim_down_to, true /*log_warning_if_over*/);
+  }
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::ReclaimMemory(uint32 bytes_to_reclaim_down_to,
+                                             bool log_warning_if_over) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
 
-  while (size_in_bytes_.value() > bytes_to_reclaim_down_to &&
+  while (size_in_bytes_ > bytes_to_reclaim_down_to &&
          !unreference_cached_resource_map_.empty()) {
     // The first element is the earliest-inserted element.
     scoped_refptr<ResourceType> resource =
@@ -588,14 +700,47 @@
     size_in_bytes_ -= first_resource_size;
   }
 
-  // Make sure that |size_in_bytes_| is less than or equal to |cache_capacity_|,
-  // otherwise it means that |unreference_cached_resource_map_| is empty. We
-  // have to increase the size of |cache_capacity_| if the system memory is
-  // large enough or evict resources from the cache even though they are still
-  // in use.
-  DLOG_IF(WARNING, size_in_bytes_.value() > cache_capacity_)
-      << "cached size: " << size_in_bytes_
-      << ", cache capacity: " << cache_capacity_;
+  if (log_warning_if_over) {
+    // Log a warning if we're still over |bytes_to_reclaim_down_to| after
+    // attempting to reclaim memory. This can occur validly when the size of
+    // the referenced images exceeds the target size.
+    DLOG_IF(WARNING, size_in_bytes_ > bytes_to_reclaim_down_to)
+        << "cached size: " << size_in_bytes_
+        << ", target size: " << bytes_to_reclaim_down_to;
+  }
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::ProcessPendingCallbacksIfUnblocked() {
+  if (callback_blocking_loading_resource_set_.empty()) {
+    ProcessPendingCallbacks();
+
+    // Now that we've processed the callbacks, if there are any non-blocking
+    // loading resources, then they're becoming blocking. Simply swap the two
+    // sets, rather than copying the contents over.
+    if (!non_callback_blocking_loading_resource_set_.empty()) {
+      callback_blocking_loading_resource_set_.swap(
+          non_callback_blocking_loading_resource_set_);
+    }
+  }
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::ProcessPendingCallbacks() {
+  DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+
+  is_processing_pending_callbacks_ = true;
+  while (!pending_callback_map_.empty()) {
+    ResourceCallbackInfo& callback_info = pending_callback_map_.front().second;
+
+    // To avoid the last reference of this object getting deleted in the
+    // callbacks.
+    scoped_refptr<CachedResourceType> holder(callback_info.cached_resource);
+    callback_info.cached_resource->RunCallbacks(callback_info.callback_type);
+
+    pending_callback_map_.erase(pending_callback_map_.begin());
+  }
+  is_processing_pending_callbacks_ = false;
 }
 
 }  // namespace loader
diff --git a/src/cobalt/render_tree/animations/animate_node.cc b/src/cobalt/render_tree/animations/animate_node.cc
index 6363ec9..0663359 100644
--- a/src/cobalt/render_tree/animations/animate_node.cc
+++ b/src/cobalt/render_tree/animations/animate_node.cc
@@ -21,6 +21,7 @@
 #include "base/debug/trace_event.h"
 #include "cobalt/base/enable_if.h"
 #include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/math/transform_2d.h"
 #include "cobalt/render_tree/child_iterator.h"
 #include "cobalt/render_tree/node_visitor.h"
 
@@ -214,6 +215,158 @@
   animated_ = true;
 }
 
+// A helper class for computing the tightest bounding rectangle that encloses
+// all animated subtrees.  It is meant to be applied to an already-animated
+// render tree, using a TraverseList provided by
+// ApplyVisitor::animated_traverse_list().  The result of the visit can be
+// retrieved from bounds().  This calculation is useful for determining a
+// "dirty rectangle" to minimize drawing.
+class AnimateNode::BoundsVisitor : public NodeVisitor {
+ public:
+  BoundsVisitor(const TraverseList& traverse_list, base::TimeDelta time_offset,
+                base::TimeDelta since);
+
+  void Visit(animations::AnimateNode* /* animate */) OVERRIDE {
+    // An invariant of AnimateNodes is that they should never contain descendant
+    // AnimateNodes.
+    NOTREACHED();
+  }
+  // Immediately switch to a templated visitor function.
+  void Visit(CompositionNode* composition) OVERRIDE { VisitNode(composition); }
+  void Visit(FilterNode* text) OVERRIDE { VisitNode(text); }
+  void Visit(ImageNode* image) OVERRIDE { VisitNode(image); }
+  void Visit(MatrixTransformNode* transform) OVERRIDE { VisitNode(transform); }
+  void Visit(PunchThroughVideoNode* punch_through) OVERRIDE {
+    VisitNode(punch_through);
+  }
+  void Visit(RectNode* rect) OVERRIDE { VisitNode(rect); }
+  void Visit(RectShadowNode* rect) OVERRIDE { VisitNode(rect); }
+  void Visit(TextNode* text) OVERRIDE { VisitNode(text); }
+
+  const math::RectF& bounds() const { return bounds_; }
+
+ private:
+  template <typename T>
+  typename base::enable_if<!ChildIterator<T>::has_children>::type VisitNode(
+      T* node);
+  template <typename T>
+  typename base::enable_if<ChildIterator<T>::has_children>::type VisitNode(
+      T* node);
+  void ProcessAnimatedNodeBounds(const TraverseListEntry& entry,
+                                 render_tree::Node* node);
+  TraverseListEntry AdvanceIterator(Node* node);
+
+  void ApplyTransform(Node* node);
+  void ApplyTransform(CompositionNode* node);
+  void ApplyTransform(MatrixTransformNode* node);
+
+  // The time offset to be passed in to individual animations.
+  base::TimeDelta time_offset_;
+
+  // The time when we "start" checking for active animations.  In other words,
+  // if an animation had expired *before* |since_|, then it is not considered
+  // animated, and not considered for the bounding box.
+  base::TimeDelta since_;
+
+  // A list of nodes that we are allowed to traverse into (i.e. a traversal that
+  // guides us to animated nodes).  It assumes that a pre-order traversal will
+  // be taken.
+  const TraverseList& traverse_list_;
+
+  // An iterator pointing to the next valid render tree node to visit.
+  TraverseList::const_iterator iterator_;
+
+  // The resulting bounding box surrounding all active animations.
+  math::RectF bounds_;
+
+  // We need to maintain a "current" transform as we traverse the tree, so that
+  // we know the transformed bounding boxes of nodes when we reach them.
+  math::Matrix3F transform_;
+};
+
+AnimateNode::BoundsVisitor::BoundsVisitor(const TraverseList& traverse_list,
+                                          base::TimeDelta time_offset,
+                                          base::TimeDelta since)
+    : time_offset_(time_offset),
+      since_(since),
+      traverse_list_(traverse_list),
+      transform_(math::Matrix3F::Identity()) {
+  iterator_ = traverse_list_.begin();
+}
+
+template <typename T>
+typename base::enable_if<!ChildIterator<T>::has_children>::type
+AnimateNode::BoundsVisitor::VisitNode(T* node) {
+  TraverseListEntry current_entry = AdvanceIterator(node);
+
+  DCHECK(current_entry.animations);
+  ProcessAnimatedNodeBounds(current_entry, node);
+}
+
+template <typename T>
+typename base::enable_if<ChildIterator<T>::has_children>::type
+AnimateNode::BoundsVisitor::VisitNode(T* node) {
+  TraverseListEntry current_entry = AdvanceIterator(node);
+
+  math::Matrix3F old_transform = transform_;
+  ApplyTransform(node);
+
+  // Traverse the child nodes, but only the ones that are on the
+  // |traverse_list_|.  In particular, the next node we are allowed to visit
+  // is the one in the traverse list pointed to by |iterator_->node|.
+  ChildIterator<T> child_iterator(node);
+  while (Node* child = child_iterator.GetCurrent()) {
+    if (iterator_ == traverse_list_.end()) {
+      // If we've reached the end of |traverse_list_| then we are done
+      // iterating and it's time to return.
+      break;
+    }
+
+    if (child == iterator_->node) {
+      // If one of our children is next up on the path to animation, traverse
+      // into it.
+      child->Accept(this);
+    }
+
+    child_iterator.Next();
+  }
+  transform_ = old_transform;
+
+  if (current_entry.animations) {
+    ProcessAnimatedNodeBounds(current_entry, node);
+  }
+}
+
+void AnimateNode::BoundsVisitor::ProcessAnimatedNodeBounds(
+    const TraverseListEntry& entry, render_tree::Node* node) {
+  TRACE_EVENT0("cobalt::renderer",
+               "AnimateNode::BoundsVisitor::ProcessAnimatedNodeBounds()");
+  if (entry.animations->GetExpiry() >= since_) {
+    bounds_.Union(transform_.MapRect(node->GetBounds()));
+  }
+}
+
+AnimateNode::TraverseListEntry AnimateNode::BoundsVisitor::AdvanceIterator(
+    Node* node) {
+  // Check that the iterator that we are advancing past is indeed the one we
+  // expect it to be.
+  DCHECK_EQ(node, iterator_->node);
+  return *(iterator_++);
+}
+
+void AnimateNode::BoundsVisitor::ApplyTransform(Node* node) {
+  UNREFERENCED_PARAMETER(node);
+}
+
+void AnimateNode::BoundsVisitor::ApplyTransform(CompositionNode* node) {
+  transform_ = transform_ * math::TranslateMatrix(node->data().offset().x(),
+                                                  node->data().offset().y());
+}
+
+void AnimateNode::BoundsVisitor::ApplyTransform(MatrixTransformNode* node) {
+  transform_ = transform_ * node->data().transform;
+}
+
 // A helper render tree visitor class used to apply compiled sub render-tree
 // animations.  Only one of these visitors is needed to visit an entire render
 // tree.
@@ -242,6 +395,11 @@
   // final animated result can be pulled from this visitor.
   const scoped_refptr<Node>& animated() const { return animated_; }
 
+  // As we compute the animated nodes, we create a new traverse list that leads
+  // to the newly created animated nodes.  This can be used afterwards to
+  // calculate the bounding boxes around the active animated nodes.
+  TraverseList* animated_traverse_list() { return &animated_traverse_list_; }
+
  private:
   template <typename T>
   typename base::enable_if<!ChildIterator<T>::has_children>::type VisitNode(
@@ -250,8 +408,8 @@
   typename base::enable_if<ChildIterator<T>::has_children>::type VisitNode(
       T* node);
   template <typename T>
-  void ApplyAnimations(const TraverseListEntry& entry,
-                       typename T::Builder* builder);
+  scoped_refptr<Node> ApplyAnimations(const TraverseListEntry& entry,
+                                      typename T::Builder* builder);
   TraverseListEntry AdvanceIterator(Node* node);
 
   // The time offset to be passed in to individual animations.
@@ -262,6 +420,12 @@
   // guides us to animated nodes).  It assumes that a pre-order traversal will
   // be taken.
   const TraverseList& traverse_list_;
+
+  // As we animate the nodes, we also keep track of a new traverse list that
+  // replaces the non-animated nodes for the animated nodes, so that we can
+  // go through and traverse the animated nodes after they have been animated.
+  TraverseList animated_traverse_list_;
+
   // An iterator pointing to the next valid render tree node to visit.
   TraverseList::const_iterator iterator_;
 };
@@ -269,6 +433,7 @@
 AnimateNode::ApplyVisitor::ApplyVisitor(const TraverseList& traverse_list,
                                         base::TimeDelta time_offset)
     : time_offset_(time_offset), traverse_list_(traverse_list) {
+  animated_traverse_list_.reserve(traverse_list.size());
   iterator_ = traverse_list_.begin();
 }
 
@@ -280,8 +445,9 @@
   // have animations.
   DCHECK(current_entry.animations);
   typename T::Builder builder(node->data());
-  ApplyAnimations<T>(current_entry, &builder);
-  animated_ = new T(builder);
+  animated_ = ApplyAnimations<T>(current_entry, &builder);
+  animated_traverse_list_.push_back(
+      TraverseListEntry(animated_, current_entry.animations));
 }
 
 template <typename T>
@@ -289,6 +455,10 @@
 AnimateNode::ApplyVisitor::VisitNode(T* node) {
   TraverseListEntry current_entry = AdvanceIterator(node);
 
+  size_t animated_traverse_list_index = animated_traverse_list_.size();
+  animated_traverse_list_.push_back(
+      TraverseListEntry(NULL, current_entry.animations));
+
   // Traverse the child nodes, but only the ones that are on the
   // |traverse_list_|.  In particular, the next node we are allowed to visit
   // is the one in the traverse list pointed to by |iterator_->node|.
@@ -326,8 +496,7 @@
       // be passed into the animations.
       builder.emplace(node->data());
     }
-    ApplyAnimations<T>(current_entry, &(*builder));
-    animated_ = new T(*builder);
+    animated_ = ApplyAnimations<T>(current_entry, &(*builder));
   } else {
     // If there were no animations targeting this node directly, then its
     // children must have been modified since otherwise it wouldn't be in
@@ -335,11 +504,13 @@
     DCHECK(children_modified);
     animated_ = new T(child_iterator.TakeReplacedChildrenBuilder());
   }
+
+  animated_traverse_list_[animated_traverse_list_index].node = animated_;
 }
 
 template <typename T>
-void AnimateNode::ApplyVisitor::ApplyAnimations(const TraverseListEntry& entry,
-                                                typename T::Builder* builder) {
+scoped_refptr<Node> AnimateNode::ApplyVisitor::ApplyAnimations(
+    const TraverseListEntry& entry, typename T::Builder* builder) {
   TRACE_EVENT0("cobalt::renderer",
                "AnimateNode::ApplyVisitor::ApplyAnimations()");
   // Cast to the specific type we expect these animations to have.
@@ -353,6 +524,8 @@
        iter != typed_node_animations->data().animations.end(); ++iter) {
     iter->Run(builder, time_offset_);
   }
+
+  return new T(*builder);
 }
 
 AnimateNode::TraverseListEntry AnimateNode::ApplyVisitor::AdvanceIterator(
@@ -374,15 +547,66 @@
   CommonInit(Builder::InternalMap(), source);
 }
 
-scoped_refptr<Node> AnimateNode::Apply(base::TimeDelta time_offset) {
+// Helper class to refcount wrap a TraverseList object so that it can be
+// passed around in a callback.
+class AnimateNode::RefCountedTraversalList
+    : public base::RefCounted<RefCountedTraversalList> {
+ public:
+  explicit RefCountedTraversalList(TraverseList* to_swap_in) {
+    traverse_list_.swap(*to_swap_in);
+  }
+  const TraverseList& traverse_list() const { return traverse_list_; }
+
+ private:
+  friend class base::RefCounted<RefCountedTraversalList>;
+  ~RefCountedTraversalList() {}
+
+  TraverseList traverse_list_;
+};
+
+// static
+math::RectF AnimateNode::GetAnimationBoundsSince(
+    const scoped_refptr<RefCountedTraversalList>& traverse_list,
+    base::TimeDelta time_offset, const scoped_refptr<Node>& animated,
+    base::TimeDelta since) {
+  TRACE_EVENT0("cobalt::renderer", "AnimateNode::GetAnimationBoundsSince()");
+
+  BoundsVisitor bounds_visitor(traverse_list->traverse_list(), time_offset,
+                               since);
+  animated->Accept(&bounds_visitor);
+  return bounds_visitor.bounds();
+}
+
+namespace {
+// Helper function to always return an empty bounding rectangle.
+math::RectF ReturnTrivialEmptyRectBound(base::TimeDelta since) {
+  UNREFERENCED_PARAMETER(since);
+  return math::RectF();
+}
+}  // namespace
+
+AnimateNode::AnimateResults AnimateNode::Apply(base::TimeDelta time_offset) {
   TRACE_EVENT0("cobalt::renderer", "AnimateNode::Apply()");
+  AnimateResults results;
   if (traverse_list_.empty()) {
-    return source_;
+    results.animated = source_;
+    // There are no animations, so there is no bounding rectangle, so setup the
+    // bounding box function to trivially return an empty rectangle.
+    results.get_animation_bounds_since =
+        base::Bind(&ReturnTrivialEmptyRectBound);
   } else {
     ApplyVisitor apply_visitor(traverse_list_, time_offset);
     source_->Accept(&apply_visitor);
-    return apply_visitor.animated();
+    results.animated = apply_visitor.animated();
+
+    // Setup a function for returning
+    results.get_animation_bounds_since = base::Bind(
+        &GetAnimationBoundsSince,
+        scoped_refptr<RefCountedTraversalList>(new RefCountedTraversalList(
+            apply_visitor.animated_traverse_list())),
+        time_offset, results.animated);
   }
+  return results;
 }
 
 void AnimateNode::CommonInit(const Builder::InternalMap& node_animation_map,
diff --git a/src/cobalt/render_tree/animations/animate_node.h b/src/cobalt/render_tree/animations/animate_node.h
index b5a11e1..d0b81c8 100644
--- a/src/cobalt/render_tree/animations/animate_node.h
+++ b/src/cobalt/render_tree/animations/animate_node.h
@@ -22,6 +22,7 @@
 
 #include "base/containers/small_map.h"
 #include "base/memory/ref_counted.h"
+#include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/animations/animation_list.h"
 #include "cobalt/render_tree/movable.h"
 #include "cobalt/render_tree/node.h"
@@ -139,10 +140,21 @@
     return base::GetTypeId<AnimateNode>();
   }
 
+  struct AnimateResults {
+    // The animated render tree, which is guaranteed to not contain any
+    // AnimateNodes.
+    scoped_refptr<Node> animated;
+
+    // Can be called in order to return a bounding rectangle around all
+    // nodes that are actively animated.  The parameter specifies a "since"
+    // time.  Any animations that had already expired *before* the "since" time
+    // will not be included in the returned bounding box.
+    // This information is likely to be used to enable optimizations where only
+    // regions of the screen that have changed are rendered.
+    base::Callback<math::RectF(base::TimeDelta)> get_animation_bounds_since;
+  };
   // Apply the animations to the sub render tree with the given |time_offset|.
-  // An animated sub-tree is returned, which is guaranteed to not contain any
-  // AnimateNodes.
-  scoped_refptr<Node> Apply(base::TimeDelta time_offset);
+  AnimateResults Apply(base::TimeDelta time_offset);
 
   // Returns the sub-tree for which the animations apply to.
   const scoped_refptr<Node> source() const { return source_; }
@@ -154,17 +166,6 @@
   const base::TimeDelta& expiry() const { return expiry_; }
 
  private:
-  // A helper render tree visitor class used to compile sub render-tree
-  // animations.
-  class TraverseListBuilder;
-  // A helper render tree visitor class used to apply compiled sub render-tree
-  // animations.  This class follows the traversal generated by
-  // TraverseListBuilder.
-  class ApplyVisitor;
-
-  void CommonInit(const Builder::InternalMap& node_animation_map,
-                  const scoped_refptr<Node>& source);
-
   // The compiled node animation list is a sequence of nodes that are either
   // animated themselves, or on the path to an animated node.  Only nodes in
   // this sequence need to be traversed.  TraverseListEntry is an entry in this
@@ -180,6 +181,27 @@
   };
   typedef std::vector<TraverseListEntry> TraverseList;
 
+  class RefCountedTraversalList;
+
+  // A helper render tree visitor class used to compile sub render-tree
+  // animations.
+  class TraverseListBuilder;
+  // A helper render tree visitor class used to apply compiled sub render-tree
+  // animations.  This class follows the traversal generated by
+  // TraverseListBuilder.
+  class ApplyVisitor;
+  // A helper class to traverse an animated tree, and compute a bounding
+  // rectangle for all active animations.
+  class BoundsVisitor;
+
+  void CommonInit(const Builder::InternalMap& node_animation_map,
+                  const scoped_refptr<Node>& source);
+
+  static math::RectF GetAnimationBoundsSince(
+      const scoped_refptr<RefCountedTraversalList>& traverse_list,
+      base::TimeDelta time_offset, const scoped_refptr<Node>& animated,
+      base::TimeDelta since);
+
   // The compiled traversal list through the sub-tree represented by |source_|
   // that guides us towards all nodes that need to be animated.
   TraverseList traverse_list_;
diff --git a/src/cobalt/render_tree/animations/animate_node_test.cc b/src/cobalt/render_tree/animations/animate_node_test.cc
index f46e63e..0321bd8 100644
--- a/src/cobalt/render_tree/animations/animate_node_test.cc
+++ b/src/cobalt/render_tree/animations/animate_node_test.cc
@@ -48,12 +48,19 @@
 template <typename T>
 scoped_refptr<AnimateNode> CreateSingleAnimation(
     const scoped_refptr<T>& target,
-    typename Animation<T>::Function anim_function) {
+    typename Animation<T>::Function anim_function, base::TimeDelta expiry) {
   AnimateNode::Builder animations_builder;
-  animations_builder.Add(target, anim_function);
+  animations_builder.Add(target, anim_function, expiry);
   return new AnimateNode(animations_builder, target);
 }
 
+template <typename T>
+scoped_refptr<AnimateNode> CreateSingleAnimation(
+    const scoped_refptr<T>& target,
+    typename Animation<T>::Function anim_function) {
+  return CreateSingleAnimation(target, anim_function, base::TimeDelta::Max());
+}
+
 class ImageFake : public Image {
  public:
   const math::Size& GetSize() const OVERRIDE { return size_; }
@@ -77,7 +84,7 @@
       CreateSingleAnimation(text_node, base::Bind(&AnimateText));
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   TextNode* animated_text_node =
       dynamic_cast<TextNode*>(animated_render_tree.get());
   EXPECT_TRUE(animated_text_node);
@@ -97,7 +104,7 @@
       CreateSingleAnimation(image_node, base::Bind(&AnimateImage));
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   ImageNode* animated_image_node =
       dynamic_cast<ImageNode*>(animated_render_tree.get());
   EXPECT_TRUE(animated_image_node);
@@ -118,7 +125,7 @@
       CreateSingleAnimation(rect_node, base::Bind(&AnimateRect));
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   RectNode* animated_rect_node =
       dynamic_cast<RectNode*>(animated_render_tree.get());
   EXPECT_TRUE(animated_rect_node);
@@ -146,21 +153,21 @@
   ImageNode* animated_image_node;
 
   animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
   EXPECT_FLOAT_EQ(3.0f, animated_image_node->data().destination_rect.width());
 
   animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(2));
+      with_animations->Apply(base::TimeDelta::FromSeconds(2)).animated;
   animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
   EXPECT_FLOAT_EQ(5.0f, animated_image_node->data().destination_rect.width());
 
   animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(4));
+      with_animations->Apply(base::TimeDelta::FromSeconds(4)).animated;
   animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
@@ -198,7 +205,7 @@
       new AnimateNode(animations_builder, image_node));
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(3));
+      with_animations->Apply(base::TimeDelta::FromSeconds(3)).animated;
 
   ImageNode* animated_image_node =
       dynamic_cast<ImageNode*>(animated_render_tree.get());
@@ -224,7 +231,7 @@
       CreateSingleAnimation(transform_node, base::Bind(&AnimateTransform));
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   MatrixTransformNode* animated_transform_node =
       dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
@@ -256,7 +263,7 @@
       new AnimateNode(animation_builder, composition_node);
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   CompositionNode* animated_composition_node =
       dynamic_cast<CompositionNode*>(animated_render_tree.get());
@@ -287,7 +294,7 @@
       CreateSingleAnimation(transform_node, base::Bind(&AnimateTransform));
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   MatrixTransformNode* animated_transform_node =
       dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
@@ -324,7 +331,7 @@
       new AnimateNode(transform_node_b);
 
   scoped_refptr<Node> animated_render_tree =
-      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+      with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   MatrixTransformNode* animated_transform_node =
       dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
@@ -342,6 +349,159 @@
   EXPECT_EQ(RectF(2.0f, 2.0f), animated_image_node->data().destination_rect);
 }
 
+void BoundsAnimateRect(RectNode::Builder* rect_node,
+                       base::TimeDelta time_elapsed) {
+  UNREFERENCED_PARAMETER(time_elapsed);
+  rect_node->rect = RectF(3.0f, 5.0f, 15.0f, 20.0f);
+}
+TEST(AnimateNodeTest, AnimationBounds) {
+  scoped_refptr<RectNode> rect_node_static(new RectNode(
+      RectF(1.0f, 1.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  scoped_refptr<RectNode> rect_node_animated(new RectNode(
+      RectF(4.0f, 4.0f, 10.0f, 10.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  CompositionNode::Builder builder;
+  builder.AddChild(rect_node_static);
+  builder.AddChild(rect_node_animated);
+  scoped_refptr<CompositionNode> composition(
+      new CompositionNode(builder.Pass()));
+
+  AnimateNode::Builder animations_builder;
+  animations_builder.Add(rect_node_animated, base::Bind(&BoundsAnimateRect));
+  scoped_refptr<AnimateNode> with_animations =
+      new AnimateNode(animations_builder, composition);
+
+  AnimateNode::AnimateResults results =
+      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+
+  math::RectF animation_bounds =
+      results.get_animation_bounds_since.Run(base::TimeDelta());
+
+  EXPECT_EQ(RectF(3.0f, 5.0f, 15.0f, 20.0f), animation_bounds);
+}
+
+TEST(AnimateNodeTest, AnimationBoundsExpiration) {
+  scoped_refptr<RectNode> rect_node_static(new RectNode(
+      RectF(1.0f, 1.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  scoped_refptr<RectNode> rect_node_animated(new RectNode(
+      RectF(4.0f, 4.0f, 10.0f, 10.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  CompositionNode::Builder builder;
+  builder.AddChild(rect_node_static);
+  builder.AddChild(rect_node_animated);
+  scoped_refptr<CompositionNode> composition(
+      new CompositionNode(builder.Pass()));
+
+  AnimateNode::Builder animations_builder;
+  animations_builder.Add(rect_node_animated, base::Bind(&BoundsAnimateRect),
+                         base::TimeDelta::FromSeconds(2));
+  scoped_refptr<AnimateNode> with_animations =
+      new AnimateNode(animations_builder, composition);
+
+  // Make sure that our animation bounds are updated when we apply animations
+  // before the expiration.
+  AnimateNode::AnimateResults results =
+      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+  math::RectF animation_bounds =
+      results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(0));
+  EXPECT_EQ(RectF(3.0f, 5.0f, 15.0f, 20.0f), animation_bounds);
+
+  // Make sure that our animation bounds are updated when we apply animations
+  // after the expiration, but we pass in a "since" value from before the
+  // animations expire.
+  results = with_animations->Apply(base::TimeDelta::FromSeconds(4));
+  animation_bounds =
+      results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(1));
+  EXPECT_EQ(RectF(3.0f, 5.0f, 15.0f, 20.0f), animation_bounds);
+
+  // Make sure that our animation bounds are empty after our animations have
+  // expired.
+  results = with_animations->Apply(base::TimeDelta::FromSeconds(4));
+  animation_bounds =
+      results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(3));
+  EXPECT_EQ(0, animation_bounds.size().GetArea());
+}
+
+void BoundsAnimateRect2(RectNode::Builder* rect_node,
+                        base::TimeDelta time_elapsed) {
+  UNREFERENCED_PARAMETER(time_elapsed);
+  rect_node->rect = RectF(2.0f, 6.0f, 10.0f, 25.0f);
+}
+TEST(AnimateNodeTest, AnimationBoundsUnionsMultipleAnimations) {
+  scoped_refptr<RectNode> rect_node_1(new RectNode(
+      RectF(1.0f, 1.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  scoped_refptr<RectNode> rect_node_2(new RectNode(
+      RectF(4.0f, 4.0f, 10.0f, 10.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  CompositionNode::Builder builder;
+  builder.AddChild(rect_node_1);
+  builder.AddChild(rect_node_2);
+  scoped_refptr<CompositionNode> composition(
+      new CompositionNode(builder.Pass()));
+
+  AnimateNode::Builder animations_builder;
+  animations_builder.Add(rect_node_1, base::Bind(&BoundsAnimateRect));
+  animations_builder.Add(rect_node_2, base::Bind(&BoundsAnimateRect2));
+  scoped_refptr<AnimateNode> with_animations =
+      new AnimateNode(animations_builder, composition);
+
+  // Make sure that our animation bounds are the union from our two animated
+  // resulting boxes.
+  AnimateNode::AnimateResults results =
+      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+  math::RectF animation_bounds =
+      results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(0));
+  EXPECT_EQ(RectF(2.0f, 5.0f, 16.0f, 26.0f), animation_bounds);
+}
+
+void AnimateTranslate(CompositionNode::Builder* composition_node,
+                      base::TimeDelta time_elapsed) {
+  UNREFERENCED_PARAMETER(time_elapsed);
+  composition_node->set_offset(math::Vector2dF(4.0f, 4.0f));
+}
+// This test makes sure that the animation bounds are calculated correctly when
+// multiple nodes on the path from the root to a leaf node are animated.  Our
+// results should use the *animated* path to the node to calculate the bounds,
+// versus the non-animated path.
+TEST(AnimateNodeTest, AnimationBoundsWorksForCompoundedTransformations) {
+  scoped_refptr<RectNode> rect_node(new RectNode(
+      RectF(8.0f, 8.0f),
+      scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+  CompositionNode::Builder composition_node_builder_1;
+  composition_node_builder_1.AddChild(rect_node);
+  scoped_refptr<CompositionNode> composition_node_1(
+      new CompositionNode(composition_node_builder_1.Pass()));
+
+  CompositionNode::Builder composition_node_builder_2;
+  composition_node_builder_2.AddChild(composition_node_1);
+  scoped_refptr<CompositionNode> composition_node_2(
+      new CompositionNode(composition_node_builder_2.Pass()));
+
+  AnimateNode::Builder animations_builder;
+  animations_builder.Add(composition_node_1, base::Bind(&AnimateTranslate));
+  animations_builder.Add(composition_node_2, base::Bind(&AnimateTranslate));
+  scoped_refptr<AnimateNode> with_animations =
+      new AnimateNode(animations_builder, composition_node_2);
+
+  // Make sure that our animation bounds are the union from our two animated
+  // resulting boxes.
+  AnimateNode::AnimateResults results =
+      with_animations->Apply(base::TimeDelta::FromSeconds(1));
+  math::RectF animation_bounds =
+      results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(0));
+  EXPECT_EQ(RectF(8.0f, 8.0f, 8.0f, 8.0f), animation_bounds);
+}
+
 }  // namespace animations
 }  // namespace render_tree
 }  // namespace cobalt
diff --git a/src/cobalt/render_tree/brush.h b/src/cobalt/render_tree/brush.h
index d98eeac..b81b95e 100644
--- a/src/cobalt/render_tree/brush.h
+++ b/src/cobalt/render_tree/brush.h
@@ -107,6 +107,12 @@
   // and destination points.
   const ColorStopList& color_stops() const { return color_stops_; }
 
+  // Returns true if, and only if the brush is horizontal.
+  bool IsHorizontal() const { return (source_.y() == dest_.y()); }
+
+  // Returns true if, and only if the brush is vertical.
+  bool IsVertical() const { return (source_.x() == dest_.x()); }
+
  private:
   math::PointF source_;
   math::PointF dest_;
diff --git a/src/cobalt/render_tree/color_rgba.h b/src/cobalt/render_tree/color_rgba.h
index ee5dbe9..f27f24c 100644
--- a/src/cobalt/render_tree/color_rgba.h
+++ b/src/cobalt/render_tree/color_rgba.h
@@ -22,10 +22,21 @@
 namespace cobalt {
 namespace render_tree {
 
+// Note: this does not handle infinities or nans.
+inline float clamp(float input, float min, float max) {
+#if defined(STARBOARD)
+  if (SB_UNLIKELY(input < min)) return min;
+  if (SB_UNLIKELY(input > max)) return max;
+#else
+  if (input < min) return min;
+  if (input > max) return max;
+#endif
+  return input;
+}
+
 // Used to specify a color in the RGB (plus alpha) space.
 // This color format is referenced by many render_tree objects in order to
-// specify a color.  It is expressed by a float for each component, and must
-// be in the range [0.0, 1.0].
+// specify a color.
 struct ColorRGBA {
  public:
   ColorRGBA() : r_(0), g_(0), b_(0), a_(0) {}
@@ -81,13 +92,76 @@
     a_ = value;
   }
 
-  float r() const { return r_; }
-  float g() const { return g_; }
-  float b() const { return b_; }
-  float a() const { return a_; }
+  // These functions clamp the color channel values between 0.0f and 1.0f.
+  float r() const { return clamp(r_, 0.0f, 1.0f); }
+  float g() const { return clamp(g_, 0.0f, 1.0f); }
+  float b() const { return clamp(b_, 0.0f, 1.0f); }
+  float a() const { return clamp(a_, 0.0f, 1.0f); }
+
+  uint8_t rgb8_r() const { return static_cast<uint8_t>(r() * 255); }
+
+  uint8_t rgb8_g() const { return static_cast<uint8_t>(g() * 255); }
+
+  uint8_t rgb8_b() const { return static_cast<uint8_t>(b() * 255); }
+
+  uint8_t rgb8_a() const { return static_cast<uint8_t>(a() * 255); }
+
+  ColorRGBA& operator=(const ColorRGBA& other) {
+    r_ = other.r_;
+    g_ = other.g_;
+    b_ = other.b_;
+    a_ = other.a_;
+    return *this;
+  }
+
+  ColorRGBA& operator-=(const ColorRGBA& other) {
+    r_ -= other.r_;
+    g_ -= other.g_;
+    b_ -= other.b_;
+    a_ -= other.a_;
+    return *this;
+  }
+
+  ColorRGBA& operator+=(const ColorRGBA& other) {
+    r_ += other.r_;
+    g_ += other.g_;
+    b_ += other.b_;
+    a_ += other.a_;
+    return *this;
+  }
+
+  ColorRGBA& operator+=(const float f) {
+    r_ += f;
+    b_ += f;
+    g_ += f;
+    a_ += f;
+    return *this;
+  }
+
+  ColorRGBA& operator-=(const float f) {
+    r_ -= f;
+    b_ -= f;
+    g_ -= f;
+    a_ -= f;
+    return *this;
+  }
+
+  ColorRGBA& operator*=(const float f) {
+    r_ *= f;
+    b_ *= f;
+    g_ *= f;
+    a_ *= f;
+    return *this;
+  }
+
+  ColorRGBA& operator/=(const float f) {
+    float one_over_f(1.0f / f);
+    *this *= one_over_f;
+    return *this;
+  }
 
  private:
-  void CheckRange(float value) {
+  void CheckRange(float value) const {
     DCHECK_LE(0.0f, value);
     DCHECK_GE(1.0f, value);
   }
@@ -100,6 +174,46 @@
          lhs.a() == rhs.a();
 }
 
+inline bool operator!=(const ColorRGBA& lhs, const ColorRGBA& rhs) {
+  return !(lhs == rhs);
+}
+
+inline ColorRGBA operator-(const ColorRGBA& lhs, const ColorRGBA& rhs) {
+  ColorRGBA color(lhs);
+  color -= rhs;
+  return color;
+}
+
+inline ColorRGBA operator+(const ColorRGBA& lhs, const ColorRGBA& rhs) {
+  ColorRGBA color(lhs);
+  color += rhs;
+  return color;
+}
+
+inline ColorRGBA operator+(const ColorRGBA& lhs, const float rhs) {
+  ColorRGBA color(lhs);
+  color += rhs;
+  return color;
+}
+
+inline ColorRGBA operator-(const ColorRGBA& lhs, const float rhs) {
+  ColorRGBA color(lhs);
+  color -= rhs;
+  return color;
+}
+
+inline ColorRGBA operator*(const ColorRGBA& lhs, const float rhs) {
+  ColorRGBA color(lhs);
+  color *= rhs;
+  return color;
+}
+
+inline ColorRGBA operator/(const ColorRGBA& lhs, const float rhs) {
+  ColorRGBA color(lhs);
+  color /= rhs;
+  return color;
+}
+
 // Used by tests.
 inline std::ostream& operator<<(std::ostream& stream, const ColorRGBA& color) {
   return stream << "rgba(" << color.r() << ", " << color.g() << ", "
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 9974067..0056361 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -128,6 +128,7 @@
   // must be destroyed before we shutdown the rasterizer thread since it may
   // contain references to render tree nodes and resources.
   last_rendered_expired_render_tree_ = NULL;
+  last_render_tree_ = NULL;
 
   // Submit a shutdown task to the rasterizer thread so that it can shutdown
   // anything that must be shutdown from that thread.
@@ -265,19 +266,45 @@
     const scoped_refptr<backend::RenderTarget>& render_target) {
   TRACE_EVENT0("cobalt::renderer",
                "Pipeline::RasterizeSubmissionToRenderTarget()");
+
+  // Keep track of the last render tree that we rendered so that we can watch
+  // if it changes, in which case we should reset our tracked
+  // |previous_animated_area_|.
+  if (submission.render_tree != last_render_tree_) {
+    last_render_tree_ = submission.render_tree;
+    previous_animated_area_ = base::nullopt;
+    last_render_time_ = base::nullopt;
+  }
+
   // Animate the render tree using the submitted animations.
   render_tree::animations::AnimateNode* animate_node =
       base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
           submission.render_tree.get());
-  scoped_refptr<Node> animated_render_tree =
+  render_tree::animations::AnimateNode::AnimateResults results =
       animate_node->Apply(submission.time_offset);
 
+  // Calculate a bounding box around the active animations.  Union it with the
+  // bounding box around active animations from the previous frame, and we get
+  // a scissor rectangle marking the dirty regions of the screen.
+  math::RectF animated_bounds = results.get_animation_bounds_since.Run(
+      last_render_time_ ? *last_render_time_ : base::TimeDelta());
+  math::Rect rounded_bounds = math::RoundOut(animated_bounds);
+  base::optional<math::Rect> redraw_area;
+  if (previous_animated_area_) {
+    redraw_area = math::UnionRects(rounded_bounds, *previous_animated_area_);
+  }
+  previous_animated_area_ = rounded_bounds;
+
   // Rasterize the animated render tree.
-  rasterizer_->Submit(animated_render_tree, render_target);
+  rasterizer::Rasterizer::Options rasterizer_options;
+  rasterizer_options.dirty = redraw_area;
+  rasterizer_->Submit(results.animated, render_target, rasterizer_options);
 
   if (!submission.on_rasterized_callback.is_null()) {
     submission.on_rasterized_callback.Run();
   }
+
+  last_render_time_ = submission.time_offset;
 }
 
 void Pipeline::InitializeRasterizerThread(
@@ -352,11 +379,10 @@
   render_tree::animations::AnimateNode* animate_node =
       base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
           submission.render_tree.get());
-  scoped_refptr<Node> animated_render_tree =
+  render_tree::animations::AnimateNode::AnimateResults results =
       animate_node->Apply(submission.time_offset);
 
-  std::string tree_dump =
-      render_tree::DumpRenderTreeToString(animated_render_tree);
+  std::string tree_dump = render_tree::DumpRenderTreeToString(results.animated);
   if (message.empty() || message == "undefined") {
     // If no filename was specified, send output to the console.
     LOG(INFO) << tree_dump.c_str();
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 975f593..3b96cc3 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -176,6 +176,15 @@
   // frame, even if it hasn't changed.
   const bool submit_even_if_render_tree_is_unchanged_;
 
+  // Keeps track of the last rendered animated render tree.
+  scoped_refptr<render_tree::Node> last_render_tree_;
+  // Keeps track of the area of the screen that animations previously existed
+  // within, so that we can know which regions of the screens would be dirty
+  // next frame.
+  base::optional<math::Rect> previous_animated_area_;
+  // The submission time used during the last render tree render.
+  base::optional<base::TimeDelta> last_render_time_;
+
   // Timers for tracking how frequently |RasterizeCurrentTree| is called and
   // the amount of time spent in |RasterizeCurrentTree| each call.
   base::CValTimeIntervalTimer<base::CValPublic>
diff --git a/src/cobalt/renderer/pipeline_test.cc b/src/cobalt/renderer/pipeline_test.cc
index c258641..c70d44e 100644
--- a/src/cobalt/renderer/pipeline_test.cc
+++ b/src/cobalt/renderer/pipeline_test.cc
@@ -52,7 +52,7 @@
 
   void Submit(const scoped_refptr<cobalt::render_tree::Node>&,
               const scoped_refptr<cobalt::renderer::backend::RenderTarget>&,
-              int options) OVERRIDE {
+              const Options& options) OVERRIDE {
     if (last_submission_time) {
       // Simulate a "wait for vsync".
       base::TimeDelta since_last_submit =
diff --git a/src/cobalt/renderer/rasterizer/benchmark.cc b/src/cobalt/renderer/rasterizer/benchmark.cc
index 5507cb7..c0d2567 100644
--- a/src/cobalt/renderer/rasterizer/benchmark.cc
+++ b/src/cobalt/renderer/rasterizer/benchmark.cc
@@ -116,8 +116,10 @@
   for (int i = 0; i < kRenderIterationCount; ++i) {
     AnimateNode* animate_node =
         base::polymorphic_downcast<AnimateNode*>(scene.get());
-    scoped_refptr<Node> animated = animate_node->Apply(
-        base::TimeDelta::FromSecondsD(i * kFixedTimeStepInSecondsPerFrame));
+    scoped_refptr<Node> animated =
+        animate_node->Apply(base::TimeDelta::FromSecondsD(
+                                i * kFixedTimeStepInSecondsPerFrame))
+            .animated;
 
     // Submit the render tree to be rendered.
     rasterizer->Submit(animated, test_surface);
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
index 7739988..bcb25db 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
@@ -45,7 +45,11 @@
       software_rasterizer_(0),
       cache_memory_usage_(
           "Memory.CachedSoftwareRasterizer.CacheUsage", 0,
-          "Total memory occupied by cached software-rasterized surfaces.") {}
+          "Total memory occupied by cached software-rasterized surfaces."),
+      cache_frame_usage_(
+          "Memory.CachedSoftwareRasterizer.FrameCacheUsage", 0,
+          "Total memory occupied by cache software-rasterizer surfaces that "
+          "were referenced this frame.") {}
 
 CachedSoftwareRasterizer::~CachedSoftwareRasterizer() {
   // Clean up any leftover surfaces.
@@ -62,19 +66,35 @@
     CacheMap::iterator current = iter;
     ++iter;
 
-    // If the surface wasn't referenced, destroy it.  If it was, mark it as
-    // being unreferenced.
-    if (!current->second.referenced) {
-      cache_memory_usage_ -= current->second.GetEstimatedMemoryUsage();
-      SbBlitterDestroySurface(current->second.surface);
-      surface_map_.erase(current);
-    } else {
+    // If the surface was referenced mark it as being unreferenced for the next
+    // frame.
+    if (current->second.referenced) {
 #if defined(ENABLE_DEBUG_CONSOLE)
       current->second.created = false;
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
       current->second.referenced = false;
     }
   }
+
+  // Reset our current frame cache usage to 0 since this is the start of a new
+  // frame.
+  cache_frame_usage_ = 0;
+}
+
+void CachedSoftwareRasterizer::PurgeUntilSpaceAvailable(int space_needed) {
+  while (space_needed + cache_memory_usage_.value() > cache_capacity_) {
+    CacheMap::iterator next_item = surface_map_.begin();
+
+    // We shouldn't call this function if it means we would have to purge
+    // elements that were referenced this frame.  This is to avoid thrashing
+    // the cache, we would prefer to cache as much as we can in a frame, and
+    // then just not cache whatever we can't without cycling what's in the cache
+    // already.
+    DCHECK(!next_item->second.referenced);
+    cache_memory_usage_ -= next_item->second.GetEstimatedMemoryUsage();
+    SbBlitterDestroySurface(next_item->second.surface);
+    surface_map_.erase(next_item);
+  }
 }
 
 CachedSoftwareRasterizer::Surface CachedSoftwareRasterizer::GetSurface(
@@ -88,8 +108,18 @@
     if (found->second.scale.x() == transform.scale().x() &&
         found->second.scale.y() == transform.scale().y()) {
 #endif
-      found->second.referenced = true;
-      return found->second;
+      std::pair<render_tree::Node*, Surface> to_insert =
+          std::make_pair(node, found->second);
+
+      // Move this surface's position in the queue to the front, since it was
+      // referenced.
+      to_insert.second.referenced = true;
+      surface_map_.erase(found);
+      surface_map_.insert(to_insert);
+
+      cache_frame_usage_ += found->second.GetEstimatedMemoryUsage();
+
+      return to_insert.second;
     }
   }
 
@@ -191,17 +221,22 @@
   software_surface.surface =
       SbBlitterCreateSurfaceFromPixelData(device_, pixel_data);
 
-  if (software_surface.GetEstimatedMemoryUsage() +
-          cache_memory_usage_.value() <=
+  if (software_surface.GetEstimatedMemoryUsage() + cache_frame_usage_.value() <=
       cache_capacity_) {
     software_surface.cached = true;
+    cache_frame_usage_ += software_surface.GetEstimatedMemoryUsage();
+
     if (found != surface_map_.end()) {
+      // This surface may have already been in the cache if it was in there
+      // with a different scale.  In that case, replace the old one.
       cache_memory_usage_ -= found->second.GetEstimatedMemoryUsage();
-      found->second = software_surface;
-    } else {
-      std::pair<CacheMap::iterator, bool> inserted =
-          surface_map_.insert(std::make_pair(node, software_surface));
+      surface_map_.erase(found);
     }
+
+    PurgeUntilSpaceAvailable(software_surface.GetEstimatedMemoryUsage());
+
+    std::pair<CacheMap::iterator, bool> inserted =
+        surface_map_.insert(std::make_pair(node, software_surface));
     cache_memory_usage_ += software_surface.GetEstimatedMemoryUsage();
   }
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
index f71d49b..10d76e9 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
@@ -17,6 +17,7 @@
 #ifndef COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
 #define COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
 
+#include "base/containers/linked_hash_map.h"
 #include "base/hash_tables.h"
 #include "base/memory/ref_counted.h"
 #include "cobalt/base/c_val.h"
@@ -116,8 +117,15 @@
   }
 
  private:
+  typedef base::linked_hash_map<render_tree::Node*, Surface> CacheMap;
+
+  // Release surfaces until we have |space_needed| free bytes in the cache.
+  // This function will never release surfaces that were referenced this frame.
+  // It is an error to call this function if it is impossible to purge
+  // unreferenced surfaces until the desired amount of free space is available.
+  void PurgeUntilSpaceAvailable(int space_needed);
+
   // The cache, mapping input render_tree::Node references to cached surfaces.
-  typedef base::hash_map<render_tree::Node*, Surface> CacheMap;
   CacheMap surface_map_;
 
   const int cache_capacity_;
@@ -133,6 +141,9 @@
   // The amount of memory currently consumed by the surfaces populating the
   // cache.
   base::CVal<base::cval::SizeInBytes> cache_memory_usage_;
+
+  // Cache memory used this frame only.
+  base::CVal<base::cval::SizeInBytes> cache_frame_usage_;
 };
 
 }  // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc
index 7206ba2..29a960a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc
@@ -23,11 +23,9 @@
 namespace rasterizer {
 namespace blitter {
 
-namespace {
 int RoundToInt(float value) {
   return static_cast<int>(std::floor(value + 0.5f));
 }
-}  // namespace
 
 math::Rect RectFToRect(const math::RectF& rectf) {
   // We convert from floating point to integer in such a way that two boxes
diff --git a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
index 29afcec..d13b77d 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
+++ b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
@@ -28,6 +28,7 @@
 namespace rasterizer {
 namespace blitter {
 
+int RoundToInt(float value);
 math::Rect RectFToRect(const math::RectF& rectf);
 
 inline SbBlitterRect RectToBlitterRect(const math::Rect& rect) {
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index dee6372..7c9304e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -51,7 +51,7 @@
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options);
+              const Options& options);
 
   render_tree::ResourceProvider* GetResourceProvider();
 
@@ -59,6 +59,7 @@
 #if defined(ENABLE_DEBUG_CONSOLE)
   void OnToggleHighlightSoftwareDraws(const std::string& message);
 #endif
+  void SetupLastFrameSurface(int width, int height);
 
   base::ThreadChecker thread_checker_;
 
@@ -68,6 +69,11 @@
 
   int64 submit_count_;
 
+  // We maintain a "final results" surface that mirrors the display buffer.
+  // This way, we can rerender only the dirty parts of the screen to this
+  // |current_frame_| buffer and then blit that to the display.
+  SbBlitterSurface current_frame_;
+
   ScratchSurfaceCache scratch_surface_cache_;
   base::optional<SurfaceCacheDelegate> surface_cache_delegate_;
   base::optional<common::SurfaceCache> surface_cache_;
@@ -90,6 +96,7 @@
     : context_(base::polymorphic_downcast<backend::GraphicsContextBlitter*>(
           graphics_context)),
       submit_count_(0),
+      current_frame_(kSbBlitterInvalidSurface),
       scratch_surface_cache_(context_->GetSbBlitterDevice(),
                              context_->GetSbBlitterContext(),
                              scratch_surface_size_in_bytes),
@@ -122,7 +129,7 @@
   }
 }
 
-HardwareRasterizer::Impl::~Impl() {}
+HardwareRasterizer::Impl::~Impl() { SbBlitterDestroySurface(current_frame_); }
 
 #if defined(ENABLE_DEBUG_CONSOLE)
 void HardwareRasterizer::Impl::OnToggleHighlightSoftwareDraws(
@@ -134,7 +141,8 @@
 
 void HardwareRasterizer::Impl::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
   TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
 
   int width = render_target->GetSize().width();
@@ -146,8 +154,12 @@
       base::polymorphic_downcast<backend::RenderTargetBlitter*>(
           render_target.get());
 
-  CHECK(SbBlitterSetRenderTarget(context,
-                                 render_target_blitter->GetSbRenderTarget()));
+  if (!SbBlitterIsSurfaceValid(current_frame_)) {
+    SetupLastFrameSurface(width, height);
+  }
+
+  CHECK(SbBlitterSetRenderTarget(
+      context, SbBlitterGetRenderTargetFromSurface(current_frame_)));
 
   // Update our surface cache to do per-frame calculations such as deciding
   // which render tree nodes are candidates for caching in this upcoming
@@ -163,7 +175,9 @@
   // purposes, so that despite the Blitter API implementation, we ensure that
   // if the output buffer is not completely rendered to, data from a previous
   // process cannot leak in.
-  if (options & Rasterizer::kSubmitOptions_Clear || submit_count_ < 3) {
+  bool cleared = false;
+  if (options.flags & Rasterizer::kSubmitFlags_Clear || submit_count_ < 3) {
+    cleared = true;
     CHECK(SbBlitterSetBlending(context, false));
     CHECK(SbBlitterSetColor(context, SbBlitterColorFromRGBA(0, 0, 0, 0)));
     CHECK(SbBlitterFillRect(context, SbBlitterMakeRect(0, 0, width, height)));
@@ -173,10 +187,16 @@
     TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
 
     // 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 a dirty rectangle was specified, limit our redrawing to within it.
+      start_bounds.Push(*options.dirty);
+    }
+
     RenderState initial_render_state(
-        render_target_blitter->GetSbRenderTarget(), Transform(),
-        BoundsStack(context_->GetSbBlitterContext(),
-                    math::Rect(render_target_blitter->GetSize())));
+        SbBlitterGetRenderTargetFromSurface(current_frame_), Transform(),
+        start_bounds);
 #if defined(ENABLE_DEBUG_CONSOLE)
     initial_render_state.highlight_software_draws =
         toggle_highlight_software_draws_;
@@ -191,6 +211,13 @@
   }
 
   // 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));
   render_target_blitter->Flip();
 
@@ -201,6 +228,12 @@
   return resource_provider_.get();
 }
 
+void HardwareRasterizer::Impl::SetupLastFrameSurface(int width, int height) {
+  current_frame_ =
+      SbBlitterCreateRenderTargetSurface(context_->GetSbBlitterDevice(), width,
+                                         height, kSbBlitterSurfaceFormatRGBA8);
+}
+
 HardwareRasterizer::HardwareRasterizer(
     backend::GraphicsContext* graphics_context,
     int scratch_surface_size_in_bytes, int surface_cache_size_in_bytes,
@@ -213,7 +246,8 @@
 
 void HardwareRasterizer::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
   return impl_->Submit(render_tree, render_target, options);
 }
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
index 6a48124..5ab8fd5 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
@@ -47,7 +47,7 @@
   // into the constructor.
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options) OVERRIDE;
+              const Options& options) OVERRIDE;
 
   render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
new file mode 100644
index 0000000..1dc7e96
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/renderer/rasterizer/blitter/linear_gradient.h"
+
+#include <algorithm>
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h"
+#include "cobalt/renderer/rasterizer/blitter/render_state.h"
+#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
+#include "cobalt/renderer/rasterizer/skia/conversions.h"
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+#if SB_HAS(BLITTER)
+
+namespace {
+
+using cobalt::render_tree::ColorRGBA;
+using cobalt::render_tree::ColorStopList;
+using cobalt::render_tree::LinearGradientBrush;
+using cobalt::renderer::rasterizer::blitter::RenderState;
+using cobalt::renderer::rasterizer::blitter::SkiaToBlitterPixelFormat;
+using cobalt::renderer::rasterizer::blitter::RectFToRect;
+using cobalt::renderer::rasterizer::skia::SkiaColorStops;
+
+int GetPixelOffsetInBytes(const SkImageInfo& image_info,
+                          const SbBlitterPixelData& pixel_data) {
+  if (image_info.width() == 1)
+    return SbBlitterGetPixelDataPitchInBytes(pixel_data);
+
+  SbBlitterPixelDataFormat pixel_format =
+      SkiaToBlitterPixelFormat(image_info.colorType());
+  return SbBlitterBytesPerPixelForFormat(pixel_format);
+}
+
+void ColorStopToPixelData(SbBlitterPixelDataFormat pixel_format,
+                          const ColorRGBA& color_stop, uint8_t* pixel_data_out,
+                          uint8_t* pixel_data_out_end) {
+  DCHECK(pixel_data_out_end >=
+         (pixel_data_out + SbBlitterBytesPerPixelForFormat(pixel_format)));
+  uint8_t final_b(color_stop.rgb8_b());
+  uint8_t final_g(color_stop.rgb8_g());
+  uint8_t final_r(color_stop.rgb8_r());
+  uint8_t final_a(color_stop.rgb8_a());
+
+  switch (pixel_format) {
+    case kSbBlitterPixelDataFormatARGB8:
+      pixel_data_out[0] = final_a;
+      pixel_data_out[1] = final_r;
+      pixel_data_out[2] = final_g;
+      pixel_data_out[3] = final_b;
+      break;
+    case kSbBlitterPixelDataFormatBGRA8:
+      pixel_data_out[0] = final_b;
+      pixel_data_out[1] = final_g;
+      pixel_data_out[2] = final_r;
+      pixel_data_out[3] = final_a;
+      break;
+    case kSbBlitterPixelDataFormatRGBA8:
+      pixel_data_out[0] = final_r;
+      pixel_data_out[1] = final_g;
+      pixel_data_out[2] = final_b;
+      pixel_data_out[3] = final_a;
+      break;
+    default:
+      NOTREACHED() << "Unknown or unsupported pixel format." << pixel_format;
+      break;
+  }
+}
+
+// This function uses Skia to render a gradient into the pixel data pointed
+// at pixel_data_begin.  This function is intended as a fallback in case when
+// RenderOptimizedLinearGradient is unable to render the gradient.
+void RenderComplexLinearGradient(const LinearGradientBrush& brush,
+                                 const ColorStopList& color_stops, int width,
+                                 int height, const SkImageInfo& image_info,
+                                 uint8_t* pixel_data_begin,
+                                 int pitch_in_bytes) {
+  SkBitmap bitmap;
+  bitmap.installPixels(image_info, pixel_data_begin, pitch_in_bytes);
+  SkCanvas canvas(bitmap);
+  canvas.clear(SkColorSetARGB(0, 0, 0, 0));
+
+  SkPoint points[2];
+  points[0].fX = 0;
+  points[0].fY = 0;
+  points[1].fX = 0;
+  points[1].fY = 0;
+
+  // The -1 offset is easiest to think about in a 2 pixel case:
+  // If width = 2, then the ending coordinate should be 1.
+  if (brush.IsHorizontal()) {
+    if (brush.dest().x() < brush.source().x()) {
+      points[0].fX = width - 1;  // Right to Left.
+    } else {
+      points[1].fX = width - 1;  // Left to Right.
+    }
+  } else {
+    if (brush.dest().y() < brush.source().y()) {
+      points[0].fY = height - 1;  // High to Low.
+    } else {
+      points[1].fY = height - 1;  // Low to High.
+    }
+  }
+
+  SkiaColorStops skia_color_stops(color_stops);
+  SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(
+      points, skia_color_stops.colors.data(), skia_color_stops.positions.data(),
+      skia_color_stops.size(), SkShader::kClamp_TileMode,
+      SkGradientShader::kInterpolateColorsInPremul_Flag, NULL));
+  SkPaint paint;
+  paint.setShader(shader);
+  canvas.drawPaint(paint);
+}
+
+// This function tries to render a "simple" gradient into the pixel data pointed
+// to in the range [pixel_data_begin, pixel_data_end).  Simple gradients here
+// are defined as has following properties:
+//   1.  Vertical or horizontal gradient.
+//   2.  Two color stops only -- one at position 0, and one at position 1.
+//
+// Having these conditions greatly simplifies the logic, and allows us to avoid
+// the overhead of dealing with Skia.
+//
+// Returns: true if it could successfully render, false if optimal conditions
+// are not met.  If false is returned, nothing is written to the pixel data
+// buffer.
+bool RenderSimpleGradient(const LinearGradientBrush& brush,
+                          const ColorStopList& color_stops, int width,
+                          int height, SbBlitterPixelDataFormat pixel_format,
+                          uint8_t* pixel_data_begin, uint8_t* pixel_data_end,
+                          int pixel_offset_in_bytes) {
+  if (color_stops.size() != 2) {
+    return false;
+  }
+
+  const float small_value(1E-6);
+  if ((color_stops[0].position < -small_value) ||
+      (color_stops[0].position > small_value)) {
+    return false;
+  }
+  if ((color_stops[1].position < 1 - small_value) ||
+      (color_stops[1].position > 1 + small_value)) {
+    return false;
+  }
+
+  const int number_pixels = width * height;
+
+  if ((number_pixels < 2) || (width < 0) || (height < 0)) {
+    return false;
+  }
+
+  ColorRGBA start_color = color_stops[0].color;
+  ColorRGBA end_color = color_stops[1].color;
+
+  // Swap start and end colors if the gradient direction is right to left or
+  // down to up.
+  if (brush.IsHorizontal()) {
+    if (brush.dest().x() < brush.source().x()) {
+      std::swap(start_color, end_color);
+    }
+  } else {
+    if (brush.dest().y() < brush.source().y()) {
+      std::swap(start_color, end_color);
+    }
+  }
+
+  ColorRGBA color_step((end_color - start_color) / (number_pixels - 1));
+  // We add the color_stop * 0.5f to match pixel positioning in software Skia.
+  ColorRGBA current_color(start_color + color_step * 0.5f);
+  ColorStopToPixelData(pixel_format, current_color, pixel_data_begin,
+                       pixel_data_end);
+
+  uint8_t* second_pixel = pixel_data_begin + pixel_offset_in_bytes;
+  uint8_t* last_pixel =
+      (pixel_data_begin + (number_pixels - 1) * pixel_offset_in_bytes);
+  for (uint8_t* current_pixel = second_pixel; current_pixel != last_pixel;
+       current_pixel += pixel_offset_in_bytes) {
+    current_color += color_step;
+    ColorStopToPixelData(pixel_format, current_color, current_pixel,
+                         pixel_data_end);
+  }
+
+  ColorStopToPixelData(pixel_format, end_color, last_pixel, pixel_data_end);
+
+  return true;
+}
+
+void RenderOptimizedLinearGradient(SbBlitterDevice device,
+                                   SbBlitterContext context,
+                                   const RenderState& render_state,
+                                   cobalt::math::RectF rect,
+                                   const LinearGradientBrush& brush) {
+  if ((rect.width() == 0) && (rect.height() == 0)) return;
+
+  DCHECK(brush.IsVertical() || brush.IsHorizontal());
+
+  // The main strategy here is to create a 1D image, and then calculate
+  // the gradient values in software.  If the gradient is simple, this can be
+  // one with optimized function (RenderSimpleGradient), which avoids calling
+  // Skia (and thus is faster).  Otherwise, we call RenderComplexLinearGradient,
+  // which uses Skia.
+
+  // One a gradient is created, a SbBlitterSurface is created and a rectangle
+  // is blitted using the blitter API.
+  int width = brush.IsHorizontal() ? rect.width() : 1;
+  int height = brush.IsVertical() ? rect.height() : 1;
+
+  SkImageInfo image_info = SkImageInfo::MakeN32Premul(width, height);
+
+  SbBlitterPixelDataFormat pixel_format =
+      SkiaToBlitterPixelFormat(image_info.colorType());
+
+  if (!SbBlitterIsPixelFormatSupportedByPixelData(device, pixel_format)) {
+    NOTREACHED() << "Pixel Format is not supported by Pixel Data";
+    return;
+  }
+
+  SbBlitterPixelData pixel_data =
+      SbBlitterCreatePixelData(device, width, height, pixel_format);
+
+  int bytes_per_pixel = GetPixelOffsetInBytes(image_info, pixel_data);
+  int pitch_in_bytes = SbBlitterGetPixelDataPitchInBytes(pixel_data);
+
+  uint8_t* pixel_data_begin =
+      static_cast<uint8_t*>(SbBlitterGetPixelDataPointer(pixel_data));
+  uint8_t* pixel_data_end = pixel_data_begin + pitch_in_bytes * height;
+
+  if (pixel_data) {
+    const ColorStopList& color_stops(brush.color_stops());
+
+    int pixel_offset_in_bytes = (width == 1) ? pitch_in_bytes : bytes_per_pixel;
+    bool render_successful = RenderSimpleGradient(
+        brush, color_stops, width, height, pixel_format, pixel_data_begin,
+        pixel_data_end, pixel_offset_in_bytes);
+    if (!render_successful) {
+      RenderComplexLinearGradient(brush, color_stops, width, height, image_info,
+                                  pixel_data_begin, pitch_in_bytes);
+    }
+
+    SbBlitterSurface surface =
+        SbBlitterCreateSurfaceFromPixelData(device, pixel_data);
+
+    if (surface) {
+      SbBlitterSetBlending(context, true);
+      SbBlitterSetModulateBlitsWithColor(context, false);
+      cobalt::math::Rect transformed_rect =
+          RectFToRect(render_state.transform.TransformRect(rect));
+      SbBlitterRect source_rect = SbBlitterMakeRect(0, 0, width, height);
+      SbBlitterRect dest_rect = SbBlitterMakeRect(
+          transformed_rect.x(), transformed_rect.y(), transformed_rect.width(),
+          transformed_rect.height());
+      SbBlitterBlitRectToRect(context, surface, source_rect, dest_rect);
+
+      SbBlitterDestroySurface(surface);
+    }
+  }
+}
+}  // namespace
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace blitter {
+
+using render_tree::LinearGradientBrush;
+
+bool RenderLinearGradient(SbBlitterDevice device, SbBlitterContext context,
+                          const RenderState& render_state,
+                          const render_tree::RectNode& rect_node) {
+  DCHECK(rect_node.data().background_brush);
+  const LinearGradientBrush* const linear_gradient_brush =
+      base::polymorphic_downcast<LinearGradientBrush*>(
+          rect_node.data().background_brush.get());
+  if (!linear_gradient_brush) return false;
+
+  // Currently, only vertical and horizontal gradients are accelerated.
+  if ((linear_gradient_brush->IsVertical() ||
+       linear_gradient_brush->IsHorizontal()) == false)
+    return false;
+
+  RenderOptimizedLinearGradient(device, context, render_state,
+                                rect_node.data().rect, *linear_gradient_brush);
+  return true;
+}
+
+}  // namespace blitter
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // #if SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
new file mode 100644
index 0000000..8340722
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
@@ -0,0 +1,46 @@
+/*
+ * 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_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
+#define COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/rect_node.h"
+#include "cobalt/renderer/rasterizer/blitter/render_state.h"
+#include "starboard/blitter.h"
+
+#ifndef SB_HAS_BLITTER
+#define SB_HAS_BLITTER
+#endif
+#if SB_HAS(BLITTER)
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace blitter {
+
+bool RenderLinearGradient(SbBlitterDevice device, SbBlitterContext context,
+                          const RenderState& render_state,
+                          const render_tree::RectNode& rect_node);
+
+}  // namespace blitter
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // #if SB_HAS(BLITTER)
+
+#endif  // COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index 07f6947..b5e0bec 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -42,6 +42,7 @@
         'cobalt_blitter_conversions.cc',
         'hardware_rasterizer.cc',
         'image.cc',
+        'linear_gradient.cc',
         'render_state.cc',
         'render_tree_blitter_conversions.cc',
         'render_tree_node_visitor.cc',
@@ -81,4 +82,4 @@
       ],
     },
   ],
-}
\ No newline at end of file
+}
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_state.h b/src/cobalt/renderer/rasterizer/blitter/render_state.h
index 8b05887..b0a828a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_state.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_state.h
@@ -25,6 +25,7 @@
 #include "cobalt/math/rect.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/math/vector2d_f.h"
+#include "cobalt/render_tree/brush.h"
 #include "starboard/blitter.h"
 
 #if SB_HAS(BLITTER)
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 8726b2e..8fc3821 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -17,6 +17,7 @@
 #include "cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h"
 
 #include "base/bind.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect.h"
 #include "cobalt/math/rect_f.h"
@@ -25,6 +26,7 @@
 #include "cobalt/math/vector2d_f.h"
 #include "cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h"
 #include "cobalt/renderer/rasterizer/blitter/image.h"
+#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 "starboard/blitter.h"
@@ -44,6 +46,9 @@
 using render_tree::Border;
 using render_tree::Brush;
 using render_tree::ColorRGBA;
+using render_tree::ColorStop;
+using render_tree::ColorStopList;
+using render_tree::LinearGradientBrush;
 using render_tree::SolidColorBrush;
 using render_tree::ViewportFilter;
 
@@ -364,34 +369,43 @@
       return;
     }
   }
-  if (rect_node->data().background_brush) {
-    if (rect_node->data().background_brush->GetTypeId() !=
-        base::GetTypeId<SolidColorBrush>()) {
-      // We can only render solid color rectangles, if we have a more
-      // complicated brush (like gradients), fallback to software.
-      RenderWithSoftwareRenderer(rect_node);
-      return;
-    }
-  }
 
   const RectF& transformed_rect =
       render_state_.transform.TransformRect(rect_node->data().rect);
 
-  // Render the solid color fill, if a brush exists.
   if (rect_node->data().background_brush) {
-    SolidColorBrush* solid_color_brush =
-        base::polymorphic_downcast<SolidColorBrush*>(
-            rect_node->data().background_brush.get());
-    ColorRGBA color = solid_color_brush->color();
+    base::TypeId background_brush_typeid(
+        rect_node->data().background_brush->GetTypeId());
 
-    if (render_state_.opacity < 1.0f) {
-      color.set_a(color.a() * render_state_.opacity);
+    if (background_brush_typeid == base::GetTypeId<SolidColorBrush>()) {
+      // Render the solid color fill, if a brush exists.
+      SolidColorBrush* solid_color_brush =
+          base::polymorphic_downcast<SolidColorBrush*>(
+              rect_node->data().background_brush.get());
+      ColorRGBA color = solid_color_brush->color();
+
+      if (render_state_.opacity < 1.0f) {
+        color.set_a(color.a() * render_state_.opacity);
+      }
+
+      SbBlitterSetBlending(context_, color.a() < 1.0f);
+      SbBlitterSetColor(context_, RenderTreeToBlitterColor(color));
+
+      SbBlitterFillRect(context_, RectFToBlitterRect(transformed_rect));
+    } else if (background_brush_typeid ==
+               base::GetTypeId<LinearGradientBrush>()) {
+      DCHECK(rect_node != NULL);
+      bool rendered_gradient =
+          RenderLinearGradient(device_, context_, render_state_, *rect_node);
+      if (!rendered_gradient) {
+        RenderWithSoftwareRenderer(rect_node);
+        return;
+      }
+    } else {
+      // If we have a more complicated brush fallback to software.
+      RenderWithSoftwareRenderer(rect_node);
+      return;
     }
-
-    SbBlitterSetBlending(context_, color.a() < 1.0f);
-    SbBlitterSetColor(context_, RenderTreeToBlitterColor(color));
-
-    SbBlitterFillRect(context_, RectFToBlitterRect(transformed_rect));
   }
 
   // Render the border, if it exists.
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
index 4d9e0ee..938a5cf 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
@@ -39,6 +39,11 @@
 #include "cobalt/renderer/rasterizer/common/surface_cache.h"
 
 #include "starboard/blitter.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+#ifndef SB_HAS_BLITTER
+#define SB_HAS_BLITTER
+#endif
 
 #if SB_HAS(BLITTER)
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
index cad2346..6622fbc 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
@@ -39,7 +39,8 @@
 
 void SoftwareRasterizer::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
   int width = render_target->GetSize().width();
   int height = render_target->GetSize().height();
 
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
index 4743332..646f3c4 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
@@ -44,7 +44,7 @@
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options) OVERRIDE;
+              const Options& options) OVERRIDE;
 
   render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
index e778d0f..97fcc63 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
@@ -40,7 +40,8 @@
 
 void SoftwareRasterizer::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
   int width = render_target->GetSize().width();
   int height = render_target->GetSize().height();
 
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
index 662353c..8ae5e84 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
@@ -44,7 +44,7 @@
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options) OVERRIDE;
+              const Options& options) OVERRIDE;
 
   render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/rasterizer.h b/src/cobalt/renderer/rasterizer/rasterizer.h
index b15d4bb..fea7fe2 100644
--- a/src/cobalt/renderer/rasterizer/rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/rasterizer.h
@@ -18,6 +18,8 @@
 #define COBALT_RENDERER_RASTERIZER_RASTERIZER_H_
 
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "cobalt/math/rect.h"
 #include "cobalt/render_tree/font.h"
 #include "cobalt/render_tree/image.h"
 #include "cobalt/render_tree/node.h"
@@ -45,7 +47,21 @@
  public:
   // When set, will clear the render target before rasterizing the render tree
   // to it.
-  static const int kSubmitOptions_Clear = (1 << 0);
+  static const int kSubmitFlags_Clear = (1 << 0);
+
+  struct Options {
+    Options() : flags(0) {}
+
+    // A bitwise combination of any of the |kSubmitFlags_*| constants defined
+    // above.
+    int flags;
+
+    // If specified, indicates which region of |render_target| is
+    // dirty and needs to be updated.  If animations are playing for example,
+    // then |dirty| can be setup to bound the animations.  A rasterizer is free
+    // to ignore this value if they wish.
+    base::optional<math::Rect> dirty;
+  };
 
   virtual ~Rasterizer() {}
 
@@ -54,11 +70,11 @@
   // above.
   virtual void Submit(const scoped_refptr<render_tree::Node>& render_tree,
                       const scoped_refptr<backend::RenderTarget>& render_target,
-                      int options) = 0;
+                      const Options& options) = 0;
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target) {
-    Submit(render_tree, render_target, 0);
+    Submit(render_tree, render_target, Options());
   }
 
   // Returns a thread-safe object from which one can produce renderer resources
diff --git a/src/cobalt/renderer/rasterizer/skia/conversions.h b/src/cobalt/renderer/rasterizer/skia/conversions.h
new file mode 100644
index 0000000..c5640e4
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/conversions.h
@@ -0,0 +1,65 @@
+/*
+ * 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_RASTERIZER_SKIA_CONVERSIONS_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_CONVERSIONS_H_
+
+#include <vector>
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace skia {
+
+inline SkColor ToSkColor(const render_tree::ColorRGBA& color) {
+  return SkColorSetARGB(color.rgb8_a(), color.rgb8_r(), color.rgb8_g(),
+                        color.rgb8_b());
+}
+
+// Helper struct to convert render_tree::ColorStopList to a format that Skia
+// methods will easily accept.
+struct SkiaColorStops {
+  explicit SkiaColorStops(const render_tree::ColorStopList& color_stops)
+      : colors(color_stops.size()),
+        positions(color_stops.size()),
+        has_alpha(false) {
+    for (size_t i = 0; i < color_stops.size(); ++i) {
+      if (color_stops[i].color.a() < 1.0f) {
+        has_alpha = true;
+      }
+
+      colors[i] = ToSkColor(color_stops[i].color);
+      positions[i] = color_stops[i].position;
+    }
+  }
+
+  std::size_t size() const {
+    DCHECK(colors.size() == positions.size());
+    return colors.size();
+  }
+
+  std::vector<SkColor> colors;
+  std::vector<SkScalar> positions;
+  bool has_alpha;
+};
+
+}  // namespace skia
+}  // namespace rasterizer
+}  // namespace renderer
+}  // namespace cobalt
+
+#endif  // COBALT_RENDERER_RASTERIZER_SKIA_CONVERSIONS_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 1c55dc0..c3e2a0d 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -47,7 +47,7 @@
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options);
+              const Options& options);
 
   render_tree::ResourceProvider* GetResourceProvider();
 
@@ -191,7 +191,8 @@
 
 void HardwareRasterizer::Impl::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   backend::RenderTargetEGL* render_target_egl =
@@ -226,8 +227,9 @@
 
   // Get a SkCanvas that outputs to our hardware render target.
   SkCanvas* canvas = sk_output_surface_->getCanvas();
+  canvas->save();
 
-  if (options & Rasterizer::kSubmitOptions_Clear) {
+  if (options.flags & Rasterizer::kSubmitFlags_Clear) {
     canvas->clear(SkColorSetARGB(0, 0, 0, 0));
   }
 
@@ -253,6 +255,7 @@
   }
 
   graphics_context_->SwapBuffers(render_target_egl);
+  canvas->restore();
 }
 
 render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
@@ -321,7 +324,8 @@
 
 void HardwareRasterizer::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
-    const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+    const scoped_refptr<backend::RenderTarget>& render_target,
+    const Options& options) {
   TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
   TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::Submit()");
   impl_->Submit(render_tree, render_target, options);
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
index 8807fc3..af2b78e 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
@@ -59,7 +59,7 @@
   // into the constructor.
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options) OVERRIDE;
+              const Options& options) OVERRIDE;
 
   render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
 
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 964fad6..5ab5536 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
@@ -16,7 +16,11 @@
 
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
 
+#if defined(STARBOARD)
+#include "starboard/file.h"
+#else
 #include <sys/stat.h>
+#endif
 #include <cmath>
 
 #include "base/at_exit.h"
@@ -247,8 +251,13 @@
     SkString path_name(SkOSPath::Join(base_path, font_file.file_name.c_str()));
 
     // Sanity check that something exists at this location.
+#if defined(STARBOARD)
+    bool is_font_file_found = SbFileExists(path_name.c_str());
+#else
     struct stat status;
-    if (0 != stat(path_name.c_str(), &status)) {
+    bool is_font_file_found = (stat(path_name.c_str(), &status) == 0);
+#endif  // defined(STARBOARD)
+    if (!is_font_file_found) {
       LOG(ERROR) << "Failed to find font file: " << path_name.c_str();
       continue;
     }
diff --git a/src/cobalt/renderer/rasterizer/stub/rasterizer.h b/src/cobalt/renderer/rasterizer/stub/rasterizer.h
index 2c4f5bf..ff69528 100644
--- a/src/cobalt/renderer/rasterizer/stub/rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/stub/rasterizer.h
@@ -31,7 +31,7 @@
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
-              int options) OVERRIDE {}
+              const Options& options) OVERRIDE {}
 
   render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
 
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png
deleted file mode 100644
index b8638eb..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png
deleted file mode 100644
index 2076087..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc
index 6e6fa75..717ab06 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.cc
+++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -311,8 +311,9 @@
 scoped_array<uint8_t> RenderTreePixelTester::RasterizeRenderTree(
     const scoped_refptr<cobalt::render_tree::Node>& tree) const {
   // Rasterize the test render tree to the rasterizer's offscreen render target.
-  rasterizer_->Submit(tree, test_surface_,
-                      rasterizer::Rasterizer::kSubmitOptions_Clear);
+  rasterizer::Rasterizer::Options rasterizer_options;
+  rasterizer_options.flags = rasterizer::Rasterizer::kSubmitFlags_Clear;
+  rasterizer_->Submit(tree, test_surface_, rasterizer_options);
 
   // Load the texture's pixel data into a CPU memory buffer and return it.
   return graphics_context_->DownloadPixelDataAsRGBA(test_surface_);
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index 0acb744..623a7e5 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -20,7 +20,7 @@
 
 #include "base/logging.h"
 #include "cobalt/base/c_val.h"
-#include "cobalt/browser/web_module.h"
+#include "cobalt/browser/stack_size_constants.h"
 #include "cobalt/script/mozjs/mozjs_global_environment.h"
 #include "third_party/mozjs/cobalt_config/include/jscustomallocator.h"
 #include "third_party/mozjs/js/src/jsapi.h"
@@ -111,7 +111,7 @@
   // Setting three quarters of the web module stack size to ensure that native
   // stack won't exceed the stack size.
   JS_SetNativeStackQuota(runtime_,
-                         browser::WebModule::kWebModuleStackSize / 4 * 3);
+                         cobalt::browser::kWebModuleStackSize / 4 * 3);
 
   JS_SetRuntimePrivate(runtime_, this);
 
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.cc b/src/cobalt/script/mozjs/mozjs_exception_state.cc
index 9b2a5ec..39a611a 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.cc
@@ -44,6 +44,8 @@
     case kURIError:
       return JSEXN_URIERR;
   }
+  NOTREACHED();
+  return JSEXN_ERR;
 }
 
 // JSErrorCallback.
diff --git a/src/cobalt/speech/chunked_byte_buffer.cc b/src/cobalt/speech/chunked_byte_buffer.cc
deleted file mode 100644
index 43772df..0000000
--- a/src/cobalt/speech/chunked_byte_buffer.cc
+++ /dev/null
@@ -1,136 +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/speech/chunked_byte_buffer.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-
-namespace {
-
-static const size_t kHeaderLength = sizeof(uint32_t);
-
-COMPILE_ASSERT(sizeof(size_t) >= kHeaderLength,
-               chunked_byte_buffer_not_supported_on_this_architecture);
-
-uint32_t ReadBigEndian32(const uint8_t* buffer) {
-  return (static_cast<uint32_t>(buffer[3])) |
-         (static_cast<uint32_t>(buffer[2]) << 8) |
-         (static_cast<uint32_t>(buffer[1]) << 16) |
-         (static_cast<uint32_t>(buffer[0]) << 24);
-}
-
-}  // namespace
-
-namespace cobalt {
-namespace speech {
-
-ChunkedByteBuffer::ChunkedByteBuffer()
-    : partial_chunk_(new Chunk()),
-      total_bytes_stored_(0) {
-}
-
-ChunkedByteBuffer::~ChunkedByteBuffer() {
-  Clear();
-}
-
-void ChunkedByteBuffer::Append(const uint8_t* start, size_t length) {
-  size_t remaining_bytes = length;
-  const uint8_t* next_data = start;
-
-  while (remaining_bytes > 0) {
-    DCHECK(partial_chunk_ != NULL);
-    size_t insert_length = 0;
-    bool header_completed = false;
-    bool content_completed = false;
-    std::vector<uint8_t>* insert_target;
-
-    if (partial_chunk_->header.size() < kHeaderLength) {
-      const size_t bytes_to_complete_header =
-          kHeaderLength - partial_chunk_->header.size();
-      insert_length = std::min(bytes_to_complete_header, remaining_bytes);
-      insert_target = &partial_chunk_->header;
-      header_completed = (remaining_bytes >= bytes_to_complete_header);
-    } else {
-      DCHECK_LT(partial_chunk_->content->size(),
-                partial_chunk_->ExpectedContentLength());
-      const size_t bytes_to_complete_chunk =
-          partial_chunk_->ExpectedContentLength() -
-          partial_chunk_->content->size();
-      insert_length = std::min(bytes_to_complete_chunk, remaining_bytes);
-      insert_target = partial_chunk_->content.get();
-      content_completed = (remaining_bytes >= bytes_to_complete_chunk);
-    }
-
-    DCHECK_GT(insert_length, 0U);
-    DCHECK_LE(insert_length, remaining_bytes);
-    DCHECK_LE(next_data + insert_length, start + length);
-    insert_target->insert(insert_target->end(),
-                          next_data,
-                          next_data + insert_length);
-    next_data += insert_length;
-    remaining_bytes -= insert_length;
-
-    if (header_completed) {
-      DCHECK_EQ(partial_chunk_->header.size(), kHeaderLength);
-      if (partial_chunk_->ExpectedContentLength() == 0) {
-        // Handle zero-byte chunks.
-        chunks_.push_back(partial_chunk_.release());
-        partial_chunk_.reset(new Chunk());
-      } else {
-        partial_chunk_->content->reserve(
-            partial_chunk_->ExpectedContentLength());
-      }
-    } else if (content_completed) {
-      DCHECK_EQ(partial_chunk_->content->size(),
-                partial_chunk_->ExpectedContentLength());
-      chunks_.push_back(partial_chunk_.release());
-      partial_chunk_.reset(new Chunk());
-    }
-  }
-  DCHECK_EQ(next_data, start + length);
-  total_bytes_stored_ += length;
-}
-
-void ChunkedByteBuffer::Append(const std::string& string) {
-  Append(reinterpret_cast<const uint8_t*>(string.data()), string.size());
-}
-
-bool ChunkedByteBuffer::HasChunks() const {
-  return !chunks_.empty();
-}
-
-scoped_ptr<std::vector<uint8_t> > ChunkedByteBuffer::PopChunk() {
-  if (chunks_.empty())
-    return scoped_ptr<std::vector<uint8_t> >().Pass();
-  scoped_ptr<Chunk> chunk(*chunks_.begin());
-  chunks_.weak_erase(chunks_.begin());
-  DCHECK_EQ(chunk->header.size(), kHeaderLength);
-  DCHECK_EQ(chunk->content->size(), chunk->ExpectedContentLength());
-  total_bytes_stored_ -= chunk->content->size();
-  total_bytes_stored_ -= kHeaderLength;
-  return chunk->content.Pass();
-}
-
-void ChunkedByteBuffer::Clear() {
-  chunks_.clear();
-  partial_chunk_.reset(new Chunk());
-  total_bytes_stored_ = 0;
-}
-
-ChunkedByteBuffer::Chunk::Chunk() : content(new std::vector<uint8_t>()) {}
-
-ChunkedByteBuffer::Chunk::~Chunk() {
-}
-
-size_t ChunkedByteBuffer::Chunk::ExpectedContentLength() const {
-  DCHECK_EQ(header.size(), kHeaderLength);
-  return static_cast<size_t>(ReadBigEndian32(&header[0]));
-}
-
-}  // namespace speech
-}  // namespace cobalt
diff --git a/src/cobalt/speech/chunked_byte_buffer.h b/src/cobalt/speech/chunked_byte_buffer.h
deleted file mode 100644
index f69dec9..0000000
--- a/src/cobalt/speech/chunked_byte_buffer.h
+++ /dev/null
@@ -1,75 +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_SPEECH_CHUNKED_BYTE_BUFFER_H_
-#define COBALT_SPEECH_CHUNKED_BYTE_BUFFER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-
-namespace cobalt {
-namespace speech {
-
-// Models a chunk-oriented byte buffer. The term chunk is herein defined as an
-// arbitrary sequence of bytes that is preceeded by N header bytes, indicating
-// its size. Data may be appended to the buffer with no particular respect of
-// chunks boundaries. However, chunks can be extracted (FIFO) only when their
-// content (according to their header) is fully available in the buffer.
-// The current implementation support only 4 byte Big Endian headers.
-// Empty chunks (i.e. the sequence 00 00 00 00) are NOT allowed.
-//
-// E.g. 00 00 00 04 xx xx xx xx 00 00 00 02 yy yy 00 00 00 04 zz zz zz zz
-//      [----- CHUNK 1 -------] [--- CHUNK 2 ---] [------ CHUNK 3 ------]
-class ChunkedByteBuffer {
- public:
-  ChunkedByteBuffer();
-  ~ChunkedByteBuffer();
-
-  // Appends |length| bytes starting from |start| to the buffer.
-  void Append(const uint8_t* start, size_t length);
-
-  // Appends bytes contained in the |string| to the buffer.
-  void Append(const std::string& string);
-
-  // Checks whether one or more complete chunks are available in the buffer.
-  bool HasChunks() const;
-
-  // If enough data is available, reads and removes the first complete chunk
-  // from the buffer. Returns a NULL pointer if no complete chunk is available.
-  scoped_ptr<std::vector<uint8_t> > PopChunk();
-
-  // Clears all the content of the buffer.
-  void Clear();
-
-  // Returns the number of raw bytes (including headers) present.
-  size_t GetTotalLength() const { return total_bytes_stored_; }
-
- private:
-  struct Chunk {
-    Chunk();
-    ~Chunk();
-
-    std::vector<uint8_t> header;
-    scoped_ptr<std::vector<uint8_t> > content;
-    size_t ExpectedContentLength() const;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Chunk);
-  };
-
-  ScopedVector<Chunk> chunks_;
-  scoped_ptr<Chunk> partial_chunk_;
-  size_t total_bytes_stored_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChunkedByteBuffer);
-};
-
-}  // namespace speech
-}  // namespace cobalt
-
-#endif  // COBALT_SPEECH_CHUNKED_BYTE_BUFFER_H_
diff --git a/src/cobalt/speech/chunked_byte_buffer_unittest.cc b/src/cobalt/speech/chunked_byte_buffer_unittest.cc
deleted file mode 100644
index 0db0acd..0000000
--- a/src/cobalt/speech/chunked_byte_buffer_unittest.cc
+++ /dev/null
@@ -1,76 +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 <string>
-#include <vector>
-
-#include "cobalt/speech/chunked_byte_buffer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cobalt {
-namespace speech {
-
-typedef std::vector<uint8_t> ByteVector;
-
-TEST(ChunkedByteBufferTest, BasicTest) {
-  ChunkedByteBuffer buffer;
-
-  const uint8_t kChunks[] = {
-      0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04,  // Chunk 1: 4 bytes
-      0x00, 0x00, 0x00, 0x02, 0x05, 0x06,              // Chunk 2: 2 bytes
-      0x00, 0x00, 0x00, 0x01, 0x07                     // Chunk 3: 1 bytes
-  };
-
-  EXPECT_EQ(0U, buffer.GetTotalLength());
-  EXPECT_FALSE(buffer.HasChunks());
-
-  // Append partially chunk 1.
-  buffer.Append(kChunks, 2);
-  EXPECT_EQ(2U, buffer.GetTotalLength());
-  EXPECT_FALSE(buffer.HasChunks());
-
-  // Complete chunk 1.
-  buffer.Append(kChunks + 2, 6);
-  EXPECT_EQ(8U, buffer.GetTotalLength());
-  EXPECT_TRUE(buffer.HasChunks());
-
-  // Append fully chunk 2.
-  buffer.Append(kChunks + 8, 6);
-  EXPECT_EQ(14U, buffer.GetTotalLength());
-  EXPECT_TRUE(buffer.HasChunks());
-
-  // Remove and check chunk 1.
-  scoped_ptr<ByteVector> chunk;
-  chunk = buffer.PopChunk().Pass();
-  EXPECT_TRUE(chunk != NULL);
-  EXPECT_EQ(4U, chunk->size());
-  EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 4, &(*chunk)[0],
-                                                  chunk->size()));
-  EXPECT_EQ(6U, buffer.GetTotalLength());
-  EXPECT_TRUE(buffer.HasChunks());
-
-  // Read and check chunk 2.
-  chunk = buffer.PopChunk().Pass();
-  EXPECT_TRUE(chunk != NULL);
-  EXPECT_EQ(2U, chunk->size());
-  EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 12, &(*chunk)[0],
-                                                  chunk->size()));
-  EXPECT_EQ(0U, buffer.GetTotalLength());
-  EXPECT_FALSE(buffer.HasChunks());
-
-  // Append fully chunk 3.
-  buffer.Append(kChunks + 14, 5);
-  EXPECT_EQ(5U, buffer.GetTotalLength());
-
-  // Remove and check chunk 3.
-  chunk = buffer.PopChunk().Pass();
-  EXPECT_TRUE(chunk != NULL);
-  EXPECT_EQ(1U, chunk->size());
-  EXPECT_EQ((*chunk)[0], kChunks[18]);
-  EXPECT_EQ(0U, buffer.GetTotalLength());
-  EXPECT_FALSE(buffer.HasChunks());
-}
-
-}  // namespace speech
-}  // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/endpointer.cc b/src/cobalt/speech/endpointer/endpointer.cc
deleted file mode 100644
index cabad1a..0000000
--- a/src/cobalt/speech/endpointer/endpointer.cc
+++ /dev/null
@@ -1,187 +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/speech/endpointer/endpointer.h"
-
-#include "base/time.h"
-
-using base::Time;
-
-namespace {
-// Only send |kFrameSize| of audio data to energy endpointer each time.
-// It should be smaller than the samples of audio bus which is passed in
-// |ProcessAudio|.
-const int kFrameSize = 160;
-}
-
-namespace cobalt {
-namespace speech {
-
-Endpointer::Endpointer(int sample_rate)
-    : speech_input_possibly_complete_silence_length_us_(-1),
-      speech_input_complete_silence_length_us_(-1),
-      audio_frame_time_us_(0),
-      sample_rate_(sample_rate),
-      frame_rate_(sample_rate / kFrameSize) {
-  Reset();
-
-  speech_input_minimum_length_us_ =
-      static_cast<int64_t>(1.7 * Time::kMicrosecondsPerSecond);
-  speech_input_complete_silence_length_us_ =
-      static_cast<int64_t>(0.5 * Time::kMicrosecondsPerSecond);
-  long_speech_input_complete_silence_length_us_ = -1;
-  long_speech_length_us_ = -1;
-  speech_input_possibly_complete_silence_length_us_ =
-      1 * Time::kMicrosecondsPerSecond;
-
-  // Set the default configuration for Push To Talk mode.
-  EnergyEndpointerParams ep_config;
-  ep_config.set_frame_period(1.0f / static_cast<float>(frame_rate_));
-  ep_config.set_frame_duration(1.0f / static_cast<float>(frame_rate_));
-  ep_config.set_endpoint_margin(0.2f);
-  ep_config.set_onset_window(0.15f);
-  ep_config.set_speech_on_window(0.4f);
-  ep_config.set_offset_window(0.15f);
-  ep_config.set_onset_detect_dur(0.09f);
-  ep_config.set_onset_confirm_dur(0.075f);
-  ep_config.set_on_maintain_dur(0.10f);
-  ep_config.set_offset_confirm_dur(0.12f);
-  ep_config.set_decision_threshold(1000.0f);
-  ep_config.set_min_decision_threshold(50.0f);
-  ep_config.set_fast_update_dur(0.2f);
-  ep_config.set_sample_rate(static_cast<float>(sample_rate));
-  ep_config.set_min_fundamental_frequency(57.143f);
-  ep_config.set_max_fundamental_frequency(400.0f);
-  ep_config.set_contamination_rejection_period(0.25f);
-  energy_endpointer_.Init(ep_config);
-}
-
-void Endpointer::Reset() {
-  old_ep_status_ = EP_PRE_SPEECH;
-  waiting_for_speech_possibly_complete_timeout_ = false;
-  waiting_for_speech_complete_timeout_ = false;
-  speech_previously_detected_ = false;
-  speech_input_complete_ = false;
-  audio_frame_time_us_ = 0;  // Reset time for packets sent to endpointer.
-  speech_end_time_us_ = -1;
-  speech_start_time_us_ = -1;
-}
-
-void Endpointer::StartSession() {
-  Reset();
-  energy_endpointer_.StartSession();
-}
-
-void Endpointer::EndSession() {
-  energy_endpointer_.EndSession();
-}
-
-void Endpointer::SetEnvironmentEstimationMode() {
-  Reset();
-  energy_endpointer_.SetEnvironmentEstimationMode();
-}
-
-void Endpointer::SetUserInputMode() {
-  energy_endpointer_.SetUserInputMode();
-}
-
-EpStatus Endpointer::Status(int64_t* time) {
-  return energy_endpointer_.Status(time);
-}
-
-EpStatus Endpointer::ProcessAudio(
-    const ShellAudioBus& audio_bus, float* rms_out) {
-  DCHECK_EQ(audio_bus.channels(), 1);
-
-  const size_t num_samples = audio_bus.frames();
-  const int16_t* audio_data = NULL;
-
-  ShellAudioBus int16_audio_bus(1, num_samples, ShellAudioBus::kInt16,
-                                ShellAudioBus::kInterleaved);
-
-  if (audio_bus.sample_type() == ShellAudioBus::kFloat32) {
-    int16_audio_bus.Assign(audio_bus);
-    DCHECK_EQ(int16_audio_bus.sample_type(), ShellAudioBus::kInt16);
-    audio_data =
-        reinterpret_cast<const int16_t*>(int16_audio_bus.interleaved_data());
-  } else {
-    DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
-    audio_data =
-        reinterpret_cast<const int16_t*>(audio_bus.interleaved_data());
-  }
-
-  EpStatus ep_status = EP_PRE_SPEECH;
-
-  // Process the input data in blocks of kFrameSize, dropping any incomplete
-  // frames at the end (which is ok since typically the caller will be recording
-  // audio in multiples of our frame size).
-  int sample_index = 0;
-  while (static_cast<size_t>(sample_index + kFrameSize) <= num_samples) {
-    // Have the endpointer process the frame.
-    energy_endpointer_.ProcessAudioFrame(
-        audio_frame_time_us_, audio_data + sample_index, kFrameSize, rms_out);
-    sample_index += kFrameSize;
-    audio_frame_time_us_ +=
-        (kFrameSize * Time::kMicrosecondsPerSecond) / sample_rate_;
-
-    // Get the status of the endpointer.
-    int64_t ep_time;
-    ep_status = energy_endpointer_.Status(&ep_time);
-
-    // Handle state changes.
-    if ((EP_SPEECH_PRESENT == ep_status) &&
-        (EP_POSSIBLE_ONSET == old_ep_status_)) {
-      speech_end_time_us_ = -1;
-      waiting_for_speech_possibly_complete_timeout_ = false;
-      waiting_for_speech_complete_timeout_ = false;
-      // Trigger SpeechInputDidStart event on first detection.
-      if (false == speech_previously_detected_) {
-        speech_previously_detected_ = true;
-        speech_start_time_us_ = ep_time;
-      }
-    }
-    if ((EP_PRE_SPEECH == ep_status) &&
-        (EP_POSSIBLE_OFFSET == old_ep_status_)) {
-      speech_end_time_us_ = ep_time;
-      waiting_for_speech_possibly_complete_timeout_ = true;
-      waiting_for_speech_complete_timeout_ = true;
-    }
-    if (ep_time > speech_input_minimum_length_us_) {
-      // Speech possibly complete timeout.
-      if ((waiting_for_speech_possibly_complete_timeout_) &&
-          (ep_time - speech_end_time_us_ >
-              speech_input_possibly_complete_silence_length_us_)) {
-        waiting_for_speech_possibly_complete_timeout_ = false;
-      }
-      if (waiting_for_speech_complete_timeout_) {
-        // The length of the silence timeout period can be held constant, or it
-        // can be changed after a fixed amount of time from the beginning of
-        // speech.
-        bool has_stepped_silence =
-            (long_speech_length_us_ > 0) &&
-            (long_speech_input_complete_silence_length_us_ > 0);
-        int64_t requested_silence_length;
-        if (has_stepped_silence &&
-            (ep_time - speech_start_time_us_) > long_speech_length_us_) {
-          requested_silence_length =
-              long_speech_input_complete_silence_length_us_;
-        } else {
-          requested_silence_length =
-              speech_input_complete_silence_length_us_;
-        }
-
-        // Speech complete timeout.
-        if ((ep_time - speech_end_time_us_) > requested_silence_length) {
-          waiting_for_speech_complete_timeout_ = false;
-          speech_input_complete_ = true;
-        }
-      }
-    }
-    old_ep_status_ = ep_status;
-  }
-  return ep_status;
-}
-
-}  // namespace speech
-}  // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/endpointer.h b/src/cobalt/speech/endpointer/endpointer.h
deleted file mode 100644
index f6c818f..0000000
--- a/src/cobalt/speech/endpointer/endpointer.h
+++ /dev/null
@@ -1,159 +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_SPEECH_ENDPOINTER_ENDPOINTER_H_
-#define COBALT_SPEECH_ENDPOINTER_ENDPOINTER_H_
-
-#include <stdint.h>
-
-#include "cobalt/speech/endpointer/energy_endpointer.h"
-#include "media/base/shell_audio_bus.h"
-
-class EpStatus;
-
-namespace cobalt {
-namespace speech {
-
-// A simple interface to the underlying energy-endpointer implementation, this
-// class lets callers provide audio as being recorded and let them poll to find
-// when the user has stopped speaking.
-//
-// There are two events that may trigger the end of speech:
-//
-// speechInputPossiblyComplete event:
-//
-// Signals that silence/noise has  been detected for a *short* amount of
-// time after some speech has been detected. It can be used for low latency
-// UI feedback. To disable it, set it to a large amount.
-//
-// speechInputComplete event:
-//
-// This event is intended to signal end of input and to stop recording.
-// The amount of time to wait after speech is set by
-// speech_input_complete_silence_length_ and optionally two other
-// parameters (see below).
-// This time can be held constant, or can change as more speech is detected.
-// In the latter case, the time changes after a set amount of time from the
-// *beginning* of speech.  This is motivated by the expectation that there
-// will be two distinct types of inputs: short search queries and longer
-// dictation style input.
-//
-// Three parameters are used to define the piecewise constant timeout function.
-// The timeout length is speech_input_complete_silence_length until
-// long_speech_length, when it changes to
-// long_speech_input_complete_silence_length.
-class Endpointer {
- public:
-  typedef ::media::ShellAudioBus ShellAudioBus;
-
-  explicit Endpointer(int sample_rate);
-
-  // Start the endpointer. This should be called at the beginning of a session.
-  void StartSession();
-
-  // Stop the endpointer.
-  void EndSession();
-
-  // Start environment estimation. Audio will be used for environment estimation
-  // i.e. noise level estimation.
-  void SetEnvironmentEstimationMode();
-
-  // Start user input. This should be called when the user indicates start of
-  // input, e.g. by pressing a button.
-  void SetUserInputMode();
-
-  // Process a segment of audio, which may be more than one frame.
-  // The status of the last frame will be returned.
-  EpStatus ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out);
-
-  // Get the status of the endpointer.
-  EpStatus Status(int64_t* time_us);
-
-  // Returns true if the endpointer detected reasonable audio levels above
-  // background noise which could be user speech, false if not.
-  bool DidStartReceivingSpeech() const {
-    return speech_previously_detected_;
-  }
-
-  bool IsEstimatingEnvironment() const {
-    return energy_endpointer_.estimating_environment();
-  }
-
-  void set_speech_input_complete_silence_length(int64_t time_us) {
-    speech_input_complete_silence_length_us_ = time_us;
-  }
-
-  void set_long_speech_input_complete_silence_length(int64_t time_us) {
-    long_speech_input_complete_silence_length_us_ = time_us;
-  }
-
-  void set_speech_input_possibly_complete_silence_length(int64_t time_us) {
-    speech_input_possibly_complete_silence_length_us_ = time_us;
-  }
-
-  void set_long_speech_length(int64_t time_us) {
-    long_speech_length_us_ = time_us;
-  }
-
-  bool speech_input_complete() const {
-    return speech_input_complete_;
-  }
-
-  int sample_rate() const { return sample_rate_; }
-
-  // RMS background noise level in dB.
-  float NoiseLevelDb() const { return energy_endpointer_.GetNoiseLevelDb(); }
-
- private:
-  // Reset internal states. Helper method common to initial input utterance
-  // and following input utternaces.
-  void Reset();
-
-  // Minimum allowable length of speech input.
-  int64_t speech_input_minimum_length_us_;
-
-  // The speechInputPossiblyComplete event signals that silence/noise has been
-  // detected for a *short* amount of time after some speech has been detected.
-  // This proporty specifies the time period.
-  int64_t speech_input_possibly_complete_silence_length_us_;
-
-  // The speechInputComplete event signals that silence/noise has been
-  // detected for a *long* amount of time after some speech has been detected.
-  // This property specifies the time period.
-  int64_t speech_input_complete_silence_length_us_;
-
-  // Same as above, this specifies the required silence period after speech
-  // detection. This period is used instead of
-  // speech_input_complete_silence_length_ when the utterance is longer than
-  // long_speech_length_. This parameter is optional.
-  int64_t long_speech_input_complete_silence_length_us_;
-
-  // The period of time after which the endpointer should consider
-  // long_speech_input_complete_silence_length_ as a valid silence period
-  // instead of speech_input_complete_silence_length_. This parameter is
-  // optional.
-  int64_t long_speech_length_us_;
-
-  // First speech onset time, used in determination of speech complete timeout.
-  int64_t speech_start_time_us_;
-
-  // Most recent end time, used in determination of speech complete timeout.
-  int64_t speech_end_time_us_;
-
-  int64_t audio_frame_time_us_;
-  EpStatus old_ep_status_;
-  bool waiting_for_speech_possibly_complete_timeout_;
-  bool waiting_for_speech_complete_timeout_;
-  bool speech_previously_detected_;
-  bool speech_input_complete_;
-  EnergyEndpointer energy_endpointer_;
-  int sample_rate_;
-  // 1 frame = (1 / frame_rate_) second of audio.
-  int frame_rate_;
-};
-
-}  // namespace speech
-}  // namespace cobalt
-
-#endif  // COBALT_SPEECH_ENDPOINTER_ENDPOINTER_H_
diff --git a/src/cobalt/speech/endpointer/endpointer_unittest.cc b/src/cobalt/speech/endpointer/endpointer_unittest.cc
deleted file mode 100644
index 29465d5..0000000
--- a/src/cobalt/speech/endpointer/endpointer_unittest.cc
+++ /dev/null
@@ -1,163 +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 <stdint.h>
-
-#include "cobalt/speech/endpointer/endpointer.h"
-#include "media/base/shell_audio_bus.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-const int kFrameRate = 50;  // 20 ms long frames for AMR encoding.
-const int kSampleRate = 8000;  // 8 k samples per second for AMR encoding.
-
-// At 8 sample per second a 20 ms frame is 160 samples, which corrsponds
-// to the AMR codec.
-const int kFrameSize = kSampleRate / kFrameRate;  // 160 samples.
-
-#if defined(OS_STARBOARD)
-SB_COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
-#else
-COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
-#endif  // defined(OS_STARBOARD)
-}  // namespace
-
-namespace cobalt {
-namespace speech {
-
-class FrameProcessor {
- public:
-  // Process a single frame of test audio samples.
-  virtual EpStatus ProcessFrame(int64_t time,
-                                int16_t* samples,
-                                int frame_size) = 0;
-};
-
-void RunEndpointerEventsTest(FrameProcessor* processor) {
-  int16_t samples[kFrameSize];
-
-  // We will create a white noise signal of 150 frames. The frames from 50 to
-  // 100 will have more power, and the endpointer should fire on those frames.
-  const int kNumFrames = 150;
-
-  // Create a random sequence of samples.
-  srand(1);
-  float gain = 0.0;
-  int64_t time = 0;
-  for (int frame_count = 0; frame_count < kNumFrames; ++frame_count) {
-    // The frames from 50 to 100 will have more power, and the endpointer
-    // should detect those frames as speech.
-    if ((frame_count >= 50) && (frame_count < 100)) {
-      gain = 2000.0;
-    } else {
-      gain = 1.0;
-    }
-    // Create random samples.
-    for (int i = 0; i < kFrameSize; ++i) {
-      float randNum = static_cast<float>(rand() - (RAND_MAX / 2)) /
-          static_cast<float>(RAND_MAX);
-      samples[i] = static_cast<int16_t>(gain * randNum);
-    }
-
-    EpStatus ep_status = processor->ProcessFrame(time, samples, kFrameSize);
-    time += static_cast<int64_t>(kFrameSize * (1e6 / kSampleRate));
-
-    // Log the status.
-    if (20 == frame_count)
-      EXPECT_EQ(EP_PRE_SPEECH, ep_status);
-    if (70 == frame_count)
-      EXPECT_EQ(EP_SPEECH_PRESENT, ep_status);
-    if (120 == frame_count)
-      EXPECT_EQ(EP_PRE_SPEECH, ep_status);
-  }
-}
-
-// This test instantiates and initializes a stand alone endpointer module.
-// The test creates FrameData objects with random noise and send them
-// to the endointer module. The energy of the first 50 frames is low,
-// followed by 500 high energy frames, and another 50 low energy frames.
-// We test that the correct start and end frames were detected.
-class EnergyEndpointerFrameProcessor : public FrameProcessor {
- public:
-  explicit EnergyEndpointerFrameProcessor(EnergyEndpointer* endpointer)
-      : endpointer_(endpointer) {}
-
-  EpStatus ProcessFrame(int64_t time,
-                        int16_t* samples,
-                        int /*frame_size*/) override {
-    endpointer_->ProcessAudioFrame(time, samples, kFrameSize, NULL);
-
-    int64_t ep_time;
-    return endpointer_->Status(&ep_time);
-  }
-
- private:
-  EnergyEndpointer* endpointer_;
-};
-
-TEST(EndpointerTest, TestEnergyEndpointerEvents) {
-  // Initialize endpointer and configure it. We specify the parameters
-  // here for a 20ms window, and a 20ms step size, which corrsponds to
-  // the narrow band AMR codec.
-  EnergyEndpointerParams ep_config;
-  ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
-  ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
-  ep_config.set_endpoint_margin(0.2f);
-  ep_config.set_onset_window(0.15f);
-  ep_config.set_speech_on_window(0.4f);
-  ep_config.set_offset_window(0.15f);
-  ep_config.set_onset_detect_dur(0.09f);
-  ep_config.set_onset_confirm_dur(0.075f);
-  ep_config.set_on_maintain_dur(0.10f);
-  ep_config.set_offset_confirm_dur(0.12f);
-  ep_config.set_decision_threshold(100.0f);
-  EnergyEndpointer endpointer;
-  endpointer.Init(ep_config);
-
-  endpointer.StartSession();
-
-  EnergyEndpointerFrameProcessor frame_processor(&endpointer);
-  RunEndpointerEventsTest(&frame_processor);
-
-  endpointer.EndSession();
-}
-
-// Test endpointer wrapper class.
-class EndpointerFrameProcessor : public FrameProcessor {
- public:
-  typedef ::media::ShellAudioBus ShellAudioBus;
-  explicit EndpointerFrameProcessor(Endpointer* endpointer)
-      : endpointer_(endpointer) {}
-
-  EpStatus ProcessFrame(int64_t /*time*/,
-                        int16_t* samples,
-                        int /*frame_size*/) override {
-    scoped_ptr<ShellAudioBus> frame(new ShellAudioBus(1, kFrameSize, samples));
-    endpointer_->ProcessAudio(*frame, NULL);
-
-    int64_t ep_time;
-    return endpointer_->Status(&ep_time);
-  }
-
- private:
-  Endpointer* endpointer_;
-};
-
-TEST(EndpointerTest, TestEmbeddedEndpointerEvents) {
-  Endpointer endpointer(kSampleRate);
-  const int64_t kMillisecondsPerMicrosecond = 1000;
-  const int64_t short_timeout = 300 * kMillisecondsPerMicrosecond;
-  endpointer.set_speech_input_possibly_complete_silence_length(short_timeout);
-  const int64_t long_timeout = 500 * kMillisecondsPerMicrosecond;
-  endpointer.set_speech_input_complete_silence_length(long_timeout);
-  endpointer.StartSession();
-
-  EndpointerFrameProcessor frame_processor(&endpointer);
-  RunEndpointerEventsTest(&frame_processor);
-
-  endpointer.EndSession();
-}
-
-}  // namespace speech
-}  // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/energy_endpointer.cc b/src/cobalt/speech/endpointer/energy_endpointer.cc
deleted file mode 100644
index bd7ca80..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer.cc
+++ /dev/null
@@ -1,379 +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/speech/endpointer/energy_endpointer.h"
-
-#include <math.h>
-#include <stddef.h>
-
-#include "base/logging.h"
-
-namespace {
-
-// Returns the RMS (quadratic mean) of the input signal.
-float RMS(const int16_t* samples, int num_samples) {
-  int64_t ssq_int64 = 0;
-  int64_t sum_int64 = 0;
-  for (int i = 0; i < num_samples; ++i) {
-    sum_int64 += samples[i];
-    ssq_int64 += samples[i] * samples[i];
-  }
-  // now convert to floats.
-  double sum = static_cast<double>(sum_int64);
-  sum /= num_samples;
-  double ssq = static_cast<double>(ssq_int64);
-  return static_cast<float>(sqrt((ssq / num_samples) - (sum * sum)));
-}
-
-int64_t Secs2Usecs(float seconds) {
-  return static_cast<int64_t>(0.5 + (1.0e6 * seconds));
-}
-
-float GetDecibel(float value) {
-  if (value > 1.0e-100)
-    return static_cast<float>(20 * log10(value));
-  return -2000.0;
-}
-
-}  // namespace
-
-namespace cobalt {
-namespace speech {
-
-// Stores threshold-crossing histories for making decisions about the speech
-// state.
-class EnergyEndpointer::HistoryRing {
- public:
-  HistoryRing() : insertion_index_(0) {}
-
-  // Resets the ring to |size| elements each with state |initial_state|
-  void SetRing(int size, bool initial_state);
-
-  // Inserts a new entry into the ring and drops the oldest entry.
-  void Insert(int64_t time_us, bool decision);
-
-  // Returns the time in microseconds of the most recently added entry.
-  int64_t EndTime() const;
-
-  // Returns the sum of all intervals during which 'decision' is true within
-  // the time in seconds specified by 'duration'. The returned interval is
-  // in seconds.
-  float RingSum(float duration_sec);
-
- private:
-  struct DecisionPoint {
-    int64_t time_us;
-    bool decision;
-  };
-
-  std::vector<DecisionPoint> decision_points_;
-  int insertion_index_;  // Index at which the next item gets added/inserted.
-
-  DISALLOW_COPY_AND_ASSIGN(HistoryRing);
-};
-
-void EnergyEndpointer::HistoryRing::SetRing(int size, bool initial_state) {
-  insertion_index_ = 0;
-  decision_points_.clear();
-  DecisionPoint init = { -1, initial_state };
-  decision_points_.resize(static_cast<size_t>(size), init);
-}
-
-void EnergyEndpointer::HistoryRing::Insert(int64_t time_us, bool decision) {
-  decision_points_[static_cast<size_t>(insertion_index_)].time_us = time_us;
-  decision_points_[static_cast<size_t>(insertion_index_)].decision = decision;
-  insertion_index_ =
-      static_cast<int>((insertion_index_ + 1) % decision_points_.size());
-}
-
-int64_t EnergyEndpointer::HistoryRing::EndTime() const {
-  int ind = insertion_index_ - 1;
-  if (ind < 0)
-    ind = static_cast<int>(decision_points_.size() - 1);
-  return decision_points_[static_cast<size_t>(ind)].time_us;
-}
-
-float EnergyEndpointer::HistoryRing::RingSum(float duration_sec) {
-  if (decision_points_.empty())
-    return 0.0;
-
-  int64_t sum_us = 0;
-  int ind = insertion_index_ - 1;
-  if (ind < 0)
-    ind = static_cast<int>(decision_points_.size() - 1);
-  int64_t end_us = decision_points_[static_cast<size_t>(ind)].time_us;
-  bool is_on = decision_points_[static_cast<size_t>(ind)].decision;
-  int64_t start_us =
-      end_us - static_cast<int64_t>(0.5 + (1.0e6 * duration_sec));
-  if (start_us < 0)
-    start_us = 0;
-  size_t n_summed = 1;  // n points ==> (n-1) intervals
-  while ((decision_points_[static_cast<size_t>(ind)].time_us > start_us) &&
-         (n_summed < decision_points_.size())) {
-    --ind;
-    if (ind < 0)
-      ind = static_cast<int>(decision_points_.size() - 1);
-    if (is_on)
-      sum_us += end_us - decision_points_[static_cast<size_t>(ind)].time_us;
-    is_on = decision_points_[static_cast<size_t>(ind)].decision;
-    end_us = decision_points_[static_cast<size_t>(ind)].time_us;
-    n_summed++;
-  }
-
-  return 1.0e-6f * sum_us;  //  Returns total time that was super threshold.
-}
-
-EnergyEndpointer::EnergyEndpointer()
-    : status_(EP_PRE_SPEECH),
-      offset_confirm_dur_sec_(0),
-      endpointer_time_us_(0),
-      fast_update_frames_(0),
-      frame_counter_(0),
-      max_window_dur_(4.0),
-      sample_rate_(0),
-      history_(new HistoryRing()),
-      decision_threshold_(0),
-      estimating_environment_(false),
-      noise_level_(0),
-      rms_adapt_(0),
-      start_lag_(0),
-      end_lag_(0),
-      user_input_start_time_us_(0) {
-}
-
-EnergyEndpointer::~EnergyEndpointer() {
-}
-
-int EnergyEndpointer::TimeToFrame(float time) const {
-  return static_cast<int32_t>(0.5 + (time / params_.frame_period()));
-}
-
-void EnergyEndpointer::Restart(bool reset_threshold) {
-  status_ = EP_PRE_SPEECH;
-  user_input_start_time_us_ = 0;
-
-  if (reset_threshold) {
-    decision_threshold_ = params_.decision_threshold();
-    rms_adapt_ = decision_threshold_;
-    noise_level_ = params_.decision_threshold() / 2.0f;
-    frame_counter_ = 0;  // Used for rapid initial update of levels.
-  }
-
-  // Set up the memories to hold the history windows.
-  history_->SetRing(TimeToFrame(max_window_dur_), false);
-
-  // Flag that indicates that current input should be used for
-  // estimating the environment. The user has not yet started input
-  // by e.g. pressed the push-to-talk button. By default, this is
-  // false for backward compatibility.
-  estimating_environment_ = false;
-}
-
-void EnergyEndpointer::Init(const EnergyEndpointerParams& params) {
-  params_ = params;
-
-  // Find the longest history interval to be used, and make the ring
-  // large enough to accommodate that number of frames.  NOTE: This
-  // depends upon ep_frame_period being set correctly in the factory
-  // that did this instantiation.
-  max_window_dur_ = params_.onset_window();
-  if (params_.speech_on_window() > max_window_dur_)
-    max_window_dur_ = params_.speech_on_window();
-  if (params_.offset_window() > max_window_dur_)
-    max_window_dur_ = params_.offset_window();
-  Restart(true);
-
-  offset_confirm_dur_sec_ = params_.offset_window() -
-                            params_.offset_confirm_dur();
-  if (offset_confirm_dur_sec_ < 0.0)
-    offset_confirm_dur_sec_ = 0.0;
-
-  user_input_start_time_us_ = 0;
-
-  // Flag that indicates that  current input should be used for
-  // estimating the environment. The user has not yet started input
-  // by e.g. pressed the push-to-talk button. By default, this is
-  // false for backward compatibility.
-  estimating_environment_ = false;
-  // The initial value of the noise and speech levels is inconsequential.
-  // The level of the first frame will overwrite these values.
-  noise_level_ = params_.decision_threshold() / 2.0f;
-  fast_update_frames_ =
-      static_cast<int64_t>(params_.fast_update_dur() / params_.frame_period());
-
-  frame_counter_ = 0;  // Used for rapid initial update of levels.
-
-  sample_rate_ = params_.sample_rate();
-  start_lag_ = static_cast<int>(sample_rate_ /
-                                params_.max_fundamental_frequency());
-  end_lag_ = static_cast<int>(sample_rate_ /
-                              params_.min_fundamental_frequency());
-}
-
-void EnergyEndpointer::StartSession() {
-  Restart(true);
-}
-
-void EnergyEndpointer::EndSession() {
-  status_ = EP_POST_SPEECH;
-}
-
-void EnergyEndpointer::SetEnvironmentEstimationMode() {
-  Restart(true);
-  estimating_environment_ = true;
-}
-
-void EnergyEndpointer::SetUserInputMode() {
-  estimating_environment_ = false;
-  user_input_start_time_us_ = endpointer_time_us_;
-}
-
-void EnergyEndpointer::ProcessAudioFrame(int64_t time_us,
-                                         const int16_t* samples,
-                                         int num_samples,
-                                         float* rms_out) {
-  endpointer_time_us_ = time_us;
-  float rms = RMS(samples, num_samples);
-
-  // Check that this is user input audio vs. pre-input adaptation audio.
-  // Input audio starts when the user indicates start of input, by e.g.
-  // pressing push-to-talk. Audio received prior to that is used to update
-  // noise and speech level estimates.
-  if (!estimating_environment_) {
-    bool decision = false;
-    if ((endpointer_time_us_ - user_input_start_time_us_) <
-        Secs2Usecs(params_.contamination_rejection_period())) {
-      decision = false;
-      DVLOG(1) << "decision: forced to false, time: " << endpointer_time_us_;
-    } else {
-      decision = (rms > decision_threshold_);
-    }
-
-    history_->Insert(endpointer_time_us_, decision);
-
-    switch (status_) {
-      case EP_PRE_SPEECH:
-        if (history_->RingSum(params_.onset_window()) >
-            params_.onset_detect_dur()) {
-          status_ = EP_POSSIBLE_ONSET;
-        }
-        break;
-
-      case EP_POSSIBLE_ONSET: {
-        float tsum = history_->RingSum(params_.onset_window());
-        if (tsum > params_.onset_confirm_dur()) {
-          status_ = EP_SPEECH_PRESENT;
-        } else {  // If signal is not maintained, drop back to pre-speech.
-          if (tsum <= params_.onset_detect_dur())
-            status_ = EP_PRE_SPEECH;
-        }
-        break;
-      }
-
-      case EP_SPEECH_PRESENT: {
-        // To induce hysteresis in the state residency, we allow a
-        // smaller residency time in the on_ring, than was required to
-        // enter the SPEECH_PERSENT state.
-        float on_time = history_->RingSum(params_.speech_on_window());
-        if (on_time < params_.on_maintain_dur())
-          status_ = EP_POSSIBLE_OFFSET;
-        break;
-      }
-
-      case EP_POSSIBLE_OFFSET:
-        if (history_->RingSum(params_.offset_window()) <=
-            offset_confirm_dur_sec_) {
-          // Note that this offset time may be beyond the end
-          // of the input buffer in a real-time system.  It will be up
-          // to the RecognizerSession to decide what to do.
-          status_ = EP_PRE_SPEECH;  // Automatically reset for next utterance.
-        } else {  // If speech picks up again we allow return to SPEECH_PRESENT.
-          if (history_->RingSum(params_.speech_on_window()) >=
-              params_.on_maintain_dur())
-            status_ = EP_SPEECH_PRESENT;
-        }
-        break;
-
-      case EP_POST_SPEECH:
-        // fall-through
-      default:
-        LOG(WARNING) << "Invalid case in switch: " << status_;
-        break;
-    }
-
-    // If this is a quiet, non-speech region, slowly adapt the detection
-    // threshold to be about 6dB above the average RMS.
-    if ((!decision) && (status_ == EP_PRE_SPEECH)) {
-      decision_threshold_ = (0.98f * decision_threshold_) + (0.02f * 2 * rms);
-      rms_adapt_ = decision_threshold_;
-    } else {
-      // If this is in a speech region, adapt the decision threshold to
-      // be about 10dB below the average RMS. If the noise level is high,
-      // the threshold is pushed up.
-      // Adaptation up to a higher level is 5 times faster than decay to
-      // a lower level.
-      if ((status_ == EP_SPEECH_PRESENT) && decision) {
-        if (rms_adapt_ > rms) {
-          rms_adapt_ = (0.99f * rms_adapt_) + (0.01f * rms);
-        } else {
-          rms_adapt_ = (0.95f * rms_adapt_) + (0.05f * rms);
-        }
-        float target_threshold = 0.3f * rms_adapt_ +  noise_level_;
-        decision_threshold_ = (.90f * decision_threshold_) +
-                              (0.10f * target_threshold);
-      }
-    }
-
-    // Set a floor
-    if (decision_threshold_ < params_.min_decision_threshold())
-      decision_threshold_ = params_.min_decision_threshold();
-  }
-
-  // Update speech and noise levels.
-  UpdateLevels(rms);
-  ++frame_counter_;
-
-  if (rms_out)
-    *rms_out = GetDecibel(rms);
-}
-
-float EnergyEndpointer::GetNoiseLevelDb() const {
-  return GetDecibel(noise_level_);
-}
-
-void EnergyEndpointer::UpdateLevels(float rms) {
-  // Update quickly initially. We assume this is noise and that
-  // speech is 6dB above the noise.
-  if (frame_counter_ < fast_update_frames_) {
-    // Alpha increases from 0 to (k-1)/k where k is the number of time
-    // steps in the initial adaptation period.
-    float alpha = static_cast<float>(frame_counter_) /
-        static_cast<float>(fast_update_frames_);
-    noise_level_ = (alpha * noise_level_) + ((1 - alpha) * rms);
-    DVLOG(1) << "FAST UPDATE, frame_counter_ " << frame_counter_
-             << ", fast_update_frames_ " << fast_update_frames_;
-  } else {
-    // Update Noise level. The noise level adapts quickly downward, but
-    // slowly upward. The noise_level_ parameter is not currently used
-    // for threshold adaptation. It is used for UI feedback.
-    if (noise_level_ < rms)
-      noise_level_ = (0.999f * noise_level_) + (0.001f * rms);
-    else
-      noise_level_ = (0.95f * noise_level_) + (0.05f * rms);
-  }
-  if (estimating_environment_ || (frame_counter_ < fast_update_frames_)) {
-    decision_threshold_ = noise_level_ * 2;  // 6dB above noise level.
-    // Set a floor
-    if (decision_threshold_ < params_.min_decision_threshold())
-      decision_threshold_ = params_.min_decision_threshold();
-  }
-}
-
-EpStatus EnergyEndpointer::Status(int64_t* status_time) const {
-  *status_time = history_->EndTime();
-  return status_;
-}
-
-}  // namespace speech
-}  // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/energy_endpointer.h b/src/cobalt/speech/endpointer/energy_endpointer.h
deleted file mode 100644
index d3822ea..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer.h
+++ /dev/null
@@ -1,162 +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.
-
-// The EnergyEndpointer class finds likely speech onset and offset points.
-//
-// The implementation described here is about the simplest possible.
-// It is based on timings of threshold crossings for overall signal
-// RMS. It is suitable for light weight applications.
-//
-// As written, the basic idea is that one specifies intervals that
-// must be occupied by super- and sub-threshold energy levels, and
-// defers decisions re onset and offset times until these
-// specifications have been met.  Three basic intervals are tested: an
-// onset window, a speech-on window, and an offset window.  We require
-// super-threshold to exceed some mimimum total durations in the onset
-// and speech-on windows before declaring the speech onset time, and
-// we specify a required sub-threshold residency in the offset window
-// before declaring speech offset. As the various residency requirements are
-// met, the EnergyEndpointer instance assumes various states, and can return the
-// ID of these states to the client (see EpStatus below).
-//
-// The levels of the speech and background noise are continuously updated. It is
-// important that the background noise level be estimated initially for
-// robustness in noisy conditions. The first frames are assumed to be background
-// noise and a fast update rate is used for the noise level. The duration for
-// fast update is controlled by the fast_update_dur_ paramter.
-//
-// If used in noisy conditions, the endpointer should be started and run in the
-// EnvironmentEstimation mode, for at least 200ms, before switching to
-// UserInputMode.
-// Audio feedback contamination can appear in the input audio, if not cut
-// out or handled by echo cancellation. Audio feedback can trigger a false
-// accept. The false accepts can be ignored by setting
-// ep_contamination_rejection_period.
-
-#ifndef COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
-#define COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "cobalt/speech/endpointer/energy_endpointer_params.h"
-
-namespace cobalt {
-namespace speech {
-
-// Endpointer status codes
-enum EpStatus {
-  EP_PRE_SPEECH = 10,
-  EP_POSSIBLE_ONSET,
-  EP_SPEECH_PRESENT,
-  EP_POSSIBLE_OFFSET,
-  EP_POST_SPEECH,
-};
-
-class EnergyEndpointer {
- public:
-  // The default construction MUST be followed by Init(), before any
-  // other use can be made of the instance.
-  EnergyEndpointer();
-  virtual ~EnergyEndpointer();
-
-  void Init(const EnergyEndpointerParams& params);
-
-  // Start the endpointer. This should be called at the beginning of a session.
-  void StartSession();
-
-  // Stop the endpointer.
-  void EndSession();
-
-  // Start environment estimation. Audio will be used for environment estimation
-  // i.e. noise level estimation.
-  void SetEnvironmentEstimationMode();
-
-  // Start user input. This should be called when the user indicates start of
-  // input, e.g. by pressing a button.
-  void SetUserInputMode();
-
-  // Computes the next input frame and modifies EnergyEndpointer status as
-  // appropriate based on the computation.
-  void ProcessAudioFrame(int64_t time_us,
-                         const int16_t* samples,
-                         int num_samples,
-                         float* rms_out);
-
-  // Returns the current state of the EnergyEndpointer and the time
-  // corresponding to the most recently computed frame.
-  EpStatus Status(int64_t* status_time_us) const;
-
-  bool estimating_environment() const {
-    return estimating_environment_;
-  }
-
-  // Returns estimated noise level in dB.
-  float GetNoiseLevelDb() const;
-
- private:
-  class HistoryRing;
-
-  // Resets the endpointer internal state.  If reset_threshold is true, the
-  // state will be reset completely, including adaptive thresholds and the
-  // removal of all history information.
-  void Restart(bool reset_threshold);
-
-  // Update internal speech and noise levels.
-  void UpdateLevels(float rms);
-
-  // Returns the number of frames (or frame number) corresponding to
-  // the 'time' (in seconds).
-  int TimeToFrame(float time) const;
-
-  EpStatus status_;  // The current state of this instance.
-  float offset_confirm_dur_sec_;  // max on time allowed to confirm POST_SPEECH
-  int64_t
-      endpointer_time_us_;  // Time of the most recently received audio frame.
-  int64_t
-      fast_update_frames_;  // Number of frames for initial level adaptation.
-  int64_t
-      frame_counter_;     // Number of frames seen. Used for initial adaptation.
-  float max_window_dur_;  // Largest search window size (seconds)
-  float sample_rate_;  // Sampling rate.
-
-  // Ring buffers to hold the speech activity history.
-  scoped_ptr<HistoryRing> history_;
-
-  // Configuration parameters.
-  EnergyEndpointerParams params_;
-
-  // RMS which must be exceeded to conclude frame is speech.
-  float decision_threshold_;
-
-  // Flag to indicate that audio should be used to estimate environment, prior
-  // to receiving user input.
-  bool estimating_environment_;
-
-  // Estimate of the background noise level. Used externally for UI feedback.
-  float noise_level_;
-
-  // An adaptive threshold used to update decision_threshold_ when appropriate.
-  float rms_adapt_;
-
-  // Start lag corresponds to the highest fundamental frequency.
-  int start_lag_;
-
-  // End lag corresponds to the lowest fundamental frequency.
-  int end_lag_;
-
-  // Time when mode switched from environment estimation to user input. This
-  // is used to time forced rejection of audio feedback contamination.
-  int64_t user_input_start_time_us_;
-
-  DISALLOW_COPY_AND_ASSIGN(EnergyEndpointer);
-};
-
-}  // namespace speech
-}  // namespace cobalt
-
-#endif  // COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
diff --git a/src/cobalt/speech/endpointer/energy_endpointer_params.cc b/src/cobalt/speech/endpointer/energy_endpointer_params.cc
deleted file mode 100644
index c71e11c..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer_params.cc
+++ /dev/null
@@ -1,55 +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/speech/endpointer/energy_endpointer_params.h"
-
-namespace cobalt {
-namespace speech {
-
-EnergyEndpointerParams::EnergyEndpointerParams() {
-  SetDefaults();
-}
-
-void EnergyEndpointerParams::SetDefaults() {
-  frame_period_ = 0.01f;
-  frame_duration_ = 0.01f;
-  endpoint_margin_ = 0.2f;
-  onset_window_ = 0.15f;
-  speech_on_window_ = 0.4f;
-  offset_window_ = 0.15f;
-  onset_detect_dur_ = 0.09f;
-  onset_confirm_dur_ = 0.075f;
-  on_maintain_dur_ = 0.10f;
-  offset_confirm_dur_ = 0.12f;
-  decision_threshold_ = 150.0f;
-  min_decision_threshold_ = 50.0f;
-  fast_update_dur_ = 0.2f;
-  sample_rate_ = 8000.0f;
-  min_fundamental_frequency_ = 57.143f;
-  max_fundamental_frequency_ = 400.0f;
-  contamination_rejection_period_ = 0.25f;
-}
-
-void EnergyEndpointerParams::operator=(const EnergyEndpointerParams& source) {
-  frame_period_ = source.frame_period();
-  frame_duration_ = source.frame_duration();
-  endpoint_margin_ = source.endpoint_margin();
-  onset_window_ = source.onset_window();
-  speech_on_window_ = source.speech_on_window();
-  offset_window_ = source.offset_window();
-  onset_detect_dur_ = source.onset_detect_dur();
-  onset_confirm_dur_ = source.onset_confirm_dur();
-  on_maintain_dur_ = source.on_maintain_dur();
-  offset_confirm_dur_ = source.offset_confirm_dur();
-  decision_threshold_ = source.decision_threshold();
-  min_decision_threshold_ = source.min_decision_threshold();
-  fast_update_dur_ = source.fast_update_dur();
-  sample_rate_ = source.sample_rate();
-  min_fundamental_frequency_ = source.min_fundamental_frequency();
-  max_fundamental_frequency_ = source.max_fundamental_frequency();
-  contamination_rejection_period_ = source.contamination_rejection_period();
-}
-
-}  // namespace speech
-}  // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/energy_endpointer_params.h b/src/cobalt/speech/endpointer/energy_endpointer_params.h
deleted file mode 100644
index 06810d0..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer_params.h
+++ /dev/null
@@ -1,137 +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_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
-#define COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
-
-namespace cobalt {
-namespace speech {
-
-// Input parameters for the EnergyEndpointer class.
-class EnergyEndpointerParams {
- public:
-  EnergyEndpointerParams();
-
-  void SetDefaults();
-
-  void operator=(const EnergyEndpointerParams& source);
-
-  // Accessors and mutators
-  float frame_period() const { return frame_period_; }
-  void set_frame_period(float frame_period) {
-    frame_period_ = frame_period;
-  }
-
-  float frame_duration() const { return frame_duration_; }
-  void set_frame_duration(float frame_duration) {
-    frame_duration_ = frame_duration;
-  }
-
-  float endpoint_margin() const { return endpoint_margin_; }
-  void set_endpoint_margin(float endpoint_margin) {
-    endpoint_margin_ = endpoint_margin;
-  }
-
-  float onset_window() const { return onset_window_; }
-  void set_onset_window(float onset_window) { onset_window_ = onset_window; }
-
-  float speech_on_window() const { return speech_on_window_; }
-  void set_speech_on_window(float speech_on_window) {
-    speech_on_window_ = speech_on_window;
-  }
-
-  float offset_window() const { return offset_window_; }
-  void set_offset_window(float offset_window) {
-    offset_window_ = offset_window;
-  }
-
-  float onset_detect_dur() const { return onset_detect_dur_; }
-  void set_onset_detect_dur(float onset_detect_dur) {
-    onset_detect_dur_ = onset_detect_dur;
-  }
-
-  float onset_confirm_dur() const { return onset_confirm_dur_; }
-  void set_onset_confirm_dur(float onset_confirm_dur) {
-    onset_confirm_dur_ = onset_confirm_dur;
-  }
-
-  float on_maintain_dur() const { return on_maintain_dur_; }
-  void set_on_maintain_dur(float on_maintain_dur) {
-    on_maintain_dur_ = on_maintain_dur;
-  }
-
-  float offset_confirm_dur() const { return offset_confirm_dur_; }
-  void set_offset_confirm_dur(float offset_confirm_dur) {
-    offset_confirm_dur_ = offset_confirm_dur;
-  }
-
-  float decision_threshold() const { return decision_threshold_; }
-  void set_decision_threshold(float decision_threshold) {
-    decision_threshold_ = decision_threshold;
-  }
-
-  float min_decision_threshold() const { return min_decision_threshold_; }
-  void set_min_decision_threshold(float min_decision_threshold) {
-    min_decision_threshold_ = min_decision_threshold;
-  }
-
-  float fast_update_dur() const { return fast_update_dur_; }
-  void set_fast_update_dur(float fast_update_dur) {
-    fast_update_dur_ = fast_update_dur;
-  }
-
-  float sample_rate() const { return sample_rate_; }
-  void set_sample_rate(float sample_rate) { sample_rate_ = sample_rate; }
-
-  float min_fundamental_frequency() const { return min_fundamental_frequency_; }
-  void set_min_fundamental_frequency(float min_fundamental_frequency) {
-    min_fundamental_frequency_ = min_fundamental_frequency;
-  }
-
-  float max_fundamental_frequency() const { return max_fundamental_frequency_; }
-  void set_max_fundamental_frequency(float max_fundamental_frequency) {
-    max_fundamental_frequency_ = max_fundamental_frequency;
-  }
-
-  float contamination_rejection_period() const {
-    return contamination_rejection_period_;
-  }
-  void set_contamination_rejection_period(
-      float contamination_rejection_period) {
-    contamination_rejection_period_ = contamination_rejection_period;
-  }
-
- private:
-  float frame_period_;  // Frame period
-  float frame_duration_;  // Window size
-  float onset_window_;  // Interval scanned for onset activity
-  float speech_on_window_;  // Inverval scanned for ongoing speech
-  float offset_window_;  // Interval scanned for offset evidence
-  float offset_confirm_dur_;  // Silence duration required to confirm offset
-  float decision_threshold_;  // Initial rms detection threshold
-  float min_decision_threshold_;  // Minimum rms detection threshold
-  float fast_update_dur_;  // Period for initial estimation of levels.
-  float sample_rate_;  // Expected sample rate.
-
-  // Time to add on either side of endpoint threshold crossings
-  float endpoint_margin_;
-  // Total dur within onset_window required to enter ONSET state
-  float onset_detect_dur_;
-  // Total on time within onset_window required to enter SPEECH_ON state
-  float onset_confirm_dur_;
-  // Minimum dur in SPEECH_ON state required to maintain ON state
-  float on_maintain_dur_;
-  // Minimum fundamental frequency for autocorrelation.
-  float min_fundamental_frequency_;
-  // Maximum fundamental frequency for autocorrelation.
-  float max_fundamental_frequency_;
-  // Period after start of user input that above threshold values are ignored.
-  // This is to reject audio feedback contamination.
-  float contamination_rejection_period_;
-};
-
-}  // namespace speech
-}  // namespace cobalt
-
-#endif  // COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
diff --git a/src/cobalt/speech/endpointer_delegate.cc b/src/cobalt/speech/endpointer_delegate.cc
index a3d0444..f8a826f 100644
--- a/src/cobalt/speech/endpointer_delegate.cc
+++ b/src/cobalt/speech/endpointer_delegate.cc
@@ -63,8 +63,8 @@
 
   endpointer_.ProcessAudio(audio_bus, NULL);
   int64_t ep_time;
-  EpStatus status = endpointer_.Status(&ep_time);
-  if (status == EP_POSSIBLE_ONSET) {
+  content::EpStatus status = endpointer_.Status(&ep_time);
+  if (status == content::EP_POSSIBLE_ONSET) {
     is_first_time_sound_started_ = true;
   }
 
diff --git a/src/cobalt/speech/endpointer_delegate.h b/src/cobalt/speech/endpointer_delegate.h
index 8a3aafa..db6a528 100644
--- a/src/cobalt/speech/endpointer_delegate.h
+++ b/src/cobalt/speech/endpointer_delegate.h
@@ -17,7 +17,7 @@
 #ifndef COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
 #define COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
 
-#include "cobalt/speech/endpointer/endpointer.h"
+#include "content/browser/speech/endpointer/endpointer.h"
 #include "media/base/audio_bus.h"
 
 namespace cobalt {
@@ -42,7 +42,7 @@
 
  private:
   // Used for detecting sound start event.
-  Endpointer endpointer_;
+  content::Endpointer endpointer_;
   // Used for recording the number of samples before notifying that it is the
   // first time the sound started. The |endpointer_| should be started and run
   // in the EnvironmentEstimation mode if used in noisy conditions for at least
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index 5d8210d..ea3ba7d 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -166,13 +166,14 @@
   static int16_t samples[kBufferSizeInBytes / sizeof(int16_t)];
   int read_bytes =
       microphone_->Read(reinterpret_cast<char*>(samples), kBufferSizeInBytes);
-  if (read_bytes > 0) {
+  // If |read_bytes| is zero, nothing should happen.
+  if (read_bytes > 0 && read_bytes % sizeof(int16_t) == 0) {
     size_t frames = read_bytes / sizeof(int16_t);
     scoped_ptr<ShellAudioBus> output_audio_bus(new ShellAudioBus(
         1, frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
     output_audio_bus->Assign(ShellAudioBus(1, frames, samples));
     data_received_callback_.Run(output_audio_bus.Pass());
-  } else if (read_bytes < 0) {
+  } else if (read_bytes != 0) {
     state_ = kError;
     error_callback_.Run(new SpeechRecognitionError(
         SpeechRecognitionError::kAborted, "Microphone read failed."));
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index aa933d3..4d431dd 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -28,18 +28,8 @@
       'sources': [
         'audio_encoder_flac.cc',
         'audio_encoder_flac.h',
-        'chunked_byte_buffer.cc',
-        'chunked_byte_buffer.h',
         'endpointer_delegate.cc',
         'endpointer_delegate.h',
-
-        'endpointer/endpointer.cc',
-        'endpointer/endpointer.h',
-        'endpointer/energy_endpointer.cc',
-        'endpointer/energy_endpointer.h',
-        'endpointer/energy_endpointer_params.cc',
-        'endpointer/energy_endpointer_params.h',
-
         'google_streaming_api.pb.cc',
         'google_streaming_api.pb.h',
         'google_streaming_api.pb.proto',
@@ -68,6 +58,7 @@
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(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',
       ],
@@ -112,30 +103,5 @@
         ],
       ],
     },
-    {
-      'target_name': 'speech_test',
-      'type': '<(gtest_target_type)',
-      'sources': [
-        'chunked_byte_buffer_unittest.cc',
-        'endpointer/endpointer_unittest.cc',
-      ],
-      'dependencies': [
-        '<(DEPTH)/base/base.gyp:run_all_unittests',
-        '<(DEPTH)/cobalt/speech/speech.gyp:speech',
-        '<(DEPTH)/testing/gtest.gyp:gtest',
-      ],
-    },
-
-    {
-      'target_name': 'speech_test_deploy',
-      'type': 'none',
-      'dependencies': [
-        'speech_test',
-      ],
-      'variables': {
-        'executable_name': 'speech_test',
-      },
-      'includes': [ '../../starboard/build/deploy.gypi' ],
-    },
   ],
 }
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc
index 27eb43a..902fd48 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -166,14 +166,17 @@
 SpeechRecognizer::SpeechRecognizer(network::NetworkModule* network_module,
                                    const EventCallback& event_callback)
     : network_module_(network_module),
-      thread_("speech_recognizer"),
       started_(false),
-      event_callback_(event_callback) {
+      event_callback_(event_callback),
+      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,
@@ -236,7 +239,7 @@
 void SpeechRecognizer::OnURLFetchComplete(const net::URLFetcher* source) {
   DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
   UNREFERENCED_PARAMETER(source);
-  started_ = false;
+  // no-op.
 }
 
 void SpeechRecognizer::StartInternal(const SpeechRecognitionConfig& config,
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h
index 90e5dcd..55cd68f 100644
--- a/src/cobalt/speech/speech_recognizer.h
+++ b/src/cobalt/speech/speech_recognizer.h
@@ -23,9 +23,9 @@
 #include "base/threading/thread.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/speech/audio_encoder_flac.h"
-#include "cobalt/speech/chunked_byte_buffer.h"
 #include "cobalt/speech/speech_recognition_config.h"
 #include "cobalt/speech/speech_recognition_event.h"
+#include "content/browser/speech/chunked_byte_buffer.h"
 #include "media/base/audio_bus.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_fetcher_delegate.h"
@@ -77,8 +77,6 @@
 
   // This is used for creating fetchers.
   network::NetworkModule* network_module_;
-  // Speech recognizer is operating in its own thread.
-  base::Thread thread_;
   // Track the start/stop state of speech recognizer.
   bool started_;
 
@@ -90,9 +88,11 @@
   scoped_ptr<net::URLFetcher> downstream_fetcher_;
   EventCallback event_callback_;
   // Used for processing proto buffer data.
-  ChunkedByteBuffer chunked_byte_buffer_;
+  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
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc
index decba88..79a5c8f 100644
--- a/src/cobalt/storage/storage_manager.cc
+++ b/src/cobalt/storage/storage_manager.cc
@@ -275,11 +275,9 @@
                sizeof(VirtualFileSystem::SerializedHeader));
       }
 
-      if (VirtualFileSystem::GetHeaderVersion(header) == -1) {
+      if (!vfs_->Deserialize(&raw_bytes[0], buffer_size)) {
         VirtualFile* vf = vfs_->Open(kDefaultSaveFile);
         vf->Write(&raw_bytes[0], buffer_size, 0 /* offset */);
-      } else {
-        vfs_->Deserialize(&raw_bytes[0], buffer_size);
       }
     }
   }
diff --git a/src/cobalt/storage/virtual_file.cc b/src/cobalt/storage/virtual_file.cc
index ee738da..64904c2 100644
--- a/src/cobalt/storage/virtual_file.cc
+++ b/src/cobalt/storage/virtual_file.cc
@@ -27,60 +27,74 @@
 // We must use forward declarations to be able to specify the
 // WARN_UNUSED_RESULT attribute.
 
-// Read size bytes from buffer into dest. Return # of bytes read.
-int ReadBuffer(uint8* dest, const uint8* buffer, int size) WARN_UNUSED_RESULT;
+// Read size bytes from buffer into dest. Updates buffer and buffer_remaining
+// on success.
+// Returns true on success, false on buffer overrun
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+                size_t* buffer_remaining) WARN_UNUSED_RESULT;
 
 // Write size bytes from source into buffer. Return # of bytes written.
-int WriteBuffer(uint8* buffer, const uint8* source, int size,
-                bool dry_run) WARN_UNUSED_RESULT;
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+                   bool dry_run) WARN_UNUSED_RESULT;
 
-int ReadBuffer(uint8* dest, const uint8* buffer, int size) {
-  memcpy(dest, buffer, static_cast<size_t>(size));
-  return size;
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+                size_t* buffer_remaining) {
+  if (size > *buffer_remaining) {
+    return false;
+  }
+  memcpy(dest, *buffer, size);
+  *buffer_remaining -= size;
+  *buffer += size;
+  return true;
 }
 
-int WriteBuffer(uint8* buffer, const uint8* source, int size, bool dry_run) {
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+                   bool dry_run) {
   if (!dry_run) {
-    memcpy(buffer, source, static_cast<size_t>(size));
+    memcpy(buffer, source, size);
   }
   return size;
 }
 
 }  // namespace
 
-VirtualFile::VirtualFile(const std::string& name) : size_(0), name_(name) {}
+VirtualFile::VirtualFile(const std::string& name) : name_(name) {}
 VirtualFile::~VirtualFile() {}
 
-int VirtualFile::Read(void* dest, int bytes, int offset) const {
-  DCHECK_GE(offset, 0);
-  if (offset > size_) {
+int VirtualFile::Read(void* dest, int bytes_in, int offset_in) const {
+  DCHECK_GE(offset_in, 0);
+  DCHECK_GE(bytes_in, 0);
+  size_t offset = static_cast<size_t>(offset_in);
+  size_t bytes = static_cast<size_t>(bytes_in);
+  size_t size = buffer_.size();
+  if (offset > size) {
     return 0;
   }
-  int bytes_to_read = std::min(size_ - offset, bytes);
+  size_t bytes_to_read = std::min(static_cast<size_t>(size - offset), bytes);
   if (bytes_to_read == 0) {
     return 0;
   }
-  memcpy(dest, &buffer_[static_cast<size_t>(offset)],
-         static_cast<size_t>(bytes_to_read));
-  return bytes_to_read;
+  memcpy(dest, &buffer_[offset], bytes_to_read);
+  return static_cast<int>(bytes_to_read);
 }
 
-int VirtualFile::Write(const void* source, int bytes, int offset) {
-  DCHECK_GE(offset, 0);
-  DCHECK_GE(bytes, 0);
-  if (static_cast<int>(buffer_.size()) < offset + bytes) {
-    buffer_.resize(static_cast<size_t>(offset + bytes));
+int VirtualFile::Write(const void* source, int bytes_in, int offset_in) {
+  DCHECK_GE(offset_in, 0);
+  DCHECK_GE(bytes_in, 0);
+  size_t bytes = static_cast<size_t>(bytes_in);
+  size_t offset = static_cast<size_t>(offset_in);
+  if (buffer_.size() < offset + bytes) {
+    buffer_.resize(offset + bytes);
   }
 
-  memcpy(&buffer_[static_cast<size_t>(offset)], source,
-         static_cast<size_t>(bytes));
-  size_ = std::max(size_, offset + bytes);
-  return bytes;
+  memcpy(&buffer_[offset], source, bytes);
+  return bytes_in;
 }
 
 int VirtualFile::Truncate(int size) {
-  size_ = std::min(size_, size);
-  return size_;
+  size_t newsize = std::min(buffer_.size(), static_cast<size_t>(size));
+  buffer_.resize(newsize);
+  return static_cast<int>(newsize);
 }
 
 int VirtualFile::Serialize(uint8* dest, bool dry_run) {
@@ -96,29 +110,36 @@
   // Write the filename
   cur_buffer +=
       WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(name_.c_str()),
-                  static_cast<int>(name_length), dry_run);
+                  static_cast<size_t>(name_length), dry_run);
 
   // NOTE: Ensure the file size is 64-bit for compatibility
   // with any existing serialized files.
-  uint64 file_size = static_cast<uint64>(size_);
+  uint64 file_size = static_cast<uint64>(buffer_.size());
   // Write the file contents size
   cur_buffer +=
       WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&file_size),
                   sizeof(file_size), dry_run);
 
   // Write the file contents
-  cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], size_, dry_run);
+  if (!buffer_.empty()) {
+    // std::vector does not define access to underlying array when empty
+    cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], buffer_.size(), dry_run);
+  }
 
   // Return the number of bytes written
   return static_cast<int>(std::distance(dest, cur_buffer));
 }
 
-int VirtualFile::Deserialize(const uint8* source) {
+int VirtualFile::Deserialize(const uint8* source, size_t buffer_remaining) {
   // Read in filename length
   const uint8* cur_buffer = source;
   uint64 name_length;
-  cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(&name_length), cur_buffer,
-                           sizeof(name_length));
+  bool success = ReadBuffer(reinterpret_cast<uint8*>(&name_length), &cur_buffer,
+                            sizeof(name_length), &buffer_remaining);
+  if (!success) {
+    DLOG(ERROR) << "Buffer overrun";
+    return -1;
+  }
 
   if (name_length >= kMaxVfsPathname) {
     DLOG(ERROR) << "Filename was longer than the maximum allowed.";
@@ -126,19 +147,34 @@
   }
   // Read in filename
   char name[kMaxVfsPathname];
-  cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(name), cur_buffer,
-                           static_cast<int>(name_length));
+  success = ReadBuffer(reinterpret_cast<uint8*>(name), &cur_buffer,
+                       static_cast<size_t>(name_length), &buffer_remaining);
+  if (!success) {
+    DLOG(ERROR) << "Buffer overrun";
+    return -1;
+  }
   name_.assign(name, name_length);
 
   // Read in file contents size.
   uint64 file_size;
-  cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(&file_size), cur_buffer,
-                           sizeof(file_size));
-  size_ = static_cast<int>(file_size);
-
+  success = ReadBuffer(reinterpret_cast<uint8*>(&file_size), &cur_buffer,
+                       sizeof(file_size), &buffer_remaining);
+  if (!success) {
+    DLOG(ERROR) << "Buffer overrun";
+    return -1;
+  }
   // Read in the file contents
-  buffer_.resize(static_cast<size_t>(size_));
-  cur_buffer += ReadBuffer(&buffer_[0], cur_buffer, size_);
+  buffer_.resize(static_cast<size_t>(file_size));
+
+  if (!buffer_.empty()) {
+    // std::vector does not define access to underlying array when empty
+    success =
+        ReadBuffer(&buffer_[0], &cur_buffer, buffer_.size(), &buffer_remaining);
+    if (!success) {
+      DLOG(ERROR) << "Buffer overrun";
+      return -1;
+    }
+  }
 
   // Return the number of bytes read
   return static_cast<int>(std::distance(source, cur_buffer));
diff --git a/src/cobalt/storage/virtual_file.h b/src/cobalt/storage/virtual_file.h
index 5741fd0..f5a9aec 100644
--- a/src/cobalt/storage/virtual_file.h
+++ b/src/cobalt/storage/virtual_file.h
@@ -41,7 +41,7 @@
   int Read(void* dest, int bytes, int offset) const;
   int Write(const void* source, int bytes, int offset);
   int Truncate(int size);
-  int Size() const { return size_; }
+  int Size() const { return static_cast<int>(buffer_.size()); }
 
  private:
   explicit VirtualFile(const std::string& name);
@@ -49,10 +49,12 @@
 
   // Returns the number of bytes written
   int Serialize(uint8* dest, const bool dry_run);
-  int Deserialize(const uint8* source);
+  // Deserializes a file, returning the size of the file or
+  // < 0 on error.
+  // |buffer_remaining| is the maximum size of |source|
+  int Deserialize(const uint8* source, size_t buffer_remaining);
 
   std::vector<uint8> buffer_;
-  int size_;
 
   std::string name_;
 
diff --git a/src/cobalt/storage/virtual_file_system.cc b/src/cobalt/storage/virtual_file_system.cc
index 70e1556..9951df6 100644
--- a/src/cobalt/storage/virtual_file_system.cc
+++ b/src/cobalt/storage/virtual_file_system.cc
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #include "cobalt/storage/virtual_file_system.h"
 
 #include "base/logging.h"
@@ -124,23 +123,27 @@
   return bytes_written;
 }
 
-void VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size) {
+bool VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size_in) {
+  const uint8* caller_buffer = buffer;
+
   base::AutoLock lock(file_table_lock_);
   ClearFileTable();
 
-  if (buffer_size < 0) {
-    DLOG(ERROR) << "Buffer size must be positive: "
-                << buffer_size << " < 0.";
-    return;
+  if (buffer_size_in < 0) {
+    DLOG(ERROR) << "Buffer size must be positive: " << buffer_size_in
+                << " < 0.";
+    return false;
   }
 
+  size_t buffer_size = static_cast<size_t>(buffer_size_in);
+
   // The size of the buffer must be checked before copying the beginning of it
   // into a SerializedHeader.
-  if (static_cast<size_t>(buffer_size) < sizeof(SerializedHeader)) {
+  if (buffer_size < sizeof(SerializedHeader)) {
     DLOG(ERROR) << "Buffer size " << buffer_size
                 << " is too small to contain a SerializedHeader of size "
                 << sizeof(SerializedHeader) << "; operation aborted.";
-    return;
+    return false;
   }
 
   // Read in expected number of files
@@ -152,23 +155,36 @@
   if (buffer_version != 0) {
     // Note: We would handle old versions here, if necessary.
     DLOG(ERROR) << "Attempted to load a different version; operation aborted.";
-    return;
-  } else if (header.file_size != buffer_size) {
+    return false;
+  } else if (static_cast<size_t>(header.file_size) != buffer_size) {
     DLOG(ERROR) << "Buffer size mismatch: " << header.file_size
                 << " != " << buffer_size;
-    return;
+    return false;
   }
 
   for (int i = 0; i < header.file_count; ++i) {
+    size_t buffer_used = static_cast<size_t>(buffer - caller_buffer);
+    if (buffer_size < buffer_used) {
+      DLOG(ERROR) << "Buffer overrun deserializing files";
+      ClearFileTable();
+      return false;
+    }
+    size_t buffer_remaining = buffer_size - buffer_used;
+
     VirtualFile* file = new VirtualFile("");
-    int bytes = file->Deserialize(buffer);
+
+    int bytes = file->Deserialize(buffer, buffer_remaining);
     if (bytes > 0) {
       buffer += bytes;
       table_[file->name_] = file;
     } else {
       DLOG(WARNING) << "Failed to deserialize virtual file system.";
+      delete file;
+      ClearFileTable();
+      return false;
     }
   }
+  return true;
 }
 
 void VirtualFileSystem::ClearFileTable() {
diff --git a/src/cobalt/storage/virtual_file_system.h b/src/cobalt/storage/virtual_file_system.h
index e0b69b0..bef9a4c 100644
--- a/src/cobalt/storage/virtual_file_system.h
+++ b/src/cobalt/storage/virtual_file_system.h
@@ -59,7 +59,8 @@
   int Serialize(uint8* buffer, bool dry_run);
 
   // Deserializes a file system from a memory buffer.
-  void Deserialize(const uint8* buffer, int buffer_size);
+  // Returns false on failure.
+  bool Deserialize(const uint8* buffer, int buffer_size);
 
   // Simple file open. Will create a file if it does not exist, and files are
   // always readable and writable.
diff --git a/src/cobalt/storage/virtual_file_system_test.cc b/src/cobalt/storage/virtual_file_system_test.cc
index e507516..de36f6f 100644
--- a/src/cobalt/storage/virtual_file_system_test.cc
+++ b/src/cobalt/storage/virtual_file_system_test.cc
@@ -21,6 +21,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#include "starboard/client_porting/poem/string_poem.h"
+
 namespace cobalt {
 namespace storage {
 
@@ -102,6 +104,32 @@
   EXPECT_EQ(4, file->Size());
 }
 
+TEST_F(VirtualFileSystemTest, Truncate) {
+  VirtualFile* file = vfs_.Open("file1.tmp");
+  ASSERT_TRUE(file);
+  EXPECT_EQ(0, file->Size());
+
+  const char* data = "test";
+  char file_contents[4];
+  // Write a few bytes of random data.
+  file->Write(data, 4, 0);
+  EXPECT_EQ(4, file->Size());
+  int bytes = file->Read(file_contents, sizeof(file_contents), 0);
+  EXPECT_EQ(4, bytes);
+  EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+  file->Truncate(3);
+  EXPECT_EQ(3, file->Size());
+  bytes = file->Read(file_contents, sizeof(file_contents), 0);
+  EXPECT_EQ(3, bytes);
+  EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+  file->Truncate(0);
+  EXPECT_EQ(0, file->Size());
+  bytes = file->Read(file_contents, sizeof(file_contents), 0);
+  EXPECT_EQ(0, bytes);
+}
+
 TEST_F(VirtualFileSystemTest, SerializeDeserialize) {
   // Create a few files and write some data
   VirtualFile* file = vfs_.Open("file1.tmp");
@@ -116,6 +144,58 @@
   int data2_size = 4;
   file->Write(data2, data2_size, 0);
 
+  file = vfs_.Open("file3.tmp");
+  EXPECT_TRUE(file != NULL);
+  const char data3[] = "";
+  int data3_size = 0;
+  file->Write(data3, data3_size, 0);
+
+  // First perform a dry run to figure out how much space we need.
+  int bytes = vfs_.Serialize(NULL, true /*dry run*/);
+  scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
+
+  // Now serialize and deserialize
+  vfs_.Serialize(buffer.get(), false /*dry run*/);
+
+  // Deserialize the data into a new vfs
+  VirtualFileSystem new_vfs;
+  ASSERT_TRUE(new_vfs.Deserialize(buffer.get(), bytes));
+
+  // Make sure the new vfs contains all the expected data.
+  char file_contents[VirtualFile::kMaxVfsPathname];
+  file = new_vfs.Open("file1.tmp");
+  EXPECT_TRUE(file != NULL);
+  bytes = file->Read(file_contents, sizeof(file_contents), 0);
+  EXPECT_EQ(data1_size, bytes);
+  EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+
+  file = new_vfs.Open("file2.tmp");
+  EXPECT_TRUE(file != NULL);
+  bytes = file->Read(file_contents, sizeof(file_contents), 0);
+  EXPECT_EQ(data2_size, bytes);
+  EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+
+  file = new_vfs.Open("file3.tmp");
+  EXPECT_TRUE(file != NULL);
+  bytes = file->Read(file_contents, sizeof(file_contents), 0);
+  EXPECT_EQ(data3_size, bytes);
+  EXPECT_EQ(0, memcmp(file_contents, data3, static_cast<size_t>(bytes)));
+}
+
+TEST_F(VirtualFileSystemTest, DeserializeTruncated) {
+  // Create a few files and write some data
+  VirtualFile* file = vfs_.Open("file1.tmp");
+  EXPECT_TRUE(file != NULL);
+  const char data1[] = "abc";
+  int data1_size = 3;
+  file->Write(data1, data1_size, 0);
+
+  file = vfs_.Open("file2.tmp");
+  EXPECT_TRUE(file != NULL);
+  const char data2[] = "defg";
+  int data2_size = 4;
+  file->Write(data2, data2_size, 0);
+
   // First perform a dry run to figure out how much space we need.
   int bytes = vfs_.Serialize(NULL, true /*dry run*/);
   scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
@@ -123,23 +203,39 @@
   // Now serialize and deserialize
   vfs_.Serialize(buffer.get(), false /*dry run*/);
 
-  // Deserialize the data into a new vfs
-  VirtualFileSystem new_vfs;
-  new_vfs.Deserialize(buffer.get(), bytes);
+  for (int i = 1; i < bytes; i++) {
+    // Corrupt the header
+    VirtualFileSystem::SerializedHeader header;
+    memcpy(&header, buffer.get(), sizeof(header));
+    header.file_size = header.file_size - i;
+    memcpy(buffer.get(), &header, sizeof(header));
 
-  // Make sure the new vfs contains all the expected data.
-  char file_contents[VirtualFile::kMaxVfsPathname];
-  file = new_vfs.Open("file1.tmp");
-  EXPECT_TRUE(file != NULL);
-  bytes = file->Read(file_contents, sizeof(file_contents), 0);
-  EXPECT_EQ(data1_size, bytes);
-  EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+    // Deserialize the data into a new vfs
+    VirtualFileSystem new_vfs;
+    EXPECT_FALSE(new_vfs.Deserialize(buffer.get(), bytes - i));
+  }
+}
 
-  file = new_vfs.Open("file2.tmp");
-  EXPECT_TRUE(file != NULL);
-  bytes = file->Read(file_contents, sizeof(file_contents), 0);
-  EXPECT_EQ(data2_size, bytes);
-  EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+TEST_F(VirtualFileSystemTest, DeserializeBadData) {
+  scoped_array<uint8> buffer(new uint8[0]);
+  EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), 0));
+  EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), -1));
+  buffer.reset(new uint8[1]);
+  EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), 1));
+  buffer.reset(new uint8[sizeof(VirtualFileSystem::SerializedHeader)]);
+  VirtualFileSystem::SerializedHeader header = {};
+  header.version = -1;
+  memcpy(buffer.get(), &header, sizeof(header));
+  EXPECT_EQ(false,
+            vfs_.Deserialize(buffer.get(),
+                             sizeof(VirtualFileSystem::SerializedHeader)));
+  memcpy(&(header.version), "SAV0", sizeof(header.version));
+  header.file_size = sizeof(VirtualFileSystem::SerializedHeader);
+  memcpy(buffer.get(), &header, sizeof(header));
+  EXPECT_EQ(true,
+            vfs_.Deserialize(buffer.get(),
+                             sizeof(VirtualFileSystem::SerializedHeader)));
+  ASSERT_EQ(0, vfs_.ListFiles().size());
 }
 
 TEST_F(VirtualFileSystemTest, GetHeaderVersion) {
diff --git a/src/cobalt/webdriver/element_driver.cc b/src/cobalt/webdriver/element_driver.cc
index 6d05638..950581a 100644
--- a/src/cobalt/webdriver/element_driver.cc
+++ b/src/cobalt/webdriver/element_driver.cc
@@ -105,34 +105,38 @@
   Keyboard::TranslateToKeyEvents(keys.utf8_keys(), Keyboard::kReleaseModifiers,
                                  events.get());
   // Dispatch the keyboard events.
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       element_message_loop_,
       base::Bind(&ElementDriver::SendKeysInternal, base::Unretained(this),
-                 base::Passed(&events)));
+                 base::Passed(&events)),
+      protocol::Response::kStaleElementReference);
 }
 
 util::CommandResult<protocol::ElementId> ElementDriver::FindElement(
     const protocol::SearchStrategy& strategy) {
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       element_message_loop_,
       base::Bind(&ElementDriver::FindElementsInternal<protocol::ElementId>,
-                 base::Unretained(this), strategy));
+                 base::Unretained(this), strategy),
+      protocol::Response::kStaleElementReference);
 }
 
 util::CommandResult<std::vector<protocol::ElementId> >
 ElementDriver::FindElements(const protocol::SearchStrategy& strategy) {
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       element_message_loop_,
       base::Bind(&ElementDriver::FindElementsInternal<ElementIdVector>,
-                 base::Unretained(this), strategy));
+                 base::Unretained(this), strategy),
+      protocol::Response::kNoSuchElement);
 }
 
 util::CommandResult<bool> ElementDriver::Equals(
     const ElementDriver* other_element_driver) {
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       element_message_loop_,
       base::Bind(&ElementDriver::EqualsInternal, base::Unretained(this),
-                 other_element_driver));
+                 other_element_driver),
+      protocol::Response::kStaleElementReference);
 }
 
 util::CommandResult<base::optional<std::string> > ElementDriver::GetAttribute(
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index ab7b3eb..9c69788 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -176,11 +176,11 @@
 };
 }  // namespace
 
-WebDriverServer::WebDriverServer(int port,
+WebDriverServer::WebDriverServer(int port, const std::string& listen_ip,
                                  const HandleRequestCallback& callback)
     : handle_request_callback_(callback) {
   // Create http server
-  factory_.reset(new net::TCPListenSocketFactory("0.0.0.0", port));
+  factory_.reset(new net::TCPListenSocketFactory(listen_ip, port));
   server_ = new net::HttpServer(*factory_, this);
   LOG(INFO) << "Starting WebDriver server on port " << port;
 }
diff --git a/src/cobalt/webdriver/server.h b/src/cobalt/webdriver/server.h
index a35a947..b81008a 100644
--- a/src/cobalt/webdriver/server.h
+++ b/src/cobalt/webdriver/server.h
@@ -79,7 +79,8 @@
       HttpMethod, const std::string&, scoped_ptr<base::Value>,
       scoped_ptr<ResponseHandler>)> HandleRequestCallback;
 
-  WebDriverServer(int port, const HandleRequestCallback& callback);
+  WebDriverServer(int port, const std::string& listen_ip,
+                  const HandleRequestCallback& callback);
 
  protected:
   // net::HttpServer::Delegate implementation.
diff --git a/src/cobalt/webdriver/util/call_on_message_loop.h b/src/cobalt/webdriver/util/call_on_message_loop.h
index 8b28a82..f3af96f 100644
--- a/src/cobalt/webdriver/util/call_on_message_loop.h
+++ b/src/cobalt/webdriver/util/call_on_message_loop.h
@@ -35,26 +35,63 @@
   CallOnMessageLoopHelper(
       const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
       const CallbackType& callback)
-      : completed_event_(false, false) {
+      : completed_event_(true, false), success_(false) {
     DCHECK_NE(base::MessageLoopProxy::current(), message_loop_proxy);
+    scoped_ptr<DeletionSignaler> dt(new DeletionSignaler(&completed_event_));
+    // Note that while MessageLoopProxy::PostTask returns false
+    // after the message loop has gone away, it still can return true
+    // even if tasks are posted during shutdown and will never be run,
+    // so we ignore this return value.
     message_loop_proxy->PostTask(
-        FROM_HERE, base::Bind(&CallOnMessageLoopHelper::CallAndSignal,
-                              base::Unretained(this), callback));
+        FROM_HERE,
+        base::Bind(&CallOnMessageLoopHelper::Call, base::Unretained(this),
+                   callback, base::Passed(&dt)));
   }
 
-  ReturnValue WaitForResult() {
+  // Waits for result, filling |out| with the return value if successful.
+  // Returns true on success or false if the message loop went away
+  // before the task was executed.
+  bool WaitForResult(ReturnValue* out) {
     completed_event_.Wait();
-    return result_;
+    if (success_) {
+      *out = result_;
+    }
+    return success_;
+  }
+
+  ~CallOnMessageLoopHelper() {
+    // We must ensure that we've waited for completion otherwise
+    // DeletionSignaler will have a use-after-free.
+    completed_event_.Wait();
   }
 
  private:
-  void CallAndSignal(const CallbackType& callback) {
+  // DeletionSignaler signals an event when the destructor is called.
+  // This allows us to use the base::Passed mechanism to signal our
+  // completed_event_ both when Call() has been invoked and when
+  // the message loop has been deleted.
+  class DeletionSignaler {
+   public:
+    base::WaitableEvent* to_signal_;
+
+    explicit DeletionSignaler(base::WaitableEvent* to_signal)
+        : to_signal_(to_signal) {}
+
+    ~DeletionSignaler() { to_signal_->Signal(); }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(DeletionSignaler);
+  };
+
+  void Call(const CallbackType& callback,
+            scoped_ptr<DeletionSignaler> dt ALLOW_UNUSED) {
     result_ = callback.Run();
-    completed_event_.Signal();
+    success_ = true;
   }
 
   base::WaitableEvent completed_event_;
   ReturnValue result_;
+  bool success_;
 };
 
 // Used with CallWeakOnMessageLoop.
@@ -71,14 +108,41 @@
 }  // namespace internal
 
 // Call the base::Callback on the specified message loop and wait for it to
-// complete, then return the result of the callback.
+// complete. Returns true if successful, or false if the underlying
+// PostTask failed. This can happen if a WebModule shuts down due to a page
+// navigation.
+//
+// On success, |out| is set to the result.
 template <class ReturnValue>
-ReturnValue CallOnMessageLoop(
+bool TryCallOnMessageLoop(
     const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
-    const base::Callback<ReturnValue(void)>& callback) {
+    const base::Callback<ReturnValue(void)>& callback, ReturnValue* out) {
   internal::CallOnMessageLoopHelper<ReturnValue> call_helper(message_loop_proxy,
                                                              callback);
-  return call_helper.WaitForResult();
+  return call_helper.WaitForResult(out);
+}
+
+// Tries to call |callback| on messageloop |message_loop_proxy|,
+// retrying a few times if the WebModule thread appears to have gone
+// away (perhaps because of navigation). If failure persists,
+// returns a CommandResult of |window_disappeared_code|.
+template <typename ReturnValue>
+util::CommandResult<ReturnValue> CallOnMessageLoopWithRetry(
+    const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
+    const base::Callback<util::CommandResult<ReturnValue>(void)>& callback,
+    protocol::Response::StatusCode window_disappeared_code) {
+  util::CommandResult<ReturnValue> result;
+
+  const int kRetryAttempts = 5;
+  bool success = false;
+  for (int i = 0; !success && i < kRetryAttempts; i++) {
+    success = TryCallOnMessageLoop(message_loop_proxy, callback, &result);
+  }
+
+  if (!success) {
+    result = util::CommandResult<ReturnValue>(window_disappeared_code);
+  }
+  return result;
 }
 
 // Supports a common pattern in the various XXXDriver classes.
@@ -97,10 +161,11 @@
     protocol::Response::StatusCode no_such_object_code) {
   typedef util::CommandResult<ReturnValue> CommandResult;
   typedef base::optional<ReturnValue> InternalResult;
-  InternalResult result = util::CallOnMessageLoop(
+  InternalResult result;
+  bool success = util::TryCallOnMessageLoop(
       message_loop,
-      base::Bind(&internal::RunWeak<T, ReturnValue>, get_weak, cb));
-  if (result) {
+      base::Bind(&internal::RunWeak<T, ReturnValue>, get_weak, cb), &result);
+  if (success && result) {
     return CommandResult(result.value());
   } else {
     return CommandResult(no_such_object_code);
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index 3d510cb..9b4b9d0 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -159,8 +159,11 @@
 
 }  // namespace
 
+const char WebDriverModule::kDefaultListenIp[] = "0.0.0.0";
+
 WebDriverModule::WebDriverModule(
-    int server_port, const CreateSessionDriverCB& create_session_driver_cb,
+    int server_port, const std::string& listen_ip,
+    const CreateSessionDriverCB& create_session_driver_cb,
     const GetScreenshotFunction& get_screenshot_function,
     const SetProxyFunction& set_proxy_function,
     const base::Closure& shutdown_cb)
@@ -405,7 +408,7 @@
       base::Thread::Options(MessageLoop::TYPE_IO, 0));
   webdriver_thread_.message_loop()->PostTask(
       FROM_HERE, base::Bind(&WebDriverModule::StartServer,
-                            base::Unretained(this), server_port));
+                            base::Unretained(this), server_port, listen_ip));
 }
 
 WebDriverModule::~WebDriverModule() {
@@ -428,11 +431,12 @@
   }
 }
 
-void WebDriverModule::StartServer(int server_port) {
+void WebDriverModule::StartServer(int server_port,
+                                  const std::string& listen_ip) {
   DCHECK(thread_checker_.CalledOnValidThread());
   // Create a new WebDriverServer and pass in the Dispatcher.
   webdriver_server_.reset(new WebDriverServer(
-      server_port,
+      server_port, listen_ip,
       base::Bind(&WebDriverDispatcher::HandleWebDriverServerRequest,
                  base::Unretained(webdriver_dispatcher_.get()))));
 }
diff --git a/src/cobalt/webdriver/web_driver_module.h b/src/cobalt/webdriver/web_driver_module.h
index 2df30dd..b0c89ed 100644
--- a/src/cobalt/webdriver/web_driver_module.h
+++ b/src/cobalt/webdriver/web_driver_module.h
@@ -48,7 +48,10 @@
   typedef base::Callback<void(const ScreenshotCompleteCallback&)>
       GetScreenshotFunction;
   typedef base::Callback<void(const std::string&)> SetProxyFunction;
-  WebDriverModule(int server_port,
+  // Use this as the default listen_ip. It means "any interface on the local
+  // machine" eg INADDR_ANY.
+  static const char kDefaultListenIp[];
+  WebDriverModule(int server_port, const std::string& listen_ip,
                   const CreateSessionDriverCB& create_session_driver_cb,
                   const GetScreenshotFunction& get_screenshot_function,
                   const SetProxyFunction& set_proxy_function,
@@ -65,7 +68,7 @@
   void OnWindowRecreated();
 
  private:
-  void StartServer(int server_port);
+  void StartServer(int server_port, const std::string& listen_ip);
   void StopServer();
   void GetServerStatus(
       const base::Value* parameters,
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index 57b0467..30c1415 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -136,9 +136,12 @@
     // It's expected that the WebDriver thread is the only other thread to call
     // this function.
     DCHECK(thread_checker_.CalledOnValidThread());
-    return util::CallOnMessageLoop(window_message_loop_,
-        base::Bind(&WindowDriver::GetElementDriver, base::Unretained(this),
-                   element_id));
+    ElementDriver* result;
+    bool success = util::TryCallOnMessageLoop(
+        window_message_loop_, base::Bind(&WindowDriver::GetElementDriver,
+                                         base::Unretained(this), element_id),
+        &result);
+    return success ? result : NULL;
   }
   DCHECK_EQ(base::MessageLoopProxy::current(), window_message_loop_);
   ElementDriverMap::iterator it = element_drivers_.find(element_id.id());
@@ -159,9 +162,10 @@
 
 util::CommandResult<void> WindowDriver::Navigate(const GURL& url) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       window_message_loop_,
-      base::Bind(&WindowDriver::NavigateInternal, base::Unretained(this), url));
+      base::Bind(&WindowDriver::NavigateInternal, base::Unretained(this), url),
+      protocol::Response::kNoSuchWindow);
 }
 
 util::CommandResult<std::string> WindowDriver::GetCurrentUrl() {
@@ -186,17 +190,21 @@
     const protocol::SearchStrategy& strategy) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  return util::CallOnMessageLoop(window_message_loop_,
+  return util::CallOnMessageLoopWithRetry(
+      window_message_loop_,
       base::Bind(&WindowDriver::FindElementsInternal<protocol::ElementId>,
-                 base::Unretained(this), strategy));
+                 base::Unretained(this), strategy),
+      protocol::Response::kNoSuchElement);
 }
 
 util::CommandResult<std::vector<protocol::ElementId> >
 WindowDriver::FindElements(const protocol::SearchStrategy& strategy) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return util::CallOnMessageLoop(window_message_loop_,
+  return util::CallOnMessageLoopWithRetry(
+      window_message_loop_,
       base::Bind(&WindowDriver::FindElementsInternal<ElementIdVector>,
-                 base::Unretained(this), strategy));
+                 base::Unretained(this), strategy),
+      protocol::Response::kNoSuchElement);
 }
 
 util::CommandResult<std::string> WindowDriver::GetSource() {
@@ -218,10 +226,11 @@
 
   SyncExecuteResultHandler result_handler;
 
-  CommandResult result = util::CallOnMessageLoop(
+  CommandResult result = util::CallOnMessageLoopWithRetry(
       window_message_loop_,
       base::Bind(&WindowDriver::ExecuteScriptInternal, base::Unretained(this),
-                 script, base::nullopt, &result_handler));
+                 script, base::nullopt, &result_handler),
+      protocol::Response::kNoSuchWindow);
   if (result.is_success()) {
     return CommandResult(protocol::ScriptResult(result_handler.result()));
   } else {
@@ -240,10 +249,11 @@
   const base::TimeDelta kDefaultAsyncTimeout =
       base::TimeDelta::FromMilliseconds(0);
   AsyncExecuteResultHandler result_handler;
-  CommandResult result = util::CallOnMessageLoop(
+  CommandResult result = util::CallOnMessageLoopWithRetry(
       window_message_loop_,
       base::Bind(&WindowDriver::ExecuteScriptInternal, base::Unretained(this),
-                 script, kDefaultAsyncTimeout, &result_handler));
+                 script, kDefaultAsyncTimeout, &result_handler),
+      protocol::Response::kNoSuchWindow);
 
   if (!result.is_success()) {
     return result;
@@ -264,16 +274,18 @@
   Keyboard::TranslateToKeyEvents(keys.utf8_keys(), Keyboard::kKeepModifiers,
                                  events.get());
   // Dispatch the keyboard events.
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       window_message_loop_,
       base::Bind(&WindowDriver::SendKeysInternal, base::Unretained(this),
-                 base::Passed(&events)));
+                 base::Passed(&events)),
+      protocol::Response::kNoSuchWindow);
 }
 
 util::CommandResult<protocol::ElementId> WindowDriver::GetActiveElement() {
-  return util::CallOnMessageLoop(
+  return util::CallOnMessageLoopWithRetry(
       window_message_loop_, base::Bind(&WindowDriver::GetActiveElementInternal,
-                                       base::Unretained(this)));
+                                       base::Unretained(this)),
+      protocol::Response::kNoSuchWindow);
 }
 
 util::CommandResult<void> WindowDriver::SwitchFrame(
@@ -318,9 +330,10 @@
 util::CommandResult<void> WindowDriver::AddCookie(
     const protocol::Cookie& cookie) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return util::CallOnMessageLoop(window_message_loop_,
-                                 base::Bind(&WindowDriver::AddCookieInternal,
-                                            base::Unretained(this), cookie));
+  return util::CallOnMessageLoopWithRetry(
+      window_message_loop_, base::Bind(&WindowDriver::AddCookieInternal,
+                                       base::Unretained(this), cookie),
+      protocol::Response::kNoSuchWindow);
 }
 
 protocol::ElementId WindowDriver::ElementToId(
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
index cd03ee2..fbba36b 100755
--- a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
+++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -51,6 +51,8 @@
 
 STARTUP_TIMEOUT_SECONDS = 2 * 60
 
+WEBDRIVER_HTTP_TIMEOUT_SECS = 2 * 60
+
 COBALT_EXIT_TIMEOUT_SECONDS = 5
 
 COBALT_WEBDRIVER_CAPABILITIES = {
@@ -180,6 +182,7 @@
     url = "http://{}:{}/".format(self._GetIPAddress(), port)
     self.webdriver = self.selenium_webdriver_module.Remote(
         url, COBALT_WEBDRIVER_CAPABILITIES)
+    self.webdriver.command_executor.set_timeout(WEBDRIVER_HTTP_TIMEOUT_SECS)
     print("Selenium Connected\n", file=self.log_file)
     _webdriver = self.webdriver
     self.test_script_started.set()
@@ -262,11 +265,10 @@
                       args.log_file) as runner:
       unittest.main(testRunner=unittest.TextTestRunner(
           verbosity=0, stream=runner.log_file))
-    return 0
   except TimeoutException:
     print("Timeout waiting for Cobalt to start", file=sys.stderr)
-    return 1
+    sys.exit(1)
 
 
 if __name__ == "__main__":
-  sys.exit(main())
+  main()
diff --git a/src/cobalt/webdriver_benchmarks/tests/test_median.py b/src/cobalt/webdriver_benchmarks/tests/test_median.py
deleted file mode 100755
index f265b15..0000000
--- a/src/cobalt/webdriver_benchmarks/tests/test_median.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/python2
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ==============================================================================
-"""Tests the functionality median function."""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import os
-import sys
-# The parent directory is a module
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
-
-# pylint: disable=C6204,C6203
-from tv_testcase import _median as median
-import unittest
-
-
-class MedianTest(unittest.TestCase):
-
-  def test_empty_case(self):
-    self.assertEqual(median([]), None)
-
-  def test_one_item(self):
-    self.assertEqual(median([1]), 1)
-
-  def test_two_items(self):
-    self.assertAlmostEqual(median([4, 1]), 2.5)
-
-  def test_three_items(self):
-    self.assertAlmostEqual(median([4, -4, -2]), -2)
-
-  def test_four_items(self):
-    self.assertAlmostEqual(median([4, 0, 1, -2]), 0.5)
-
-
-if __name__ == '__main__':
-  sys.exit(unittest.main())
diff --git a/src/content/browser/speech/chunked_byte_buffer.cc b/src/content/browser/speech/chunked_byte_buffer.cc
index a43e40a..8ac9c92 100644
--- a/src/content/browser/speech/chunked_byte_buffer.cc
+++ b/src/content/browser/speech/chunked_byte_buffer.cc
@@ -7,15 +7,15 @@
 #include <algorithm>
 #include <utility>
 
-#include "base/lazy_instance.h"
+#include "base/basictypes.h"
 #include "base/logging.h"
 
 namespace {
 
 static const size_t kHeaderLength = sizeof(uint32_t);
 
-static_assert(sizeof(size_t) >= kHeaderLength,
-              "chunked byte buffer not supported on this architecture");
+COMPILE_ASSERT(sizeof(size_t) >= kHeaderLength,
+               chunked_byte_buffer_not_supported_on_this_architecture);
 
 uint32_t ReadBigEndian32(const uint8_t* buffer) {
   return (static_cast<uint32_t>(buffer[3])) |
@@ -103,16 +103,16 @@
   return !chunks_.empty();
 }
 
-std::unique_ptr<std::vector<uint8_t>> ChunkedByteBuffer::PopChunk() {
+scoped_ptr<std::vector<uint8_t> > ChunkedByteBuffer::PopChunk() {
   if (chunks_.empty())
-    return std::unique_ptr<std::vector<uint8_t>>();
-  std::unique_ptr<Chunk> chunk(*chunks_.begin());
+    return scoped_ptr<std::vector<uint8_t> >().Pass();
+  scoped_ptr<Chunk> chunk(*chunks_.begin());
   chunks_.weak_erase(chunks_.begin());
   DCHECK_EQ(chunk->header.size(), kHeaderLength);
   DCHECK_EQ(chunk->content->size(), chunk->ExpectedContentLength());
   total_bytes_stored_ -= chunk->content->size();
   total_bytes_stored_ -= kHeaderLength;
-  return std::move(chunk->content);
+  return chunk->content.Pass();
 }
 
 void ChunkedByteBuffer::Clear() {
diff --git a/src/content/browser/speech/chunked_byte_buffer.h b/src/content/browser/speech/chunked_byte_buffer.h
index 41e273d..483ddb9 100644
--- a/src/content/browser/speech/chunked_byte_buffer.h
+++ b/src/content/browser/speech/chunked_byte_buffer.h
@@ -5,16 +5,12 @@
 #ifndef CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
 #define CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
 
-#include <stddef.h>
-#include <stdint.h>
-
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
-#include "content/common/content_export.h"
 
 namespace content {
 
@@ -28,7 +24,7 @@
 //
 // E.g. 00 00 00 04 xx xx xx xx 00 00 00 02 yy yy 00 00 00 04 zz zz zz zz
 //      [----- CHUNK 1 -------] [--- CHUNK 2 ---] [------ CHUNK 3 ------]
-class CONTENT_EXPORT ChunkedByteBuffer {
+class ChunkedByteBuffer {
  public:
   ChunkedByteBuffer();
   ~ChunkedByteBuffer();
@@ -44,7 +40,7 @@
 
   // If enough data is available, reads and removes the first complete chunk
   // from the buffer. Returns a NULL pointer if no complete chunk is available.
-  std::unique_ptr<std::vector<uint8_t>> PopChunk();
+  scoped_ptr<std::vector<uint8_t> > PopChunk();
 
   // Clears all the content of the buffer.
   void Clear();
@@ -58,7 +54,7 @@
     ~Chunk();
 
     std::vector<uint8_t> header;
-    std::unique_ptr<std::vector<uint8_t>> content;
+    scoped_ptr<std::vector<uint8_t> > content;
     size_t ExpectedContentLength() const;
 
    private:
@@ -66,7 +62,7 @@
   };
 
   ScopedVector<Chunk> chunks_;
-  std::unique_ptr<Chunk> partial_chunk_;
+  scoped_ptr<Chunk> partial_chunk_;
   size_t total_bytes_stored_;
 
   DISALLOW_COPY_AND_ASSIGN(ChunkedByteBuffer);
diff --git a/src/content/browser/speech/chunked_byte_buffer_unittest.cc b/src/content/browser/speech/chunked_byte_buffer_unittest.cc
index d8a5cb2..9b7be0f 100644
--- a/src/content/browser/speech/chunked_byte_buffer_unittest.cc
+++ b/src/content/browser/speech/chunked_byte_buffer_unittest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <stdint.h>
-
 #include <string>
 #include <vector>
 
@@ -42,8 +40,8 @@
   EXPECT_TRUE(buffer.HasChunks());
 
   // Remove and check chunk 1.
-  std::unique_ptr<ByteVector> chunk;
-  chunk = buffer.PopChunk();
+  scoped_ptr<ByteVector> chunk;
+  chunk = buffer.PopChunk().Pass();
   EXPECT_TRUE(chunk != NULL);
   EXPECT_EQ(4U, chunk->size());
   EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 4, &(*chunk)[0],
@@ -52,7 +50,7 @@
   EXPECT_TRUE(buffer.HasChunks());
 
   // Read and check chunk 2.
-  chunk = buffer.PopChunk();
+  chunk = buffer.PopChunk().Pass();
   EXPECT_TRUE(chunk != NULL);
   EXPECT_EQ(2U, chunk->size());
   EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 12, &(*chunk)[0],
@@ -65,7 +63,7 @@
   EXPECT_EQ(5U, buffer.GetTotalLength());
 
   // Remove and check chunk 3.
-  chunk = buffer.PopChunk();
+  chunk = buffer.PopChunk().Pass();
   EXPECT_TRUE(chunk != NULL);
   EXPECT_EQ(1U, chunk->size());
   EXPECT_EQ((*chunk)[0], kChunks[18]);
diff --git a/src/content/browser/speech/endpointer/endpointer.cc b/src/content/browser/speech/endpointer/endpointer.cc
index 1758970..e6d9941 100644
--- a/src/content/browser/speech/endpointer/endpointer.cc
+++ b/src/content/browser/speech/endpointer/endpointer.cc
@@ -4,13 +4,15 @@
 
 #include "content/browser/speech/endpointer/endpointer.h"
 
-#include "base/time/time.h"
-#include "content/browser/speech/audio_buffer.h"
+#include "base/time.h"
 
 using base::Time;
 
 namespace {
-const int kFrameRate = 50;  // 1 frame = 20ms of audio.
+// Only send |kFrameSize| of audio data to energy endpointer each time.
+// It should be smaller than the samples of audio bus which is passed in
+// |ProcessAudio|.
+const int kFrameSize = 160;
 }
 
 namespace content {
@@ -20,11 +22,9 @@
       speech_input_complete_silence_length_us_(-1),
       audio_frame_time_us_(0),
       sample_rate_(sample_rate),
-      frame_size_(0) {
+      frame_rate_(sample_rate / kFrameSize) {
   Reset();
 
-  frame_size_ = static_cast<int>(sample_rate / static_cast<float>(kFrameRate));
-
   speech_input_minimum_length_us_ =
       static_cast<int64_t>(1.7 * Time::kMicrosecondsPerSecond);
   speech_input_complete_silence_length_us_ =
@@ -36,8 +36,8 @@
 
   // Set the default configuration for Push To Talk mode.
   EnergyEndpointerParams ep_config;
-  ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
-  ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
+  ep_config.set_frame_period(1.0f / static_cast<float>(frame_rate_));
+  ep_config.set_frame_duration(1.0f / static_cast<float>(frame_rate_));
   ep_config.set_endpoint_margin(0.2f);
   ep_config.set_onset_window(0.15f);
   ep_config.set_speech_on_window(0.4f);
@@ -62,7 +62,7 @@
   waiting_for_speech_complete_timeout_ = false;
   speech_previously_detected_ = false;
   speech_input_complete_ = false;
-  audio_frame_time_us_ = 0; // Reset time for packets sent to endpointer.
+  audio_frame_time_us_ = 0;  // Reset time for packets sent to endpointer.
   speech_end_time_us_ = -1;
   speech_start_time_us_ = -1;
 }
@@ -89,24 +89,42 @@
   return energy_endpointer_.Status(time);
 }
 
-EpStatus Endpointer::ProcessAudio(const AudioChunk& raw_audio, float* rms_out) {
-  const int16_t* audio_data = raw_audio.SamplesData16();
-  const int num_samples = raw_audio.NumSamples();
+EpStatus Endpointer::ProcessAudio(
+    const ShellAudioBus& audio_bus, float* rms_out) {
+  DCHECK_EQ(audio_bus.channels(), 1);
+
+  const size_t num_samples = audio_bus.frames();
+  const int16_t* audio_data = NULL;
+
+  ShellAudioBus int16_audio_bus(1, num_samples, ShellAudioBus::kInt16,
+                                ShellAudioBus::kInterleaved);
+
+  if (audio_bus.sample_type() == ShellAudioBus::kFloat32) {
+    int16_audio_bus.Assign(audio_bus);
+    DCHECK_EQ(int16_audio_bus.sample_type(), ShellAudioBus::kInt16);
+    audio_data =
+        reinterpret_cast<const int16_t*>(int16_audio_bus.interleaved_data());
+  } else {
+    DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+    audio_data =
+        reinterpret_cast<const int16_t*>(audio_bus.interleaved_data());
+  }
+
   EpStatus ep_status = EP_PRE_SPEECH;
 
-  // Process the input data in blocks of frame_size_, dropping any incomplete
+  // Process the input data in blocks of kFrameSize, dropping any incomplete
   // frames at the end (which is ok since typically the caller will be recording
   // audio in multiples of our frame size).
   int sample_index = 0;
-  while (sample_index + frame_size_ <= num_samples) {
+  while (static_cast<size_t>(sample_index + kFrameSize) <= num_samples) {
     // Have the endpointer process the frame.
     energy_endpointer_.ProcessAudioFrame(audio_frame_time_us_,
                                          audio_data + sample_index,
-                                         frame_size_,
+                                         kFrameSize,
                                          rms_out);
-    sample_index += frame_size_;
-    audio_frame_time_us_ += (frame_size_ * Time::kMicrosecondsPerSecond) /
-                         sample_rate_;
+    sample_index += kFrameSize;
+    audio_frame_time_us_ +=
+        (kFrameSize * Time::kMicrosecondsPerSecond) / sample_rate_;
 
     // Get the status of the endpointer.
     int64_t ep_time;
diff --git a/src/content/browser/speech/endpointer/endpointer.h b/src/content/browser/speech/endpointer/endpointer.h
index 5790672..986ed0a 100644
--- a/src/content/browser/speech/endpointer/endpointer.h
+++ b/src/content/browser/speech/endpointer/endpointer.h
@@ -8,14 +8,12 @@
 #include <stdint.h>
 
 #include "content/browser/speech/endpointer/energy_endpointer.h"
-#include "content/common/content_export.h"
+#include "media/base/shell_audio_bus.h"
 
 class EpStatus;
 
 namespace content {
 
-class AudioChunk;
-
 // A simple interface to the underlying energy-endpointer implementation, this
 // class lets callers provide audio as being recorded and let them poll to find
 // when the user has stopped speaking.
@@ -44,8 +42,10 @@
 // The timeout length is speech_input_complete_silence_length until
 // long_speech_length, when it changes to
 // long_speech_input_complete_silence_length.
-class CONTENT_EXPORT Endpointer {
+class Endpointer {
  public:
+  typedef ::media::ShellAudioBus ShellAudioBus;
+
   explicit Endpointer(int sample_rate);
 
   // Start the endpointer. This should be called at the beginning of a session.
@@ -64,7 +64,7 @@
 
   // Process a segment of audio, which may be more than one frame.
   // The status of the last frame will be returned.
-  EpStatus ProcessAudio(const AudioChunk& raw_audio, float* rms_out);
+  EpStatus ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out);
 
   // Get the status of the endpointer.
   EpStatus Status(int64_t* time_us);
@@ -99,6 +99,8 @@
     return speech_input_complete_;
   }
 
+  int sample_rate() const { return sample_rate_; }
+
   // RMS background noise level in dB.
   float NoiseLevelDb() const { return energy_endpointer_.GetNoiseLevelDb(); }
 
@@ -146,7 +148,8 @@
   bool speech_input_complete_;
   EnergyEndpointer energy_endpointer_;
   int sample_rate_;
-  int32_t frame_size_;
+  // 1 frame = (1 / frame_rate_) second of audio.
+  int frame_rate_;
 };
 
 }  // namespace content
diff --git a/src/content/browser/speech/endpointer/endpointer_unittest.cc b/src/content/browser/speech/endpointer/endpointer_unittest.cc
index 53ec4d1..929b8ad 100644
--- a/src/content/browser/speech/endpointer/endpointer_unittest.cc
+++ b/src/content/browser/speech/endpointer/endpointer_unittest.cc
@@ -4,8 +4,8 @@
 
 #include <stdint.h>
 
-#include "content/browser/speech/audio_buffer.h"
 #include "content/browser/speech/endpointer/endpointer.h"
+#include "media/base/shell_audio_bus.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -15,8 +15,13 @@
 // At 8 sample per second a 20 ms frame is 160 samples, which corrsponds
 // to the AMR codec.
 const int kFrameSize = kSampleRate / kFrameRate;  // 160 samples.
-static_assert(kFrameSize == 160, "invalid frame size");
-}
+
+#if defined(OS_STARBOARD)
+SB_COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
+#else
+COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
+#endif  // defined(OS_STARBOARD)
+}  // namespace
 
 namespace content {
 
@@ -79,7 +84,7 @@
 
   EpStatus ProcessFrame(int64_t time,
                         int16_t* samples,
-                        int frame_size) override {
+                        int /*frame_size*/) override {
     endpointer_->ProcessAudioFrame(time, samples, kFrameSize, NULL);
     int64_t ep_time;
     return endpointer_->Status(&ep_time);
@@ -119,15 +124,15 @@
 // Test endpointer wrapper class.
 class EndpointerFrameProcessor : public FrameProcessor {
  public:
+  typedef ::media::ShellAudioBus ShellAudioBus;
   explicit EndpointerFrameProcessor(Endpointer* endpointer)
       : endpointer_(endpointer) {}
 
-  EpStatus ProcessFrame(int64_t time,
+  EpStatus ProcessFrame(int64_t /*time*/,
                         int16_t* samples,
-                        int frame_size) override {
-    scoped_refptr<AudioChunk> frame(
-        new AudioChunk(reinterpret_cast<uint8_t*>(samples), kFrameSize * 2, 2));
-    endpointer_->ProcessAudio(*frame.get(), NULL);
+                        int /*frame_size*/) override {
+    scoped_ptr<ShellAudioBus> frame(new ShellAudioBus(1, kFrameSize, samples));
+    endpointer_->ProcessAudio(*frame, NULL);
     int64_t ep_time;
     return endpointer_->Status(&ep_time);
   }
@@ -137,8 +142,6 @@
 };
 
 TEST(EndpointerTest, TestEmbeddedEndpointerEvents) {
-  const int kSampleRate = 8000;  // 8 k samples per second for AMR encoding.
-
   Endpointer endpointer(kSampleRate);
   const int64_t kMillisecondsPerMicrosecond = 1000;
   const int64_t short_timeout = 300 * kMillisecondsPerMicrosecond;
diff --git a/src/content/browser/speech/endpointer/energy_endpointer.cc b/src/content/browser/speech/endpointer/energy_endpointer.cc
index fc1d871..b8a01d5 100644
--- a/src/content/browser/speech/endpointer/energy_endpointer.cc
+++ b/src/content/browser/speech/endpointer/energy_endpointer.cc
@@ -12,7 +12,6 @@
 #include <stddef.h>
 
 #include "base/logging.h"
-#include "base/macros.h"
 
 namespace {
 
@@ -37,7 +36,7 @@
 
 float GetDecibel(float value) {
   if (value > 1.0e-100)
-    return 20 * log10(value);
+    return static_cast<float>(20 * log10(value));
   return -2000.0;
 }
 
@@ -81,20 +80,21 @@
   insertion_index_ = 0;
   decision_points_.clear();
   DecisionPoint init = { -1, initial_state };
-  decision_points_.resize(size, init);
+  decision_points_.resize(static_cast<size_t>(size), init);
 }
 
 void EnergyEndpointer::HistoryRing::Insert(int64_t time_us, bool decision) {
-  decision_points_[insertion_index_].time_us = time_us;
-  decision_points_[insertion_index_].decision = decision;
-  insertion_index_ = (insertion_index_ + 1) % decision_points_.size();
+  decision_points_[static_cast<size_t>(insertion_index_)].time_us = time_us;
+  decision_points_[static_cast<size_t>(insertion_index_)].decision = decision;
+  insertion_index_ =
+      static_cast<int>((insertion_index_ + 1) % decision_points_.size());
 }
 
 int64_t EnergyEndpointer::HistoryRing::EndTime() const {
   int ind = insertion_index_ - 1;
   if (ind < 0)
-    ind = decision_points_.size() - 1;
-  return decision_points_[ind].time_us;
+    ind = static_cast<int>(decision_points_.size() - 1);
+  return decision_points_[static_cast<size_t>(ind)].time_us;
 }
 
 float EnergyEndpointer::HistoryRing::RingSum(float duration_sec) {
@@ -104,23 +104,23 @@
   int64_t sum_us = 0;
   int ind = insertion_index_ - 1;
   if (ind < 0)
-    ind = decision_points_.size() - 1;
-  int64_t end_us = decision_points_[ind].time_us;
-  bool is_on = decision_points_[ind].decision;
+    ind = static_cast<int>(decision_points_.size() - 1);
+  int64_t end_us = decision_points_[static_cast<size_t>(ind)].time_us;
+  bool is_on = decision_points_[static_cast<size_t>(ind)].decision;
   int64_t start_us =
       end_us - static_cast<int64_t>(0.5 + (1.0e6 * duration_sec));
   if (start_us < 0)
     start_us = 0;
   size_t n_summed = 1;  // n points ==> (n-1) intervals
-  while ((decision_points_[ind].time_us > start_us) &&
+  while ((decision_points_[static_cast<size_t>(ind)].time_us > start_us) &&
          (n_summed < decision_points_.size())) {
     --ind;
     if (ind < 0)
-      ind = decision_points_.size() - 1;
+      ind = static_cast<int>(decision_points_.size() - 1);
     if (is_on)
-      sum_us += end_us - decision_points_[ind].time_us;
-    is_on = decision_points_[ind].decision;
-    end_us = decision_points_[ind].time_us;
+      sum_us += end_us - decision_points_[static_cast<size_t>(ind)].time_us;
+    is_on = decision_points_[static_cast<size_t>(ind)].decision;
+    end_us = decision_points_[static_cast<size_t>(ind)].time_us;
     n_summed++;
   }
 
@@ -298,6 +298,8 @@
         }
         break;
 
+      case EP_POST_SPEECH:
+        // fall-through
       default:
         LOG(WARNING) << "Invalid case in switch: " << status_;
         break;
@@ -364,7 +366,7 @@
       noise_level_ = (0.95f * noise_level_) + (0.05f * rms);
   }
   if (estimating_environment_ || (frame_counter_ < fast_update_frames_)) {
-    decision_threshold_ = noise_level_ * 2; // 6dB above noise level.
+    decision_threshold_ = noise_level_ * 2;  // 6dB above noise level.
     // Set a floor
     if (decision_threshold_ < params_.min_decision_threshold())
       decision_threshold_ = params_.min_decision_threshold();
diff --git a/src/content/browser/speech/endpointer/energy_endpointer.h b/src/content/browser/speech/endpointer/energy_endpointer.h
index 7b0b292..2d379b9 100644
--- a/src/content/browser/speech/endpointer/energy_endpointer.h
+++ b/src/content/browser/speech/endpointer/energy_endpointer.h
@@ -42,9 +42,8 @@
 #include <memory>
 #include <vector>
 
-#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 #include "content/browser/speech/endpointer/energy_endpointer_params.h"
-#include "content/common/content_export.h"
 
 namespace content {
 
@@ -57,7 +56,7 @@
   EP_POST_SPEECH,
 };
 
-class CONTENT_EXPORT EnergyEndpointer {
+class EnergyEndpointer {
  public:
   // The default construction MUST be followed by Init(), before any
   // other use can be made of the instance.
@@ -125,7 +124,7 @@
   float sample_rate_;  // Sampling rate.
 
   // Ring buffers to hold the speech activity history.
-  std::unique_ptr<HistoryRing> history_;
+  scoped_ptr<HistoryRing> history_;
 
   // Configuration parameters.
   EnergyEndpointerParams params_;
diff --git a/src/content/browser/speech/endpointer/energy_endpointer_params.h b/src/content/browser/speech/endpointer/energy_endpointer_params.h
index 1510435..303b3b0 100644
--- a/src/content/browser/speech/endpointer/energy_endpointer_params.h
+++ b/src/content/browser/speech/endpointer/energy_endpointer_params.h
@@ -5,12 +5,10 @@
 #ifndef CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
 #define CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
 
-#include "content/common/content_export.h"
-
 namespace content {
 
 // Input parameters for the EnergyEndpointer class.
-class CONTENT_EXPORT EnergyEndpointerParams {
+class EnergyEndpointerParams {
  public:
   EnergyEndpointerParams();
 
diff --git a/src/content/browser/speech/speech.gyp b/src/content/browser/speech/speech.gyp
new file mode 100644
index 0000000..2db9fdf
--- /dev/null
+++ b/src/content/browser/speech/speech.gyp
@@ -0,0 +1,60 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'targets': [
+    {
+      'target_name': 'speech',
+      'type': 'static_library',
+      'sources': [
+        'chunked_byte_buffer.cc',
+        'chunked_byte_buffer.h',
+        'endpointer/endpointer.cc',
+        'endpointer/endpointer.h',
+        'endpointer/energy_endpointer.cc',
+        'endpointer/energy_endpointer.h',
+        'endpointer/energy_endpointer_params.cc',
+        'endpointer/energy_endpointer_params.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/media/media.gyp:media',
+      ],
+    },
+    {
+      'target_name': 'speech_test',
+      'type': '<(gtest_target_type)',
+      'sources': [
+        'chunked_byte_buffer_unittest.cc',
+        'endpointer/endpointer_unittest.cc',
+      ],
+      'dependencies': [
+        'speech',
+        '<(DEPTH)/base/base.gyp:run_all_unittests',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+
+    {
+      'target_name': 'speech_test_deploy',
+      'type': 'none',
+      'dependencies': [
+        'speech_test',
+      ],
+      'variables': {
+        'executable_name': 'speech_test',
+      },
+      'includes': [ '../../../starboard/build/deploy.gypi' ],
+    },
+  ],
+}
diff --git a/src/media/media.gyp b/src/media/media.gyp
index 12ebe12..4f6d3d0 100644
--- a/src/media/media.gyp
+++ b/src/media/media.gyp
@@ -431,6 +431,14 @@
           'dependencies': [
             '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
             'shared_memory_support',
+          ],
+        }],
+        ['OS != "ios" and cobalt != 1', {
+          'dependencies': [
+            # This is only used in [x11|gl|skcanvas]_video_renderer.cc, but not
+            # when compiling Cobalt. When linking as a shared library, the
+            # unused optimized YUV functions are left as undefined symbols
+            # after they get dead-stripped.
             'yuv_convert',
           ],
         }],
diff --git a/src/nb/analytics/memory_tracker.cc b/src/nb/analytics/memory_tracker.cc
index cf6c868..8cb7664 100644
--- a/src/nb/analytics/memory_tracker.cc
+++ b/src/nb/analytics/memory_tracker.cc
@@ -17,6 +17,7 @@
 #include "nb/analytics/memory_tracker.h"
 
 #include "nb/analytics/memory_tracker_impl.h"
+#include "nb/scoped_ptr.h"
 #include "starboard/once.h"
 
 namespace nb {
@@ -26,7 +27,14 @@
 }  // namespace
 
 MemoryTracker* MemoryTracker::Get() {
-  return GetMemoryTrackerImplSingleton();
+  MemoryTracker* t = GetMemoryTrackerImplSingleton();
+  return t;
+}
+
+nb::scoped_ptr<MemoryTrackerPrintThread>
+CreateDebugPrintThread(MemoryTracker* memory_tracker) {
+  return nb::scoped_ptr<MemoryTrackerPrintThread>(
+     new MemoryTrackerPrintThread(memory_tracker));
 }
 
 }  // namespace analytics
diff --git a/src/nb/analytics/memory_tracker.h b/src/nb/analytics/memory_tracker.h
index 27ef75f..4884c89 100644
--- a/src/nb/analytics/memory_tracker.h
+++ b/src/nb/analytics/memory_tracker.h
@@ -20,11 +20,14 @@
 #include <vector>
 #include "starboard/configuration.h"
 #include "starboard/types.h"
+#include "nb/scoped_ptr.h"
 
 namespace nb {
+
 namespace analytics {
 
 class MemoryTracker;
+class MemoryTrackerPrintThread;
 class AllocationVisitor;
 class AllocationGroup;
 class AllocationRecord;
@@ -120,7 +123,8 @@
 
 // Contains an allocation record for a pointer including it's size and what
 // AllocationGroup it was constructed under.
-struct AllocationRecord {
+class AllocationRecord {
+ public:
   AllocationRecord() : size(0), allocation_group(NULL) {}
   AllocationRecord(size_t sz, AllocationGroup* group)
       : size(sz), allocation_group(group) {}
@@ -131,6 +135,13 @@
   AllocationGroup* allocation_group;
 };
 
+// Creates a SimpleThread that will output the state of the memory
+// periodically. Start()/Cancel()/Join() are called AUTOMATICALLY with
+// this object. Start() is on the returned thread before it is returned.
+// Join() is automatically called on destruction.
+scoped_ptr<MemoryTrackerPrintThread>
+    CreateDebugPrintThread(MemoryTracker* memory_tracker);
+
 }  // namespace analytics
 }  // namespace nb
 
diff --git a/src/nb/analytics/memory_tracker_helpers.cc b/src/nb/analytics/memory_tracker_helpers.cc
index 2f4776a..a1eedc8 100644
--- a/src/nb/analytics/memory_tracker_helpers.cc
+++ b/src/nb/analytics/memory_tracker_helpers.cc
@@ -27,7 +27,8 @@
 namespace analytics {
 
 AllocationGroup::AllocationGroup(const std::string& name)
-    : name_(name), allocation_bytes_(0), num_allocations_(0) {}
+    : name_(name), allocation_bytes_(0), num_allocations_(0) {
+}
 
 AllocationGroup::~AllocationGroup() {}
 
@@ -35,7 +36,6 @@
   if (num_bytes == 0)
     return;
   int num_alloc_diff = num_bytes > 0 ? 1 : -1;
-
   allocation_bytes_.fetch_add(num_bytes);
   num_allocations_.fetch_add(num_alloc_diff);
 }
@@ -276,36 +276,5 @@
   return pointer_map_array_[ToIndex(ptr)];
 }
 
-SimpleThread::SimpleThread(const std::string& name)
-    : thread_(kSbThreadInvalid), name_(name) {}
-
-SimpleThread::~SimpleThread() {}
-
-void SimpleThread::Start() {
-  SbThreadEntryPoint entry_point = ThreadEntryPoint;
-
-  thread_ = SbThreadCreate(0,                    // default stack_size.
-                           kSbThreadNoPriority,  // default priority.
-                           kSbThreadNoAffinity,  // default affinity.
-                           true,                 // joinable.
-                           name_.c_str(), entry_point, this);
-
-  // SbThreadCreate() above produced an invalid thread handle.
-  SB_DCHECK(thread_ != kSbThreadInvalid);
-  return;
-}
-
-void* SimpleThread::ThreadEntryPoint(void* context) {
-  SimpleThread* this_ptr = static_cast<SimpleThread*>(context);
-  this_ptr->Run();
-  return NULL;
-}
-
-void SimpleThread::DoJoin() {
-  if (!SbThreadJoin(thread_, NULL)) {
-    SB_DCHECK(false) << "Could not join thread.";
-  }
-}
-
 }  // namespace analytics
 }  // namespace nb
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
index d07651c..d004620 100644
--- a/src/nb/analytics/memory_tracker_helpers.h
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -22,6 +22,7 @@
 
 #include "nb/analytics/memory_tracker.h"
 #include "nb/atomic.h"
+#include "nb/simple_thread.h"
 #include "starboard/mutex.h"
 #include "starboard/thread.h"
 #include "starboard/types.h"
@@ -31,7 +32,7 @@
 namespace analytics {
 
 class AllocationGroup;
-struct AllocationRecord;
+class AllocationRecord;
 
 template <typename Type>
 class ThreadLocalPointer {
@@ -89,7 +90,7 @@
 // together, such as "Javascript" or "Graphics".
 class AllocationGroup {
  public:
-  AllocationGroup(const std::string& name);
+  explicit AllocationGroup(const std::string& name);
   ~AllocationGroup();
   const std::string& name() const { return name_; }
 
@@ -103,6 +104,8 @@
   const std::string name_;
   nb::atomic_int64_t allocation_bytes_;
   nb::atomic_int32_t num_allocations_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroup);
 };
 
 // A self locking data structure that maps strings -> AllocationGroups. This is
@@ -124,6 +127,8 @@
   Map group_map_;
   AllocationGroup* unaccounted_group_;
   mutable starboard::Mutex mutex_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(AtomicStringAllocationGroupMap);
 };
 
 class AllocationGroupStack {
@@ -222,42 +227,6 @@
   AtomicAllocationMap pointer_map_array_[kNumElements];
 };
 
-class SimpleThread {
- public:
-  explicit SimpleThread(const std::string& name);
-  virtual ~SimpleThread() = 0;
-
-  // Subclasses should override the Run method.
-  virtual void Run() = 0;
-
-  void Join() {
-    Cancel();
-    DoJoin();
-  }
-
-  // If Join() is intended to interrupt the Run() function then override
-  // Cancel() to send a signal.
-  // Example:
-  //   virtual void Cancel() { finished_ = true; }
-  //   virtual void Run() {
-  //     while (!finished_) { /* do work */ }
-  //   }
-  virtual void Cancel() {}
-
-  // Calls SbThreadCreate() and starts running code.
-  void Start();
-
- private:
-  static void* ThreadEntryPoint(void* context);
-  void DoJoin();
-  void DoStart();
-
-  const std::string name_;
-  SbThread thread_;
-
-  SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);
-};
-
 }  // namespace analytics
 }  // namespace nb
 
diff --git a/src/nb/analytics/memory_tracker_impl.cc b/src/nb/analytics/memory_tracker_impl.cc
index fecda24..3a9bdf4 100644
--- a/src/nb/analytics/memory_tracker_impl.cc
+++ b/src/nb/analytics/memory_tracker_impl.cc
@@ -20,6 +20,7 @@
 #include <sstream>
 
 #include "nb/atomic.h"
+#include "starboard/log.h"
 #include "starboard/atomic.h"
 
 namespace nb {
@@ -196,25 +197,25 @@
   // We might do something more interesting with UnMapMemory calls later.
   OnDealloc(context, memory);
 }
-
+

 void MemoryTrackerImpl::OnPushAllocationGroup(
     void* context,
     NbMemoryScopeInfo* memory_scope_info) {
   MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context);
-  uintptr_t* cached_handle = &(memory_scope_info->cached_handle_);
+  void** cached_handle = &(memory_scope_info->cached_handle_);
   const bool allows_caching = memory_scope_info->allows_caching_;
   const char* group_name = memory_scope_info->memory_scope_name_;
 
   AllocationGroup* group = NULL;
-  if (allows_caching && *cached_handle != 0) {
-    group = reinterpret_cast<AllocationGroup*>(cached_handle);
+  if (allows_caching && *cached_handle != NULL) {
+    group = static_cast<AllocationGroup*>(*cached_handle);
   } else {
     group = t->GetAllocationGroup(group_name);
     if (allows_caching) {
       // Flush all pending writes so that the the pointee is well formed
       // by the time the pointer becomes visible to other threads.
       SbAtomicMemoryBarrier();
-      *cached_handle = reinterpret_cast<uintptr_t>(group);
+      *cached_handle = static_cast<void*>(group);
     }
   }
 
@@ -269,118 +270,6 @@
   owner_->SetMemoryDeletionEnabled(prev_state_);
 }
 
-// TODO: Get rid of the nb::SimpleThread
-class MemoryTrackerPrintThread : public SimpleThread {
- public:
-  MemoryTrackerPrintThread(MemoryTracker* owner)
-      : SimpleThread("MemoryTrackerPrintThread"),
-        finished_(false),
-        owner_(owner) {}
-
-  // Overridden so that the thread can exit gracefully.
-  virtual void Cancel() SB_OVERRIDE { finished_.store(true); }
-
-  virtual void Run() {
-    struct NoMemTracking {
-      NoMemTracking(MemoryTracker* owner) : owner_(owner) {
-        prev_val_ = owner_->IsMemoryTrackingEnabled();
-        owner_->SetMemoryTrackingEnabled(false);
-      }
-      ~NoMemTracking() { owner_->SetMemoryTrackingEnabled(prev_val_); }
-
-      bool prev_val_;
-      MemoryTracker* owner_;
-    };
-
-    while (!finished_.load()) {
-      NoMemTracking no_mem_tracking_in_this_scope(owner_);
-
-      // std::map<std::string, const AllocationGroup*> output;
-      // typedef std::map<std::string, const AllocationGroup*>::const_iterator
-      // MapIt;
-      std::vector<const AllocationGroup*> vector_output;
-      owner_->GetAllocationGroups(&vector_output);
-
-      typedef std::map<std::string, const AllocationGroup*> Map;
-      typedef Map::const_iterator MapIt;
-
-      Map output;
-      for (int i = 0; i < vector_output.size(); ++i) {
-        const AllocationGroup* group = vector_output[i];
-        output[group->name()] = group;
-      }
-
-      int32_t num_allocs = 0;
-      int64_t total_bytes = 0;
-
-      struct F {
-        static void PrintRow(std::stringstream& ss,
-                             const std::string& v1,
-                             const std::string& v2,
-                             const std::string& v3) {
-          ss.width(20);
-          ss << std::left << v1;
-          ss.width(13);
-          ss << std::right << v2 << "  ";
-          ss.width(7);
-          ss << std::right << v3 << "\n";
-        }
-      };
-
-      if (owner_->IsMemoryTrackingEnabled()) {
-        // If this isn't true then it would cause an infinite loop. The
-        // following will likely crash.
-        SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
-      }
-
-      std::stringstream ss;
-      for (MapIt it = output.begin(); it != output.end(); ++it) {
-        const AllocationGroup* group = it->second;
-        if (!group) {
-          continue;
-        }
-
-        int32_t num_group_allocs = -1;
-        int64_t total_group_bytes = -1;
-
-        group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
-        SB_DCHECK(-1 != num_group_allocs);
-        SB_DCHECK(-1 != total_group_bytes);
-        num_allocs += num_group_allocs;
-        total_bytes += total_group_bytes;
-
-        ss.width(20);
-        ss << std::left << it->first;
-        ss.width(13);
-        ss << std::right << NumberFormatWithCommas(total_group_bytes) << "  ";
-        ss.width(7);
-        ss << std::right << NumberFormatWithCommas(num_group_allocs) << "\n";
-      }
-      ss << "-------------------------------\n";
-
-      SB_LOG(INFO) << "\n"
-                   << "Total Bytes Allocated: "
-                   << NumberFormatWithCommas(total_bytes) << "\n"
-                   << "Total allocations: "
-                   << NumberFormatWithCommas(num_allocs) << "\n\n" << ss.str();
-
-      SbThreadSleep(250);
-    }
-  }
-
- private:
-  atomic_bool finished_;
-  MemoryTracker* owner_;
-};
-
-void MemoryTrackerImpl::Debug_EnablePrintOutThread() {
-  if (debug_output_thread_) {
-    return;
-  }  // Already enabled.
-  debug_output_thread_.reset(new MemoryTrackerPrintThread(this));
-  debug_output_thread_->Start();
-}
-
 MemoryTrackerImpl::MemoryTrackerImpl()
     : thread_filter_id_(kSbThreadInvalidId) {
   total_bytes_allocated_.store(0);
@@ -393,16 +282,12 @@
 MemoryTrackerImpl::~MemoryTrackerImpl() {
   // If we are currently hooked into allocation tracking...
   if (global_hooks_installed_) {
-    SbMemorySetReporter(NULL);
+    RemoveGlobalTrackingHooks();
     // For performance reasons no locking is used on the tracker.
     // Therefore give enough time for other threads to exit this tracker
     // before fully destroying this object.
     SbThreadSleep(250 * kSbTimeMillisecond);  // 250 millisecond wait.
   }
-  if (debug_output_thread_) {
-    debug_output_thread_->Join();
-    debug_output_thread_.reset();
-  }
 }
 
 bool MemoryTrackerImpl::AddMemoryTracking(const void* memory, size_t size) {
@@ -447,6 +332,9 @@
     AddAllocationBytes(size);
     group->AddAllocation(size);
   } else {
+    // Handles the case where the memory hasn't been properly been reported
+    // released. This is less serious than you would think because the memory
+    // allocator in the system will recycle the memory and it will come back.
     AllocationRecord unexpected_alloc;
     atomic_allocation_map_.Get(memory, &unexpected_alloc);
     AllocationGroup* prev_group = unexpected_alloc.allocation_group;
@@ -458,12 +346,16 @@
       prev_group_name = "none";
     }
 
-    SB_DCHECK(added)
-        << "\nUnexpected condition, previous allocation was not removed:\n"
-        << "\tprevious alloc group: " << prev_group_name << "\n"
-        << "\tnew alloc group: " << group->name() << "\n"
-        << "\tprevious size: " << unexpected_alloc.size << "\n"
-        << "\tnew size: " << size << "\n";
+    if (!added) {

+      std::stringstream ss;

+      ss << "\nUnexpected condition, previous allocation was not removed:\n"

+         << "\tprevious alloc group: " << prev_group_name << "\n"

+         << "\tnew alloc group: " << group->name() << "\n"

+         << "\tprevious size: " << unexpected_alloc.size << "\n"

+         << "\tnew size: " << size << "\n";

+

+      SbLogRaw(ss.str().c_str());

+    }
   }
   return added;
 }
@@ -537,5 +429,115 @@
   owner_->SetMemoryTrackingEnabled(prev_value_);
 }
 
+
+MemoryTrackerPrintThread::MemoryTrackerPrintThread(
+    MemoryTracker* memory_tracker)
+    : SimpleThread("MemoryTrackerPrintThread"),
+    finished_(false),
+    memory_tracker_(memory_tracker) {
+  Start();
+}
+
+MemoryTrackerPrintThread::~MemoryTrackerPrintThread() {
+  Cancel();
+  Join();
+}
+
+void MemoryTrackerPrintThread::Cancel() {
+  finished_.store(true);
+}
+
+void MemoryTrackerPrintThread::Run() {
+  struct NoMemTracking {
+    NoMemTracking(MemoryTracker* owner) : owner_(owner) {
+      prev_val_ = owner_->IsMemoryTrackingEnabled();
+      owner_->SetMemoryTrackingEnabled(false);
+    }
+    ~NoMemTracking() { owner_->SetMemoryTrackingEnabled(prev_val_); }
+
+    bool prev_val_;
+    MemoryTracker* owner_;
+  };
+
+  while (!finished_.load()) {
+    NoMemTracking no_mem_tracking_in_this_scope(memory_tracker_);
+
+    // std::map<std::string, const AllocationGroup*> output;
+    // typedef std::map<std::string, const AllocationGroup*>::const_iterator
+    // MapIt;
+    std::vector<const AllocationGroup*> vector_output;
+    memory_tracker_->GetAllocationGroups(&vector_output);
+
+    typedef std::map<std::string, const AllocationGroup*> Map;
+    typedef Map::const_iterator MapIt;
+
+    Map output;
+    for (int i = 0; i < vector_output.size(); ++i) {
+      const AllocationGroup* group = vector_output[i];
+      output[group->name()] = group;
+    }
+
+    int32_t num_allocs = 0;
+    int64_t total_bytes = 0;
+
+    struct F {
+      static void PrintRow(std::stringstream* ss,
+          const std::string& v1,
+          const std::string& v2,
+          const std::string& v3) {
+        ss->width(20);
+        *ss << std::left << v1;
+        ss->width(13);
+        *ss << std::right << v2 << "  ";
+        ss->width(10);
+        *ss << std::right << v3 << "\n";
+      }
+    };
+
+    if (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;
+
+    F::PrintRow(&ss, "NAME", "BYTES", "NUM ALLOCS");
+    ss << "---------------------------------------------\n";
+    for (MapIt it = output.begin(); it != output.end(); ++it) {
+      const AllocationGroup* group = it->second;
+      if (!group) {
+        continue;
+      }
+
+      int32_t num_group_allocs = -1;
+      int64_t total_group_bytes = -1;
+
+      group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
+      SB_DCHECK(-1 != num_group_allocs);
+      SB_DCHECK(-1 != total_group_bytes);
+      num_allocs += num_group_allocs;
+      total_bytes += total_group_bytes;
+
+      F::PrintRow(&ss,
+                  it->first,
+                  NumberFormatWithCommas(total_group_bytes),
+                  NumberFormatWithCommas(num_group_allocs));
+    }
+    ss << "---------------------------------------------\n";
+
+    std::stringstream final_ss;
+    final_ss << "\n"
+      << "Total Bytes Allocated: "
+      << NumberFormatWithCommas(total_bytes) << "\n"
+      << "Total allocations: "
+      << NumberFormatWithCommas(num_allocs) << "\n\n" << ss.str();
+
+    SbLogRaw(final_ss.str().c_str());
+
+    SbThreadSleep(1000 * 1000);
+  }
+}
+
 }  // namespace analytics
 }  // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h
index 7508a71..1575e7b 100644
--- a/src/nb/analytics/memory_tracker_impl.h
+++ b/src/nb/analytics/memory_tracker_impl.h
@@ -60,6 +60,8 @@
   // generated for the supplied allocation which can be queried immediately
   // with GetMemoryTracking(...).
   bool InstallGlobalTrackingHooks() SB_OVERRIDE {
+    if (global_hooks_installed_)
+      return true;
     global_hooks_installed_ = true;
     bool ok = SbMemorySetReporter(GetMemoryReporter());
     ok |= NbSetMemoryScopeReporter(GetMemoryScopeReporter());
@@ -123,10 +125,6 @@
   // allocations.
   void SetThreadFilter(SbThreadId tid);
   bool IsCurrentThreadAllowedToReport() const;
-  // Spawns a thread who's lifetime is coupled to that of this owning
-  // MemoryTrackerImpl. This thread will output the state of the memory
-  // periodically.
-  void Debug_EnablePrintOutThread();
 
  private:
   struct DisableMemoryTrackingInScope {
@@ -169,7 +167,6 @@
   AtomicStringAllocationGroupMap alloc_group_map_;
 
   atomic_int64_t total_bytes_allocated_;
-  scoped_ptr<SimpleThread> debug_output_thread_;
 
   // THREAD LOCAL SECTION.
   ThreadLocalBoolean memory_deletion_enabled_tls_;
@@ -178,6 +175,22 @@
   bool global_hooks_installed_;
 };
 
+// Start() is called when this object is created, and Cancel() & Join() are
+// called during destruction.
+class MemoryTrackerPrintThread : private SimpleThread {
+ public:
+  MemoryTrackerPrintThread(MemoryTracker* memory_tracker);
+  virtual ~MemoryTrackerPrintThread() SB_OVERRIDE;
+
+  // Overridden so that the thread can exit gracefully.
+  virtual void Cancel() SB_OVERRIDE;
+  virtual void Run() SB_OVERRIDE;
+
+ private:
+  atomic_bool finished_;
+  MemoryTracker* memory_tracker_;
+};
+
 }  // namespace analytics
 }  // namespace nb
 
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc
index 8236447..309d771 100644
--- a/src/nb/analytics/memory_tracker_impl_test.cc
+++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -380,14 +380,14 @@
   }
   const bool kCaching = false;
   NbMemoryScopeInfo memory_scope = {
-      0,        "MyName",     __FILE__,
+      NULL, "MyName", __FILE__,
       __LINE__, __FUNCTION__, false};  // false to disallow caching.
 
   // Pushing the memory scope should trigger the caching operation to be
   // attempted. However, because caching was explicitly disabled this handle
   // should retain the value of 0.
   NbPushMemoryScope(&memory_scope);
-  EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_, uintptr_t(0));
+  EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ == NULL);
 
   // ... and still assert that the group was created with the expected name.
   AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName");
@@ -405,18 +405,18 @@
     return;
   }
   NbMemoryScopeInfo memory_scope = {
-      0,         // Cached handle.
+      NULL,      // Cached handle.
       "MyName",  // Memory scope name.
       __FILE__, __LINE__, __FUNCTION__,
       true  // Allows caching.
   };
 
   NbPushMemoryScope(&memory_scope);
-  EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ != uintptr_t(0));
+  EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ != NULL);
   AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName");
 
-  EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ ==
-                          reinterpret_cast<uintptr_t>(group));
+  EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_,
+                        static_cast<void*>(group));
 }
 
 // Tests the expectation that the macro TRACK_MEMORY_SCOPE will capture the
@@ -793,7 +793,7 @@
         << "This pointer should not be in the map.";
   }
 
-  NoMemTracking no_tracking_in_scope;  // DEBUG!!
+  NoMemTracking no_tracking_in_scope;
   allocated_pts_[memory] = AllocationRecord(alloc_size, current_group);
 }
 
diff --git a/src/nb/memory_scope.h b/src/nb/memory_scope.h
index 9c9446b..d7d37bf 100644
--- a/src/nb/memory_scope.h
+++ b/src/nb/memory_scope.h
@@ -17,8 +17,6 @@
 #ifndef NB_MEMORY_SCOPE_H_
 #define NB_MEMORY_SCOPE_H_
 
-#include "starboard/types.h"
-
 ///////////////////////////////////////////////////////////////////////////////
 // Macros to define the memory scope objects. These are objects that are used
 // to annotate sections of the code base as belonging to a particular memory
@@ -58,13 +56,13 @@
 // tracking.
 #define TRACK_MEMORY_STATIC_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
   static NbMemoryScopeInfo memory_scope_handle_##LineNum =                \
-      { 0, Str, FileStr, LineNum, FuncStr, true };                        \
+      { NULL, Str, FileStr, LineNum, FuncStr, true };                     \
   NbPushMemoryScope(&memory_scope_handle_##LineNum);                      \
   NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
 
 #define TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
   NbMemoryScopeInfo memory_scope_handle_##LineNum = {                         \
-      0, Str, FileStr, LineNum, FuncStr, false};                              \
+      NULL, Str, FileStr, LineNum, FuncStr, false};                           \
   NbPushMemoryScope(&memory_scope_handle_##LineNum);                          \
   NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
 
@@ -109,7 +107,7 @@
   // cached_handle_ allows a cached result of the the fields represented in
   // this struct to be generated and the handle be placed into this field.
   // See also allows_caching_.
-  uintptr_t cached_handle_;
+  void* cached_handle_;
 
   // Represents the name of the memory scope. I.E. "Javascript" or "Gfx".
   const char* memory_scope_name_;
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 855c800..360ac03 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -20,43 +20,45 @@
       'variables': {
         'includes_starboard': 1,
       },
-      'sources': [
-        'allocator.h',
-        'allocator_decorator.cc',
-        'allocator_decorator.h',
-        'analytics/memory_tracker.cc',
-        'analytics/memory_tracker.h',
-        'analytics/memory_tracker_impl.cc',
-        'analytics/memory_tracker_impl.h',
-        'analytics/memory_tracker_helpers.cc',
-        'analytics/memory_tracker_helpers.h',
-        'atomic.h',
-        'fixed_no_free_allocator.cc',
-        'fixed_no_free_allocator.h',
-        'hash.cc',
-        'hash.h',
-        'memory_pool.cc',
-        'memory_pool.h',
-        'memory_scope.cc',
-        'memory_scope.h',
-        'move.h',
-        'pointer_arithmetic.h',
-        'rect.h',
-        'ref_counted.cc',
-        'ref_counted.h',
-        'reuse_allocator.cc',
-        'reuse_allocator.h',
-        'scoped_ptr.h',
-        'thread_collision_warner.cc',
-        'thread_collision_warner.h',
-        'thread_local_object.h',
-      ],
-
-      'dependencies': [
-        '<(DEPTH)/starboard/starboard.gyp:starboard',
-      ],
-
       'conditions': [
+        ['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',
+            'analytics/memory_tracker_impl.h',
+            'analytics/memory_tracker_helpers.cc',
+            'analytics/memory_tracker_helpers.h',
+            'atomic.h',
+            'fixed_no_free_allocator.cc',
+            'fixed_no_free_allocator.h',
+            'hash.cc',
+            'hash.h',
+            'memory_pool.cc',
+            'memory_pool.h',
+            'memory_scope.cc',
+            'memory_scope.h',
+            'move.h',
+            'pointer_arithmetic.h',
+            'rect.h',
+            'ref_counted.cc',
+            'ref_counted.h',
+            'reuse_allocator.cc',
+            'reuse_allocator.h',
+            'scoped_ptr.h',
+            'simple_thread.cc',
+            'simple_thread.h',
+            'thread_collision_warner.cc',
+            'thread_collision_warner.h',
+            'thread_local_object.h',
+          ],
+          'dependencies': [
+            '<(DEPTH)/starboard/starboard.gyp:starboard',
+          ],
+        }],
         ['target_arch == "ps4"', {
           'sources': [
             'kernel_contiguous_allocator_ps4.cc',
@@ -71,22 +73,26 @@
     {
       'target_name': 'nb_test',
       'type': '<(gtest_target_type)',
-      'sources': [
-        'analytics/memory_tracker_helpers_test.cc',
-        'analytics/memory_tracker_impl_test.cc',
-        'analytics/memory_tracker_test.cc',
-        'atomic_test.cc',
-        'fixed_no_free_allocator_test.cc',
-        'memory_scope_test.cc',
-        'reuse_allocator_test.cc',
-        'run_all_unittests.cc',
-        'test_thread.h',
-        'thread_local_object_test.cc',
-      ],
-      'dependencies': [
-        'nb',
-        '<(DEPTH)/testing/gmock.gyp:gmock',
-        '<(DEPTH)/testing/gtest.gyp:gtest',
+      'conditions': [
+        ['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
+          'sources': [
+            'analytics/memory_tracker_helpers_test.cc',
+            'analytics/memory_tracker_impl_test.cc',
+            'analytics/memory_tracker_test.cc',
+            'atomic_test.cc',
+            'fixed_no_free_allocator_test.cc',
+            'memory_scope_test.cc',
+            'reuse_allocator_test.cc',
+            'run_all_unittests.cc',
+            'test_thread.h',
+            'thread_local_object_test.cc',
+          ],
+          'dependencies': [
+            'nb',
+            '<(DEPTH)/testing/gmock.gyp:gmock',
+            '<(DEPTH)/testing/gtest.gyp:gtest',
+          ],
+        }]
       ],
     },
     {
diff --git a/src/nb/simple_thread.cc b/src/nb/simple_thread.cc
new file mode 100644
index 0000000..7bfea39
--- /dev/null
+++ b/src/nb/simple_thread.cc
@@ -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.

+ */

+

+#include "nb/simple_thread.h"

+

+#include "starboard/mutex.h"

+#include "starboard/thread.h"

+#include "starboard/types.h"

+#include "starboard/log.h"

+

+namespace nb {

+

+SimpleThread::SimpleThread(const std::string& name)

+    : thread_(kSbThreadInvalid), name_(name),

+      join_called_(false) {}

+

+SimpleThread::~SimpleThread() {

+  SB_DCHECK(join_called_.load()) << "Join not called on thread.";

+}

+

+void SimpleThread::Start() {

+  SbThreadEntryPoint entry_point = ThreadEntryPoint;

+

+  thread_ = SbThreadCreate(0,                    // default stack_size.

+                           kSbThreadNoPriority,  // default priority.

+                           kSbThreadNoAffinity,  // default affinity.

+                           true,                 // joinable.

+                           name_.c_str(), entry_point, this);

+

+  // SbThreadCreate() above produced an invalid thread handle.

+  SB_DCHECK(thread_ != kSbThreadInvalid);

+  return;

+}

+

+void* SimpleThread::ThreadEntryPoint(void* context) {

+  SimpleThread* this_ptr = static_cast<SimpleThread*>(context);

+  this_ptr->Run();

+  return NULL;

+}

+

+void SimpleThread::Join() {

+  SB_DCHECK(join_called_.load() == false);

+  if (!SbThreadJoin(thread_, NULL)) {

+    SB_DCHECK(false) << "Could not join thread.";

+  }

+  join_called_.store(true);

+}

+

+}  // namespace nb

diff --git a/src/nb/simple_thread.h b/src/nb/simple_thread.h
new file mode 100644
index 0000000..ce3bbfe
--- /dev/null
+++ b/src/nb/simple_thread.h
@@ -0,0 +1,57 @@
+/*

+ * Copyright 2016 Google Inc. All Rights Reserved.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+#ifndef NB_THREAD_H_

+#define NB_THREAD_H_

+

+#include <string>

+

+#include "nb/atomic.h"

+#include "starboard/thread.h"

+#include "starboard/types.h"

+

+namespace nb {

+

+class SimpleThread {

+ public:

+  explicit SimpleThread(const std::string& name);

+  virtual ~SimpleThread();

+

+  // Subclasses should override the Run method.

+  virtual void Run() = 0;

+

+  // Signals to the thread to break out of it's loop.

+  virtual void Cancel() {}

+

+  // Called by the main thread, this will cause Run() to be invoked

+  // on another thread.

+  void Start();

+  // Destroys the threads resources.

+  void Join();

+

+ private:

+  static void* ThreadEntryPoint(void* context);

+

+  const std::string name_;

+  SbThread thread_;

+  atomic_bool join_called_;

+

+  SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);

+};

+

+}  // namespace nb

+

+#endif  // NB_THREAD_H_

diff --git a/src/net/socket/ssl_client_socket_openssl.cc b/src/net/socket/ssl_client_socket_openssl.cc
index 1adcfb5..1505cb0 100644
--- a/src/net/socket/ssl_client_socket_openssl.cc
+++ b/src/net/socket/ssl_client_socket_openssl.cc
@@ -1092,7 +1092,7 @@
     DCHECK(recv_buffer_);
     int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
     // A write into a memory BIO should always succeed.
-    CHECK_EQ(result, ret);
+    DCHECK_EQ(result, ret);
   }
   recv_buffer_ = NULL;
   transport_recv_busy_ = false;
diff --git a/src/starboard/blitter.h b/src/starboard/blitter.h
index e807f63..3ed8258 100644
--- a/src/starboard/blitter.h
+++ b/src/starboard/blitter.h
@@ -513,8 +513,7 @@
 // This function is not thread-safe.
 //
 // |out_pixel_data|: A pointer to a region of memory with a size of
-//                   (surface_height * |pitch_in_bytes| *
-//                   SbBlitterBytesPerPixelForFormat(pixel_format) bytes.
+//                   surface_height * |pitch_in_bytes| bytes.
 SB_EXPORT bool SbBlitterDownloadSurfacePixels(
     SbBlitterSurface surface,
     SbBlitterPixelDataFormat pixel_format,
diff --git a/src/starboard/build/copy_data.py b/src/starboard/build/copy_data.py
index cd1d6a2..c19c020 100644
--- a/src/starboard/build/copy_data.py
+++ b/src/starboard/build/copy_data.py
@@ -14,7 +14,6 @@
 # limitations under the License.
 
 # This file is based on build/copy_test_data_ios.py
-
 """Copies data files or directories into a given output directory.
 
 Since the output of this script is intended to be use by GYP, all resulting
@@ -29,6 +28,12 @@
 if os.name == 'nt':
   import win32api
 
+# The name of an environment variable that when set to |'1'|, signals to us
+# that we should log all output directories that we have staged a copy to to
+# our stderr output. This output could then, for example, be captured and
+# analyzed by an external tool that is interested in these directories.
+_ShouldLogEnvKey = 'STARBOARD_GYP_SHOULD_LOG_COPIES'
+
 
 class WrongNumberOfArgumentsException(Exception):
   pass
@@ -99,14 +104,27 @@
   """Called by GYP using pymod_do_main."""
   parser = argparse.ArgumentParser()
   parser.add_argument('-o', dest='output_dir', help='output directory')
-  parser.add_argument('--inputs', action='store_true', dest='list_inputs',
-                      help='prints a list of all input files')
-  parser.add_argument('--outputs', action='store_true', dest='list_outputs',
-                      help='prints a list of all output files')
-  parser.add_argument('input_paths', metavar='path', nargs='+',
-                      help='path to an input file or directory')
+  parser.add_argument(
+      '--inputs',
+      action='store_true',
+      dest='list_inputs',
+      help='prints a list of all input files')
+  parser.add_argument(
+      '--outputs',
+      action='store_true',
+      dest='list_outputs',
+      help='prints a list of all output files')
+  parser.add_argument(
+      'input_paths',
+      metavar='path',
+      nargs='+',
+      help='path to an input file or directory')
   options = parser.parse_args(argv)
 
+  if os.environ.get(_ShouldLogEnvKey, None) == '1':
+    if options.output_dir is not None:
+      print >> sys.stderr, 'COPY_LOG:', options.output_dir
+
   escaped_files = [EscapePath(x) for x in options.input_paths]
   files_to_copy = CalcInputs(escaped_files)
   if options.list_inputs:
@@ -133,5 +151,6 @@
     print result
   return 0
 
+
 if __name__ == '__main__':
   sys.exit(main(sys.argv))
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 838b5e8..d21e420 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -405,20 +405,6 @@
 #error "Your platform must define SB_HAS_THREAD_PRIORITY_SUPPORT."
 #endif
 
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-
-#if !defined(SB_HAS_REAL_TIME_PRIORITY_SUPPORT)
-#error "Your platform must define SB_HAS_REAL_TIME_PRIORITY_SUPPORT."
-#endif
-
-#else  // SB_HAS(THREAD_PRIORITY_SUPPORT)
-
-#if defined(SB_HAS_REAL_TIME_PRIORITY_SUPPORT)
-#error "Don't define SB_HAS_REAL_TIME_PRIORITY_SUPPORT without priorities."
-#endif
-
-#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
-
 #if !defined(SB_PREFERRED_RGBA_BYTE_ORDER)
 // Legal values for SB_PREFERRED_RGBA_BYTE_ORDER are defined in this file above
 // as SB_PREFERRED_RGBA_BYTE_ORDER_*.
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index 34be5d0..d061e46 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -192,6 +192,7 @@
         '<(DEPTH)/starboard/shared/pthread/once.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
         '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h
index bcf356a..4e572ea 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/creator/shared/configuration_public.h
@@ -148,7 +148,7 @@
 // scheduling on threads.
 #define SB_HAS_THREAD_PRIORITY_SUPPORT 0
 
-// --- Attribute Configuration -----------------------------------------------
+// --- Compiler Configuration ------------------------------------------------
 
 // The platform's annotation for forcing a C function to be inlined.
 #define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 1030459..ff6149d 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -178,6 +178,7 @@
       '<(DEPTH)/starboard/shared/pthread/once.cc',
       '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
       '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+      '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
       '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
       '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
       '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform.gyp b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
index 0f82c74..94f15db 100644
--- a/src/starboard/linux/x64directfb/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
@@ -22,6 +22,9 @@
         '<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc',
         '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
         '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_blitter.cc',
+        '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
+        '<(DEPTH)/starboard/shared/stub/image_decode.cc',
+        '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
       ],
       'defines': [
         # This must be defined when building Starboard, and must not when
@@ -31,7 +34,6 @@
       'dependencies': [
         '<(DEPTH)/starboard/common/common.gyp:common',
         '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_platform',
-        '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_base_symbolize',
         '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
         '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
       ],
diff --git a/src/starboard/linux/x64x11/sanitizer_options.cc b/src/starboard/linux/x64x11/sanitizer_options.cc
index 91d1d26..2ac2410 100644
--- a/src/starboard/linux/x64x11/sanitizer_options.cc
+++ b/src/starboard/linux/x64x11/sanitizer_options.cc
@@ -32,6 +32,9 @@
   __attribute__((weak))                   \
   __attribute__((used))
 
+// Newline separated list of issues to suppress, see
+// 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";
 }
diff --git a/src/starboard/nplb/microphone_get_speech_api_key_test.cc b/src/starboard/nplb/microphone_get_speech_api_key_test.cc
deleted file mode 100644
index 67b5347..0000000
--- a/src/starboard/nplb/microphone_get_speech_api_key_test.cc
+++ /dev/null
@@ -1,33 +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/microphone.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
-
-TEST(SbMicrophoneGetSpeechApiKeyTest, SunnyDay) {
-  const char* speech_api_key = SbMicrophoneGetSpeechApiKey();
-
-  ASSERT_NE(speech_api_key, static_cast<const char*>(NULL));
-  EXPECT_NE(speech_api_key[0], '\0');
-}
-
-#endif  // SB_HAS(MICROPHONE) && SB_VERSION(2)
-
-}  // namespace nplb
-}  // namespace starboard
diff --git a/src/starboard/raspi/1/starboard_platform.gyp b/src/starboard/raspi/1/starboard_platform.gyp
index 4f3603f..fb203a6 100644
--- a/src/starboard/raspi/1/starboard_platform.gyp
+++ b/src/starboard/raspi/1/starboard_platform.gyp
@@ -41,6 +41,7 @@
         '<(DEPTH)/starboard/raspi/shared/open_max/open_max_component.h',
         '<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.cc',
         '<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.h',
+        '<(DEPTH)/starboard/raspi/shared/thread_create_priority.cc',
         '<(DEPTH)/starboard/raspi/shared/window_create.cc',
         '<(DEPTH)/starboard/raspi/shared/window_destroy.cc',
         '<(DEPTH)/starboard/raspi/shared/window_get_platform_handle.cc',
@@ -202,6 +203,7 @@
         '<(DEPTH)/starboard/shared/pthread/once.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+        '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
         '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
         '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 0d4b088..ba25c7f 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -78,7 +78,7 @@
 // broken in that higher priority threads do not always have priority over lower
 // priority threads.  It looks like the thread created last will always have the
 // highest priority.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 1
 
 // --- Attribute Configuration -----------------------------------------------
 
diff --git a/src/starboard/raspi/shared/thread_create_priority.cc b/src/starboard/raspi/shared/thread_create_priority.cc
new file mode 100644
index 0000000..b36cd1f
--- /dev/null
+++ b/src/starboard/raspi/shared/thread_create_priority.cc
@@ -0,0 +1,71 @@
+// 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/shared/pthread/thread_create_priority.h"
+
+#include <sched.h>
+
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+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.
+
+  // 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);
+      break;
+    case kSbThreadNoPriority:
+    case kSbThreadPriorityNormal:
+      result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
+      break;
+    case kSbThreadPriorityHigh:
+      thread_sched_param.sched_priority = 1;
+      result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+      break;
+    case kSbThreadPriorityHighest:
+      thread_sched_param.sched_priority = 2;
+      result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+      break;
+    case kSbThreadPriorityRealTime:
+      thread_sched_param.sched_priority = 3;
+      result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+      break;
+    default:
+      SB_NOTREACHED();
+      break;
+  }
+
+  if (result != 0) {
+    SB_NOTREACHED();
+  }
+}
+#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
+
+}  // namespace pthread
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/pthread/thread_create.cc b/src/starboard/shared/pthread/thread_create.cc
index f2ab13d..6cc9028 100644
--- a/src/starboard/shared/pthread/thread_create.cc
+++ b/src/starboard/shared/pthread/thread_create.cc
@@ -21,68 +21,25 @@
 
 #include "starboard/log.h"
 #include "starboard/shared/pthread/is_success.h"
+#include "starboard/shared/pthread/thread_create_priority.h"
 #include "starboard/string.h"
 
-#if SB_HAS(THREAD_PRIORITY_SUPPORT) && SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-#if !defined(_POSIX_PRIORITY_SCHEDULING)
-#error "The _POSIX_PRIORITY_SCHEDULING define indicates that a pthreads \
-system supports thread priorities, however this define is not \
-defined on this system, contradicting the Starboard configuration \
-indicating that priority scheduling is supported."
-#endif  // !defined(_POSIX_PRIORITY_SCHEDULING)
-#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT) && SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+namespace starboard {
+namespace shared {
+namespace pthread {
+
+#if !SB_HAS(THREAD_PRIORITY_SUPPORT)
+// Default implementation without thread priority support
+void ThreadSetPriority(SbThreadPriority /* priority */) {
+}
+#endif
+
+}  // namespace pthread
+}  // namespace shared
+}  // namespace starboard
 
 namespace {
 
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-#if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-
-int SbThreadPriorityToPthread(SbThreadPriority priority) {
-  switch (priority) {
-    case kSbThreadPriorityLowest:
-      SB_NOTREACHED() << "Lowest priority threads should use SCHED_OTHER.";
-      return 0;
-      break;
-    case kSbThreadPriorityLow: return 2;
-    case kSbThreadNoPriority:
-    // Fall through on purpose to default to kThreadPriority_Normal.
-    case kSbThreadPriorityNormal: return 3;
-    case kSbThreadPriorityHigh: return 4;
-    case kSbThreadPriorityHighest: return 5;
-    case kSbThreadPriorityRealTime: return 6;
-    default:
-      SB_NOTREACHED();
-      return 0;
-  }
-}
-
-#else  // SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-
-int SbThreadPriorityToNice(SbThreadPriority priority) {
-  switch (priority) {
-    case kSbThreadPriorityLowest:
-      return 19;
-    case kSbThreadPriorityLow:
-      return 18;
-    case kSbThreadNoPriority:
-    // Fall through on purpose to default to kThreadPriority_Normal.
-    case kSbThreadPriorityNormal:
-      return 10;
-    case kSbThreadPriorityHigh:
-      return 2;
-    case kSbThreadPriorityHighest:
-      return 1;
-    case kSbThreadPriorityRealTime:
-      return 0;
-    default:
-      SB_NOTREACHED();
-      return 0;
-  }
-}
-
-#endif  // #if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-#endif  // #if SB_HAS(THREAD_PRIORITY_SUPPORT)
-
 struct ThreadParams {
   SbThreadAffinity affinity;
   SbThreadEntryPoint entry_point;
@@ -100,26 +57,7 @@
     SbThreadSetName(thread_params->name);
   }
 
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-#if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-  // Use Linux' regular scheduler for lowest priority threads.  Real-time
-  // priority threads (of any priority) will always have priority over
-  // non-real-time threads (e.g. threads whose scheduler is setup to be
-  // SCHED_OTHER, the default scheduler).
-  if (thread_params->priority != kSbThreadPriorityLowest) {
-    // Note that use of sched_setscheduler() has been found to be more reliably
-    // supported than pthread_setschedparam(), so we are using that.
-    struct sched_param thread_sched_param;
-    thread_sched_param.sched_priority =
-        SbThreadPriorityToPthread(thread_params->priority);
-    sched_setscheduler(0, SCHED_FIFO, &thread_sched_param);
-  }
-#else   // #if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-  // If we don't have real time thread priority support, then set the nice
-  // value instead for soft priority support.
-  setpriority(PRIO_PROCESS, 0, SbThreadPriorityToNice(thread_params->priority));
-#endif  // SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
+  starboard::shared::pthread::ThreadSetPriority(thread_params->priority);
 
   delete thread_params;
 
diff --git a/src/starboard/shared/pthread/thread_create_priority.h b/src/starboard/shared/pthread/thread_create_priority.h
new file mode 100644
index 0000000..1c67ef5
--- /dev/null
+++ b/src/starboard/shared/pthread/thread_create_priority.h
@@ -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.
+
+#ifndef STARBOARD_SHARED_PTHREAD_THREAD_CREATE_PRIORITY_H_
+#define STARBOARD_SHARED_PTHREAD_THREAD_CREATE_PRIORITY_H_
+
+#include "starboard/thread.h"
+
+namespace starboard {
+namespace shared {
+namespace pthread {
+
+// Set priority of the current thread.
+//
+// Implement this in a platform-specific thread_create_priority.cc if the
+// platform SB_HAS(THREAD_PRIORITY_SUPPORT)
+void ThreadSetPriority(SbThreadPriority priority);
+
+}  // namespace pthread
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_PTHREAD_THREAD_CREATE_PRIORITY_H_
diff --git a/src/starboard/shared/stub/microphone_get_speech_api_key.cc b/src/starboard/shared/stub/microphone_get_speech_api_key.cc
deleted file mode 100644
index 98038b4..0000000
--- a/src/starboard/shared/stub/microphone_get_speech_api_key.cc
+++ /dev/null
@@ -1,23 +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/microphone.h"
-
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
-
-const char* SbMicrophoneGetSpeechApiKey() {
-  return "";
-}
-
-#endif  // SB_HAS(MICROPHONE) && SB_VERSION(2)
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index 036175e..c342ff9 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -148,7 +148,7 @@
 #define SB_IS_WCHAR_T_SIGNED 1
 #endif
 
-// --- Attribute Configuration -----------------------------------------------
+// --- Compiler Configuration ------------------------------------------------
 
 // The platform's annotation for forcing a C function to be inlined.
 #define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
@@ -169,6 +169,11 @@
 // the current linking unit.
 #define SB_IMPORT_PLATFORM
 
+// On some platforms the __GNUC__ is defined even though parts of the
+// functionality are missing. Setting this to non-zero allows disabling missing
+// functionality encountered.
+#undef SB_HAS_QUIRK_COMPILER_SAYS_GNUC_BUT_ISNT
+
 // --- Extensions Configuration ----------------------------------------------
 
 // GCC/Clang doesn't define a long long hash function, except for Android and
diff --git a/src/third_party/icu/source/common/cmemory.c b/src/third_party/icu/source/common/cmemory.c
index bef32cb..231a79c 100644
--- a/src/third_party/icu/source/common/cmemory.c
+++ b/src/third_party/icu/source/common/cmemory.c
@@ -67,7 +67,7 @@
             (*pFree)(pContext, buffer);
         } else {
 #if defined(STARBOARD)
-            SbMemoryFree(buffer);
+            SbMemoryDeallocate(buffer);
 #else
             free(buffer);
 #endif
@@ -94,7 +94,7 @@
             (*pFree)(pContext, buffer);
         } else {
 #if defined(STARBOARD)
-            SbMemoryFree(buffer);
+            SbMemoryDeallocate(buffer);
 #else
             free(buffer);
 #endif
diff --git a/src/third_party/mozjs/cobalt_config/include/js-confdefs.h b/src/third_party/mozjs/cobalt_config/include/js-confdefs.h
index 28f22d6..6a5d596 100644
--- a/src/third_party/mozjs/cobalt_config/include/js-confdefs.h
+++ b/src/third_party/mozjs/cobalt_config/include/js-confdefs.h
@@ -8,7 +8,7 @@
 
 #include "starboard/configuration.h"
 
-#if SB_IS(64_BIT)
+#if SB_HAS(64_BIT_POINTERS)
 #define JS_BYTES_PER_WORD 8
 #define JS_BITS_PER_WORD_LOG2 6
 #else
diff --git a/src/third_party/mozjs/js/public/Anchor.h b/src/third_party/mozjs/js/public/Anchor.h
index 0d458e6..623fbf0 100644
--- a/src/third_party/mozjs/js/public/Anchor.h
+++ b/src/third_party/mozjs/js/public/Anchor.h
@@ -11,6 +11,10 @@
 
 #include "mozilla/Attributes.h"
 
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#endif
+
 class JSFunction;
 class JSObject;
 class JSScript;
@@ -110,10 +114,16 @@
     void operator=(const Anchor &other) MOZ_DELETE;
 };
 
+#if defined(STARBOARD)
+#if SB_HAS_QUIRK(COMPILER_SAYS_GNUC_BUT_ISNT)
+#define ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND
+#endif
+#endif
+
 template<typename T>
 inline Anchor<T>::~Anchor()
 {
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND)
     /*
      * No code is generated for this. But because this is marked 'volatile', G++ will
      * assume it has important side-effects, and won't delete it. (G++ never looks at
@@ -154,7 +164,7 @@
      */
     volatile T sink;
     sink = hold;
-#endif  /* defined(__GNUC__) */
+#endif  /* defined(__GNUC__) && !defined(ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND) */
 }
 
 } // namespace JS
diff --git a/src/third_party/mozjs/js/public/CharacterEncoding.h b/src/third_party/mozjs/js/public/CharacterEncoding.h
index 7065a4b..29f55af 100644
--- a/src/third_party/mozjs/js/public/CharacterEncoding.h
+++ b/src/third_party/mozjs/js/public/CharacterEncoding.h
@@ -127,7 +127,7 @@
     TwoByteCharsZ(jschar *chars, size_t length)
       : Base(chars, length)
     {
-        JS_ASSERT(chars[length] = '\0');
+        JS_ASSERT(chars[length] == '\0');
     }
 };
 
diff --git a/src/third_party/mozjs/js/src/frontend/Parser.h b/src/third_party/mozjs/js/src/frontend/Parser.h
index f0a7fb8..1c6e0e9 100644
--- a/src/third_party/mozjs/js/src/frontend/Parser.h
+++ b/src/third_party/mozjs/js/src/frontend/Parser.h
@@ -326,7 +326,11 @@
            LazyScript *lazyOuterFunction);
     ~Parser();
 
+#if defined(STARBOARD)
+    friend void MarkParser(JSTracer *trc, AutoGCRooter *parser);
+#else
     friend void js::frontend::MarkParser(JSTracer *trc, AutoGCRooter *parser);
+#endif
 
     const char *getFilename() const { return tokenStream.getFilename(); }
     JSVersion versionNumber() const { return tokenStream.versionNumber(); }
diff --git a/src/third_party/mozjs/js/src/frontend/TokenStream.cpp b/src/third_party/mozjs/js/src/frontend/TokenStream.cpp
index 02da46f..21866fc 100644
--- a/src/third_party/mozjs/js/src/frontend/TokenStream.cpp
+++ b/src/third_party/mozjs/js/src/frontend/TokenStream.cpp
@@ -675,7 +675,7 @@
 
     err.argumentsType = (flags & JSREPORT_UC) ? ArgumentsAreUnicode : ArgumentsAreASCII;
 
-    if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &err.message,
+    if (!js_ExpandErrorArgumentsVA(cx, js_GetErrorMessage, NULL, errorNumber, &err.message,
                                  &err.report, err.argumentsType, args))
     {
         return false;
diff --git a/src/third_party/mozjs/js/src/jsapi.cpp b/src/third_party/mozjs/js/src/jsapi.cpp
index 85b9856..4aa997b 100644
--- a/src/third_party/mozjs/js/src/jsapi.cpp
+++ b/src/third_party/mozjs/js/src/jsapi.cpp
@@ -5001,6 +5001,63 @@
 
 typedef Vector<char, 8, TempAllocPolicy> FileContents;
 
+#if defined(STARBOARD)
+static bool
+ReadCompleteFile(JSContext *cx, SbFile file, FileContents &buffer)
+{
+    SbFileInfo info;
+    bool success = SbFileGetInfo(file, &info);
+    if (!success) {
+        return false;
+    }
+    const int64_t kFileSize = info.size;
+    buffer.resize(kFileSize);
+    if (SbFileReadAll(file, buffer.begin(), kFileSize) < 0) {
+        return false;
+    }
+
+    return true;
+}
+
+class AutoFile
+{
+    SbFile sb_file_;
+  public:
+    AutoFile() {}
+    ~AutoFile()
+    {
+        SbFileClose(sb_file_);
+    }
+    SbFile sb_file() const { return sb_file_; }
+    bool open(JSContext *cx, const char *filename);
+    bool readAll(JSContext *cx, FileContents &buffer)
+    {
+        return ReadCompleteFile(cx, sb_file_, buffer);
+    }
+};
+
+/*
+ * Open a source file for reading. Supports "-" and NULL to mean stdin. The
+ * return value must be fclosed unless it is stdin.
+ */
+bool
+AutoFile::open(JSContext *cx, const char *filename)
+{
+    // Starboard does not support stdin.
+    if (!filename || strcmp(filename, "-") == 0) {
+        return false;
+    } else {
+        sb_file_ = SbFileOpen(filename, kSbFileOpenOnly | kSbFileRead, NULL,
+            NULL);
+        if (!SbFileIsValid(sb_file_)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
+                                 filename, "No such file or directory");
+            return false;
+        }
+    }
+    return true;
+}
+#else  // #if defined(STARBOARD)
 static bool
 ReadCompleteFile(JSContext *cx, FILE *fp, FileContents &buffer)
 {
@@ -5070,6 +5127,7 @@
     return true;
 }
 
+#endif  // #if defined(STARBOARD)
 
 JS::CompileOptions::CompileOptions(JSContext *cx, JSVersion version)
     : principals(NULL),
diff --git a/src/third_party/mozjs/js/src/jsclone.cpp b/src/third_party/mozjs/js/src/jsclone.cpp
index 8df21bf..6869515 100644
--- a/src/third_party/mozjs/js/src/jsclone.cpp
+++ b/src/third_party/mozjs/js/src/jsclone.cpp
@@ -135,7 +135,8 @@
                 uint32_t tag = uint32_t(u >> 32);
                 if (tag == SCTAG_TRANSFER_MAP) {
                     u = LittleEndian::readUint64(point++);
-                    js_free(reinterpret_cast<void*>(u));
+                    js_free(reinterpret_cast<void*>(
+                            static_cast<uintptr_t>(u)));
                 } else {
                     // The only things in the transfer map should be
                     // SCTAG_TRANSFER_MAP tags paired with pointers. If we find
@@ -312,7 +313,11 @@
     // 32 bits, so we create a 64 temporary and discard the unused bits.
     uint64_t tmp;
     bool ret = read(&tmp);
+#if defined(STARBOARD)
+    *p = reinterpret_cast<void*>(static_cast<uintptr_t>(tmp));
+#else
     *p = reinterpret_cast<void*>(tmp);
+#endif
     return ret;
 }
 
diff --git a/src/third_party/mozjs/js/src/jscntxt.cpp b/src/third_party/mozjs/js/src/jscntxt.cpp
index d145786..24b00bd 100644
--- a/src/third_party/mozjs/js/src/jscntxt.cpp
+++ b/src/third_party/mozjs/js/src/jscntxt.cpp
@@ -743,7 +743,7 @@
  * Returns true if the expansion succeeds (can fail if out of memory).
  */
 JSBool
-js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
+js_ExpandErrorArgumentsVA(JSContext *cx, JSErrorCallback callback,
                         void *userRef, const unsigned errorNumber,
                         char **messagep, JSErrorReport *reportp,
                         ErrorArgumentsType argumentsType, va_list ap)
@@ -922,7 +922,7 @@
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
 
-    if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
+    if (!js_ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
                                  &message, &report, argumentsType, ap)) {
         return JS_FALSE;
     }
@@ -933,7 +933,7 @@
         js_free(message);
     if (report.messageArgs) {
         /*
-         * js_ExpandErrorArguments owns its messageArgs only if it had to
+         * js_ExpandErrorArgumentsVA owns its messageArgs only if it had to
          * inflate the arguments (from regular |char *|s).
          */
         if (argumentsType == ArgumentsAreASCII) {
@@ -949,6 +949,21 @@
     return warning;
 }
 
+static bool
+js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
+                     void *userRef, const unsigned errorNumber,
+                     char **messagep, JSErrorReport *reportp,
+                     ErrorArgumentsType argumentsType, ...)
+{
+    va_list ap;
+    va_start(ap, argumentsType);
+    bool expanded = js_ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
+                                               messagep, reportp, argumentsType, ap);
+    va_end(ap);
+    return expanded;
+}
+
+
 bool
 js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
                             void *userRef, const unsigned errorNumber,
@@ -966,9 +981,8 @@
     report.messageArgs = args;
 
     char *message;
-    va_list dummy;
     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
-                                 &message, &report, ArgumentsAreUnicode, dummy)) {
+                                 &message, &report, ArgumentsAreUnicode)) {
         return false;
     }
 
diff --git a/src/third_party/mozjs/js/src/jscntxt.h b/src/third_party/mozjs/js/src/jscntxt.h
index d918d49..9339c07 100644
--- a/src/third_party/mozjs/js/src/jscntxt.h
+++ b/src/third_party/mozjs/js/src/jscntxt.h
@@ -2165,10 +2165,10 @@
                             const jschar **args);
 
 extern JSBool
-js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
-                        void *userRef, const unsigned errorNumber,
-                        char **message, JSErrorReport *reportp,
-                        js::ErrorArgumentsType argumentsType, va_list ap);
+js_ExpandErrorArgumentsVA(JSContext *cx, JSErrorCallback callback,
+                          void *userRef, const unsigned errorNumber,
+                          char **message, JSErrorReport *reportp,
+                          js::ErrorArgumentsType argumentsType, va_list ap);
 #endif
 
 namespace js {
diff --git a/src/third_party/mozjs/js/src/jslibmath.h b/src/third_party/mozjs/js/src/jslibmath.h
index 20aa19a..d077cbf 100644
--- a/src/third_party/mozjs/js/src/jslibmath.h
+++ b/src/third_party/mozjs/js/src/jslibmath.h
@@ -12,12 +12,23 @@
 #include <math.h>
 #include "jsnum.h"
 
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#endif
+
 /*
  * Use system provided math routines.
  */
 
+#if defined(STARBOARD)
+#if SB_HAS_QUIRK(COMPILER_SAYS_GNUC_BUT_ISNT)
+#define ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND
+#endif
+#endif
+
 /* The right copysign function is not always named the same thing. */
-#ifdef __GNUC__
+// Check for quirk COMPILER_SAYS_GNUC_BUT_ISNT when picking copysign function.
+#if defined(__GNUC__) && !defined(ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND)
 #define js_copysign __builtin_copysign
 #elif defined _WIN32
 #define js_copysign _copysign
@@ -65,7 +76,7 @@
 
 inline double
 NumberMod(double a, double b) {
-    if (b == 0) 
+    if (b == 0)
         return js_NaN;
     return js_fmod(a, b);
 }
diff --git a/src/third_party/mozjs/js/src/jstypedarray.cpp b/src/third_party/mozjs/js/src/jstypedarray.cpp
index 8019563..58f3ac5 100644
--- a/src/third_party/mozjs/js/src/jstypedarray.cpp
+++ b/src/third_party/mozjs/js/src/jstypedarray.cpp
@@ -34,7 +34,9 @@
 
 #include "vm/GlobalObject-inl.h"
 
-# ifdef XP_WIN
+#if defined(STARBOARD)
+// empty
+# elif defined(XP_WIN)
 #  include "jswin.h"
 # else
 #  include <sys/mman.h>
@@ -336,7 +338,7 @@
    return true;
 }
 
-#if defined(JS_ION) && defined(JS_CPU_X64)
+#if defined(JS_ION) && defined(JS_CPU_X64) && !defined(STARBOARD)
 // To avoid dynamically checking bounds on each load/store, asm.js code relies
 // on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
 // if we can guarantee that *any* out-of-bounds access generates a fault. This
@@ -440,7 +442,7 @@
         MOZ_CRASH();
 #endif
 }
-#else  /* defined(JS_ION) && defined(JS_CPU_X64) */
+#else  /* defined(JS_ION) && defined(JS_CPU_X64) && !defined(STARBOARD)*/
 bool
 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
diff --git a/src/third_party/mozjs/js/src/jstypedarray.h b/src/third_party/mozjs/js/src/jstypedarray.h
index 4751d53..b8e1f99 100644
--- a/src/third_party/mozjs/js/src/jstypedarray.h
+++ b/src/third_party/mozjs/js/src/jstypedarray.h
@@ -371,14 +371,22 @@
 public:
     static Class class_;
 
+#if defined(STARBOARD)
+    template<Value ValueGetter(DataViewObject &view)>
+    static bool
+    getterImpl(JSContext *cx, CallArgs args);
+#endif
+
 private:
     static Class protoClass;
 
     static inline bool is(const Value &v);
 
+#if !defined(STARBOARD)
     template<Value ValueGetter(DataViewObject &view)>
     static bool
     getterImpl(JSContext *cx, CallArgs args);
+#endif
 
     template<Value ValueGetter(DataViewObject &view)>
     static JSBool
diff --git a/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp b/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp
index 9b879bb..a7d8efa 100644
--- a/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp
+++ b/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp
@@ -27,7 +27,11 @@
 
 void* OffsetPointer(void* base, int64_t offset) {
   uintptr_t base_as_int = reinterpret_cast<uintptr_t>(base);
-  return reinterpret_cast<void*>(base_as_int + offset);
+#if defined(STARBOARD)
+    return reinterpret_cast<void*>(base_as_int + static_cast<uintptr_t>(offset));
+#else
+    return reinterpret_cast<void*>(base_as_int + offset);
+#endif
 }
 }  // namespace
 
diff --git a/src/third_party/mozjs/js/src/shell/js.cpp b/src/third_party/mozjs/js/src/shell/js.cpp
index 03d0010..5987c08 100644
--- a/src/third_party/mozjs/js/src/shell/js.cpp
+++ b/src/third_party/mozjs/js/src/shell/js.cpp
@@ -69,12 +69,15 @@
 #include <io.h>     /* for isatty() */
 #endif
 
-#ifdef XP_WIN
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#define PATH_MAX (SB_FILE_MAX_PATH + 1)
+#elif defined(XP_WIN)
 # include <io.h>
 # include <direct.h>
 # include "jswin.h"
 # define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
-#elif !defined(STARBOARD)
+#else
 # include <libgen.h>
 #endif
 
diff --git a/src/third_party/mozjs/mfbt/Endian.h b/src/third_party/mozjs/mfbt/Endian.h
index 5d2f905..43d2c17 100644
--- a/src/third_party/mozjs/mfbt/Endian.h
+++ b/src/third_party/mozjs/mfbt/Endian.h
@@ -71,6 +71,10 @@
 
 #include <string.h>
 
+#if defined(STARBOARD)
+#include "starboard/byte_swap.h"
+#endif
+
 #if defined(_MSC_VER) && _MSC_VER >= 1300
 #  include <stdlib.h>
 #  pragma intrinsic(_byteswap_ushort)
@@ -184,7 +188,9 @@
 {
   static T swap(T value)
   {
-#if defined(__clang__) || defined(__GNUC__)
+#if defined(STARBOARD)
+    return static_cast<T>(SbByteSwapU32(value));
+#elif defined(__clang__) || defined(__GNUC__)
     return T(__builtin_bswap32(value));
 #elif defined(_MSC_VER)
     return T(_byteswap_ulong(value));
@@ -202,7 +208,9 @@
 {
   static inline T swap(T value)
   {
-#if defined(__clang__) || defined(__GNUC__)
+#if defined(STARBOARD)
+    return static_cast<T>(SbByteSwapU64(value));
+#elif defined(__clang__) || defined(__GNUC__)
     return T(__builtin_bswap64(value));
 #elif defined(_MSC_VER)
     return T(_byteswap_uint64(value));
diff --git a/src/third_party/mozjs/mfbt/decimal/Decimal.cpp b/src/third_party/mozjs/mfbt/decimal/Decimal.cpp
index de10f83..5af6be1 100644
--- a/src/third_party/mozjs/mfbt/decimal/Decimal.cpp
+++ b/src/third_party/mozjs/mfbt/decimal/Decimal.cpp
@@ -34,6 +34,10 @@
 #include <algorithm>
 #include <float.h>
 
+#if defined(STARBOARD)
+#include "starboard/double.h"
+#endif
+
 namespace WebCore {
 
 namespace DecimalPrivate {
@@ -684,13 +688,19 @@
 
 Decimal Decimal::fromDouble(double doubleValue)
 {
+#if defined(STARBOARD)
+    if (SbDoubleIsFinite(doubleValue))
+        return fromString(mozToString(doubleValue));
+    if (SbDoubleIsNan(doubleValue))
+        return nan();
+    return infinity(doubleValue < 0 ? Negative : Positive);
+#else
     if (std::isfinite(doubleValue))
         return fromString(mozToString(doubleValue));
-
     if (std::isinf(doubleValue))
         return infinity(doubleValue < 0 ? Negative : Positive);
-
     return nan();
+#endif
 }
 
 Decimal Decimal::fromString(const String& str)
diff --git a/src/third_party/mozjs/mozjs.gyp b/src/third_party/mozjs/mozjs.gyp
index 3de7f79..e01f0eb 100644
--- a/src/third_party/mozjs/mozjs.gyp
+++ b/src/third_party/mozjs/mozjs.gyp
@@ -122,6 +122,14 @@
         'js-confdefs.h',
       ],
       'conditions': [
+        # These W flags do not work with the ps3 compiler.
+        ['target_arch == "ps3"', {
+          'cflags!': [
+            '-Wno-invalid-offsetof',
+            '-Wno-uninitialized',
+            '-Wno-unused',
+          ]
+        }],
         [ 'target_arch == "x64" and cobalt_enable_jit == 1', {
           'sources': [
             'js/src/assembler/assembler/MacroAssemblerX86Common.cpp',
@@ -220,6 +228,16 @@
           '-include',
           'js-confdefs.h',
         ],
+        'conditions': [
+          # These W flags do not work with the ps3 compiler.
+          ['target_arch == "ps3"', {
+            'cflags!': [
+              '-Wno-invalid-offsetof',
+              '-Wno-uninitialized',
+              '-Wno-unused',
+            ]
+          }],
+        ],
       },
       # Mark this target as a hard dependency because targets that depend on
       # this one need to wait for the build_include_directory to be generated.
@@ -365,7 +383,7 @@
             'js/src/builtin/embedjs.py',
             '-DUSE_ZLIB',
             '-p',
-            '<(CC) -E',
+            '<(CC_HOST) -E',
             '-m',
             'js/src/js.msg',
             '-o',
diff --git a/src/third_party/openssl/openssl.gyp b/src/third_party/openssl/openssl.gyp
index e84bd8d..484a49e 100644
--- a/src/third_party/openssl/openssl.gyp
+++ b/src/third_party/openssl/openssl.gyp
@@ -225,8 +225,12 @@
         'openssl/crypto/des/cfb64enc.c',
         'openssl/crypto/des/cfb_enc.c',
         'openssl/crypto/des/des_enc.c',
-        'openssl/crypto/des/des_old.c',
-        'openssl/crypto/des/des_old2.c',
+        # All our code uses the uppercase DES_foo() methods, so we don't need
+        # the compatability glue. When linking as a shared library, the unused
+        # glue functions are left as undefined symbols after they get
+        # dead-stripped, so just don't build them.
+        # 'openssl/crypto/des/des_old.c',
+        # 'openssl/crypto/des/des_old2.c',
         'openssl/crypto/des/ecb3_enc.c',
         'openssl/crypto/des/ecb_enc.c',
         'openssl/crypto/des/ede_cbcm_enc.c',