Import Cobalt 11.132145

Change-Id: I61a98ecc60d7e8f59bb80efaa2cce4440e2ef99f
diff --git a/src/base/synchronization/waitable_event_posix.cc b/src/base/synchronization/waitable_event_posix.cc
index 7066aee..e7c1abf 100644
--- a/src/base/synchronization/waitable_event_posix.cc
+++ b/src/base/synchronization/waitable_event_posix.cc
@@ -159,7 +159,7 @@
 
 bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
   base::ThreadRestrictions::AssertWaitAllowed();
-  const Time end_time(Time::Now() + max_time);
+  const TimeTicks end_time(TimeTicks::Now() + max_time);
   const bool finite_time = max_time.ToInternalValue() >= 0;
 
   kernel_->lock_.Acquire();
@@ -184,7 +184,7 @@
   // again before unlocking it.
 
   for (;;) {
-    const Time current_time(Time::Now());
+    const TimeTicks current_time(TimeTicks::Now());
 
     if (sw.fired() || (finite_time && current_time >= end_time)) {
       const bool return_value = sw.fired();
diff --git a/src/base/timer.cc b/src/base/timer.cc
index b2b7954..04b553e 100644
--- a/src/base/timer.cc
+++ b/src/base/timer.cc
@@ -236,9 +236,9 @@
     // Setup member variables and the next tasks before the current one runs as
     // we cannot access any member variables after calling task.Run().
     NewScheduledTaskInfo task_info = SetupNewScheduledTask(delay_);
-    base::Time task_start_time = base::Time::Now();
+    base::TimeTicks task_start_time = base::TimeTicks::Now();
     task.Run();
-    base::TimeDelta task_duration = base::Time::Now() - task_start_time;
+    base::TimeDelta task_duration = base::TimeTicks::Now() - task_start_time;
     if (task_duration >= delay_) {
       PostNewScheduledTask(task_info, base::TimeDelta::FromInternalValue(0));
     } else {
diff --git a/src/cobalt/account/user_authorizer.h b/src/cobalt/account/user_authorizer.h
index a128c0b..863a122 100644
--- a/src/cobalt/account/user_authorizer.h
+++ b/src/cobalt/account/user_authorizer.h
@@ -73,6 +73,10 @@
   // On success, a scoped_ptr holding a valid AccessToken is returned.
   virtual scoped_ptr<AccessToken> RefreshAuthorization(SbUser user) = 0;
 
+  // Signals that the account manager is shutting down, and unblocks any pending
+  // request. Calling other methods after |Shutdown| may have no effect.
+  virtual void Shutdown() {}
+
   // Instantiates an instance of the platform-specific implementation.
   static UserAuthorizer* Create();
 
diff --git a/src/cobalt/base/clock.h b/src/cobalt/base/clock.h
index b3d418b..8867f4e 100644
--- a/src/cobalt/base/clock.h
+++ b/src/cobalt/base/clock.h
@@ -49,6 +49,31 @@
   ~SystemMonotonicClock() OVERRIDE {}
 };
 
+// The MinimumResolutionClock modifies the output of an existing clock by
+// clamping its minimum resolution to a predefined amount.  This is implemented
+// by rounding down the existing clock's time to the previous multiple of the
+// desired clock resolution.
+class MinimumResolutionClock : public Clock {
+ public:
+  MinimumResolutionClock(scoped_refptr<Clock> parent,
+                         const base::TimeDelta& min_resolution)
+      : parent_(parent),
+        min_resolution_in_microseconds_(min_resolution.InMicroseconds()) {
+    DCHECK(parent);
+  }
+
+  base::TimeDelta Now() override {
+    base::TimeDelta now = parent_->Now();
+    int64 microseconds = now.InMicroseconds();
+    return base::TimeDelta::FromMicroseconds(
+        microseconds - (microseconds % min_resolution_in_microseconds_));
+  }
+
+ private:
+  scoped_refptr<Clock> parent_;
+  const int64_t min_resolution_in_microseconds_;
+};
+
 // The OffsetClock takes a parent clock and an offset upon construction, and
 // when queried for the time it returns the time of the parent clock offset by
 // the specified offset.
diff --git a/src/cobalt/base/language.cc b/src/cobalt/base/language.cc
index 93869a1..fc72003 100644
--- a/src/cobalt/base/language.cc
+++ b/src/cobalt/base/language.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/base/language.h"
 
+#include <algorithm>
+
 #include "base/basictypes.h"
 #include "base/logging.h"
 #include "third_party/icu/source/common/unicode/uloc.h"
@@ -42,4 +44,28 @@
   // We should end up with something like "en" or "en-US".
   return language;
 }
+
+std::string GetSystemLanguageScript() {
+  char buffer[ULOC_LANG_CAPACITY];
+  UErrorCode icu_result = U_ZERO_ERROR;
+
+  // Combine the ISO language and script.
+  uloc_getLanguage(NULL, buffer, arraysize(buffer), &icu_result);
+  if (!U_SUCCESS(icu_result)) {
+    DLOG(FATAL) << __FUNCTION__ << ": Unable to get language from ICU for "
+                << "default locale " << uloc_getDefault() << ".";
+    return "en";
+  }
+
+  std::string language = buffer;
+  uloc_getScript(NULL, buffer, arraysize(buffer), &icu_result);
+  if (U_SUCCESS(icu_result) && buffer[0]) {
+    language += "-";
+    language += buffer;
+  }
+
+  // We should end up with something like "en" or "en-Latn".
+  return language;
+}
+
 }  // namespace base
diff --git a/src/cobalt/base/language.h b/src/cobalt/base/language.h
index 174035f..4e59a72 100644
--- a/src/cobalt/base/language.h
+++ b/src/cobalt/base/language.h
@@ -19,12 +19,18 @@
 
 namespace base {
 
-// Gets the system language.
+// Gets the system language and ISO 3166-1 country code.
 // NOTE: should be in the format described by bcp47.
 // http://www.rfc-editor.org/rfc/bcp/bcp47.txt
 // Example: "en-US" or "de"
 std::string GetSystemLanguage();
 
+// Gets the system language and ISO 15924 script code.
+// NOTE: should be in the format described by bcp47.
+// http://www.rfc-editor.org/rfc/bcp/bcp47.txt
+// Example: "en-US" or "de"
+std::string GetSystemLanguageScript();
+
 }  // namespace base
 
 #endif  // COBALT_BASE_LANGUAGE_H_
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 35b7f2a..3e864d4 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -475,7 +475,6 @@
   // Create the main components of our browser.
   BrowserModule::Options options(web_options);
   options.web_module_options.name = "MainWebModule";
-  options.language = language;
   options.initial_deep_link = GetInitialDeepLink();
   options.network_module_options.preferred_language = language;
   options.command_line_auto_mem_settings =
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 5153bbb..01ece66 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -226,6 +226,7 @@
       can_play_type_handler_(media::MediaModule::CreateCanPlayTypeHandler()),
       network_module_(&storage_manager_, event_dispatcher_,
                       options_.network_module_options),
+      splash_screen_cache_(new SplashScreenCache()),
       web_module_loaded_(true /* manually_reset */,
                          false /* initially_signalled */),
       web_module_recreated_callback_(options_.web_module_recreated_callback),
@@ -259,7 +260,6 @@
       waiting_for_error_retry_(false),
       will_quit_(false),
       application_state_(initial_application_state),
-      splash_screen_cache_(new SplashScreenCache()),
       main_web_module_generation_(0),
       next_timeline_id_(1),
       current_splash_screen_timeline_id_(-1),
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index a2ef629..db86c6d 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -84,7 +84,6 @@
     storage::StorageManager::Options storage_manager_options;
     WebModule::Options web_module_options;
     media::MediaModule::Options media_module_options;
-    std::string language;
     std::string initial_deep_link;
     base::Closure web_module_recreated_callback;
     memory_settings::AutoMemSettings command_line_auto_mem_settings;
@@ -424,6 +423,9 @@
   // that may be producing render trees.
   base::MessageQueue render_tree_submission_queue_;
 
+  // The splash screen cache.
+  scoped_ptr<SplashScreenCache> splash_screen_cache_;
+
   // Sets up everything to do with web page management, from loading and
   // parsing the web page and all referenced files to laying it out.  The
   // web module will ultimately produce a render tree that can be passed
@@ -535,9 +537,6 @@
   // screen will be displayed.
   base::optional<GURL> fallback_splash_screen_url_;
 
-  // The splash screen cache.
-  scoped_ptr<SplashScreenCache> splash_screen_cache_;
-
   // Number of main web modules that have take place so far, helpful for
   // ditinguishing lingering events produced by older web modules as we switch
   // from one to another.  This is incremented with each navigation.
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 54bf81a..87cfbf3 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -26,6 +26,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop_proxy.h"
 #include "base/stringprintf.h"
+#include "cobalt/base/language.h"
 #include "cobalt/base/startup_timer.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/base/type_id.h"
@@ -543,6 +544,9 @@
       web_module_stat_tracker_->dom_stat_tracker(), data.initial_url,
       data.network_module->GetUserAgent(),
       data.network_module->preferred_language(),
+      data.options.font_language_script_override.empty()
+          ? base::GetSystemLanguageScript()
+          : data.options.font_language_script_override,
       data.options.navigation_callback,
       base::Bind(&WebModule::Impl::OnError, base::Unretained(this)),
       data.network_module->cookie_jar(), data.network_module->GetPostSender(),
@@ -983,6 +987,8 @@
     return;
   }
 
+  layout_manager_->Purge();
+
   // Retain the remote typeface cache when reducing memory.
   PurgeResourceCaches(true /*should_retain_remote_typeface_cache*/);
   window_->document()->PurgeCachedResources();
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index da3a9c1..b41e8c9 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -187,6 +187,11 @@
     // the suspend state.
     bool should_retain_remote_typeface_cache_on_suspend;
 
+    // The language and script to use with fonts. If left empty, then the
+    // language-script combination provided by base::GetSystemLanguageScript()
+    // is used.
+    std::string font_language_script_override;
+
     // The splash screen cache object, owned by the BrowserModule.
     SplashScreenCache* splash_screen_cache;
 
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 9ce0c99..ad8eeb1 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-119147
\ No newline at end of file
+132145
\ No newline at end of file
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
index 5c3447b..6a834a1 100644
--- a/src/cobalt/dom/custom_event_test.cc
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -63,7 +63,8 @@
             1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
             dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
             NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL,
-            NULL, NULL, url_, "", "en-US", base::Callback<void(const GURL&)>(),
+            NULL, NULL, url_, "", "en-US", "en",
+            base::Callback<void(const GURL&)>(),
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index fa07b55..cc2cc92 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -123,7 +123,7 @@
       html_element_context_->resource_provider(),
       html_element_context_->remote_typeface_cache(),
       base::Bind(&Document::OnTypefaceLoadEvent, base::Unretained(this)),
-      html_element_context_->language()));
+      html_element_context_->font_language_script()));
 
   if (HasBrowsingContext()) {
     if (html_element_context_->remote_typeface_cache()) {
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
index a115fb8..105ec2e 100644
--- a/src/cobalt/dom/error_event_test.cc
+++ b/src/cobalt/dom/error_event_test.cc
@@ -63,7 +63,8 @@
             1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
             dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
             NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL,
-            NULL, NULL, url_, "", "en-US", base::Callback<void(const GURL&)>(),
+            NULL, NULL, url_, "", "en-US", "en",
+            base::Callback<void(const GURL&)>(),
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index 4fff414..d101291 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -43,12 +43,12 @@
 FontCache::FontCache(render_tree::ResourceProvider** resource_provider,
                      loader::font::RemoteTypefaceCache* remote_typeface_cache,
                      const base::Closure& external_typeface_load_event_callback,
-                     const std::string& language)
+                     const std::string& language_script)
     : resource_provider_(resource_provider),
       remote_typeface_cache_(remote_typeface_cache),
       external_typeface_load_event_callback_(
           external_typeface_load_event_callback),
-      language_(language),
+      language_script_(language_script),
       font_face_map_(new FontFaceMap()),
       last_inactive_process_time_(base::TimeTicks::Now()) {}
 
@@ -92,13 +92,32 @@
 
 void FontCache::PurgeCachedResources() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  font_face_map_->clear();
-  font_list_map_.clear();
-  inactive_font_set_.clear();
-  font_map_.clear();
-  character_fallback_typeface_maps_.clear();
   requested_remote_typeface_cache_.clear();
+
+  // Remove all font lists that are unreferenced outside of the cache and reset
+  // those that are retained to their initial state.
+  for (FontListMap::iterator iter = font_list_map_.begin();
+       iter != font_list_map_.end();) {
+    FontListInfo& font_list_info = iter->second;
+    if (font_list_info.font_list->HasOneRef()) {
+      font_list_map_.erase(iter++);
+      continue;
+    }
+    DLOG(WARNING) << "Unable to purge font list!";
+    font_list_info.font_list->Reset();
+    ++iter;
+  }
+
   local_typeface_map_.clear();
+  font_map_.clear();
+  inactive_font_set_.clear();
+
+  // Walk the character fallback maps, clearing their typefaces.
+  for (CharacterFallbackTypefaceMaps::iterator iter =
+           character_fallback_typeface_maps_.begin();
+       iter != character_fallback_typeface_maps_.end(); ++iter) {
+    iter->second.clear();
+  }
 }
 
 void FontCache::ProcessInactiveFontListsAndFonts() {
@@ -192,7 +211,7 @@
   DCHECK(resource_provider());
   return GetCachedLocalTypeface(
       resource_provider()->GetCharacterFallbackTypeface(utf32_character, style,
-                                                        language_));
+                                                        language_script_));
 }
 
 scoped_refptr<render_tree::GlyphBuffer> FontCache::CreateGlyphBuffer(
@@ -200,7 +219,7 @@
     FontList* font_list) {
   DCHECK(resource_provider());
   return resource_provider()->CreateGlyphBuffer(
-      text_buffer, static_cast<size_t>(text_length), language_, is_rtl,
+      text_buffer, static_cast<size_t>(text_length), language_script_, is_rtl,
       font_list);
 }
 
@@ -209,7 +228,7 @@
                               render_tree::FontVector* maybe_used_fonts) {
   DCHECK(resource_provider());
   return resource_provider()->GetTextWidth(
-      text_buffer, static_cast<size_t>(text_length), language_, is_rtl,
+      text_buffer, static_cast<size_t>(text_length), language_script_, is_rtl,
       font_list, maybe_used_fonts);
 }
 
@@ -340,14 +359,13 @@
     *state = FontListFont::kLoadedState;
     return GetFontFromTypefaceAndSize(typeface, size);
   } else {
-    if (cached_remote_typeface->IsLoading()) {
-      if (requested_remote_typeface_iterator->second->HasActiveRequestTimer()) {
-        *state = FontListFont::kLoadingWithTimerActiveState;
-      } else {
-        *state = FontListFont::kLoadingWithTimerExpiredState;
-      }
-    } else {
+    if (cached_remote_typeface->IsLoadingComplete()) {
       *state = FontListFont::kUnavailableState;
+    } else if (requested_remote_typeface_iterator->second
+                   ->HasActiveRequestTimer()) {
+      *state = FontListFont::kLoadingWithTimerActiveState;
+    } else {
+      *state = FontListFont::kLoadingWithTimerExpiredState;
     }
     return NULL;
   }
diff --git a/src/cobalt/dom/font_cache.h b/src/cobalt/dom/font_cache.h
index e90adf9..a0d5761 100644
--- a/src/cobalt/dom/font_cache.h
+++ b/src/cobalt/dom/font_cache.h
@@ -173,7 +173,7 @@
   FontCache(render_tree::ResourceProvider** resource_provider,
             loader::font::RemoteTypefaceCache* remote_typeface_cache,
             const base::Closure& external_typeface_load_event_callback,
-            const std::string& language);
+            const std::string& language_script);
 
   // Set a new font face map. If it matches the old font face map then nothing
   // is done. Otherwise, it is updated with the new value and the remote
