Import Cobalt 13.95615

Change-Id: Id63ba05afe04cf6cef5344b716b7f5a2929de0a5
diff --git a/src/base/base_paths_starboard.cc b/src/base/base_paths_starboard.cc
index e02d2f4..b3b6e8e 100644
--- a/src/base/base_paths_starboard.cc
+++ b/src/base/base_paths_starboard.cc
@@ -91,25 +91,21 @@
       return PathProviderStarboard(base::DIR_CACHE, result);
 
     case base::DIR_SYSTEM_FONTS:
-#if SB_API_VERSION >= 4
       if (SbSystemGetPath(kSbSystemPathFontDirectory, path,
                           SB_ARRAY_SIZE_INT(path))) {
         *result = FilePath(path);
         return true;
       }
       DLOG(INFO) << "DIR_SYSTEM_FONTS not defined.";
-#endif  // SB_API_VERSION >= 4
       return false;
 
     case base::DIR_SYSTEM_FONTS_CONFIGURATION:
-#if SB_API_VERSION >= 4
       if (SbSystemGetPath(kSbSystemPathFontConfigurationDirectory, path,
                           SB_ARRAY_SIZE_INT(path))) {
         *result = FilePath(path);
         return true;
       }
       DLOG(INFO) << "DIR_SYSTEM_FONTS_CONFIGURATION not defined.";
-#endif  // SB_API_VERSION >= 4
       return false;
   }
 
diff --git a/src/base/time_starboard.cc b/src/base/time_starboard.cc
index 9225951..75dd719 100644
--- a/src/base/time_starboard.cc
+++ b/src/base/time_starboard.cc
@@ -98,7 +98,7 @@
 
 // static
 TimeTicks TimeTicks::ThreadNow() {
-#if SB_API_VERSION >= 3 && SB_HAS(TIME_THREAD_NOW)
+#if SB_HAS(TIME_THREAD_NOW)
   return TimeTicks(SbTimeGetMonotonicThreadNow());
 #else
   return HighResNow();
@@ -107,7 +107,7 @@
 
 // static
 bool TimeTicks::HasThreadNow() {
-#if SB_API_VERSION >= 3 && SB_HAS(TIME_THREAD_NOW)
+#if SB_HAS(TIME_THREAD_NOW)
   return true;
 #else
   return false;
diff --git a/src/cobalt/base/c_val_collection_timer_stats.h b/src/cobalt/base/c_val_collection_timer_stats.h
index bee81ee..2fdd87a 100644
--- a/src/cobalt/base/c_val_collection_timer_stats.h
+++ b/src/cobalt/base/c_val_collection_timer_stats.h
@@ -71,6 +71,9 @@
     start_time_ = base::TimeTicks();
   }
 
+  // Stops any started timer and this sample is ignored.
+  void Cancel() { start_time_ = base::TimeTicks(); }
+
   // Flush the collection. This causes |CValCollectionStats| to update its stats
   // and clear the entries.
   void Flush() { entry_stats_.Flush(); }
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 97fff2f..1629c67 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -96,6 +96,8 @@
         'suspend_fuzzer.h',
         'switches.cc',
         'switches.h',
+        'system_platform_error_handler.cc',
+        'system_platform_error_handler.h',
         'trace_manager.cc',
         'trace_manager.h',
         'url_handler.cc',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index cfc4a45..870d7b5 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/browser/browser_module.h"
 
+#include <algorithm>
 #include <vector>
 
 #include "base/bind.h"
@@ -252,10 +253,13 @@
       timeout_polling_thread_(kTimeoutPollingThreadName),
       render_timeout_count_(0),
 #endif
+      on_error_retry_count_(0),
       will_quit_(false),
       application_state_(initial_application_state),
       splash_screen_cache_(new SplashScreenCache()),
-      produced_render_tree_(false) {
+      navigation_produced_main_render_tree_(false) {
+  h5vcc_url_handler_.reset(new H5vccURLHandler(this));
+
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
   on_error_triggered_count_ = 0;
@@ -330,12 +334,15 @@
 
   fallback_splash_screen_url_ = options.fallback_splash_screen_url;
   // Synchronously construct our WebModule object.
-  NavigateInternal(url);
+  Navigate(url);
   DCHECK(web_module_);
 }
 
 BrowserModule::~BrowserModule() {
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  if (on_error_retry_timer_.IsRunning()) {
+    on_error_retry_timer_.Stop();
+  }
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   SbCoreDumpUnregisterHandler(BrowserModule::CoreDumpHandler, this);
 #endif
@@ -343,42 +350,17 @@
 
 void BrowserModule::Navigate(const GURL& url) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Navigate()");
+  // Reset the waitable event regardless of the thread. This ensures that the
+  // webdriver won't incorrectly believe that the webmodule has finished loading
+  // when it calls Navigate() and waits for the |web_module_loaded_| signal.
   web_module_loaded_.Reset();
 
-  // Always post this as a task in case this is being called from the WebModule.
-  self_message_loop_->PostTask(
-      FROM_HERE, base::Bind(&BrowserModule::NavigateInternal, weak_this_, url));
-}
-
-void BrowserModule::Reload() {
-  TRACE_EVENT0("cobalt::browser", "BrowserModule::Reload()");
-  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
-  DCHECK(web_module_);
-  web_module_->ExecuteJavascript(
-      "location.reload();",
-      base::SourceLocation("[object BrowserModule]", 1, 1),
-      NULL /* output: succeeded */);
-}
-
-#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
-// static
-void BrowserModule::CoreDumpHandler(void* browser_module_as_void) {
-  BrowserModule* browser_module =
-      static_cast<BrowserModule*>(browser_module_as_void);
-  SbCoreDumpLogInteger("BrowserModule.on_error_triggered_count_",
-                       browser_module->on_error_triggered_count_);
-#if defined(COBALT_CHECK_RENDER_TIMEOUT)
-  SbCoreDumpLogInteger("BrowserModule.recovery_mechanism_triggered_count_",
-                       browser_module->recovery_mechanism_triggered_count_);
-  SbCoreDumpLogInteger("BrowserModule.timeout_response_trigger_count_",
-                       browser_module->timeout_response_trigger_count_);
-#endif  // defined(COBALT_CHECK_RENDER_TIMEOUT)
-}
-#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
-
-void BrowserModule::NavigateInternal(const GURL& url) {
-  TRACE_EVENT0("cobalt::browser", "BrowserModule::NavigateInternal()");
-  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  // Repost to our own message loop if necessary.
+  if (MessageLoop::current() != self_message_loop_) {
+    self_message_loop_->PostTask(
+        FROM_HERE, base::Bind(&BrowserModule::Navigate, weak_this_, url));
+    return;
+  }
 
   // First try the registered handlers (e.g. for h5vcc://). If one of these
   // handles the URL, we don't use the web module.
@@ -386,6 +368,11 @@
     return;
   }
 
+  on_error_url_.clear();
+  if (on_error_retry_timer_.IsRunning()) {
+    on_error_retry_timer_.Stop();
+  }
+
   // Destroy old WebModule first, so we don't get a memory high-watermark after
   // the second WebModule's constructor runs, but before scoped_ptr::reset() is
   // run.
@@ -402,12 +389,15 @@
   const math::Size& viewport_size = GetViewportSize();
 
   DestroySplashScreen();
+  navigation_produced_main_render_tree_ = false;
   base::optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
   if (fallback_splash_screen_url_ ||
       (key && splash_screen_cache_->IsSplashScreenCached(*key))) {
     // Create the splash screen layer.
-    splash_screen_layer_ =
-        render_tree_combiner_->CreateLayer(kSplashScreenZIndex);
+    if (render_tree_combiner_) {
+      splash_screen_layer_ =
+          render_tree_combiner_->CreateLayer(kSplashScreenZIndex);
+    }
 
     splash_screen_.reset(new SplashScreen(
         application_state_,
@@ -470,6 +460,32 @@
   }
 }
 
+void BrowserModule::Reload() {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::Reload()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  DCHECK(web_module_);
+  web_module_->ExecuteJavascript(
+      "location.reload();",
+      base::SourceLocation("[object BrowserModule]", 1, 1),
+      NULL /* output: succeeded */);
+}
+
+#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+// static
+void BrowserModule::CoreDumpHandler(void* browser_module_as_void) {
+  BrowserModule* browser_module =
+      static_cast<BrowserModule*>(browser_module_as_void);
+  SbCoreDumpLogInteger("BrowserModule.on_error_triggered_count_",
+                       browser_module->on_error_triggered_count_);
+#if defined(COBALT_CHECK_RENDER_TIMEOUT)
+  SbCoreDumpLogInteger("BrowserModule.recovery_mechanism_triggered_count_",
+                       browser_module->recovery_mechanism_triggered_count_);
+  SbCoreDumpLogInteger("BrowserModule.timeout_response_trigger_count_",
+                       browser_module->timeout_response_trigger_count_);
+#endif  // defined(COBALT_CHECK_RENDER_TIMEOUT)
+}
+#endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
+
 void BrowserModule::OnLoad() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnLoad()");
   // Repost to our own message loop if necessary. This also prevents
@@ -484,6 +500,9 @@
   // changed unless the corresponding benchmark logic is changed as well.
   LOG(INFO) << "Loaded WebModule";
 
+  // Clear |on_error_retry_count_| after a successful load.
+  on_error_retry_count_ = 0;
+
   on_load_event_time_ = base::TimeTicks::Now().ToInternalValue();
   web_module_loaded_.Signal();
 }
@@ -541,15 +560,16 @@
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnRenderTreeProduced()");
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
 
-  if (splash_screen_ && !produced_render_tree_) {
+  if (splash_screen_ && !navigation_produced_main_render_tree_) {
     splash_screen_->Shutdown();
   }
-  produced_render_tree_ = true;
-
   if (application_state_ == base::kApplicationStatePreloading ||
       !render_tree_combiner_ || !main_web_module_layer_) {
     return;
   }
+
+  navigation_produced_main_render_tree_ = true;
+
   renderer::Submission renderer_submission(layout_results.render_tree,
                                            layout_results.layout_time);
   renderer_submission.on_rasterized_callback = base::Bind(
@@ -760,16 +780,44 @@
 
 void BrowserModule::OnError(const GURL& url, const std::string& error) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnError()");
+  if (MessageLoop::current() != self_message_loop_) {
+    self_message_loop_->PostTask(
+        FROM_HERE, base::Bind(&BrowserModule::OnError, weak_this_, url, error));
+    return;
+  }
+
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   on_error_triggered_count_++;
 #endif
-  LOG(ERROR) << error;
-  std::string url_string = "h5vcc://network-failure";
 
-  // Retry the current URL.
-  url_string += "?retry-url=" + url.spec();
+  on_error_url_ = url.spec();
 
-  Navigate(GURL(url_string));
+  // Start the OnErrorRetry() timer if it isn't already running.
+  // The minimum delay between calls to OnErrorRetry() exponentially grows as
+  // |on_error_retry_count_| increases. |on_error_retry_count_| is reset when
+  // OnLoad() is called.
+  if (!on_error_retry_timer_.IsRunning()) {
+    const int64 kBaseRetryDelayInMilliseconds = 1000;
+    // Cap the max error shift at 10 (1024 * kBaseDelayInMilliseconds)
+    // This results in the minimum delay being capped at ~17 minutes.
+    const int kMaxOnErrorRetryCountShift = 10;
+    int64 min_delay = kBaseRetryDelayInMilliseconds << std::min(
+                          kMaxOnErrorRetryCountShift, on_error_retry_count_);
+    int64 required_delay = std::max(
+        min_delay -
+            (base::TimeTicks::Now() - on_error_retry_time_).InMilliseconds(),
+        static_cast<int64>(0));
+
+    on_error_retry_timer_.Start(
+        FROM_HERE, base::TimeDelta::FromMilliseconds(required_delay), this,
+        &BrowserModule::OnErrorRetry);
+  }
+}
+
+void BrowserModule::OnErrorRetry() {
+  ++on_error_retry_count_;
+  on_error_retry_time_ = base::TimeTicks::Now();
+  TryURLHandlers(GURL("h5vcc://network-failure?retry-url=" + on_error_url_));
 }
 
 bool BrowserModule::FilterKeyEvent(base::Token type,
@@ -1091,9 +1139,6 @@
 #if defined(ENABLE_SCREENSHOT)
   screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
 #endif  // defined(ENABLE_SCREENSHOT)
-  // TODO: Pass in dialog closure instead of system window, and initialize
-  // earlier.
-  h5vcc_url_handler_.reset(new H5vccURLHandler(this, system_window_.get()));
 
   media_module_ =
       media::MediaModule::Create(system_window_.get(), GetResourceProvider(),
@@ -1211,6 +1256,12 @@
     FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_,
                       Resume(resource_provider));
   }
+
+  // If no navigate has occurred since the last OnError call, then attempt to
+  // navigate to |on_error_url_| now.
+  if (!on_error_url_.empty()) {
+    Navigate(GURL(on_error_url_));
+  }
 }
 
 math::Size BrowserModule::GetViewportSize() {
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 386eb0e..16961ec 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -23,6 +23,7 @@
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
+#include "base/timer.h"
 #include "cobalt/account/account_manager.h"
 #include "cobalt/base/application_state.h"
 #include "cobalt/base/message_queue.h"
@@ -34,6 +35,7 @@
 #include "cobalt/browser/screen_shot_writer.h"
 #include "cobalt/browser/splash_screen.h"
 #include "cobalt/browser/suspend_fuzzer.h"
+#include "cobalt/browser/system_platform_error_handler.h"
 #include "cobalt/browser/url_handler.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/dom/array_buffer.h"
@@ -107,6 +109,10 @@
   // Reloads web module.
   void Reload();
 
+  SystemPlatformErrorHandler* system_platform_error_handler() {
+    return &system_platform_error_handler_;
+  }
+
   // Adds/removes a URL handler.
   void AddURLHandler(const URLHandler::URLHandlerCallback& callback);
   void RemoveURLHandler(const URLHandler::URLHandlerCallback& callback);
@@ -159,9 +165,6 @@
 #endif  // defined(COBALT_CHECK_RENDER_TIMEOUT)
 #endif  // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
 
-  // Recreates web module with the given URL.
-  void NavigateInternal(const GURL& url);
-
   // Called when the WebModule's Window.onload event is fired.
   void OnLoad();
 
@@ -214,6 +217,10 @@
   // Error callback for any error that stops the program.
   void OnError(const GURL& url, const std::string& error);
 
+  // OnErrorRetry() runs a retry URL through the URL handlers. It should only be
+  // called by |on_error_retry_timer_|.
+  void OnErrorRetry();
+
   // Filters a key event.
   // Returns true if the event should be passed on to other handlers,
   // false if it was consumed within this function.
@@ -328,6 +335,9 @@
   // The browser module runs on this message loop.
   MessageLoop* const self_message_loop_;
 
+  // Handler for system errors, which is owned by browser module.
+  SystemPlatformErrorHandler system_platform_error_handler_;
+
   // Collection of URL handlers that can potentially handle a URL before
   // using it to initialize a new WebModule.
   URLHandlerCollection url_handlers_;
@@ -448,6 +458,20 @@
   int render_timeout_count_;
 #endif
 
+  // The URL associated with the last OnError() call. It is cleared on the next
+  // call to Navigate().
+  std::string on_error_url_;
+  // The number of OnErrorRetry() calls that have occurred since the last
+  // OnDone() call. This is used to determine the exponential backoff delay
+  // between the call to OnError() and the timer call to OnErrorRetry().
+  int on_error_retry_count_;
+  // The time OnErrorRetry() was last called. This is used to limit how
+  // frequently |on_error_retry_timer_| can call OnErrorRetry().
+  base::TimeTicks on_error_retry_time_;
+  // The timer for the next call to OnErrorRetry(). It is started in OnError()
+  // when it is not already active.
+  base::OneShotTimer<BrowserModule> on_error_retry_timer_;
+
   // Set when the application is about to quit. May be set from a thread other
   // than the one hosting this object, and read from another.
   bool will_quit_;
@@ -474,8 +498,9 @@
   // The splash screen cache.
   scoped_ptr<SplashScreenCache> splash_screen_cache_;
 
-  // Whether or not the main WebModule has produced any render trees yet.
-  bool produced_render_tree_;
+  // Whether or not the main WebModule has produced any render trees yet for the
+  // current navigation.
+  bool navigation_produced_main_render_tree_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/h5vcc_url_handler.cc b/src/cobalt/browser/h5vcc_url_handler.cc
index 94a8ed9..d02a4a3 100644
--- a/src/cobalt/browser/h5vcc_url_handler.cc
+++ b/src/cobalt/browser/h5vcc_url_handler.cc
@@ -18,6 +18,7 @@
 
 #include "base/bind.h"
 #include "cobalt/browser/browser_module.h"
+#include "cobalt/browser/system_platform_error_handler.h"
 
 namespace cobalt {
 namespace browser {
@@ -75,12 +76,10 @@
 const char kRetryParam[] = "retry-url";
 }  // namespace
 
-H5vccURLHandler::H5vccURLHandler(BrowserModule* browser_module,
-                                 system_window::SystemWindow* system_window)
+H5vccURLHandler::H5vccURLHandler(BrowserModule* browser_module)
     : ALLOW_THIS_IN_INITIALIZER_LIST(URLHandler(
           browser_module,
-          base::Bind(&H5vccURLHandler::HandleURL, base::Unretained(this)))),
-      system_window_(system_window) {}
+          base::Bind(&H5vccURLHandler::HandleURL, base::Unretained(this)))) {}
 
 bool H5vccURLHandler::HandleURL(const GURL& url) {
   bool was_handled = false;
@@ -97,20 +96,21 @@
 }
 
 bool H5vccURLHandler::HandleNetworkFailure() {
-  system_window::SystemWindow::DialogOptions dialog_options;
-  dialog_options.message_code =
-      system_window::SystemWindow::kDialogConnectionError;
-  dialog_options.callback = base::Bind(
-      &H5vccURLHandler::OnNetworkFailureDialogResponse, base::Unretained(this));
-  system_window_->ShowDialog(dialog_options);
+  SystemPlatformErrorHandler::SystemPlatformErrorOptions options;
+  options.error_type = kSbSystemPlatformErrorTypeConnectionError;
+  options.callback =
+      base::Bind(&H5vccURLHandler::OnNetworkFailureSystemPlatformResponse,
+                 base::Unretained(this));
+  browser_module()->system_platform_error_handler()->RaiseSystemPlatformError(
+      options);
   return true;
 }
 
-void H5vccURLHandler::OnNetworkFailureDialogResponse(
-    system_window::SystemWindow::DialogResponse response) {
+void H5vccURLHandler::OnNetworkFailureSystemPlatformResponse(
+    SbSystemPlatformErrorResponse response) {
   const std::string retry_url = GetH5vccUrlQueryParam(url_, kRetryParam);
   // A positive response means we should retry.
-  if (response == system_window::SystemWindow::kDialogPositiveResponse &&
+  if (response == kSbSystemPlatformErrorResponsePositive &&
       retry_url.length() > 0) {
     GURL url(retry_url);
     if (url.is_valid()) {
diff --git a/src/cobalt/browser/h5vcc_url_handler.h b/src/cobalt/browser/h5vcc_url_handler.h
index 203e4a2..6fa46a7 100644
--- a/src/cobalt/browser/h5vcc_url_handler.h
+++ b/src/cobalt/browser/h5vcc_url_handler.h
@@ -17,7 +17,6 @@
 
 #include "cobalt/account/account_manager.h"
 #include "cobalt/browser/url_handler.h"
-#include "cobalt/system_window/system_window.h"
 
 namespace cobalt {
 namespace browser {
@@ -27,19 +26,16 @@
 // handled separately, e.g. by showing a system dialog.
 class H5vccURLHandler : public URLHandler {
  public:
-  explicit H5vccURLHandler(BrowserModule* browser_module,
-                           system_window::SystemWindow* system_window);
+  explicit H5vccURLHandler(BrowserModule* browser_module);
   ~H5vccURLHandler() {}
 
  private:
   bool HandleURL(const GURL& url);
   bool HandleNetworkFailure();
 
-  // Dialog response handlers.
-  void OnNetworkFailureDialogResponse(
-      system_window::SystemWindow::DialogResponse response);
+  void OnNetworkFailureSystemPlatformResponse(
+      SbSystemPlatformErrorResponse response);
 
-  system_window::SystemWindow* system_window_;
   GURL url_;
 };
 
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
index 2d5df3a..855e8ad 100644
--- a/src/cobalt/browser/memory_settings/auto_mem.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -121,7 +121,8 @@
     return setting.Pass();
   }
 
-  setting->set_value(MemorySetting::kStarboardAPI, -1);
+  // This will mark the value as invalid.
+  setting->set_value(MemorySetting::kUnset, -1);
   return setting.Pass();
 }
 
diff --git a/src/cobalt/browser/memory_settings/auto_mem_test.cc b/src/cobalt/browser/memory_settings/auto_mem_test.cc
index 9982a6f..3d6c0dc 100644
--- a/src/cobalt/browser/memory_settings/auto_mem_test.cc
+++ b/src/cobalt/browser/memory_settings/auto_mem_test.cc
@@ -432,6 +432,18 @@
   EXPECT_LE(memory_consumption, kSmallEngineGpuMemorySize);
 }
 
+// Tests that if the gpu memory could not be queried then the resulting
+// max_gpu_bytes will not be valid.
+TEST(AutoMem, NoDefaultGpuMemory) {
+  AutoMemSettings command_line_settings(AutoMemSettings::kTypeCommandLine);
+  AutoMemSettings build_settings(AutoMemSettings::kTypeBuild);
+
+  AutoMem auto_mem(kResolution1080p, command_line_settings,
+                   build_settings);
+
+  EXPECT_FALSE(auto_mem.max_gpu_bytes()->valid());
+}
+
 }  // namespace memory_settings
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/render_tree_combiner.cc b/src/cobalt/browser/render_tree_combiner.cc
index 78fe4be..74727ec 100644
--- a/src/cobalt/browser/render_tree_combiner.cc
+++ b/src/cobalt/browser/render_tree_combiner.cc
@@ -35,6 +35,7 @@
 RenderTreeCombiner::Layer::~Layer() {
   DCHECK(render_tree_combiner_);
   render_tree_combiner_->RemoveLayer(this);
+  render_tree_combiner_->SubmitToRenderer();
 }
 
 void RenderTreeCombiner::Layer::Submit(
diff --git a/src/cobalt/browser/system_platform_error_handler.cc b/src/cobalt/browser/system_platform_error_handler.cc
new file mode 100644
index 0000000..7c1ef7d
--- /dev/null
+++ b/src/cobalt/browser/system_platform_error_handler.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/system_platform_error_handler.h"
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace browser {
+namespace {
+
+void OnSystemPlatformErrorResponse(SbSystemPlatformErrorResponse response,
+                                   void* user_data) {
+  DCHECK(user_data);
+  SystemPlatformErrorHandler* error_handler =
+      static_cast<SystemPlatformErrorHandler*>(user_data);
+  error_handler->HandleSystemPlatformErrorResponse(response);
+}
+
+}  // namespace
+
+void SystemPlatformErrorHandler::RaiseSystemPlatformError(
+    const SystemPlatformErrorOptions& options) {
+  current_system_platform_error_callback_ = options.callback;
+
+  SbSystemPlatformError handle = SbSystemRaisePlatformError(
+      options.error_type, OnSystemPlatformErrorResponse, this);
+  if (!SbSystemPlatformErrorIsValid(handle) &&
+      !current_system_platform_error_callback_.is_null()) {
+    DLOG(WARNING) << "Did not handle error: " << options.error_type;
+    current_system_platform_error_callback_.Reset();
+  }
+}
+
+void SystemPlatformErrorHandler::HandleSystemPlatformErrorResponse(
+    SbSystemPlatformErrorResponse response) {
+  DCHECK(!current_system_platform_error_callback_.is_null());
+  current_system_platform_error_callback_.Run(response);
+  current_system_platform_error_callback_.Reset();
+}
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/system_platform_error_handler.h b/src/cobalt/browser/system_platform_error_handler.h
new file mode 100644
index 0000000..64b01a2
--- /dev/null
+++ b/src/cobalt/browser/system_platform_error_handler.h
@@ -0,0 +1,55 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_SYSTEM_PLATFORM_ERROR_HANDLER_H_
+#define COBALT_BROWSER_SYSTEM_PLATFORM_ERROR_HANDLER_H_
+
+#include "base/callback.h"
+#include "starboard/system.h"
+
+namespace cobalt {
+namespace browser {
+
+// This class handles raising system errors to the platform and returning the
+// response to the caller via the provided callback.
+class SystemPlatformErrorHandler {
+ public:
+  // Type of callback to run when the platform responds to a system error.
+  typedef base::Callback<void(SbSystemPlatformErrorResponse response)>
+      SystemPlatformErrorCallback;
+
+  // Options structure for raised system platform errors, including both the
+  // system error being raised and the callback to run with the response.
+  struct SystemPlatformErrorOptions {
+    SbSystemPlatformErrorType error_type;
+    SystemPlatformErrorCallback callback;
+  };
+
+  // Raises a system error with the specified error type and callback.
+  void RaiseSystemPlatformError(const SystemPlatformErrorOptions& options);
+
+  // Called when the platform responds to the system error.
+  void HandleSystemPlatformErrorResponse(
+      SbSystemPlatformErrorResponse response);
+
+ private:
+  // The current platform error callback. Only one platform error callback may
+  // be active at a time.
+  SystemPlatformErrorCallback current_system_platform_error_callback_;
+};
+
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_SYSTEM_PLATFORM_ERROR_HANDLER_H_
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 248bc70..96e2ce3 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-93830
\ No newline at end of file
+95615
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
index 21e6bef..2bc87b6 100644
--- a/src/cobalt/build/config/base.gni
+++ b/src/cobalt/build/config/base.gni
@@ -152,7 +152,7 @@
 #   'stub'        -- Stub graphics rasterization.  A rasterizer object will
 #                    still be available and valid, but it will do nothing.
 if (!defined(rasterizer_type)) {
-  rasterizer_type = "hardware"
+  rasterizer_type = "direct-gles"
 }
 
 # If set to true, will enable support for rendering only the regions of the
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index 02c5ec2..110e70d 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -174,7 +174,7 @@
     #                    output window.
     #   'stub'        -- Stub graphics rasterization.  A rasterizer object will
     #                    still be available and valid, but it will do nothing.
-    'rasterizer_type%': 'hardware',
+    'rasterizer_type%': 'direct-gles',
 
     # If set to 1, will enable support for rendering only the regions of the
     # display that are modified due to animations, instead of re-rendering the
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index ec42f43..85603a2 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -81,6 +81,8 @@
 #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
       partial_layout_is_enabled_(true),
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
+      selector_tree_(new cssom::SelectorTree()),
+      should_recreate_selector_tree_(false),
       navigation_start_clock_(options.navigation_start_clock
                                   ? options.navigation_start_clock
                                   : new base::SystemMonotonicClock()),
@@ -550,28 +552,30 @@
 namespace {
 
 void RemoveRulesFromCSSRuleListFromSelectorTree(
-    cssom::SelectorTree* selector_tree,
-    const scoped_refptr<cssom::CSSRuleList>& css_rule_list) {
+    const scoped_refptr<cssom::CSSRuleList>& css_rule_list,
+    cssom::SelectorTree* maybe_selector_tree) {
   for (unsigned int i = 0; i < css_rule_list->length(); ++i) {
     cssom::CSSRule* rule = css_rule_list->Item(i);
 
     cssom::CSSStyleRule* css_style_rule = rule->AsCSSStyleRule();
     if (css_style_rule && css_style_rule->added_to_selector_tree()) {
-      selector_tree->RemoveRule(css_style_rule);
+      if (maybe_selector_tree) {
+        maybe_selector_tree->RemoveRule(css_style_rule);
+      }
       css_style_rule->set_added_to_selector_tree(false);
     }
 
     cssom::CSSMediaRule* css_media_rule = rule->AsCSSMediaRule();
     if (css_media_rule) {
-      RemoveRulesFromCSSRuleListFromSelectorTree(selector_tree,
-                                                 css_media_rule->css_rules());
+      RemoveRulesFromCSSRuleListFromSelectorTree(css_media_rule->css_rules(),
+                                                 maybe_selector_tree);
     }
   }
 }
 
 void AppendRulesFromCSSRuleListToSelectorTree(
-    cssom::SelectorTree* selector_tree,
-    const scoped_refptr<cssom::CSSRuleList>& css_rule_list) {
+    const scoped_refptr<cssom::CSSRuleList>& css_rule_list,
+    cssom::SelectorTree* selector_tree) {
   for (unsigned int i = 0; i < css_rule_list->length(); ++i) {
     cssom::CSSRule* rule = css_rule_list->Item(i);
 
@@ -584,21 +588,26 @@
     cssom::CSSMediaRule* css_media_rule = rule->AsCSSMediaRule();
     if (css_media_rule) {
       if (css_media_rule->condition_value()) {
-        AppendRulesFromCSSRuleListToSelectorTree(selector_tree,
-                                                 css_media_rule->css_rules());
+        AppendRulesFromCSSRuleListToSelectorTree(css_media_rule->css_rules(),
+                                                 selector_tree);
       } else {
-        RemoveRulesFromCSSRuleListFromSelectorTree(selector_tree,
-                                                   css_media_rule->css_rules());
+        RemoveRulesFromCSSRuleListFromSelectorTree(css_media_rule->css_rules(),
+                                                   selector_tree);
       }
     }
   }
 }
 
-void UpdateSelectorTreeFromCSSStyleSheet(
-    cssom::SelectorTree* selector_tree,
+void AppendRulesFromCSSStyleSheetToSelectorTree(
+    const scoped_refptr<cssom::CSSStyleSheet>& style_sheet,
+    cssom::SelectorTree* selector_tree) {
+  AppendRulesFromCSSRuleListToSelectorTree(style_sheet->css_rules(),
+                                           selector_tree);
+}
+
+void ClearAddedToSelectorTreeFromCSSStyleSheetRules(
     const scoped_refptr<cssom::CSSStyleSheet>& style_sheet) {
-  AppendRulesFromCSSRuleListToSelectorTree(selector_tree,
-                                           style_sheet->css_rules());
+  RemoveRulesFromCSSRuleListFromSelectorTree(style_sheet->css_rules(), NULL);
 }
 
 }  // namespace
@@ -789,16 +798,37 @@
     UpdateStyleSheets();
     UpdateMediaRules();
 
+    // If the selector tree is being recreated, then clear the added state from
+    // the document's style sheets. This will cause them to be added to the new
+    // selector tree.
+    if (should_recreate_selector_tree_) {
+      DLOG(WARNING) << "A style sheet was removed from the document or the "
+                       "document's style sheets have been reordered. This "
+                       "triggers a recreation of the selector tree and should "
+                       "be avoided if possible.";
+      if (user_agent_style_sheet_) {
+        ClearAddedToSelectorTreeFromCSSStyleSheetRules(user_agent_style_sheet_);
+      }
+      for (unsigned int style_sheet_index = 0;
+           style_sheet_index < style_sheets_->length(); ++style_sheet_index) {
+        scoped_refptr<cssom::CSSStyleSheet> css_style_sheet =
+            style_sheets_->Item(style_sheet_index)->AsCSSStyleSheet();
+        ClearAddedToSelectorTreeFromCSSStyleSheetRules(css_style_sheet);
+      }
+      selector_tree_.reset(new cssom::SelectorTree());
+      should_recreate_selector_tree_ = false;
+    }
+
     if (user_agent_style_sheet_) {
-      UpdateSelectorTreeFromCSSStyleSheet(&selector_tree_,
-                                          user_agent_style_sheet_);
+      AppendRulesFromCSSStyleSheetToSelectorTree(user_agent_style_sheet_,
+                                                 selector_tree_.get());
     }
     for (unsigned int style_sheet_index = 0;
          style_sheet_index < style_sheets_->length(); ++style_sheet_index) {
       scoped_refptr<cssom::CSSStyleSheet> css_style_sheet =
           style_sheets_->Item(style_sheet_index)->AsCSSStyleSheet();
-
-      UpdateSelectorTreeFromCSSStyleSheet(&selector_tree_, css_style_sheet);
+      AppendRulesFromCSSStyleSheetToSelectorTree(css_style_sheet,
+                                                 selector_tree_.get());
     }
     scoped_refptr<HTMLHtmlElement> current_html = html();
     if (current_html) {
@@ -914,6 +944,22 @@
          child = child->next_element_sibling()) {
       child->CollectStyleSheetsOfElementAndDescendants(&style_sheet_vector);
     }
+
+    // Check for the removal or reordering of any of the pre-existing style
+    // sheets. In either of these cases, the selector tree must be recreated.
+    if (style_sheets_->length() > style_sheet_vector.size()) {
+      should_recreate_selector_tree_ = true;
+    }
+    for (unsigned int style_sheet_index = 0;
+         !should_recreate_selector_tree_ &&
+         style_sheet_index < style_sheets_->length();
+         ++style_sheet_index) {
+      if (style_sheets_->Item(style_sheet_index)->AsCSSStyleSheet().get() !=
+          style_sheet_vector[style_sheet_index]->AsCSSStyleSheet().get()) {
+        should_recreate_selector_tree_ = true;
+      }
+    }
+
     style_sheets_ = new cssom::StyleSheetList(style_sheet_vector, this);
     are_style_sheets_dirty_ = false;
   }
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index d0f8853..5291806 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -251,7 +251,7 @@
     return &scripts_to_be_executed_;
   }
 
-  cssom::SelectorTree* selector_tree() { return &selector_tree_; }
+  cssom::SelectorTree* selector_tree() { return selector_tree_.get(); }
 
   // Returns a mapping from keyframes name to CSSKeyframesRule.  This can be
   // used to quickly lookup the @keyframes rule given a string identifier.
@@ -465,7 +465,12 @@
   // List of document observers.
   ObserverList<DocumentObserver> observers_;
   // Selector Tree.
-  cssom::SelectorTree selector_tree_;
+  scoped_ptr<cssom::SelectorTree> selector_tree_;
+  // This is set when the document has a style sheet removed or the order of its
+  // style sheets changed. In this case, it is more straightforward to simply
+  // recreate the selector tree than to attempt to manage updating all of its
+  // internal state.
+  bool should_recreate_selector_tree_;
   // The document's latest sample from the global clock, used for updating
   // animations.
   const scoped_refptr<base::Clock> navigation_start_clock_;
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index a539d68..6aff330 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -588,26 +588,28 @@
 }
 
 void HTMLElement::InvalidateMatchingRulesRecursively() {
-  if (!matching_rules_valid_) {
-    return;
-  }
+  InvalidateMatchingRulesRecursivelyInternal(true /*is_initial_invalidation*/);
+}
 
-  matching_rules_valid_ = false;
+void HTMLElement::InvalidateMatchingRulesRecursivelyInternal(
+    bool is_initial_invalidation) {
+  if (matching_rules_valid_) {
+    // Move |matching_rules_| into |old_matching_rules_|. This is used for
+    // determining whether or not the matching rules actually changed when they
+    // are updated.
+    old_matching_rules_.swap(matching_rules_);
 
-  // Move |matching_rules_| into |old_matching_rules_|. This is used for
-  // determining whether or not the matching rules actually changed when they
-  // are updated.
-  old_matching_rules_.swap(matching_rules_);
-
-  matching_rules_.clear();
-  rule_matching_state_.matching_nodes.clear();
-  rule_matching_state_.descendant_potential_nodes.clear();
-  rule_matching_state_.following_sibling_potential_nodes.clear();
-  for (int pseudo_element_type = 0; pseudo_element_type < kMaxPseudoElementType;
-       ++pseudo_element_type) {
-    if (pseudo_elements_[pseudo_element_type]) {
-      pseudo_elements_[pseudo_element_type]->ClearMatchingRules();
+    matching_rules_.clear();
+    rule_matching_state_.matching_nodes.clear();
+    rule_matching_state_.descendant_potential_nodes.clear();
+    rule_matching_state_.following_sibling_potential_nodes.clear();
+    for (int pseudo_element_type = 0;
+         pseudo_element_type < kMaxPseudoElementType; ++pseudo_element_type) {
+      if (pseudo_elements_[pseudo_element_type]) {
+        pseudo_elements_[pseudo_element_type]->ClearMatchingRules();
+      }
     }
+    matching_rules_valid_ = false;
   }
 
   // Invalidate matching rules on all children.
@@ -615,18 +617,22 @@
        element = element->next_element_sibling()) {
     HTMLElement* html_element = element->AsHTMLElement();
     if (html_element) {
-      html_element->InvalidateMatchingRulesRecursively();
+      html_element->InvalidateMatchingRulesRecursivelyInternal(
+          false /*is_initial_invalidation*/);
     }
   }
 
-  // Invalidate matching rules on all following siblings if sibling combinators
-  // are used.
-  if (node_document()->selector_tree()->has_sibling_combinators()) {
+  // Invalidate matching rules on all following siblings if this is the initial
+  // invalidation and sibling combinators are used; if this is not the initial
+  // invalidation, then these will already be handled by a previous call.
+  if (is_initial_invalidation &&
+      node_document()->selector_tree()->has_sibling_combinators()) {
     for (Element* element = next_element_sibling(); element;
          element = element->next_element_sibling()) {
       HTMLElement* html_element = element->AsHTMLElement();
       if (html_element) {
-        html_element->InvalidateMatchingRulesRecursively();
+        html_element->InvalidateMatchingRulesRecursivelyInternal(
+            false /*is_initial_invalidation*/);
       }
     }
   }
@@ -1216,7 +1222,6 @@
   if (!matching_rules_valid_) {
     dom_stat_tracker_->OnUpdateMatchingRules();
     UpdateMatchingRules(this);
-    matching_rules_valid_ = true;
 
     // Check for whether the matching rules have changed. If they have, then a
     // new computed style must be generated from them.
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 5806cfc..21fb9ee 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -190,8 +190,8 @@
   }
   // Returns the rule matching state of this element.
   RuleMatchingState* rule_matching_state() { return &rule_matching_state_; }
-  // Invalidates the matching rules and rule matching state in this element and
-  // its descendants.
+  // Invalidates the matching rules and rule matching state in this element,
+  // its descendants and its siblings.
   void InvalidateMatchingRulesRecursively();
 
   // Computed style related methods.
@@ -263,6 +263,7 @@
     return descendant_computed_styles_valid_;
   }
   bool matching_rules_valid() const { return matching_rules_valid_; }
+  void set_matching_rules_valid() { matching_rules_valid_ = true; }
 
   // Returns whether the element has been designated.
   //   https://www.w3.org/TR/selectors4/#hover-pseudo
@@ -317,6 +318,11 @@
   // directionality does not invalidate the computed style.
   void SetDirectionality(const std::string& value);
 
+  // Invalidates the matching rules and rule matching state in this element and
+  // its descendants. In the case where this is the the initial invalidation,
+  // it will also invalidate the rule matching state of its siblings.
+  void InvalidateMatchingRulesRecursivelyInternal(bool is_initial_invalidation);
+
   // Clear the list of active background images, and notify the animated image
   // tracker to stop the animations.
   void ClearActiveBackgroundImages();
@@ -374,12 +380,11 @@
   cssom::RulesWithCascadePrecedence old_matching_rules_;
   cssom::RulesWithCascadePrecedence matching_rules_;
   RuleMatchingState rule_matching_state_;
+  bool matching_rules_valid_;
 
   // This contains information about the boxes generated from the element.
   scoped_ptr<LayoutBoxes> layout_boxes_;
 
-  bool matching_rules_valid_;
-
   scoped_ptr<PseudoElement> pseudo_elements_[kMaxPseudoElementType];
   base::WeakPtr<DOMStringMap> dataset_;
 
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 51200ba..779f7d3 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -1528,7 +1528,7 @@
   EndProcessingMediaPlayerCallback();
 }
 
-void HTMLMediaElement::TimeChanged(bool eos_played) {
+void HTMLMediaElement::TimeChanged() {
   DCHECK(player_);
   if (!player_) {
     return;
@@ -1554,9 +1554,8 @@
   // When the current playback position reaches the end of the media resource
   // when the direction of playback is forwards, then the user agent must follow
   // these steps:
-  eos_played |=
-      !SbDoubleIsNan(dur) && (0.0f != dur) && now >= dur && playback_rate_ > 0;
-  if (eos_played) {
+  if (!SbDoubleIsNan(dur) && (0.0f != dur) && now >= dur &&
+      playback_rate_ > 0) {
     // If the media element has a loop attribute specified and does not have a
     // current media controller,
     if (loop()) {
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index 31314e3..b3d1f0d 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -231,7 +231,7 @@
   // WebMediaPlayerClient methods
   void NetworkStateChanged() OVERRIDE;
   void ReadyStateChanged() OVERRIDE;
-  void TimeChanged(bool eos_played) OVERRIDE;
+  void TimeChanged() OVERRIDE;
   void DurationChanged() OVERRIDE;
   void OutputModeChanged() OVERRIDE;
   void PlaybackStateChanged() OVERRIDE;
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc
index 1437827..938d78f 100644
--- a/src/cobalt/dom/rule_matching.cc
+++ b/src/cobalt/dom/rule_matching.cc
@@ -683,6 +683,8 @@
       }
     }
   }
+
+  current_element->set_matching_rules_valid();
 }
 
 scoped_refptr<Element> QuerySelector(Node* node, const std::string& selectors,
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index 73f9fdb..6a562fc 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -244,11 +244,14 @@
   head_->set_inner_html("<style>:focus {}</style>");
   body_->set_inner_html("<div tabIndex=\"-1\"/>");
   body_->first_element_child()->AsHTMLElement()->Focus();
+  // This is required because RunFocusingSteps() earlies out as a result of the
+  // document not having a browsing context.
+  root_->InvalidateMatchingRulesRecursively();
   UpdateAllMatchingRules();
 
   cssom::RulesWithCascadePrecedence* matching_rules =
       body_->first_element_child()->AsHTMLElement()->matching_rules();
-  ASSERT_EQ(2, matching_rules->size());
+  ASSERT_EQ(1, matching_rules->size());
   EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0),
             (*matching_rules)[0].first);
 }
@@ -705,5 +708,103 @@
   EXPECT_EQ(0, node_list->length());
 }
 
+TEST_F(RuleMatchingTest, StyleElementRemoval) {
+  head_->set_inner_html("<style>* {}</style>");
+  body_->set_inner_html("<div/>");
+  UpdateAllMatchingRules();
+  head_->set_inner_html("<style/>");
+  UpdateAllMatchingRules();
+
+  cssom::RulesWithCascadePrecedence* matching_rules =
+      body_->first_element_child()->AsHTMLElement()->matching_rules();
+  ASSERT_EQ(0, matching_rules->size());
+}
+
+TEST_F(RuleMatchingTest, StyleElementAddition) {
+  head_->set_inner_html("<style/>");
+  body_->set_inner_html("<div/>");
+  UpdateAllMatchingRules();
+  head_->set_inner_html("<style>* {}</style>");
+  UpdateAllMatchingRules();
+
+  cssom::RulesWithCascadePrecedence* matching_rules =
+      body_->first_element_child()->AsHTMLElement()->matching_rules();
+  ASSERT_EQ(1, matching_rules->size());
+}
+
+TEST_F(RuleMatchingTest, StyleElementReorderingOneMatching) {
+  scoped_refptr<HTMLElement> div1 =
+      document_->CreateElement("div")->AsHTMLElement();
+  div1->set_inner_html("<style/>");
+
+  scoped_refptr<HTMLElement> div2 =
+      document_->CreateElement("div")->AsHTMLElement();
+  div2->set_inner_html("<style>* {}</style>");
+
+  body_->set_inner_html("<div/>");
+  head_->AppendChild(div1);
+  head_->AppendChild(div2);
+
+  UpdateAllMatchingRules();
+
+  cssom::RulesWithCascadePrecedence* matching_rules =
+      head_->first_element_child()->AsHTMLElement()->matching_rules();
+  ASSERT_EQ(1, matching_rules->size());
+  EXPECT_NE(GetDocumentStyleSheet(0)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+  EXPECT_EQ(GetDocumentStyleSheet(1)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+
+  head_->RemoveChild(div2);
+  head_->InsertBefore(div2, div1);
+
+  UpdateAllMatchingRules();
+
+  matching_rules =
+      head_->first_element_child()->AsHTMLElement()->matching_rules();
+  ASSERT_EQ(1, matching_rules->size());
+  EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+  EXPECT_NE(GetDocumentStyleSheet(1)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+}
+
+TEST_F(RuleMatchingTest, StyleElementReorderingTwoMatching) {
+  scoped_refptr<HTMLElement> div1 =
+      document_->CreateElement("div")->AsHTMLElement();
+  div1->set_inner_html("<style>* {}</style>");
+
+  scoped_refptr<HTMLElement> div2 =
+      document_->CreateElement("div")->AsHTMLElement();
+  div2->set_inner_html("<style>* {}</style>");
+
+  body_->set_inner_html("<div/>");
+  head_->AppendChild(div1);
+  head_->AppendChild(div2);
+
+  UpdateAllMatchingRules();
+
+  cssom::RulesWithCascadePrecedence* matching_rules =
+      head_->first_element_child()->AsHTMLElement()->matching_rules();
+  ASSERT_EQ(2, matching_rules->size());
+  EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+  EXPECT_NE(GetDocumentStyleSheet(1)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+
+  head_->RemoveChild(div2);
+  head_->InsertBefore(div2, div1);
+
+  UpdateAllMatchingRules();
+
+  matching_rules =
+      head_->first_element_child()->AsHTMLElement()->matching_rules();
+  ASSERT_EQ(2, matching_rules->size());
+  EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+  EXPECT_NE(GetDocumentStyleSheet(1)->css_rules()->Item(0),
+            (*matching_rules)[0].first);
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index fab5c8c..67cd077 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -16,6 +16,7 @@
 
 #include <algorithm>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "cobalt/base/polymorphic_downcast.h"
@@ -288,6 +289,18 @@
 
 scoped_refptr<Crypto> Window::crypto() const { return crypto_; }
 
+std::string Window::Btoa(const std::string& string_to_encode) {
+  std::string output;
+  base::Base64Encode(string_to_encode, &output);
+  return output;
+}
+
+std::string Window::Atob(const std::string& encoded_string) {
+  std::string output;
+  base::Base64Decode(encoded_string, &output);
+  return output;
+}
+
 int Window::SetTimeout(const WindowTimers::TimerCallbackArg& handler,
                        int timeout) {
   DLOG_IF(WARNING, timeout < 0)
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 9cd4cb6..0a60269 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -228,6 +228,11 @@
   //   https://www.w3.org/TR/WebCryptoAPI/#crypto-interface
   scoped_refptr<Crypto> crypto() const;
 
+  // base64 encoding and decoding
+  std::string Btoa(const std::string& string_to_encode);
+
+  std::string Atob(const std::string& encoded_string);
+
   // Web API: WindowTimers (implements)
   //   https://www.w3.org/TR/html5/webappapis.html#timers
   //
diff --git a/src/cobalt/dom/window.idl b/src/cobalt/dom/window.idl
index d04fb7a..8749653 100644
--- a/src/cobalt/dom/window.idl
+++ b/src/cobalt/dom/window.idl
@@ -36,6 +36,10 @@
   // the user agent
   readonly attribute Navigator navigator;
 
+  // base64 encoding and decoding
+  DOMString btoa(DOMString string_to_encode);
+  DOMString atob(DOMString encoded_string);
+
   // Custom, not in any spec.
   //
   readonly attribute Console console;
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 34a3853..ff4edae 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -1638,20 +1638,21 @@
   }
 }
 
-void Box::ApplyTransformActionToCoordinate(TransformAction action,
+bool Box::ApplyTransformActionToCoordinate(TransformAction action,
                                            math::Vector2dF* coordinate) const {
   std::vector<math::Vector2dF> coordinate_vector;
   coordinate_vector.push_back(*coordinate);
-  ApplyTransformActionToCoordinates(action, &coordinate_vector);
+  bool result = ApplyTransformActionToCoordinates(action, &coordinate_vector);
   *coordinate = coordinate_vector[0];
+  return result;
 }
 
-void Box::ApplyTransformActionToCoordinates(
+bool Box::ApplyTransformActionToCoordinates(
     TransformAction action, std::vector<math::Vector2dF>* coordinates) const {
   const scoped_refptr<cssom::PropertyValue>& transform =
       computed_style()->transform();
   if (transform == cssom::KeywordValue::GetNone()) {
-    return;
+    return true;
   }
 
   // The border box offset is calculated in two steps because we want to
@@ -1673,6 +1674,11 @@
   if (!matrix.IsIdentity()) {
     if (action == kEnterTransform) {
       matrix = matrix.Inverse();
+      // The matrix is not invertible. Return that applying the transform
+      // failed.
+      if (matrix.IsZeros()) {
+        return false;
+      }
     }
 
     for (std::vector<math::Vector2dF>::iterator coordinate_iter =
@@ -1702,6 +1708,7 @@
       coordinate += containing_block_offset_from_root_as_float;
     }
   }
+  return true;
 }
 
 }  // namespace layout
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 51c1758..e90a9f1 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -572,9 +572,11 @@
                               RenderSequence other_render_sequence);
 
   // Applies the specified transform action to the provided coordinates.
-  void ApplyTransformActionToCoordinate(TransformAction action,
+  // Returns false if the transform is not invertible and the action requires
+  // it being inverted.
+  bool ApplyTransformActionToCoordinate(TransformAction action,
                                         math::Vector2dF* coordinate) const;
-  void ApplyTransformActionToCoordinates(
+  bool ApplyTransformActionToCoordinates(
       TransformAction action, std::vector<math::Vector2dF>* coordinates) const;
 
  protected:
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index dc00344..18bf26b 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -17,6 +17,7 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/debug/trace_event.h"
 #include "base/memory/scoped_ptr.h"
 #include "cobalt/cssom/computed_style.h"
 #include "cobalt/cssom/css_computed_style_declaration.h"
@@ -64,6 +65,7 @@
 scoped_refptr<render_tree::Image> GetVideoFrame(
     const scoped_refptr<ShellVideoFrameProvider>& frame_provider,
     render_tree::ResourceProvider* resource_provider) {
+  TRACE_EVENT0("cobalt::layout", "GetVideoFrame()");
   SbDecodeTarget decode_target = frame_provider->GetCurrentSbDecodeTarget();
   if (SbDecodeTargetIsValid(decode_target)) {
 #if SB_HAS(GRAPHICS)
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index 7f0cc65..8f38f9e 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -116,9 +116,7 @@
 void UpdateCamera(
     float width_to_height_aspect_ratio, scoped_refptr<input::Camera3D> camera,
     float max_horizontal_fov_rad, float max_vertical_fov_rad,
-    render_tree::MatrixTransform3DNode::Builder* transform_node_builder,
-    base::TimeDelta time) {
-  UNREFERENCED_PARAMETER(time);
+    render_tree::MatrixTransform3DNode::Builder* transform_node_builder) {
   float vertical_fov_rad =
       std::min(max_vertical_fov_rad,
                2 * static_cast<float>(atan(tan(max_horizontal_fov_rad * 0.5f) /
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index c61322a..6c3e322 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -231,9 +231,7 @@
 }
 
 void AnimateVideoImage(const ReplacedBox::ReplaceImageCB& replace_image_cb,
-                       ImageNode::Builder* image_node_builder,
-                       base::TimeDelta time) {
-  UNREFERENCED_PARAMETER(time);
+                       ImageNode::Builder* image_node_builder) {
   DCHECK(!replace_image_cb.is_null());
   DCHECK(image_node_builder);
 
@@ -250,14 +248,27 @@
 void AnimateVideoWithLetterboxing(
     const ReplacedBox::ReplaceImageCB& replace_image_cb,
     math::SizeF destination_size,
-    CompositionNode::Builder* composition_node_builder, base::TimeDelta time) {
-  UNREFERENCED_PARAMETER(time);
-
+    CompositionNode::Builder* composition_node_builder) {
   DCHECK(!replace_image_cb.is_null());
   DCHECK(composition_node_builder);
 
   scoped_refptr<render_tree::Image> image = replace_image_cb.Run();
 
+  // If the image hasn't changed, then no need to change anything else.  The
+  // image should be the first child (see AddLetterboxedImageToRenderTree()).
+  if (!composition_node_builder->children().empty()) {
+    render_tree::ImageNode* existing_image_node =
+        base::polymorphic_downcast<render_tree::ImageNode*>(
+            composition_node_builder->GetChild(0)->get());
+    if (existing_image_node->data().source.get() == image.get()) {
+      return;
+    }
+  }
+
+  // Reset the composition node from whatever it was before, we will recreate
+  // it anew in each animation frame.
+  *composition_node_builder = CompositionNode::Builder();
+
   // TODO: Detect better when the intrinsic video size is used for the
   //   node size, and trigger a re-layout from the media element when the size
   //   changes.
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
index 97e84fe..f825db7 100644
--- a/src/cobalt/layout/topmost_event_target.cc
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -81,8 +81,12 @@
   if (layout_boxes) {
     const Box* box = layout_boxes->boxes().front();
     if (box->computed_style() && box->IsTransformed()) {
-      box->ApplyTransformActionToCoordinate(Box::kEnterTransform,
-                                            &element_coordinate);
+      // Early out if the transform cannot be applied. This can occur if the
+      // transform matrix is not invertible.
+      if (!box->ApplyTransformActionToCoordinate(Box::kEnterTransform,
+                                                 &element_coordinate)) {
+        return;
+      }
     }
 
     scoped_refptr<dom::HTMLElement> html_element = element->AsHTMLElement();
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index 77b0def..f377d78 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -90,7 +90,7 @@
   scoped_refptr<render_tree::animations::AnimateNode> animate_node =
       new render_tree::animations::AnimateNode(layout_results.render_tree);
   scoped_refptr<render_tree::Node> animated_tree =
-      animate_node->Apply(layout_results.layout_time).animated;
+      animate_node->Apply(layout_results.layout_time).animated->source();
 
   bool results =
       pixel_tester.TestTree(animated_tree, GetParam().base_file_path);
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 5421370..01788fd 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -281,6 +281,7 @@
   std::string json_results = RunWebPlatformTest(test_url, &got_results);
   EXPECT_TRUE(got_results);
   std::vector<TestResult> results = ParseResults(json_results);
+  bool failed_at_least_once = false;
   for (size_t i = 0; i < results.size(); ++i) {
     const WebPlatformTestInfo& test_info = GetParam();
     const TestResult& test_result = results[i];
@@ -290,6 +291,15 @@
     if (it != test_info.exceptions.end()) {
       should_pass = !should_pass;
     }
+    // If expected to fail but current subtest did not fail, wait to report
+    // the entire test failed after the last subtest passed and none failed.
+    if (!should_pass && test_result.status == WebPlatformTestInfo::kPass &&
+        (i != results.size() - 1 || failed_at_least_once)) {
+      should_pass = true;
+    } else {
+      failed_at_least_once = failed_at_least_once ||
+                             test_result.status == WebPlatformTestInfo::kFail;
+    }
     EXPECT_PRED_FORMAT2(CheckResult, should_pass, test_result);
   }
 }
diff --git a/src/cobalt/loader/embedded_resources/you_tube_logo.png b/src/cobalt/loader/embedded_resources/you_tube_logo.png
deleted file mode 100644
index 14563d0..0000000
--- a/src/cobalt/loader/embedded_resources/you_tube_logo.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/loader/embedded_resources/youtube_splash_screen.css b/src/cobalt/loader/embedded_resources/youtube_splash_screen.css
deleted file mode 100644
index 81ff7cb..0000000
--- a/src/cobalt/loader/embedded_resources/youtube_splash_screen.css
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2015 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-body {
-  overflow: hidden;
-  font-size: 1.4815vh;  /* Corresponds to 16px at 1080p. */
-}
-
-#splash {
-  background-color: #e62117;
-  background-image: url("h5vcc-embedded://you_tube_logo.png");
-  background-position: center center;
-  background-repeat: no-repeat;
-  background-size: 50%;
-  height: 100%;
-  left: 0;
-  position: absolute;
-  top: 0;
-  width: 100%;
-}
-
-#loading {
-  position: absolute;
-  top: 52em;
-  width: 100%;
-}
-
-#spinner {
-  /* The spinner starts with display set to none, and JavaScript will set this
-     to 'block' after some time has passed, if the splash screen is still
-     visible. */
-  display: none;
-
-  height: 5.33em;
-  margin: 0 auto;
-  position: relative;
-  width: 5.33em;
-}
-
-.dot {
-  background-color: #cbcbcb;
-  border-radius: 50%;
-  height: 1.17em;
-  position: absolute;
-  width: 1.17em;
-}
-
-@keyframes fade1 {
-  0%,100% {opacity: 0}
-  50% {opacity: 1}
-}
-
-@keyframes fade2 {
-  0%,100% {opacity: .25}
-  37.5% {opacity: 1}
-  87.5% {opacity: 0}
-}
-
-@keyframes fade3 {
-  0%,100% {opacity: .5}
-  25% {opacity: 1}
-  75% {opacity: 0}
-}
-
-@keyframes fade4 {
-  0%,100% {opacity: .75}
-  12.5% {opacity: 1}
-  62.5% {opacity: 0}
-}
-
-@keyframes fade5 {
-  0%,100% {opacity: 1}
-  50% {opacity: 0}
-}
-
-@keyframes fade6 {
-  0%,100% {opacity: .75}
-  37.5% {opacity: 0}
-  87.5% {opacity: 1}
-}
-
-@keyframes fade7 {
-  0%,100% {opacity: .5}
-  25% {opacity: 0}
-  75% {opacity: 1}
-}
-
-@keyframes fade8 {
-  0%,100% {opacity: .25}
-  12.5% {opacity: 0}
-  62.5% {opacity: 1}
-}
-
-#dot1 {
-  animation: fade8 .72s infinite ease;
-  left: 0;
-  top: 2.09em;
-}
-
-#dot2 {
-  animation: fade7 .72s infinite ease;
-  left: .61em;
-  top: .61em;
-}
-
-#dot3 {
-  animation: fade6 .72s infinite ease;
-  left: 2.09em;
-  top: 0;
-}
-
-#dot4 {
-  animation: fade5 .72s infinite ease;
-  right: .61em;
-  top: .61em;
-}
-
-#dot5 {
-  animation: fade4 .72s infinite ease;
-  right: 0;
-  top: 2.09em;
-}
-
-#dot6 {
-  animation: fade3 .72s infinite ease;
-  bottom: .61em;
-  right: .61em;
-}
-
-#dot7 {
-  animation: fade2 .72s infinite ease;
-  bottom: 0;
-  left: 2.09em;
-}
-
-#dot8 {
-  animation: fade1 .72s infinite ease;
-  bottom: .61em;
-  left: .61em;
-}
-
-.hidden {
-  height: 0;
-  visibility: hidden;
-}
diff --git a/src/cobalt/loader/embedded_resources/youtube_splash_screen.html b/src/cobalt/loader/embedded_resources/youtube_splash_screen.html
index d40bf9b..0c704e5 100644
--- a/src/cobalt/loader/embedded_resources/youtube_splash_screen.html
+++ b/src/cobalt/loader/embedded_resources/youtube_splash_screen.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <!--
-  Copyright 2015 Google Inc. All Rights Reserved.
+  Copyright 2017 Google Inc. All Rights Reserved.
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -15,34 +15,205 @@
   limitations under the License.
 -->
 <html>
+  <head>
+      <meta http-equiv="Content-Security-Policy" content="default-src 'none';
+      script-src 'unsafe-inline';
+      style-src 'unsafe-inline';
+      img-src 'self' data:;">
+<style>
+#background {
+  background-color: #282828;
+  margin: 0;
+  height: 100vh;
+  width: 100vw;
+  transition: 200ms;
+  transition-delay: 500ms;
+}
+#lozengeContainer {
+  position: fixed;
+  z-index: 1;
+  top: 50%;
+  left: 25%;
+  transform: translate(0, -50%);
+  width: 33%;
+  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
+  transition-delay: 0ms;
+}
+#lozengeContainer.move {
+  transform: translate(-51%, -50%);
 
-<head>
-  <meta http-equiv="Content-Security-Policy" content="default-src 'none';
-      script-src h5vcc-embedded://*/splash_screen.js;
-      style-src h5vcc-embedded://*/youtube_splash_screen.css;
-      img-src h5vcc-embedded://*/you_tube_logo.png;">
-<link rel="stylesheet" type="text/css"
-    href="h5vcc-embedded://youtube_splash_screen.css">
+}
+#lozengeBg {
+  position: absolute;
+  z-index: -1;
+  background-color: #282828;
+}
+
+
+#wordmarkContainer {
+  position: absolute;
+  z-index: -1;
+  top: 50%;
+  left: 25%;
+  transform: translate(0, -50%);
+  width: 33%;
+  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
+  transition-delay: 0ms;
+}
+#wordmarkContainer.move {
+  transform: translate(54%, -50%);
+  z-index: 0;
+}
+#wordmark {
+  background-size: contain;
+}
+
+#spinnerContainer {
+  position: fixed;
+  top: 71.5%;
+  left: 50%;
+  width: 166px;
+  height: 166px;
+  opacity: .2;
+  margin: 0 0 0 -83px;
+  display: none;
+}
+
+#spinnerContainer.active {
+  display: block;
+  animation: loading 5s linear infinite;
+}
+
+#loader {
+  border-radius: 50%;
+  color: #ffffff;
+  position: relative;
+  width: 166px;
+  height: 166px;
+  box-shadow: inset 0 0 0 15px;
+}
+
+.beforeafter {
+  background-color: #282828;
+  position: absolute;
+  top: -1px;
+  width: 84px;
+  height: 169px;
+}
+#loadbefore {
+  left: -1px;
+  transform-origin: 83px 83px;
+  animation: loading 1000ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite 750ms;
+}
+
+#loadafter {
+  left: 83px;
+  transform-origin: 0 83px;
+  animation: loading 1000ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite;
+}
+
+@keyframes loading {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+@media only screen and (min-device-width : 1921px){
+  #lozengeBg {
+    width: 960px;
+    height: 425px;
+  }
+  #lozenge {
+    background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAngAAAG8CAYAAABNBtgFAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAIABJREFUeJzt3X2wXHWd5/H3veQ5lyRIeIhAQImCAQlRHLM4jgF3doijrqFcq2TH0YHSrVVwlbAsI+wwtRvGuLK4WynKksWRcXdZRq0guAsZx4EwKsYxEBG5hCRg5CkBIrl5vHm4yd0/fn2Svn277+2H0+d3Tvf7VdXV3adPn/4mVqiP39/3nNMzjKSCOh6YUHo9HZhUej0VmFJ6Pan0GaV9jy/7fuX7XmBm2fseYFbFb55Q8b7yGLX2q6y3mvI/w1jK/3xjmQxMq2M/gL3AwTr2GwT217HfwdIxaxkCdlfZvqPKtt2l/cfabwAo/8/5TuBI2ftdwOEaxyz/s5f/+Q4A+8apV1JO9RjwpLpMBPrqeC4PSUlgaWVbZciaWdpPiuUIIUAmkrBZHgiTgNnKtj3AobLnJJRWPkuqwoCnoptO6NTMInR1pgIzStuO51hQOoFjHZ0ZpW0zONaBqvWcBDdJ+VQr+FU+7yIEyd0c61ru4Fi43F16vav0/gChM3qAsbuxUi4Z8JSlJCydUHpOHjMJYauPEKpq7ZMEuJmEsGbwkpSV3YRQuJNjS9kDhA5j8hgo7Ze831Xaf0/Z9mQfu49qKwOeGnECoVN2QsXras/lwWxm6fXk7EuWpFzaz+gQuIfQVRyo8Vy5TarJgNd9eoHZwEml55NLrytDWrXgJknKj2qhr3Lba8CrwPbS4zVGnpCjDmXAK75ejoW12cAppfcn1Xg/G4f0JalbHeFY0NsOvFJ6nbx/tfRI3m9n5BnZKggDXn5NAk4FTis9n04Ia6eXbU+6bz2RapQkdbYkEL4KvAhsA14qPb9ICIgvlJ4PRapRVRjw4pkNnAWcAcwtPc4oez8Hg5skqRiOEELf86XHi8BvS69fKL3eHq26LmTAa59e4Ezg7NLjzaVH8n5GvNIkScrcTuBZ4LnSc/J6MyEIGklSZMBr3UTgHOBtwFuB84BzS4+pEeuSJKko9gLPlB5PARuAfkL4c+m3CQa8xkwDFgALgQuBdwLnU9/tlSRJUmP2A78G1gOPl56f5NidT1SDAW9spwLvLXu8HTguakWSJHW3w8ATwI9Lj58QTvJQGQPeSJOAxcC/BP4QeEvUaiRJUj2eAf4O+AHwCC7rGvAId1n4EPBh4I/w5AdJkopsJ7AauI8Q+PbELSeObg14vcClwKeAy/FkCEmSOtEe4HvAtwjLuV0Te7ot4J0KfBr4JOFSJZIkqTs8C9wF3EG4cHNH65aA9zZgGfAneMN7SZK62X7g28B/BTZGrqVtOj3gvQe4Hvgg3n9VkiQdcwT4PvBVYG3kWlLXqQHvfGAF8MexC5EkSbn3feBGwsWVO0KndbXmEgYpn8BwJ0mS6vMRQna4Ezgtci2p6JQO3hTgS8B1eEasJElq3iDwX4CvlF4XUicEvEuBrxPuAytJkpSGzcDVhAsoF06Rl2hnA38D/AjDnSRJStc8wgWT7wLeELeUxhW1g3cZ4S/8lMh1SJKkzvcy8KfAP8QupF5F6+BNBm4DHsBwJ0mSsvFG4IeEK3RMjFxLXYrUwTsXuBtYGLsQSZLUtX4BXEGY0cutonTwPkr4CzXcSZKkmN4FPEa4tEpu5T3g9QC3AN8B+iLXIkmSBDADWAX8JSGr5E6el2inEu4V99HYhUiSJNVwD/BnhHvc5kZeA94bCCdSvDt2IZIkSeP4CfBhYEfsQhJ5DHhzCGeqnB+7EEmSpDr9mnAZt5diFwL5C3hnES5cfHbkOiRJkhr1G8IdtrZEriNXJ1mcC/wYw50kSSqmNwGPEO6CEVVeOnjzCH8hb4xdiCRJUoteBN5LxE5eHgLe6cCjwBmxC5EkSUrJc8D7CGEvc7GXaE8C/h7DnSRJ6ixvJlwRZGaMH48Z8KYBPyDM3kmSJHWatxMuiDwp6x+OFfB6gDvwOneSJKmzXQp8PesfjRXwrgf+daTfliRJytKVwL/L8gdjnGTxQeD7wHHZ/7QkSVIUh4ElhHMP2i7rgHcO8HMiDRxKkiRFNAC8g3BB5LbKcol2EnA3hjtJktSdZhGy0IR2/1CWAe8WQmqVJEnqVouAv2z3j2S1RPvPgR8Szp6VJEnqZoeB9xPu4tUWWQS8E4EngTnt/ylJkqRCeB64ENjRjoNnsUT73zDcSZIklZsLfLVdB293B28J4TYdkiRJGmmYMMb2UNoHbmfAmwL0A29q309IkiQV2jPAAuBAmgdt5xLtlzDcSZIkjeUc4ItpH7RdHbyzgKcJXTxJkiTVthc4G3glrQO2q4P3ZQx3kiRJ9ZgO3JzmAdvRwXsn8Au85p0kSVK9DgFvA55N42Dt6OD9Zwx3kiRJjZhIuOtXKtLu4F0M/DTdQ0qSJHWFYeAi4PFWD5R2B+9LKR9PkiSpW/QA/yGVA6XYwVsArMflWUmSpGYNAW8FftPKQdLs4F2L4U6SJKkVE0jhunhpdfDmAFuASekcTpIkqWvtIdyrdkezB0irg/cZDHeSJElp6AP+TSsHSKOD10vo3p3R+qEkSZJEuB7eWwhn1jYsjQ7eEgx3kiRJaTobuKTZL6cR8D6RwjEkSZI00qea/WKrS7QzgG3A1NYOI0mSpAq7gVOBfY1+sdUO3kcw3EmSJLXD8cCHmvliqwHvgy1+X5IkSbVd3syXWlminQy8SlimlSRJUvr2ALOBA418qZUO3vsw3EmSJLVTHyFzNaSVgNfUmrAkSZIa8oFGv9BKwHP+TpIkqf3+sNEvNDuDdybh7hWSJElqvzmES9PVpdkOXsNrwZIkSWra4kZ2bjbgNX3rDEmSJDVscSM7NxvwGvoRSZIktWRxIzs3M4N3OvBC41+TJElSC04GXqtnx2Y6eIua+I4kSZJaU3cGaybg/bMmviNJkqTWtDXg2cGTJEnK3sX17tjoDN5kYACY0tjXJEmS1KJ9wExgaLwdG+3gXYjhTpIkKYZpwHn17NhowHtH47VIkiQpJQvq2cmAJ0mSVBwGPEmSpA5zYT07NXKSxSRgd+lZkiRJ2XsdOHG8nRrp4J2P4U6SJCmmNwBnjLdTIwHvguZrkSRJUkrGXaZtJOC9vYVCJEmSlA4DniRJUocZ90xaA54kSVKxjNvBq/cs2pOAV1ssRpIkSa0bJtyybHetHert4M1PpRxJkiS1qodxVlbrDXjntF6LJEmSUjJmNqs34L01hUIkSZKUjnPH+tAOniRJUvGMOT5Xb8B7SwqFSJIkKR1vG+vDes6inQjsAyakU48kSZJadBiYDhyo9mE9Hbw3Y7iTJEnKk+OAs2t9WE/A8wQLSZKk/JlX64N6At6YZ2lIkiQpiponwdYT8DzBQpIkKX/eXOsDO3iSJEnFVHOMzg6eJElSMdXMaONdJmUasDflYiRJktS6w8BU4FDlB+N18M5sSzmSJElq1XHAGdU+GC/gnZV6KZIkSUrLWdU22sGTJEkqrrnVNhrwJEmSiutN1Ta6RCtJklRcVZtxBjxJkqTiairgVV3XlSRJUi6cVW3jWNfBmwwMAj1tKUeSJEmtGiJcC2+ofONYHbwzMdxJkiTl2QTgjZUbxwt4kiRJyrezKjeMFfBG7SxJkqTcOatyw1gB77T21SFJkqSUnF65YayAN2pnSZIk5U5DM3hz2liIJEmS0jEqsxnwJEmSiq2hJVpn8CRJkvJvVFOu1oWOJwIH8Dp4kiRJeXeIcIOKo7GuVgdvDoY7SZKkIpgInFy+YayAJ0mSpGIYcSZtrYDn/J0kSVJx1BXwRl1PRZIkSbk1YvXVJVpJkqTiG7H6agdPkiSp+Orq4DmDJ0mSVBx1BbzZGRQiSZKkdJxa/qZWwDu1xnZJRffggzB/fuwqJEnpOrH8Ta2Ad2KN7ZKK7rLL4Ikn4LbbYNas2NVIktIxojlX7VZlJwCvZ1SMpKwNl/2r374dbr4Z7rgDhobi1SRJSsM0YBCqd/BOyrYWSdHMng233x46epddFrsaSVJrjt6uzIAnKczkPfgg3HcfzJsXuxpJUnOOZrhqAe/kKtskdYMPfxieegpuvdX5PEkqnjEDnpdIkbrZpEmwbBls2gSf/SxMmBC7IklSfU5JXlQLeF4iRdKx+bzHHoPFi2NXI0ka39GroFQLeF4iRdIxF1wADz8Mq1Y5nydJ+Xa0SWcHT1J9li51Pk+S8u3omJ0zeJLql8znbdgAV14JvbWulS5JimDMy6QY8CSN7ZRT4JvfhHXrnM+TpPwYs4PnDJ6k+ixc6HyeJOXHCcmLagHvDRkWIqkTLF0KTz4Jy5dDX1/saiSpWx3NcJX3op0M7M+4GElZGq5yB+o0bd0KN90Ed90FR46097ckSeWOAMfB6A7eCaP3laQGzJnjfJ4kxdFLqYtXGfBcnpWUjoUL4aGH4O67Ye7c2NVIUrc4AQx4ktqppwc+/vFwWRXn8yQpCwY8SRmZOhVuvBE2bgzXz+vpiV2RJHUql2glZSyZz3v0UVi0KHY1ktSJqnbwPMlCUvstWhRCnvN5kpS2E8GAJymWyvm8adNiVyRJnaDqEq0BT1K2kvm8p58Ogc/5PElqRdUlWu9DKymOuXPDkq3zeZLUiqodvBkRCpGkY8rn8047LXY1klQ0s2B0wJsZoRBJGql8Pu/GG8MyriSpHjPAgCcpz/r6wgkYGzY4nydJ9ZkJLtFKKoLy+byLLopdjSTlmR08SQWzaBH8/OfhYslz5sSuRpLyaCZAz/DIjcPV9pTUQYY75J/5nj2wYgXcdhsMDsauRpLyYj8wtTzgzQQGYlUjKSOdEvASzz8P110H3/1u7EokKS+mlC/ROn8nqXjmzoXvfAcefhgWLoxdjSTlwczygOf8naTiWrwY1q1zPk+SYIYBT1Ln6O2FK6+EjRvhhhtg0qTYFUlSDDNdopXUefr64MtfhqeegqVLY1cjSVmzgyepg82bB6tWOZ8nqds4gyepCyTzeXfeCbNnx65GktqtrzzgTYtWhiS1W28vXHUVbNoEy5Y5nyepkx1fHvD6opUhSVmZNQtuvdX5PEmdbMQM3vRoZUhS1srn8+bPj12NJKVpumfRSupuixfDE0/A7bc7nyepU0x3iVaSJkyAz37W+TxJnWK6S7SSlCifz1uyJHY1ktSs4w14klRp3jx44AF48EHn8yQVkSdZSFJNl13mfJ6kIpruhY4laSzl83lXXx3eS1K+OYMnSXWZNQtWrgwdvcsui12NJI2lz7NoJakR8+eH2Tzn8yTl14iANzlaGZJUNMl83te+Frp7kpQffT3Dx94M195PUscY9p966rZvh5tvhjvugKGh2NVI0t4k4E0G9kctRVI2DHjt098fLpS8enXsSiR1uWSJdkrUKiSpEyTzefffH66lJ0mRJAHv+KhVSFIn+dCHwt0wbr3V+TxJUSQBzxMsJClNkyaF5dpNm8J19Lx+nqQMJQFvWtQqJKlTzZ4d7oTx2GOweHHsaiR1iSTgeZFjSWqnCy6Ahx+GVaucz5PUdi7RSlKWli51Pk9S2yUBz7tYSFJWkvm8DRvgyiuht3f870hSA5zBk6RYTjkFvvlNWLfO+TxJqUoC3sSoVUhSN1u40Pk8SanyQseSlBdLl8KTT8Ly5dDn5Iyk5nmShSTlyZQpcOONsHGj83mSmuYSrSTl0Zw5zudJaponWUhSniXzeXffDXPnxq5GUkH0VjxLkvLo4x8Pl1VxPk9SHZJgd3zUKiRJ45s61fk8SXXxvw6SVDTJfN5PfwqLFsWuRlIOJQFvRtQqJEmNW7QIHn3U+TxJoyQBrydqFZKk5vT0jJzPmz49dkWSciAJeP4XQZKKLJnP6+8Pga/H/98udbMk4E2IWoUkKR1z54Yl20cfdT5P6mLeqkySOlH5fN7pp8euRlLGvFWZJHWqZD7v6afD8u3UqbErkpQRb1UmSZ2ury+cgLFhg/N5UpfwVmWS1C3K5/Pe9a7Y1UhqI29VJkndZtEiWLs2XCx5zpzY1UhqA29VJkndqLc33O5s40bn86QOZOdOkrpZ+Xzexz4WuxpJKTHgSZLCfN7f/i08/DAsXBi7GkktSgLezKhVSJLyYfFiWLfO+Typ4OzgSZJGKp/P+/M/h0mTYlckqUEGPElSdX198Fd/BU89BUuXxq5GUgMMeJKksc2bB6tWOZ8nFYgzeJKk+iTzeXfeCbNnx65G0hjs4EmS6tfbC1ddBZs2wbJlzudJOdUzHJ4HsIsndYfh4dgVqJNs3gzXXw/33hu7Ekll7OBJkppXPp83f37saiSVOIMnSWrd4sXwxBNw++3O50k5kCzRumYjdQuXaNVuAwPh9mcrV8LBg7GrkbqSAU/qNgY8ZWXzZvj85+HBB2NXInUdZ/AkSe0xbx488EAIeM7nSZky4EmS2uuyy5zPkzLWMww9wJHYhUjKiEu0imlgAP7iL+DrX4ehodjVSB2rZzicQTsQuxBJGTHgKQ/6+8OFklevjl2J1JFcopUkSeowvcCu2EVIkrrEwEDo3C1YYPdOaqMJeIkUSVK7DQ3BHXfAzTfD9u2xq5E63oTYBUiSOtzq1aFr198fuxKpaziDJ0lqj82b4SMfgSVLDHdSxgx4kqR0DQzAddfBeefBfffFrkbqSi7RSpLS4ZydlBtJwNsFzIhZiCSpwNasgS9+EX75y9iVSOLYEq1n0kqSGrd5M1x+OVxyieFOyhGXaCVJjRsYgOXLYeVKOHgwdjWSKhjwJEn1O3IE7roLbroJtm6NXY2kGpKAt5NwT1pJkqpbswauvRbWr49diaRxeJkUSdLYnn322Jyd4U4qBJdoJUnV7dkDK1bAbbfB4GDsaiQ1wIAnSRrJOTup8Mpn8CRJ3e4f/xG+8AWXYqWCcwZPkgTPPw9XXAGLFxvupA7gEq0kdTPn7KSOlAS83VGrkCRla3g4zNndeKNzdlIHSgLekahVSJKys3ZtuG/s2rWxK5HUJskM3r6oVUiS2i+Zs7v4YsOd1OGSDt6hqFVIktpncBBuucU5O6mLJAHvQNQqJEnpGx6Ge+6BG24I3TtJXSMJePujViFJSpdzdlJXS2bwhqJWIUlKx8svO2cn6WgHb2/UKiRJrRkcDDN2K1aEa9tJ6mpJwBuOWoUkqTnO2UmqIgl4u6JWIUlq3GOPwdVXuxQraRTvRStJRbN1K1x1Ffze7xnuJFXlrcokqSics5NUJ29VJklF8L3vwbJlztlJqksS8LxVmSTl0fr1cO21sGZN7EokFUgyg+etyiQpT5I5u4suMtxJapi3KpOkPDl4EL72NVi+3Dk7SU3zVmWSlBf33gvXXw+bN8euRFLBJQHPJVpJisU5O0kpS2bwPMlCkrK2fTt85jPO2UlKXdLBc9BDkrJy8CCsXBnm7AYGYlcjqQMlAe9g1CokqVs4ZycpA3bwJCkL/f1wzTXw0EOxK5HUBZzBk6R22r4dPvc5WLDAcCcpM14HT5LawTk7SRElAW931CokqZOsXg2f/zxs2hS7EkldKlmi9ULHktSq/n5YsiQ8DHeSIkoCnku0ktSs8jm71atjVyNJeyeUvdkJzIxViSQVztAQfOMbcNNNztlJypMD5QHPLp4k1Wv1ali2LCzLSlK+7CkPeHujlSFJRdHfH4KdS7GS8mtPb/mbaGVIUt4NDMC11zpnJ6kI9lTO4EmSyg0NwR13wM03h5MpJCn/9rpEK0m1OGcnqZgMeJI0yubNIdjdf3/sSiSpGbvKZ/AMeJK628AAXHcdnHee4U5SkY2YwfMkC0ndyTk7SZ1lRMDbFa0MSYplzRr4whfgiSdiVyJJadnrEq2k7rR5M1x+OVxyieFOUqfxJAtJXWZgAJYvh5Ur4eDB2NVIUjvsMuBJ6g5HjsBdd8GNN8K2bbGrkaR22u2FjiV1vjVrwl0o1q+PXYkkZWHErcoMeJI6S/mcneFOUvfY6Vm0kjrPnj2wYgXcdhsMDsauRpKytsslWkmdI5mzu+km2Lo1djWSFIsdPEkd4pFH4ItfdClWkipuVWYHT1LxPP88XHGFc3aSdMzOnuGRG4ar7yepYwx3yD9z5+wkqZoDwJQJFRt3AjMjFCNJ9RkePnY9O+fsJKnSToDKgLcLA56kvFq7NszZrV0buxJJyqudAL3VNkpSriRzdhdfbLiTpLHtgtEdPAOepPwYHIRbbnHOTpLqV3OJVpLiGh6Ge+6BG24I3TtJUr3s4EnKIefsJKkVAzB6Bu93EQqRJHj5ZefsJKl1O2B0B+/1CIVI6maDg2HGbsWKcG07SVIrXofRAW9HhEIkdSPn7CSpHap28Ax4ktpv3Tq45hqXYiUpfdth9AyeS7SS2mfrVrjqKnj3uw13ktQezuBJyohzdpKUFQOepAx897tw3XXO2UlSNqqeZGHAk5SO9evh2mthzZrYlUhSN9kBo2fwPMlCUmuSObuLLjLcSVK2jlBjifYAsA+YlnVFkgru4MEwZ3fLLc7ZSVIcO4BhGB3wkg8NeJLqd++9cP31sHlz7EokqZsdXYmtXKKF0vVTJGlc69fDJZfA5Zcb7iQpvqPnUhjwJDVu+3b49Keds5OkfDma4aot0RrwJFV38CCsXAnLl8PAQOxqJEkjvZq8qBbwXsmwEElF4ZydJOWdHTxJdervh6uvhocfjl2JJGls25IX1WbwtlXZJqnbbN8On/scLFhguJOkYvhd8sIOnqSRnLOTpKI6OmZXLeC9WmWbpG6wejVcc41zdpJUTK8lL6ot0b5WZZukTtbfD0uWhIfhTpKKyoAniZFzdqtXx65GktSao6uwPcPVdzgATMqoGElZGh6GoSH4xjfgppucs5OkzrAX6EveVJvBg3AWxpxMypGUrdWrYdmysCwrSeoUI66CUquD9ziwMINiJEmS1Lp/At6dvKk2gwdeKkWSJKlIRnTwagW8lzIoRJIkSemoK+C9nEEhkiRJSseI7FYr4G3NoBBJkiSlY8Tqqx08SZKk4hvRnHMGT5IkqfhcopUkSeowIwJerevgTSTczaKn/fVIkiSpBYeAycDRWFerg3cI70krSZJUBNsoC3dQO+CBc3iSJElFMGq0bqyA5xyeJElS/r1YucGAJ0mSVGwNdfBcopUkScq/UdcvHivgjWr3SZIkKXcaWqLd0r46JEmSlJItlRvGCnjPt68OSZIkpWRL5YZaFzqGcMG8QbzYsSRJUl4NAVNLz0eN1cE7gGfSSpIk5dmLVIQ7GDvggcu0kiRJebal2sbxAl7VL0mSJCkXqjbjDHiSJEnFtaXaRpdoJUmSius31TbawZMkSSoul2glSZI6zJZqG8e6Dh7ANGBv+rVIkiSpRYcJ18A7VPnBeB28fcC2dlQkSZKklrxMlXAH4wc8gE3p1iJJkqQU1Mxo9QS8DSkWIkmSpHRsrPWBHTxJkqRieq7WB3bwJEmSiumZWh/UE/Bqtv8kSZIUzbO1PhjvMikAEwln005Irx5JkiS14DDQB+yv9mE9HbxDjLHGK0mSpMxtoUa4g/oCHrhMK0mSlCdPj/VhvQGv5hCfJEmSMtc/1od28CRJkopnzOabHTxJkqTiGfMydvWcRQtwEvBqCsVIkiSpdbOAnbU+rLeD9xqwLZVyJEmS1IrnGSPcQf0BD+DJ1mqRJElSCsbNZAY8SZKkYjHgSZIkdRgDniRJUod5Yrwd6j2LFmASsLv0LEmSpOztA2YQ7kVbUyMdvIPAr1upSJIkSS35FeOEO2gs4AE83lwtkiRJSsFj9exkwJMkSSqOurKYAU+SJKk46urgNXKSBcAUYACY3Hg9kiRJasE+YCYwNN6OjXbw9lNncpQkSVKq1lFHuIPGAx7A2ia+I0mSpNb8tN4dmwl4P2viO5IkSWpN3Rms0Rk8gNOBFxr/miRJklpwMvBaPTs208F7EdjSxPckSZLUnGeoM9xBcwEPYE2T35MkSVLj1jSyswFPkiQp/9Y0snMzM3gAZwG/ae6rkiRJatAcYFu9OzfbwduCc3iSJElZ6KeBcAfNBzyA/9fCdyVJklSfHzX6hVYC3v0tfFeSJEn1abip1uwMHoT70b4GHN/8ISRJkjSGPcBs4EAjX2qlg3cAWN3C9yVJkjS2B2gw3EFrAQ/g/7b4fUmSJNW2qpkvtbJECzADeAWY0tphJEmSVGE34fIoexv9YqsdvF14soUkSVI7fJ8mwh20HvAAvp3CMSRJkjRS0xmr1SVagOMId7U4o/VDSZIkCXgOmAc0FdXS6OAdBv46heNIkiQp+B80Ge4gnQ4ehAHALcCkdA4nSZLUtfYAZwKvN3uANDp4AFuBe1I6liRJUjf7Fi2EO0ivgwdwIfA40JPeISVJkrrKEHAOYQavaWl18AB+CTyY4vEkSZK6zSpaDHeQbgcP4D3AT9I9pCRJUlcYBt4FPNbqgdLs4AH8FLt4kiRJzfgOKYQ7SL+DB/BO4Bc4iydJklSvQ8B8YHMaB0u7gwcheX6nDceVJEnqVHeSUriD9nTwAN4E9ANT2nN4SZKkjrGXcNeKbWkdsB0dPAi3Lvtqm44tSZLUSW4hxXAH7evgAUwFniJ08yRJkjTaRuAC4ECaB21XBw9gELi6jceXJEkqsmHg35JyuIP2BjyAB4D/3ebfkCRJKqJvAQ+148DtXKJNzAZ+Bcxp/09JkiQVwgvAAmBHOw7e7g4ewHbgk4Q2pCRJUrc7DHyCNoU7yCbgAfw9cFtGvyVJkpRnXwEeaecPZLFEm5gE/Ax4R3Y/KUmSlCs/B34fGGrnj2QZ8ADOBdYCM7P9WUmSpOh2AgsJ1wtuq6yWaBMbCGvOhzP+XUmSpJgOAx8jg3AH2Qc8gB8A/zHC70qSJMXy74EfZvVjWS/RHv1dwvXxPh7n5yVJkjJzF/BnWf5grIAHMI1wcb93xytBkiSprX4CvB84mOWPxgx4ACcBjwLz4pYhSZKUuieB9xJOrshUjBm8cq8BlwIvRq5DkiQpTb8BPkCEcAfxAx6EW3VcAmyNXYgkSVIKXiQsy0ZrYOUh4AFsJvxFvBwsMprKAAAE0UlEQVS7EEmSpBa8QFidzORyKLXEnsGrdBbwI+DsyHVIkiQ1ags5CHeQnw5eYgthGPGpyHVIkiQ14inCLciihzvIX8CDMIv3PuCfYhciSZJUh0eBPwBeil1IIo8BD+B3hBbnvbELkSRJGsM9hPMIXo9dSLm8BjyAvcBHgRVAzkYFJUlSlxsG/hNwBbA/ci2j5O0ki1r+FfDXQF/sQiRJUtfbRbj12KrYhdRSlIAHcC7wf4ALYxciSZK61jpC125T7ELGkucl2kobgEXAf8clW0mSlK0jwFeBi8l5uINidfDKLQHuAk6OXIckSep8W4E/JVyrtxCK1MEr9yBwPvA/YxciSZI62v8C3k6Bwh0Ut4NX7v3A14G3xC5EkiR1jOeAqwlNpcIpagev3D8AC4BbyOFpypIkqVAGCZc/OZ+ChjvojA5eubmE/1E+QWeEV0mSlI0h4NvAzcCLkWtpWacFvMTbCRdI/kDsQiRJUu7dB3wJ6I9dSFo6NeAl3gPcAPwx0BO5FkmSlB9HgB8AXwF+FrmW1HV6wEvMB64F/gSYHLkWSZIUz37CUuxtwDORa2mbbgl4iVOBzwKfJMzrSZKk7vAs8DfAN4BXI9fSdt0W8BK9wKXAp4DLgalRq5EkSe2wB/ge8C3gx3TRnbC6NeCVmwl8uPT4I+D4uOVIkqQW7AT+jnDixP2EkNd1DHgjTQYWE8LevwDmRa1GkiTVYyPwQ0KgewQ4GLec+Ax4Y5sDvBf4feAPCBc9PC5qRZIkdbfDwK8IS64/Bn4CbItaUQ4Z8BrTB1wAXAi8s/R8PjApZlGSJHWog8CTwOPAL0vPvwL2xSyqCAx4rZsInAO8rfSYD7wVOBdP3pAkqR6DwIbS4ynC5Us2lJ4PRayrsAx47dMLnEmY43szcHbpkbz2ZA5JUjfZRbhUyXMVz88CvyVceFgpMeDFczIhAJ5RepwFnE64Pt8ZhGv2efcNSVIRDBPm4H5LuI/r82WPF4AtwPZYxXUjA15+TSaEvNMIJ3ucVvb+jaVtpwInxipQktQVtgOvAC8RQtxLwNay9y+Wnrv+zNU8MeAV3wRgNnBS6fnU0nOy7ZSyz5JtkqTuNEwIbK+VPb9SsW1b6TnZdjhKpWqJAa/7HMfIQHhK6XkWcELpMavK86wYxUqSahoAdlR5Ln+ddN/KA5uzbl3AgKd69TAy9FULguWv+0qP40vb+vByMpKUOEC4w8JOwskHe0qPaiGtWoDbkX3JKhIDnrI0iRD0ZhGCXxIAy0NgX5V9+oAZwDTCbOIJpedp2ZYvqYvtJcyY7SCEs32MDGa7S5/tKXtUhrfdhJC2B+fV1GYGPBXdDEJwrBUAx/t8CuF6heM9S8qnfRwLXOXPg8D+sue9pe0DjAxoBwjBa7zPpUIx4En1GS8AJoFxIqHjCCFUHkcImNNL22YSrpFYbVt5V3IWYVm8fFv5saWYdgNDpddJoDpC6FjV2pZ0wA4TghOETtah0rF2V2yrFdwqnyVVYcCTiqmXEAwTSSCEsLQ9ofQ66VrC6G5kElBrva/cv1o3Mwmn5SqPAyHozqjy56BU63gX/k7Cci3tWLIfL0CUB5VadlH7DMSdjB52T7pN4/1O5X6Nvi//syVhCUYGrWFCNysxUNomqQD+P1ZVyObI+Hk2AAAAAElFTkSuQmCC') no-repeat right;
+    width: 1267px;
+    height: 425px;
+  }
+  #wordmark {
+    background: transparent url('data:image/png;base64,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') no-repeat;
+    width: 1267px;
+    height: 385px;
+  }
+}
+
+@media only screen and (min-device-width : 1281px) and (max-device-width : 1920px) {
+  #lozengeBg {
+    width: 480px;
+    height: 212px;
+  }
+  #lozenge {
+    background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATwAAADeCAYAAACg/rw7AAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAEalJREFUeJzt3W+MHPV9x/H3Lec/Z599NpiYvxY0DlUNxXEbwCGp6jzCPAiNaQKEPKgUGlUiCRI4pYlAoVFMhCpKHiAUJUoqqigESJSIB4mvrRRMS6ibQoyTcBBsAqHBxrGx7/zv7LNz2we/Gd/e3u7d/pv57Z/3Sxrt7uzO3NcS+vCb78z8pq+I2lABGALmA4uBAWBh8t1S4Kzk/fLktR9YkrxfmPweYBCYl7xfBvQl2y4t+3vLyz6X7iOVbl9qKKl1LkuSGmdzGjhSw74mgbGydUVgtGzdOHCibN2hss+HgT+UbX8KOFphH0eSGkl+W0y2PZysO5H8/hgwkdQ4WcO/RznqM/AakgbMckKopMsQIUzmJ68LgEXJsiBZ108Ij3Qfabik4VRriKj9peGchmgaoGl4jiavh4GTwPFkOZmsm0hex5Lt0+UQ0wNYNerlwFsKrATOIQTQ8mRZNsvrUkIwLaywPylvJwgBeJgQnqOEMDxU8r789R1gH1Mj057SbYG3BLgYuAi4ALgQWAGcm7yel7yuIIy4pF51EjiQLG8nr/uT17eAPcDvkqVrwrHTAm8pcBnwnmT5I6aC7WKm+liSWuco8CZTQfgbYFeyvEoHBWK7Bt4C4L3ANcCVhHD7Y8IhqKT2sg/4NSEAfwH8DNhBGEW2lXYJvLOB64BrgasJYTc/akWSmjEBvEgIv/8GhoGDUSsibuANATcDtwB/wdyXLUjqXKeBZ4HvAk8w89KiXMQIvLXAXcDHmHmtl6TuNw58D/gqYRSYmzwDbz1wH+HQtfwCVkm9aRj4MvBcHn8sj8C7FPhnYFP2f0pSh/ohsBl4Pcs/kuUV/f3A3wO/wrCTNLtNhKy4mwz7+VmN8N4NPEY44ypJ9fgZcCvwWqt3nMUI72bg5xh2khpzNSFDbmn1jlsZeH3AFsJp5/LZOCSpHksJR4n308KTnK06pJ0H/Cvw8dbsTpLOeBz4G8LFzE1pReANEC4k/HDzu5Kkin5EuHZ3vJmdNBt48wkXEN7Q3G4kaU7DwF/RxEivmR5eH/AvGHaS8rGRkDkN9/SaCbx/BD7RxPaSVK9PAF9qdONGD2k/CjyJt4hJyl+RcMnKk/Vu2EjgXUK44Xeo/k0lqSUOE6aRq+tWtHoPac8iXBtj2EmKaSnwHeq8Da3ewLsDeH+d20hSFt5PyKSa1XNIezEwQnhqlyS1g6PA5YRnbsypnhHeAxh2ktrLICGbalLrCG8d8Dw+IFpS+ykCf054cNCsag2wL9fxW0nKUx9hkoG5f1jDCG8NYWI+r7mT1K6KwBWE8wxV1TJquxPDTlJ76yPMsD77j+YY4S0B9gKLW1KSJGXnOHA+4aLkiuYa4d2EYSepMywizLhe1VyB1/IpliUpQ7NOQjzbIe0QsJ8wm7EkdYJTwLnAWKUvZxvhXYdhJ6mzzAOur/blbIG3oeWlSFL2/rLaF7MF3gczKESSslY1u6r18JYDB/DuCkmdZ5LQxztY/kW1QPvALN9JUjsrANdW+6KSD2RXiyRlrmKGVQu8dRkWIklZ+7NKK6v18N4GVmZYjCRlaR9wXvnKSiO88zDsJHW2lYT7aqepFHjvzb4WScrc2vIVBp6kbjXjXESlwLsih0IkKWszsqxS4P1JDoVIUtZmZFn5Wdo+4AjOgSep8x0nTGI8ma4oH+FdhGEnqTssAi4sXVEeeO/OrxZJytx7Sj+UB95lORYiSVlbXfqhPPAuzbEQScratEwrD7xL8qtDkjJ3SemH8sBblV8dkpS5S0o/OMKT1M2mDeJKr8NbAIwTrsWTpG5QBAaAkzB9hHcxhp2k7tJHyZFraeDZv5PUjS5K35QG3gURCpGkrJ3JttLAmzFZniR1gTPZ5ghPUrerOMK7sMIPJanTnck2D2kldbuKI7wZT/iRpC5w5qFkhUor1QO2boU1a2JXIeXhzGAuDbyFhJlB1Ss2boSdO+Ghh2DZstjVSFlaTLjb4kzgnRuvFkXT3w933gm7dsHtt4fPUnd6F0wF3rsiFqLYVqyARx4JI76NG2NXI2XhXJgKvBURC1G7WLMm9PaeegpWr57791LnmBZ4nrDQlBtugJdeggcftL+nbrESpgLv7IiFqB3Nnw+bN9vfU7dYDlOBtzxiIWpnaX/vhRdgw4bY1UiNMvBUhyuvhKefhh/8wP6eOtHZMBV450QsRJ1k0yb7e+pE54A9PDUi7e+98gp88pNQKH80itR2po3wDDzVb+VK+Na34Pnn7e+p3U3r4Q1FLESdbt06+3tqd8vAwFMrbdoEv/wlbNkCg4Oxq5FKLYWpxzQeJ7m5Vj2iWJz7N83YuxfuvRcefRQmJ7P9W9LcTgADfUWYB0zErkY5yzrwUjt2wF13wbZt+fw9qboFBTycVZbWrYOf/AQeewxW+SRQRTVk4Cl7fX3w8Y+Hy1js7ymeoQJhcjwpewMDcM898Oqr4fq9vr7YFam3LC6QnL2QcnP++eH6veeeg/XrY1ej3rGkACyKXYV61Pr1IfTs7ykfiw08xVXe31vkf47KzICBp/aQ9vdefjkEoP09td4ie3hqL6tWhUNc+3tqvaEC3mGhdlTa37vootjVqDt4SKs2lvb3Xn45HO4O+P9mNWWgAHgVqNrb4GA4ofHKK/b31IxBA0+do7S/d9VVsatR5xm0h6fOs349bN8eLl4+//zY1ahzLDLw1JkKhXB72quv2t9TrRYaeOpspf29m26KXY3a26ICMD92FVLTVq2CJ54IU82vWxe7GrWneQVgQewqpJbZsCE8VMj+nmZaWAAWxq5CaqnS/t4XvhAeKyklMx73x65CysTgIHzlK+HB4Zs2xa5G8c1zAlB1v9WrwyMk7e/1ukUFwMvW1RvS/t43vwkrVsSuRvkrOFuKekuhALfdBrt2webN9vd6y5LC3L+RutCyZfDgg/b3ekwhWaTeVNrfW7MmdjXKVn8BWBK7Cim6DRtg50545BH7e91rsaM7KdXfD7ffbn+vi/UVYRQfxt17isXYFbS/3bvhjjtg69bYlag1xhzhSdWsXg0//nEIPPt7XcHJA6S5bNxof687LOwrgsc2vchD2saMjsIXvwhf+xqcPh27GtXJwOtVBl5zRkbCiY3h4diVqA728CT1DANPqsfoaBjZrV3r6K4D9QPjOM27NLvTp+Eb34D77oMDB2JXo8ac7AcmMPCk6oaHw6huZCR2JWrOCQ9ppWp274aPfASuv96w6xIGnlRudBQ+9zm4/HJ46qnY1aiFnN5dStmn63r9wBG8l1a9bts2uPNOePHF2JUoO8cKwGTsKqRodu+GG2+ED33IsOt+pz2kVW8aHYUtW+Dhh2FiInY1ykk/cDh2EVJuJifh0Ufh3nth797Y1ShfR/rxXlr1im3b4K67YMeO2JUojskCcCx2FVKmSvt0hl0vO94POMeNutPRo/DAA/DQQzA+HrsaxXeqHzgRuwqppezTqbKT/cDJ2FVILfPMM+F6Og9dNdOJAmHyAKmzvfkm3HqrfTrN5lQ6PZTUmezTqXbHDTx1pmIx9Onuucc+nWp1wsBT59m+PfTptm+PXYk6y3gBOBq7CqkmaZ/u2msNOzXiSD8Gntrd+Djcf799OjXraD9wPHYVUkXFIjz+OHz+82F0JzVn3B6e2pN9OrXeeAFnS1E72bPHPp2yMuYhrdrD+Hjo0T3wQLi2Tmq94wae4rJPp/yMG3iK54UX4DOf8dBVeTlmD0/527sXbrsNrr7asFOejvTjBKDKi306xXWsHxiLXYV6wPe/D5s326dTTGN9RZiPc+L1nmJOjzLZsSM8R2Lbtnz+nlTdgnQ+PC8+Vmulfbr3vc+wUzs4AUykz6UdAwYiFqNuMTER+nT332+fTu1kDMJzadMP58WrRV3hhz+Eu+8OTwmT2sthmB54UmPs06n9jQIUkg8HIxaiTnXgAHzqU/bp1AkOwdQIz8BT7SYm4OGHYcsWGB2NXY1Ui4MwFXjvRCxEncQ+nTrTOzAVeIciFqJOMDIS7nt9+unYlUiNOAhTPTwDT5UdOACf/jSsXWvYqZPZw9Ms7NOpu0wLvH0RC1G7GR6Gz37WPp26yT6YOqQ9ELEQtYuREbj++rAYduou+2Eq8H4fsRDFVtqnGx6OXY2Uhf0AfcmcGQtxAoHeUizC6dPw9a/Dvffap1O3WwSM95VMEnQYWBKrGuVs69YwP93ISOxKpKwdAwZhaoQHsAtYHaceScrMayTZVihZ+XacWiQpU2euQikNvL0RCpGkrO1J35QG3lsRCpGkrJ3JttLA21Phh5LU6SqO8DykldSNzmSbIzxJ3a7iCM8HhkrqRr9L35Reh7eAcLdFX/71SFImioQnMp6E6SO8k9jHk9Rd3iYJO5geeABv5FqKJGXrt6UfygPPPp6kbvJG6QdHeJK62RulH8oD7/X86pCkzL1R+qE88HblV4ckZW5appUHnvN6S+om0wKv9Do8CNfgHQEW51ePJGXiOGFS48l0RfkIrwj8Os+KJCkjv6Yk7GBm4AG8nE8tkpSpGVlWKfBeyqEQScrar8pXVAq8HTkUIklZe7F8RaXA25lDIZKUtRmBV36WNvU2sDLjYiQpK7+nQoZVGuGBozxJnW3G6A6qB97PMyxEkrJWMcOqBd5PMyxEkrJWMcOq9fDOBvZTPRAlqV1NAucCB8u/qBZoB/F6PEmdaYQKYQezj+CezaYWScpU1eyaLfCeyaAQScpa1eyq1sMDGCL08ea1vh5JysQpQv9urNKXs43wxoD/yqIiScrIs1QJO5j7LOxjra1FkjL13dm+nO2QFsLkeXtxQlBJ7e84cD5wuNoP5hrhHQGeaGVFkpSR7zFL2MHcIzyANYR5pfpaUpIktV4R+FPmuH64ljspRoDhVlQkSRkZpoabJWoZ4QGsA17AUZ6k9lMEriJk1KxqvVd2B/B4MxVJUkaepIawg9pHeAAXEw5vBxsqSZJa7yhwBfDbWn5cz2wo/wfc10hFkpSRL1Fj2EF9IzyAfuA/gffXt5kktdz/AB8ETte6Qb2BB3ApYfrkpfVvKkktcZhwMvU39WzUyASfrwN/SzgzIkl5KwKfos6wg8ZnNP4esKXBbSWpGfcTzszWrZFD2jPbAt8GPtH4LiSpLo8Dt9LgEWYzgQcwH/g+8OHmdiNJc/o34AZgotEdNPuQngngZuBHTe5HkmbzI2ATTYQdtOapZOPAjcwxD5UkNegJQsaMN7ujVj2GcYLQy/sKnr2V1BpFQqbcSpMju1SzPbxKbgG+QZg8VJIacQT4O1p85JhF4AG8mzA9/NXZ7F5SF/tfwqhud6t33KpD2nKvAR8A/oEw7bIkzWUc+DxwLRmEHWQ3wit1KfBPwF/jfHqSZioCPwDupoG7J+qR1Qiv1OvAxwgTDvx7Dn9PUuf4D8KI7qNkHHaQzwiv3HuBOwkhOJD/n5cU2Tjh9tSvEiYiyU2MwEsNERqTNxOmeDkrXimSMvYH4KeEoPsOcChGETEDr9Q5wHWEoe01wFpgXtSKJDXjFLCTMGfdc4Tbwt6JWhHtE3jlFhLmurqG8Oi1y5LlXTGLklTR74FXgV3AL4CfAT8HTsQsqpJ2DbxqlgGrmQrAS4CLgAuAVcDiaJVJ3esY8CawB3iLcCIyDbhdwGi80urTaYE3l2XAhYQHDl2QvF+RLCuBc5NlBR4yq7edAg4A+5NlX/L5ACHU9hCeY/MWHRRoc+m2wKvHcsIh8jnJ+2VVXtP3S5P3gxiWag+nCE/tOkSY8nw0eZ++Hqqw7h3CIWiUkwax9XLgNWMBIfiGCEG4JPk8SAjFBcCiZFmQ/KafEJz9ye8XEi7LSQN0KPluKMd/h7I3RnjIzBhTATVO6G8dSb4bTV4PAycJdycdT94fSrZJl7FkOZp8rzoYeO1pPqEfOUAIxiWEMEyDFEIwFsrWLWPqbpblJftbytRlP4uT/afSUE71JfspldZRqvRvzWb53D+ZppaRR5GZh1knmDl90CjTZ+9JwyQ1QehPQbhs4nCFOkr/VhpCldadJgRYWscxWjTDh1rn/wH97O5y1wa/fwAAAABJRU5ErkJggg==') no-repeat right;
+    width: 633px;
+    height: 212px;
+  }
+  #wordmark {
+    background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoYAAADHCAYAAACEEZKZAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAIABJREFUeJzt3Xm4XFWZ7/HvyRwSCGAYIiEMCQEkzJNM2iqO0A6ANqBtq61evQqt9rWv2Fcbh4t2N1dBpfuKto336aZtR2aBIIQGCTNoGAImCElkCEQTIGQg59T9460yJ4eqU7v2Xmu9e+/6fZ7nPITk1Fqrxv3WWu9610Cj0UBEREQy87pwDjj1K31kjPcARERERKQcFBiKiIiICKDAUERERESaFBiKiIiICKDAUERERESaFBiKiIiICADjgCXAbKf+dwVWOPWdxUxguVPfS4E5Tn2L5KHaV2mpdImIBDcGuNqx/1c69p3FUY59X+PYt4iIiPShMcDPHfv3DLyyOMax76sc+xYREZE+NAa4Edjg1L9mDNvbgD0vIiIiIsmMAZ4HbnLq/xBgglPf3UwGDnLq+2bseRERERFJprUr2SvPcBJwmFPf3RyOX9DqmfcpIiIifaoVGF7rOIayLid7jksbT0RERCS5VmC4CL+yMWXdgOI1rhXY8yEiIiKS1PAC117Ll2WdMfTakaxlZBEREXExPDD0Wr6c2fwpkznADk59ey7ri4iISB8bHhjOBwadxlG2WUOvZeRB7HkQERERSW54YLgGWOg0jrLlGXotIy8EVjv1LSIiIn1uzIj/91pOLtuModd4tIwsIiIibkYGhl4bH8pU6HobYJ5T357HE4qIiEifGxkY3g2sdBjHJOBgh37bORIY69DvSuzxFxEREXExMjAcwm/zg1de30he+Y7XYY+/iIiIiIuRgSH4LWeWZQPK0U79ahlZREREXA00Go2Rf7cj8ATtg8aYVgC7Ju5zpDHA74FpifsdAmbgs4wvEspLPkwkqgHvAfQxr9e6nnOJrl3wtxK4J/VAKEeh631JHxSCPd4KCkVERMRVp1lBr2VN77I1OgZPRERE+lanwNCrnqF3nqFX/woMRURExF2nwHAhdhJKat4zhh6B4RrgVod+RURERLbQKTD0OrPXs9D1DsBch36vAzY59CsiIiKyhdF2HnssJ08CDnLoF2y20mPHl5aRRUREpBTKFhiC3wYQr2Vsr8dZREREZAujBYbLgUWpBjKMV4HpYx36vA97nEXqYMDxx0s/3mcRqbFuRayvTTKKLXnM3I0DDnPoV7OFIiIiUhrdAkOP/DePQtcHAlsl7hMUGIqIiEiJdAsMbwLWphjICKlnDT3yGtcC/+XQr4iIiEhb3QLDDcCCBOMYKXU9QY/6hQuwx1dERESkFLoFhuBzPF7qGUOPvEaVqREREZFSGWg0Gt1+ZzawJMFYhlsPTAM2JuhrJj47g/ci/eMqUlddP8gi0e7g/qTXm9RWlhnDpc2flFIWuvaYLVyKgkIREREpmSyBIfgse6bK+/PIL9QysoiIiJRO1sDQI88w1U5hj8LWHo+niIiIyKiy5BgCTAFWARPjDmcLK4BdI/cxCVgDTIjcz3AbgOnA8wn7FKk75XxJSnq9SW1lnTFci9U0TClFoetDSRsUAtyMgkIREREpoayBIfjkxcXeGOJR2Fr5hSIiIlJKvQSGHucmx94Y4rEjWcfgiYiISCn1EhguwvL+UooduB0duf2RVmCPo4iIiEjp9BIYQvpl0EOIlwM4B9gpUtudaBlZRERESqvXwDD1MmjMQtcey8gey/EiIiIimfQaGM4HBmMMZBSx8gxTLyMPYo+fiIiISCn1GhiuARbGGMgoYs3spQ4MFwKrE/cpIiIiklmvgSGkX06OcTLJVGBehHZHo93IIiIiUmp5AsPUx7nFKHT9SmBs4Da70cYTERERKbU8geE9wMrQA+ki9HJy6mXklcDdifsUERER6UmewHCI9JsoQm9ASb0j+TrscRMREREprXE5b/dz4N0hB9JFyEBuTOD2ski9/N6LGcA+wN7AXKy24wxgR2A7YDL2mE1r/v5q7AD555p/fhybEV0BPAg80PzvumT3QERE8pqHTb4chtX33R377N8aGACeBdZjn/HLgIeA25o/T6Ufbmm9HHss5wJ7NP9/JvZYtq6lk7EyfGCbeYewx/YPwNPAk82fh7HHeTHwu2T3oGmg0Wjkud2OwBPkm3HMYz0WmGwM0NZ+wH0B2slqCAu0Ui+/tzMGe/Mfh50TfRSwc4R+BoFfATc1f+ZjHy4iseT6IAtgwKlf8VX119vhwAeAE8mfw98A7gB+Bnwfiwn6yf7AG7Br6ZFYIBjDk1hVk19iq4+/itTPH+UNDAHuBA4NOJZujgRuD9DOXwLfDdBOVndhwZiXscAJwFuxD4HUp70AbMBe0D8Ffgg87zAGqbeqX6ilWqr4epsKnAZ8BDtVLKQXgZ8AX8JWjepoANuf8C7gFOIFgt38FrgM+DcsDguuyIxf6uXRUHmGxwRqJyuv3chzgK9iU/+XYgGxR1AIMBELTv8Fmxa/AHiF01iyajj+6D5JHZ+rOt6nKtgK+J/AY8CFhA8KAcYDp2KzWRewOfWoDqYBn8SWdW8GzsQvKARbpv4rbLb2LuCDBD46uMiM4bHYMmEqP8C+7RS1GMunS+U47MWUykHA54C3k26pP48hbPbwC9hzUjaeF5NYs1B1vE8tXvdNz1V2dbpPVXm9nQx8Hdg1wlhGsxx4H3B94n5D2hb4a+AMyh/oLgfOAb5HgJS7IoHhWGAV6R6wZcBuBdvYHniGdMs/a4DpwKYEfc0AzsWC5yotbw1i32I/S7lOhqnTRayljveppSoX6qzq+FzV6T6V/fW2E/Ad4E8jjqWbQeATwLccx5DHOODD2KTFdOex9Op+4KMUnLQrMqOU+uzfWRQvdH00aYOm+cQPCscBf4PtYjqdagWFYF8wPorlpZzkPBYRkap7E/BrfINCsM/2b2IzWVUxD9vocQHVCwrBNtfeCPwzlkKQS9GlxtTHvBUtMxO6HmI3sR+fWdiL4O+xxOIqm4ElL38Ty0kUEZHenAVcgVUOKYuzgM97DyKDj2GbOTw3i4YwgG0wuhWYnaeBooFh6o0VRQO71PULYwaGx2KnqaQ+xSW2j2PT4DO8ByIiUhFjgW9js3Opj3vN4mxsJ28ZTcDK7XyLek1K7I+VuDm41xsWDQxXAIsKttGLIoHduIK379V9WEJoDO/ElqlfFql9b4dj0/lzvAciIlJyY4GLsby4shrANkbM9R7ICNsAVwHv9R5IJDsBN2ATSZmF2LV6bYA2sjqE/Nuy96fAmnsOsWYL34vt0J7U7RcrbjfsBa3gUESkvQHgX7HaemW3NVZDuCx58FOwoPB13gOJbBqWXrB/1huECAxTLidPooc7N0LqvIEYj8s7sG9dZS5DE9JMrF7mDt4DEREpoXOAP/ceRA+OAz7kPQhsguky0tc19jINC4Iz1TIOEWDcRNqTLPIuBx8edBSjW0v4Go/HYDOFZcwfiWkOVqA7aAFPEZGKOx34jPcgcvgiNlvn6QLgtc5jSG0mNrvcdcY2RGC4AdsZm8qROW+XcsZwAfa4hLIzVgy6X4Ojo4B/8B6EiEhJ7I1tNqminbD6hl4+hJ0W0o/ejJ3iMqpQS5Ipj8c7IsdtiixB5xFyGXksNlPoeQRPGZyJLaWLiPSzsdg5uVUuUfYJYLJDv3sD5zn0WyZfxo7V6yhUYJgyz3AudoJJLw7CdiWnEvLx+DTw6oDtVdUAVrRzO++BiIg4+iTVr7U3HXhP4j4HgH8h7SbUMpoMfG20XwgVGC5t/qQwQO/5ginzC5cCSwK1tRfwd4HaqoOdsGLeIiL9aAZWE7AOzkzc3wfpn80m3bydUXIsQ+5uTTlr2GueYcpvVyEfh/Oof1maXn2QfOkEIiJV92X8N26EMg9bzUthG+yxk806nkYTMjBMmWfY687klDOGoR6H1wBvCdRWnQxQrbM3RURC+YD3AAJLVWrn05TrmMAyeDVWPuglBhqNRqhOpgCrSHOkzDNkr223NbCaNLX/NmC5E0XL9wwAt5E2oK2a1wHXR2w/2Bsjh1gFYOt4n1q87pueq+zqdJ8870udLMMOM4hpW+AxbNZQtnQptqy8hZDBUozafZ1MJ/vh0IeSriD0zYSp6fhmFBR28znvAYiISCGzgAMj93EGCgo7OQHYZeRfhg6YUuYZHprx91LmF4ZaRv50oHbq7E/IcTi4iIiUylsjtj0B+GjE9qtuHG12h4cODFOem5w14MsaQIYQ4v4fjAU9njYA/w6chH2j26r5MxM4EfgW8Ae30W32Me8BiIhIITHPKn4ntpPbw1PAudis3ByszN4EYHzzz3tiy7hfAX7nNEaAU0b+Rcgcw5ZlwK6hG23jF8DxGX5vMVbUMrYVhLnfF+J7luTNWILzb7r83rbAV4H/Fn1Ena3DToV5NkLbdcqHaqnjfWpRjmE4uk/dlTXH8EXgCmz17i5gOfB08992xFKwjseuMTM9BtjGeux6EvK0sJYb8Jlo+Sds5e+FjL8/EdtU+aloI+qsAeyOxW5AnNy7ayK02c6hdH+zT8VqAaYQYhl9MvBnAdrJ6wrsQ6NbUAi2oecjzR+vD8nJ2KymiEg/G8TO/90N+0y8EAsMV2Kfzw1sBusW7Kziuc3fL4NJxMmp3w14VYR2u7kQW83KGhSCBcV/DXwjyohGN8CIDShVDgy3pfsGlANIt/EkxDLy2/BLkr0fC0p7/db2bXzLx5zu2LeIiLcnsdIjHweeyHibdc3fL8sZ9L2WoMvidNJd/1sewwK8vD5DtomZ0LZYfY3xoM3Hvr2k0C1/8JAko7D7Oz9AO+8O0EYeDaxwdC/fcIY7G/h1sNH05rWoPpWI9KffYkHVL3Pe/rMFbhtSjGu1x2rSWRSrTLIO+F+BxtKL47AzuIE4geEaYGGEdtvptgEl1a7VhdjSahGTiZuEO5rLgFsL3H4Tfjupx2LlfURE+snTwBuwWaq8BrFyLt45k6FPQJlF2o2nYDO3PwrQzk9IvxllW4Y9B7GmWVPmGY4m1XE7Ie7vG7Hg0MP5Adq4FnggQDt5vNGpXxERD4PAycCSAG3dA1wVoJ0i5mKVL0J5C/E3vI30Q2ySpKjBZlupHd36Q6zAMNXxeIfQ+ckfD+yXaBwhNp6cEKCNPB4HbgzU1sWB2unVGxk2DS4iUnPnEPZAiW8HbCuPsVhJl1BeH7CtrEIG11cGbCurPy7nxwoM78F2Q8U2DasF1M5+pDmebyVwd4B2XhugjTwuB4YCtfWTQO30antgf6e+RURSWortLA7pavxr084N1M4Y0l9PNxE2V/MWYGPA9rL4Y2perMBwiDCbMbLodJxOqvzC6ygeWO1C5wA3tpsDtrUYq+fo4RinfkVEUjqbMEuWw71I2gMq2glVWm5/LGcupYcIcxxuyzrgvoDtZbEvzXS2mFu5Uy0ndwoMY5+/2BJi+tij1lLLLYHbWxC4vaxilDsQESmTR4iXshNyaTqPUJMjxwZqpxeLI7R5b4Q2RzOWZnAeMzCcT7glytF0CgBTLC2Gmhn1mu16Hit3ENLtgdvLSoGhiNTdvxLvuhpy9SiPWYHa8bgWPBihzYcjtNnNKyBuYBgq966bAzr8fYrAMFQuZap6iyM9TPgyBSme83b2BKY49S0iksKPI7Z9H3GOF80q1BF9qcvUQPgJFrDZ4dT2hvhVwUPs1u1mD2wTynA7Azsk6DvE/RtD5+A2tqUR2lwUoc0sxtD8tiMiUlMxlixbBrETsLyECAyn0gxuEoux2fbxCG12MxfiB4ap6hmOXE5OtUM1RGA4B7+ZrhhFNJ/FCn16SFWeSESkjkLURcxrG4pfC+eR/hg8sHOoQ3s6QpvdzIL4D+BC7CSU2EbOuKUIDEOd8JJqk0w7sXYQe324aMZQRCS/h5z737ng7b2uAWsjtOmxrL87xA8MB0mzBX7kCScpAsNQZ0KHqt2Ux6pI7S6L1G43IQukioj0G88ZQ4CXFbz9vkFG0buQpWpa1kVos5sZwPgUU64pAsORS4gpAsNQy+SewUysgqapz3ls2d2pXxGROihy7nIIRfcGeOQXQpzZvRjBZjdjgV1SBIYpNqAM/5aQahNCHQLD30dq1yNpFhQYiogU4ZUf3jK94O33CDKK3oWu7gFhViTz2DFFYLiC+DtVp7F5R9PuNKt3R3QfsDxQW7MDtZNHrBwGj6RZgO146Q51ERHJxutLfct2BW/vFRjWyc6pdu+kWE7eZ8R/Ywo1WzgBW9P3EiuHIVbuYhaej6eISJVtxO+LPdjO5Lx2RLVsQ5ieKjBMsZzcWj5OkXwa6v68PFA7ea2P1G6sJeosFBiKiOTnuZxcJDD0vJ7GWn1LUdVlpJ1SBYY3ET+RshUQxk4+XUu4MyWLbs0v6rlI7cbYup9V0RwVEZF+5jljWCQVyDMwjJFj6GW7VIHhBuDGyH20dibHnjFcgN2fEHYJ1E5esc7cjBVwZqEZQxGR/GJVq8iiyIyhPvvDmJayQvjPI7ffCghj5xiGXBav6wv5Bce+NWMoIpKfZypQkRzBFMfgpjbg0Oe0cQk7i51nOJ0007khA9ztA7ZVJi869r2tY98iIlXnGRgWqShSx0mBIjOoeSWdMVza/Kmy0PfBO4jZVLF2s/B4I4mI1MUzjn1PKHDboqemiJma+rDp2MvJsYWe9Sxas6moWJtEPDefqI6hiEh+qx37LvL57X09rYttUgeGKcrWxBQ6sNULOTzNGIqI5OeZIz6+wG312R/GuNSB4QLC7ehNLcbOau+l5Dra2nsAIiIVFqsmXxZbFbitPvvDSL6UHLIGYGo3E74Wo6q0hzfJewAiIhUW60Ss2KZ6D6AmJqQODKG6y8kx8iOLfDuS9mKfky0iUmeegWGRL/a6noYx2SMwTHFucgwxxq1vOOEV2dUmItLvPA8omOh0WxnGIzBcBCx36LeIFdi4QyuSaCvt6VujiEh+nlUlilAaUSAegSHANU795hVr+VtBTHgKtkVE8vOsQ+tx0odsKXm5mpaqBYZVXf4WERHpxUbHvouUnFG5mjAGvALD+cCgU9+9GkSBYZWM9R6AiEiFVXVXsmYbA/EKDNcAC5367tVCbLwxKFk2PG3oERERyckrMITqlK2JueytZFkREREpDQWG3VVlnCIiIkVVJc1LIhloNBpefY8BngB29BpABiuBGcBQpPbdHvymmDkZnvctxP2q+vjbqeN9avG6b3qusqvTfarb622kKj5XVRxzNy73yXPGcAjbhFJm84kXFIqIiIiUimdgCHCVc//dxDgGT0REROqjEenHhedSMtgy8hP4B6jtDGHLyCsj9qGl5Di0lNxeHe9TS92W9ur4XNXpPtXt9TZSFZ8r7+tpbXgHZCuBu53H0Mk9xA0KRURERErFOzCE8u76Leu4RERERKIoQ2BY1uPxFBiKiIhIX/HOMQQ7wmwVMM17IMOsAV5G/HpO3g++cgw7q/r426njfWqpW85XHZ+rOt2nur3eRqric+V9Pa2NMswYlvEs4iqd5SwiIiISRBkCQyhfYFjW5W0RERGRaMqwlAwwE1juPYhhZpFmPN4PvpaSO6v6+Nup431qqdvSXh2fqzrdp7q93kaq4nPlfT2tjbLMGK4AFnkPomkR5QpSRURERJIoS2AI5VlOLss4RERERJIqU2BYluPnVKZGRERE+lJZcgwBJgLPAFMdx7AWK1OzIVF/3g++cgw7q/r426njfWqpW85XHZ+rOt2nur3eRqric+V9Pa2NMs0YbgBudB7DAtIFhSIiIiKlUqbAEPyXk7WMLCIiIn2rbIGhd2DmHZiKiIiIuClTjmFLFXMb8vJ+8JVj2FnVx99OHe9TS91yvur4XNXpPtXt9TZSFZ8r7+tpbZRtxlBEREREnCgwFBEREREAxnkPQERERKTCUqehRaUZQxEREREBFBiKiIjIZlt5D0B8KTD0tcZ7ADW0znsAIiIVNt6x7+cc+5YmBYZSNxu9ByAiUmGe+XJDTreVYRQYioiISMs23gPISbONgSgw9KVzmcMb9B6AiEiFqVpJn1Ng6Gu99wBq6HnvAYiIVNjWjn0X+fxWfnkgCgx96YUc3ibvAYiIVJhnYFhkxUf55YEoMPSlF3J4a70HICJSYdMc+y5yTdRnfyAKDH1p2TM8BdsiIvl5zhgWWUVT+bdAFBj6UmAY3rPeAxARqbCpjn0XuSauDjaKPqfA0Je+4YSnwFBEJL+qzhj+Idgo+pwCQ1/egeHESO16HqmkwFBEJL8dHPsuUotQgWEgCgx9eQeGkyK163mkkgJDEZH8dnLsu8g1UYFhIAoMfXkHhrGMdexbHw4iIvnt6Nh3kTxB5RgGogrnvp70HkAknsnLzzj2LSJSdZ4zhr8vcNu6fvaPx4LeVdjjswq7r6tG/F27/89FgaGvp70HEMkUx75XOvYtIlJ1njOGuYMZYHmwUfRuDDAUqe0pWN7+VsCuPdyuQffgsV2AuU6BoS/vGcOtiLOcrcBQRKR6xgAzHPsvMlmyLNgoerc18VLDtsl5uwFgevOnFxOVY+jLe8YwVlmC7SO1m4X3YyoiUlU7AxMc+y/y+e05YxhT6vJBkxUY+vqdc/+xcgG3i9RuFo879i0iUmW7O/f/RIHbrqeeK0bJV+AUGPpah+9ycqxvIl45KuuobwKyiEhsuzv3X/R66DVrOBCx7dSbOdcpMPT3qGPfsV5wXjkqjzr1KyJSB7s79v0cxcuNPRpgHHnkzQPMIvVEy0YFhv48E2anRWp3l0jtdvNbp35D8zySSsTz5CLx9QrHvh8N0Mb9AdrII+Z7ZlbEtttSYOhvqWPf20Zqd7dI7XazxKnf0PS+FE+xjsqU8jvQse9HArSxKEAbecTMA0wZGG4CXYDKwOsbDsRb8p0Tqd1uHgjY1saAbfVqsmPfUg5rHfv23DwmfiYC+zj2H2LFxyswjHm+dMrAcC0oMCyDBx37fnmENqfit5QcMsheF7CtXmkpTzY59t1r3TOph/3wPfQixIzhEnw+u3eO2LYCwz60mHgV07uJseS7X4Q2swo5Y+jJ8+QBKYdBx75nO/Ytfo5x7j/EjOEg8FCAdnoVK3gbQ9oVuOdanYqvF4DfOPW9V4Q2D4vQZhYrKHbO5kieMzYxZnKlWp537HtPx77Fz+uc+w/1xf7eQO30Yt9I7e5F2jqGj4MCw7K4w6nfWYTPJzoicHtZ3Rq4vdWB2+uF5yH2Ug4vOPYd4wujlNskfAPDZwlXVeKXgdrpRazd3KknWlaAAsOyuN2x79AvvNcGbi+r0I+hZ2C4R4Q2vZ4Xycfz9XdIhDaPi9CmhPMW0hdSHu5eoBGorYWB2unFPsTJDX9VhDZHo8CwRG5z7DvkC29fYGbA9noR+jGMdSB6FgcEbm8AOCdwmxKX5wk+8wi/AeXzgdvrV2+N1O77I7Wb1a8CtvUAxc5czmMCcYK44yO0ORoFhiVyD82kTwcnBmzrnQHb6sULhA8MPWdsjiTse/Mvmm1KdaS+sA03ALwmYHtvIP0Frq6+Qfics/2AEwK32auQgWEDuC5ge1m9PnB7B5E+3/chUGBYFi8CC5z6PgjYO0A7A8DpAdrJYwGwIXCbnoexb0u4XM3dgK8HakvSedy5/1AzUxOA8wO1JfZ+/mbgNr9M3LN+swidZ+8RGJ5G2HI/7w3YVhYN4C5QYFgmHi/kljMCtHEiYQLMPK6J0OaKCG324t0B2tgGuIR4J9xIPJ5HZQK8A9g+QDtfwbdoch29H/hooLbeBbw9UFt5rQLuC9zmlaQvAzcDOClQW9uQfnl/Cc2VMgWG5XG1Y98fothOxInAuYHGkkeMx877wvw+ihVNnQZcjs0IS/V4n/s9Bfirgm2cAXwqwFjkpS4APl6wjUOBCwOMpagFhA/insInd/+zwNgA7ZxN+i/0d7f+oMCwPB4mbJ5FLyYA3wXG57z9PwJzww2nJ3dhj11o3jOGU7EP/zxLPAcAt5B+R5uEE+M13atPk+/s3AnA36Ml5JgGsCXlH5OvIP6J2CrVtJCDyumGSO3+OFK7ozkQOKtgGycDnwgwll7d2PqDAsNy+ZFj368C/i+9f9v5DGGWovO6OFK7Zbgwn4Qlm2fNW9kOuxjfRby6WpLGMvw2pLVMBn5O9pJWY7GL2j3A3+Cft9YPTsZml88jWzWDfbDrzOWUJ8VkQaR2f4DPqWJfJH9+4GnYNS31e6cBXNb6n4FGI1TpoGA8B+T9QbYXtivIcxxXAx8Glnf5vR2ArwHviT6izoawZOxYs3urCJNnVdTt2NLCfF56Istk4CgsJ/FUynvOcuzXtNfnRsz7dQv23HrbhOWqXoy9Fp/EgsCpWJH8/bBxvg2/clXdhH6eSnfhbFqKvW4WYyWPNmDP02zgTcQ7oSOvx7C6rbEez/n47IgfwmbNv0S2s5vnNH/31JiDGsXdWGoBUL7AcCq+35K9A0PweyEPtw77tnUp9oJplc6YjhW/PQH7ZrO1y+g2u5S4idMLgFdHbL9Xa4BFWILwFOBl2Ad93hSAlBQY9u48iuf5iemXwLBqzsVSFmI5Bd+VuN8B38eu64uxa+lYbAl/DjYbfyJ2zfdcwf08FpgC5QsMp+FbP64MgeFJwE+8B1ERrwOuj9j+N/BdJq8TBYa9OxX4j4jt9xMFhuV0BHGPhB2HpWXMiNhH1W3CZpT/uOFSOYblcxk2vS6ju594ScstnifSiFyPT46USAqPEDcoBAt6tAlqdD9jRBUOBYblswmr/SWj+wLxv7X/V+T2PZ0I/MJ7EDKqldgZsnV0Hf7188TXfybq59v4HnFadv808i8UGJbT9/CvY1Zm95CmFMFyLC+kbq7GCsDe7z0Q6cqzvmksQ1he2a+9ByJuhkhXQ3E1tlFSXupm2uwKV2BYTi+iQ+dH87eky/G5IlE/qbzI5mTvBz0HIpn81HsAEVyEzYQuw16P0n8uBx5N2N/X8T3mtIyGgE+2+wcFhuX17/idn1xml2C11VLxKJIa09fYfPzUA54DkUzuwnai18XTWI1DgEHSBgdVth7fjZmh/UN7lWguAAAGUUlEQVTi/p7DTiWRzS4C7mz3DwoMy6uBnYe5wXsgJbKG9LuEb8NqS9bBUqz4aksdl8nr6CLvAQT0Caw+aMtSr4FUzH8Cb6Eeu6Gvw2otpvY9YKFDv2W0ilECZQWG5bYY+Jz3IErkY/gcVfeS5NwKGgT+HHhh2N+tZMuLtJTT/2PL562qLualJxUpMMzmDiyoqXr5ogZ2WpZX339JtoLTddYAPoidJ92WAsPyOxe4ynsQJfA9bHndw3ewUwSq7Iu0/7asPMPyewZ7DVbZUuC/d/h76a5V1uWTbD5woIq+j6VHeHkQONOx/zL4RywlqyMFhuXXAE6nHGf3ermN9heVVNZhp1BU1ZXAlzv8mwLDavgK8Lz3IHJ6DngH7UuGLEk8lirayOayRSux2Z4qLimvBP6H9yCA72IlbPrRJWTItVRgWA1rsGPoPJZRvT0M/Cn+uZbnY8cbVc0i7BzlToWSlWdYDU8B/9t7EDkMYuepd9pA80jCsVTVvVhw2HIZcI7TWPJqLV+WJXXlTOpXcaKba4A/w96To1JgWB1LgDdSzeAkr6XYsXdlWDp5HstxrJJlWML6aMVdFRhWx9eoVu2/BvA+LJDp5BGqOfuVUrvTQT6HX2pNHudjJWrKYiPwLuBa74EkcgnwNrb8gtGRAsNqeQA4hv5YVr4XOI5yzZJeSnVyvR4FXkP3x08la6pjIzb7W4WNKIPAB4B/6/J76+ivL7t53N7m7xrY41uFclpXUo4l5JHWAW+lGo9hEV8HTqGHVTcFhtXzGHAsMN97IBH9FHgV8IT3QNo4g/KfofwgFlRnWaZbRjUCDTH3Uf4csxewC9FFGX9fy8mj6/R5sxE4FcuZK6ubsZm5rsuXTjZg4/sc5R1jXs9haRyfosf7psCwmp4G3oSdjlKnF/N6bJPJydiLuow2YMuzd3sPpIMrgaPIPtM6RH3qNPaL/8A+7MsYHD6KfXEdddfjCNqA0tka4Dej/Psg8CGsPmTZTpH5BfZZWfYvng1sc97rqc9RtFcB+5Mz3UCBYXUNAV8CDqceRTuvBg4G/tl7IBn8Hst9vM57IMNsBM7C8kh6PTBeO5Or5zzgI8Am74EM8wPgUOws816oZE1nd9J549hw51OuNKPvYEFhWb/gt3MDcADwf8iYi1dCT2CzyCdgq4u5KDCsvnuwD4T3Mfo3y7K6Fytl8WaqtRFiNTbms/H/ELkVOAL4KvlmkBUYVtOFwBuAx53HsQyb5T8N+9LUKy0ld9Yuv7CTO4CDsJql6+MMp6vVwF8AH8b/czGP57F8yP2w02ayBOVlsALbaT0bG3chCgzroYEVDt0HeCc2g1jGZaaWIWw32JuAQ+ht2alMNgFfwIIyjyOefoPlkBwN/KpAOwoMq+sGYB6WZ5Z69nAldorFPlhecF5aSu6s3Y7k0awD/g57Ti4i3WuigaU4zMNO6qm6JdjM2z7At7DXehktxFIJ5gDfJNCpLgONRqnih2n4HhQ+4Nh3aLOxHYynYG9W7/s2hFW8/yl2LNYy3+FEcTzwt8CfRO7nFuzD6oeEyTHdD9vUEFPs15/XB5n3+2q4OVgS/WnA+Ij9LMJSPr5PmPyx7cg309iL0M9TqtfbTIrt2t4Dm0l6P3Z9DW0I+Bm2WnFnhPbLYjw2O/8eLF1nsuNYFgM/wnb8R0kdKFtgOABsO8q/v0h1q/972hkLWl6NzdDNAyZE7vMF7AJyF/BL4Hrgych9lsVsrJDoKcCBFJ+Z34QtKV2OfQiH3izS6X3XwPeLmuQzHdtpeSrwSooHiYNYysoVWMmme0f/dSmhidhBAadgmyy2L9BWA9t8dwk2O1jHL/mj2Qqr+nA89v46GJgSqa8hbPbyNuAmbIUg+gx72QJDSWM8sC8wF9gT+1b6cuzDYrvmf7du/u4UNgeRG4G1zT8/C/wB+6b/DHYyw2NYIvlD2DeZOu2Yzmtr4EhsuXlPYDdgVywQm9D87wCWpL0ReyyfwN78i7HA+g42P+4ivZgMHIalG8zGXn+zsPf5yNffBmzJ7Ck2v4/vxS5KVdpEIKMbi00OHNH8797ALsAOWNDT+ux/FpuIWcnm18Od2IrFU2mHXGpjsdn6fbHHcib2Gb8T9j5rfdZPY8tJgtZ77lnsRJhnsMmTx7C824ewNJ/k773/D+QQJ/r9ePvuAAAAAElFTkSuQmCC') no-repeat;
+    width: 633px;
+    height: 192px;
+  }
+  #loader {
+    transform: scale(0.5, 0.5);
+  }
+}
+@media only screen and (max-device-width : 1280px) {
+  #lozengeBg {
+    width: 320px;
+    height: 141px;
+  }
+  #lozenge {
+    background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAM4AAACQCAYAAABAm1RDAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAC0tJREFUeJzt3X9sVeUdx/F3j7ellVYKll8KxG04MzNFMuPw1wbZFmVuRvzPH5mZ+ofBbQoYJGLiP6jEoOKIMWqIZluIf0mcU4gxKzpUNGZMNpEVXAwqBax4S4stbWn3x/cc7untvb0/eu597rn380pOeu6Pc++XpB+e5zk953nqRqgJU4EE0AJMAs4EJgMNwFn+a61APdAM1PmPCT0H0Ag0+fvB8fifcUbo+5r94wLBd2fS4r9eiCGgJ8trPf7r2d57Cjju7w8AJ/z9PqDf3+8FBv39JDASei7pf0Z36PhvgZOh7/6mwH9P7NRVeHA8oA2YDkzDfpmnpv1sxX5Rg5/BNoXifiklGkFAu7Fw9WLBSvr73VjAkmk/jwFdwFfAcNmrzpOr4NQBc4F5/nauv50DzCQVljb/vVJ7RrAABSE6AhwCvvS3g/72BQ4CVo7gfA+4DFgAnB/aGkv/1VID+oEDQAewH9gDfOA/VzKlCM4MYBnwK2AR1mqIlNvXwHvAa8DLwNEoPzzK4CwB/gBcx+iBsYhrp4DtwEbgzSg+MIrg/BR4FLh84h8lUnLvA2uAHRP5kIkEZzrwBHDrRAoQcWQLsIIiu3DFBufnwJ+A2cUdLlIRDgO3AW8UeqBXxJetxPqLCo3E3SzgdeC+Qg8spMWpAx4r5ktEYuAprOuWVyQKCc4GYFVRJYnEw5NYjyqnfLtqK1FopPqtIM8eVT4tzi+AbYy+iFGkWp0CfkmOEwa5gjMdu4RhVlRVicTAEeBixjlVnaurthGFRmrPTOxkQVbjtTg/Ad6Kth6RWPkZ8PdML4wXnF3Aj0tTj0gsfECWDGTrqi3JdoBIDbkMOzk2RrbgrChdLSKxck+mJzN11WZid9XplmMRm0NhHtAZfjJTi7MMhUYkkABuSH8yU3CuK30tIrHy6/Qn0rtqHnbLaWv6G0VqWDdwNnZVATC2xfkhCo1IuinAReEn0oNzZflqEYmVq8IP0oPzozIWIhIno7KRHpxLyliISJwsCD8InxxIYFOTTipvPSKxMIBNrTwIo1ucC1BoRLJpwDICjA7O98tfi0isZAzOBRneKCIppxsXBUckfz8IdsLB+a6DQkTi5DvBTjg455W/DpFYOS/YCU5H12NL2WkmG5HshrGlLAeCFmcuCo1ILh52b87prtq57moRiZU5kArOTIeFiMTJDEgFRysPiORnNqSCM8NhISJxMhPU4kRr2za48ELXVUhpzYJUcDTNbRSuvRY++gg2boRW3UhbpUYFRycHopJIwD33wP79sHy5PZZqoq5aSbW1wdNPWwt0zTWuq5HonAOpKwf60b04EzcyzkzcW7fC6tVw4ED56pFSGAQa6kbsEoJvXVdTFcYLDsDAAGzaBOvWQTJZnpqkFFo8YKrrKmpGQwOsWmXjnzvvBK+YRb+lArR6aB618mtrg+efhw8/hMWLXVcjhVNwnFq4ENrb4eWXYf5819VI/qaqq1YJli2DPXts7NPc7Loaya1VwakUTU2wdi10dMDtt2v8U9nUVas4s2fD5s02/rniCtfVSGZTPWySNak0CxfCzp2wZQvMm+e6GhntTA+biV0qUV0d3HQT7Nun8U9lmeIBZ7quQnIIxj+ffGJBqqtzXVGta/CwKwckDubMsa7bu+/CokWuq6llTR4w2XUVUqBFiyw8W7bYyQQpt8ke0OK6CilCMP7p6LBuXJM6DmVUr65a3DU324mDffs0/imfszyg0XUVEoF586zr9tZbdipbSqlBXbVqc/XV9sfTzZs1/imdJo+xyxlK3HmeXbbT0QEPPGC3M0iUmnVWrZo1N8PDD8PHH9uFpBKVeg9b+1Oq2fz5dutCe7vGP9Fo0cmBWrJ4sY1/nnnGbqaTYk3y0CQdtcXz4K677PbtVas0/ilOo04M1KrWVtiwwcY/11/vuprYUXBq3fz58Mormr63MI11I5BjTiPJW67poSrd0BA89xw89BB0dbmupqIpOFGKe3ACySQ8+CA8+6yFScZQcKJULcEJ7N1rJxC2b3ddScXRGEekCAqOjJVMwooVsGCBWpssElhXTdeii04OFCABHEcTdsgbb1grs3ev60ri4KS6arXuwAG48UZbw0ehyVe/LvCsVcmk3Tm6aZMtPyIFSQAnXRchZTQ8DC+8AGvWaBxTvP4Ethqb1IIdO2DlSti923UlcXfSw5Zmk2r26ac2jlmyRKGJRk8CLWNYvXp7Yf16eOIJ6OtzXU01GUgAp1xXIREbHoYXX7TrzTo7XVdTjU4kgF7XVUiE3nsP7r5bXbLS6vPQyYHqcPAg3HwzXHmlQlN6AwlAnd840zjGheMJoMd1FVKEkRF46SW4/374/HPX1dSaQY1x4mjXLruubNcu15XUqhMe6qrFR2cn3HKLrQ2q0Lj0rcY4cdDXZ2OY9ettTCOuDSaAbtdVSBbBOGbNGjtrJpWiW2OcSrV7N9x7L7z9tutKZKwTHpB0XYWEdHbCHXfApZcqNJUrmQCOua5CsHtiHn8cHnlE45jKl0ygFse9rVth9Wq7G1PiQMFxavduuz9mxw7XlUhhjnnAN66rqDldXbB8uY1jFJo4UotTVgMDdo//unV2z7/EVbLOn7S1H62TM3HjTYH76qvWLdM4Ju4G8VedBvjKZSVVbe9eWLrU1qBRaKpBF6SmwD3ssJDq1NVlN5RpGtlqcwhSC+cqOFEJppFdu1bjmOp0FFLBOeKwkOqxfbsti6EZMavZYUgFRzM6RGHpUtcVSOkdhtQYRy2OSH6OQCo4anFE8nMI1OKIFOoopILzhcNCROLkC7DFc8FOEvQDZ7irR6TiDQNNwEDQ4gwBX7qrRyQWDgEDMHrx3M+clCISH58FO+HgfFr+OkRi5X/BTjg4/3VQiEic7At2wsHpcFCISJycblzCwdmX4Y0iknK6cakL3XoVTMDeWP56RCreSaAFf+nPcIszBPzHRUUiMfAxofVyvbQXPypvLSKx8a/wg/TgfFjGQkTi5J/hB+nBeaeMhYjEyc7wg7q0eVk8bOKOaeWrR6TiJYGzsWvVgLEtzjBqdUTS7SQUGhgbHIBXy1OLSGy8nv5EelcNYAZ2z0F96esRqXhDwFzSZoLK1OIcBf5WjopEYmAbGaZPyxQcgD+WthaR2Hgy05OZumqBd4HLS1SMSBy8DyzK9EK2FgdgNTBOrkSq3upsL4wXnJ3An6OvRSQW/gJkXYR1vK4a2B999gDnRFqSSGXrBC7GX5kgk/FaHICvgVuBUxEWJVLJTgG/YZzQQO7gALQDK6OoSCQG7gPezPWmfIIDdnp6/YTKEal8jwEb83ljrjFOukeBNYXXI1LxNlDAmeRCgwPwO+yPQolcbxSJgSGse/ZUIQcVExyAJcCfgDnFHS5SEQ5hJ7/aCz0w3zFOunZgAfAi+iOpxM8I9h//AooIDRQfHIBjwG+BK4B/TOBzRMrpHeAq4DZynHIeT7FdtUyuBn4PXA9Miu5jRSZsELvi/0ki+k8+yuAEpgE3ANdhYZoe/VeI5NSFheQ14K/YlACRKUVwRn0+cD5wCXCRvx9sLaX9aqkRvdgMmwf8n//GpnLaTwnH36UOznhmAfOw6+DmAuf622yslWrzf+pO1No0iLUaX/lbJ7aG05fA59gZsYNkuMmsHFwGJ1+t2O3c0/xtqv9cKzDF31qBZn+b7L+nGVs9Sy2bGz3YKn892CwxvaEtCRz3f3YD3/jbMf/nEf+1ihWH4EShEQvRZKABC9sZWODqSYWs0d+vx/7A25J2PKHX8T/Hw7qkrVm+Myx4fzZBDZn0A33jHDuM/RKG9fnHhXX77x0h9cs5iP1Cpx/Tg/2BMHg9qKHXf67bf70bW6nsRJbvrDr/B9aWlvwbNMFRAAAAAElFTkSuQmCC') no-repeat right;
+    width: 422px;
+    height: 141px;
+  }
+  #wordmark {
+    background: transparent url('data:image/png;base64,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') no-repeat;
+    width: 422px;
+    height: 128px;
+  }
+  #loader {
+    transform: scale(0.33, 0.33);
+  }
+}
+</style>
+
 </head>
 
 <body>
-  <div id="splash">
-    <img src="h5vcc-embedded://you_tube_logo.png" class="hidden">
+<div id="background">
+<div id="lozengeContainer">
+  <div id="lozengeBg"></div>
+  <div id="lozenge"></div>
+</div>
+<div id="wordmarkContainer">
+  <div id="wordmark"></div>
+</div>
+<div id="spinnerContainer" class="active">
+  <div id="loader">
+    <div id="loadbefore" class="beforeafter"></div>
+    <div id="loadafter" class="beforeafter"></div>
   </div>
-  <div id="loading">
-    <div id="spinner">
-      <div class="dot" id="dot1"></div>
-      <div class="dot" id="dot2"></div>
-      <div class="dot" id="dot3"></div>
-      <div class="dot" id="dot4"></div>
-      <div class="dot" id="dot5"></div>
-      <div class="dot" id="dot6"></div>
-      <div class="dot" id="dot7"></div>
-      <div class="dot" id="dot8"></div>
-    </div>
-  </div>
-  <script type="text/javascript" src="h5vcc-embedded://splash_screen.js">
-  </script>
-</body>
+</div>
+</div>
+<script>
+  function backgroundTransitionEnd(e) {
+    if (e.target == e.currentTarget) {
+      window.close();
+    }
+  }
 
+  function expand() {
+    document.getElementById('lozengeContainer').className = 'move';
+    document.getElementById('wordmarkContainer').className = 'move';
+    document.getElementById('spinnerContainer').classList.remove("active");
+    document.getElementById('background').addEventListener('transitionend',
+        backgroundTransitionEnd);
+    document.getElementById('background').style.opacity = 0;
+    return '';
+  }
+
+  window.onbeforeunload = expand;
+</script>
+</body>
 </html>
diff --git a/src/cobalt/loader/image/image.h b/src/cobalt/loader/image/image.h
index 2b72010..740c9b5 100644
--- a/src/cobalt/loader/image/image.h
+++ b/src/cobalt/loader/image/image.h
@@ -140,10 +140,7 @@
       scoped_refptr<FrameProvider> frame_provider,
       const math::RectF& destination_rect,
       const math::Matrix3F& local_transform,
-      render_tree::ImageNode::Builder* image_node_builder,
-      base::TimeDelta time) {
-    UNREFERENCED_PARAMETER(time);
-
+      render_tree::ImageNode::Builder* image_node_builder) {
     image_node_builder->source = frame_provider->GetFrame();
     image_node_builder->destination_rect = destination_rect;
     image_node_builder->local_transform = local_transform;
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc
index 1183eea..49d52a3 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.cc
+++ b/src/cobalt/loader/image/image_decoder_starboard.cc
@@ -23,7 +23,7 @@
 #include "starboard/decode_target.h"
 #include "starboard/image.h"
 
-#if SB_TRUE && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
 
 namespace cobalt {
 namespace loader {
@@ -35,11 +35,7 @@
     : ImageDataDecoder(resource_provider),
       mime_type_(mime_type),
       format_(format),
-#if SB_TRUE
       provider_(resource_provider->GetSbDecodeTargetGraphicsContextProvider()),
-#else   // #if SB_TRUE
-      provider_(resource_provider->GetSbDecodeTargetProvider()),
-#endif  // #if SB_TRUE
       target_(kSbDecodeTargetInvalid) {
   TRACE_EVENT0("cobalt::loader::image",
                "ImageDecoderStarboard::ImageDecoderStarboard()");
@@ -75,6 +71,6 @@
 }  // namespace loader
 }  // namespace cobalt
 
-#endif  // SB_TRUE && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 
 #endif  // #if defined(STARBOARD)
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 6a07f01..cf037bf 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -184,10 +184,8 @@
         '<(input_directory)/cobalt_splash_screen.css',
         '<(input_directory)/cobalt_splash_screen.html',
         '<(input_directory)/equirectangular_40_40.msh',
-        '<(input_directory)/youtube_splash_screen.css',
         '<(input_directory)/youtube_splash_screen.html',
         '<(input_directory)/splash_screen.js',
-        '<(input_directory)/you_tube_logo.png',
       ],
       'actions': [
         {
diff --git a/src/cobalt/loader/unencoded_data_urls/lozenge_1080.png b/src/cobalt/loader/unencoded_data_urls/lozenge_1080.png
new file mode 100644
index 0000000..1e131c4
--- /dev/null
+++ b/src/cobalt/loader/unencoded_data_urls/lozenge_1080.png
Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/lozenge_2160.png b/src/cobalt/loader/unencoded_data_urls/lozenge_2160.png
new file mode 100644
index 0000000..49f2670
--- /dev/null
+++ b/src/cobalt/loader/unencoded_data_urls/lozenge_2160.png
Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/lozenge_720.png b/src/cobalt/loader/unencoded_data_urls/lozenge_720.png
new file mode 100644
index 0000000..1a10d3f
--- /dev/null
+++ b/src/cobalt/loader/unencoded_data_urls/lozenge_720.png
Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/wordmark_1080.png b/src/cobalt/loader/unencoded_data_urls/wordmark_1080.png
new file mode 100644
index 0000000..1c153f1
--- /dev/null
+++ b/src/cobalt/loader/unencoded_data_urls/wordmark_1080.png
Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/wordmark_2160.png b/src/cobalt/loader/unencoded_data_urls/wordmark_2160.png
new file mode 100644
index 0000000..3d106c5
--- /dev/null
+++ b/src/cobalt/loader/unencoded_data_urls/wordmark_2160.png
Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/wordmark_720.png b/src/cobalt/loader/unencoded_data_urls/wordmark_720.png
new file mode 100644
index 0000000..7fcb9f1
--- /dev/null
+++ b/src/cobalt/loader/unencoded_data_urls/wordmark_720.png
Binary files differ
diff --git a/src/cobalt/math/matrix3_f.cc b/src/cobalt/math/matrix3_f.cc
index 2f1f19d..929340e 100644
--- a/src/cobalt/math/matrix3_f.cc
+++ b/src/cobalt/math/matrix3_f.cc
@@ -84,6 +84,12 @@
   return matrix;
 }
 
+bool Matrix3F::IsZeros() const {
+  return data_[M00] == 0.0f && data_[M01] == 0.0f && data_[M02] == 0.0f &&
+         data_[M10] == 0.0f && data_[M11] == 0.0f && data_[M12] == 0.0f &&
+         data_[M20] == 0.0f && data_[M21] == 0.0f && data_[M22] == 0.0f;
+}
+
 bool Matrix3F::IsIdentity() const {
   return data_[M00] == 1.0f && data_[M01] == 0.0f && data_[M02] == 0.0f &&
          data_[M10] == 0.0f && data_[M11] == 1.0f && data_[M12] == 0.0f &&
diff --git a/src/cobalt/math/matrix3_f.h b/src/cobalt/math/matrix3_f.h
index d4560a6..c2e8c63 100644
--- a/src/cobalt/math/matrix3_f.h
+++ b/src/cobalt/math/matrix3_f.h
@@ -39,6 +39,7 @@
     data_[8] = m22;
   }
 
+  bool IsZeros() const;
   bool IsIdentity() const;
 
   bool IsEqual(const Matrix3F& rhs) const;
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index beee2aa..b3b5cb6 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -194,7 +194,7 @@
  public:
   virtual void NetworkStateChanged() = 0;
   virtual void ReadyStateChanged() = 0;
-  virtual void TimeChanged(bool eos_played) = 0;
+  virtual void TimeChanged() = 0;
   virtual void DurationChanged() = 0;
   virtual void OutputModeChanged() = 0;
   virtual void PlaybackStateChanged() = 0;
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index c8cf80a..bd0d14b 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -590,8 +590,7 @@
   // Update our paused time.
   if (state_.paused) state_.paused_time = pipeline_->GetMediaTime();
 
-  const bool eos_played = false;
-  GetClient()->TimeChanged(eos_played);
+  GetClient()->TimeChanged();
 }
 
 void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) {
@@ -600,9 +599,7 @@
     OnPipelineError(status);
     return;
   }
-
-  const bool eos_played = true;
-  GetClient()->TimeChanged(eos_played);
+  GetClient()->TimeChanged();
 }
 
 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index 1909a5c..58618a2 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -35,7 +35,7 @@
   // WebMediaPlayerClient methods
   void NetworkStateChanged() OVERRIDE {}
   void ReadyStateChanged() OVERRIDE {}
-  void TimeChanged(bool) OVERRIDE {}
+  void TimeChanged() OVERRIDE {}
   void DurationChanged() OVERRIDE {}
   void OutputModeChanged() OVERRIDE {}
   void PlaybackStateChanged() OVERRIDE {}
@@ -48,8 +48,9 @@
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
   std::string SourceURL() const OVERRIDE { return ""; }
 #if defined(COBALT_MEDIA_SOURCE_2016)
-  void EncryptedMediaInitDataEncountered(EmeInitDataType, const unsigned char*,
-                                         unsigned) OVERRIDE {}
+  void EncryptedMediaInitDataEncountered(EmeInitDataType init_data_type,
+                                         const unsigned char* init_data,
+                                         unsigned init_data_length) OVERRIDE {}
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 };
 
diff --git a/src/cobalt/render_tree/animations/animate_node.cc b/src/cobalt/render_tree/animations/animate_node.cc
index 58c11b4..46435b5 100644
--- a/src/cobalt/render_tree/animations/animate_node.cc
+++ b/src/cobalt/render_tree/animations/animate_node.cc
@@ -66,7 +66,8 @@
                       TraverseList* traverse_list)
       : animation_map_(animation_map),
         traverse_list_(traverse_list),
-        expiry_(-base::TimeDelta::Max()) {}
+        expiry_(-base::TimeDelta::Max()),
+        depends_on_time_expiry_(-base::TimeDelta::Max()) {}
 
   void Visit(animations::AnimateNode* animate) OVERRIDE;
   void Visit(CompositionNode* composition) OVERRIDE { VisitNode(composition); }
@@ -117,6 +118,10 @@
   // The time after which all animations will have completed and be constant.
   base::TimeDelta expiry_;
 
+  // Similar to |expiry_| but accumulated only for animations whose callback
+  // depends on the time parameter.
+  base::TimeDelta depends_on_time_expiry_;
+
   friend class AnimateNode;
 };
 
@@ -146,6 +151,9 @@
 
   // Update our expiry in accordance with the sub-AnimateNode's expiry.
   expiry_ = std::max(expiry_, animate->expiry());
+
+  depends_on_time_expiry_ =
+      std::max(depends_on_time_expiry_, animate->depends_on_time_expiry());
 }
 
 template <typename T>
@@ -208,8 +216,10 @@
 void AnimateNode::TraverseListBuilder::AddToTraverseList(
     Node* node, AnimateNode::Builder::InternalMap::const_iterator found) {
   if (found != animation_map_.end()) {
-    traverse_list_->push_back(TraverseListEntry(node, found->second));
+    traverse_list_->push_back(TraverseListEntry(node, found->second, false));
     expiry_ = std::max(expiry_, found->second->GetExpiry());
+    depends_on_time_expiry_ = std::max(depends_on_time_expiry_,
+                                       found->second->GetDependsOnTimeExpiry());
   } else {
     traverse_list_->push_back(TraverseListEntry(node));
   }
@@ -304,7 +314,9 @@
   TraverseListEntry current_entry = AdvanceIterator(node);
 
   DCHECK(current_entry.animations);
-  ProcessAnimatedNodeBounds(current_entry, node);
+  if (current_entry.did_animate_previously) {
+    ProcessAnimatedNodeBounds(current_entry, node);
+  }
 }
 
 template <typename T>
@@ -336,7 +348,7 @@
   }
   transform_ = old_transform;
 
-  if (current_entry.animations) {
+  if (current_entry.did_animate_previously) {
     ProcessAnimatedNodeBounds(current_entry, node);
   }
 }
@@ -376,7 +388,8 @@
 // tree.
 class AnimateNode::ApplyVisitor : public NodeVisitor {
  public:
-  ApplyVisitor(const TraverseList& traverse_list, base::TimeDelta time_offset);
+  ApplyVisitor(const TraverseList& traverse_list, base::TimeDelta time_offset,
+               const base::optional<base::TimeDelta>& snapshot_time);
 
   void Visit(animations::AnimateNode* /* animate */) OVERRIDE {
     // An invariant of AnimateNodes is that they should never contain descendant
@@ -405,7 +418,9 @@
   // As we compute the animated nodes, we create a new traverse list that leads
   // to the newly created animated nodes.  This can be used afterwards to
   // calculate the bounding boxes around the active animated nodes.
-  TraverseList* animated_traverse_list() { return &animated_traverse_list_; }
+  const TraverseList& animated_traverse_list() const {
+    return animated_traverse_list_;
+  }
 
  private:
   template <typename T>
@@ -415,8 +430,8 @@
   typename base::enable_if<ChildIterator<T>::has_children>::type VisitNode(
       T* node);
   template <typename T>
-  scoped_refptr<Node> ApplyAnimations(const TraverseListEntry& entry,
-                                      typename T::Builder* builder);
+  scoped_refptr<T> ApplyAnimations(const TraverseListEntry& entry,
+                                   typename T::Builder* builder);
   TraverseListEntry AdvanceIterator(Node* node);
 
   // The time offset to be passed in to individual animations.
@@ -435,11 +450,18 @@
 
   // An iterator pointing to the next valid render tree node to visit.
   TraverseList::const_iterator iterator_;
+
+  // Time at which the existing source render tree was created/last animated
+  // at.
+  base::optional<base::TimeDelta> snapshot_time_;
 };
 
-AnimateNode::ApplyVisitor::ApplyVisitor(const TraverseList& traverse_list,
-                                        base::TimeDelta time_offset)
-    : time_offset_(time_offset), traverse_list_(traverse_list) {
+AnimateNode::ApplyVisitor::ApplyVisitor(
+    const TraverseList& traverse_list, base::TimeDelta time_offset,
+    const base::optional<base::TimeDelta>& snapshot_time)
+    : time_offset_(time_offset),
+      traverse_list_(traverse_list),
+      snapshot_time_(snapshot_time) {
   animated_traverse_list_.reserve(traverse_list.size());
   iterator_ = traverse_list_.begin();
 }
@@ -452,9 +474,18 @@
   // have animations.
   DCHECK(current_entry.animations);
   typename T::Builder builder(node->data());
-  animated_ = ApplyAnimations<T>(current_entry, &builder);
+  scoped_refptr<T> animated = ApplyAnimations<T>(current_entry, &builder);
+  // If nothing ends up getting animated, then just re-use the existing node.
+  bool did_animate = false;
+  if (animated->data() == node->data()) {
+    animated_ = node;
+  } else {
+    animated_ = animated.get();
+    did_animate = true;
+  }
+
   animated_traverse_list_.push_back(
-      TraverseListEntry(animated_, current_entry.animations));
+      TraverseListEntry(animated_, current_entry.animations, did_animate));
 }
 
 template <typename T>
@@ -464,7 +495,7 @@
 
   size_t animated_traverse_list_index = animated_traverse_list_.size();
   animated_traverse_list_.push_back(
-      TraverseListEntry(NULL, current_entry.animations));
+      TraverseListEntry(NULL, current_entry.animations, false));
 
   // Traverse the child nodes, but only the ones that are on the
   // |traverse_list_|.  In particular, the next node we are allowed to visit
@@ -482,41 +513,60 @@
       // If one of our children is next up on the path to animation, traverse
       // into it.
       child->Accept(this);
-      // Traversing into the child means that it was animated, and so replaced
-      // by an animated node while it was visited.  Thus, replace it in the
-      // current node's child list with its animated version.
-      child_iterator.ReplaceCurrent(animated_);
-      children_modified = true;
+      if (animated_ != child) {
+        // Traversing into the child and seeing |animated_| emerge from the
+        // traversal equal to something other than |child| means that the child
+        // was animated, and so replaced by an animated node while it was
+        // visited.  Thus, replace it in the current node's child list with its
+        // animated version.
+        child_iterator.ReplaceCurrent(animated_);
+        children_modified = true;
+      }
     }
 
     child_iterator.Next();
   }
 
+  base::optional<typename T::Builder> builder;
+  if (children_modified) {
+    // Reuse the modified Builder object from child traversal if one of
+    // our children was animated.
+    builder.emplace(child_iterator.TakeReplacedChildrenBuilder());
+  }
+
+  bool did_animate = false;
   if (current_entry.animations) {
-    base::optional<typename T::Builder> builder;
-    if (children_modified) {
-      // Reuse the modified Builder object from child traversal if one of
-      // our children was animated.
-      builder.emplace(child_iterator.TakeReplacedChildrenBuilder());
-    } else {
+    if (!builder) {
       // Create a fresh copy of the Builder object for this animated node, to
       // be passed into the animations.
       builder.emplace(node->data());
     }
-    animated_ = ApplyAnimations<T>(current_entry, &(*builder));
+    typename T::Builder original_builder(*builder);
+    scoped_refptr<T> animated = ApplyAnimations<T>(current_entry, &(*builder));
+    if (!(original_builder == *builder)) {
+      did_animate = true;
+    }
+    // If the data didn't actually change, then no animation took place and
+    // so we should note this by not modifying the original render tree node.
+    animated_ = animated->data() == node->data() ? node : animated.get();
   } else {
-    // If there were no animations targeting this node directly, then its
-    // children must have been modified since otherwise it wouldn't be in
-    // the traverse list.
-    DCHECK(children_modified);
-    animated_ = new T(child_iterator.TakeReplacedChildrenBuilder());
+    // If there were no animations targeting this node directly, it may still
+    // need to be animated if its children are animated, which will be the
+    // case if |builder| is populated.
+    if (builder) {
+      animated_ = new T(std::move(*builder));
+    } else {
+      animated_ = node;
+    }
   }
 
   animated_traverse_list_[animated_traverse_list_index].node = animated_;
+  animated_traverse_list_[animated_traverse_list_index].did_animate_previously =
+      did_animate;
 }
 
 template <typename T>
-scoped_refptr<Node> AnimateNode::ApplyVisitor::ApplyAnimations(
+scoped_refptr<T> AnimateNode::ApplyVisitor::ApplyAnimations(
     const TraverseListEntry& entry, typename T::Builder* builder) {
   TRACE_EVENT0("cobalt::renderer",
                "AnimateNode::ApplyVisitor::ApplyAnimations()");
@@ -525,11 +575,16 @@
       base::polymorphic_downcast<const AnimationList<T>*>(
           entry.animations.get());
 
-  // Iterate through each animation applying them one at a time.
-  for (typename AnimationList<T>::InternalList::const_iterator iter =
-           typed_node_animations->data().animations.begin();
-       iter != typed_node_animations->data().animations.end(); ++iter) {
-    iter->Run(builder, time_offset_);
+  // Only execute the animation updates on nodes that have not expired.
+  if (!snapshot_time_ ||
+      typed_node_animations->data().expiry >= *snapshot_time_) {
+    TRACE_EVENT0("cobalt::renderer", "Running animation callbacks");
+    // Iterate through each animation applying them one at a time.
+    for (typename AnimationList<T>::InternalList::const_iterator iter =
+             typed_node_animations->data().animations.begin();
+         iter != typed_node_animations->data().animations.end(); ++iter) {
+      iter->Run(builder, time_offset_);
+    }
   }
 
   return new T(*builder);
@@ -559,9 +614,9 @@
 class AnimateNode::RefCountedTraversalList
     : public base::RefCounted<RefCountedTraversalList> {
  public:
-  explicit RefCountedTraversalList(TraverseList* to_swap_in) {
-    traverse_list_.swap(*to_swap_in);
-  }
+  explicit RefCountedTraversalList(const TraverseList& traverse_list)
+      : traverse_list_(traverse_list) {}
+
   const TraverseList& traverse_list() const { return traverse_list_; }
 
  private:
@@ -594,24 +649,41 @@
 
 AnimateNode::AnimateResults AnimateNode::Apply(base::TimeDelta time_offset) {
   TRACE_EVENT0("cobalt::renderer", "AnimateNode::Apply()");
+  if (snapshot_time_) {
+    // Assume we are always animating forward.
+    DCHECK_LE(*snapshot_time_, time_offset);
+  }
+
   AnimateResults results;
   if (traverse_list_.empty()) {
-    results.animated = source_;
+    results.animated = this;
     // There are no animations, so there is no bounding rectangle, so setup the
     // bounding box function to trivially return an empty rectangle.
     results.get_animation_bounds_since =
         base::Bind(&ReturnTrivialEmptyRectBound);
   } else {
-    ApplyVisitor apply_visitor(traverse_list_, time_offset);
+    ApplyVisitor apply_visitor(traverse_list_, time_offset, snapshot_time_);
     source_->Accept(&apply_visitor);
-    results.animated = apply_visitor.animated();
 
-    // Setup a function for returning
+    // Setup a function for returning the bounds on the regions modified by
+    // animations given a specific starting point in time ("since").  This
+    // can be used by rasterizers to determine which regions need to be
+    // re-drawn or not.
     results.get_animation_bounds_since = base::Bind(
         &GetAnimationBoundsSince,
         scoped_refptr<RefCountedTraversalList>(new RefCountedTraversalList(
             apply_visitor.animated_traverse_list())),
-        time_offset, results.animated);
+        time_offset, apply_visitor.animated());
+
+    if (apply_visitor.animated() == source()) {
+      // If no animations were actually applied, indicate this by returning
+      // this exact node as the animated node.
+      results.animated = this;
+    } else {
+      results.animated = new AnimateNode(apply_visitor.animated_traverse_list(),
+                                         apply_visitor.animated(), expiry_,
+                                         depends_on_time_expiry_, time_offset);
+    }
   }
   return results;
 }
@@ -638,6 +710,7 @@
   }
 
   expiry_ = traverse_list_builder.expiry_;
+  depends_on_time_expiry_ = traverse_list_builder.depends_on_time_expiry_;
 }
 
 }  // namespace animations
diff --git a/src/cobalt/render_tree/animations/animate_node.h b/src/cobalt/render_tree/animations/animate_node.h
index 70ed9ba..c71e620 100644
--- a/src/cobalt/render_tree/animations/animate_node.h
+++ b/src/cobalt/render_tree/animations/animate_node.h
@@ -20,6 +20,7 @@
 
 #include "base/containers/small_map.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/animations/animation_list.h"
 #include "cobalt/render_tree/movable.h"
@@ -80,12 +81,30 @@
     }
 
     template <typename T>
+    void Add(
+        const scoped_refptr<T>& target_node,
+        const typename Animation<T>::TimeIndependentFunction& single_animation,
+        base::TimeDelta expiry) {
+      AddInternal(target_node,
+                  scoped_refptr<AnimationListBase>(
+                      new AnimationList<T>(single_animation, expiry)));
+    }
+
+    template <typename T>
     void Add(const scoped_refptr<T>& target_node,
              const typename Animation<T>::Function& single_animation) {
       AddInternal(target_node,
                   scoped_refptr<AnimationListBase>(new AnimationList<T>(
                       single_animation, base::TimeDelta::Max())));
     }
+    template <typename T>
+    void Add(const scoped_refptr<T>& target_node,
+             const typename Animation<T>::TimeIndependentFunction&
+                 single_animation) {
+      AddInternal(target_node,
+                  scoped_refptr<AnimationListBase>(new AnimationList<T>(
+                      single_animation, base::TimeDelta::Max())));
+    }
 
     // Merge all mappings from another AnimateNode::Builder into this one.
     // There cannot be any keys that are in both the merge target and source.
@@ -140,8 +159,11 @@
 
   struct AnimateResults {
     // The animated render tree, which is guaranteed to not contain any
-    // AnimateNodes.
-    scoped_refptr<Node> animated;
+    // AnimateNodes.  Note that it is returned as an AnimateNode...  The actual
+    // animated render tree can be obtained via |animated->source()|, the
+    // AnimateNode however allows one to animate the animated node so that
+    // it can be checked if anything has actually been animated or not.
+    scoped_refptr<AnimateNode> animated;
 
     // Can be called in order to return a bounding rectangle around all
     // nodes that are actively animated.  The parameter specifies a "since"
@@ -152,6 +174,9 @@
     base::Callback<math::RectF(base::TimeDelta)> get_animation_bounds_since;
   };
   // Apply the animations to the sub render tree with the given |time_offset|.
+  // Note that if |animated| in the results is equivalent to |this| on which
+  // Apply() is called, then no animations have actually taken place, and a
+  // re-render can be avoided.
   AnimateResults Apply(base::TimeDelta time_offset);
 
   // Returns the sub-tree for which the animations apply to.
@@ -163,6 +188,12 @@
   // all x, y >= expiry().
   const base::TimeDelta& expiry() const { return expiry_; }
 
+  // Similar to |expiry()|, but returns the expiration for all animations whose
+  // callback function actually depends on the time parameter passed into them.
+  const base::TimeDelta& depends_on_time_expiry() const {
+    return depends_on_time_expiry_;
+  }
+
  private:
   // The compiled node animation list is a sequence of nodes that are either
   // animated themselves, or on the path to an animated node.  Only nodes in
@@ -170,12 +201,19 @@
   // list, complete with animations to be applied to the given node, if any.
   struct TraverseListEntry {
     TraverseListEntry(Node* node,
-                      const scoped_refptr<AnimationListBase>& animations)
-        : node(node), animations(animations) {}
-    explicit TraverseListEntry(Node* node) : node(node) {}
+                      const scoped_refptr<AnimationListBase>& animations,
+                      bool did_animate_previously)
+        : node(node),
+          animations(animations),
+          did_animate_previously(did_animate_previously) {}
+    explicit TraverseListEntry(Node* node)
+        : node(node), did_animate_previously(false) {}
 
     Node* node;
     scoped_refptr<AnimationListBase> animations;
+    // Used when checking animation bounds to see which nodes were actually
+    // animated and whose bounds we need to accumulate.
+    bool did_animate_previously;
   };
   typedef std::vector<TraverseListEntry> TraverseList;
 
@@ -192,6 +230,18 @@
   // rectangle for all active animations.
   class BoundsVisitor;
 
+  // This private constructor is used by AnimateNode::Apply() to create a new
+  // AnimateNode that matches the resulting animated render tree.
+  AnimateNode(const TraverseList& traverse_list, scoped_refptr<Node> source,
+              const base::TimeDelta& expiry,
+              const base::TimeDelta& depends_on_time_expiry,
+              const base::TimeDelta& snapshot_time)
+      : traverse_list_(traverse_list),
+        source_(source),
+        expiry_(expiry),
+        depends_on_time_expiry_(depends_on_time_expiry),
+        snapshot_time_(snapshot_time) {}
+
   void CommonInit(const Builder::InternalMap& node_animation_map,
                   const scoped_refptr<Node>& source);
 
@@ -205,6 +255,12 @@
   TraverseList traverse_list_;
   scoped_refptr<Node> source_;
   base::TimeDelta expiry_;
+  base::TimeDelta depends_on_time_expiry_;
+  // Time when the |source_| render tree was last animated, if known.  This
+  // will get set when Apply() is called to produce a new AnimateNode that can
+  // then be Apply()d again.  It can be used during the second apply to check
+  // if some animations have expired.
+  base::optional<base::TimeDelta> snapshot_time_;
 };
 
 }  // namespace animations
diff --git a/src/cobalt/render_tree/animations/animate_node_test.cc b/src/cobalt/render_tree/animations/animate_node_test.cc
index 7f539b9..b19733e 100644
--- a/src/cobalt/render_tree/animations/animate_node_test.cc
+++ b/src/cobalt/render_tree/animations/animate_node_test.cc
@@ -81,10 +81,10 @@
   scoped_refptr<AnimateNode> with_animations =
       CreateSingleAnimation(text_node, base::Bind(&AnimateText));
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   TextNode* animated_text_node =
-      dynamic_cast<TextNode*>(animated_render_tree.get());
+      dynamic_cast<TextNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_text_node);
   EXPECT_EQ(ColorRGBA(.5f, .5f, .5f), animated_text_node->data().color);
 }
@@ -101,10 +101,10 @@
   scoped_refptr<AnimateNode> with_animations =
       CreateSingleAnimation(image_node, base::Bind(&AnimateImage));
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   ImageNode* animated_image_node =
-      dynamic_cast<ImageNode*>(animated_render_tree.get());
+      dynamic_cast<ImageNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
   EXPECT_EQ(RectF(2.0f, 2.0f), animated_image_node->data().destination_rect);
@@ -122,10 +122,10 @@
   scoped_refptr<AnimateNode> with_animations =
       CreateSingleAnimation(rect_node, base::Bind(&AnimateRect));
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
   RectNode* animated_rect_node =
-      dynamic_cast<RectNode*>(animated_render_tree.get());
+      dynamic_cast<RectNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_rect_node);
   EXPECT_TRUE(animated_rect_node);
   EXPECT_EQ(RectF(2.0f, 2.0f), animated_rect_node->data().rect);
@@ -147,26 +147,29 @@
   scoped_refptr<AnimateNode> with_animations =
       CreateSingleAnimation(image_node, base::Bind(&AnimateImageAddWithTime));
 
-  scoped_refptr<Node> animated_render_tree;
+  scoped_refptr<AnimateNode> animated_render_tree;
   ImageNode* animated_image_node;
 
   animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
-  animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
+  animated_image_node =
+      dynamic_cast<ImageNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
   EXPECT_FLOAT_EQ(3.0f, animated_image_node->data().destination_rect.width());
 
   animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(2)).animated;
-  animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
+  animated_image_node =
+      dynamic_cast<ImageNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
   EXPECT_FLOAT_EQ(5.0f, animated_image_node->data().destination_rect.width());
 
   animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(4)).animated;
-  animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
+  animated_image_node =
+      dynamic_cast<ImageNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_image_node);
   EXPECT_TRUE(animated_image_node);
   EXPECT_FLOAT_EQ(9.0f, animated_image_node->data().destination_rect.width());
@@ -202,11 +205,11 @@
   scoped_refptr<AnimateNode> with_animations(
       new AnimateNode(animations_builder, image_node));
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(3)).animated;
 
   ImageNode* animated_image_node =
-      dynamic_cast<ImageNode*>(animated_render_tree.get());
+      dynamic_cast<ImageNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_image_node);
 
   EXPECT_FLOAT_EQ(21.0f, animated_image_node->data().destination_rect.width());
@@ -228,11 +231,11 @@
   scoped_refptr<AnimateNode> with_animations =
       CreateSingleAnimation(transform_node, base::Bind(&AnimateTransform));
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   MatrixTransformNode* animated_transform_node =
-      dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
+      dynamic_cast<MatrixTransformNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_transform_node);
 
   EXPECT_EQ(math::TranslateMatrix(2.0f, 2.0f),
@@ -260,11 +263,11 @@
   scoped_refptr<AnimateNode> with_animations =
       new AnimateNode(animation_builder, composition_node);
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   CompositionNode* animated_composition_node =
-      dynamic_cast<CompositionNode*>(animated_render_tree.get());
+      dynamic_cast<CompositionNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_composition_node);
 
   ImageNode* animated_image_node_a = dynamic_cast<ImageNode*>(
@@ -291,11 +294,11 @@
   scoped_refptr<AnimateNode> with_animations =
       CreateSingleAnimation(transform_node, base::Bind(&AnimateTransform));
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   MatrixTransformNode* animated_transform_node =
-      dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
+      dynamic_cast<MatrixTransformNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_transform_node);
 
   // Check that the matrix transform node is animated.
@@ -328,11 +331,11 @@
   scoped_refptr<AnimateNode> with_animations =
       new AnimateNode(transform_node_b);
 
-  scoped_refptr<Node> animated_render_tree =
+  scoped_refptr<AnimateNode> animated_render_tree =
       with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
 
   MatrixTransformNode* animated_transform_node =
-      dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
+      dynamic_cast<MatrixTransformNode*>(animated_render_tree->source().get());
   EXPECT_TRUE(animated_transform_node);
 
   // Check that the image node is animated.
diff --git a/src/cobalt/render_tree/animations/animation_list.h b/src/cobalt/render_tree/animations/animation_list.h
index 78f1e65..b51144d 100644
--- a/src/cobalt/render_tree/animations/animation_list.h
+++ b/src/cobalt/render_tree/animations/animation_list.h
@@ -17,6 +17,7 @@
 
 #include <list>
 
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/time.h"
@@ -68,6 +69,16 @@
   // and ultimately add that to a AnimateNode::Builder object so that it can be
   // mapped to a specific TextNode that it should be applied to.
   typedef base::Callback<void(typename T::Builder*, base::TimeDelta)> Function;
+  typedef base::Callback<void(typename T::Builder*)> TimeIndependentFunction;
+
+  // Helper function to convert from time independent function to a time
+  // dependent function.
+  static void CallTimeIndependentFunction(
+      const TimeIndependentFunction& time_independent_function,
+      typename T::Builder* builder, base::TimeDelta time) {
+    UNREFERENCED_PARAMETER(time);
+    time_independent_function.Run(builder);
+  }
 };
 
 // The AnimationListBase is used so that we can acquire a non-template handle
@@ -77,6 +88,7 @@
 class AnimationListBase : public base::RefCountedThreadSafe<AnimationListBase> {
  public:
   virtual base::TimeDelta GetExpiry() const = 0;
+  virtual base::TimeDelta GetDependsOnTimeExpiry() const = 0;
 
  protected:
   virtual ~AnimationListBase() {}
@@ -100,18 +112,36 @@
   struct Builder {
     DECLARE_AS_MOVABLE(Builder);
 
-    Builder() : expiry(base::TimeDelta::Max()) {}
+    Builder() : expiry(base::TimeDelta::Max()),
+                depends_on_time_expiry(base::TimeDelta::Max()) {}
     explicit Builder(Moved moved) { animations.swap(moved->animations); }
     explicit Builder(const typename Animation<T>::Function& single_animation,
                      base::TimeDelta expiry)
-        : expiry(expiry) {
+        : expiry(expiry), depends_on_time_expiry(expiry) {
       animations.push_back(single_animation);
     }
+    explicit Builder(
+        const typename Animation<T>::TimeIndependentFunction& single_animation,
+        base::TimeDelta expiry)
+        : expiry(expiry),
+          // Since this animation is time independent, we mark its
+          // time-dependent expiration to be already expired.
+          depends_on_time_expiry(-base::TimeDelta::Max()) {
+      // Transform from a time independent function to a time dependent
+      // function, since we store all functions as time dependent functions.
+      animations.push_back(base::Bind(Animation<T>::CallTimeIndependentFunction,
+                                      single_animation));
+    }
 
     InternalList animations;
     // When do the animations expire?  base::TimeDelta::Max() implies that they
     // never expire.
     base::TimeDelta expiry;
+
+    // Similar to |expiry|, but only set for animations that depend on the
+    // time parameter passed into their animation callback functions.
+    // Optimizations can be performed for animations that don't depend on this.
+    base::TimeDelta depends_on_time_expiry;
   };
 
   explicit AnimationList(typename Builder::Moved builder) : data_(builder) {}
@@ -122,10 +152,17 @@
       const typename Animation<T>::Function& single_animation,
       base::TimeDelta expiry)
       : data_(single_animation, expiry) {}
+  explicit AnimationList(
+      const typename Animation<T>::TimeIndependentFunction& single_animation,
+      base::TimeDelta expiry)
+      : data_(single_animation, expiry) {}
 
   const Builder& data() const { return data_; }
 
   base::TimeDelta GetExpiry() const OVERRIDE { return data_.expiry; }
+  base::TimeDelta GetDependsOnTimeExpiry() const OVERRIDE {
+    return data_.depends_on_time_expiry;
+  }
 
  private:
   ~AnimationList() OVERRIDE {}
diff --git a/src/cobalt/render_tree/blur_filter.h b/src/cobalt/render_tree/blur_filter.h
index 5f6fbe7..cbe95a0 100644
--- a/src/cobalt/render_tree/blur_filter.h
+++ b/src/cobalt/render_tree/blur_filter.h
@@ -29,6 +29,10 @@
   // |blur_sigma| must be non-negative.
   explicit BlurFilter(float blur_sigma) { set_blur_sigma(blur_sigma); }
 
+  bool operator==(const BlurFilter& other) const {
+    return blur_sigma_ == other.blur_sigma_;
+  }
+
   void set_blur_sigma(float blur_sigma) {
     DCHECK_LE(0.0f, blur_sigma);
     blur_sigma_ = blur_sigma;
diff --git a/src/cobalt/render_tree/border.h b/src/cobalt/render_tree/border.h
index 761c99f..9b72706 100644
--- a/src/cobalt/render_tree/border.h
+++ b/src/cobalt/render_tree/border.h
@@ -53,6 +53,11 @@
   Border(const BorderSide& left, const BorderSide& right, const BorderSide& top,
          const BorderSide& bottom);
 
+  bool operator==(const Border& other) const {
+    return left == other.left && right == other.right && top == other.top &&
+           bottom == other.bottom;
+  }
+
   BorderSide left;
   BorderSide right;
   BorderSide top;
diff --git a/src/cobalt/render_tree/brush.h b/src/cobalt/render_tree/brush.h
index b5be829..841ca6c 100644
--- a/src/cobalt/render_tree/brush.h
+++ b/src/cobalt/render_tree/brush.h
@@ -47,6 +47,10 @@
  public:
   explicit SolidColorBrush(const ColorRGBA& color) : color_(color) {}
 
+  bool operator==(const SolidColorBrush& other) const {
+    return color_ == other.color_;
+  }
+
   // A type-safe branching.
   void Accept(BrushVisitor* visitor) const OVERRIDE;
 
@@ -70,6 +74,10 @@
   ColorStop(float position, const ColorRGBA& color)
       : position(position), color(color) {}
 
+  bool operator==(const ColorStop& other) const {
+    return position == other.position && color == other.color;
+  }
+
   float position;
   ColorRGBA color;
 };
@@ -134,12 +142,20 @@
                             source_color, dest_color)
       {}
 
+  bool operator==(const LinearGradientBrush& other) const {
+    return data_ == other.data_;
+  }
+
   struct Data {
     Data();
     Data(const math::PointF& source, const math::PointF& dest,
          const ColorStopList& color_stops);
     Data(const math::PointF& source, const math::PointF& dest);
 
+    bool operator==(const Data& other) const {
+      return source_ == other.source_ && dest_ == other.dest_ &&
+             color_stops_ == other.color_stops_;
+    }
     math::PointF source_;
     math::PointF dest_;
 
@@ -203,6 +219,11 @@
                       float radius_y, const ColorRGBA& source_color,
                       const ColorRGBA& dest_color);
 
+  bool operator==(const RadialGradientBrush& other) const {
+    return center_ == other.center_ && radius_x_ == other.radius_x_ &&
+           radius_y_ == other.radius_y_ && color_stops_ == other.color_stops_;
+  }
+
   // A type-safe branching.
   void Accept(BrushVisitor* visitor) const OVERRIDE;
 
@@ -228,6 +249,36 @@
 
 scoped_ptr<Brush> CloneBrush(const Brush* brush);
 
+class EqualsBrushVisitor : public BrushVisitor {
+ public:
+  explicit EqualsBrushVisitor(Brush* brush_a)
+      : brush_a_(brush_a), result_(false) {}
+
+  bool result() const { return result_; }
+
+  void Visit(const SolidColorBrush* solid_color_brush) OVERRIDE {
+    result_ =
+        brush_a_->GetTypeId() == base::GetTypeId<SolidColorBrush>() &&
+        *static_cast<const SolidColorBrush*>(brush_a_) == *solid_color_brush;
+  }
+
+  void Visit(const LinearGradientBrush* linear_gradient_brush) OVERRIDE {
+    result_ = brush_a_->GetTypeId() == base::GetTypeId<LinearGradientBrush>() &&
+              *static_cast<const LinearGradientBrush*>(brush_a_) ==
+                  *linear_gradient_brush;
+  }
+
+  void Visit(const RadialGradientBrush* radial_gradient_brush) OVERRIDE {
+    result_ = brush_a_->GetTypeId() == base::GetTypeId<RadialGradientBrush>() &&
+              *static_cast<const RadialGradientBrush*>(brush_a_) ==
+                  *radial_gradient_brush;
+  }
+
+ private:
+  Brush* brush_a_;
+  bool result_;
+};
+
 }  // namespace render_tree
 }  // namespace cobalt
 
diff --git a/src/cobalt/render_tree/composition_node.h b/src/cobalt/render_tree/composition_node.h
index a6175a0..d39da3f 100644
--- a/src/cobalt/render_tree/composition_node.h
+++ b/src/cobalt/render_tree/composition_node.h
@@ -72,6 +72,10 @@
       children_.swap(moved->children_);
     }
 
+    bool operator==(const Builder& other) const {
+      return offset_ == other.offset_ && children_ == other.children_;
+    }
+
     // Add a child node to the list of children we are building.  When a
     // CompositionNode is constructed from this CompositionNode::Builder, it
     // will have as children all nodes who were passed into the builder via this
@@ -106,6 +110,9 @@
   explicit CompositionNode(Builder::Moved builder)
       : data_(builder), cached_bounds_(ComputeBounds()) {}
 
+  explicit CompositionNode(Builder&& builder)
+      : data_(builder.Pass()), cached_bounds_(ComputeBounds()) {}
+
   void Accept(NodeVisitor* visitor) OVERRIDE;
   math::RectF GetBounds() const OVERRIDE;
 
diff --git a/src/cobalt/render_tree/filter_node.h b/src/cobalt/render_tree/filter_node.h
index dc3df7c..612d76a 100644
--- a/src/cobalt/render_tree/filter_node.h
+++ b/src/cobalt/render_tree/filter_node.h
@@ -51,6 +51,13 @@
 
     math::RectF GetBounds() const;
 
+    bool operator==(const Builder& other) const {
+      return source == other.source && opacity_filter == other.opacity_filter &&
+             viewport_filter == other.viewport_filter &&
+             blur_filter == other.blur_filter &&
+             map_to_mesh_filter == other.map_to_mesh_filter;
+    }
+
     // The source tree, which will be used as the input to the filters specified
     // in this FilterNode.
     scoped_refptr<render_tree::Node> source;
diff --git a/src/cobalt/render_tree/image_node.h b/src/cobalt/render_tree/image_node.h
index ccd4773..7b5f048 100644
--- a/src/cobalt/render_tree/image_node.h
+++ b/src/cobalt/render_tree/image_node.h
@@ -36,6 +36,12 @@
             const math::RectF& destination_rect,
             const math::Matrix3F& local_transform);
 
+    bool operator==(const Builder& other) const {
+      return source == other.source &&
+             destination_rect == other.destination_rect &&
+             local_transform == other.local_transform;
+    }
+
     // A source of pixels. May be smaller or larger than layed out image.
     // The class does not own the image, it merely refers it from a resource
     // pool.
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h
index 060b080..a5c248f 100644
--- a/src/cobalt/render_tree/map_to_mesh_filter.h
+++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -47,6 +47,12 @@
  public:
   struct Builder {
     Builder() {}
+    bool operator==(const Builder& other) const {
+      return resolution_matched_meshes_ == other.resolution_matched_meshes_ &&
+             left_eye_default_mesh_ == other.left_eye_default_mesh_ &&
+             right_eye_default_mesh_ == other.right_eye_default_mesh_;
+    }
+
     void SetDefaultMeshes(
         const scoped_refptr<render_tree::Mesh>& left_eye_mesh,
         const scoped_refptr<render_tree::Mesh>& right_eye_mesh) {
@@ -113,6 +119,10 @@
     DCHECK(stereo_mode != kLeftRightUnadjustedTextureCoords);
   }
 
+  bool operator==(const MapToMeshFilter& other) const {
+    return stereo_mode_ == other.stereo_mode_ && data_ == other.data_;
+  }
+
   StereoMode stereo_mode() const { return stereo_mode_; }
 
   // The omission of the |resolution| parameter will yield the default
diff --git a/src/cobalt/render_tree/matrix_transform_3d_node.h b/src/cobalt/render_tree/matrix_transform_3d_node.h
index bbb517a..3732b68 100644
--- a/src/cobalt/render_tree/matrix_transform_3d_node.h
+++ b/src/cobalt/render_tree/matrix_transform_3d_node.h
@@ -37,6 +37,10 @@
     Builder(const scoped_refptr<Node>& source, const glm::mat4& transform)
         : source(source), transform(transform) {}
 
+    bool operator==(const Builder& other) const {
+      return source == other.source && transform == other.transform;
+    }
+
     // The subtree that will be rendered with |transform| applied to it.
     scoped_refptr<Node> source;
 
diff --git a/src/cobalt/render_tree/matrix_transform_node.h b/src/cobalt/render_tree/matrix_transform_node.h
index 3baafcc..93d9685 100644
--- a/src/cobalt/render_tree/matrix_transform_node.h
+++ b/src/cobalt/render_tree/matrix_transform_node.h
@@ -34,6 +34,10 @@
     Builder(const scoped_refptr<Node>& source, const math::Matrix3F& transform)
         : source(source), transform(transform) {}
 
+    bool operator==(const Builder& other) const {
+      return source == other.source && transform == other.transform;
+    }
+
     // The subtree that will be rendered with |transform| applied to it.
     scoped_refptr<Node> source;
 
diff --git a/src/cobalt/render_tree/opacity_filter.h b/src/cobalt/render_tree/opacity_filter.h
index 93e8718..113c909 100644
--- a/src/cobalt/render_tree/opacity_filter.h
+++ b/src/cobalt/render_tree/opacity_filter.h
@@ -27,6 +27,10 @@
  public:
   explicit OpacityFilter(float opacity) { set_opacity(opacity); }
 
+  bool operator==(const OpacityFilter& other) const {
+    return opacity_ == other.opacity_;
+  }
+
   void set_opacity(float opacity) {
     DCHECK_LE(0.0f, opacity);
     DCHECK_GE(1.0f, opacity);
diff --git a/src/cobalt/render_tree/punch_through_video_node.h b/src/cobalt/render_tree/punch_through_video_node.h
index 3683d6a..f96da9a 100644
--- a/src/cobalt/render_tree/punch_through_video_node.h
+++ b/src/cobalt/render_tree/punch_through_video_node.h
@@ -45,6 +45,10 @@
     Builder(const math::RectF& rect, const SetBoundsCB& set_bounds_cb)
         : rect(rect), set_bounds_cb(set_bounds_cb) {}
 
+    bool operator==(const Builder& other) const {
+      return rect == other.rect && set_bounds_cb.Equals(other.set_bounds_cb);
+    }
+
     // The destination rectangle (size includes border).
     math::RectF rect;
     const SetBoundsCB set_bounds_cb;
diff --git a/src/cobalt/render_tree/rect_node.cc b/src/cobalt/render_tree/rect_node.cc
index 17b046e..b628303 100644
--- a/src/cobalt/render_tree/rect_node.cc
+++ b/src/cobalt/render_tree/rect_node.cc
@@ -84,6 +84,23 @@
       border(moved->border.Pass()),
       rounded_corners(moved->rounded_corners.Pass()) {}
 
+bool RectNode::Builder::operator==(const Builder& other) const {
+  bool brush_equals = !background_brush && !other.background_brush;
+  if (background_brush && other.background_brush) {
+    EqualsBrushVisitor brush_equals_visitor(background_brush.get());
+    other.background_brush->Accept(&brush_equals_visitor);
+    brush_equals = brush_equals_visitor.result();
+  }
+  bool border_equals = (!border && !other.border) ||
+                       (border && other.border && *border == *other.border);
+  bool rounded_corners_equals = (!rounded_corners && !other.rounded_corners) ||
+                                (rounded_corners && other.rounded_corners &&
+                                 *rounded_corners == *other.rounded_corners);
+
+  return rect == other.rect && brush_equals && border_equals &&
+         rounded_corners_equals;
+}
+
 void RectNode::Accept(NodeVisitor* visitor) { visitor->Visit(this); }
 
 math::RectF RectNode::GetBounds() const { return data_.rect; }
diff --git a/src/cobalt/render_tree/rect_node.h b/src/cobalt/render_tree/rect_node.h
index a257cd8..c70fd44 100644
--- a/src/cobalt/render_tree/rect_node.h
+++ b/src/cobalt/render_tree/rect_node.h
@@ -52,6 +52,8 @@
     explicit Builder(const Builder& other);
     explicit Builder(Moved moved);
 
+    bool operator==(const Builder& other) const;
+
     // The destination rectangle (size includes border).
     math::RectF rect;
 
diff --git a/src/cobalt/render_tree/rect_shadow_node.h b/src/cobalt/render_tree/rect_shadow_node.h
index 072c80b..8ff107e 100644
--- a/src/cobalt/render_tree/rect_shadow_node.h
+++ b/src/cobalt/render_tree/rect_shadow_node.h
@@ -38,6 +38,11 @@
             float spread)
         : rect(rect), shadow(shadow), inset(inset), spread(spread) {}
 
+    bool operator==(const Builder& other) const {
+      return rect == other.rect && rounded_corners == other.rounded_corners &&
+             shadow == other.shadow && inset == other.inset &&
+             spread == other.spread;
+    }
     // The destination rectangle.
     math::RectF rect;
 
diff --git a/src/cobalt/render_tree/rounded_corners.h b/src/cobalt/render_tree/rounded_corners.h
index 7646f65..ef3bf38 100644
--- a/src/cobalt/render_tree/rounded_corners.h
+++ b/src/cobalt/render_tree/rounded_corners.h
@@ -81,6 +81,12 @@
         bottom_right(bottom_right),
         bottom_left(bottom_left) {}
 
+  bool operator==(const RoundedCorners& other) const {
+    return top_left == other.top_left && top_right == other.top_right &&
+           bottom_right == other.bottom_right &&
+           bottom_left == other.bottom_left;
+  }
+
   RoundedCorners Inset(float left, float top, float right, float bottom) const {
     return RoundedCorners(
         top_left.Inset(left, top), top_right.Inset(right, top),
diff --git a/src/cobalt/render_tree/shadow.h b/src/cobalt/render_tree/shadow.h
index caefb8a..057c30a 100644
--- a/src/cobalt/render_tree/shadow.h
+++ b/src/cobalt/render_tree/shadow.h
@@ -32,6 +32,11 @@
          const ColorRGBA& color)
       : offset(offset), blur_sigma(blur_sigma), color(color) {}
 
+  bool operator==(const Shadow& other) const {
+    return offset == other.offset && blur_sigma == other.blur_sigma &&
+           color == other.color;
+  }
+
   // Since the blur parameters represent standard deviations, most of the
   // blur is contained within 3 of them, so report that as the extent of the
   // blur.
diff --git a/src/cobalt/render_tree/text_node.h b/src/cobalt/render_tree/text_node.h
index 2dde330..3d35b43 100644
--- a/src/cobalt/render_tree/text_node.h
+++ b/src/cobalt/render_tree/text_node.h
@@ -46,6 +46,11 @@
             const scoped_refptr<GlyphBuffer>& glyph_buffer,
             const ColorRGBA& color);
 
+    bool operator==(const Builder& other) const {
+      return offset == other.offset && glyph_buffer == other.glyph_buffer &&
+             color == other.color && shadows == other.shadows;
+    }
+
     math::Vector2dF offset;
 
     // All of the glyph data needed to render the text.
diff --git a/src/cobalt/render_tree/viewport_filter.h b/src/cobalt/render_tree/viewport_filter.h
index 92f3d58..e4a0afa 100644
--- a/src/cobalt/render_tree/viewport_filter.h
+++ b/src/cobalt/render_tree/viewport_filter.h
@@ -33,6 +33,11 @@
                           const RoundedCorners& rounded_corners)
       : viewport_(viewport), rounded_corners_(rounded_corners) {}
 
+  bool operator==(const ViewportFilter& other) const {
+    return viewport_ == other.viewport_ &&
+           rounded_corners_ == other.rounded_corners_;
+  }
+
   const math::RectF& viewport() const { return viewport_; }
 
   bool has_rounded_corners() const { return !!rounded_corners_; }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
index 168df28..6db8fa8 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
@@ -7,7 +7,7 @@
 varying vec2 vMatrixCoord_Stage0;

 varying vec2 vMatrixCoord_Stage1;

 

-void main() 

+void main()

 {

   vec4 output_Stage0;

   {

diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 3f8d7dd..8827ef4 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -82,18 +82,23 @@
       submission_disposal_thread_("Rasterizer Submission Disposal"),
       submit_even_if_render_tree_is_unchanged_(
           submit_even_if_render_tree_is_unchanged),
-      last_render_animations_active_(false),
+      last_did_rasterize_(false),
+      last_animations_expired_(true),
       rasterize_periodic_timer_("Renderer.Rasterize.Duration",
                                 kRasterizePeriodicTimerEntriesPerUpdate,
                                 false /*enable_entry_list_c_val*/),
-      ALLOW_THIS_IN_INITIALIZER_LIST(rasterize_animations_interval_timer_(
-          "Renderer.Rasterize.AnimationsInterval",
-          kRasterizeAnimationsTimerMaxEntries, true /*enable_entry_list_c_val*/,
-          base::Bind(&Pipeline::FrameStatsOnFlushCallback,
-                     base::Unretained(this)))),
       rasterize_animations_timer_("Renderer.Rasterize.Animations",
                                   kRasterizeAnimationsTimerMaxEntries,
                                   true /*enable_entry_list_c_val*/),
+      ALLOW_THIS_IN_INITIALIZER_LIST(rasterize_periodic_interval_timer_(
+          "Renderer.Rasterize.DurationInterval",
+          kRasterizeAnimationsTimerMaxEntries, true /*enable_entry_list_c_val*/,
+          base::Bind(&Pipeline::FrameStatsOnFlushCallback,
+                     base::Unretained(this)))),
+      rasterize_animations_interval_timer_(
+          "Renderer.Rasterize.AnimationsInterval",
+          kRasterizeAnimationsTimerMaxEntries,
+          true /*enable_entry_list_c_val*/),
       new_render_tree_rasterize_count_(
           "Count.Renderer.Rasterize.NewRenderTree", 0,
           "Total number of new render trees rasterized."),
@@ -136,7 +141,7 @@
       clear_on_shutdown_mode_(clear_on_shutdown_mode),
       enable_fps_stdout_(options.enable_fps_stdout),
       enable_fps_overlay_(options.enable_fps_overlay),
-      fps_overlay_updated_(false) {
+      fps_overlay_update_pending_(false) {
   TRACE_EVENT0("cobalt::renderer", "Pipeline::Pipeline()");
   // The actual Pipeline can be constructed from any thread, but we want
   // rasterizer_thread_checker_ to be associated with the rasterizer thread,
@@ -170,6 +175,7 @@
   // must be destroyed before we shutdown the rasterizer thread since it may
   // contain references to render tree nodes and resources.
   last_render_tree_ = NULL;
+  last_animated_render_tree_ = NULL;
 
   // Submit a shutdown task to the rasterizer thread so that it can shutdown
   // anything that must be shutdown from that thread.
@@ -263,18 +269,19 @@
   DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("cobalt::renderer", "Pipeline::RasterizeCurrentTree()");
 
-  base::TimeTicks now = base::TimeTicks::Now();
-  Submission submission = submission_queue_->GetCurrentSubmission(now);
+  base::TimeTicks start_rasterize_time = base::TimeTicks::Now();
+  Submission submission =
+      submission_queue_->GetCurrentSubmission(start_rasterize_time);
 
   bool is_new_render_tree = submission.render_tree != last_render_tree_;
   bool has_render_tree_changed =
-      last_render_animations_active_ || is_new_render_tree;
+      !last_animations_expired_ || is_new_render_tree;
 
   // If our render tree hasn't changed from the one that was previously
   // rendered and it's okay on this system to not flip the display buffer
   // frequently, then we can just not do anything here.
-  if (!fps_overlay_updated_ && !submit_even_if_render_tree_is_unchanged_ &&
-      !has_render_tree_changed) {
+  if (!fps_overlay_update_pending_ &&
+      !submit_even_if_render_tree_is_unchanged_ && !has_render_tree_changed) {
     return;
   }
 
@@ -283,77 +290,93 @@
   render_tree::animations::AnimateNode* animate_node =
       base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
           submission.render_tree.get());
-  bool are_animations_active = animate_node->expiry() > submission.time_offset;
 
-  // If animations are going from being inactive to active, then set the c_val
-  // prior to starting the animation so that it's in the correct state while the
-  // tree is being rendered.
-  // Also, start the interval timer now. While the first entry only captures a
-  // partial interval, it's recorded to include the duration of the first
-  // submission. All subsequent entries will record a full interval.
-  if (!last_render_animations_active_ && are_animations_active) {
-    has_active_animations_c_val_ = true;
-    rasterize_animations_interval_timer_.Start(now);
-  }
+  // Rasterize the last submitted render tree.
+  bool did_rasterize =
+      RasterizeSubmissionToRenderTarget(submission, render_target_);
+
+  bool animations_expired = animate_node->expiry() <= submission.time_offset;
+
+  UpdateRasterizeStats(did_rasterize, animations_expired, is_new_render_tree,
+                       start_rasterize_time, base::TimeTicks::Now());
+
+  last_did_rasterize_ = did_rasterize;
+  last_animations_expired_ = animations_expired;
+}
+
+void Pipeline::UpdateRasterizeStats(bool did_rasterize, bool animations_expired,
+                                    bool is_new_render_tree,
+                                    base::TimeTicks start_time,
+                                    base::TimeTicks end_time) {
+  bool last_animations_active =
+      !last_animations_expired_ && last_did_rasterize_;
+  bool animations_active = !animations_expired && did_rasterize;
 
   // The rasterization is only timed with the periodic timer when the render
   // tree has changed. This ensures that the frames being timed are consistent
   // between platforms that submit unchanged trees and those that don't.
-  bool should_run_periodic_timer = has_render_tree_changed;
-
-  // The rasterization is only timed with the animations timer when there are
-  // animations to track. This applies when animations were active during either
-  // the last rasterization or the current one. The reason for including the
-  // last one is that if animations have just expired, then this rasterization
-  // produces the final state of the animated tree.
-  bool should_run_animations_timer =
-      last_render_animations_active_ || are_animations_active;
-
-  if (should_run_periodic_timer) {
-    rasterize_periodic_timer_.Start(now);
-  }
-  if (should_run_animations_timer) {
-    rasterize_animations_timer_.Start(now);
+  if (did_rasterize) {
+    rasterize_periodic_timer_.Start(start_time);
+    rasterize_periodic_timer_.Stop(end_time);
   }
 
-  // Rasterize the last submitted render tree.
-  RasterizeSubmissionToRenderTarget(submission, render_target_);
+  if (last_animations_active || animations_active) {
+    // The rasterization is only timed with the animations timer when there are
+    // animations to track. This applies when animations were active during
+    // either the last rasterization or the current one. The reason for
+    // including the last one is that if animations have just expired, then this
+    // rasterization produces the final state of the animated tree.
+    if (did_rasterize) {
+      rasterize_animations_timer_.Start(start_time);
+      rasterize_animations_timer_.Stop(end_time);
+    }
 
-  // Update now with the post-submission time.
-  now = base::TimeTicks::Now();
+    // If animations are going from being inactive to active, then set the c_val
+    // prior to starting the animation so that it's in the correct state while
+    // the tree is being rendered.
+    // Also, start the interval timer now. While the first entry only captures a
+    // partial interval, it's recorded to include the duration of the first
+    // submission. All subsequent entries will record a full interval.
+    if (!last_animations_active && animations_active) {
+      has_active_animations_c_val_ = true;
+      rasterize_periodic_interval_timer_.Start(start_time);
+      rasterize_animations_interval_timer_.Start(start_time);
+    }
 
-  if (should_run_periodic_timer) {
-    rasterize_periodic_timer_.Stop(now);
-  }
-  if (should_run_animations_timer) {
-    rasterize_animations_interval_timer_.Stop(now);
-    rasterize_animations_timer_.Stop(now);
+    if (!did_rasterize) {
+      // If we didn't actually rasterize anything, don't count this sample.
+      rasterize_periodic_interval_timer_.Cancel();
+      rasterize_animations_interval_timer_.Cancel();
+    } else {
+      rasterize_periodic_interval_timer_.Stop(end_time);
+      rasterize_animations_interval_timer_.Stop(end_time);
+    }
+
     // If animations are active, then they are guaranteed at least one more
     // interval. Start the timer to record its duration.
-    if (are_animations_active) {
-      rasterize_animations_interval_timer_.Start(now);
+    if (animations_active) {
+      rasterize_periodic_interval_timer_.Start(end_time);
+      rasterize_animations_interval_timer_.Start(end_time);
+    }
+
+    // Check for if the animations are starting or ending.
+    if (!last_animations_active && animations_active) {
+      animations_start_time_ = end_time.ToInternalValue();
+    } else if (last_animations_active && !animations_active) {
+      animations_end_time_ = end_time.ToInternalValue();
+      has_active_animations_c_val_ = false;
+      rasterize_animations_interval_timer_.Flush();
+      rasterize_animations_timer_.Flush();
     }
   }
 
   if (is_new_render_tree) {
     ++new_render_tree_rasterize_count_;
-    new_render_tree_rasterize_time_ = now.ToInternalValue();
+    new_render_tree_rasterize_time_ = end_time.ToInternalValue();
   }
-
-  // Check for if the animations are starting or ending.
-  if (!last_render_animations_active_ && are_animations_active) {
-    animations_start_time_ = now.ToInternalValue();
-  } else if (last_render_animations_active_ && !are_animations_active) {
-    animations_end_time_ = now.ToInternalValue();
-    has_active_animations_c_val_ = false;
-    rasterize_animations_interval_timer_.Flush();
-    rasterize_animations_timer_.Flush();
-  }
-
-  last_render_animations_active_ = are_animations_active;
 }
 
-void Pipeline::RasterizeSubmissionToRenderTarget(
+bool Pipeline::RasterizeSubmissionToRenderTarget(
     const Submission& submission,
     const scoped_refptr<backend::RenderTarget>& render_target) {
   TRACE_EVENT0("cobalt::renderer",
@@ -364,14 +387,17 @@
   // |previous_animated_area_|.
   if (submission.render_tree != last_render_tree_) {
     last_render_tree_ = submission.render_tree;
+    last_animated_render_tree_ = NULL;
     previous_animated_area_ = base::nullopt;
     last_render_time_ = base::nullopt;
   }
 
   // Animate the render tree using the submitted animations.
   render_tree::animations::AnimateNode* animate_node =
-      base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
-          submission.render_tree.get());
+      last_animated_render_tree_
+          ? last_animated_render_tree_.get()
+          : base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
+                submission.render_tree.get());
 
   // Some animations require a GL graphics context to be current.  Specifically,
   // a call to SbPlayerGetCurrentFrame() may be made to get the current video
@@ -381,6 +407,13 @@
   render_tree::animations::AnimateNode::AnimateResults results =
       animate_node->Apply(submission.time_offset);
 
+  if (results.animated == last_animated_render_tree_ &&
+      !submit_even_if_render_tree_is_unchanged_ &&
+      !fps_overlay_update_pending_) {
+    return false;
+  }
+  last_animated_render_tree_ = results.animated;
+
   // Calculate a bounding box around the active animations.  Union it with the
   // bounding box around active animations from the previous frame, and we get
   // a scissor rectangle marking the dirty regions of the screen.
@@ -393,9 +426,11 @@
   }
   previous_animated_area_ = rounded_bounds;
 
-  scoped_refptr<render_tree::Node> submit_tree = results.animated;
+  scoped_refptr<render_tree::Node> submit_tree = results.animated->source();
   if (enable_fps_overlay_ && fps_overlay_) {
-    submit_tree = fps_overlay_->AnnotateRenderTreeWithOverlay(results.animated);
+    submit_tree =
+        fps_overlay_->AnnotateRenderTreeWithOverlay(results.animated->source());
+    fps_overlay_update_pending_ = false;
   }
 
   // Rasterize the animated render tree.
@@ -408,6 +443,8 @@
   }
 
   last_render_time_ = submission.time_offset;
+
+  return true;
 }
 
 void Pipeline::InitializeRasterizerThread(
@@ -499,7 +536,8 @@
   render_tree::animations::AnimateNode::AnimateResults results =
       animate_node->Apply(submission.time_offset);
 
-  std::string tree_dump = render_tree::DumpRenderTreeToString(results.animated);
+  std::string tree_dump =
+      render_tree::DumpRenderTreeToString(results.animated->source());
   if (message.empty() || message == "undefined") {
     // If no filename was specified, send output to the console.
     LOG(INFO) << tree_dump.c_str();
@@ -533,6 +571,7 @@
   }
 
   enable_fps_overlay_ = !enable_fps_overlay_;
+  fps_overlay_update_pending_ = enable_fps_overlay_;
 }
 #endif  // #if defined(ENABLE_DEBUG_CONSOLE)
 
@@ -575,7 +614,7 @@
     }
 
     fps_overlay_->UpdateOverlay(flush_results);
-    fps_overlay_updated_ = true;
+    fps_overlay_update_pending_ = true;
   }
 
   if (enable_fps_stdout_) {
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index e304e59..49308c9 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -118,10 +118,17 @@
 
   // Rasterize the animated |render_tree_submission| to |render_target|,
   // applying the time_offset in the submission to the animations.
-  void RasterizeSubmissionToRenderTarget(
+  // Returns true only if a rasterization actually took place.
+  bool RasterizeSubmissionToRenderTarget(
       const Submission& render_tree_submission,
       const scoped_refptr<backend::RenderTarget>& render_target);
 
+  // Updates the rasterizer timer stats according to the |start_time| and
+  // |end_time| of the most recent rasterize call.
+  void UpdateRasterizeStats(bool did_rasterize, bool are_animations_active,
+                            bool is_new_render_tree, base::TimeTicks start_time,
+                            base::TimeTicks end_time);
+
   // This method is executed on the rasterizer thread and is responsible for
   // constructing the rasterizer.
   void InitializeRasterizerThread(
@@ -193,6 +200,10 @@
 
   // Keeps track of the last rendered animated render tree.
   scoped_refptr<render_tree::Node> last_render_tree_;
+
+  scoped_refptr<render_tree::animations::AnimateNode>
+      last_animated_render_tree_;
+
   // Keeps track of the area of the screen that animations previously existed
   // within, so that we can know which regions of the screens would be dirty
   // next frame.
@@ -201,22 +212,31 @@
   base::optional<base::TimeDelta> last_render_time_;
   // Keep track of whether the last rendered tree had active animations. This
   // allows us to skip rasterizing that render tree if we see it again and it
-  // did not have active animations.
-  bool last_render_animations_active_;
+  // did have expired animations.
+  bool last_animations_expired_;
+
+  // Did a rasterization take place in the last frame?
+  bool last_did_rasterize_;
 
   // Timer tracking the amount of time spent in
   // |RasterizeSubmissionToRenderTarget| when the render tree has changed.
   // The tracking is flushed when the max count is hit.
   base::CValCollectionTimerStats<base::CValPublic> rasterize_periodic_timer_;
+  // Timer tracking the amount of time spent in
+  // |RasterizeSubmissionToRenderTarget| while animations are active. The
+  // tracking is flushed when the animations expire.
+  base::CValCollectionTimerStats<base::CValDebug> rasterize_animations_timer_;
+
+  // Accumulates render tree rasterization interval times but does not flush
+  // them until the maximum number of samples is gathered.
+  base::CValCollectionTimerStats<base::CValPublic>
+      rasterize_periodic_interval_timer_;
+
   // Timer tracking the amount of time between calls to
   // |RasterizeSubmissionToRenderTarget| while animations are active. The
   // tracking is flushed when the animations expire.
   base::CValCollectionTimerStats<base::CValPublic>
       rasterize_animations_interval_timer_;
-  // Timer tracking the amount of time spent in
-  // |RasterizeSubmissionToRenderTarget| while animations are active. The
-  // tracking is flushed when the animations expire.
-  base::CValCollectionTimerStats<base::CValDebug> rasterize_animations_timer_;
 
   // The total number of new render trees that have been rasterized.
   base::CVal<int> new_render_tree_rasterize_count_;
@@ -254,7 +274,9 @@
   bool enable_fps_overlay_;
 
   base::optional<FpsOverlay> fps_overlay_;
-  bool fps_overlay_updated_;
+
+  // True if the overlay has been updated and it needs to be re-rasterized.
+  bool fps_overlay_update_pending_;
 };
 
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
index fcabd2d..3261089 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -42,41 +42,55 @@
 
 void SetBlurRRectUniforms(const ShaderFragmentColorBlurRrects& shader,
     math::RectF rect, render_tree::RoundedCorners corners, float sigma) {
-  // Ensure a minimum radius for each corner to avoid division by zero.
-  const float kMinSize = 0.01f;
+  const float kBlurExtentInPixels = kBlurExtentInSigmas * sigma;
+  const float kOffsetScale = kBlurDistance / kBlurExtentInPixels;
 
+  // Ensure a minimum radius for each corner to avoid division by zero.
+  // NOTE: The rounded rect is already specified in terms of sigma.
+  const float kMinSize = 0.01f * kOffsetScale;
   rect.Outset(kMinSize, kMinSize);
   corners = corners.Inset(-kMinSize, -kMinSize, -kMinSize, -kMinSize);
   corners = corners.Normalize(rect);
 
-  // Specify the blur extent size and the (min.y, max.y) for the rect.
-  const float kBlurExtentInPixels = kBlurExtentInSigmas * sigma;
-  GL_CALL(glUniform3f(shader.u_blur_extent(),
-                      kBlurExtentInPixels, rect.y(), rect.bottom()));
+  // A normalized rounded rect should have at least one Y value which the
+  // corners do not cross.
+  const float kCenterY =
+      0.5f * (rect.y() + std::max(corners.top_left.vertical,
+                                  corners.top_right.vertical)) +
+      0.5f * (rect.bottom() - std::max(corners.bottom_left.vertical,
+                                       corners.bottom_right.vertical));
 
-  // Set the "start" and "scale" values so that (pos - start) * scale is in the
-  // first quadrant and normalized. Then specify "radius" so that normalized *
-  // radius + start specifies a point on the respective corner.
-  GL_CALL(glUniform4f(shader.u_spread_start_x(),
+  // The blur extent is (blur_size.y, rect_min.y, rect_max.y, rect_center.y).
+  GL_CALL(glUniform4f(shader.u_blur_extent(),
+                      kBlurExtentInPixels * kOffsetScale,
+                      rect.y(), rect.bottom(), kCenterY));
+
+  // The blur rounded rect is split into top and bottom halves.
+  // The "start" values represent (left_start.xy, right_start.xy).
+  // The "scale" values represent (left_radius.x, 1 / left_radius.y,
+  //   right_radius.x, 1 / right_radius.y). The sign of the scale value helps
+  //   to translate between position and corner offset values, where the corner
+  //   offset is positive if the position is inside the rounded corner.
+  GL_CALL(glUniform4f(shader.u_blur_start_top(),
                       rect.x() + corners.top_left.horizontal,
-                      rect.right() - corners.top_right.horizontal,
-                      rect.x() + corners.bottom_left.horizontal,
-                      rect.right() - corners.bottom_right.horizontal));
-  GL_CALL(glUniform4f(shader.u_spread_start_y(),
                       rect.y() + corners.top_left.vertical,
-                      rect.y() + corners.top_right.vertical,
+                      rect.right() - corners.top_right.horizontal,
+                      rect.y() + corners.top_right.vertical));
+  GL_CALL(glUniform4f(shader.u_blur_start_bottom(),
+                      rect.x() + corners.bottom_left.horizontal,
                       rect.bottom() - corners.bottom_left.vertical,
+                      rect.right() - corners.bottom_right.horizontal,
                       rect.bottom() - corners.bottom_right.vertical));
-  GL_CALL(glUniform4f(shader.u_spread_scale_y(),
-                      -1.0f / corners.top_left.vertical,
-                      -1.0f / corners.top_right.vertical,
-                      1.0f / corners.bottom_left.vertical,
-                      1.0f / corners.bottom_right.vertical));
-  GL_CALL(glUniform4f(shader.u_spread_radius_x(),
+  GL_CALL(glUniform4f(shader.u_blur_scale_top(),
                       -corners.top_left.horizontal,
+                      -1.0f / corners.top_left.vertical,
                       corners.top_right.horizontal,
+                      -1.0f / corners.top_right.vertical));
+  GL_CALL(glUniform4f(shader.u_blur_scale_bottom(),
                       -corners.bottom_left.horizontal,
-                      corners.bottom_right.horizontal));
+                      1.0f / corners.bottom_left.vertical,
+                      corners.bottom_right.horizontal,
+                      1.0f / corners.bottom_right.vertical));
 }
 }  // namespace
 
@@ -89,10 +103,12 @@
 }
 
 DrawRectShadowBlur::VertexAttributesRound::VertexAttributesRound(
-    float x, float y, const RCorner& init) {
+    float x, float y, float offset_scale, const RCorner& rcorner) {
   position[0] = x;
   position[1] = y;
-  rcorner_scissor = RCorner(position, init);
+  offset[0] = x * offset_scale;
+  offset[1] = y * offset_scale;
+  rcorner_scissor = RCorner(position, rcorner);
 }
 
 DrawRectShadowBlur::DrawRectShadowBlur(GraphicsState* graphics_state,
@@ -117,12 +133,10 @@
   OptionalRoundedCorners scaled_base_corners(base_corners);
   if (scaled_base_corners) {
     scaled_base_corners = scaled_base_corners->Scale(scale.x(), scale.y());
-    scaled_base_corners = scaled_base_corners->Normalize(scaled_base_rect);
   }
   spread_rect_.Scale(scale.x(), scale.y());
   if (spread_corners_) {
     spread_corners_ = spread_corners_->Scale(scale.x(), scale.y());
-    spread_corners_ = spread_corners_->Normalize(spread_rect_);
   }
 
   // The blur algorithms used by the shaders do not produce good results with
@@ -168,13 +182,6 @@
     SetupVertexShader(graphics_state, program->GetVertexShader());
     SetFragmentUniforms(program->GetFragmentShader().u_color(),
                         program->GetFragmentShader().u_scale_add());
-    float sigma_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
-    GL_CALL(glUniform2f(program->GetFragmentShader().u_sigma_scale(),
-                        sigma_scale, sigma_scale));
-    // Pre-calculate the scale values to calculate the normalized gaussian.
-    GL_CALL(glUniform2f(program->GetFragmentShader().u_gaussian_scale(),
-                        -1.0f / (2.0f * blur_sigma_ * blur_sigma_),
-                        1.0f / (kSqrt2 * kSqrtPi * blur_sigma_)));
     SetBlurRRectUniforms(program->GetFragmentShader(),
                          spread_rect_, *spread_corners_, blur_sigma_);
     GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
@@ -234,6 +241,10 @@
       sizeof(VertexAttributesRound), vertex_buffer_ +
       offsetof(VertexAttributesRound, position));
   graphics_state->VertexAttribPointer(
+      shader.a_offset(), 2, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesRound), vertex_buffer_ +
+      offsetof(VertexAttributesRound, offset));
+  graphics_state->VertexAttribPointer(
       shader.a_rcorner(), 4, GL_FLOAT, GL_FALSE,
       sizeof(VertexAttributesRound), vertex_buffer_ +
       offsetof(VertexAttributesRound, rcorner_scissor));
@@ -310,10 +321,9 @@
 
 void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
     const math::RectF& inner_rect, const math::RectF& outer_rect) {
-  // Express offset in terms of blur sigma for the shader.
+  // The spread rect and offsets should be expressed in terms of sigma for the
+  // shader.
   float offset_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
-
-  // The spread rect should also be expressed in terms of sigma.
   spread_rect_.Scale(offset_scale, offset_scale);
 
   // The box shadow is a triangle strip covering the area between outer rect
@@ -360,21 +370,15 @@
 
 void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
     const RRectAttributes (&rrect)[8]) {
+  // The spread rect and offsets should be expressed in terms of sigma for the
+  // shader.
+  float offset_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
+  spread_rect_.Scale(offset_scale, offset_scale);
+  *spread_corners_ = spread_corners_->Scale(offset_scale, offset_scale);
+
   // The shadowed area is already split into quads.
   for (int i = 0; i < arraysize(rrect); ++i) {
-    uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
-    const math::RectF& bounds = rrect[i].bounds;
-    const RCorner& rcorner = rrect[i].rcorner;
-    attributes_round_.emplace_back(bounds.x(), bounds.y(), rcorner);
-    attributes_round_.emplace_back(bounds.right(), bounds.y(), rcorner);
-    attributes_round_.emplace_back(bounds.x(), bounds.bottom(), rcorner);
-    attributes_round_.emplace_back(bounds.right(), bounds.bottom(), rcorner);
-    indices_.emplace_back(vert);
-    indices_.emplace_back(vert + 1);
-    indices_.emplace_back(vert + 2);
-    indices_.emplace_back(vert + 1);
-    indices_.emplace_back(vert + 2);
-    indices_.emplace_back(vert + 3);
+    AddQuad(rrect[i].bounds, offset_scale, rrect[i].rcorner);
   }
 
   graphics_state->ReserveVertexData(
@@ -385,6 +389,12 @@
 void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
     const RRectAttributes (&rrect_outer)[4],
     const RRectAttributes (&rrect_inner)[8]) {
+  // The spread rect and offsets should be expressed in terms of sigma for the
+  // shader.
+  float offset_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
+  spread_rect_.Scale(offset_scale, offset_scale);
+  *spread_corners_ = spread_corners_->Scale(offset_scale, offset_scale);
+
   // Draw the area between the inner rect and outer rect using the outer rect's
   // rounded corners. The inner quads already exclude the inscribed rectangle.
   for (int i = 0; i < arraysize(rrect_inner); ++i) {
@@ -392,19 +402,7 @@
       math::RectF rect = math::IntersectRects(
           rrect_inner[i].bounds, rrect_outer[o].bounds);
       if (!rect.IsEmpty()) {
-        // Use two triangles to draw the intersection.
-        const RCorner& rcorner = rrect_outer[o].rcorner;
-        uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
-        attributes_round_.emplace_back(rect.x(), rect.y(), rcorner);
-        attributes_round_.emplace_back(rect.right(), rect.y(), rcorner);
-        attributes_round_.emplace_back(rect.x(), rect.bottom(), rcorner);
-        attributes_round_.emplace_back(rect.right(), rect.bottom(), rcorner);
-        indices_.emplace_back(vert);
-        indices_.emplace_back(vert + 1);
-        indices_.emplace_back(vert + 2);
-        indices_.emplace_back(vert + 1);
-        indices_.emplace_back(vert + 2);
-        indices_.emplace_back(vert + 3);
+        AddQuad(rect, offset_scale, rrect_outer[o].rcorner);
       }
     }
   }
@@ -414,6 +412,21 @@
   graphics_state->ReserveVertexIndices(indices_.size());
 }
 
+void DrawRectShadowBlur::AddQuad(const math::RectF& rect, float scale,
+    const RCorner& rcorner) {
+  uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+  attributes_round_.emplace_back(rect.x(), rect.y(), scale, rcorner);
+  attributes_round_.emplace_back(rect.right(), rect.y(), scale, rcorner);
+  attributes_round_.emplace_back(rect.x(), rect.bottom(), scale, rcorner);
+  attributes_round_.emplace_back(rect.right(), rect.bottom(), scale, rcorner);
+  indices_.emplace_back(vert);
+  indices_.emplace_back(vert + 1);
+  indices_.emplace_back(vert + 2);
+  indices_.emplace_back(vert + 1);
+  indices_.emplace_back(vert + 2);
+  indices_.emplace_back(vert + 3);
+}
+
 }  // namespace egl
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
index 107af85..6ba4e85 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
@@ -73,8 +73,10 @@
   };
 
   struct VertexAttributesRound {
-    VertexAttributesRound(float x, float y, const RCorner& init);
+    VertexAttributesRound(float x, float y, float offset_scale,
+                          const RCorner& rcorner);
     float position[2];
+    float offset[2];
     RCorner rcorner_scissor;
   };
 
@@ -95,6 +97,7 @@
   void SetGeometry(GraphicsState* graphics_state,
                    const RRectAttributes (&rrect_outer)[4],
                    const RRectAttributes (&rrect_inner)[8]);
+  void AddQuad(const math::RectF& rect, float scale, const RCorner& rcorner);
 
   math::RectF spread_rect_;
   OptionalRoundedCorners spread_corners_;
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index bcd8e49..6043afa 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -300,6 +300,8 @@
           purge_skia_font_caches_on_destruction)) {
 }
 
+HardwareRasterizer::~HardwareRasterizer() {}
+
 void HardwareRasterizer::Submit(
     const scoped_refptr<render_tree::Node>& render_tree,
     const scoped_refptr<backend::RenderTarget>& render_target,
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
index cd7eb6e..fd3c144 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
@@ -52,6 +52,7 @@
                               int scratch_surface_cache_size_in_bytes,
                               int offscreen_target_cache_size_in_bytes,
                               bool purge_skia_font_caches_on_destruction);
+  virtual ~HardwareRasterizer();
 
   void Submit(const scoped_refptr<render_tree::Node>& render_tree,
               const scoped_refptr<backend::RenderTarget>& render_target,
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 72b7da7..677f941 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -64,8 +64,12 @@
 }
 
 bool IsOnlyScaleAndTranslate(const math::Matrix3F& matrix) {
-  return matrix(2, 0) == 0 && matrix(2, 1) == 0 && matrix(2, 2) == 1 &&
-         matrix(0, 1) == 0 && matrix(1, 0) == 0;
+  const float kEpsilon = 0.0001f;
+  return std::abs(matrix(0, 1)) < kEpsilon &&
+         std::abs(matrix(1, 0)) < kEpsilon &&
+         std::abs(matrix(2, 0)) < kEpsilon &&
+         std::abs(matrix(2, 1)) < kEpsilon &&
+         std::abs(matrix(2, 2) - 1.0f) < kEpsilon;
 }
 
 math::Matrix3F GetTexcoordTransform(
@@ -672,15 +676,28 @@
   // Get a suitable cache of the render tree node if one exists, or allocate
   // a new offscreen target if possible. The OffscreenTargetErrorFunction will
   // determine whether any caches are fit for use.
-  *out_content_cached = offscreen_target_manager_->GetCachedOffscreenTarget(
-      node, base::Bind(&OffscreenTargetErrorFunction, mapped_bounds),
-      out_target_info);
-  if (!(*out_content_cached)) {
-    offscreen_target_manager_->AllocateOffscreenTarget(node,
-        content_size, mapped_bounds, out_target_info);
+
+  // Do not cache rotating nodes since these will result in inappropriate
+  // reuse of offscreen targets. Transforms that are rotations of angles in
+  // the first quadrant will produce the same mapped rect sizes as angles in
+  // the other 3 quadrants. Also avoid caching reflections.
+  bool allow_caching = IsOnlyScaleAndTranslate(draw_state_.transform) &&
+                       draw_state_.transform(0, 0) > 0.0f &&
+                       draw_state_.transform(1, 1) > 0.0f;
+  if (allow_caching) {
+    *out_content_cached = offscreen_target_manager_->GetCachedOffscreenTarget(
+        node, base::Bind(&OffscreenTargetErrorFunction, mapped_bounds),
+        out_target_info);
+    if (!(*out_content_cached)) {
+      offscreen_target_manager_->AllocateOffscreenTarget(node,
+          content_size, mapped_bounds, out_target_info);
+    } else {
+      // Maintain the size of the cached contents to avoid scaling artifacts.
+      content_size = out_target_info->region.size();
+    }
   } else {
-    // Maintain the size of the cached contents to avoid scaling artifacts.
-    content_size = out_target_info->region.size();
+    *out_content_cached = false;
+    out_target_info->framebuffer = nullptr;
   }
 
   // If no offscreen target could be allocated, then the render tree node will
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
index efc8747..ffbee99 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
@@ -14,26 +14,19 @@
 

 precision mediump float;

 

-// The rounded spread rect is represented in a way to optimize calculation of

-// the extents. Each element of a vec4 represents a corner's value -- order

-// is top left, top right, bottom left, bottom right. Extents for each corner

-// can be calculated as:

-//   extents_x = start_x + radius_x * sqrt(1 - scaled_y^2) where

-//   scaled_y = clamp((pos.yyyy - start_y) * scale_y, 0.0, 1.0)

-// To simplify handling left vs right and top vs bottom corners, the sign of

-// scale_y and radius_x handles negation as needed.

-uniform vec4 u_spread_start_x;

-uniform vec4 u_spread_start_y;

-uniform vec4 u_spread_scale_y;

-uniform vec4 u_spread_radius_x;

+// The blur rounded rect is split into top and bottom halves.

+// The "start" values represent (left_start.xy, right_start.xy).

+// The "scale" values represent (left_radius.x, 1 / left_radius.y,

+//   right_radius.x, 1 / right_radius.y). The sign of the scale value helps

+//   to translate between position and corner offset values, where the corner

+//   offset is positive if the position is inside the rounded corner.

+uniform vec4 u_blur_start_top;

+uniform vec4 u_blur_start_bottom;

+uniform vec4 u_blur_scale_top;

+uniform vec4 u_blur_scale_bottom;

 

-// The blur extent specifies (3 * sigma, min_rect_y, max_rect_y). This is used

-// to clamp the interval over which integration should be evaluated.

-uniform vec3 u_blur_extent;

-

-// The gaussian scale uniform is used to simplify calculation of the gaussian

-// function at a particular point.

-uniform vec2 u_gaussian_scale;

+// The blur extent specifies (blur_size, min_rect_y, max_rect_y, center_rect_y).

+uniform vec4 u_blur_extent;

 

 // The scale_add uniform is used to switch the shader between generating

 // outset shadows and inset shadows. It impacts the shadow gradient and

@@ -44,39 +37,25 @@
 

 uniform vec4 u_color;

 

-// Blur calculations happen in terms in sigma distances. Use sigma_scale to

-// translate pixel distances into sigma distances.

-uniform vec2 u_sigma_scale;

-

 varying vec2 v_offset;

 varying vec4 v_rcorner;

 

 #include "function_is_outside_rcorner.inc"

 #include "function_gaussian_integral.inc"

 

-vec2 GetXExtents(float y) {

-  // Use x^2 / a^2 + y^2 / b^2 = 1 to solve for the x value of each rounded

-  // corner at the given y.

-  vec4 scaled = clamp((y - u_spread_start_y) * u_spread_scale_y, 0.0, 1.0);

-  vec4 root = sqrt(1.0 - scaled * scaled);

-  vec4 extent = u_spread_start_x + u_spread_radius_x * root;

-

-  // If the y value was before a corner started, then the calculated extent

-  // would equal the unrounded rectangle's extents (since negative values were

-  // clamped to 0 in the above calculation). So smaller extents (i.e. extents

-  // closer to the rectangle center), represent the relevant corners' extents.

-  return vec2(max(extent.x, extent.z), min(extent.y, extent.w));

-}

-

 float GetXBlur(float x, float y) {

-  // Get the integral over the interval occupied by the rectangle.

-  vec2 pos = (GetXExtents(y) - x) * u_sigma_scale;

-  return GaussianIntegral(pos);

-}

+  // Solve for X of the rounded corners at the given Y based on the equation

+  // for an ellipse: x^2 / a^2 + y^2 / b^2 = 1.

+  vec4 corner_start =

+      (y < u_blur_extent.w) ? u_blur_start_top : u_blur_start_bottom;

+  vec4 corner_scale =

+      (y < u_blur_extent.w) ? u_blur_scale_top : u_blur_scale_bottom;

+  vec2 scaled = clamp((y - corner_start.yw) * corner_scale.yw, 0.0, 1.0);

+  vec2 root = sqrt(1.0 - scaled * scaled);

+  vec2 extent_x = corner_start.xz + corner_scale.xz * root;

 

-vec3 GetGaussian(vec3 offset) {

-  // Evaluate the gaussian at the given offsets.

-  return exp(offset * offset * u_gaussian_scale.x);

+  // Get the integral over the interval occupied by the rectangle.

+  return GaussianIntegral(extent_x - x);

 }

 

 float GetBlur(vec2 pos) {

@@ -111,12 +90,13 @@
   vec3 xblur2 = vec3(GetXBlur(pos.x, pos2.x),

                      GetXBlur(pos.x, pos2.y),

                      GetXBlur(pos.x, pos2.z));

-  vec3 yblur1 = GetGaussian(offset1) * weight;

-  vec3 yblur2 = GetGaussian(offset2) * weight;

+  vec3 yblur1 = exp(-offset1 * offset1) * weight;

+  vec3 yblur2 = exp(-offset2 * offset2) * weight;

 

-  // Since each yblur value should be scaled by u_gaussian_scale.y, save some

-  // cycles and multiply the sum by it.

-  return (dot(xblur1, yblur1) + dot(xblur2, yblur2)) * u_gaussian_scale.y;

+  // Since each yblur value should be normalized by kNormalizeGaussian, just

+  // scale the sum by it.

+  const float kNormalizeGaussian = 0.564189584;  // 1 / sqrt(pi)

+  return (dot(xblur1, yblur1) + dot(xblur2, yblur2)) * kNormalizeGaussian;

 }

 

 void main() {

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
index 3d9ff46..fbcead7 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
@@ -15,6 +15,7 @@
 uniform vec4 u_clip_adjustment;

 uniform mat3 u_view_matrix;

 attribute vec2 a_position;

+attribute vec2 a_offset;

 attribute vec4 a_rcorner;

 varying vec2 v_offset;

 varying vec4 v_rcorner;

@@ -23,6 +24,6 @@
   vec3 pos2d = u_view_matrix * vec3(a_position, 1);

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

                      u_clip_adjustment.zw, 0, pos2d.z);

-  v_offset = a_position;

+  v_offset = a_offset;

   v_rcorner = a_rcorner;

 }

diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
index e1cb238..00dc1ee 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -27,6 +27,7 @@
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 #include "cobalt/renderer/backend/egl/render_target.h"
 #include "cobalt/renderer/rasterizer/common/find_node.h"
+#include "cobalt/renderer/rasterizer/egl/hardware_rasterizer.h"
 #include "cobalt/renderer/rasterizer/lib/exported/graphics.h"
 #include "cobalt/renderer/rasterizer/lib/exported/video.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
@@ -154,8 +155,12 @@
 
   backend::GraphicsContextEGL* graphics_context_;
 
+#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
+  egl::HardwareRasterizer hardware_rasterizer_;
+#else
   skia::HardwareRasterizer hardware_rasterizer_;
-  skia::HardwareRasterizer::Options options_;
+#endif
+  Rasterizer::Options options_;
 
   // The main offscreen render target to use when rendering UI or rectangular
   // video.
@@ -178,7 +183,7 @@
                                int skia_atlas_width, int skia_atlas_height,
                                int skia_cache_size_in_bytes,
                                int scratch_surface_cache_size_in_bytes,
-                               int surface_cache_size_in_bytes,
+                               int rasterizer_gpu_cache_size_in_bytes,
                                bool purge_skia_font_caches_on_destruction)
     : graphics_context_(
           base::polymorphic_downcast<backend::GraphicsContextEGL*>(
@@ -186,11 +191,12 @@
       hardware_rasterizer_(
           graphics_context, skia_atlas_width, skia_atlas_height,
           skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
-          surface_cache_size_in_bytes, purge_skia_font_caches_on_destruction),
+          rasterizer_gpu_cache_size_in_bytes,
+          purge_skia_font_caches_on_destruction),
       video_projection_type_(kCbLibVideoProjectionTypeNone),
       video_stereo_mode_(render_tree::StereoMode::kMono),
       video_texture_rgb_(0) {
-  options_.flags = skia::HardwareRasterizer::kSubmitFlags_Clear;
+  options_.flags = Rasterizer::kSubmitFlags_Clear;
   graphics_context_->MakeCurrent();
 
   // TODO: Import the correct size for this and any other textures from the lib
@@ -397,12 +403,14 @@
 ExternalRasterizer::ExternalRasterizer(
     backend::GraphicsContext* graphics_context, int skia_atlas_width,
     int skia_atlas_height, int skia_cache_size_in_bytes,
-    int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes,
+    int scratch_surface_cache_size_in_bytes,
+    int rasterizer_gpu_cache_size_in_bytes,
     bool purge_skia_font_caches_on_destruction)
     : impl_(new Impl(
           graphics_context, skia_atlas_width, skia_atlas_height,
           skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
-          surface_cache_size_in_bytes, purge_skia_font_caches_on_destruction)) {
+          rasterizer_gpu_cache_size_in_bytes,
+          purge_skia_font_caches_on_destruction)) {
 }
 
 ExternalRasterizer::~ExternalRasterizer() {}
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
index 9ad1fd0..d2f32e6 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
@@ -37,7 +37,7 @@
                      int skia_atlas_width, int skia_atlas_height,
                      int skia_cache_size_in_bytes,
                      int scratch_surface_cache_size_in_bytes,
-                     int surface_cache_size_in_bytes,
+                     int rasterizer_gpu_cache_size_in_bytes,
                      bool purge_skia_font_caches_on_destruction);
   virtual ~ExternalRasterizer();
 
diff --git a/src/cobalt/renderer/rasterizer/lib/lib.gyp b/src/cobalt/renderer/rasterizer/lib/lib.gyp
index 0b5a2c5..53a5e51 100644
--- a/src/cobalt/renderer/rasterizer/lib/lib.gyp
+++ b/src/cobalt/renderer/rasterizer/lib/lib.gyp
@@ -27,6 +27,7 @@
        'dependencies': [
          '<(DEPTH)/base/base.gyp:base',
          '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
+         '<(DEPTH)/cobalt/renderer/rasterizer/egl/rasterizer.gyp:hardware_rasterizer',
          '<(DEPTH)/cobalt/renderer/rasterizer/skia/common.gyp:common',
          '<(DEPTH)/cobalt/renderer/rasterizer/skia/rasterizer.gyp:hardware_rasterizer',
          '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
diff --git a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
index f98baf7..2b2ea4b 100644
--- a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
+++ b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
@@ -28,7 +28,11 @@
           options.skia_glyph_texture_atlas_dimensions.height(),
           options.skia_cache_size_in_bytes,
           options.scratch_surface_cache_size_in_bytes,
+#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
+          options.offscreen_target_cache_size_in_bytes,
+#else
           options.surface_cache_size_in_bytes,
+#endif
           options.purge_skia_font_caches_on_destruction));
 }
 }  // namespace
diff --git a/src/cobalt/renderer/submission_queue.cc b/src/cobalt/renderer/submission_queue.cc
index 20a1e39..b7b861e 100644
--- a/src/cobalt/renderer/submission_queue.cc
+++ b/src/cobalt/renderer/submission_queue.cc
@@ -89,8 +89,11 @@
         base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
             submission_queue_.front().render_tree.get());
 
-    if (animate_node->expiry() <= submission_time(now) &&
-        animate_node->expiry() <=
+    // Check the expiration of only animations that depend on the time
+    // parameter, since they are the only ones that will be affected by snapping
+    // time.
+    if (animate_node->depends_on_time_expiry() <= submission_time(now) &&
+        animate_node->depends_on_time_expiry() <=
             latest_to_submission_time + render_time(now)) {
       to_submission_time_in_ms_.SnapToTarget();
     }
diff --git a/src/cobalt/system_window/system_window.cc b/src/cobalt/system_window/system_window.cc
index 3a21efb..1e2c38f 100644
--- a/src/cobalt/system_window/system_window.cc
+++ b/src/cobalt/system_window/system_window.cc
@@ -211,51 +211,6 @@
   }
 }
 
-void OnDialogClose(SbSystemPlatformErrorResponse response, void* user_data) {
-  DCHECK(user_data);
-  SystemWindow* system_window = static_cast<SystemWindow*>(user_data);
-  system_window->HandleDialogClose(response);
-}
-
-void SystemWindow::ShowDialog(const SystemWindow::DialogOptions& options) {
-  SbSystemPlatformErrorType error_type =
-      kSbSystemPlatformErrorTypeConnectionError;
-  switch (options.message_code) {
-    case kDialogConnectionError:
-      error_type = kSbSystemPlatformErrorTypeConnectionError;
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-
-  SbSystemPlatformError handle =
-      SbSystemRaisePlatformError(error_type, OnDialogClose, this);
-  if (SbSystemPlatformErrorIsValid(handle)) {
-    current_dialog_callback_ = options.callback;
-  } else {
-    DLOG(WARNING) << "Failed to notify user of error: " << options.message_code;
-  }
-}
-
-void SystemWindow::HandleDialogClose(SbSystemPlatformErrorResponse response) {
-  DCHECK(!current_dialog_callback_.is_null());
-  switch (response) {
-    case kSbSystemPlatformErrorResponsePositive:
-      current_dialog_callback_.Run(kDialogPositiveResponse);
-      break;
-    case kSbSystemPlatformErrorResponseNegative:
-      current_dialog_callback_.Run(kDialogNegativeResponse);
-      break;
-    case kSbSystemPlatformErrorResponseCancel:
-      current_dialog_callback_.Run(kDialogCancelResponse);
-      break;
-    default:
-      DLOG(WARNING) << "Unrecognized dialog response: " << response;
-      break;
-  }
-}
-
 void HandleInputEvent(const SbEvent* event) {
   if (event->type != kSbEventTypeInput) {
     return;
diff --git a/src/cobalt/system_window/system_window.h b/src/cobalt/system_window/system_window.h
index b9f6017..121313c 100644
--- a/src/cobalt/system_window/system_window.h
+++ b/src/cobalt/system_window/system_window.h
@@ -15,17 +15,12 @@
 #ifndef COBALT_SYSTEM_WINDOW_SYSTEM_WINDOW_H_
 #define COBALT_SYSTEM_WINDOW_SYSTEM_WINDOW_H_
 
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
 #include "base/optional.h"
 #include "cobalt/base/event_dispatcher.h"
 #include "cobalt/math/size.h"
 #include "cobalt/system_window/input_event.h"
 #include "starboard/input.h"
 #include "starboard/key.h"
-#include "starboard/system.h"
 
 namespace cobalt {
 namespace system_window {
@@ -36,38 +31,10 @@
 // create a display render target for a graphics system.
 class SystemWindow {
  public:
-  // Enumeration of possible responses for the dialog callback.
-  enum DialogResponse {
-    kDialogPositiveResponse,
-    kDialogNegativeResponse,
-    kDialogCancelResponse
-  };
-
-  // Type of callback to run when user closes a dialog.
-  typedef base::Callback<void(DialogResponse response)> DialogCallback;
-
-  // Enumeration of possible message codes for a dialog.
-  enum DialogMessageCode {
-    kDialogConnectionError
-  };
-
-  // Options structure for dialog creation. It is expected that each platform
-  // will implement a modal dialog with possible support for:
-  // A message code specifying the text to be displayed, which should be
-  // localized according to the platform.
-  // A callback indicating the user's response: positive, negative or cancel.
-  struct DialogOptions {
-    DialogMessageCode message_code;
-    DialogCallback callback;
-  };
-
   SystemWindow(base::EventDispatcher* event_dispatcher,
                const base::optional<math::Size>& window_size);
   ~SystemWindow();
 
-  // Launches a system dialog.
-  void ShowDialog(const DialogOptions& options);
-
   // Returns the dimensions of the window.
   math::Size GetWindowSize() const;
 
@@ -92,9 +59,6 @@
   // Handles a single Starboard input event, dispatching any appropriate events.
   void HandleInputEvent(const SbInputData& data);
 
-  // Called when the user closes the dialog.
-  void HandleDialogClose(SbSystemPlatformErrorResponse response);
-
  private:
   void UpdateModifiers(SbKey key, bool pressed);
   InputEvent::Modifiers GetModifiers();
@@ -107,9 +71,6 @@
   SbWindow window_;
 
   bool key_down_;
-
-  // The current dialog callback. Only one dialog may be open at a time.
-  DialogCallback current_dialog_callback_;
 };
 
 }  // namespace system_window
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index c1774a4..4c2e893 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/nb/nb.gyp b/src/nb/nb.gyp
index 8c50a66..b33d946 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -60,6 +60,8 @@
             'scoped_ptr.h',
             'simple_thread.cc',
             'simple_thread.h',
+            'simple_profiler.cc',
+            'simple_profiler.h',
             'std_allocator.h',
             'string_interner.cc',
             'string_interner.h',
@@ -104,6 +106,7 @@
             'multipart_allocator_test.cc',
             'rewindable_vector_test.cc',
             'run_all_unittests.cc',
+            'simple_profiler_test.cc',
             'std_allocator_test.cc',
             'string_interner_test.cc',
             'test_thread.h',
diff --git a/src/nb/simple_profiler.cc b/src/nb/simple_profiler.cc
new file mode 100644
index 0000000..6b313aa
--- /dev/null
+++ b/src/nb/simple_profiler.cc
@@ -0,0 +1,187 @@
+/*

+ * Copyright 2017 Google Inc. All Rights Reserved.

+ *

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

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

+ * You may obtain a copy of the License at

+ *

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

+ *

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

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

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

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

+ * limitations under the License.

+ */

+

+#include <algorithm>

+#include <string>

+#include <vector>

+

+#include "nb/rewindable_vector.h"

+#include "nb/simple_profiler.h"

+#include "nb/thread_local_object.h"

+#include "starboard/atomic.h"

+#include "starboard/once.h"

+#include "starboard/string.h"

+#include "starboard/time.h"

+

+namespace nb {

+namespace {

+

+class SimpleProfilerManager {

+ public:

+  struct Entry {

+    Entry() : name(nullptr), time_delta(0), indent_value(0) {}

+    Entry(const char* n, SbTimeMonotonic dt, int ind)

+        : name(n), time_delta(dt), indent_value(ind) {}

+    Entry(const Entry& e) = default;

+    const char* name;

+    SbTimeMonotonic time_delta;

+    int indent_value;

+  };

+

+  using MessageHandlerFunction = SimpleProfiler::MessageHandlerFunction;

+  using ClockFunction = SimpleProfiler::ClockFunction;

+  SimpleProfilerManager()

+      : default_enabled_(true), message_handler_(nullptr),

+        clock_function_(nullptr) {}

+

+  int BeginInstance(const char* name) {

+    Data& d = ThreadLocal();

+    if (!d.enable_flag) {

+      return -1;

+    }

+    SbTimeMonotonic now = NowTime();

+    // SbAtomicMemoryBarrier() to Keep order of operations so clock doesn't

+    // get sampled out of order.

+    SbAtomicMemoryBarrier();

+    d.profiles.emplace_back(name, now, d.instance_count);

+    ++d.instance_count;

+    return d.profiles.size() - 1;

+  }

+

+  void Output(const Entry& entry, std::stringstream* sstream) {

+    for (auto i = 0; i < entry.indent_value; ++i) {

+      (*sstream) << ' ';

+    }

+    (*sstream) << entry.name << ": " << entry.time_delta << "us\n";

+  }

+

+  void FinishInstance(int index) {

+    Data& d = ThreadLocal();

+    if (!d.enable_flag || index < 0) {

+      return;

+    }

+

+    --d.instance_count;

+    Entry& entry = d.profiles[static_cast<size_t>(index)];

+    entry.time_delta = NowTime() - entry.time_delta;

+    // SbAtomicMemoryBarrier() to Keep order of operations so clock doesn't

+    // get sampled out of order.

+    SbAtomicMemoryBarrier();

+

+    if (d.instance_count == 0) {

+      std::stringstream ss;

+      for (auto it = d.profiles.begin(); it != d.profiles.end(); ++it) {

+        Output(*it, &ss);

+      }

+      d.profiles.clear();

+      HandleMessage(ss.str());

+    }

+  }

+  bool ThreadLocalEnabled() { return ThreadLocal().enable_flag; }

+  void SetThreadLocalEnabled(bool value) {

+    Data& d = ThreadLocal();

+    d.enable_flag = value;

+    d.enable_flag_set = true;

+  }

+

+  bool IsEnabled() const {

+    const Data& d = ThreadLocal();

+    if (d.enable_flag_set) {

+      return d.enable_flag;

+    }

+    return default_enabled_;

+  }

+  void SetDefaultEnabled(bool value) { default_enabled_ = value; }

+

+  void SetMessageHandler(MessageHandlerFunction handler) {

+    message_handler_ = handler;

+  }

+

+  void SetClockFunction(ClockFunction fcn) {

+    clock_function_ = fcn;

+  }

+

+  SbTimeMonotonic NowTime() {

+    if (!clock_function_) {

+      return SbTimeGetMonotonicNow();

+    } else {

+      return clock_function_();

+    }

+  }

+

+ private:

+  struct Data {

+    bool enable_flag = true;

+    bool enable_flag_set = false;

+    int instance_count = 0;

+    std::vector<Entry> profiles;

+  };

+

+  void HandleMessage(const std::string& str) {

+    if (message_handler_) {

+      message_handler_(str.c_str());

+    } else {

+      SbLogRaw(str.c_str());

+    }

+  }

+

+  Data& ThreadLocal() { return *thread_local_.GetOrCreate(); }

+  const Data& ThreadLocal() const { return *thread_local_.GetOrCreate(); }

+  mutable nb::ThreadLocalObject<Data> thread_local_;

+  bool default_enabled_;

+  MessageHandlerFunction message_handler_;

+  ClockFunction clock_function_;

+};

+

+SB_ONCE_INITIALIZE_FUNCTION(SimpleProfilerManager, GetSimpleProfilerManager);

+

+}  // namespace

+

+SimpleProfiler::EnableScope::EnableScope(bool enabled) {

+  SimpleProfilerManager* mgr = GetSimpleProfilerManager();

+  prev_enabled_ = mgr->ThreadLocalEnabled();

+  mgr->SetThreadLocalEnabled(enabled);

+}

+

+SimpleProfiler::EnableScope::~EnableScope() {

+  GetSimpleProfilerManager()->SetThreadLocalEnabled(prev_enabled_);

+}

+

+SimpleProfiler::SimpleProfiler(const char* name) {

+  momento_index_ = GetSimpleProfilerManager()->BeginInstance(name);

+}

+

+SimpleProfiler::~SimpleProfiler() {

+  GetSimpleProfilerManager()->FinishInstance(momento_index_);

+}

+

+void SimpleProfiler::SetEnabledByDefault(bool value) {

+  GetSimpleProfilerManager()->SetDefaultEnabled(value);

+}

+

+bool SimpleProfiler::IsEnabled() {

+  return GetSimpleProfilerManager()->IsEnabled();

+}

+

+void SimpleProfiler::SetLoggingFunction(MessageHandlerFunction fcn) {

+  GetSimpleProfilerManager()->SetMessageHandler(fcn);

+}

+

+void SimpleProfiler::SetClockFunction(ClockFunction fcn) {

+  GetSimpleProfilerManager()->SetClockFunction(fcn);

+}

+

+}  // namespace nb

diff --git a/src/nb/simple_profiler.h b/src/nb/simple_profiler.h
new file mode 100644
index 0000000..2c9e4f4
--- /dev/null
+++ b/src/nb/simple_profiler.h
@@ -0,0 +1,105 @@
+/*

+ * Copyright 2017 Google Inc. All Rights Reserved.

+ *

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

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

+ * You may obtain a copy of the License at

+ *

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

+ *

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

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

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

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

+ * limitations under the License.

+ */

+

+#ifndef NB_SIMPLE_PROFILER_H_

+#define NB_SIMPLE_PROFILER_H_

+

+#include <string>

+

+#include "starboard/time.h"

+#include "starboard/types.h"

+

+namespace nb {

+

+// SimpleProfiler is useful for development. It will allow the developer

+// to track where the CPU spends most of it's time in a call chain.

+// CPU time is tracked via thread-local-storage. When the last SimpleProfiler

+// in the thread is destroyed then a printout of the timing will be printed

+// to the output log.

+//

+// By default, SimpleProfiler will generate output whenever it's used. To

+// selectively profile a thread see class SimpleProfiler::EnableScope.

+//

+// Example:

+//  void Foo() {

+//    SimpleProfiler profile(__FUNCTION__);

+//    Bar();

+//    Baz();

+//  }

+//  void Bar() {

+//    SimpleProfiler profile(__FUNCTION__);

+//    // ... do something expensive ...

+//    Qux();

+//  }

+//  void Baz() {

+//    SimpleProfiler profile(__FUNCTION__);

+//    // ... do something cheap ...

+//  }

+//  void Qux() {

+//    SimpleProfiler profile(__FUNCTION__);

+//    // ... do something nearly free ...

+//  }

+//

+//  Outputs: "Foo: 25us\n"

+//           " Bar: 20us\n"

+//           "  Qux: 1us\n"

+//           " Baz: 4us\n"

+class SimpleProfiler {

+ public:

+  explicit SimpleProfiler(const char* name);

+  ~SimpleProfiler();

+  // EnableScope can be used to enable and disable SimpleProfiler in the

+  // thread. A scoped object is used so that SimpleProfiler

+  // constructor / destructor order is maintained in relation to

+  // enabling / disabling.

+  // Example:

+  //  // Assume SimpleProfiler was globally disabled by default.

+  //  void Foo() {

+  //     SimpleProfiler::ThreadScope enable_scope(true);  // enabled in scope.

+  //     SimpleProfiler profile(__FUNCTION__);

+  //     DoWork();

+  //  }

+  class EnableScope {

+   public:

+    explicit EnableScope(bool enabled);

+    ~EnableScope();

+   private:

+    bool prev_enabled_;

+  };

+  // If SetThreadLocalEnableFlag() isn't explicitly set by the thread

+  // then this |input| value will control whether the SimpleProfiler

+  // is active or not. For best results, set this value as early as

+  // possible during program execution.

+  static void SetEnabledByDefault(bool value);

+  // Is SimpleProfiler enabled? If Get/SetThreadLocalEnableFlag() isn't

+  // set then this will return an enabled by default flag, which defaults

+  // to true.

+  static bool IsEnabled();

+  typedef void (*MessageHandlerFunction)(const char* msg);

+  // Useful for tests. Setting to nullptr will reset the behavior to

+  // default functionality.

+  static void SetLoggingFunction(MessageHandlerFunction fcn);

+  // Useful for tests. Setting to nullptr will reset the behavior to

+  // default functionality.

+  typedef SbTimeMonotonic (*ClockFunction)();

+  static void SetClockFunction(ClockFunction fcn);

+ private:

+  int momento_index_;

+};

+

+}  // namespace nb

+

+#endif  // NB_SIMPLE_PROFILER_H_

diff --git a/src/nb/simple_profiler_test.cc b/src/nb/simple_profiler_test.cc
new file mode 100644
index 0000000..41b5cee
--- /dev/null
+++ b/src/nb/simple_profiler_test.cc
@@ -0,0 +1,95 @@
+/*

+ * Copyright 2017 Google Inc. All Rights Reserved.

+ *

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

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

+ * You may obtain a copy of the License at

+ *

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

+ *

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

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

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

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

+ * limitations under the License.

+ */

+

+#include "nb/simple_profiler.h"

+

+#include <map>

+#include <string>

+#include <vector>

+

+#include "starboard/configuration.h"

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

+

+namespace nb {

+class SimpleProfilerTest : public ::testing::Test {

+ public:

+  SimpleProfilerTest() {}

+  virtual void SetUp() SB_OVERRIDE {

+    s_log_buffer_.clear();

+    SimpleProfiler::SetLoggingFunction(TestLogFunction);

+  }

+  virtual void TearDown() SB_OVERRIDE {

+    SimpleProfiler::SetLoggingFunction(nullptr);

+    SimpleProfiler::SetClockFunction(nullptr);

+    s_log_buffer_.clear();

+  }

+

+  std::string GetProfilerOutput() { return s_log_buffer_; }

+

+ private:

+  static std::string s_log_buffer_;

+  static void TestLogFunction(const char* value) { s_log_buffer_ = value; }

+};

+std::string SimpleProfilerTest::s_log_buffer_;

+

+struct CallChain {

+  static void Foo() {

+    SimpleProfiler profile("Foo");

+    Bar();

+    Qux();

+  }

+

+  static void Bar() {

+    SimpleProfiler profile("Bar");

+    Baz();

+  }

+

+  static void Baz() { SimpleProfiler profile("Baz"); }

+  static void Qux() { SimpleProfiler profile("Qux"); }

+};

+

+TEST_F(SimpleProfilerTest, IsEnabledByDefault) {

+  EXPECT_TRUE(SimpleProfiler::IsEnabled());

+}

+

+SbTimeMonotonic NullTime() {

+  return SbTimeMonotonic(0);

+}

+// Tests the expectation that SimpleProfiler can be used in a call

+// chain and will generate the expected string.

+TEST_F(SimpleProfilerTest, CallChain) {

+  SimpleProfiler::SetClockFunction(NullTime);

+  CallChain::Foo();

+  std::string profiler_log = GetProfilerOutput();

+

+  std::string expected_output =

+      "Foo: 0us\n"

+      " Bar: 0us\n"

+      "  Baz: 0us\n"

+      " Qux: 0us\n";

+

+  EXPECT_EQ(expected_output, profiler_log) << " actual output:\n"

+                                           << profiler_log;

+}

+

+TEST_F(SimpleProfilerTest, EnableScopeDisabled) {

+  SimpleProfiler::EnableScope enable(false);

+  CallChain::Foo();

+  std::string profiler_log = GetProfilerOutput();

+  EXPECT_TRUE(profiler_log.empty());

+}

+

+}  // namespace nb

diff --git a/src/net/dial/dial_http_server.cc b/src/net/dial/dial_http_server.cc
index a1a44c4..482d27d 100644
--- a/src/net/dial/dial_http_server.cc
+++ b/src/net/dial/dial_http_server.cc
@@ -76,9 +76,6 @@
   // get the port information
   int ret = http_server_->GetLocalAddress(addr);
 
-#if defined(OS_STARBOARD)
-
-#if SB_API_VERSION >= 4
   if (ret != 0) {
     return ERR_FAILED;
   }
@@ -101,24 +98,6 @@
   }
 
   return ERR_FAILED;
-#else
-  SbSocketAddress address;
-  ret |= SbSocketGetLocalInterfaceAddress(&address) ? 0 : -1;
-  address.port = addr->port();
-  return (ret == 0 && addr->FromSbSocketAddress(&address)) ? OK : ERR_FAILED;
-#endif  // SB_API_VERSION >= 4
-
-#else
-  // Now get the IPAddress of the network card.
-  SockaddrStorage sock_addr;
-  struct sockaddr_in *in = (struct sockaddr_in *)sock_addr.addr;
-  ret |= lb_get_local_ip_address(&in->sin_addr);
-  in->sin_family = AF_INET;
-  in->sin_port = htons(addr->port());
-
-  return (ret == 0 && addr->FromSockAddr(sock_addr.addr, sock_addr.addr_len))
-      ? OK : ERR_FAILED;
-#endif
 }
 
 void DialHttpServer::OnHttpRequest(int conn_id,
diff --git a/src/starboard/linux/x64x11/skia/atomic_public.h b/src/starboard/linux/x64x11/skia/atomic_public.h
new file mode 100644
index 0000000..048b3c9
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/atomic_public.h
@@ -0,0 +1,20 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_LINUX_X64X11_SKIA_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SKIA_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_SKIA_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/skia/configuration_public.h b/src/starboard/linux/x64x11/skia/configuration_public.h
new file mode 100644
index 0000000..ad03114
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/configuration_public.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Desktop X86 Linux configured for the future
+// starboard API.
+
+#ifndef STARBOARD_LINUX_X64X11_SKIA_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SKIA_CONFIGURATION_PUBLIC_H_
+
+// This is not a released configuration, so it should implement the
+// experimental API version to validate trunk's viability.
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Include the X64X11 Linux configuration.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_SKIA_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/skia/gyp_configuration.gypi b/src/starboard/linux/x64x11/skia/gyp_configuration.gypi
new file mode 100644
index 0000000..06b67cc
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/gyp_configuration.gypi
@@ -0,0 +1,41 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    # Use the skia hardware rasterizer.
+    'rasterizer_type': 'hardware',
+  },
+  'target_defaults': {
+    'default_configuration': 'linux-x64x11-skia_debug',
+    'configurations': {
+      'linux-x64x11-skia_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'linux-x64x11-skia_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'linux-x64x11-skia_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'linux-x64x11-skia_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '../gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/skia/gyp_configuration.py b/src/starboard/linux/x64x11/skia/gyp_configuration.py
new file mode 100644
index 0000000..45b4461
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/gyp_configuration.py
@@ -0,0 +1,27 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
+
+import logging
+
+# Import the shared Linux platform configuration.
+from starboard.linux.shared import gyp_configuration
+
+
+def CreatePlatformConfig():
+  try:
+    return gyp_configuration.PlatformConfig('linux-x64x11-skia')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
diff --git a/src/starboard/linux/x64x11/skia/starboard_platform.gyp b/src/starboard/linux/x64x11/skia/starboard_platform.gyp
new file mode 100644
index 0000000..528d1cd
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/starboard_platform.gyp
@@ -0,0 +1,33 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../starboard_platform.gypi'
+  ],
+  'targets': [
+    {
+      'target_name': 'starboard_platform',
+      'type': 'static_library',
+      'sources': ['<@(starboard_platform_sources)'],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<@(starboard_platform_dependencies)',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/x64x11/skia/thread_types_public.h b/src/starboard/linux/x64x11/skia/thread_types_public.h
new file mode 100644
index 0000000..5e74da8
--- /dev/null
+++ b/src/starboard/linux/x64x11/skia/thread_types_public.h
@@ -0,0 +1,20 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_LINUX_X64X11_SKIA_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SKIA_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_SKIA_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/nplb/audio_sink_helpers.cc b/src/starboard/nplb/audio_sink_helpers.cc
index 981c0e1..51aa218 100644
--- a/src/starboard/nplb/audio_sink_helpers.cc
+++ b/src/starboard/nplb/audio_sink_helpers.cc
@@ -151,7 +151,7 @@
 bool AudioSinkTestEnvironment::WaitUntilAllFramesAreConsumed() {
   ScopedLock lock(mutex_);
   SbTimeMonotonic start = SbTimeGetMonotonicNow();
-  while (frames_appended_ == frames_consumed_) {
+  while (frames_appended_ != frames_consumed_) {
     SbTime time_elapsed = SbTimeGetMonotonicNow() - start;
     if (time_elapsed >= kTimeToTry) {
       return false;
diff --git a/src/starboard/nplb/system_get_total_gpu_memory_test.cc b/src/starboard/nplb/system_get_total_gpu_memory_test.cc
index 0cd7d9b..c39814c 100644
--- a/src/starboard/nplb/system_get_total_gpu_memory_test.cc
+++ b/src/starboard/nplb/system_get_total_gpu_memory_test.cc
@@ -23,7 +23,7 @@
   if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
     // If we claim to have GPU memory reporting capabilities, then this value
     // should be larger than 0.
-    EXPECT_LT(0, SbSystemGetTotalCPUMemory());
+    EXPECT_LT(0, SbSystemGetTotalGPUMemory());
   }
 }
 
diff --git a/src/starboard/raspi/2/skia/atomic_public.h b/src/starboard/raspi/2/skia/atomic_public.h
new file mode 100644
index 0000000..dca0903
--- /dev/null
+++ b/src/starboard/raspi/2/skia/atomic_public.h
@@ -0,0 +1,20 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_RASPI_2_SKIA_ATOMIC_PUBLIC_H_
+#define STARBOARD_RASPI_2_SKIA_ATOMIC_PUBLIC_H_
+
+#include "starboard/raspi/2/atomic_public.h"
+
+#endif  // STARBOARD_RASPI_2_SKIA_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/raspi/2/skia/configuration_public.h b/src/starboard/raspi/2/skia/configuration_public.h
new file mode 100644
index 0000000..6c65ff5
--- /dev/null
+++ b/src/starboard/raspi/2/skia/configuration_public.h
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Raspberry PI 2 Raspbian.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_RASPI_2_SKIA_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_RASPI_2_SKIA_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/raspi/2/configuration_public.h"
+
+#endif  // STARBOARD_RASPI_2_SKIA_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/2/skia/gyp_configuration.gypi b/src/starboard/raspi/2/skia/gyp_configuration.gypi
new file mode 100644
index 0000000..21c8ea4
--- /dev/null
+++ b/src/starboard/raspi/2/skia/gyp_configuration.gypi
@@ -0,0 +1,43 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'variables': {
+    # Use the skia hardware rasterizer.
+    'rasterizer_type': 'hardware',
+  },
+
+  'target_defaults': {
+    'default_configuration': 'raspi-2-skia_debug',
+    'configurations': {
+      'raspi-2-skia_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'raspi-2-skia_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'raspi-2-skia_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'raspi-2-skia_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '../architecture.gypi',
+    '../../shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/raspi/2/skia/gyp_configuration.py b/src/starboard/raspi/2/skia/gyp_configuration.py
new file mode 100644
index 0000000..ccf358f
--- /dev/null
+++ b/src/starboard/raspi/2/skia/gyp_configuration.py
@@ -0,0 +1,32 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, os.path.join(_SCRIPT_DIR, '../..'))
+
+# pylint: disable=g-import-not-at-top
+from shared.gyp_configuration import RaspiPlatformConfig
+
+
+def CreatePlatformConfig():
+  try:
+    return RaspiPlatformConfig('raspi-2-skia')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
diff --git a/src/starboard/raspi/2/skia/starboard_platform.gyp b/src/starboard/raspi/2/skia/starboard_platform.gyp
new file mode 100644
index 0000000..a304ac6
--- /dev/null
+++ b/src/starboard/raspi/2/skia/starboard_platform.gyp
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+  'includes': [
+    '../../shared/starboard_platform.gypi',
+  ],
+}
diff --git a/src/starboard/raspi/2/skia/thread_types_public.h b/src/starboard/raspi/2/skia/thread_types_public.h
new file mode 100644
index 0000000..98cf1fb
--- /dev/null
+++ b/src/starboard/raspi/2/skia/thread_types_public.h
@@ -0,0 +1,20 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_RASPI_2_SKIA_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_RASPI_2_SKIA_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/raspi/2/thread_types_public.h"
+
+#endif  // STARBOARD_RASPI_2_SKIA_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index 2ad5415..c219c59 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -86,6 +86,8 @@
       audio_sink_(kSbAudioSinkInvalid),
       can_accept_more_data_(true),
       process_audio_data_scheduled_(false),
+      process_audio_data_closure_(
+          Bind(&AudioRendererImpl::ProcessAudioData, this)),
       decoder_needs_full_reset_(false),
       audio_frame_tracker_(audio_frame_tracker.Pass()) {
   SB_DCHECK(decoder_ != NULL);
@@ -177,6 +179,12 @@
     // TODO: Remove SetPlaybackRate() support from audio sink as it only need to
     // support play/pause.
     audio_sink_->SetPlaybackRate(playback_rate_ > 0.0 ? 1.0 : 0.0);
+    if (playback_rate_ > 0.0) {
+      if (process_audio_data_scheduled_) {
+        Remove(process_audio_data_closure_);
+      }
+      ProcessAudioData();
+    }
   }
 }
 
@@ -353,9 +361,7 @@
   ++pending_decoder_outputs_;
 
   if (process_audio_data_scheduled_) {
-    // A ProcessAudioData() callback has been scheduled and we should let it
-    // process the output.
-    return;
+    Remove(process_audio_data_closure_);
   }
 
   process_audio_data_scheduled_ = true;
@@ -381,7 +387,7 @@
       const SbTimeMonotonic delay = kMaxCachedFrames * kSbTimeSecond /
                                     decoder_->GetSamplesPerSecond() / 4;
       process_audio_data_scheduled_ = true;
-      Schedule(Bind(&AudioRendererImpl::ProcessAudioData, this), delay);
+      Schedule(process_audio_data_closure_, delay);
       return;
     }
 
@@ -419,6 +425,12 @@
     }
   }
 
+  if (seeking_.load() || playback_rate_ == 0.0) {
+    process_audio_data_scheduled_ = true;
+    Schedule(process_audio_data_closure_, 5 * kSbTimeMillisecond);
+    return;
+  }
+
   int64_t frames_in_buffer =
       frames_sent_to_sink_.load() - frames_consumed_by_sink_.load();
   if (kMaxCachedFrames - frames_in_buffer < kFrameAppendUnit &&
@@ -431,13 +443,21 @@
       delay = frames_to_delay * kSbTimeSecond / decoder_->GetSamplesPerSecond();
     }
     process_audio_data_scheduled_ = true;
-    Schedule(Bind(&AudioRendererImpl::ProcessAudioData, this), delay);
+    Schedule(process_audio_data_closure_, delay);
   }
 }
 
 bool AudioRendererImpl::AppendAudioToFrameBuffer() {
   SB_DCHECK(BelongsToCurrentThread());
 
+  if (seeking_.load() && time_stretcher_.IsQueueFull()) {
+    seeking_.store(false);
+  }
+
+  if (seeking_.load() || playback_rate_ == 0.0) {
+    return false;
+  }
+
   int frames_in_buffer = static_cast<int>(frames_sent_to_sink_.load() -
                                           frames_consumed_by_sink_.load());
 
@@ -447,18 +467,13 @@
 
   int offset_to_append = frames_sent_to_sink_.load() % kMaxCachedFrames;
 
-  // When |playback_rate_| is 0, try to fill the buffer with playback rate as 1.
-  // Otherwise the preroll will never finish.
-  float playback_rate_to_fill =
-      playback_rate_ == 0.0 ? 1.f : static_cast<float>(playback_rate_);
   scoped_refptr<DecodedAudio> decoded_audio =
-      time_stretcher_.Read(kFrameAppendUnit, playback_rate_to_fill);
+      time_stretcher_.Read(kFrameAppendUnit, playback_rate_);
   SB_DCHECK(decoded_audio);
   if (decoded_audio->frames() == 0 && eos_state_.load() == kEOSDecoded) {
     eos_state_.store(kEOSSentToSink);
   }
-  audio_frame_tracker_->AddFrames(decoded_audio->frames(),
-                                  playback_rate_to_fill);
+  audio_frame_tracker_->AddFrames(decoded_audio->frames(), playback_rate_);
   // TODO: Support kSbMediaAudioFrameStorageTypePlanar.
   decoded_audio->SwitchFormatTo(sink_sample_type_,
                                 kSbMediaAudioFrameStorageTypeInterleaved);
@@ -482,12 +497,6 @@
 
   frames_sent_to_sink_.fetch_add(frames_appended);
 
-  int64_t preroll_frames =
-      decoder_->GetSamplesPerSecond() * kPrerollTime / kSbTimeSecond;
-  if (seeking_.load() && frames_sent_to_sink_.load() > preroll_frames) {
-    seeking_.store(false);
-  }
-
   return frames_appended > 0;
 }
 
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index f1b7730..cd36913 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -44,10 +44,6 @@
 // |AudioDecoder| interface, rather than a platform specific implementation.
 class AudioRendererImpl : public AudioRenderer, private JobQueue::JobOwner {
  public:
-  // Preroll is considered as finished after either the amount of audio caches
-  // exceeds kPrerollTime or if EOS is reached.
-  static const size_t kPrerollTime = kSbTimeSecond / 4;
-
   AudioRendererImpl(
       scoped_ptr<AudioDecoder> decoder,
       const SbMediaAudioHeader& audio_header,
@@ -146,6 +142,7 @@
 
   bool can_accept_more_data_;
   bool process_audio_data_scheduled_;
+  Closure process_audio_data_closure_;
 
   // Our owner will attempt to seek to pts 0 when playback begins.  In
   // general, seeking could require a full reset of the underlying decoder on
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc
index 517c3ef..4037ee2 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc
@@ -185,19 +185,16 @@
   const int kFramesPerBuffer = 1024;
 
   int frames_written = 0;
-  int preroll_frames = kDefaultSamplesPerSecond *
-                       AudioRendererImpl::kPrerollTime / kSbTimeSecond;
 
-  while (frames_written <= preroll_frames) {
-    SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+  while (audio_renderer_->IsSeekingInProgress()) {
+    SbMediaTime pts =
+        frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
     WriteSample(CreateInputBuffer(pts));
     CallConsumedCB();
     SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer));
     frames_written += kFramesPerBuffer;
   }
 
-  EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
-
   WriteEndOfStream();
 
   EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
@@ -210,9 +207,11 @@
   SbMediaTime media_time = audio_renderer_->GetCurrentTime();
 
   while (!audio_renderer_->IsEndOfStreamPlayed()) {
-    SbThreadSleep(AudioRendererImpl::kPrerollTime);
+    SbThreadSleep(kSbTimeMillisecond);
     SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
-    EXPECT_GT(new_media_time, media_time);
+    // TODO: Replace it with EXPECT_GT once audio time reporting is more
+    //       accurate.
+    EXPECT_GE(new_media_time, media_time);
     media_time = new_media_time;
   }
 }
@@ -229,24 +228,16 @@
   const int kFramesPerBuffer = 1024;
 
   int frames_written = 0;
-  int preroll_frames = kDefaultSamplesPerSecond *
-                       AudioRendererImpl::kPrerollTime / kSbTimeSecond *
-                       kPlaybackRate * 2;
 
-  while (frames_written <= preroll_frames) {
-    SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+  while (audio_renderer_->IsSeekingInProgress()) {
+    SbMediaTime pts =
+        frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
     WriteSample(CreateInputBuffer(pts));
     CallConsumedCB();
     SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer));
     frames_written += kFramesPerBuffer;
-
-    if (!audio_renderer_->IsSeekingInProgress()) {
-      break;
-    }
   }
 
-  EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
-
   WriteEndOfStream();
 
   EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
@@ -259,9 +250,11 @@
   SbMediaTime media_time = audio_renderer_->GetCurrentTime();
 
   while (!audio_renderer_->IsEndOfStreamPlayed()) {
-    SbThreadSleep(AudioRendererImpl::kPrerollTime);
+    SbThreadSleep(kSbTimeMillisecond);
     SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
-    EXPECT_GT(new_media_time, media_time);
+    // TODO: Replace it with EXPECT_GT once audio time reporting is more
+    //       accurate.
+    EXPECT_GE(new_media_time, media_time);
     media_time = new_media_time;
   }
 }
@@ -271,31 +264,30 @@
 
   const int kFramesPerBuffer = 1024;
 
-  int frames_written = 0;
-  int preroll_frames = kDefaultSamplesPerSecond *
-                       AudioRendererImpl::kPrerollTime / kSbTimeSecond;
-
   audio_renderer_->Play();
 
-  while (frames_written <= preroll_frames) {
-    SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+  int frames_written = 0;
+
+  while (audio_renderer_->IsSeekingInProgress()) {
+    SbMediaTime pts =
+        frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
     WriteSample(CreateInputBuffer(pts));
     CallConsumedCB();
     SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer));
     frames_written += kFramesPerBuffer;
   }
 
-  EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
-
   WriteEndOfStream();
   SendDecoderOutput(new DecodedAudio);
 
   SbMediaTime media_time = audio_renderer_->GetCurrentTime();
 
   while (!audio_renderer_->IsEndOfStreamPlayed()) {
-    SbThreadSleep(AudioRendererImpl::kPrerollTime);
+    SbThreadSleep(kSbTimeMillisecond);
     SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
-    EXPECT_GT(new_media_time, media_time);
+    // TODO: Replace it with EXPECT_GT once audio time reporting is more
+    //       accurate.
+    EXPECT_GE(new_media_time, media_time);
     media_time = new_media_time;
   }
 }
@@ -354,22 +346,19 @@
   const int kFramesPerBuffer = 1024;
 
   int frames_written = 0;
-  int preroll_frames = kDefaultSamplesPerSecond *
-                       AudioRendererImpl::kPrerollTime / kSbTimeSecond;
 
-  while (frames_written <= preroll_frames) {
-    SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+  while (audio_renderer_->IsSeekingInProgress()) {
+    SbMediaTime pts =
+        frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
     WriteSample(CreateInputBuffer(pts));
     CallConsumedCB();
     SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer / 2));
     frames_written += kFramesPerBuffer / 2;
-    pts = frames_written / kDefaultSamplesPerSecond;
+    pts = frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
     SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer / 2));
     frames_written += kFramesPerBuffer / 2;
   }
 
-  EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
-
   WriteEndOfStream();
 
   EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
@@ -382,9 +371,11 @@
   SbMediaTime media_time = audio_renderer_->GetCurrentTime();
 
   while (!audio_renderer_->IsEndOfStreamPlayed()) {
-    SbThreadSleep(AudioRendererImpl::kPrerollTime);
+    SbThreadSleep(kSbTimeMillisecond);
     SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
-    EXPECT_GT(new_media_time, media_time);
+    // TODO: Replace it with EXPECT_GT once audio time reporting is more
+    //       accurate.
+    EXPECT_GE(new_media_time, media_time);
     media_time = new_media_time;
   }
 }
@@ -395,20 +386,21 @@
   const int kFramesPerBuffer = 1024;
 
   int frames_written = 0;
-  int preroll_frames = kDefaultSamplesPerSecond *
-                       AudioRendererImpl::kPrerollTime / kSbTimeSecond;
 
-  while (frames_written <= preroll_frames) {
-    SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+  while (audio_renderer_->IsSeekingInProgress()) {
+    SbMediaTime pts =
+        frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
+    SbMediaTime output_pts = pts;
     WriteSample(CreateInputBuffer(pts));
     CallConsumedCB();
-    frames_written += kFramesPerBuffer;
+    frames_written += kFramesPerBuffer / 2;
+    pts = frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond;
+    WriteSample(CreateInputBuffer(pts));
+    CallConsumedCB();
+    frames_written += kFramesPerBuffer / 2;
+    SendDecoderOutput(CreateDecodedAudio(output_pts, kFramesPerBuffer));
   }
 
-  SendDecoderOutput(CreateDecodedAudio(0, frames_written));
-
-  EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
-
   WriteEndOfStream();
 
   EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
@@ -421,13 +413,16 @@
   SbMediaTime media_time = audio_renderer_->GetCurrentTime();
 
   while (!audio_renderer_->IsEndOfStreamPlayed()) {
-    SbThreadSleep(AudioRendererImpl::kPrerollTime);
+    SbThreadSleep(kSbTimeMillisecond);
     SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
-    EXPECT_GT(new_media_time, media_time);
+    // TODO: Replace it with EXPECT_GT once audio time reporting is more
+    //       accurate.
+    EXPECT_GE(new_media_time, media_time);
     media_time = new_media_time;
   }
 }
 
+// TODO: Implement test for Seek()
 TEST_F(AudioRendererImplTest, Seek) {}
 
 }  // namespace
diff --git a/src/starboard/shared/starboard/player/filter/wsola_internal.cc b/src/starboard/shared/starboard/player/filter/wsola_internal.cc
index f1f32f5..3742d0a 100644
--- a/src/starboard/shared/starboard/player/filter/wsola_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/wsola_internal.cc
@@ -33,6 +33,12 @@
 #include "starboard/memory.h"
 #include "starboard/shared/internal_only.h"
 
+// TODO: Detect Neon on ARM platform and enable SIMD.
+#if SB_IS(ARCH_X86)
+#define USE_SIMD 1
+#include <xmmintrin.h>
+#endif  // SB_IS(ARCH_X86)
+
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -68,9 +74,53 @@
   SB_DCHECK(frame_offset_a + num_frames <= a->frames());
   SB_DCHECK(frame_offset_b + num_frames <= b->frames());
 
-  SbMemorySet(dot_product, 0, sizeof(*dot_product) * a->channels());
   const float* a_frames = reinterpret_cast<const float*>(a->buffer());
   const float* b_frames = reinterpret_cast<const float*>(b->buffer());
+
+// SIMD optimized variants can provide a massive speedup to this operation.
+#if defined(USE_SIMD)
+  const int rem = num_frames % 4;
+  const int last_index = num_frames - rem;
+  const int channels = a->channels();
+  for (int ch = 0; ch < channels; ++ch) {
+    const float* a_src = a_frames + frame_offset_a * a->channels() + ch;
+    const float* b_src = b_frames + frame_offset_b * b->channels() + ch;
+
+#if SB_IS(ARCH_X86)
+    // First sum all components.
+    __m128 m_sum = _mm_setzero_ps();
+    for (int s = 0; s < last_index; s += 4) {
+      m_sum = _mm_add_ps(
+          m_sum, _mm_mul_ps(_mm_loadu_ps(a_src + s), _mm_loadu_ps(b_src + s)));
+    }
+
+    // Reduce to a single float for this channel. Sadly, SSE1,2 doesn't have a
+    // horizontal sum function, so we have to condense manually.
+    m_sum = _mm_add_ps(_mm_movehl_ps(m_sum, m_sum), m_sum);
+    _mm_store_ss(dot_product + ch,
+                 _mm_add_ss(m_sum, _mm_shuffle_ps(m_sum, m_sum, 1)));
+#elif SB_IS(ARCH_ARM)
+    // First sum all components.
+    float32x4_t m_sum = vmovq_n_f32(0);
+    for (int s = 0; s < last_index; s += 4)
+      m_sum = vmlaq_f32(m_sum, vld1q_f32(a_src + s), vld1q_f32(b_src + s));
+
+    // Reduce to a single float for this channel.
+    float32x2_t m_half = vadd_f32(vget_high_f32(m_sum), vget_low_f32(m_sum));
+    dot_product[ch] = vget_lane_f32(vpadd_f32(m_half, m_half), 0);
+#endif  // SB_IS(ARCH_X86)
+  }
+
+  if (!rem) {
+    return;
+  }
+  num_frames = rem;
+  frame_offset_a += last_index;
+  frame_offset_b += last_index;
+#else   // defined(USE_SIMD)
+  SbMemorySet(dot_product, 0, sizeof(*dot_product) * a->channels());
+#endif  // defined(USE_SIMD)
+
   for (int k = 0; k < a->channels(); ++k) {
     const float* ch_a = a_frames + frame_offset_a * a->channels() + k;
     const float* ch_b = b_frames + frame_offset_b * b->channels() + k;
diff --git a/src/starboard/shared/starboard/player/job_queue.h b/src/starboard/shared/starboard/player/job_queue.h
index 361d82e..53b7576 100644
--- a/src/starboard/shared/starboard/player/job_queue.h
+++ b/src/starboard/shared/starboard/player/job_queue.h
@@ -53,6 +53,7 @@
     void Schedule(Closure closure, SbTimeMonotonic delay = 0) {
       job_queue_->Schedule(closure, this, delay);
     }
+    void Remove(Closure closure) { job_queue_->Remove(closure); }
     void CancelPendingJobs() { job_queue_->RemoveJobsByToken(this); }
 
    private:
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
index 6ebcda9..acc7057 100644
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -26,9 +26,11 @@
 
 #include "starboard/event.h"
 #include "starboard/log.h"
+#include "starboard/mutex.h"
 #include "starboard/shared/starboard/application.h"
 #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
+#include "starboard/shared/uwp/async_utils.h"
 #include "starboard/shared/uwp/window_internal.h"
 #include "starboard/shared/win32/thread_private.h"
 #include "starboard/shared/win32/wchar_utils.h"
@@ -42,6 +44,7 @@
 using starboard::shared::starboard::CommandLine;
 using starboard::shared::uwp::ApplicationUwp;
 using starboard::shared::uwp::GetArgvZero;
+using starboard::shared::uwp::WaitForResult;
 using starboard::shared::win32::stringToPlatformString;
 using starboard::shared::win32::wchar_tToUTF8;
 using starboard::shared::starboard::player::VideoFrame;
@@ -59,6 +62,13 @@
 using Windows::Foundation::TimeSpan;
 using Windows::Foundation::TypedEventHandler;
 using Windows::Foundation::Uri;
+using Windows::Media::Protection::HdcpProtection;
+using Windows::Media::Protection::HdcpSession;
+using Windows::Media::Protection::HdcpSetProtectionResult;
+using Windows::Networking::Connectivity::ConnectionProfile;
+using Windows::Networking::Connectivity::NetworkConnectivityLevel;
+using Windows::Networking::Connectivity::NetworkInformation;
+using Windows::Networking::Connectivity::NetworkStatusChangedEventHandler;
 using Windows::System::Threading::ThreadPoolTimer;
 using Windows::System::Threading::TimerElapsedHandler;
 using Windows::System::UserAuthenticationStatus;
@@ -203,7 +213,7 @@
 
 ref class App sealed : public IFrameworkView {
  public:
-  App() : previously_activated_(false) {}
+  App() : previously_activated_(false), has_internet_access_(false) {}
 
   // IFrameworkView methods.
   virtual void Initialize(CoreApplicationView^ applicationView) {
@@ -215,7 +225,37 @@
     applicationView->Activated +=
         ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(
             this, &App::OnActivated);
+
+    has_internet_access_ = HasInternetAccess();
+
+    SB_LOG(INFO) << "Has internet access? " << std::boolalpha
+                 << has_internet_access_;
+
+    NetworkInformation::NetworkStatusChanged +=
+        ref new NetworkStatusChangedEventHandler(this, &App::OnNetworkChanged);
   }
+
+  void OnNetworkChanged(Platform::Object^ sender) {
+    SB_UNREFERENCED_PARAMETER(sender);
+    bool has_internet_access = HasInternetAccess();
+
+    if (has_internet_access == has_internet_access_) {
+      return;
+    }
+
+    SB_LOG(INFO) << "NetworkChanged.  Has internet access? " << std::boolalpha
+      << has_internet_access;
+
+    has_internet_access_ = has_internet_access;
+
+    const SbEventType network_event =
+        (has_internet_access ? kSbEventTypeNetworkConnect
+                             : kSbEventTypeNetworkDisconnect);
+
+    ApplicationUwp::Get()->Inject(
+        new ApplicationUwp::Event(network_event, nullptr, nullptr));
+  }
+
   virtual void SetWindow(CoreWindow^ window) {
     ApplicationUwp::Get()->SetCoreWindow(window);
     window->KeyUp += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
@@ -363,7 +403,33 @@
     }
   }
 
+  bool HasInternetAccess() {
+    ConnectionProfile^ connection_profile =
+        NetworkInformation::GetInternetConnectionProfile();
+
+    if (connection_profile == nullptr) {
+      return false;
+    }
+    const NetworkConnectivityLevel connectivity_level =
+        connection_profile->GetNetworkConnectivityLevel();
+
+    switch (connectivity_level) {
+      case NetworkConnectivityLevel::InternetAccess:
+      case NetworkConnectivityLevel::ConstrainedInternetAccess:
+        return true;
+      case NetworkConnectivityLevel::None:
+      case NetworkConnectivityLevel::LocalAccess:
+        return false;
+    }
+    SB_NOTREACHED() << "Unknown network connectivity level found "
+                    << sbwin32::platformStringToString(
+                            connectivity_level.ToString());
+
+    return false;
+  }
+
   bool previously_activated_;
+  bool has_internet_access_;
   // Only valid if previously_activated_ is true
   ActivationKind previous_activation_kind_;
   std::vector<std::string> args_;
@@ -448,6 +514,23 @@
   return false;
 }
 
+Windows::Media::Protection::HdcpSession^ ApplicationUwp::GetHdcpSession() {
+  if (!hdcp_session_) {
+    hdcp_session_ = ref new HdcpSession();
+  }
+  return hdcp_session_;
+}
+
+void ApplicationUwp::ResetHdcpSession() {
+  // delete will call the destructor, but not free memory.
+  // The destructor is called explicitly so that HDCP session can be
+  // torn down immediately.
+  if (hdcp_session_) {
+    delete hdcp_session_;
+    hdcp_session_ = nullptr;
+  }
+}
+
 void ApplicationUwp::Inject(Application::Event* event) {
   RunInMainThreadAsync([this, event]() {
     bool result = DispatchAndDelete(event);
@@ -529,6 +612,54 @@
   return stringToPlatformString(localized_strings_.GetString(id, fallback));
 }
 
+bool ApplicationUwp::IsHdcpOn() {
+  ::starboard::ScopedLock lock(hdcp_session_mutex_);
+
+  return GetHdcpSession()->IsEffectiveProtectionAtLeast(HdcpProtection::On);
+}
+
+bool ApplicationUwp::TurnOnHdcp() {
+  HdcpSetProtectionResult protection_result;
+  {
+    ::starboard::ScopedLock lock(hdcp_session_mutex_);
+
+    protection_result =
+      WaitForResult(GetHdcpSession()->SetDesiredMinProtectionAsync(
+        HdcpProtection::On));
+  }
+
+  if (IsHdcpOn()) {
+    return true;
+  }
+
+  // If the operation did not have intended result, log something.
+  switch (protection_result) {
+  case HdcpSetProtectionResult::Success:
+    SB_LOG(INFO) << "Successfully set HDCP.";
+    break;
+  case HdcpSetProtectionResult::NotSupported:
+    SB_LOG(INFO) << "HDCP is not supported.";
+    break;
+  case HdcpSetProtectionResult::TimedOut:
+    SB_LOG(INFO) << "Setting HDCP timed out.";
+    break;
+  case HdcpSetProtectionResult::UnknownFailure:
+    SB_LOG(INFO) << "Unknown failure returned while setting HDCP.";
+    break;
+  }
+
+  return false;
+}
+
+bool ApplicationUwp::TurnOffHdcp() {
+  {
+    ::starboard::ScopedLock lock(hdcp_session_mutex_);
+    ResetHdcpSession();
+  }
+  bool success = !SbMediaIsOutputProtected();
+  return success;
+}
+
 void ApplicationUwp::AcceptFrame(SbPlayer player,
                                  const scoped_refptr<VideoFrame>& frame,
                                  int x,
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h
index fda18e2..e62305c 100644
--- a/src/starboard/shared/uwp/application_uwp.h
+++ b/src/starboard/shared/uwp/application_uwp.h
@@ -32,6 +32,8 @@
 namespace shared {
 namespace uwp {
 
+using Windows::Media::Protection::HdcpSession;
+
 // Returns win32's GetModuleFileName(). For cases where we'd like an argv[0].
 std::string GetArgvZero();
 
@@ -97,6 +99,12 @@
 
   Platform::String^ GetString(const char* id, const char* fallback) const;
 
+  bool IsHdcpOn();
+  // Returns true on success.
+  bool TurnOnHdcp();
+  // Returns true on success.
+  bool TurnOffHdcp();
+
  private:
   // --- Application overrides ---
   bool IsStartImmediate() SB_OVERRIDE { return false; }
@@ -116,6 +124,11 @@
                    int width,
                    int height) SB_OVERRIDE;
 
+  // These two functions should only be called while holding
+  // |hdcp_session_mutex_|.
+  Windows::Media::Protection::HdcpSession^ GetHdcpSession();
+  void ResetHdcpSession();
+
   // The single open window, if any.
   SbWindow window_;
   Platform::Agile<Windows::UI::Core::CoreWindow> core_window_;
@@ -123,9 +136,13 @@
   shared::starboard::LocalizedStrings localized_strings_;
 
   Mutex mutex_;
-  // Locked by mutex_
+  // |timer_event_map_| is locked by |mutex_|.
   std::unordered_map<SbEventId, Windows::System::Threading::ThreadPoolTimer^>
     timer_event_map_;
+
+  // |hdcp_session_| is locked by |hdcp_session_mutex_|.
+  Mutex hdcp_session_mutex_;
+  Windows::Media::Protection::HdcpSession^ hdcp_session_;
 };
 
 }  // namespace uwp
diff --git a/src/starboard/shared/uwp/async_utils.h b/src/starboard/shared/uwp/async_utils.h
index 7e01f82..ad4d4e7 100644
--- a/src/starboard/shared/uwp/async_utils.h
+++ b/src/starboard/shared/uwp/async_utils.h
@@ -34,6 +34,7 @@
   concurrency::create_task(operation,
                            task_continuation_context::use_arbitrary())
       .then([&event](TResult result) {
+        SB_UNREFERENCED_PARAMETER(result);
         BOOL success = SetEvent(event);
         SB_DCHECK(success);
       }, task_continuation_context::use_arbitrary());
diff --git a/src/starboard/shared/uwp/media_is_output_protected.cc b/src/starboard/shared/uwp/media_is_output_protected.cc
new file mode 100644
index 0000000..117f65a
--- /dev/null
+++ b/src/starboard/shared/uwp/media_is_output_protected.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

+// You may obtain a copy of the License at

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+#include "starboard/media.h"

+

+#include "starboard/shared/uwp/application_uwp.h"

+

+bool SbMediaIsOutputProtected() {

+  return starboard::shared::uwp::ApplicationUwp::Get()->IsHdcpOn();

+}

diff --git a/src/starboard/shared/uwp/media_set_output_protection.cc b/src/starboard/shared/uwp/media_set_output_protection.cc
new file mode 100644
index 0000000..1a3cc31
--- /dev/null
+++ b/src/starboard/shared/uwp/media_set_output_protection.cc
@@ -0,0 +1,50 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

+// You may obtain a copy of the License at

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+#include "starboard/media.h"

+

+#include "starboard/log.h"

+#include "starboard/shared/uwp/application_uwp.h"

+#include "starboard/time.h"

+

+using starboard::shared::uwp::ApplicationUwp;

+

+bool SbMediaSetOutputProtection(bool should_enable_dhcp) {

+  bool is_hdcp_on = SbMediaIsOutputProtected();

+

+  if (is_hdcp_on == should_enable_dhcp) {

+    return true;

+  }

+

+  SB_LOG(INFO) << "Attempting to "

+               << (should_enable_dhcp ? "enable" : "disable")

+               << " output protection.  Current status: "

+               << (is_hdcp_on ? "enabled" : "disabled");

+  SbTimeMonotonic tick = SbTimeGetMonotonicNow();

+

+  bool hdcp_success = false;

+  if (should_enable_dhcp) {

+    hdcp_success = ApplicationUwp::Get()->TurnOnHdcp();

+  } else {

+    hdcp_success = ApplicationUwp::Get()->TurnOffHdcp();

+  }

+

+  is_hdcp_on = (hdcp_success ? should_enable_dhcp : !should_enable_dhcp);

+

+  SbTimeMonotonic tock = SbTimeGetMonotonicNow();

+  SB_LOG(INFO) << "Output protection is "

+    << (is_hdcp_on ? "enabled" : "disabled") << ".  Toggling HDCP took "

+    << (tock - tick) / kSbTimeMillisecond << " milliseconds.";

+  return hdcp_success;

+}

diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
index 85f0f72..8d082dc 100644
--- a/src/starboard/shared/uwp/starboard_platform.gypi
+++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -19,6 +19,8 @@
       'application_uwp.h',
       'async_utils.h',
       'get_home_directory.cc',
+      'media_is_output_protected.cc',
+      'media_set_output_protection.cc',
       'system_clear_platform_error.cc',
       'system_get_device_type.cc',
       'system_get_property.cc',
diff --git a/src/starboard/shared/win32/drm_system_playready.cc b/src/starboard/shared/win32/drm_system_playready.cc
index 38fb628..bfa8dcd 100644
--- a/src/starboard/shared/win32/drm_system_playready.cc
+++ b/src/starboard/shared/win32/drm_system_playready.cc
@@ -184,8 +184,9 @@
       }
 
       if (item.second->IsHDCPRequired()) {
-        // TODO: Enforce HDCP
-        // if (!is_hdcp_enabled()) { return kFailure; }
+        if (!SbMediaSetOutputProtection(true)) {
+          return kFailure;
+        }
       }
 
       return kSuccess;
diff --git a/src/starboard/shared/win32/media_is_output_protected.cc b/src/starboard/shared/win32/media_is_output_protected.cc
index 213e5f5..03e6642 100644
--- a/src/starboard/shared/win32/media_is_output_protected.cc
+++ b/src/starboard/shared/win32/media_is_output_protected.cc
@@ -1,21 +1,20 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/media.h"
-
-bool SbMediaIsOutputProtected() {
-  // Pretend that HDCP is always on.
-  // TODO: Fix this with proper HDCP query.
-  return true;
-}
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

+// You may obtain a copy of the License at

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+#include "starboard/media.h"

+

+bool SbMediaIsOutputProtected() {

+  SB_NOTIMPLEMENTED();

+  return false;

+}

diff --git a/src/starboard/shared/x11/window_internal.h b/src/starboard/shared/x11/window_internal.h
index 5dcc153..6740a9e 100644
--- a/src/starboard/shared/x11/window_internal.h
+++ b/src/starboard/shared/x11/window_internal.h
@@ -15,6 +15,7 @@
 #ifndef STARBOARD_SHARED_X11_WINDOW_INTERNAL_H_
 #define STARBOARD_SHARED_X11_WINDOW_INTERNAL_H_
 
+#include <X11/extensions/Xrender.h>
 #include <X11/Xlib.h>
 
 #include "starboard/configuration.h"
@@ -22,18 +23,12 @@
 #include "starboard/shared/starboard/player/video_frame_internal.h"
 #include "starboard/window.h"
 
-#if SB_TRUE || SB_IS(PLAYER_PUNCHED_OUT)
-#include <X11/extensions/Xrender.h>
-#endif  // SB_TRUE ||
-        // SB_IS(PLAYER_PUNCHED_OUT)
-
 struct SbWindowPrivate {
   SbWindowPrivate(Display* display, const SbWindowOptions* options);
   ~SbWindowPrivate();
 
   Window window;
 
-#if SB_TRUE || SB_IS(PLAYER_PUNCHED_OUT)
   typedef ::starboard::shared::starboard::player::VideoFrame VideoFrame;
 
   // Composites graphics and the given video frame video for this window. In
@@ -80,8 +75,6 @@
 
   // A cached XRender Picture wrapper for |gl_window|.
   Picture gl_picture;
-#endif  // SB_TRUE ||
-        // SB_IS(PLAYER_PUNCHED_OUT)
 
   // The display that this window was created from.
   Display* display;
diff --git a/src/starboard/win/shared/gyp_configuration.gypi b/src/starboard/win/shared/gyp_configuration.gypi
index 6b18f49..c112351 100644
--- a/src/starboard/win/shared/gyp_configuration.gypi
+++ b/src/starboard/win/shared/gyp_configuration.gypi
@@ -139,6 +139,11 @@
                 # Enable some warnings, even those that are disabled by default.
                 # See https://msdn.microsoft.com/en-us/library/23k5d385.aspx
                 'WarningLevel': '4',
+                'AdditionalOptions': [
+                  # Warn if an enumeration value is unhandled in switch (C4062).
+                  # This warning is off by default, so it must be turned on explicitly.
+                  '/w44062',
+                ],
               },
             },
           }],
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi
index 42dacc9..2a4362a 100644
--- a/src/starboard/win/shared/starboard_platform.gypi
+++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -246,7 +246,6 @@
         '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
         '<(DEPTH)/starboard/shared/stub/image_decode.cc',
         '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
-        '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
         '<(DEPTH)/starboard/shared/stub/system_get_stack.cc',
         '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
         '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
diff --git a/src/starboard/win/win32/starboard_platform.gypi b/src/starboard/win/win32/starboard_platform.gypi
index 2335dbd..b96b643 100644
--- a/src/starboard/win/win32/starboard_platform.gypi
+++ b/src/starboard/win/win32/starboard_platform.gypi
@@ -33,6 +33,8 @@
       '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
       '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
       '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
+      '<(DEPTH)/starboard/shared/stub/media_is_output_protected.cc',
+      '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
       '<(DEPTH)/starboard/win/shared/system_get_path.cc',
       '<@(uwp_incompatible_win32)',
     ],
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
index 4f10d28..7559686 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
@@ -107,8 +107,7 @@
             out = sizeof(mDXGIBuffer);
             hr = static_cast<ID3D11DeviceChild*>(mD3DTexture)->
                 GetPrivateData(kCobaltDxgiBuffer, &out, &mDXGIBuffer);
-            ASSERT(SUCCEEDED(hr));
-            if (mDXGIBuffer != nullptr) {
+            if (SUCCEEDED(hr) && mDXGIBuffer != nullptr) {
               mDXGIBuffer->AddRef();
             }
             break;
@@ -148,8 +147,11 @@
 
         D3D11_TEXTURE2D_DESC texture_desc;
         d3Texture->GetDesc(&texture_desc);
-        if ((texture_desc.BindFlags & D3D11_BIND_RENDER_TARGET) == 0)
+
+        if (texture_desc.Format == DXGI_FORMAT_NV12)
         {
+            // NV12 textures cannot be rendered to,
+            // so don't proceed to making a swap chain.
             return egl::Error(EGL_SUCCESS);
         }
     }
diff --git a/src/third_party/libevent/kqueue.c b/src/third_party/libevent/kqueue.c
index 556b73c..bcad213 100644
--- a/src/third_party/libevent/kqueue.c
+++ b/src/third_party/libevent/kqueue.c
@@ -30,6 +30,22 @@
 #include "config.h"
 #endif
 
+#ifdef STARBOARD
+#include "libevent-starboard.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+
+// Use libevent's local compatibility  versions of these.
+#include "third_party/libevent/compat/sys/queue.h"
+#include "third_party/libevent/compat/sys/_libevent_time.h"
+
+// Include Starboard poems after all system headers.
+#include "starboard/client_porting/poem/stdio_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else  // STARBOARD
 #define _GNU_SOURCE 1
 
 #include <sys/types.h>
@@ -50,6 +66,7 @@
 #ifdef HAVE_INTTYPES_H
 #include <inttypes.h>
 #endif
+#endif  // STARBOARD
 
 /* Some platforms apparently define the udata field of struct kevent as
  * intptr_t, whereas others define it as void*.  There doesn't seem to be an
@@ -62,6 +79,9 @@
 
 #include "event.h"
 #include "event-internal.h"
+#ifndef STARBOARD
+#include "evsignal.h"
+#endif  // STARBOARD
 #include "log.h"
 
 #define EVLIST_X_KQINKERNEL	0x1000
@@ -148,11 +168,12 @@
 	 * stick an error in events[0].  If kqueue is broken, then
 	 * kevent will fail.
 	 */
+   // https://github.com/azat/libevent/commit/a9f6f32e7773347334149d89bc58bf9b984e1714
 	if (kevent(kq,
 		kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
 	    kqueueop->events[0].ident != -1 ||
-	    kqueueop->events[0].flags != EV_ERROR) {
-		event_warn("%s: detected broken kqueue; not using.", __func__);
+	    !(kqueueop->events[0].flags & EV_ERROR)) {
+    event_warn("%s: detected broken kqueue; not using.", __func__);
 		free(kqueueop->changes);
 		free(kqueueop->events);
 		free(kqueueop);
@@ -234,7 +255,6 @@
 	kqop->nchanges = 0;
 	if (res == -1) {
 		if (errno != EINTR) {
-                        event_warn("kevent");
 			return (-1);
 		}
 
diff --git a/src/third_party/libevent/libevent.gyp b/src/third_party/libevent/libevent.gyp
index 3b9f4ed..d62a6c9 100644
--- a/src/third_party/libevent/libevent.gyp
+++ b/src/third_party/libevent/libevent.gyp
@@ -24,6 +24,7 @@
             'evrpc.c',
             'evutil.c',
             'http.c',
+            'kqueue.c',
             'log.c',
             'poll.c',
             'select.c',
@@ -54,10 +55,22 @@
               'include_dirs': [ 'starboard' ],
               'conditions': [
                 [ 'sb_libevent_method == "epoll"', {
-                  'sources!': [ 'poll.c' ],
+                  'sources!': [
+                    'kqueue.c',
+                    'poll.c',
+                  ],
                 }],
                 [ 'sb_libevent_method == "poll"', {
-                  'sources!': [ 'epoll.c' ],
+                  'sources!': [
+                    'epoll.c',
+                    'kqueue.c',
+                  ],
+                }],
+                [ 'sb_libevent_method == "kqueue"', {
+                  'sources!': [
+                    'epoll.c',
+                    'poll.c',
+                  ],
                 }],
                 [ 'target_os == "linux"', {
                   'sources': [ 'epoll_sub.c' ],
@@ -74,6 +87,10 @@
                   'include_dirs': [ 'starboard/linux' ],
                   }
                 ],
+                [ 'target_os == "ios"', {
+                  'include_dirs': [ 'starboard/darwin' ],
+                  }
+                ],
                 [ 'target_os == "orbis"', {
                   'include_dirs': [ 'starboard/ps4' ],
                   }
diff --git a/src/third_party/libevent/starboard/darwin/libevent-starboard.h b/src/third_party/libevent/starboard/darwin/libevent-starboard.h
new file mode 100644
index 0000000..e1d5a42
--- /dev/null
+++ b/src/third_party/libevent/starboard/darwin/libevent-starboard.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBEVENT_STARBOARD_H_
+#define _LIBEVENT_STARBOARD_H_
+
+#include "starboard/atomic.h"
+#include "starboard/types.h"
+
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+/* Define if clock_gettime is available in libc */
+/* #undef _EVENT_DNS_USE_CPU_CLOCK_FOR_ID */
+
+/* Define is no secure id variant is available */
+#define _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID 1
+
+/* Define to 1 if you have the `clock_gettime' function. */
+/* #undef _EVENT_HAVE_CLOCK_GETTIME */
+
+/* Define if /dev/poll is available */
+/* #undef _EVENT_HAVE_DEVPOLL */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define _EVENT_HAVE_DLFCN_H 1
+
+/* Define if your system supports the epoll system calls */
+/* #undef _EVENT_HAVE_EPOLL */
+
+/* Define to 1 if you have the `epoll_ctl' function. */
+/* #undef _EVENT_HAVE_EPOLL_CTL */
+
+/* Define if your system supports event ports */
+/* #undef _EVENT_HAVE_EVENT_PORTS */
+
+/* Define to 1 if you have the `fcntl' function. */
+#define _EVENT_HAVE_FCNTL 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define _EVENT_HAVE_FCNTL_H 1
+
+/* Define to 1 if the system has the type `fd_mask'. */
+#define _EVENT_HAVE_FD_MASK 1
+
+/* Define to 1 if you have the `getaddrinfo' function. */
+#define _EVENT_HAVE_GETADDRINFO 1
+
+/* Define to 1 if you have the `getegid' function. */
+#define _EVENT_HAVE_GETEGID 1
+
+/* Define to 1 if you have the `geteuid' function. */
+#define _EVENT_HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getnameinfo' function. */
+#define _EVENT_HAVE_GETNAMEINFO 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define _EVENT_HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#define _EVENT_HAVE_INET_NTOP 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define _EVENT_HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `issetugid' function. */
+#define _EVENT_HAVE_ISSETUGID 1
+
+/* Define to 1 if you have the `kqueue' function. */
+#define _EVENT_HAVE_KQUEUE 1
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef _EVENT_HAVE_LIBNSL */
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+#define _EVENT_HAVE_LIBRESOLV 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+/* #undef _EVENT_HAVE_LIBRT */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef _EVENT_HAVE_LIBSOCKET */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define _EVENT_HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netinet/in6.h> header file. */
+/* #undef _EVENT_HAVE_NETINET_IN6_H */
+
+/* Define to 1 if you have the `poll' function. */
+#define _EVENT_HAVE_POLL 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define _EVENT_HAVE_POLL_H 1
+
+/* Define to 1 if you have the `port_create' function. */
+/* #undef _EVENT_HAVE_PORT_CREATE */
+
+/* Define to 1 if you have the <port.h> header file. */
+/* #undef _EVENT_HAVE_PORT_H */
+
+/* Define to 1 if you have the `select' function. */
+#define _EVENT_HAVE_SELECT 1
+
+/* Define if F_SETFD is defined in <fcntl.h> */
+#define _EVENT_HAVE_SETFD 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define _EVENT_HAVE_SIGACTION 1
+
+/* Define to 1 if you have the `signal' function. */
+#define _EVENT_HAVE_SIGNAL 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define _EVENT_HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define _EVENT_HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define _EVENT_HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define _EVENT_HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define _EVENT_HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define _EVENT_HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcpy' function. */
+#define _EVENT_HAVE_STRLCPY 1
+
+/* Define to 1 if you have the `strsep' function. */
+#define _EVENT_HAVE_STRSEP 1
+
+/* Define to 1 if you have the `strtok_r' function. */
+#define _EVENT_HAVE_STRTOK_R 1
+
+/* Define to 1 if you have the `strtoll' function. */
+#define _EVENT_HAVE_STRTOLL 1
+
+/* Define to 1 if the system has the type `struct in6_addr'. */
+#define _EVENT_HAVE_STRUCT_IN6_ADDR 1
+
+/* Define to 1 if you have the <sys/devpoll.h> header file. */
+/* #undef _EVENT_HAVE_SYS_DEVPOLL_H */
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+/* #undef _EVENT_HAVE_SYS_EPOLL_H */
+
+/* Define to 1 if you have the <sys/event.h> header file. */
+#define _EVENT_HAVE_SYS_EVENT_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define _EVENT_HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define _EVENT_HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#define _EVENT_HAVE_SYS_QUEUE_H 1
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define _EVENT_HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define _EVENT_HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define _EVENT_HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define _EVENT_HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define _EVENT_HAVE_SYS_TYPES_H 1
+
+/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
+#define _EVENT_HAVE_TAILQFOREACH 1
+
+/* Define if timeradd is defined in <sys/time.h> */
+#define _EVENT_HAVE_TIMERADD 1
+
+/* Define if timerclear is defined in <sys/time.h> */
+#define _EVENT_HAVE_TIMERCLEAR 1
+
+/* Define if timercmp is defined in <sys/time.h> */
+#define _EVENT_HAVE_TIMERCMP 1
+
+/* Define if timerisset is defined in <sys/time.h> */
+#define _EVENT_HAVE_TIMERISSET 1
+
+/* Define to 1 if the system has the type `uint16_t'. */
+#define _EVENT_HAVE_UINT16_T 1
+
+/* Define to 1 if the system has the type `uint32_t'. */
+#define _EVENT_HAVE_UINT32_T 1
+
+/* Define to 1 if the system has the type `uint64_t'. */
+#define _EVENT_HAVE_UINT64_T 1
+
+/* Define to 1 if the system has the type `uint8_t'. */
+#define _EVENT_HAVE_UINT8_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define _EVENT_HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vasprintf' function. */
+#define _EVENT_HAVE_VASPRINTF 1
+
+/* Define if kqueue works correctly with pipes */
+#define HAVE_WORKING_KQUEUE 1
+#define _EVENT_HAVE_WORKING_KQUEUE 1
+
+/* Name of package */
+#define _EVENT_PACKAGE "libevent"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define _EVENT_PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define _EVENT_PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define _EVENT_PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define _EVENT_PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define _EVENT_PACKAGE_VERSION ""
+
+/* Define to 1 if you have the ANSI C header files. */
+#define _EVENT_STDC_HEADERS 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define _EVENT_TIME_WITH_SYS_TIME 1
+
+/* Version number of package */
+#define _EVENT_VERSION "1.4.13-stable"
+
+/* Define to appropriate substitue if compiler doesnt have __func__ */
+/* #undef _EVENT___func__ */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef _EVENT_const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef _EVENT___cplusplus
+/* #undef _EVENT_inline */
+#endif
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef _EVENT_pid_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef _EVENT_size_t */
+
+/* Define to unsigned int if you dont have it */
+/* #undef _EVENT_socklen_t */
+
+#endif  // _LIBEVENT_STARBOARD_H_
diff --git a/src/third_party/mozjs/mfbt/Assertions.h b/src/third_party/mozjs/mfbt/Assertions.h
index a520892..5e3f391 100644
--- a/src/third_party/mozjs/mfbt/Assertions.h
+++ b/src/third_party/mozjs/mfbt/Assertions.h
@@ -154,7 +154,7 @@
  * in release builds as well as debug builds.  But if the failure is one that
  * should be debugged and fixed, MOZ_ASSERT is generally preferable.
  */
-#if defined(STARBOARD) && (SB_API_VERSION >= 4)
+#if defined(STARBOARD)
 #  define MOZ_CRASH() \
      do { \
        *((volatile int*) NULL) = 123; \
diff --git a/src/third_party/openssl/openssl/crypto/evp/e_aes.c b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
index fb0d026..96b6934 100644
--- a/src/third_party/openssl/openssl/crypto/evp/e_aes.c
+++ b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
@@ -112,7 +112,7 @@
 
 #  define MAXBITCHUNK     ((size_t)1<<(sizeof(size_t)*8-4))
 
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
 static bool sb_translate_mode(int flags, SbCryptographyBlockCipherMode *mode) {
   switch (flags & EVP_CIPH_MODE) {
     case EVP_CIPH_CBC_MODE:
@@ -338,7 +338,7 @@
     return 1;                                \
   }
 
-#else  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#else  // defined(OPENSSL_SYS_STARBOARD)
 
 static inline bool sb_has_stream(EVP_CIPHER_CTX *context) {
   return false;
@@ -409,7 +409,7 @@
 
 #define SB_TRY_CIPHER(context, out, in, len)
 
-#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#endif  // defined(OPENSSL_SYS_STARBOARD)
 
 #  ifdef VPAES_ASM
 int vpaes_set_encrypt_key(const unsigned char *userKey, int bits,
@@ -963,11 +963,11 @@
 static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
 {
     EVP_AES_GCM_CTX *gctx = c->cipher_data;
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
     SbCryptographyDestroyTransformer(gctx->gcm.gcm_transformer);
     SbCryptographyDestroyTransformer(gctx->gcm.ctr_transformer);
     SbCryptographyDestroyTransformer(gctx->gcm.ecb_transformer);
-#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#endif  // defined(OPENSSL_SYS_STARBOARD)
     OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
     if (gctx->iv != c->iv)
         OPENSSL_free(gctx->iv);
@@ -1003,7 +1003,7 @@
         gctx->taglen = -1;
         gctx->iv_gen = 0;
         gctx->tls_aad_len = -1;
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
         gctx->ctr = NULL;
         gctx->gcm.gcm_transformer = kSbCryptographyInvalidTransformer;
         gctx->gcm.ctr_transformer = kSbCryptographyInvalidTransformer;
diff --git a/src/third_party/openssl/openssl/crypto/evp/evp.h b/src/third_party/openssl/openssl/crypto/evp/evp.h
index 6e50fa7..7f03387 100644
--- a/src/third_party/openssl/openssl/crypto/evp/evp.h
+++ b/src/third_party/openssl/openssl/crypto/evp/evp.h
@@ -436,7 +436,7 @@
     int buf_len;                /* number we have left */
     unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */
     unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
     // A handle to a transformer for the full stream cipher. Intermediate state
     // is kept internally to the transformer, so fields like |iv| and |num| are
     // not used.
@@ -444,7 +444,7 @@
     // This is not used for GCM. The GCM transformer is inside the GCM context,
     // which is stored as the |cipher_data| for the GCM cipher.
     SbCryptographyTransformer stream_transformer;
-#endif  // defined(STARBOARD) && SB_API_VERSION >= 4
+#endif  // defined(STARBOARD)
     unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */
     int num;                    /* used by cfb/ofb/ctr mode */
     void *app_data;             /* application stuff */
diff --git a/src/third_party/openssl/openssl/crypto/evp/evp_enc.c b/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
index 3021e52..8e85db5 100644
--- a/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
+++ b/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
@@ -548,9 +548,9 @@
 
 int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c)
 {
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
     SbCryptographyDestroyTransformer(c->stream_transformer);
-#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#endif  // defined(OPENSSL_SYS_STARBOARD)
 #ifndef OPENSSL_FIPS
     if (c->cipher != NULL) {
         if (c->cipher->cleanup && !c->cipher->cleanup(c))
diff --git a/src/third_party/openssl/openssl/crypto/modes/gcm128.c b/src/third_party/openssl/openssl/crypto/modes/gcm128.c
index 1df77da..18433b1 100644
--- a/src/third_party/openssl/openssl/crypto/modes/gcm128.c
+++ b/src/third_party/openssl/openssl/crypto/modes/gcm128.c
@@ -87,7 +87,7 @@
 } while(0)
 
 
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
 static const int kBlockSizeBits = 128;
 static const int kBlockSizeBytes = 16;
 static inline void sb_block128(GCM128_CONTEXT *ctx,
@@ -120,7 +120,7 @@
     (*ctr)(in, out, blocks, key, ivec);
   }
 }
-#else  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#else  // defined(OPENSSL_SYS_STARBOARD)
 static inline void sb_block128(GCM128_CONTEXT *ctx,
                                block128_f block,
                                const unsigned char in[16],
@@ -140,7 +140,7 @@
   SB_UNREFERENCED_PARAMETER(ctx);
   (*ctr)(in, out, blocks, key, ivec);
 }
-#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#endif  // defined(OPENSSL_SYS_STARBOARD)
 
 /*-
  * Even though permitted values for TABLE_BITS are 8, 4 and 1, it should
diff --git a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
index db4bf70..421b0d8 100644
--- a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
+++ b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
@@ -121,7 +121,7 @@
     block128_f block;
     void *key;
 
-#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#if defined(OPENSSL_SYS_STARBOARD)
     // Whether in decrypt or encrypt mode.
     int encrypt;
 
@@ -145,7 +145,7 @@
     // mode. If valid, then this is used instead of any other state in the
     // context.
     SbCryptographyTransformer gcm_transformer;
-#endif  // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+#endif  // defined(OPENSSL_SYS_STARBOARD)
 };
 
 struct xts128_context {