@@ -283,7 +283,7 @@
   // logic into the font cache when the loader interface improves.
   loader::font::RemoteTypefaceCache* const remote_typeface_cache_;
   const base::Closure external_typeface_load_event_callback_;
-  const std::string language_;
+  const std::string language_script_;
 
   // Font-face related
   // The cache contains a map of font faces and handles requesting typefaces by
diff --git a/src/cobalt/dom/font_cache_test.cc b/src/cobalt/dom/font_cache_test.cc
index 0f1f1dd..50b1c5e 100644
--- a/src/cobalt/dom/font_cache_test.cc
+++ b/src/cobalt/dom/font_cache_test.cc
@@ -79,6 +79,7 @@
           &mock_resource_provider_)),
       rtc(new loader::font::RemoteTypefaceCache(
           "test_cache", 32 * 1024 /* 32 KB */,
+          true /*are_loading_retries_enabled*/,
           base::Bind(&loader::MockLoaderFactory::CreateTypefaceLoader,
                      base::Unretained(&loader_factory_)))),
       font_cache_(
diff --git a/src/cobalt/dom/font_list.cc b/src/cobalt/dom/font_list.cc
index 04743c0..f2d44ef 100644
--- a/src/cobalt/dom/font_list.cc
+++ b/src/cobalt/dom/font_list.cc
@@ -47,22 +47,20 @@
   fonts_.push_back(FontListFont(""));
 }
 
-bool FontList::IsVisible() const {
+void FontList::Reset() {
   for (size_t i = 0; i < fonts_.size(); ++i) {
-    // While any font in the font list is loading with an active timer, the font
-    // is made transparent. "In cases where textual content is loaded before
-    // downloadable fonts are available, user agents may... render text
-    // transparently with fallback fonts to avoid a flash of  text using a
-    // fallback font. In cases where the font download fails user agents must
-    // display text, simply leaving transparent text is considered
-    // non-conformant behavior."
-    //   https://www.w3.org/TR/css3-fonts/#font-face-loading
-    if (fonts_[i].state() == FontListFont::kLoadingWithTimerActiveState) {
-      return false;
-    }
+    FontListFont& font_list_font = fonts_[i];
+    font_list_font.set_state(FontListFont::kUnrequestedState);
+    font_list_font.set_font(NULL);
   }
 
-  return true;
+  primary_font_ = NULL;
+  is_font_metrics_set_ = false;
+  is_space_width_set_ = false;
+  is_ellipsis_info_set_ = false;
+  ellipsis_font_ = NULL;
+
+  fallback_typeface_to_font_map_.clear();
 }
 
 void FontList::ResetLoadingFonts() {
@@ -89,6 +87,24 @@
   }
 }
 
+bool FontList::IsVisible() const {
+  for (size_t i = 0; i < fonts_.size(); ++i) {
+    // While any font in the font list is loading with an active timer, the font
+    // is made transparent. "In cases where textual content is loaded before
+    // downloadable fonts are available, user agents may... render text
+    // transparently with fallback fonts to avoid a flash of  text using a
+    // fallback font. In cases where the font download fails user agents must
+    // display text, simply leaving transparent text is considered
+    // non-conformant behavior."
+    //   https://www.w3.org/TR/css3-fonts/#font-face-loading
+    if (fonts_[i].state() == FontListFont::kLoadingWithTimerActiveState) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 scoped_refptr<render_tree::GlyphBuffer> FontList::CreateGlyphBuffer(
     const char16* text_buffer, int32 text_length, bool is_rtl) {
   return font_cache_->CreateGlyphBuffer(text_buffer, text_length, is_rtl, this);
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h
index 3cbc097..28c5435 100644
--- a/src/cobalt/dom/font_list.h
+++ b/src/cobalt/dom/font_list.h
@@ -57,7 +57,7 @@
   void set_state(State state) { state_ = state; }
 
   const scoped_refptr<render_tree::Font>& font() const { return font_; }
-  void set_font(const scoped_refptr<render_tree::Font> font) { font_ = font; }
+  void set_font(const scoped_refptr<render_tree::Font>& font) { font_ = font; }
 
  private:
   std::string family_name_;
@@ -116,7 +116,8 @@
 
   FontList(FontCache* font_cache, const FontListKey& font_list_key);
 
-  bool IsVisible() const;
+  // Resets the font list back to its initial state.
+  void Reset();
 
   // Reset loading fonts sets all font list fonts with a state of
   // |kLoadingState| back to |kUnrequestedState|, which will cause them to be
@@ -126,6 +127,8 @@
   // reset, as they may change if the loading font is now available.
   void ResetLoadingFonts();
 
+  bool IsVisible() const;
+
   // Given a string of text, returns the glyph buffer needed to render it. In
   // the case where |maybe_bounds| is non-NULL, it will also be populated with
   // the bounds of the rect.
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index d9c502d..60d0e15 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -55,7 +55,7 @@
         reduced_image_cache_capacity_manager,
     loader::font::RemoteTypefaceCache* remote_typeface_cache,
     loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
-    const std::string& language,
+    const std::string& font_language_script,
     base::ApplicationState initial_application_state,
     float video_playback_rate_multiplier)
     : fetcher_factory_(fetcher_factory),
@@ -74,7 +74,7 @@
       remote_typeface_cache_(remote_typeface_cache),
       mesh_cache_(mesh_cache),
       dom_stat_tracker_(dom_stat_tracker),
-      language_(language),
+      font_language_script_(font_language_script),
       page_visibility_state_(initial_application_state),
       video_playback_rate_multiplier_(video_playback_rate_multiplier),
       sync_load_thread_("Synchronous Load"),
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index 7847423..cab1c71 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -63,7 +63,7 @@
           reduced_image_cache_capacity_manager,
       loader::font::RemoteTypefaceCache* remote_typeface_cache,
       loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
-      const std::string& language,
+      const std::string& font_language_script,
       base::ApplicationState initial_application_state,
       float video_playback_rate_multiplier = 1.0);
   ~HTMLElementContext();
@@ -113,7 +113,9 @@
 
   DomStatTracker* dom_stat_tracker() { return dom_stat_tracker_; }
 
-  const std::string& language() const { return language_; }
+  const std::string& font_language_script() const {
+    return font_language_script_;
+  }
 
   float video_playback_rate_multiplier() const {
     return video_playback_rate_multiplier_;
@@ -151,7 +153,7 @@
   loader::font::RemoteTypefaceCache* const remote_typeface_cache_;
   loader::mesh::MeshCache* const mesh_cache_;
   DomStatTracker* const dom_stat_tracker_;
-  const std::string language_;
+  const std::string font_language_script_;
   page_visibility::PageVisibilityState page_visibility_state_;
   const float video_playback_rate_multiplier_;
 
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 2272cb8..380491c 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -124,7 +124,6 @@
       muted_(false),
       paused_(true),
       seeking_(false),
-      loop_(false),
       controls_(false),
       last_time_update_event_wall_time_(0),
       last_time_update_event_movie_time_(std::numeric_limits<float>::max()),
@@ -569,13 +568,18 @@
 }
 
 bool HTMLMediaElement::loop() const {
-  MLOG() << loop_;
-  return loop_;
+  MLOG() << HasAttribute("loop");
+  return HasAttribute("loop");
 }
 
 void HTMLMediaElement::set_loop(bool loop) {
-  MLOG() << loop;
-  loop_ = loop;
+  // The value of 'loop' is true when the 'loop' attribute is present.
+  // The value of the attribute is irrelevant.
+  if (loop) {
+    SetAttribute("loop", "");
+  } else {
+    RemoveAttribute("loop");
+  }
 }
 
 void HTMLMediaElement::Play() {
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index 31314e3..dc0fdaa 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -296,7 +296,6 @@
   bool muted_;
   bool paused_;
   bool seeking_;
-  bool loop_;
   bool controls_;
 
   // The last time a timeupdate event was sent (wall clock).
diff --git a/src/cobalt/dom/local_storage_database.cc b/src/cobalt/dom/local_storage_database.cc
index 844f584..02fc976 100644
--- a/src/cobalt/dom/local_storage_database.cc
+++ b/src/cobalt/dom/local_storage_database.cc
@@ -103,6 +103,7 @@
   write_statement.BindString(2, value);
   bool ok = write_statement.Run();
   DCHECK(ok);
+  sql_context->FlushOnChange();
 }
 
 void SqlDelete(const std::string& id, const std::string& key,
@@ -117,6 +118,7 @@
   delete_statement.BindString(1, key);
   bool ok = delete_statement.Run();
   DCHECK(ok);
+  sql_context->FlushOnChange();
 }
 
 void SqlClear(const std::string& id, storage::SqlContext* sql_context) {
@@ -129,6 +131,7 @@
   clear_statement.BindString(0, id);
   bool ok = clear_statement.Run();
   DCHECK(ok);
+  sql_context->FlushOnChange();
 }
 }  // namespace
 
@@ -156,20 +159,17 @@
   TRACK_MEMORY_SCOPE("Storage");
   Init();
   storage_->GetSqlContext(base::Bind(&SqlWrite, id, key, value));
-  storage_->FlushOnChange();
 }
 
 void LocalStorageDatabase::Delete(const std::string& id,
                                   const std::string& key) {
   Init();
   storage_->GetSqlContext(base::Bind(&SqlDelete, id, key));
-  storage_->FlushOnChange();
 }
 
 void LocalStorageDatabase::Clear(const std::string& id) {
   Init();
   storage_->GetSqlContext(base::Bind(&SqlClear, id));
-  storage_->FlushOnChange();
 }
 
 void LocalStorageDatabase::Flush(const base::Closure& callback) {
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index 10e3e53..53f53bd 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -53,7 +53,7 @@
         1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
         dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
         NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL, NULL,
-        dom_stat_tracker_.get(), url_, "", "en-US",
+        dom_stat_tracker_.get(), url_, "", "en-US", "en",
         base::Callback<void(const GURL&)>(), base::Bind(&StubErrorCallback),
         NULL, network_bridge::PostSender(),
         std::string() /* default security policy */, dom::kCspEnforcementEnable,
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index c11b61c..66dadf7 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -74,6 +74,14 @@
   DISALLOW_COPY_AND_ASSIGN(RelayLoadEvent);
 };
 
+namespace {
+// Ensure that the timer resolution is at the lowest 20 microseconds in
+// order to mitigate potential Spectre-related attacks.  This is following
+// Mozilla's lead as described here:
+//   https://www.mozilla.org/en-US/security/advisories/mfsa2018-01/
+const int64_t kPerformanceTimerMinResolutionInMicroseconds = 20;
+}  // namespace
+
 Window::Window(int width, int height, float device_pixel_ratio,
                base::ApplicationState initial_application_state,
                cssom::CSSParser* css_parser, Parser* dom_parser,
@@ -94,6 +102,7 @@
                MediaSource::Registry* media_source_registry,
                DomStatTracker* dom_stat_tracker, const GURL& url,
                const std::string& user_agent, const std::string& language,
+               const std::string& font_language_script,
                const base::Callback<void(const GURL&)> navigation_callback,
                const base::Callback<void(const std::string&)>& error_callback,
                network_bridge::CookieJar* cookie_jar,
@@ -122,13 +131,19 @@
           web_media_player_factory, script_runner, script_value_factory,
           media_source_registry, resource_provider, animated_image_tracker,
           image_cache, reduced_image_cache_capacity_manager,
-          remote_typeface_cache, mesh_cache, dom_stat_tracker, language,
-          initial_application_state, video_playback_rate_multiplier)),
+          remote_typeface_cache, mesh_cache, dom_stat_tracker,
+          font_language_script, initial_application_state,
+          video_playback_rate_multiplier)),
       performance_(new Performance(
 #if defined(ENABLE_TEST_RUNNER)
-          clock_type == kClockTypeTestRunner ? test_runner_->GetClock() :
+          clock_type == kClockTypeTestRunner
+              ? test_runner_->GetClock()
+              :
 #endif
-                                             new base::SystemMonotonicClock())),
+              new base::MinimumResolutionClock(
+                  new base::SystemMonotonicClock(),
+                  base::TimeDelta::FromMicroseconds(
+                      kPerformanceTimerMinResolutionInMicroseconds)))),
       ALLOW_THIS_IN_INITIALIZER_LIST(document_(new Document(
           html_element_context_.get(),
           Document::Options(
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 080aa88..335353f 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -135,6 +135,7 @@
       MediaSourceRegistry* media_source_registry,
       DomStatTracker* dom_stat_tracker, const GURL& url,
       const std::string& user_agent, const std::string& language,
+      const std::string& font_language_script,
       const base::Callback<void(const GURL&)> navigation_callback,
       const base::Callback<void(const std::string&)>& error_callback,
       network_bridge::CookieJar* cookie_jar,
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index f1ca6f4..845c319 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -52,7 +52,8 @@
             1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
             dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
             NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL,
-            NULL, NULL, url_, "", "en-US", base::Callback<void(const GURL &)>(),
+            NULL, NULL, url_, "", "en-US", "en",
+            base::Callback<void(const GURL &)>(),
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
diff --git a/src/cobalt/h5vcc/h5vcc_account_manager.cc b/src/cobalt/h5vcc/h5vcc_account_manager.cc
index 0bdfffd..2cb65fd 100644
--- a/src/cobalt/h5vcc/h5vcc_account_manager.cc
+++ b/src/cobalt/h5vcc/h5vcc_account_manager.cc
@@ -15,59 +15,62 @@
 #include "cobalt/h5vcc/h5vcc_account_manager.h"
 
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "starboard/user.h"
 
 namespace cobalt {
 namespace h5vcc {
 
 H5vccAccountManager::H5vccAccountManager()
-    : thread_("AccountManager"), owning_message_loop_(MessageLoop::current()),
-      user_authorizer_(account::UserAuthorizer::Create()) {
+    : user_authorizer_(account::UserAuthorizer::Create()),
+      owning_message_loop_(MessageLoop::current()), thread_("AccountManager") {
   thread_.Start();
 }
 
 void H5vccAccountManager::GetAuthToken(
     const AccessTokenCallbackHolder& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   DLOG(INFO) << "Get authorization token.";
-  scoped_ptr<AccessTokenCallbackReference> token_callback(
-      new AccessTokenCallbackHolder::Reference(this, callback));
-  thread_.message_loop()->PostTask(
-      FROM_HERE, base::Bind(&H5vccAccountManager::RequestOperationInternal,
-                            this, kGetToken, base::Passed(&token_callback)));
+  PostOperation(kGetToken, callback);
 }
 
 void H5vccAccountManager::RequestPairing(
     const AccessTokenCallbackHolder& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   DLOG(INFO) << "Request application linking.";
-  scoped_ptr<AccessTokenCallbackReference> token_callback(
-      new AccessTokenCallbackHolder::Reference(this, callback));
-  thread_.message_loop()->PostTask(
-      FROM_HERE, base::Bind(&H5vccAccountManager::RequestOperationInternal,
-                            this, kPairing, base::Passed(&token_callback)));
+  PostOperation(kPairing, callback);
 }
 
 void H5vccAccountManager::RequestUnpairing(
     const AccessTokenCallbackHolder& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   DLOG(INFO) << "Request application unlinking.";
-  scoped_ptr<AccessTokenCallbackReference> token_callback(
-      new AccessTokenCallbackHolder::Reference(this, callback));
+  PostOperation(kUnpairing, callback);
+}
+
+void H5vccAccountManager::PostOperation(
+    OperationType operation_type, const AccessTokenCallbackHolder& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  AccessTokenCallbackReference* token_callback =
+      new AccessTokenCallbackHolder::Reference(this, callback);
+  pending_callbacks_.push_back(token_callback);
   thread_.message_loop()->PostTask(
       FROM_HERE, base::Bind(&H5vccAccountManager::RequestOperationInternal,
-                            this, kUnpairing, base::Passed(&token_callback)));
+                            user_authorizer_.get(), operation_type,
+                            base::Bind(&H5vccAccountManager::PostResult,
+                                       owning_message_loop_,
+                                       base::AsWeakPtr(this),
+                                       token_callback)));
 }
 
 H5vccAccountManager::~H5vccAccountManager() {
   DCHECK(thread_checker_.CalledOnValidThread());
+  // Give the UserAuthorizer a chance to abort any long running pending requests
+  // before the message loop gets shut down.
+  user_authorizer_->Shutdown();
 }
 
+// static
 void H5vccAccountManager::RequestOperationInternal(
-    OperationType operation,
-    scoped_ptr<AccessTokenCallbackReference> token_callback) {
-  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
+    account::UserAuthorizer* user_authorizer, OperationType operation,
+    const base::Callback<void(const std::string&, uint64_t)>& post_result) {
   SbUser current_user = SbUserGetCurrent();
   DCHECK(SbUserIsValid(current_user));
 
@@ -75,18 +78,18 @@
 
   switch (operation) {
     case kPairing:
-      access_token = user_authorizer_->AuthorizeUser(current_user);
+      access_token = user_authorizer->AuthorizeUser(current_user);
       DLOG_IF(INFO, !access_token) << "User authorization request failed.";
       break;
     case kUnpairing:
-      if (user_authorizer_->DeauthorizeUser(current_user)) {
+      if (user_authorizer->DeauthorizeUser(current_user)) {
         break;
       }
       // The user canceled the flow, or there was some error. Fall into the next
       // case to get an access token if available and return that.
       DLOG(INFO) << "User deauthorization request failed. Try to get token.";
     case kGetToken:
-      access_token = user_authorizer_->RefreshAuthorization(current_user);
+      access_token = user_authorizer->RefreshAuthorization(current_user);
       DLOG_IF(INFO, !access_token) << "Authorization refresh request failed.";
       break;
   }
@@ -109,18 +112,37 @@
     }
   }
 
-  owning_message_loop_->PostTask(
+  post_result.Run(token_value, expiration_in_seconds);
+}
+
+// static
+void H5vccAccountManager::PostResult(
+    MessageLoop* message_loop,
+    base::WeakPtr<H5vccAccountManager> h5vcc_account_manager,
+    AccessTokenCallbackReference* token_callback,
+    const std::string& token, uint64_t expiration_in_seconds) {
+  message_loop->PostTask(
       FROM_HERE,
-      base::Bind(&H5vccAccountManager::SendResult, this,
-                 base::Passed(&token_callback), token_value,
-                 expiration_in_seconds));
+      base::Bind(&H5vccAccountManager::SendResult, h5vcc_account_manager,
+                 token_callback, token, expiration_in_seconds));
 }
 
 void H5vccAccountManager::SendResult(
-    scoped_ptr<AccessTokenCallbackReference> token_callback,
+    AccessTokenCallbackReference* token_callback,
     const std::string& token, uint64_t expiration_in_seconds) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  ScopedVector<AccessTokenCallbackReference>::iterator found = std::find(
+      pending_callbacks_.begin(), pending_callbacks_.end(), token_callback);
+  if (found == pending_callbacks_.end()) {
+    DLOG(ERROR) << "Account manager callback not valid.";
+    return;
+  }
+  // In case a new account manager request is made as part of the callback,
+  // erase the callback in the pending vector before running it, but we can't
+  // delete it until after we've made the callback.
+  pending_callbacks_.weak_erase(found);
   token_callback->value().Run(token, expiration_in_seconds);
+  delete token_callback;
 }
 
 }  // namespace h5vcc
diff --git a/src/cobalt/h5vcc/h5vcc_account_manager.h b/src/cobalt/h5vcc/h5vcc_account_manager.h
index 9885bb7..1a33ace 100644
--- a/src/cobalt/h5vcc/h5vcc_account_manager.h
+++ b/src/cobalt/h5vcc/h5vcc_account_manager.h
@@ -19,6 +19,8 @@
 #include <string>
 
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
 #include "base/message_loop.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
@@ -34,7 +36,8 @@
 // one-at-time on another thread in FIFO order. When a request is complete, the
 // AccessTokenCallback will be fired on the thread that the H5vccAccountManager
 // was created on.
-class H5vccAccountManager : public script::Wrappable {
+class H5vccAccountManager : public script::Wrappable,
+                            public base::SupportsWeakPtr<H5vccAccountManager> {
  public:
   typedef script::CallbackFunction<bool(const std::string&, uint64_t)>
       AccessTokenCallback;
@@ -59,27 +62,49 @@
 
   ~H5vccAccountManager();
 
-  void RequestOperationInternal(
-      OperationType operation,
-      scoped_ptr<AccessTokenCallbackReference> token_callback);
-  void SendResult(scoped_ptr<AccessTokenCallbackReference> token_callback,
-                        const std::string& token,
-                        uint64_t expiration_in_seconds);
+  // Posts an operation to the account manager thread.
+  void PostOperation(OperationType operation_type,
+                     const AccessTokenCallbackHolder& callback);
+
+  // Processes an operation on the account manager thread. Static because
+  // H5vccAccountManager may have been destructed before this runs.
+  static void RequestOperationInternal(
+      account::UserAuthorizer* user_authorizer, OperationType operation,
+      const base::Callback<void(const std::string&, uint64_t)>& post_result);
+
+  // Posts the result of an operation from the account manager thread back to
+  // the owning thread. Static because H5vccAccountManager may have been
+  // destructed before this runs.
+  static void PostResult(
+      MessageLoop* message_loop,
+      base::WeakPtr<H5vccAccountManager> h5vcc_account_manager,
+      AccessTokenCallbackReference* token_callback,
+      const std::string& token, uint64_t expiration_in_seconds);
+
+  // Sends the result of an operation to the callback on the owning thread.
+  void SendResult(AccessTokenCallbackReference* token_callback,
+                  const std::string& token, uint64_t expiration_in_seconds);
+
+  // The platform-specific user authorizer for getting access tokens.
+  scoped_ptr<account::UserAuthorizer> user_authorizer_;
+
+  // Scoped holder of the callbacks that are currently waiting for a response.
+  ScopedVector<AccessTokenCallbackReference> pending_callbacks_;
 
   // Thread checker for the thread that creates this instance.
   base::ThreadChecker thread_checker_;
 
-  // Each incoming request will have a corresponding task posted to this
-  // thread's message loop and will be handled in a FIFO manner.
-  base::Thread thread_;
-
   // The message loop that the H5vccAccountManager was created on. The public
   // interface must be called from this message loop, and callbacks will be
   // fired on this loop as well.
   MessageLoop* owning_message_loop_;
 
-  // The platform-specific user authorizer for getting access tokens.
-  scoped_ptr<account::UserAuthorizer> user_authorizer_;
+  // Each incoming request will have a corresponding task posted to this
+  // thread's message loop and will be handled in a FIFO manner.
+  // This is last so that all the other fields are valid before the thread gets
+  // constructed, and they remain valid until the thread gets destructed and its
+  // message loop gets flushed.
+  base::Thread thread_;
 
   friend class scoped_refptr<H5vccAccountManager>;
   DISALLOW_COPY_AND_ASSIGN(H5vccAccountManager);
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index 2b48e1f..de6442c 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -60,6 +60,7 @@
 
   void Suspend();
   void Resume();
+  void Purge();
 
   bool IsRenderTreePending() const;
 
@@ -253,7 +254,15 @@
 void LayoutManager::Impl::Suspend() {
   // Mark that we are suspended so that we don't try to perform any layouts.
   suspended_ = true;
+  Purge();
+}
 
+void LayoutManager::Impl::Resume() {
+  // Re-enable layouts.
+  suspended_ = false;
+}
+
+void LayoutManager::Impl::Purge() {
   // Invalidate any cached layout boxes from the document prior to clearing
   // the initial containing block. That'll ensure that the full box tree is
   // destroyed when the containing block is destroyed and that no children of
@@ -263,13 +272,8 @@
   // Clear our reference to the initial containing block to allow any resources
   // like images that were referenced by it to be released.
   initial_containing_block_ = NULL;
-}
 
-void LayoutManager::Impl::Resume() {
-  // Mark that we are no longer suspended and indicate that the layout is
-  // dirty since when Suspend() was called we invalidated our previous layout.
   DirtyLayout();
-  suspended_ = false;
 }
 
 bool LayoutManager::Impl::IsRenderTreePending() const {
@@ -417,6 +421,7 @@
 
 void LayoutManager::Suspend() { impl_->Suspend(); }
 void LayoutManager::Resume() { impl_->Resume(); }
+void LayoutManager::Purge() { impl_->Purge(); }
 bool LayoutManager::IsRenderTreePending() const {
   return impl_->IsRenderTreePending();
 }
diff --git a/src/cobalt/layout/layout_manager.h b/src/cobalt/layout/layout_manager.h
index 1a973a5..577cb6b 100644
--- a/src/cobalt/layout/layout_manager.h
+++ b/src/cobalt/layout/layout_manager.h
@@ -73,6 +73,7 @@
 
   void Suspend();
   void Resume();
+  void Purge();
 
   bool IsRenderTreePending() const;
 
diff --git a/src/cobalt/loader/fetcher.h b/src/cobalt/loader/fetcher.h
index 3e00461..1064b92 100644
--- a/src/cobalt/loader/fetcher.h
+++ b/src/cobalt/loader/fetcher.h
@@ -61,7 +61,12 @@
   };
 
   // Concrete Fetcher subclass should start fetching immediately in constructor.
-  explicit Fetcher(Handler* handler) : handler_(handler) {}
+  explicit Fetcher(Handler* handler)
+      : handler_(handler), did_fail_from_transient_error_(false) {}
+
+  bool did_fail_from_transient_error() const {
+    return did_fail_from_transient_error_;
+  }
 
   // Concrete Fetcher subclass should cancel fetching in destructor.
   virtual ~Fetcher() = 0;
@@ -69,8 +74,14 @@
  protected:
   Handler* handler() const { return handler_; }
 
+  void SetFailedFromTransientError() { did_fail_from_transient_error_ = true; }
+
  private:
   Handler* handler_;
+
+  // Whether or not the fetcher failed from an error that is considered
+  // transient, indicating that the same fetch may later succeed.
+  bool did_fail_from_transient_error_;
 };
 
 }  // namespace loader
diff --git a/src/cobalt/loader/font/remote_typeface_cache.h b/src/cobalt/loader/font/remote_typeface_cache.h
index ba8240c..f3a04ed 100644
--- a/src/cobalt/loader/font/remote_typeface_cache.h
+++ b/src/cobalt/loader/font/remote_typeface_cache.h
@@ -57,7 +57,7 @@
     const std::string& name, uint32 cache_capacity,
     loader::LoaderFactory* loader_factory) {
   return make_scoped_ptr<RemoteTypefaceCache>(new RemoteTypefaceCache(
-      name, cache_capacity,
+      name, cache_capacity, true /*are_loading_retries_enabled*/,
       base::Bind(&loader::LoaderFactory::CreateTypefaceLoader,
                  base::Unretained(loader_factory))));
 }
diff --git a/src/cobalt/loader/image/image_cache.h b/src/cobalt/loader/image/image_cache.h
index 378d842..3a08252 100644
--- a/src/cobalt/loader/image/image_cache.h
+++ b/src/cobalt/loader/image/image_cache.h
@@ -48,10 +48,10 @@
 inline static scoped_ptr<ImageCache> CreateImageCache(
     const std::string& name, uint32 cache_capacity,
     loader::LoaderFactory* loader_factory) {
-  return make_scoped_ptr<ImageCache>(
-      new ImageCache(name, cache_capacity,
-                     base::Bind(&loader::LoaderFactory::CreateImageLoader,
-                                base::Unretained(loader_factory))));
+  return make_scoped_ptr<ImageCache>(new ImageCache(
+      name, cache_capacity, false /*are_loading_retries_enabled*/,
+      base::Bind(&loader::LoaderFactory::CreateImageLoader,
+                 base::Unretained(loader_factory))));
 }
 
 // The ReducedCacheCapacityManager is a helper class that manages state which
diff --git a/src/cobalt/loader/loader.cc b/src/cobalt/loader/loader.cc
index d5e453d..e42a21b 100644
--- a/src/cobalt/loader/loader.cc
+++ b/src/cobalt/loader/loader.cc
@@ -82,7 +82,7 @@
       is_suspended_(is_suspended) {
   DCHECK(!fetcher_creator_.is_null());
   DCHECK(decoder_);
-  DCHECK(!on_error.is_null());
+  DCHECK(!on_error_.is_null());
 
   if (!is_suspended_) {
     Start();
@@ -128,6 +128,11 @@
   Start();
 }
 
+bool Loader::DidFailFromTransientError() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return fetcher_ && fetcher_->did_fail_from_transient_error();
+}
+
 void Loader::Start() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!is_suspended_);
diff --git a/src/cobalt/loader/loader.h b/src/cobalt/loader/loader.h
index 60c8b3f..ab9935c 100644
--- a/src/cobalt/loader/loader.h
+++ b/src/cobalt/loader/loader.h
@@ -24,15 +24,12 @@
 #include "base/threading/thread_checker.h"
 #include "cobalt/loader/decoder.h"
 #include "cobalt/loader/fetcher.h"
-#include "cobalt/render_tree/resource_provider.h"
 
 namespace cobalt {
 namespace loader {
 
 // Loader class consists of a Fetcher and a Decoder, that loads and decodes a
-// resource respectively. See the Loader design doc under the Cobalt intranet
-// home page.
-// TODO: Migrate Loader design doc to markdown in this directory.
+// resource respectively.
 class Loader {
  public:
   typedef base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> FetcherCreator;
@@ -59,13 +56,16 @@
   // called.
   void Resume(render_tree::ResourceProvider* resource_provider);
 
+  bool DidFailFromTransientError() const;
+
  private:
   class FetcherToDecoderAdapter;
 
   // Starts the fetch-and-decode.
   void Start();
 
-  FetcherCreator fetcher_creator_;
+  const FetcherCreator fetcher_creator_;
+
   scoped_ptr<Decoder> decoder_;
   scoped_ptr<FetcherToDecoderAdapter> fetcher_to_decoder_adaptor_;
   scoped_ptr<Fetcher> fetcher_;
@@ -73,8 +73,8 @@
   base::CancelableClosure fetcher_creator_error_closure_;
   base::ThreadChecker thread_checker_;
 
-  OnErrorFunction on_error_;
-  OnDestructionFunction on_destruction_;
+  const OnErrorFunction on_error_;
+  const OnDestructionFunction on_destruction_;
 
   bool is_suspended_;
 
diff --git a/src/cobalt/loader/mesh/mesh_cache.h b/src/cobalt/loader/mesh/mesh_cache.h
index b8ffe8c..6749032 100644
--- a/src/cobalt/loader/mesh/mesh_cache.h
+++ b/src/cobalt/loader/mesh/mesh_cache.h
@@ -48,9 +48,10 @@
 inline static scoped_ptr<MeshCache> CreateMeshCache(
     const std::string& name, uint32 cache_capacity,
     loader::LoaderFactory* loader_factory) {
-  return make_scoped_ptr<MeshCache>(new MeshCache(
-      name, cache_capacity, base::Bind(&loader::LoaderFactory::CreateMeshLoader,
-                                       base::Unretained(loader_factory))));
+  return make_scoped_ptr<MeshCache>(
+      new MeshCache(name, cache_capacity, false /*are_loading_retries_enabled*/,
+                    base::Bind(&loader::LoaderFactory::CreateMeshLoader,
+                               base::Unretained(loader_factory))));
 }
 
 }  // namespace mesh
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc
index 08fd0a3..82ec0eb 100644
--- a/src/cobalt/loader/net_fetcher.cc
+++ b/src/cobalt/loader/net_fetcher.cc
@@ -133,6 +133,19 @@
   if (status.is_success() && IsResponseCodeSuccess(response_code)) {
     handler()->OnDone(this);
   } else {
+    // Check for response codes and errors that are considered transient. These
+    // are the ones that net::URLFetcherCore is willing to attempt retries on,
+    // along with ERR_NAME_RESOLUTION_FAILED, which indicates a socket error.
+    if (response_code >= 500 ||
+        status.error() == net::ERR_TEMPORARILY_THROTTLED ||
+        status.error() == net::ERR_NETWORK_CHANGED ||
+        status.error() == net::ERR_NAME_RESOLUTION_FAILED ||
+        status.error() == net::ERR_CONNECTION_RESET ||
+        status.error() == net::ERR_CONNECTION_CLOSED ||
+        status.error() == net::ERR_CONNECTION_ABORTED) {
+      SetFailedFromTransientError();
+    }
+
     std::string msg(
         base::StringPrintf("NetFetcher error on %s: %s, response code %d",
                            source->GetURL().spec().c_str(),
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index ddb4300..ab01730 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_LOADER_RESOURCE_CACHE_H_
 #define COBALT_LOADER_RESOURCE_CACHE_H_
 
+#include <algorithm>
 #include <list>
 #include <map>
 #include <string>
@@ -27,6 +28,7 @@
 #include "base/memory/scoped_vector.h"
 #include "base/stringprintf.h"
 #include "base/threading/thread_checker.h"
+#include "base/timer.h"
 #include "cobalt/base/c_val.h"
 #include "cobalt/csp/content_security_policy.h"
 #include "cobalt/loader/decoder.h"
@@ -97,10 +99,7 @@
   };
 
   // Request fetching and decoding a single resource based on the url.
-  CachedResource(const GURL& url,
-                 const csp::SecurityCallback& security_callback,
-                 const CreateLoaderFunction& create_loader_function,
-                 ResourceCacheType* resource_cache);
+  CachedResource(const GURL& url, ResourceCacheType* resource_cache);
 
   // Resource is available. CachedResource is a wrapper of the resource
   // and there is no need to fetch or load this resource again. |loader_|
@@ -114,7 +113,8 @@
   // available.
   scoped_refptr<ResourceType> TryGetResource();
 
-  bool IsLoading();
+  // Whether not the resource located at |url_| is finished loading.
+  bool IsLoadingComplete();
 
   const GURL& url() const { return url_; }
 
@@ -128,6 +128,17 @@
 
   ~CachedResource();
 
+  // Start loading the resource located at |url_|. This encompasses both
+  // fetching and decoding it.
+  void StartLoading();
+
+  // Schedule a loading retry on the resource located at |url_|. While there is
+  // no limit on the number of retry attempts that can occur, the retry
+  // scheduling uses an exponential backoff. The wait time doubles with each
+  // subsequent attempt until a maximum wait time of 1024 seconds (~17 minutes)
+  // is reached.
+  void ScheduleLoadingRetry();
+
   // Callbacks for decoders.
   //
   // Notify that the resource is loaded successfully.
@@ -159,9 +170,14 @@
   // triggered from within the resource initialization callstack, and we are
   // not prepared to handle that. These members let us ensure that we are fully
   // initialized before we proceed with any completion callbacks.
-  bool completion_callbacks_enabled_;
+  bool are_completion_callbacks_enabled_;
   base::Closure completion_callback_;
 
+  // When the resource cache is set to allow retries and a transient loading
+  // error causes a resource to fail to load, a retry is scheduled.
+  int retry_count_;
+  scoped_ptr<base::Timer> retry_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(CachedResource);
 };
 
@@ -211,19 +227,14 @@
 //////////////////////////////////////////////////////////////////////////
 
 template <typename CacheType>
-CachedResource<CacheType>::CachedResource(
-    const GURL& url, const csp::SecurityCallback& security_callback,
-    const CreateLoaderFunction& create_loader_function,
-    ResourceCacheType* resource_cache)
+CachedResource<CacheType>::CachedResource(const GURL& url,
+                                          ResourceCacheType* resource_cache)
     : url_(url),
       resource_cache_(resource_cache),
-      completion_callbacks_enabled_(false) {
+      are_completion_callbacks_enabled_(false),
+      retry_count_(0) {
   DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
-
-  loader_ = create_loader_function.Run(
-      url, security_callback,
-      base::Bind(&CachedResource::OnLoadingSuccess, base::Unretained(this)),
-      base::Bind(&CachedResource::OnLoadingError, base::Unretained(this)));
+  StartLoading();
 }
 
 template <typename CacheType>
@@ -233,27 +244,19 @@
     : url_(url),
       resource_(resource),
       resource_cache_(resource_cache),
-      completion_callbacks_enabled_(false) {
+      are_completion_callbacks_enabled_(false),
+      retry_count_(0) {
   DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
 }
 
 template <typename CacheType>
-scoped_refptr<typename CacheType::ResourceType>
-CachedResource<CacheType>::TryGetResource() {
-  DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
-
-  return resource_;
-}
-
-template <typename CacheType>
-bool CachedResource<CacheType>::IsLoading() {
-  return loader_;
-}
-
-template <typename CacheType>
 CachedResource<CacheType>::~CachedResource() {
   DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
 
+  if (retry_timer_) {
+    retry_timer_->Stop();
+  }
+
   resource_cache_->NotifyResourceDestroyed(this);
 
   for (int i = 0; i < kCallbackTypeCount; ++i) {
@@ -262,6 +265,51 @@
 }
 
 template <typename CacheType>
+scoped_refptr<typename CacheType::ResourceType>
+CachedResource<CacheType>::TryGetResource() {
+  DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
+  return resource_;
+}
+
+template <typename CacheType>
+void CachedResource<CacheType>::StartLoading() {
+  DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
+  DCHECK(!loader_);
+  loader_ = resource_cache_->StartLoadingResource(this);
+}
+
+template <typename CacheType>
+bool CachedResource<CacheType>::IsLoadingComplete() {
+  return !loader_ && !retry_timer_;
+}
+
+template <typename CacheType>
+void CachedResource<CacheType>::ScheduleLoadingRetry() {
+  DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
+  DCHECK(!loader_);
+  DCHECK(!retry_timer_ || !retry_timer_->IsRunning());
+
+  LOG(WARNING) << "Scheduling loading retry for '" << url_ << "'";
+  resource_cache_->NotifyResourceLoadingRetryScheduled(this);
+
+  // The delay starts at 1 second and doubles every subsequent retry until the
+  // maxiumum delay of 1024 seconds (~17 minutes) is reached. After this, all
+  // additional attempts also wait 1024 seconds.
+  const int64 kBaseRetryDelayInMilliseconds = 1000;
+  const int kMaxRetryCountShift = 10;
+  int64 delay = kBaseRetryDelayInMilliseconds
+                << std::min(kMaxRetryCountShift, retry_count_++);
+
+  // The retry timer is lazily created the first time that it is needed.
+  if (!retry_timer_) {
+    retry_timer_.reset(new base::Timer(false, false));
+  }
+  retry_timer_->Start(
+      FROM_HERE, base::TimeDelta::FromMilliseconds(delay),
+      base::Bind(&CachedResource::StartLoading, base::Unretained(this)));
+}
+
+template <typename CacheType>
 void CachedResource<CacheType>::OnLoadingSuccess(
     const scoped_refptr<ResourceType>& resource) {
   DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
@@ -269,12 +317,13 @@
   resource_ = resource;
 
   loader_.reset();
+  retry_timer_.reset();
 
   completion_callback_ =
       base::Bind(&ResourceCacheType::NotifyResourceLoadingComplete,
                  base::Unretained(resource_cache_), base::Unretained(this),
                  kOnLoadingSuccessCallbackType);
-  if (completion_callbacks_enabled_) {
+  if (are_completion_callbacks_enabled_) {
     completion_callback_.Run();
   }
 }
@@ -283,15 +332,25 @@
 void CachedResource<CacheType>::OnLoadingError(const std::string& error) {
   DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
 
-  LOG(WARNING) << "Error while loading '" << url_ << "': " << error;
+  LOG(WARNING) << " Error while loading '" << url_ << "': " << error;
+
+  bool should_retry = resource_cache_->are_loading_retries_enabled() &&
+                      loader_->DidFailFromTransientError();
 
   loader_.reset();
-  completion_callback_ =
-      base::Bind(&ResourceCacheType::NotifyResourceLoadingComplete,
-                 base::Unretained(resource_cache_), base::Unretained(this),
-                 kOnLoadingErrorCallbackType);
-  if (completion_callbacks_enabled_) {
-    completion_callback_.Run();
+
+  if (should_retry) {
+    ScheduleLoadingRetry();
+  } else {
+    retry_timer_.reset();
+
+    completion_callback_ =
+        base::Bind(&ResourceCacheType::NotifyResourceLoadingComplete,
+                   base::Unretained(resource_cache_), base::Unretained(this),
+                   kOnLoadingErrorCallbackType);
+    if (are_completion_callbacks_enabled_) {
+      completion_callback_.Run();
+    }
   }
 }
 
@@ -330,7 +389,7 @@
 
 template <typename CacheType>
 void CachedResource<CacheType>::EnableCompletionCallbacks() {
-  completion_callbacks_enabled_ = true;
+  are_completion_callbacks_enabled_ = true;
   if (!completion_callback_.is_null()) {
     completion_callback_.Run();
   }
@@ -398,6 +457,7 @@
   };
 
   ResourceCache(const std::string& name, uint32 cache_capacity,
+                bool are_load_retries_enabled,
                 const CreateLoaderFunction& create_loader_function);
 
   // |CreateCachedResource| returns CachedResource. If the CachedResource is not
@@ -441,10 +501,16 @@
       ResourceMap;
   typedef typename ResourceMap::iterator ResourceMapIterator;
 
+  scoped_ptr<Loader> StartLoadingResource(CachedResourceType* cached_resource);
+
   // Called by CachedResource objects after they finish loading.
   void NotifyResourceLoadingComplete(CachedResourceType* cached_resource,
                                      CallbackType callback_type);
 
+  // Called by CachedResource objects when they fail to load as a result of a
+  // transient error and are scheduling a retry.
+  void NotifyResourceLoadingRetryScheduled(CachedResourceType* cached_resource);
+
   // Called by the destructor of CachedResource to remove CachedResource from
   // |cached_resource_map_| and either immediately free the resource from memory
   // or add it to |unreference_cached_resource_map_|, depending on whether the
@@ -467,10 +533,15 @@
   // |callback_blocking_loading_resource_set_| is empty.
   void ProcessPendingCallbacksIfUnblocked();
 
+  bool are_loading_retries_enabled() const {
+    return are_loading_retries_enabled_;
+  }
+
   // The name of this resource cache object, useful while debugging.
   const std::string name_;
 
   uint32 cache_capacity_;
+  bool are_loading_retries_enabled_;
 
   CreateLoaderFunction create_loader_function_;
 
@@ -531,9 +602,11 @@
 template <typename CacheType>
 ResourceCache<CacheType>::ResourceCache(
     const std::string& name, uint32 cache_capacity,
+    bool are_loading_retries_enabled,
     const CreateLoaderFunction& create_loader_function)
     : name_(name),
       cache_capacity_(cache_capacity),
+      are_loading_retries_enabled_(are_loading_retries_enabled),
       create_loader_function_(create_loader_function),
       is_processing_pending_callbacks_(false),
       are_callbacks_disabled_(false),
@@ -595,22 +668,9 @@
   // If we reach this point, then the resource doesn't exist yet.
   ++count_resources_requested_;
 
-  // 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());
-  }
-  ++count_resources_loading_;
-
   // 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));
+  scoped_refptr<CachedResourceType> cached_resource(
+      new CachedResourceType(url, this));
   cached_resource_map_.insert(
       std::make_pair(url.spec(), cached_resource.get()));
 
@@ -668,6 +728,40 @@
 }
 
 template <typename CacheType>
+scoped_ptr<Loader> ResourceCache<CacheType>::StartLoadingResource(
+    CachedResourceType* cached_resource) {
+  DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+  const std::string& url = cached_resource->url().spec();
+
+  // The resource should not already be in either of the loading sets.
+  DCHECK(callback_blocking_loading_resource_set_.find(url) ==
+         callback_blocking_loading_resource_set_.end());
+  DCHECK(non_callback_blocking_loading_resource_set_.find(url) ==
+         non_callback_blocking_loading_resource_set_.end());
+
+  // 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);
+  } else {
+    non_callback_blocking_loading_resource_set_.insert(url);
+  }
+
+  ++count_resources_loading_;
+
+  return create_loader_function_.Run(
+      cached_resource->url(), security_callback_,
+      base::Bind(&CachedResourceType::OnLoadingSuccess,
+                 base::Unretained(cached_resource)),
+      base::Bind(&CachedResourceType::OnLoadingError,
+                 base::Unretained(cached_resource)));
+}
+
+template <typename CacheType>
 void ResourceCache<CacheType>::NotifyResourceLoadingComplete(
     CachedResourceType* cached_resource, CallbackType callback_type) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
@@ -707,6 +801,29 @@
 }
 
 template <typename CacheType>
+void ResourceCache<CacheType>::NotifyResourceLoadingRetryScheduled(
+    CachedResourceType* cached_resource) {
+  DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+  const std::string& url = cached_resource->url().spec();
+
+  // Remove the resource from those currently loading. It'll be re-added once
+  // the retry starts.
+
+  // 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);
+  }
+
+  --count_resources_loading_;
+
+  ProcessPendingCallbacksIfUnblocked();
+}
+
+template <typename CacheType>
 void ResourceCache<CacheType>::NotifyResourceDestroyed(
     CachedResourceType* cached_resource) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
diff --git a/src/cobalt/media/base/decoder_buffer_cache.cc b/src/cobalt/media/base/decoder_buffer_cache.cc
index 21908d2..a7ee245 100644
--- a/src/cobalt/media/base/decoder_buffer_cache.cc
+++ b/src/cobalt/media/base/decoder_buffer_cache.cc
@@ -42,10 +42,10 @@
     base::TimeDelta media_time) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  ClearSegmentsBeforeMediaTime(media_time, &audio_buffers_,
-                               &audio_key_frame_timestamps_);
-  ClearSegmentsBeforeMediaTime(media_time, &video_buffers_,
-                               &video_key_frame_timestamps_);
+  audio_buffer_index_ -= ClearSegmentsBeforeMediaTime(
+      media_time, &audio_buffers_, &audio_key_frame_timestamps_);
+  video_buffer_index_ -= ClearSegmentsBeforeMediaTime(
+      media_time, &video_buffers_, &video_key_frame_timestamps_);
 }
 
 void DecoderBufferCache::ClearAll() {
@@ -55,6 +55,8 @@
   audio_key_frame_timestamps_.clear();
   video_buffers_.clear();
   video_key_frame_timestamps_.clear();
+  audio_buffer_index_ = 0;
+  video_buffer_index_ = 0;
 }
 
 void DecoderBufferCache::StartResuming() {
@@ -94,7 +96,7 @@
 }
 
 // static
-void DecoderBufferCache::ClearSegmentsBeforeMediaTime(
+size_t DecoderBufferCache::ClearSegmentsBeforeMediaTime(
     base::TimeDelta media_time, Buffers* buffers,
     KeyFrameTimestamps* key_frame_timestamps) {
   // Use K to denote a key frame and N for non-key frame.  If the cache contains
@@ -112,15 +114,20 @@
     key_frame_timestamps->erase(key_frame_timestamps->begin());
   }
   if (key_frame_timestamps->empty()) {
-    return;
+    return 0;
   }
+
+  size_t buffers_removed = 0;
   while (scoped_refptr<DecoderBuffer> buffer = buffers->front()) {
     if (buffer->is_key_frame() &&
         buffer->timestamp() == key_frame_timestamps->front()) {
       break;
     }
     buffers->pop_front();
+    ++buffers_removed;
   }
+
+  return buffers_removed;
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/base/decoder_buffer_cache.h b/src/cobalt/media/base/decoder_buffer_cache.h
index cff5a5c..0a37528 100644
--- a/src/cobalt/media/base/decoder_buffer_cache.h
+++ b/src/cobalt/media/base/decoder_buffer_cache.h
@@ -49,7 +49,7 @@
   typedef std::deque<scoped_refptr<DecoderBuffer> > Buffers;
   typedef std::deque<base::TimeDelta> KeyFrameTimestamps;
 
-  static void ClearSegmentsBeforeMediaTime(
+  static size_t ClearSegmentsBeforeMediaTime(
       base::TimeDelta media_time, Buffers* buffers,
       KeyFrameTimestamps* key_frame_timestamps);
 
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index d32d468..fe2ae00 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -373,8 +373,10 @@
   DCHECK(!seek_cb.is_null());
 
   if (audio_read_in_progress_ || video_read_in_progress_) {
-    message_loop_->PostTask(
-        FROM_HERE, base::Bind(&SbPlayerPipeline::Seek, this, time, seek_cb));
+    const TimeDelta kDelay = TimeDelta::FromMilliseconds(50);
+    message_loop_->PostDelayedTask(
+        FROM_HERE, base::Bind(&SbPlayerPipeline::Seek, this, time, seek_cb),
+        kDelay);
     return;
   }
 
@@ -693,8 +695,6 @@
     audio_stream_ = audio_stream;
     video_stream_ = video_stream;
 
-    buffering_state_cb_.Run(kHaveMetadata);
-
     bool is_encrypted = audio_stream_->audio_decoder_config().is_encrypted();
     natural_size_ = video_stream_->video_decoder_config().natural_size();
     is_encrypted |= video_stream_->video_decoder_config().is_encrypted();
@@ -824,6 +824,7 @@
       NOTREACHED();
       break;
     case kSbPlayerStatePrerolling:
+      buffering_state_cb_.Run(kHaveMetadata);
       break;
     case kSbPlayerStatePresenting:
       buffering_state_cb_.Run(kPrerollCompleted);
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 9b30cba..e17dd70 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -135,19 +135,24 @@
 void StarboardPlayer::WriteBuffer(DemuxerStream::Type type,
                                   const scoped_refptr<DecoderBuffer>& buffer) {
   DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(buffer);
 
-  // When |state_| is kPlaying, cache all buffer appended.  When |state_| is
-  // kSuspended, there may still be left-over buffers appended from the pipeline
-  // so they also should be cached.
-  // When |state_| is resuming, all buffers come from the cache and shouldn't be
-  // cached.
-  if (state_ != kResuming) {
-    decoder_buffer_cache_.AddBuffer(type, buffer);
-  }
+  decoder_buffer_cache_.AddBuffer(type, buffer);
 
-  if (state_ == kSuspended) {
-    return;
+  if (state_ != kSuspended) {
+    WriteNextBufferFromCache(type);
   }
+}
+
+// TODO: Move this after CreatePlayer() in a follow up CL.  To keep the function
+//       here makes code review easier.
+void StarboardPlayer::WriteNextBufferFromCache(DemuxerStream::Type type) {
+  DCHECK(state_ != kSuspended);
+
+  const scoped_refptr<DecoderBuffer>& buffer =
+      decoder_buffer_cache_.GetBuffer(type);
+  DCHECK(buffer);
+  decoder_buffer_cache_.AdvanceToNextBuffer(type);
 
   DCHECK(SbPlayerIsValid(player_));
 
@@ -494,8 +499,7 @@
   if (state_ == kResuming) {
     DemuxerStream::Type stream_type = SbMediaTypeToDemuxerStreamType(type);
     if (decoder_buffer_cache_.GetBuffer(stream_type)) {
-      WriteBuffer(stream_type, decoder_buffer_cache_.GetBuffer(stream_type));
-      decoder_buffer_cache_.AdvanceToNextBuffer(stream_type);
+      WriteNextBufferFromCache(stream_type);
       return;
     }
     if (!decoder_buffer_cache_.GetBuffer(DemuxerStream::AUDIO) &&
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index ed318e0..a2c9dc5 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -111,6 +111,9 @@
       DecodingBuffers;
 
   void CreatePlayer();
+
+  void WriteNextBufferFromCache(DemuxerStream::Type type);
+
   void ClearDecoderBufferCache();
 
   void OnDecoderStatus(SbPlayer player, SbMediaType type,
diff --git a/src/cobalt/media/filters/shell_demuxer.cc b/src/cobalt/media/filters/shell_demuxer.cc
index 1764db8..45e3af5 100644
--- a/src/cobalt/media/filters/shell_demuxer.cc
+++ b/src/cobalt/media/filters/shell_demuxer.cc
@@ -195,7 +195,8 @@
       stopped_(false),
       flushing_(false),
       audio_reached_eos_(false),
-      video_reached_eos_(false) {
+      video_reached_eos_(false),
+      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
   DCHECK(message_loop_);
   DCHECK(buffer_allocator_);
   DCHECK(data_source_);
@@ -239,7 +240,10 @@
       blocking_thread_.message_loop_proxy(), FROM_HERE,
       base::Bind(&ShellDemuxer::ParseConfigBlocking, base::Unretained(this),
                  status_cb),
-      base::Bind(&ShellDemuxer::ParseConfigDone, base::Unretained(this),
+      // ParseConfigDone() will run on the current thread.  Use a WeakPtr to
+      // ensure that ParseConfigDone() won't run if the current instance is
+      // destroyed.
+      base::Bind(&ShellDemuxer::ParseConfigDone, weak_ptr_factory_.GetWeakPtr(),
                  status_cb));
 }
 
@@ -289,6 +293,11 @@
 void ShellDemuxer::ParseConfigDone(const PipelineStatusCB& status_cb,
                                    PipelineStatus status) {
   DCHECK(MessageLoopBelongsToCurrentThread());
+
+  if (stopped_) {
+    return;
+  }
+
   // if the blocking parser thread cannot parse config we're done.
   if (status != PIPELINE_OK) {
     status_cb.Run(status);
@@ -442,7 +451,14 @@
   // Notify host of each disjoint range.
   host_->OnBufferedTimeRangesChanged(buffered);
 
-  IssueNextRequest();
+  // Post the task with a delay to make the request loop a bit friendly to
+  // other tasks as otherwise IssueNextRequest(), Request(), AllocateBuffer(),
+  // and Download() can form a tight loop on the |blocking_thread_|.
+  const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(5);
+  blocking_thread_.message_loop_proxy()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ShellDemuxer::IssueNextRequest, base::Unretained(this)),
+      kDelay);
 }
 
 void ShellDemuxer::IssueNextRequest() {
diff --git a/src/cobalt/media/filters/shell_demuxer.h b/src/cobalt/media/filters/shell_demuxer.h
index f976278..ca8ea29 100644
--- a/src/cobalt/media/filters/shell_demuxer.h
+++ b/src/cobalt/media/filters/shell_demuxer.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/memory/weak_ptr.h"
 #include "base/message_loop.h"
 #include "base/threading/thread.h"
 #include "cobalt/media/base/decoder_buffer.h"
@@ -180,6 +181,8 @@
   scoped_refptr<ShellAU> requested_au_;
   bool audio_reached_eos_;
   bool video_reached_eos_;
+
+  base::WeakPtrFactory<ShellDemuxer> weak_ptr_factory_;
 };
 
 }  // namespace media
diff --git a/src/cobalt/media/filters/source_buffer_range.cc b/src/cobalt/media/filters/source_buffer_range.cc
index c708c57..5903ab5 100644
--- a/src/cobalt/media/filters/source_buffer_range.cc
+++ b/src/cobalt/media/filters/source_buffer_range.cc
@@ -98,7 +98,6 @@
 }
 
 void SourceBufferRange::Seek(DecodeTimestamp timestamp) {
-  DCHECK(CanSeekTo(timestamp));
   DCHECK(!keyframe_map_.empty());
 
   KeyframeMap::iterator result = GetFirstKeyframeAtOrBefore(timestamp);
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
index 36315ef..152fb6e 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
@@ -361,7 +361,7 @@
   // These are purged if they are unreferenced outside of the style set.
   for (int i = 0; i < styles_.count(); ++i) {
     SkAutoTUnref<SkTypeface>& typeface = styles_[i]->typeface;
-    if (typeface.get() != NULL && typeface->getRefCnt() == 1) {
+    if (typeface.get() != NULL && typeface->unique()) {
       typeface.reset(NULL);
     }
   }
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc
index 99e0414..7b75647 100644
--- a/src/cobalt/storage/storage_manager.cc
+++ b/src/cobalt/storage/storage_manager.cc
@@ -177,8 +177,14 @@
 
 void StorageManager::GetSqlContext(const SqlCallback& callback) {
   TRACE_EVENT0("cobalt::storage", __FUNCTION__);
-  sql_message_loop_->PostTask(FROM_HERE,
-                              base::Bind(callback, sql_context_.get()));
+  if (MessageLoop::current()->message_loop_proxy() != sql_message_loop_) {
+    sql_message_loop_->PostTask(FROM_HERE,
+                                base::Bind(&StorageManager::GetSqlContext,
+                                           base::Unretained(this), callback));
+    return;
+  }
+
+  callback.Run(sql_context_.get());
 }
 
 void StorageManager::FlushOnChange() {
@@ -266,6 +272,8 @@
     return;
   }
 
+  initialized_ = true;
+
   vfs_.reset(new VirtualFileSystem());
   sql_vfs_.reset(new SqlVfs("cobalt_vfs", vfs_.get()));
   // Savegame has finished loading. Now initialize the database connection.
@@ -278,13 +286,14 @@
   DCHECK(loaded_raw_bytes);
   Savegame::ByteVector& raw_bytes = *loaded_raw_bytes;
   VirtualFileSystem::SerializedHeader header = {};
+  bool has_upgrade_data = false;
 
   if (raw_bytes.size() > 0) {
     const char* buffer = reinterpret_cast<char*>(&raw_bytes[0]);
     int buffer_size = static_cast<int>(raw_bytes.size());
     // Is this upgrade data?
     if (upgrade::UpgradeReader::IsUpgradeData(buffer, buffer_size)) {
-      upgrade_handler_->OnUpgrade(this, buffer, buffer_size);
+      has_upgrade_data = true;
     } else {
       if (raw_bytes.size() >= sizeof(VirtualFileSystem::SerializedHeader)) {
         memcpy(&header, &raw_bytes[0],
@@ -331,7 +340,11 @@
   SqlCreateSchemaTable(connection_.get());
   SqlUpdateDatabaseUserVersion(connection_.get());
 
-  initialized_ = true;
+  if (has_upgrade_data) {
+    const char* buffer = reinterpret_cast<char*>(&raw_bytes[0]);
+    int buffer_size = static_cast<int>(raw_bytes.size());
+    upgrade_handler_->OnUpgrade(this, buffer, buffer_size);
+  }
 }
 
 void StorageManager::StopFlushOnChangeTimers() {
@@ -433,6 +446,11 @@
   TRACE_EVENT0("cobalt::storage", __FUNCTION__);
   DCHECK(!sql_message_loop_->BelongsToCurrentThread());
 
+  // Make sure that the on change timers fire if they're running.
+  sql_message_loop_->PostTask(
+      FROM_HERE, base::Bind(&StorageManager::FireRunningOnChangeTimers,
+                            base::Unretained(this)));
+
   // The SQL thread may be communicating with the savegame I/O thread still,
   // flushing all pending updates.  This process can require back and forth
   // communication.  This method exists to wait for that communication to
@@ -451,6 +469,16 @@
   no_flushes_pending_.Wait();
 }
 
+void StorageManager::FireRunningOnChangeTimers() {
+  TRACE_EVENT0("cobalt::storage", __FUNCTION__);
+  DCHECK(sql_message_loop_->BelongsToCurrentThread());
+
+  if (flush_on_last_change_timer_->IsRunning() ||
+      flush_on_change_max_delay_timer_->IsRunning()) {
+    OnFlushOnChangeTimerFired();
+  }
+}
+
 void StorageManager::OnDestroy() {
   TRACE_EVENT0("cobalt::storage", __FUNCTION__);
   DCHECK(sql_message_loop_->BelongsToCurrentThread());
diff --git a/src/cobalt/storage/storage_manager.h b/src/cobalt/storage/storage_manager.h
index 26a990f..9988f92 100644
--- a/src/cobalt/storage/storage_manager.h
+++ b/src/cobalt/storage/storage_manager.h
@@ -115,7 +115,7 @@
   friend class StorageManagerTest;
 
   // Flushes all queued flushes to the savegame thread.
-  virtual void FlushInternal();
+  void FlushInternal();
 
   // Initialize the SQLite database. This blocks until the savegame load is
   // complete.
@@ -137,6 +137,9 @@
   // outside the SQL message loop (such as from StorageManager's destructor).
   void FinishIO();
 
+  // This function will immediately the on change timers if they are running.
+  void FireRunningOnChangeTimers();
+
   // Called by the destructor, to ensure we destroy certain objects on the
   // sql thread.
   void OnDestroy();
diff --git a/src/cobalt/storage/storage_manager_test.cc b/src/cobalt/storage/storage_manager_test.cc
index 2d94612..b0029f0 100644
--- a/src/cobalt/storage/storage_manager_test.cc
+++ b/src/cobalt/storage/storage_manager_test.cc
@@ -146,6 +146,11 @@
         new StorageManagerType(upgrade_handler.Pass(), options));
   }
 
+  template <typename StorageManagerType>
+  void FinishIO() {
+    storage_manager_->FinishIO();
+  }
+
   MessageLoop message_loop_;
   scoped_ptr<StorageManager> storage_manager_;
 };
@@ -200,7 +205,6 @@
       *dynamic_cast<MockStorageManager*>(storage_manager_.get());
 
   // When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
-  // We will wait for this in TimedWait().
   ON_CALL(storage_manager, QueueFlush(_))
       .WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
   EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
@@ -254,7 +258,6 @@
       *dynamic_cast<MockStorageManager*>(storage_manager_.get());
 
   // When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
-  // We will wait for this in TimedWait().
   ON_CALL(storage_manager, QueueFlush(_))
       .WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
   EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
@@ -267,6 +270,29 @@
   EXPECT_EQ(true, waiter.IsSignaled());
 }
 
+TEST_F(StorageManagerTest, FlushOnShutdown) {
+  // Test that pending flushes are completed on shutdown.
+  Init<MockStorageManager>();
+
+  storage_manager_->GetSqlContext(base::Bind(&FlushCallback));
+  message_loop_.RunUntilIdle();
+
+  FlushWaiter waiter;
+  MockStorageManager& storage_manager =
+      *dynamic_cast<MockStorageManager*>(storage_manager_.get());
+
+  // When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
+  ON_CALL(storage_manager, QueueFlush(_))
+      .WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
+  EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
+
+  storage_manager_->FlushOnChange();
+  FinishIO<StorageManager>();
+  storage_manager_.reset();
+
+  EXPECT_TRUE(waiter.IsSignaled());
+}
+
 TEST_F(StorageManagerTest, Upgrade) {
   Savegame::ByteVector initial_data;
   initial_data.push_back('U');
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index 7af4b99..cc35227 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -163,7 +163,8 @@
       error_(false),
       sent_(false),
       stop_timeout_(false),
-      upload_complete_(false) {
+      upload_complete_(false),
+      active_requests_count_(0) {
   DCHECK(settings_);
   dom::GlobalStats::GetInstance()->Add(this);
   xhr_id_ = ++s_xhr_sequence_num_;
@@ -365,8 +366,9 @@
   // Step 9
   sent_ = true;
   // Now that a send is happening, prevent this object
-  // from being collected until it's complete or aborted.
-  PreventGarbageCollection();
+  // from being collected until it's complete or aborted
+  // if no currently active request has called it before.
+  IncrementActiveRequests();
   FireProgressEvent(this, base::Tokens::loadstart());
   if (!upload_complete_) {
     FireProgressEvent(upload_, base::Tokens::loadstart());
@@ -677,7 +679,7 @@
     ChangeState(kDone);
     UpdateProgress();
     // Undo the ref we added in Send()
-    AllowGarbageCollection();
+    DecrementActiveRequests();
   } else {
     HandleRequestError(kNetworkError);
   }
@@ -775,7 +777,7 @@
   FireProgressEvent(this, base::Tokens::loadend());
 
   fetch_callback_.reset();
-  AllowGarbageCollection();
+  DecrementActiveRequests();
 }
 
 void XMLHttpRequest::OnTimeout() {
@@ -856,6 +858,21 @@
   }
 }
 
+void XMLHttpRequest::IncrementActiveRequests() {
+  if (active_requests_count_ == 0) {
+    PreventGarbageCollection();
+  }
+  active_requests_count_++;
+}
+
+void XMLHttpRequest::DecrementActiveRequests() {
+  DCHECK_GT(active_requests_count_, 0);
+  active_requests_count_--;
+  if (active_requests_count_ == 0) {
+    AllowGarbageCollection();
+  }
+}
+
 void XMLHttpRequest::PreventGarbageCollection() {
   settings_->global_environment()->PreventGarbageCollection(
       make_scoped_refptr(this));
diff --git a/src/cobalt/xhr/xml_http_request.h b/src/cobalt/xhr/xml_http_request.h
index ab9a5e4..209292a 100644
--- a/src/cobalt/xhr/xml_http_request.h
+++ b/src/cobalt/xhr/xml_http_request.h
@@ -218,6 +218,13 @@
   void AllowGarbageCollection();
   void StartRequest(const std::string& request_body);
 
+  // The following two methods are used to determine if garbage collection is
+  // needed. It is legal to reuse XHR and send a new request in last request's
+  // onload event listener. We should not allow garbage collection until
+  // the last request is fetched.
+  void IncrementActiveRequests();
+  void DecrementActiveRequests();
+
   // Accessors / mutators for testing.
   const GURL& request_url() const { return request_url_; }
   bool error() const { return error_; }
@@ -271,6 +278,7 @@
   bool sent_;
   bool stop_timeout_;
   bool upload_complete_;
+  int active_requests_count_;
 
   static bool verbose_;
   // Unique ID for debugging.
diff --git a/src/net/base/net_errors_starboard.cc b/src/net/base/net_errors_starboard.cc
index 1b07eeb..d9d6532 100644
--- a/src/net/base/net_errors_starboard.cc
+++ b/src/net/base/net_errors_starboard.cc
@@ -46,6 +46,10 @@
       return OK;
     case kSbSocketPending:
       return ERR_IO_PENDING;
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+    case kSbSocketErrorConnectionReset:
+      return ERR_CONNECTION_RESET;
+#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
     case kSbSocketErrorFailed:
       return ERR_FAILED;
     default:
diff --git a/src/starboard/nplb/socket_accept_test.cc b/src/starboard/nplb/socket_accept_test.cc
index 9c3357c..aceef33 100644
--- a/src/starboard/nplb/socket_accept_test.cc
+++ b/src/starboard/nplb/socket_accept_test.cc
@@ -54,7 +54,7 @@
 
   // Accept should result in an error.
   EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketGetLastError(server_socket));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketGetLastError(server_socket));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
@@ -67,7 +67,7 @@
 
   // Accept should result in an error.
   EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketGetLastError(server_socket));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketGetLastError(server_socket));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
diff --git a/src/starboard/nplb/socket_bind_test.cc b/src/starboard/nplb/socket_bind_test.cc
index f813c0a..d40a9a0 100644
--- a/src/starboard/nplb/socket_bind_test.cc
+++ b/src/starboard/nplb/socket_bind_test.cc
@@ -50,7 +50,7 @@
 TEST_P(SbSocketBindTest, RainyDayNullSocket) {
   SbSocketAddress address =
       GetUnspecifiedAddress(GetAddressType(), GetPortNumberForTests());
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(kSbSocketInvalid, &address));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketBind(kSbSocketInvalid, &address));
 }
 
 TEST_P(SbSocketBindTest, RainyDayNullAddress) {
@@ -58,7 +58,7 @@
   ASSERT_TRUE(SbSocketIsValid(server_socket));
 
   // Binding with a NULL address should fail.
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(server_socket, NULL));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketBind(server_socket, NULL));
 
   // Even though that failed, binding the same socket now with 0.0.0.0:2048
   // should work.
@@ -70,7 +70,7 @@
 }
 
 TEST_F(SbSocketBindTest, RainyDayNullNull) {
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(kSbSocketInvalid, NULL));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketBind(kSbSocketInvalid, NULL));
 }
 
 #if SB_HAS(IPV6)
@@ -81,7 +81,7 @@
   // Binding with the wrong address type should fail.
   SbSocketAddress address =
       GetUnspecifiedAddress(GetClientAddressType(), GetPortNumberForTests());
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(server_socket, &address));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketBind(server_socket, &address));
 
   // Even though that failed, binding the same socket now with the server
   // address type should work.
@@ -105,8 +105,8 @@
       SbSocketResolve(kTestHostName, GetFilterType());
   ASSERT_NE(kNull, resolution);
   EXPECT_LT(0, resolution->address_count);
-  EXPECT_EQ(kSbSocketErrorFailed,
-            SbSocketBind(server_socket, &resolution->addresses[0]));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(
+      SbSocketBind(server_socket, &resolution->addresses[0]));
 
   EXPECT_TRUE(SbSocketDestroy(server_socket));
   SbSocketFreeResolution(resolution);
diff --git a/src/starboard/nplb/socket_clear_last_error_test.cc b/src/starboard/nplb/socket_clear_last_error_test.cc
index bce6015..b5be314 100644
--- a/src/starboard/nplb/socket_clear_last_error_test.cc
+++ b/src/starboard/nplb/socket_clear_last_error_test.cc
@@ -30,7 +30,7 @@
 
   // Accept on the unbound socket should result in an error.
   EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketGetLastError(server_socket));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketGetLastError(server_socket));
 
   // After we clear the error, it should be OK.
   EXPECT_TRUE(SbSocketClearLastError(server_socket));
diff --git a/src/starboard/nplb/socket_connect_test.cc b/src/starboard/nplb/socket_connect_test.cc
index 34378cf..9b82d9c 100644
--- a/src/starboard/nplb/socket_connect_test.cc
+++ b/src/starboard/nplb/socket_connect_test.cc
@@ -31,18 +31,18 @@
 
 TEST_P(SbSocketConnectTest, RainyDayNullSocket) {
   SbSocketAddress address = GetUnspecifiedAddress(GetAddressType(), 2048);
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(kSbSocketInvalid, &address));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketConnect(kSbSocketInvalid, &address));
 }
 
 TEST_P(SbSocketConnectTest, RainyDayNullAddress) {
   SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
   ASSERT_TRUE(SbSocketIsValid(socket));
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(socket, NULL));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketConnect(socket, NULL));
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
 TEST_F(SbSocketConnectTest, RainyDayNullNull) {
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(kSbSocketInvalid, NULL));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketConnect(kSbSocketInvalid, NULL));
 }
 
 #if SB_HAS(IPV6)
diff --git a/src/starboard/nplb/socket_helpers.h b/src/starboard/nplb/socket_helpers.h
index 38086d4..558b249 100644
--- a/src/starboard/nplb/socket_helpers.h
+++ b/src/starboard/nplb/socket_helpers.h
@@ -15,6 +15,8 @@
 #ifndef STARBOARD_NPLB_SOCKET_HELPERS_H_
 #define STARBOARD_NPLB_SOCKET_HELPERS_H_
 
+#include <vector>
+
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/socket.h"
 #include "starboard/socket_waiter.h"
@@ -175,6 +177,34 @@
   EXPECT_LE(timeout, TimedWaitTimed(waiter, timeout));
 }
 
+// Socket operations may return specific (e.g. kSbSocketErrorConnectionReset) or
+// general (e.g. kSbSocketErrorFailed) error codes, and while in some cases
+// it may be important that we obtain a specific error message, in other cases
+// it will just be used as a hint and so these methods are provided to make
+// it easy to test against specific or general errors.
+static inline bool SocketErrorIn(
+    SbSocketError error,
+    const std::vector<SbSocketError>& expected_set) {
+  for (size_t i = 0; i < expected_set.size(); ++i) {
+    if (expected_set[i] == error) {
+      return true;
+    }
+  }
+  return false;
+}
+
+#define EXPECT_SB_SOCKET_ERROR_IN(error, ...)        \
+  do {                                               \
+    EXPECT_TRUE(SocketErrorIn(error, {__VA_ARGS__})) \
+        << "With " #error " = " << error;            \
+  } while (false)
+
+#define EXPECT_SB_SOCKET_ERROR_IS_ERROR(error)                          \
+  do {                                                                  \
+    EXPECT_FALSE(SocketErrorIn(error, {kSbSocketOk, kSbSocketPending})) \
+        << "With " #error " = " << error;                               \
+  } while (false)
+
 }  // namespace nplb
 }  // namespace starboard
 
diff --git a/src/starboard/nplb/socket_listen_test.cc b/src/starboard/nplb/socket_listen_test.cc
index aaeabab..57067e1 100644
--- a/src/starboard/nplb/socket_listen_test.cc
+++ b/src/starboard/nplb/socket_listen_test.cc
@@ -30,7 +30,7 @@
 };
 
 TEST_F(SbSocketListenTest, RainyDayInvalid) {
-  EXPECT_EQ(kSbSocketErrorFailed, SbSocketListen(kSbSocketInvalid));
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketListen(kSbSocketInvalid));
 }
 
 TEST_P(SbSocketListenTest, SunnyDayUnbound) {
diff --git a/src/starboard/nplb/socket_send_to_test.cc b/src/starboard/nplb/socket_send_to_test.cc
index fae82a2..88d4bd0 100644
--- a/src/starboard/nplb/socket_send_to_test.cc
+++ b/src/starboard/nplb/socket_send_to_test.cc
@@ -47,15 +47,14 @@
 
   // Continue sending to the socket until it fails to send. It's expected that
   // SbSocketSendTo will fail when the server socket closes, but the application
-  // should
-  // not terminate.
-  SbTime start = SbTimeGetNow();
+  // should not terminate.
+  SbTime start = SbTimeGetMonotonicNow();
   SbTime now = start;
   SbTime kTimeout = kSbTimeSecond;
   int result = 0;
   while (result >= 0 && (now - start < kTimeout)) {
     result = SbSocketSendTo(trio->server_socket, send_buf, kBufSize, NULL);
-    now = SbTimeGetNow();
+    now = SbTimeGetMonotonicNow();
   }
 
   delete[] send_buf;
@@ -68,6 +67,26 @@
   EXPECT_EQ(-1, result);
 }
 
+TEST(SbSocketSendToTest, RainyDayUnconnectedSocket) {
+  SbSocket socket =
+      SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
+  ASSERT_TRUE(SbSocketIsValid(socket));
+
+  char buf[16];
+  int result = SbSocketSendTo(socket, buf, sizeof(buf), NULL);
+  EXPECT_EQ(-1, result);
+
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+  EXPECT_SB_SOCKET_ERROR_IN(SbSocketGetLastError(socket),
+                            kSbSocketErrorConnectionReset,
+                            kSbSocketErrorFailed);
+#else
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketGetLastError(socket));
+#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+
+  EXPECT_TRUE(SbSocketDestroy(socket));
+}
+
 TEST_P(PairSbSocketSendToTest, RainyDaySendToClosedSocket) {
   ConnectedTrio trio =
       CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
@@ -91,8 +110,14 @@
   // Wait for the thread to exit and check the last socket error.
   void* thread_result;
   EXPECT_TRUE(SbThreadJoin(send_thread, &thread_result));
-  // Check that the server_socket failed, as expected.
-  EXPECT_EQ(SbSocketGetLastError(trio.server_socket), kSbSocketErrorFailed);
+
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+  EXPECT_SB_SOCKET_ERROR_IN(SbSocketGetLastError(trio.server_socket),
+                            kSbSocketErrorConnectionReset,
+                            kSbSocketErrorFailed);
+#else
+  EXPECT_SB_SOCKET_ERROR_IS_ERROR(SbSocketGetLastError(trio.server_socket));
+#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
 
   // Clean up the server socket.
   EXPECT_TRUE(SbSocketDestroy(trio.server_socket));
diff --git a/src/starboard/nplb/socket_wrapper_test.cc b/src/starboard/nplb/socket_wrapper_test.cc
index 1ec2ca4..95127f3 100644
--- a/src/starboard/nplb/socket_wrapper_test.cc
+++ b/src/starboard/nplb/socket_wrapper_test.cc
@@ -38,6 +38,7 @@
   scoped_ptr<ConnectedTrioWrapped> trio =
       CreateAndConnectWrapped(GetServerAddressType(), GetClientAddressType(),
                               GetPortNumberForTests(), kSocketTimeout);
+  ASSERT_TRUE(trio);
   ASSERT_TRUE(trio->server_socket);
   ASSERT_TRUE(trio->server_socket->IsValid());
 
diff --git a/src/starboard/shared/alsa/alsa_util.cc b/src/starboard/shared/alsa/alsa_util.cc
index 3d1c04d..b0f0287 100644
--- a/src/starboard/shared/alsa/alsa_util.cc
+++ b/src/starboard/shared/alsa/alsa_util.cc
@@ -231,14 +231,15 @@
   }
 }
 
-void AlsaDrain(void* playback_handle) {
+bool AlsaDrain(void* playback_handle) {
   if (playback_handle) {
     snd_pcm_t* handle = reinterpret_cast<snd_pcm_t*>(playback_handle);
 
     int error;
     error = snd_pcm_drain(handle);
-    SB_DCHECK(error >= 0);
+    ALSA_CHECK(error, snd_pcm_drain, false);
   }
+  return true;
 }
 
 }  // namespace alsa
diff --git a/src/starboard/shared/alsa/alsa_util.h b/src/starboard/shared/alsa/alsa_util.h
index c0d068f..5815828 100644
--- a/src/starboard/shared/alsa/alsa_util.h
+++ b/src/starboard/shared/alsa/alsa_util.h
@@ -36,7 +36,7 @@
                     int frames_to_write);
 int AlsaGetBufferedFrames(void* playback_handle);
 void AlsaCloseDevice(void* playback_handle);
-void AlsaDrain(void* playback_handle);
+bool AlsaDrain(void* playback_handle);
 
 }  // namespace alsa
 }  // namespace shared
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
index 2984b6a..850838f 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -90,7 +90,11 @@
   packet.data = const_cast<uint8_t*>(input_buffer.data());
   packet.size = input_buffer.size();
 
+#if LIBAVUTIL_VERSION_MAJOR > 52
+  av_frame_unref(av_frame_);
+#else   // LIBAVUTIL_VERSION_MAJOR > 52
   avcodec_get_frame_defaults(av_frame_);
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
   int frame_decoded = 0;
   int result =
       avcodec_decode_audio4(codec_context_, av_frame_, &frame_decoded, &packet);
@@ -246,7 +250,11 @@
     return;
   }
 
+#if LIBAVUTIL_VERSION_MAJOR > 52
+  av_frame_ = av_frame_alloc();
+#else   // LIBAVUTIL_VERSION_MAJOR > 52
   av_frame_ = avcodec_alloc_frame();
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
   if (av_frame_ == NULL) {
     SB_LOG(ERROR) << "Unable to allocate audio frame";
     TeardownCodec();
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.h b/src/starboard/shared/ffmpeg/ffmpeg_common.h
index 4203e91..9c63c1d 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -27,6 +27,16 @@
 
 #include "starboard/shared/internal_only.h"
 
+#if !defined(LIBAVUTIL_VERSION_MAJOR)
+#error "LIBAVUTIL_VERSION_MAJOR not defined"
+#endif  // !defined(LIBAVUTIL_VERSION_MAJOR)
+
+#if LIBAVUTIL_VERSION_MAJOR > 52
+#define PIX_FMT_NONE AV_PIX_FMT_NONE
+#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P
+#define PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
+
 namespace starboard {
 namespace shared {
 namespace ffmpeg {
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index 955cade..e1e9129 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -39,6 +39,58 @@
   return width * height * 3 / 2;
 }
 
+#if LIBAVUTIL_VERSION_MAJOR > 52
+
+void ReleaseBuffer(void* opaque, uint8_t* data) {
+  SbMemorySet(data, 0, sizeof(data));
+  SbMemoryDeallocate(data);
+}
+
+int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame, int flags) {
+  if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
+      codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
+    SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt;
+    return AVERROR(EINVAL);
+  }
+
+  int ret =
+      av_image_check_size(codec_context->width, codec_context->height, 0, NULL);
+  if (ret < 0) {
+    return ret;
+  }
+
+  // Align to kAlignment * 2 as we will divide y_stride by 2 for u and v planes
+  size_t y_stride = AlignUp(codec_context->width, kAlignment * 2);
+  size_t uv_stride = y_stride / 2;
+  size_t aligned_height = AlignUp(codec_context->height, kAlignment * 2);
+
+  uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned(
+      kAlignment, GetYV12SizeInBytes(y_stride, aligned_height)));
+
+  frame->data[0] = frame_buffer;
+  frame->linesize[0] = y_stride;
+
+  frame->data[1] = frame_buffer + y_stride * aligned_height;
+  frame->linesize[1] = uv_stride;
+
+  frame->data[2] = frame->data[1] + uv_stride * aligned_height / 2;
+  frame->linesize[2] = uv_stride;
+
+  frame->opaque = frame;
+  frame->width = codec_context->width;
+  frame->height = codec_context->height;
+  frame->format = codec_context->pix_fmt;
+
+  frame->reordered_opaque = codec_context->reordered_opaque;
+
+  frame->buf[0] = av_buffer_create(frame_buffer,
+                                   GetYV12SizeInBytes(y_stride, aligned_height),
+                                   &ReleaseBuffer, frame->opaque, 0);
+  return 0;
+}
+
+#else  // LIBAVUTIL_VERSION_MAJOR > 52
+
 int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame) {
   if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
       codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
@@ -93,6 +145,7 @@
   SbMemorySet(frame->data, 0, sizeof(frame->data));
 }
 
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
 }  // namespace
 
 VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
@@ -219,7 +272,11 @@
 bool VideoDecoder::DecodePacket(AVPacket* packet) {
   SB_DCHECK(packet != NULL);
 
+#if LIBAVUTIL_VERSION_MAJOR > 52
+  av_frame_unref(av_frame_);
+#else   // LIBAVUTIL_VERSION_MAJOR > 52
   avcodec_get_frame_defaults(av_frame_);
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
   int frame_decoded = 0;
   int result =
       avcodec_decode_video2(codec_context_, av_frame_, &frame_decoded, packet);
@@ -286,8 +343,12 @@
   codec_context_->thread_count = 2;
   codec_context_->opaque = this;
   codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
+#if LIBAVUTIL_VERSION_MAJOR > 52
+  codec_context_->get_buffer2 = AllocateBuffer;
+#else   // LIBAVUTIL_VERSION_MAJOR > 52
   codec_context_->get_buffer = AllocateBuffer;
   codec_context_->release_buffer = ReleaseBuffer;
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
 
   codec_context_->extradata = NULL;
   codec_context_->extradata_size = 0;
@@ -307,7 +368,11 @@
     return;
   }
 
+#if LIBAVUTIL_VERSION_MAJOR > 52
+  av_frame_ = av_frame_alloc();
+#else   // LIBAVUTIL_VERSION_MAJOR > 52
   av_frame_ = avcodec_alloc_frame();
+#endif  // LIBAVUTIL_VERSION_MAJOR > 52
   if (av_frame_ == NULL) {
     SB_LOG(ERROR) << "Unable to allocate audio frame";
     TeardownCodec();
diff --git a/src/starboard/shared/posix/socket_internal.cc b/src/starboard/shared/posix/socket_internal.cc
index 7a74c9e..12778c6 100644
--- a/src/starboard/shared/posix/socket_internal.cc
+++ b/src/starboard/shared/posix/socket_internal.cc
@@ -46,6 +46,12 @@
     case EWOULDBLOCK:
 #endif
       return kSbSocketPending;
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+    case ECONNRESET:
+    case ENETRESET:
+    case EPIPE:
+      return kSbSocketErrorConnectionReset;
+#endif  // #if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
   }
 
   // Here's where we would be more nuanced if we need to be.
diff --git a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
index 033b543..bd9a428 100644
--- a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
@@ -15,6 +15,7 @@
 #include "starboard/shared/starboard/player/filter/player_components.h"
 
 #include "starboard/audio_sink.h"
+#include "starboard/common/scoped_ptr.h"
 #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h"
 #include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
@@ -41,18 +42,16 @@
     return scoped_ptr<PlayerComponents>(NULL);
   }
 
-  AudioDecoderImpl* audio_decoder = new AudioDecoderImpl(
-      audio_parameters.audio_codec, audio_parameters.audio_header);
+  scoped_ptr<AudioDecoderImpl> audio_decoder(new AudioDecoderImpl(
+      audio_parameters.audio_codec, audio_parameters.audio_header));
   if (!audio_decoder->is_valid()) {
-    delete audio_decoder;
     return scoped_ptr<PlayerComponents>(NULL);
   }
 
-  VideoDecoderImpl* video_decoder = new VideoDecoderImpl(
+  scoped_ptr<VideoDecoderImpl> video_decoder(new VideoDecoderImpl(
       video_parameters.video_codec, video_parameters.output_mode,
-      video_parameters.decode_target_graphics_context_provider);
+      video_parameters.decode_target_graphics_context_provider));
   if (!video_decoder->is_valid()) {
-    delete video_decoder;
     return scoped_ptr<PlayerComponents>(NULL);
   }
 
diff --git a/src/starboard/shared/win32/socket_internal.cc b/src/starboard/shared/win32/socket_internal.cc
index d61dc1b..6691afa 100644
--- a/src/starboard/shared/win32/socket_internal.cc
+++ b/src/starboard/shared/win32/socket_internal.cc
@@ -34,9 +34,22 @@
   switch (error) {
     case 0:
       return kSbSocketOk;
+
+    // Microsoft Winsock error codes:
+    //   https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
     case WSAEINPROGRESS:
     case WSAEWOULDBLOCK:
       return kSbSocketPending;
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+    case WSAECONNRESET:
+    case WSAENETRESET:
+      return kSbSocketErrorConnectionReset;
+
+    // Microsoft System Error codes:
+    //   https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
+    case ERROR_BROKEN_PIPE:
+      return kSbSocketErrorConnectionReset;
+#endif  // #if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
   }
 
   // Here's where we would be more nuanced if we need to be.
diff --git a/src/starboard/socket.h b/src/starboard/socket.h
index e44ddc0..115b176 100644
--- a/src/starboard/socket.h
+++ b/src/starboard/socket.h
@@ -64,10 +64,14 @@
   // clever and wait on it with a SbSocketWaiter.
   kSbSocketPending,
 
-  // The operation failed for some reason.
-  //
-  // TODO: It's unclear if we actually care about why, so leaving the rest
-  // of this enumeration blank until it becomes clear that we do.
+#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+  // This socket error is generated when the connection is reset unexpectedly
+  // and the connection is now invalid.
+  // This might happen for example if an read packet has the "TCP RST" bit set.
+  kSbSocketErrorConnectionReset,
+#endif  // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT)
+
+  // The operation failed for some other reason not specified above.
   kSbSocketErrorFailed,
 } SbSocketError;
 
diff --git a/src/starboard/tools/raspi/run_test.py b/src/starboard/tools/raspi/run_test.py
index 58c925c..326b654 100755
--- a/src/starboard/tools/raspi/run_test.py
+++ b/src/starboard/tools/raspi/run_test.py
@@ -132,7 +132,13 @@
     process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
     process.sendline(_RASPI_PASSWORD)
 
-    test_command = raspi_test_path + ' ' + flags
+    # Escape command line metacharacters in the flags
+    meta_chars = '()[]{}%!^"<>&|'
+    meta_re = re.compile('(' + '|'.join(
+        re.escape(char) for char in list(meta_chars)) + ')')
+    escaped_flags = re.subn(meta_re, r'\\\1', flags)[0]
+
+    test_command = raspi_test_path + ' ' + escaped_flags
     test_time_tag = 'TEST-{time}'.format(time=time.time())
     test_success_tag = 'succeeded'
     test_failure_tag = 'failed'
diff --git a/src/third_party/freetype2/src/cff/cffgload.c b/src/third_party/freetype2/src/cff/cffgload.c
index 1b664c3..482cc5a 100644
--- a/src/third_party/freetype2/src/cff/cffgload.c
+++ b/src/third_party/freetype2/src/cff/cffgload.c
@@ -594,6 +594,14 @@
     first = outline->n_contours <= 1

             ? 0 : outline->contours[outline->n_contours - 2] + 1;

 

+    /* in malformed fonts it can happen that a contour was started */

+    /* but no points were added                                    */

+    if ( outline->n_contours && first == outline->n_points )

+    {

+      outline->n_contours--;

+      return;

+    }

+

     /* We must not include the last point in the path if it */

     /* is located on the first point.                       */

     if ( outline->n_points > 1 )

diff --git a/src/third_party/libxml/src/libxml.h b/src/third_party/libxml/src/libxml.h
index 572de43..7301d38 100644
--- a/src/third_party/libxml/src/libxml.h
+++ b/src/third_party/libxml/src/libxml.h
@@ -9,6 +9,8 @@
 #ifndef __XML_LIBXML_H__
 #define __XML_LIBXML_H__
 
+#include <libxml/xmlstring.h>
+
 #ifndef NO_LARGEFILE_SOURCE
 #ifndef _LARGEFILE_SOURCE
 #define _LARGEFILE_SOURCE
@@ -95,6 +97,7 @@
 int __xmlRandom(void);
 #endif
 
+XMLPUBFUN xmlChar * XMLCALL xmlEscapeFormatString(xmlChar **msg);
 int xmlNop(void);
 
 #ifdef IN_LIBXML
diff --git a/src/third_party/libxml/src/parser.c b/src/third_party/libxml/src/parser.c
index 855b5ea..22dabb8 100644
--- a/src/third_party/libxml/src/parser.c
+++ b/src/third_party/libxml/src/parser.c
@@ -2105,7 +2105,6 @@
 	ctxt->input->line++; ctxt->input->col = 1;			\
     } else ctxt->input->col++;						\
     ctxt->input->cur += l;				\
-    if (*ctxt->input->cur == '%') xmlParserHandlePEReference(ctxt);	\
   } while (0)
 
 #define CUR_CHAR(l) xmlCurrentChar(ctxt, &l)
@@ -3376,13 +3375,6 @@
 	    len += l;
 	    NEXTL(l);
 	    c = CUR_CHAR(l);
-	    if (c == 0) {
-		count = 0;
-		GROW;
-                if (ctxt->instate == XML_PARSER_EOF)
-                    return(NULL);
-		c = CUR_CHAR(l);
-	    }
 	}
     }
     if ((len > XML_MAX_NAME_LENGTH) &&
@@ -3390,6 +3382,16 @@
         xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Name");
         return(NULL);
     }
+    if (ctxt->input->cur - ctxt->input->base < len) {
+        /*
+         * There were a couple of bugs where PERefs lead to to a change
+         * of the buffer. Check the buffer size to avoid passing an invalid
+         * pointer to xmlDictLookup.
+        */
+        xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR,
+                    "unexpected change of input buffer");
+        return (NULL);
+    }
     if ((*ctxt->input->cur == '\n') && (ctxt->input->cur[-1] == '\r'))
         return(xmlDictLookup(ctxt->dict, ctxt->input->cur - (len + 1), len));
     return(xmlDictLookup(ctxt->dict, ctxt->input->cur - len, len));
diff --git a/src/third_party/libxml/src/relaxng.c b/src/third_party/libxml/src/relaxng.c
index 68c3c24..9dccfd7 100644
--- a/src/third_party/libxml/src/relaxng.c
+++ b/src/third_party/libxml/src/relaxng.c
@@ -2219,7 +2219,8 @@
         XML_SNPRINTF(msg, 1000, "Unknown error code %d\n", err);
     }
     msg[1000 - 1] = 0;
-    return (xmlStrdup((xmlChar *) msg));
+    xmlChar *result = xmlCharStrdup(msg);
+    return (xmlEscapeFormatString(&result));
 }
 
 /**
diff --git a/src/third_party/libxml/src/xmlIO.c b/src/third_party/libxml/src/xmlIO.c
index 6d1fd67..2df8c26 100644
--- a/src/third_party/libxml/src/xmlIO.c
+++ b/src/third_party/libxml/src/xmlIO.c
@@ -1704,7 +1704,7 @@
     else {
 	xmlChar msg[500];
 	xmlStrPrintf(msg, 500,
-		    (const xmlChar *) "xmlZMemBuffExtend:  %s %lu bytes.\n",
+		    "xmlZMemBuffExtend:  %s %lu bytes.\n",
 		    "Allocation failure extending output buffer to",
 		    new_size );
 	xmlIOErr(XML_IO_WRITE, (const char *) msg);
@@ -1750,7 +1750,7 @@
 	if ( z_err != Z_OK ) {
 	    xmlChar msg[500];
 	    xmlStrPrintf(msg, 500,
-			(const xmlChar *) "xmlZMemBuffAppend:  %s %d %s - %d",
+			"xmlZMemBuffAppend:  %s %d %s - %d",
 			"Compression error while appending",
 			len, "bytes to buffer.  ZLIB error", z_err );
 	    xmlIOErr(XML_IO_WRITE, (const char *) msg);
@@ -1823,7 +1823,7 @@
     else {
 	xmlChar msg[500];
 	xmlStrPrintf(msg, 500,
-		    (const xmlChar *) "xmlZMemBuffGetContent:  %s - %d\n",
+		    "xmlZMemBuffGetContent:  %s - %d\n",
 		    "Error flushing zlib buffers.  Error code", z_err );
 	xmlIOErr(XML_IO_WRITE, (const char *) msg);
     }
@@ -2028,7 +2028,7 @@
 	if ( len < 0 ) {
 	    xmlChar msg[500];
 	    xmlStrPrintf(msg, 500,
-			(const xmlChar *) "xmlIOHTTPWrite:  %s\n%s '%s'.\n",
+			"xmlIOHTTPWrite:  %s\n%s '%s'.\n",
 			"Error appending to internal buffer.",
 			"Error sending document to URI",
 			ctxt->uri );
@@ -2100,7 +2100,7 @@
     if ( http_content == NULL ) {
 	xmlChar msg[500];
 	xmlStrPrintf(msg, 500,
-		     (const xmlChar *) "xmlIOHTTPCloseWrite:  %s '%s' %s '%s'.\n",
+		     "xmlIOHTTPCloseWrite:  %s '%s' %s '%s'.\n",
 		     "Error retrieving content.\nUnable to",
 		     http_mthd, "data to URI", ctxt->uri );
 	xmlIOErr(XML_IO_WRITE, (const char *) msg);
@@ -2172,7 +2172,7 @@
 	    else {
                 xmlChar msg[500];
                 xmlStrPrintf(msg, 500,
-    (const xmlChar *) "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
+                    "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
 			    http_mthd, content_lgth,
 			    "bytes to URI", ctxt->uri,
 			    "failed.  HTTP return code:", http_rtn );
diff --git a/src/third_party/libxml/src/xmlschemas.c b/src/third_party/libxml/src/xmlschemas.c
index efdf6a8..3755cab 100644
--- a/src/third_party/libxml/src/xmlschemas.c
+++ b/src/third_party/libxml/src/xmlschemas.c
@@ -1087,7 +1087,7 @@
 static void
 xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt,
 		     const char *funcName,
-		     const char *message);
+		     const char *message) LIBXML_ATTR_FORMAT(3,0);
 static int
 xmlSchemaCheckCOSSTDerivedOK(xmlSchemaAbstractCtxtPtr ctxt,
 			     xmlSchemaTypePtr type,
@@ -1771,7 +1771,7 @@
     }
     FREE_AND_NULL(str)
 
-    return (*buf);
+    return (xmlEscapeFormatString(buf));
 }
 
 /**
@@ -1891,7 +1891,7 @@
  *
  * Handle a parser error
  */
-static void
+static void LIBXML_ATTR_FORMAT(4,0)
 xmlSchemaPErr(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int error,
               const char *msg, const xmlChar * str1, const xmlChar * str2)
 {
@@ -1924,7 +1924,7 @@
  *
  * Handle a parser error
  */
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaPErr2(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node,
                xmlNodePtr child, int error,
                const char *msg, const xmlChar * str1, const xmlChar * str2)
@@ -1953,7 +1953,7 @@
  *
  * Handle a parser error
  */
-static void
+static void LIBXML_ATTR_FORMAT(7,0)
 xmlSchemaPErrExt(xmlSchemaParserCtxtPtr ctxt, xmlNodePtr node, int error,
 		const xmlChar * strData1, const xmlChar * strData2,
 		const xmlChar * strData3, const char *msg, const xmlChar * str1,
@@ -2004,7 +2004,7 @@
                      extra);
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(2,0)
 xmlSchemaPSimpleInternalErr(xmlNodePtr node,
 			    const char *msg, const xmlChar *str)
 {
@@ -2015,18 +2015,21 @@
 #define WXS_ERROR_TYPE_ERROR 1
 #define WXS_ERROR_TYPE_WARNING 2
 /**
- * xmlSchemaErr3:
+ * xmlSchemaErr4Line:
  * @ctxt: the validation context
- * @node: the context node
+ * @errorLevel: the error level
  * @error: the error code
+ * @node: the context node
+ * @line: the line number
  * @msg: the error message
  * @str1: extra data
  * @str2: extra data
  * @str3: extra data
+ * @str4: extra data
  *
  * Handle a validation error
  */
-static void
+static void LIBXML_ATTR_FORMAT(6,0)
 xmlSchemaErr4Line(xmlSchemaAbstractCtxtPtr ctxt,
 		  xmlErrorLevel errorLevel,
 		  int error, xmlNodePtr node, int line, const char *msg,
@@ -2141,7 +2144,7 @@
  *
  * Handle a validation error
  */
-static void
+static void LIBXML_ATTR_FORMAT(4,0)
 xmlSchemaErr3(xmlSchemaAbstractCtxtPtr actxt,
 	      int error, xmlNodePtr node, const char *msg,
 	      const xmlChar *str1, const xmlChar *str2, const xmlChar *str3)
@@ -2150,7 +2153,7 @@
 	msg, str1, str2, str3, NULL);
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(4,0)
 xmlSchemaErr4(xmlSchemaAbstractCtxtPtr actxt,
 	      int error, xmlNodePtr node, const char *msg,
 	      const xmlChar *str1, const xmlChar *str2,
@@ -2160,7 +2163,7 @@
 	msg, str1, str2, str3, str4);
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(4,0)
 xmlSchemaErr(xmlSchemaAbstractCtxtPtr actxt,
 	     int error, xmlNodePtr node, const char *msg,
 	     const xmlChar *str1, const xmlChar *str2)
@@ -2183,7 +2186,7 @@
 	/*
 	* Don't try to format other nodes than element and
 	* attribute nodes.
-	* Play save and return an empty string.
+	* Play safe and return an empty string.
 	*/
 	*msg = xmlStrdup(BAD_CAST "");
 	return(*msg);
@@ -2248,6 +2251,13 @@
 	TODO
 	return (NULL);
     }
+
+    /*
+     * xmlSchemaFormatItemForReport() also returns an escaped format
+     * string, so do this before calling it below (in the future).
+     */
+    xmlEscapeFormatString(msg);
+
     /*
     * VAL TODO: The output of the given schema component is currently
     * disabled.
@@ -2264,7 +2274,7 @@
     return (*msg);
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(3,0)
 xmlSchemaInternalErr2(xmlSchemaAbstractCtxtPtr actxt,
 		     const char *funcName,
 		     const char *message,
@@ -2275,24 +2285,21 @@
 
     if (actxt == NULL)
         return;
-    msg = xmlStrdup(BAD_CAST "Internal error: ");
-    msg = xmlStrcat(msg, BAD_CAST funcName);
-    msg = xmlStrcat(msg, BAD_CAST ", ");
+    msg = xmlStrdup(BAD_CAST "Internal error: %s, ");
     msg = xmlStrcat(msg, BAD_CAST message);
     msg = xmlStrcat(msg, BAD_CAST ".\n");
 
     if (actxt->type == XML_SCHEMA_CTXT_VALIDATOR)
-	xmlSchemaErr(actxt, XML_SCHEMAV_INTERNAL, NULL,
-	    (const char *) msg, str1, str2);
-
+	xmlSchemaErr3(actxt, XML_SCHEMAV_INTERNAL, NULL,
+        (const char *) msg, (const xmlChar *) funcName, str1, str2);
     else if (actxt->type == XML_SCHEMA_CTXT_PARSER)
-	xmlSchemaErr(actxt, XML_SCHEMAP_INTERNAL, NULL,
-	    (const char *) msg, str1, str2);
+	xmlSchemaErr3(actxt, XML_SCHEMAP_INTERNAL, NULL,
+        (const char *) msg, (const xmlChar *) funcName, str1, str2);
 
     FREE_AND_NULL(msg)
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(3,0)
 xmlSchemaInternalErr(xmlSchemaAbstractCtxtPtr actxt,
 		     const char *funcName,
 		     const char *message)
@@ -2301,7 +2308,7 @@
 }
 
 #if 0
-static void
+static void LIBXML_ATTR_FORMAT(3,0)
 xmlSchemaPInternalErr(xmlSchemaParserCtxtPtr pctxt,
 		     const char *funcName,
 		     const char *message,
@@ -2313,7 +2320,7 @@
 }
 #endif
 
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaCustomErr4(xmlSchemaAbstractCtxtPtr actxt,
 		   xmlParserErrors error,
 		   xmlNodePtr node,
@@ -2338,7 +2345,7 @@
     FREE_AND_NULL(msg)
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaCustomErr(xmlSchemaAbstractCtxtPtr actxt,
 		   xmlParserErrors error,
 		   xmlNodePtr node,
@@ -2353,7 +2360,7 @@
 
 
 
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaCustomWarning(xmlSchemaAbstractCtxtPtr actxt,
 		   xmlParserErrors error,
 		   xmlNodePtr node,
@@ -2378,7 +2385,7 @@
 
 
 
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaKeyrefErr(xmlSchemaValidCtxtPtr vctxt,
 		   xmlParserErrors error,
 		   xmlSchemaPSVIIDCNodePtr idcNode,
@@ -2478,11 +2485,13 @@
 	msg = xmlStrcat(msg, BAD_CAST " '");
 	if (type->builtInType != 0) {
 	    msg = xmlStrcat(msg, BAD_CAST "xs:");
-	    msg = xmlStrcat(msg, type->name);
-	} else
-	    msg = xmlStrcat(msg,
-		xmlSchemaFormatQName(&str,
-		    type->targetNamespace, type->name));
+	    str = xmlStrdup(type->name);
+	} else {
+		const xmlChar *qName = xmlSchemaFormatQName(&str, type->targetNamespace, type->name);
+		if (!str)
+		str = xmlStrdup(qName);
+	}
+	msg = xmlStrcat(msg, xmlEscapeFormatString(&str));
 	msg = xmlStrcat(msg, BAD_CAST "'");
 	FREE_AND_NULL(str);
     }
@@ -2527,7 +2536,7 @@
     FREE_AND_NULL(msg)
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaComplexTypeErr(xmlSchemaAbstractCtxtPtr actxt,
 		        xmlParserErrors error,
 		        xmlNodePtr node,
@@ -2619,7 +2628,7 @@
 		str = xmlStrcat(str, BAD_CAST ", ");
 	}
 	str = xmlStrcat(str, BAD_CAST " ).\n");
-	msg = xmlStrcat(msg, BAD_CAST str);
+	msg = xmlStrcat(msg, xmlEscapeFormatString(&str));
 	FREE_AND_NULL(str)
     } else
       msg = xmlStrcat(msg, BAD_CAST "\n");
@@ -2627,7 +2636,7 @@
     xmlFree(msg);
 }
 
-static void
+static void LIBXML_ATTR_FORMAT(8,0)
 xmlSchemaFacetErr(xmlSchemaAbstractCtxtPtr actxt,
 		  xmlParserErrors error,
 		  xmlNodePtr node,
@@ -2918,7 +2927,7 @@
  *
  * Reports an error during parsing.
  */
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaPCustomErrExt(xmlSchemaParserCtxtPtr ctxt,
 		    xmlParserErrors error,
 		    xmlSchemaBasicItemPtr item,
@@ -2954,7 +2963,7 @@
  *
  * Reports an error during parsing.
  */
-static void
+static void LIBXML_ATTR_FORMAT(5,0)
 xmlSchemaPCustomErr(xmlSchemaParserCtxtPtr ctxt,
 		    xmlParserErrors error,
 		    xmlSchemaBasicItemPtr item,
@@ -2979,7 +2988,7 @@
  *
  * Reports an attribute use error during parsing.
  */
-static void
+static void LIBXML_ATTR_FORMAT(6,0)
 xmlSchemaPAttrUseErr4(xmlSchemaParserCtxtPtr ctxt,
 		    xmlParserErrors error,
 		    xmlNodePtr node,
@@ -3101,7 +3110,7 @@
  * Reports a simple type validation error.
  * TODO: Should this report the value of an element as well?
  */
-static void
+static void LIBXML_ATTR_FORMAT(8,0)
 xmlSchemaPSimpleTypeErr(xmlSchemaParserCtxtPtr ctxt,
 			xmlParserErrors error,
 			xmlSchemaBasicItemPtr ownerItem ATTRIBUTE_UNUSED,
@@ -3143,11 +3152,13 @@
 		msg = xmlStrcat(msg, BAD_CAST " '");
 		if (type->builtInType != 0) {
 		    msg = xmlStrcat(msg, BAD_CAST "xs:");
-		    msg = xmlStrcat(msg, type->name);
-		} else
-		    msg = xmlStrcat(msg,
-			xmlSchemaFormatQName(&str,
-			    type->targetNamespace, type->name));
+		    str = xmlStrdup(type->name);
+		} else {
+			const xmlChar *qName = xmlSchemaFormatQName(&str, type->targetNamespace, type->name);
+			if (!str)
+				str = xmlStrdup(qName);
+		}
+		msg = xmlStrcat(msg, xmlEscapeFormatString(&str));
 		msg = xmlStrcat(msg, BAD_CAST "'.");
 		FREE_AND_NULL(str);
 	    }
@@ -3160,7 +3171,9 @@
 	}
 	if (expected) {
 	    msg = xmlStrcat(msg, BAD_CAST " Expected is '");
-	    msg = xmlStrcat(msg, BAD_CAST expected);
+	    xmlChar *expectedEscaped = xmlCharStrdup(expected);
+	    msg = xmlStrcat(msg, xmlEscapeFormatString(&expectedEscaped));
+	    FREE_AND_NULL(expectedEscaped);
 	    msg = xmlStrcat(msg, BAD_CAST "'.\n");
 	} else
 	    msg = xmlStrcat(msg, BAD_CAST "\n");
diff --git a/src/third_party/libxml/src/xmlstring.c b/src/third_party/libxml/src/xmlstring.c
index 0a268fc..c0cbcb1 100644
--- a/src/third_party/libxml/src/xmlstring.c
+++ b/src/third_party/libxml/src/xmlstring.c
@@ -984,5 +984,60 @@
     return(xmlUTF8Strndup(utf, len));
 }
 
+/**
+ * xmlEscapeFormatString:
+ * @msg:  a pointer to the string in which to escape '%' characters.
+ * Must be a heap-allocated buffer created by libxml2 that may be
+ * returned, or that may be freed and replaced.
+ *
+ * Replaces the string pointed to by 'msg' with an escaped string.
+ * Returns the same string with all '%' characters escaped.
+ */
+xmlChar *
+xmlEscapeFormatString(xmlChar **msg)
+{
+    xmlChar *msgPtr = NULL;
+    xmlChar *result = NULL;
+    xmlChar *resultPtr = NULL;
+    size_t count = 0;
+    size_t msgLen = 0;
+    size_t resultLen = 0;
+
+    if (!msg || !*msg)
+        return(NULL);
+
+    for (msgPtr = *msg; *msgPtr != '\0'; ++msgPtr) {
+        ++msgLen;
+        if (*msgPtr == '%')
+            ++count;
+    }
+
+    if (count == 0)
+        return(*msg);
+
+    resultLen = msgLen + count + 1;
+    result = (xmlChar *) xmlMallocAtomic(resultLen * sizeof(xmlChar));
+    if (result == NULL) {
+        /* Clear *msg to prevent format string vulnerabilities in
+           out-of-memory situations. */
+        xmlFree(*msg);
+        *msg = NULL;
+        xmlErrMemory(NULL, NULL);
+        return(NULL);
+    }
+
+    for (msgPtr = *msg, resultPtr = result; *msgPtr != '\0'; ++msgPtr, ++resultPtr) {
+        *resultPtr = *msgPtr;
+        if (*msgPtr == '%')
+            *(++resultPtr) = '%';
+    }
+    result[resultLen - 1] = '\0';
+
+    xmlFree(*msg);
+    *msg = result;
+
+    return *msg;
+}
+
 #define bottom_xmlstring
 #include "elfgcchack.h"