Import Cobalt 13.101483

Change-Id: Ie3cc107cc4c8d9cc0552d1d34835e860f6ee8521
diff --git a/src/base/atomicops.h b/src/base/atomicops.h
index 4b2f4da..97db317 100644
--- a/src/base/atomicops.h
+++ b/src/base/atomicops.h
@@ -60,7 +60,15 @@
 
 // Use AtomicWord for a machine-sized pointer.  It will use the Atomic32 or
 // Atomic64 routines below, depending on your architecture.
+#if defined(OS_STARBOARD)
+#if SB_HAS(64_BIT_POINTERS)
+typedef SbAtomic64 AtomicWord;
+#else
+typedef SbAtomic32 AtomicWord;
+#endif
+#else
 typedef intptr_t AtomicWord;
+#endif
 
 // Atomically execute:
 //      result = *ptr;
diff --git a/src/base/basictypes.h b/src/base/basictypes.h
index c393fd7..c903434 100644
--- a/src/base/basictypes.h
+++ b/src/base/basictypes.h
@@ -11,6 +11,10 @@
 
 #include "base/port.h"    // Types that only need exist on certain systems
 
+#if defined(OS_STARBOARD)
+#include "starboard/types.h"
+#endif  // defined(OS_STARBOARD)
+
 #ifndef COMPILER_MSVC
 // stdint.h is part of C99 but MSVC doesn't have it.
 #include <stdint.h>         // For intptr_t.
@@ -19,8 +23,15 @@
 typedef signed char         schar;
 typedef signed char         int8;
 typedef short               int16;
+#if defined(OS_STARBOARD)
+typedef int32_t             int32;
+#else
 typedef int                 int32;
+#endif  // defined(OS_STARBOARD)
 
+#if defined(OS_STARBOARD)
+typedef int64_t int64;
+#else
 // The NSPR system headers define 64-bit as |long| when possible, except on
 // Mac OS X.  In order to not have typedef mismatches, we do the same on LP64.
 //
@@ -31,6 +42,7 @@
 #else
 typedef long long           int64;
 #endif
+#endif  // defined(OS_STARBOARD)
 
 // NOTE: unsigned types are DANGEROUS in loops and other arithmetical
 // places.  Use the signed types unless your variable represents a bit
@@ -38,22 +50,37 @@
 // use 'unsigned' to express "this value should always be positive";
 // use assertions for this.
 
+#if defined(OS_STARBOARD)
+typedef uint8_t            uint8;
+typedef uint16_t           uint16;
+typedef uint32_t           uint32;
+#else
 typedef unsigned char      uint8;
 typedef unsigned short     uint16;
 typedef unsigned int       uint32;
+#endif  // defined(OS_STARBOARD)
 
+#if defined(OS_STARBOARD)
+typedef uint64_t uint64;
+#else
 // See the comment above about NSPR and 64-bit.
 #if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
 typedef unsigned long uint64;
 #else
 typedef unsigned long long uint64;
 #endif
+#endif  // defined(OS_STARBOARD)
 
 // A type to represent a Unicode code-point value. As of Unicode 4.0,
 // such values require up to 21 bits.
 // (For type-checking on pointers, make this explicitly signed,
 // and it should always be the signed version of whatever int32 is.)
-typedef signed int         char32;
+#if defined(OS_STARBOARD)
+typedef int32_t         char32;
+#else
+typedef signed int      char32;
+#endif  // defined(OS_STARBOARD)
+
 
 #if defined(COBALT_WIN)
 #pragma warning(push)
diff --git a/src/cobalt/accessibility/screen_reader_tests.cc b/src/cobalt/accessibility/screen_reader_tests.cc
index 9adc74d..714f243 100644
--- a/src/cobalt/accessibility/screen_reader_tests.cc
+++ b/src/cobalt/accessibility/screen_reader_tests.cc
@@ -101,6 +101,10 @@
     quit_event_.Signal();
     screen_reader_.reset();
   }
+  void OnClose(base::TimeDelta close_time) {
+    UNREFERENCED_PARAMETER(close_time);
+    Quit();
+  }
 
   scoped_refptr<script::Wrappable> CreateWindowAttribute(
       const scoped_refptr<dom::Window>& window,
@@ -167,7 +171,7 @@
       url, base::kApplicationStateStarted,
       base::Bind(&LiveRegionMutationTest::OnRenderTreeProducedStub),
       base::Bind(&LiveRegionMutationTest::OnError, base::Unretained(this)),
-      base::Bind(&LiveRegionMutationTest::Quit, base::Unretained(this)),
+      base::Bind(&LiveRegionMutationTest::OnClose, base::Unretained(this)),
       base::Closure(), /* window_minimize_callback */
       NULL /* media_module */, &network_module, kDefaultViewportSize,
       kDefaultVideoPixelRatio, &resource_provider, kRefreshRate,
diff --git a/src/cobalt/bindings/testing/bindings_sandbox_main.cc b/src/cobalt/bindings/testing/bindings_sandbox_main.cc
index 0b73fa9..30f7e25 100644
--- a/src/cobalt/bindings/testing/bindings_sandbox_main.cc
+++ b/src/cobalt/bindings/testing/bindings_sandbox_main.cc
@@ -27,8 +27,9 @@
 
 int SandboxMain(int argc, char** argv) {
   scoped_refptr<Window> test_window = new Window();
-  cobalt::script::JavaScriptEngine::Options js_options;
-  StandaloneJavascriptRunner standalone_runner(js_options, test_window);
+  cobalt::script::JavaScriptEngine::Options javascript_engine_options;
+  StandaloneJavascriptRunner standalone_runner(javascript_engine_options,
+                                               test_window);
   standalone_runner.RunInteractive();
   return 0;
 }
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 0d7d821..ba9db81 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -378,19 +378,6 @@
 #endif
     "h5vcc:";
 
-#if !defined(COBALT_FORCE_CSP)
-dom::CspEnforcementType StringToCspMode(const std::string& mode) {
-  if (mode == "disable") {
-    return dom::kCspEnforcementDisable;
-  } else if (mode == "enable") {
-    return dom::kCspEnforcementEnable;
-  } else {
-    DLOG(INFO) << "Invalid CSP mode: " << mode << ": use [disable|enable]";
-    return dom::kCspEnforcementEnable;
-  }
-}
-#endif  // !defined(COBALT_FORCE_CSP)
-
 struct NonTrivialStaticFields {
   NonTrivialStaticFields() : system_language(base::GetSystemLanguage()) {}
 
@@ -401,6 +388,11 @@
   DISALLOW_COPY_AND_ASSIGN(NonTrivialStaticFields);
 };
 
+struct SecurityFlags {
+  csp::CSPHeaderPolicy csp_header_policy;
+  network::HTTPSRequirement https_requirement;
+};
+
 // |non_trivial_static_fields| will be lazily created on the first time it's
 // accessed.
 base::LazyInstance<NonTrivialStaticFields> non_trivial_static_fields =
@@ -493,7 +485,7 @@
   ApplyCommandLineSettingsToRendererOptions(&options.renderer_module_options);
 
   if (command_line->HasSwitch(browser::switches::kDisableJavaScriptJit)) {
-    options.web_module_options.javascript_options.disable_jit = true;
+    options.web_module_options.javascript_engine_options.disable_jit = true;
   }
 
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
@@ -516,6 +508,7 @@
 
   // User can specify an extra search path entry for files loaded via file://.
   options.web_module_options.extra_web_file_dir = GetExtraWebFileDir();
+  SecurityFlags security_flags{csp::kCSPRequired, network::kHTTPSRequired};
   options.web_module_options.location_policy = kYouTubeTvLocationPolicy;
   // Set callback to be notified when a navigation occurs that destroys the
   // underlying WebModule.
@@ -531,29 +524,24 @@
         command_line->GetSwitchValueASCII(browser::switches::kProxy);
   }
 
-#if !defined(COBALT_FORCE_CSP)
-  if (command_line->HasSwitch(browser::switches::kCspMode)) {
-    options.web_module_options.csp_enforcement_mode = StringToCspMode(
-        command_line->GetSwitchValueASCII(browser::switches::kCspMode));
-  }
-  if (options.web_module_options.csp_enforcement_mode !=
-      dom::kCspEnforcementEnable) {
-    options.web_module_options.location_policy = "h5vcc-location-src *";
-  }
-#endif  // !defined(COBALT_FORCE_CSP)
-
 #if defined(ENABLE_IGNORE_CERTIFICATE_ERRORS)
   if (command_line->HasSwitch(browser::switches::kIgnoreCertificateErrors)) {
     options.network_module_options.ignore_certificate_errors = true;
   }
 #endif  // defined(ENABLE_IGNORE_CERTIFICATE_ERRORS)
 
-#if !defined(COBALT_FORCE_HTTPS)
-  if (command_line->HasSwitch(switches::kAllowHttp)) {
-    DLOG(INFO) << "Allowing insecure HTTP connections";
-    options.network_module_options.require_https = false;
+  if (!command_line->HasSwitch(switches::kRequireHTTPSLocation)) {
+    security_flags.https_requirement = network::kHTTPSOptional;
   }
-#endif  // !defined(COBALT_FORCE_HTTPS)
+
+  if (!command_line->HasSwitch(browser::switches::kRequireCSP)) {
+    security_flags.csp_header_policy = csp::kCSPOptional;
+  }
+
+  if (command_line->HasSwitch(browser::switches::kProd)) {
+    security_flags.https_requirement = network::kHTTPSRequired;
+    security_flags.csp_header_policy = csp::kCSPRequired;
+  }
 
   if (command_line->HasSwitch(switches::kVideoPlaybackRateMultiplier)) {
     double playback_rate = 1.0;
@@ -598,6 +586,20 @@
   }
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
+// Production-builds override all switches to the most secure configuration.
+#if defined(COBALT_FORCE_HTTPS)
+  security_flags.https_requirement = network::kHTTPSRequired;
+#endif  // defined(COBALT_FORCE_HTTPS)
+
+#if defined(COBALT_FORCE_CSP)
+  security_flags.csp_header_policy = csp::kCSPRequired;
+#endif  // defined(COBALT_FORCE_CSP)
+
+  options.network_module_options.https_requirement =
+      security_flags.https_requirement;
+  options.web_module_options.require_csp = security_flags.csp_header_policy;
+  options.web_module_options.csp_enforcement_mode = dom::kCspEnforcementEnable;
+
   if (command_line->HasSwitch(browser::switches::kDisableNavigationWhitelist)) {
     LOG(ERROR) << "\n"
                << "  *** Disabling the default navigation whitelist! ***\n"
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 31ab25d..1a0f89a 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -257,7 +257,10 @@
       will_quit_(false),
       application_state_(initial_application_state),
       splash_screen_cache_(new SplashScreenCache()),
-      navigation_produced_main_render_tree_(false) {
+      main_web_module_generation_(0),
+      next_timeline_id_(1),
+      current_splash_screen_timeline_id_(-1),
+      current_main_web_module_timeline_id_(-1) {
   h5vcc_url_handler_.reset(new H5vccURLHandler(this));
 
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
@@ -328,7 +331,7 @@
       &network_module_, GetViewportSize(), GetResourceProvider(),
       kLayoutMaxRefreshFrequencyInHz,
       base::Bind(&BrowserModule::GetDebugServer, base::Unretained(this)),
-      options_.web_module_options.javascript_options));
+      options_.web_module_options.javascript_engine_options));
   lifecycle_observers_.AddObserver(debug_console_.get());
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
@@ -392,6 +395,14 @@
   }
   web_module_.reset(NULL);
 
+  // Increment the navigation generation so that we can attach it to event
+  // callbacks as a way of identifying the new web module from the old ones.
+  ++main_web_module_generation_;
+  current_splash_screen_timeline_id_ = next_timeline_id_++;
+  current_main_web_module_timeline_id_ = next_timeline_id_++;
+
+  main_web_module_layer_->Reset();
+
   // Wait until after the old WebModule is destroyed before setting the navigate
   // time so that it won't be included in the time taken to load the URL.
   navigate_time_ = base::TimeTicks::Now().ToInternalValue();
@@ -399,8 +410,7 @@
   // Show a splash screen while we're waiting for the web page to load.
   const math::Size& viewport_size = GetViewportSize();
 
-  DestroySplashScreen();
-  navigation_produced_main_render_tree_ = false;
+  DestroySplashScreen(base::TimeDelta());
   base::optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
   if (fallback_splash_screen_url_ ||
       (key && splash_screen_cache_->IsSplashScreenCached(*key))) {
@@ -459,7 +469,7 @@
   web_module_.reset(new WebModule(
       url, application_state_,
       base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
-                 base::Unretained(this)),
+                 base::Unretained(this), main_web_module_generation_),
       base::Bind(&BrowserModule::OnError, base::Unretained(this)),
       base::Bind(&BrowserModule::OnWindowClose, base::Unretained(this)),
       base::Bind(&BrowserModule::OnWindowMinimize, base::Unretained(this)),
@@ -544,11 +554,12 @@
 }
 
 void BrowserModule::QueueOnRenderTreeProduced(
+    int main_web_module_generation,
     const browser::WebModule::LayoutResults& layout_results) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::QueueOnRenderTreeProduced()");
   render_tree_submission_queue_.AddMessage(
       base::Bind(&BrowserModule::OnRenderTreeProduced, base::Unretained(this),
-                 layout_results));
+                 main_web_module_generation, layout_results));
   self_message_loop_->PostTask(
       FROM_HERE,
       base::Bind(&BrowserModule::ProcessRenderTreeSubmissionQueue, weak_this_));
@@ -567,11 +578,18 @@
 }
 
 void BrowserModule::OnRenderTreeProduced(
+    int main_web_module_generation,
     const browser::WebModule::LayoutResults& layout_results) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnRenderTreeProduced()");
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
 
-  if (splash_screen_ && !navigation_produced_main_render_tree_) {
+  if (main_web_module_generation != main_web_module_generation_) {
+    // Ignore render trees produced by old stale web modules.  This might happen
+    // during a navigation transition.
+    return;
+  }
+
+  if (splash_screen_ && !splash_screen_->ShutdownSignaled()) {
     splash_screen_->Shutdown();
   }
   if (application_state_ == base::kApplicationStatePreloading ||
@@ -579,18 +597,26 @@
     return;
   }
 
-  navigation_produced_main_render_tree_ = true;
-
   renderer::Submission renderer_submission(layout_results.render_tree,
                                            layout_results.layout_time);
+  // Set the timeline id for the main web module.  The main web module is
+  // assumed to be an interactive experience for which the default timeline
+  // configuration is already designed for, so we don't configure anything
+  // explicitly.
+  renderer_submission.timeline_info.id = current_main_web_module_timeline_id_;
+
   renderer_submission.on_rasterized_callback = base::Bind(
       &BrowserModule::OnRendererSubmissionRasterized, base::Unretained(this));
-  main_web_module_layer_->Submit(renderer_submission, true /* receive_time */);
+  if (!splash_screen_) {
+    render_tree_combiner_->SetTimelineLayer(main_web_module_layer_.get());
+  }
+  main_web_module_layer_->Submit(renderer_submission);
 
 #if defined(ENABLE_SCREENSHOT)
   screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
       layout_results.render_tree, layout_results.layout_time));
 #endif
+  SubmitCurrentRenderTreeToRenderer();
 }
 
 void BrowserModule::OnSplashScreenRenderTreeProduced(
@@ -606,20 +632,42 @@
 
   renderer::Submission renderer_submission(layout_results.render_tree,
                                            layout_results.layout_time);
+  // We customize some of the renderer pipeline timeline behavior to cater for
+  // non-interactive splash screen playback.
+  renderer_submission.timeline_info.id = current_splash_screen_timeline_id_;
+  // Since the splash screen is non-interactive, latency is not a concern.
+  // Latency reduction implies a speedup in animation playback speed which can
+  // make the splash screen play out quicker than intended.
+  renderer_submission.timeline_info.allow_latency_reduction = false;
+  // Increase the submission queue size to a larger value than usual.  This
+  // is done because a) since we do not attempt to reduce latency, the queue
+  // tends to fill up more and b) the pipeline may end up receiving a number
+  // of render tree submissions caused by updated main web module render trees,
+  // which can fill the submission queue.  Blowing the submission queue is
+  // particularly bad for the splash screen as it results in dropping of older
+  // submissions, which results in skipping forward during animations, which
+  // sucks.
+  renderer_submission.timeline_info.max_submission_queue_size =
+      std::max(8, renderer_submission.timeline_info.max_submission_queue_size);
+
   renderer_submission.on_rasterized_callback = base::Bind(
       &BrowserModule::OnRendererSubmissionRasterized, base::Unretained(this));
-  splash_screen_layer_->Submit(renderer_submission, false /* receive_time */);
+  render_tree_combiner_->SetTimelineLayer(splash_screen_layer_.get());
+  splash_screen_layer_->Submit(renderer_submission);
 
 #if defined(ENABLE_SCREENSHOT)
-// TODO: write screen shot using render_tree_combinder_ (to combine
+// TODO: write screen shot using render_tree_combiner_ (to combine
 // splash screen and main web_module). Consider when the splash
 // screen is overlaid on top of the main web module render tree, and
 // a screenshot is taken : there will be a race condition on which
 // web module update their render tree last.
 #endif
+
+  SubmitCurrentRenderTreeToRenderer();
 }
 
-void BrowserModule::OnWindowClose() {
+void BrowserModule::OnWindowClose(base::TimeDelta close_time) {
+  UNREFERENCED_PARAMETER(close_time);
 #if defined(ENABLE_DEBUG_CONSOLE)
   if (input_device_manager_fuzzer_) {
     return;
@@ -712,6 +760,8 @@
 
   debug_console_layer_->Submit(renderer::Submission(
       layout_results.render_tree, layout_results.layout_time));
+
+  SubmitCurrentRenderTreeToRenderer();
 }
 
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
@@ -905,17 +955,26 @@
   return false;
 }
 
-void BrowserModule::DestroySplashScreen() {
+void BrowserModule::DestroySplashScreen(base::TimeDelta close_time) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::DestroySplashScreen()");
   if (MessageLoop::current() != self_message_loop_) {
     self_message_loop_->PostTask(
-        FROM_HERE, base::Bind(&BrowserModule::DestroySplashScreen, weak_this_));
+        FROM_HERE, base::Bind(&BrowserModule::DestroySplashScreen, weak_this_,
+                              close_time));
     return;
   }
   if (splash_screen_) {
     lifecycle_observers_.RemoveObserver(splash_screen_.get());
   }
-  splash_screen_layer_.reset(NULL);
+  if (splash_screen_layer_) {
+    if (!close_time.is_zero()) {
+      // Ensure that the renderer renders each frame up until the window.close()
+      // is called on the splash screen's timeline, in order to ensure that the
+      // splash screen shutdown transition plays out completely.
+      renderer_module_->pipeline()->TimeFence(close_time);
+    }
+    splash_screen_layer_.reset(NULL);
+  }
   splash_screen_.reset(NULL);
 }
 
@@ -1146,8 +1205,7 @@
       RendererModuleWithCameraOptions(options_.renderer_module_options,
                                       input_device_manager_->camera_3d())));
 
-  render_tree_combiner_.reset(
-      new RenderTreeCombiner(renderer_module_.get(), GetViewportSize()));
+  render_tree_combiner_.reset(new RenderTreeCombiner());
   // Create the main web module layer.
   main_web_module_layer_ =
       render_tree_combiner_->CreateLayer(kMainWebModuleZIndex);
@@ -1322,7 +1380,7 @@
       static_cast<int>(auto_mem_->image_cache_size_in_bytes()->value());
   options_.web_module_options.remote_typeface_cache_capacity = static_cast<int>(
       auto_mem_->remote_typeface_cache_size_in_bytes()->value());
-  options_.web_module_options.javascript_options.gc_threshold_bytes =
+  options_.web_module_options.javascript_engine_options.gc_threshold_bytes =
       static_cast<size_t>(
           auto_mem_->javascript_gc_threshold_in_bytes()->value());
   if (web_module_) {
@@ -1357,5 +1415,13 @@
   }
 }
 
+void BrowserModule::SubmitCurrentRenderTreeToRenderer() {
+  base::optional<renderer::Submission> submission =
+      render_tree_combiner_->GetCurrentSubmission();
+  if (submission) {
+    renderer_module_->pipeline()->Submit(*submission);
+  }
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 7389e9c..32ec219 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -179,8 +179,10 @@
   // Glue function to deal with the production of the main render tree,
   // and will manage handing it off to the renderer.
   void QueueOnRenderTreeProduced(
+      int web_module_generation,
       const browser::WebModule::LayoutResults& layout_results);
   void OnRenderTreeProduced(
+      int web_module_generation,
       const browser::WebModule::LayoutResults& layout_results);
 
   // Glue function to deal with the production of the splash screen render tree,
@@ -240,10 +242,10 @@
   bool TryURLHandlers(const GURL& url);
 
   // Destroys the splash screen, if currently displayed.
-  void DestroySplashScreen();
+  void DestroySplashScreen(base::TimeDelta close_time);
 
   // Called when web module has received window.close().
-  void OnWindowClose();
+  void OnWindowClose(base::TimeDelta close_time);
 
   // Called when web module has received window.minimize().
   void OnWindowMinimize();
@@ -315,6 +317,11 @@
   // Applies the current AutoMem settings to all applicable submodules.
   void ApplyAutoMemSettings();
 
+  // If it exists, takes the current combined render tree from
+  // |render_tree_combiner_| and submits it to the pipeline in the renderer
+  // module.
+  void SubmitCurrentRenderTreeToRenderer();
+
   // TODO:
   //     WeakPtr usage here can be avoided if BrowserModule has a thread to
   //     own where it can ensure that its tasks are all resolved when it is
@@ -509,9 +516,20 @@
   // The splash screen cache.
   scoped_ptr<SplashScreenCache> splash_screen_cache_;
 
-  // Whether or not the main WebModule has produced any render trees yet for the
-  // current navigation.
-  bool navigation_produced_main_render_tree_;
+  // Number of main web modules that have take place so far, helpful for
+  // ditinguishing lingering events produced by older web modules as we switch
+  // from one to another.  This is incremented with each navigation.
+  int main_web_module_generation_;
+
+  // Keeps track of a unique next ID to be assigned to new splash screen or
+  // main web module timelines.
+  int next_timeline_id_;
+
+  // The following values are specified on submissions sent into the renderer
+  // so that the renderer can identify when it is changing from one timeline
+  // to another (in which case it may need to clear its submission queue).
+  int current_splash_screen_timeline_id_;
+  int current_main_web_module_timeline_id_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index 79a6cd5..ec0aba7 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -167,11 +167,11 @@
     network::NetworkModule* network_module, const math::Size& window_dimensions,
     render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
     const debug::Debugger::GetDebugServerCallback& get_debug_server_callback,
-    const script::JavaScriptEngine::Options& js_options) {
+    const script::JavaScriptEngine::Options& javascript_engine_options) {
   mode_ = GetInitialMode();
 
   WebModule::Options web_module_options;
-  web_module_options.javascript_options = js_options;
+  web_module_options.javascript_engine_options = javascript_engine_options;
   web_module_options.name = "DebugConsoleWebModule";
   // The debug console does not load any image assets.
   web_module_options.image_cache_capacity = 0;
@@ -192,8 +192,8 @@
       new WebModule(GURL(kInitialDebugConsoleUrl), initial_application_state,
                     render_tree_produced_callback,
                     base::Bind(&DebugConsole::OnError, base::Unretained(this)),
-                    base::Closure(), /* window_close_callback */
-                    base::Closure(), /* window_minimize_callback */
+                    WebModule::CloseCallback(), /* window_close_callback */
+                    base::Closure(),            /* window_minimize_callback */
                     &stub_media_module_, network_module, window_dimensions,
                     1.f /*video_pixel_ratio*/, resource_provider,
                     layout_refresh_rate, web_module_options));
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h
index 6ed291d..3889f9a 100644
--- a/src/cobalt/browser/debug_console.h
+++ b/src/cobalt/browser/debug_console.h
@@ -45,7 +45,7 @@
       render_tree::ResourceProvider* resource_provider,
       float layout_refresh_rate,
       const debug::Debugger::GetDebugServerCallback& get_debug_server_callback,
-      const script::JavaScriptEngine::Options& js_options);
+      const script::JavaScriptEngine::Options& javascript_engine_options);
   ~DebugConsole();
 
   // Filters a key event.
diff --git a/src/cobalt/browser/debug_console/console_values.js b/src/cobalt/browser/debug_console/console_values.js
index 516c249..7dac9df 100644
--- a/src/cobalt/browser/debug_console/console_values.js
+++ b/src/cobalt/browser/debug_console/console_values.js
@@ -22,7 +22,7 @@
   this.DEFAULT_ACTIVE_SET =
       'Cobalt DevTools WebDriver ' +
       'Memory.CPU Memory.MainWebModule Memory.JS Memory.Font ' +
-      'Count.MainWebModule.ImageCache.RequestedResources ' +
+      'Count.MainWebModule.ImageCache.Resource ' +
       'Count.MainWebModule.DOM.HtmlElement Count.MainWebModule.Layout.Box ' +
       'Event.Count.MainWebModule.KeyDown.DOM.HtmlElement.Added ' +
       'Event.Count.MainWebModule.KeyDown.Layout.Box.Created ' +
diff --git a/src/cobalt/browser/lib/cobalt.def b/src/cobalt/browser/lib/cobalt.def
index 6fb1539..7a73eb4 100644
--- a/src/cobalt/browser/lib/cobalt.def
+++ b/src/cobalt/browser/lib/cobalt.def
@@ -18,6 +18,7 @@
     CbLibGraphicsSetBeginRenderFrameCallback
     CbLibGraphicsSetEndRenderFrameCallback
     CbLibGrapicsGetMainTextureHandle
+    CbLibGraphicsSetTargetMainTextureSize
 
     ; From cobalt/render/rasterizer/lib/exported/video.h:
     CbLibVideoSetOnUpdateProjectionType
diff --git a/src/cobalt/browser/render_tree_combiner.cc b/src/cobalt/browser/render_tree_combiner.cc
index 74727ec..87d4d1f 100644
--- a/src/cobalt/browser/render_tree_combiner.cc
+++ b/src/cobalt/browser/render_tree_combiner.cc
@@ -21,7 +21,6 @@
 #include "base/time.h"
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/rect_node.h"
-#include "cobalt/renderer/renderer_module.h"
 #include "cobalt/renderer/submission.h"
 
 namespace cobalt {
@@ -35,25 +34,24 @@
 RenderTreeCombiner::Layer::~Layer() {
   DCHECK(render_tree_combiner_);
   render_tree_combiner_->RemoveLayer(this);
-  render_tree_combiner_->SubmitToRenderer();
 }
 
 void RenderTreeCombiner::Layer::Submit(
-    const base::optional<renderer::Submission>& render_tree_submission,
-    bool receive_time) {
+    const base::optional<renderer::Submission>& render_tree_submission) {
   render_tree_ = render_tree_submission;
-  if (receive_time) {
-    receipt_time_ = base::TimeTicks::HighResNow();
-  } else {
-    receipt_time_ = base::nullopt;
-  }
-  DCHECK(render_tree_combiner_);
-  render_tree_combiner_->SubmitToRenderer();
+  receipt_time_ = base::TimeTicks::HighResNow();
 }
 
-RenderTreeCombiner::RenderTreeCombiner(
-    renderer::RendererModule* renderer_module, const math::Size& viewport_size)
-    : renderer_module_(renderer_module), viewport_size_(viewport_size) {}
+base::optional<base::TimeDelta> RenderTreeCombiner::Layer::CurrentTimeOffset() {
+  if (!receipt_time_) {
+    return base::nullopt;
+  } else {
+    return render_tree_->time_offset +
+           (base::TimeTicks::HighResNow() - *receipt_time_);
+  }
+}
+
+RenderTreeCombiner::RenderTreeCombiner() : timeline_layer_(NULL) {}
 
 scoped_ptr<RenderTreeCombiner::Layer> RenderTreeCombiner::CreateLayer(
     int z_index) {
@@ -66,7 +64,19 @@
   return scoped_ptr<RenderTreeCombiner::Layer>(layers_[z_index]);
 }
 
+void RenderTreeCombiner::SetTimelineLayer(Layer* layer) {
+  if (layer != NULL) {
+    DCHECK(OwnsLayer(layer));
+  }
+
+  timeline_layer_ = layer;
+}
+
 void RenderTreeCombiner::RemoveLayer(const Layer* layer) {
+  if (timeline_layer_ == layer) {
+    SetTimelineLayer(NULL);
+  }
+
   for (auto it = layers_.begin(); it != layers_.end(); /* no increment */) {
     if (it->second == layer) {
       it = layers_.erase(it);
@@ -76,36 +86,41 @@
   }
 }
 
-void RenderTreeCombiner::SubmitToRenderer() {
+base::optional<renderer::Submission>
+RenderTreeCombiner::GetCurrentSubmission() {
   render_tree::CompositionNode::Builder builder;
 
   // Add children for all layers in order.
-  base::optional<renderer::Submission> first_tree = base::nullopt;
-  base::optional<renderer::Submission> combined_submission = base::nullopt;
+  Layer* first_layer_with_render_tree = NULL;
   for (auto it = layers_.begin(); it != layers_.end(); ++it) {
     RenderTreeCombiner::Layer* layer = it->second;
     if (layer->render_tree_) {
       builder.AddChild(layer->render_tree_->render_tree);
-      first_tree = layer->render_tree_;
-      // Make the combined submission with the first receipt_time_ we find.
-      if (!combined_submission && layer->receipt_time_) {
-        combined_submission = renderer::Submission(*layer->render_tree_);
-        combined_submission->time_offset =
-            layer->render_tree_->time_offset +
-            (base::TimeTicks::HighResNow() - *layer->receipt_time_);
-      }
+      first_layer_with_render_tree = layer;
     }
   }
-  if (!first_tree) {
-    return;
-  }
-  if (!combined_submission) {
-    // None of the layers store the time.
-    combined_submission = renderer::Submission(*first_tree);
+  if (!first_layer_with_render_tree) {
+    return base::nullopt;
   }
 
-  combined_submission->render_tree = new render_tree::CompositionNode(builder);
-  renderer_module_->pipeline()->Submit(*combined_submission);
+  Layer* timeline_layer = (timeline_layer_ && timeline_layer_->render_tree_)
+                              ? timeline_layer_
+                              : first_layer_with_render_tree;
+
+  renderer::Submission submission(new render_tree::CompositionNode(builder),
+                                  *timeline_layer->CurrentTimeOffset());
+  submission.timeline_info = timeline_layer->render_tree_->timeline_info;
+  return submission;
 }
+
+bool RenderTreeCombiner::OwnsLayer(Layer* layer) {
+  for (const auto& iter_layer : layers_) {
+    if (iter_layer.second == layer) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/render_tree_combiner.h b/src/cobalt/browser/render_tree_combiner.h
index 65f3127..a9bde63 100644
--- a/src/cobalt/browser/render_tree_combiner.h
+++ b/src/cobalt/browser/render_tree_combiner.h
@@ -20,7 +20,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/optional.h"
 #include "base/time.h"
-#include "cobalt/renderer/renderer_module.h"
 #include "cobalt/renderer/submission.h"
 
 namespace cobalt {
@@ -50,44 +49,55 @@
     // Submit render tree to the layer, and specify whether the time
     // received should be stored.
     void Submit(
-        const base::optional<renderer::Submission>& render_tree_submission,
-        bool receive_time = false);
+        const base::optional<renderer::Submission>& render_tree_submission);
 
    private:
     friend class RenderTreeCombiner;
 
     explicit Layer(RenderTreeCombiner* render_tree_combiner = NULL);
 
+    // Returns the current submission time for this particular layer.  This is
+    // called by the RenderTreeCombiner on the |timeline_layer_| to determine
+    // which value to pass in as the submission time for the renderer.
+    base::optional<base::TimeDelta> CurrentTimeOffset();
+
     RenderTreeCombiner* render_tree_combiner_;
 
     base::optional<renderer::Submission> render_tree_;
     base::optional<base::TimeTicks> receipt_time_;
   };
 
-  explicit RenderTreeCombiner(renderer::RendererModule* renderer_module,
-                              const math::Size& viewport_size);
+  RenderTreeCombiner();
   ~RenderTreeCombiner() {}
 
   // Create a Layer with a given |z_index|. If a Layer already exists
   // at |z_index|, return NULL, and no Layer is created.
   scoped_ptr<Layer> CreateLayer(int z_index);
 
+  // Returns a current submission object that can be passed into a renderer
+  // for rasterization.  If no layers with render trees exist, this will return
+  // a base::nullopt.
+  base::optional<renderer::Submission> GetCurrentSubmission();
+
+  // Names a single layer as the one responsible for providing the timeline
+  // id and configuration to the output combined render tree.  Only a single
+  // layer can be responsible for providing the timeline.
+  void SetTimelineLayer(Layer* layer);
+
  private:
+  // Returns true if the specified layer exists in this render tree combiner's
+  // current list of layers (e.g. |layers_|).
+  bool OwnsLayer(Layer* layer);
+
   // The layers keyed on their z_index.
   std::map<int, Layer*> layers_;
 
   // Removes a layer from |layers_|. Called by the Layer destructor.
   void RemoveLayer(const Layer* layer);
 
-  // Combines the cached render trees and renders the result.
-  void SubmitToRenderer();
-
-  // Local reference to the render pipeline, so we can submit the combined tree.
-  // Reference counted pointer not necessary here.
-  renderer::RendererModule* renderer_module_;
-
-  // The size of the output viewport.
-  math::Size viewport_size_;
+  // Which layer is currently controlling the receipt time submitted to the
+  // rasterizer.
+  RenderTreeCombiner::Layer* timeline_layer_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index e2fa6ba..811baa0 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -30,16 +30,17 @@
 
 const int kSplashShutdownSeconds = 2;
 
-void PostCallbackToMessageLoop(const base::Closure& callback,
-                               MessageLoop* message_loop) {
+typedef base::Callback<void(base::TimeDelta)> Callback;
+void PostCallbackToMessageLoop(const Callback& callback,
+                               MessageLoop* message_loop,
+                               base::TimeDelta time) {
   DCHECK(message_loop);
-  message_loop->PostTask(FROM_HERE, callback);
+  message_loop->PostTask(FROM_HERE, base::Bind(callback, time));
 }
 
 // TODO: consolidate definitions of BindToLoop / BindToCurrentLoop
 // from here and media in base.
-base::Closure BindToLoop(const base::Closure& callback,
-                         MessageLoop* message_loop) {
+Callback BindToLoop(const Callback& callback, MessageLoop* message_loop) {
   return base::Bind(&PostCallbackToMessageLoop, callback, message_loop);
 }
 
@@ -58,10 +59,12 @@
     const base::optional<GURL>& fallback_splash_screen_url,
     const GURL& initial_main_web_module_url,
     SplashScreenCache* splash_screen_cache,
-    const base::Callback<void()>& on_splash_screen_shutdown_complete)
+    const base::Callback<void(base::TimeDelta)>&
+        on_splash_screen_shutdown_complete)
     : render_tree_produced_callback_(render_tree_produced_callback),
       self_message_loop_(MessageLoop::current()),
-      on_splash_screen_shutdown_complete_(on_splash_screen_shutdown_complete) {
+      on_splash_screen_shutdown_complete_(on_splash_screen_shutdown_complete),
+      shutdown_signaled_(false) {
   WebModule::Options web_module_options;
   web_module_options.name = "SplashScreenWebModule";
 
@@ -86,10 +89,11 @@
     web_module_options.splash_screen_cache = splash_screen_cache;
   }
 
-  base::Callback<void()> on_window_close(
+  base::Callback<void(base::TimeDelta)> on_window_close(
       BindToLoop(on_splash_screen_shutdown_complete, self_message_loop_));
 
-  web_module_options.on_before_unload_fired_but_not_handled = on_window_close;
+  web_module_options.on_before_unload_fired_but_not_handled =
+      base::Bind(on_window_close, base::TimeDelta());
 
   DCHECK(url_to_pass);
   web_module_.reset(new WebModule(
@@ -113,12 +117,18 @@
 void SplashScreen::Shutdown() {
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(web_module_);
+  DCHECK(!ShutdownSignaled()) << "Shutdown() should be called at most once.";
+
   if (!on_splash_screen_shutdown_complete_.callback().is_null()) {
     MessageLoop::current()->PostDelayedTask(
-        FROM_HERE, on_splash_screen_shutdown_complete_.callback(),
+        FROM_HERE,
+        base::Bind(on_splash_screen_shutdown_complete_.callback(),
+                   base::TimeDelta()),
         base::TimeDelta::FromSeconds(kSplashShutdownSeconds));
   }
   web_module_->InjectBeforeUnloadEvent();
+
+  shutdown_signaled_ = true;
 }
 
 }  // namespace browser
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index 3f1b8c3..f9315d2 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -33,18 +33,18 @@
 //
 class SplashScreen : public LifecycleObserver {
  public:
-  SplashScreen(
-      base::ApplicationState initial_application_state,
-      const WebModule::OnRenderTreeProducedCallback&
-          render_tree_produced_callback,
-      network::NetworkModule* network_module,
-      const math::Size& window_dimensions,
-      render_tree::ResourceProvider* resource_provider,
-      float layout_refresh_rate,
-      const base::optional<GURL>& fallback_splash_screen_url,
-      const GURL& initial_main_web_module_url,
-      cobalt::browser::SplashScreenCache* splash_screen_cache,
-      const base::Callback<void()>& on_splash_screen_shutdown_complete);
+  SplashScreen(base::ApplicationState initial_application_state,
+               const WebModule::OnRenderTreeProducedCallback&
+                   render_tree_produced_callback,
+               network::NetworkModule* network_module,
+               const math::Size& window_dimensions,
+               render_tree::ResourceProvider* resource_provider,
+               float layout_refresh_rate,
+               const base::optional<GURL>& fallback_splash_screen_url,
+               const GURL& initial_main_web_module_url,
+               cobalt::browser::SplashScreenCache* splash_screen_cache,
+               const base::Callback<void(base::TimeDelta)>&
+                   on_splash_screen_shutdown_complete);
   ~SplashScreen();
 
   void SetSize(const math::Size& window_dimensions, float video_pixel_ratio) {
@@ -72,6 +72,9 @@
   // no handlers, |on_splash_screen_shutdown_complete_| is run immediately.
   void Shutdown();
 
+  // Returns whether Shutdown() has been called before or not.
+  bool ShutdownSignaled() const { return shutdown_signaled_; }
+
  private:
   // Run when window.close() is called by the WebModule.
   void OnWindowClosed();
@@ -88,7 +91,11 @@
 
   // This is called by Shutdown (via window.close) or after
   // the time limit has been exceeded.
-  base::CancelableClosure on_splash_screen_shutdown_complete_;
+  base::CancelableCallback<void(base::TimeDelta)>
+      on_splash_screen_shutdown_complete_;
+
+  // True if SplashScreen::Shutdown() has been called.
+  bool shutdown_signaled_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 11e98c3..aa69973 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -19,18 +19,10 @@
 namespace switches {
 
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-// Allow insecure HTTP network connections.
-const char kAllowHttp[] = "allow_http";
 
 // Decode audio data using ShellRawAudioDecoderStub.
 const char kAudioDecoderStub[] = "audio_decoder_stub";
 
-// Set the content security policy enforcement mode: disable | enable
-// disable: Allow all resource loads. Ignore CSP totally.
-// enable: default mode. Enforce CSP strictly.  Require CSP headers or fail
-// the initial document load.
-const char kCspMode[] = "csp_mode";
-
 // Switches different debug console modes: on | hud | off
 const char kDebugConsoleMode[] = "debug_console";
 
@@ -66,6 +58,10 @@
 // app run as if it has no local storage.
 const char kNullSavegame[] = "null_savegame";
 
+// Several checks are not enabled by default in non-production(gold) build. Use
+// this flag to simulate production build behavior.
+const char kProd[] = "prod";
+
 // Specifies a proxy to use for network connections.
 const char kProxy[] = "proxy";
 
@@ -75,6 +71,14 @@
 // Creates a remote debugging server and listens on the specified port.
 const char kRemoteDebuggingPort[] = "remote_debugging_port";
 
+// Forbid Cobalt to start without receiving csp headers which is enabled by
+// default in production.
+const char kRequireCSP[] = "require_csp";
+
+// Ask Cobalt to only accept https url which is enabled by default in
+// production.
+const char kRequireHTTPSLocation[] = "require_https";
+
 // If this flag is set, Cobalt will automatically shutdown after the specified
 // number of seconds have passed.
 const char kShutdownAfter[] = "shutdown_after";
@@ -130,7 +134,7 @@
 const char kFPSOverlay[] = "fps_overlay";
 
 // Disables the hard-coded navigation whitelist without disabling any other
-// security checks. This is enabled in Gold builds.
+// security checks. This is enabled in production(gold) builds.
 const char kDisableNavigationWhitelist[] = "disable_navigation_whitelist";
 
 // Determines the capacity of the image cache which manages image surfaces
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 4dc02ef..a0bbfde 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -20,9 +20,7 @@
 namespace switches {
 
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-extern const char kAllowHttp[];
 extern const char kAudioDecoderStub[];
-extern const char kCspMode[];
 extern const char kDebugConsoleMode[];
 extern const char kDisableWebDriver[];
 extern const char kDisableWebmVp9[];
@@ -73,6 +71,10 @@
 
 extern const char kVideoPlaybackRateMultiplier[];
 
+extern const char kProd[];
+extern const char kRequireHTTPSLocation[];
+extern const char kRequireCSP[];
+
 }  // namespace switches
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/testdata/dual-playback-demo/bear.mp4 b/src/cobalt/browser/testdata/dual-playback-demo/bear.mp4
new file mode 100644
index 0000000..3763b59
--- /dev/null
+++ b/src/cobalt/browser/testdata/dual-playback-demo/bear.mp4
Binary files differ
diff --git a/src/cobalt/browser/testdata/dual-playback-demo/dual-playback-demo.html b/src/cobalt/browser/testdata/dual-playback-demo/dual-playback-demo.html
new file mode 100644
index 0000000..f22d953
--- /dev/null
+++ b/src/cobalt/browser/testdata/dual-playback-demo/dual-playback-demo.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Dual Playback Demo</title>
+    <style>
+        body {
+          margin: 0;
+        }
+
+        #player-layer {
+          width: 100%;
+          height: 100%;
+        }
+
+        video {
+          width: 100%;
+          height: 100%;
+        }
+
+        #ui-layer {
+          position: absolute;
+          top: 15%;
+          height: 85%;
+          width: 100%;
+          background-color: rgba(33, 33, 33, .75);
+          padding: 24px;
+        }
+
+        .item {
+          width: 427px;
+          height: 240px;
+          display: inline-block;
+          margin: 24px;
+          vertical-align: middle;
+        }
+    </style>
+  </head>
+  <body>
+    <div id="player-layer">
+      <video class="primary" muted="" autoplay="1" src="bear.mp4" loop=""></video>
+    </div>
+    <div id="ui-layer">
+      <div class="item" style="background-color: #DB4437"></div>
+      <div class="item" style="background-color: #9E9E9E">
+        <video class="secondary" muted="" autoplay="1" src="bear.mp4" loop=""></video>
+      </div>
+      <div class="item" style="background-color: #4285F4"></div>
+      <div class="item" style="background-color: #0F9D58"></div>
+      <div class="item" style="background-color: #F4B400"></div>
+    </div>
+  </body>
+</html>
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 150b2c8..2991246 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -424,7 +424,8 @@
 
   dom_parser_.reset(new dom_parser::Parser(
       kDOMMaxElementDepth,
-      base::Bind(&WebModule::Impl::OnError, base::Unretained(this))));
+      base::Bind(&WebModule::Impl::OnError, base::Unretained(this)),
+      data.options.require_csp));
   DCHECK(dom_parser_);
 
   blob_registry_.reset(new dom::Blob::Registry);
@@ -487,8 +488,8 @@
       new browser::WebModuleStatTracker(name_, data.options.track_event_stats));
   DCHECK(web_module_stat_tracker_);
 
-  javascript_engine_ =
-      script::JavaScriptEngine::CreateEngine(data.options.javascript_options);
+  javascript_engine_ = script::JavaScriptEngine::CreateEngine(
+      data.options.javascript_engine_options);
   DCHECK(javascript_engine_);
 
 #if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
@@ -531,7 +532,8 @@
       data.options.navigation_callback,
       base::Bind(&WebModule::Impl::OnError, base::Unretained(this)),
       data.network_module->cookie_jar(), data.network_module->GetPostSender(),
-      data.options.location_policy, data.options.csp_enforcement_mode,
+      data.options.location_policy, data.options.require_csp,
+      data.options.csp_enforcement_mode,
       base::Bind(&WebModule::Impl::OnCspPolicyChanged, base::Unretained(this)),
       base::Bind(&WebModule::Impl::OnRanAnimationFrameCallbacks,
                  base::Unretained(this)),
@@ -1039,7 +1041,7 @@
     const GURL& initial_url, base::ApplicationState initial_application_state,
     const OnRenderTreeProducedCallback& render_tree_produced_callback,
     const OnErrorCallback& error_callback,
-    const base::Closure& window_close_callback,
+    const CloseCallback& window_close_callback,
     const base::Closure& window_minimize_callback,
     media::MediaModule* media_module, network::NetworkModule* network_module,
     const math::Size& window_dimensions, float video_pixel_ratio,
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index ba23707..281664b 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -128,6 +128,9 @@
     // can't be changed from the whitelisted origins.
     std::string location_policy;
 
+    // Whether Cobalt is forbidden to render without receiving CSP headers.
+    csp::CSPHeaderPolicy require_csp;
+
     // Image cache capacity in bytes.
     int image_cache_capacity;
 
@@ -172,7 +175,7 @@
     // To support 3D camera movements.
     scoped_refptr<input::Camera3D> camera_3d;
 
-    script::JavaScriptEngine::Options javascript_options;
+    script::JavaScriptEngine::Options javascript_engine_options;
 
     // The video playback rate will be multiplied with the following value.  Its
     // default value is 1.0.
@@ -202,12 +205,13 @@
   typedef base::Callback<void(const LayoutResults&)>
       OnRenderTreeProducedCallback;
   typedef base::Callback<void(const GURL&, const std::string&)> OnErrorCallback;
+  typedef dom::Window::CloseCallback CloseCallback;
 
   WebModule(const GURL& initial_url,
             base::ApplicationState initial_application_state,
             const OnRenderTreeProducedCallback& render_tree_produced_callback,
             const OnErrorCallback& error_callback,
-            const base::Closure& window_close_callback,
+            const CloseCallback& window_close_callback,
             const base::Closure& window_minimize_callback,
             media::MediaModule* media_module,
             network::NetworkModule* network_module,
@@ -288,7 +292,7 @@
         base::ApplicationState initial_application_state,
         const OnRenderTreeProducedCallback& render_tree_produced_callback,
         const OnErrorCallback& error_callback,
-        const base::Closure& window_close_callback,
+        const CloseCallback& window_close_callback,
         const base::Closure& window_minimize_callback,
         media::MediaModule* media_module,
         network::NetworkModule* network_module,
@@ -315,7 +319,7 @@
     base::ApplicationState initial_application_state;
     OnRenderTreeProducedCallback render_tree_produced_callback;
     OnErrorCallback error_callback;
-    const base::Closure& window_close_callback;
+    const CloseCallback& window_close_callback;
     const base::Closure& window_minimize_callback;
     media::MediaModule* media_module;
     network::NetworkModule* network_module;
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index c1d9146..f701966 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-98776
\ No newline at end of file
+101483
\ No newline at end of file
diff --git a/src/cobalt/csp/content_security_policy.h b/src/cobalt/csp/content_security_policy.h
index 5725ffb..95789a1 100644
--- a/src/cobalt/csp/content_security_policy.h
+++ b/src/cobalt/csp/content_security_policy.h
@@ -42,6 +42,12 @@
   std::string header;
 };
 
+// Whether Cobalt can start without CSP headers.
+enum CSPHeaderPolicy {
+  kCSPRequired,
+  kCSPOptional,
+};
+
 // A callback that a URL fetcher will call to check if the URL is permitted
 // by our security policy. This may be called multiple times if the URL results
 // in a redirect. The callback should return |true| if the URL is safe to
diff --git a/src/cobalt/csp/directive_list.cc b/src/cobalt/csp/directive_list.cc
index f0fb591..7ce85e0 100644
--- a/src/cobalt/csp/directive_list.cc
+++ b/src/cobalt/csp/directive_list.cc
@@ -38,7 +38,7 @@
   std::string encoded;
   bool ok = base::Base64Encode(digest_piece, &encoded);
   if (ok) {
-    return "sha256-" + encoded;
+    return "'sha256-" + encoded + "'";
   } else {
     DLOG(WARNING) << "Base64Encode failed on " << content;
     return "sha256-...";
@@ -221,10 +221,22 @@
     suffix =
         " Note that 'unsafe-inline' is ignored if either a hash or nonce value "
         "is present in the source list.";
+  } else if (directive->hash_or_nonce_present()) {
+    suffix =
+        " Either the 'unsafe-inline' keyword, a hash (" + hash_value +
+        "), or a nonce ('nonce-...') is required to enable inline execution.";
+    DigestValue digest_value;
+    HashAlgorithm hash_algorithm;
+    SourceList::ParseHash(hash_value.c_str(),
+                          hash_value.c_str() + hash_value.length(),
+                          &digest_value, &hash_algorithm);
+    if (directive->AllowHash(HashValue(hash_algorithm, digest_value))) {
+      return true;
+    }
   } else {
     suffix =
-        " Either the 'unsafe-inline' keyword, a hash ('" + hash_value +
-        "'), or a nonce ('nonce-...') is required to enable inline execution.";
+        " Either the 'unsafe-inline' keyword, a hash (" + hash_value +
+        "), or a nonce ('nonce-...') is required to enable inline execution.";
     if (directive == default_src_)
       suffix = suffix + " Note also that '" +
                std::string(is_script ? "script" : "style") +
diff --git a/src/cobalt/csp/source_list.cc b/src/cobalt/csp/source_list.cc
index 1f6fcd1..bc4615d 100644
--- a/src/cobalt/csp/source_list.cc
+++ b/src/cobalt/csp/source_list.cc
@@ -404,6 +404,7 @@
 // hash-algorithm    = "sha1" / "sha256" / "sha384" / "sha512"
 // hash-value        = 1*( ALPHA / DIGIT / "+" / "/" / "=" )
 //
+// static
 bool SourceList::ParseHash(const char* begin, const char* end,
                            DigestValue* hash, HashAlgorithm* hash_algorithm) {
   std::string prefix;
diff --git a/src/cobalt/csp/source_list.h b/src/cobalt/csp/source_list.h
index f3b3d9c..921ac47 100644
--- a/src/cobalt/csp/source_list.h
+++ b/src/cobalt/csp/source_list.h
@@ -56,6 +56,9 @@
   uint8 hash_algorithms_used() const { return hash_algorithms_used_; }
   bool hash_or_nonce_present() const;
 
+  static bool ParseHash(const char* begin, const char* end, DigestValue* hash,
+                        HashAlgorithm* hash_algorithm);
+
  private:
   bool ParseSource(const char* begin, const char* end,
                    SourceConfig* source_config);
@@ -66,8 +69,6 @@
                  SourceConfig::WildcardDisposition* port_disposition);
   bool ParsePath(const char* begin, const char* end, std::string* path);
   bool ParseNonce(const char* begin, const char* end, std::string* nonce);
-  bool ParseHash(const char* begin, const char* end, DigestValue* hash,
-                 HashAlgorithm* hash_algorithm);
 
   void AddSourceLocalhost();
   void AddSourceLocalNetwork();
diff --git a/src/cobalt/csp/source_list_test.cc b/src/cobalt/csp/source_list_test.cc
index febdbce..3f298d8 100644
--- a/src/cobalt/csp/source_list_test.cc
+++ b/src/cobalt/csp/source_list_test.cc
@@ -525,5 +525,34 @@
 }
 #endif
 
+TEST_F(SourceListTest, TestInvalidHash) {
+  std::string sources = "'sha256-c3uoUQo23pT8hqB5MoAZnI9LiPUc+lWgGBKHfV07iAM='";
+  SourceList source_list(&checker_, csp_.get(), "style-src");
+  ParseSourceList(&source_list, sources);
+
+  std::string hash_value =
+      "'sha256-IegLaWGTFJzK5gbj1YVsl+RfqHIqXhXan88eiG9GQwE='";
+  DigestValue digest_value;
+  HashAlgorithm hash_algorithm;
+  EXPECT_TRUE(SourceList::ParseHash(hash_value.c_str(),
+                                    hash_value.c_str() + hash_value.length(),
+                                    &digest_value, &hash_algorithm));
+  EXPECT_FALSE(source_list.AllowHash(HashValue(hash_algorithm, digest_value)));
+}
+
+TEST_F(SourceListTest, TestValidHash) {
+  std::string sources = "'sha256-IegLaWGTFJzK5gbj1YVsl+RfqHIqXhXan88eiG9GQwE='";
+  SourceList source_list(&checker_, csp_.get(), "style-src");
+  ParseSourceList(&source_list, sources);
+
+  std::string hash_value = sources;
+  DigestValue digest_value;
+  HashAlgorithm hash_algorithm;
+  EXPECT_TRUE(SourceList::ParseHash(hash_value.c_str(),
+                                    hash_value.c_str() + hash_value.length(),
+                                    &digest_value, &hash_algorithm));
+  EXPECT_TRUE(source_list.AllowHash(HashValue(hash_algorithm, digest_value)));
+}
+
 }  // namespace csp
 }  // namespace cobalt
diff --git a/src/cobalt/csp/testdata/invalid_hash.html b/src/cobalt/csp/testdata/invalid_hash.html
new file mode 100644
index 0000000..76991b4
--- /dev/null
+++ b/src/cobalt/csp/testdata/invalid_hash.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!--
+  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.
+-->
+<html>
+
+<head>
+  <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'sha256-c3uoUQo23pT8hqB5MoAZnI9LiPUc+lWgGBKHfV07iAM=';">
+</head>
+
+<style>
+  body {
+    background-color: #00ff00;
+  }
+</style>
+<body></body>
+</html>
diff --git a/src/cobalt/csp/testdata/valid_hash.html b/src/cobalt/csp/testdata/valid_hash.html
new file mode 100644
index 0000000..8b4a9ba
--- /dev/null
+++ b/src/cobalt/csp/testdata/valid_hash.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!--
+  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.
+-->
+<html>
+
+<head>
+  <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'sha256-IegLaWGTFJzK5gbj1YVsl+RfqHIqXhXan88eiG9GQwE=';">
+</head>
+
+<style>
+  body {
+    background-color: #00ff00;
+  }
+</style>
+<body></body>
+</html>
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 36e8e8a..47f3bc5 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -87,6 +87,8 @@
 %token kBackgroundSizeToken                   // background-size
 %token kBackgroundToken                       // background
 %token kBorderToken                           // border
+%token kBorderBottomLeftRadiusToken           // border-bottom-left-radius
+%token kBorderBottomRightRadiusToken          // border-bottom-right-radius
 %token kBorderBottomToken                     // border-bottom
 %token kBorderBottomColorToken                // border-bottom-color
 %token kBorderBottomStyleToken                // border-bottom-style
@@ -104,6 +106,8 @@
 %token kBorderStyleToken                      // border-style
 %token kBorderTopToken                        // border-top
 %token kBorderTopColorToken                   // border-top-color
+%token kBorderTopLeftRadiusToken              // border-top-left-radius
+%token kBorderTopRightRadiusToken             // border-top-right-radius
 %token kBorderTopStyleToken                   // border-top-style
 %token kBorderTopWidthToken                   // border-top-width
 %token kBorderWidthToken                      // border-width
@@ -523,6 +527,8 @@
                        background_size_property_value
                        background_size_property_value_without_common_values
                        border_color_property_value
+                       border_radius_element
+                       border_radius_element_with_common_values
                        border_radius_property_value
                        border_style_property_value
                        border_width_element
@@ -757,6 +763,7 @@
 %union { cssom::PropertyListValue::Builder* property_list; }
 %type <property_list> background_size_property_list
                       border_color_property_list
+                      border_radius_property_list
                       border_style_property_list
                       border_width_property_list
                       comma_separated_animation_direction_list
@@ -1325,6 +1332,14 @@
     $$ = TrivialStringPiece::FromCString(
             cssom::GetPropertyName(cssom::kBorderProperty));
   }
+  | kBorderBottomLeftRadiusToken {
+    $$ = TrivialStringPiece::FromCString(
+            cssom::GetPropertyName(cssom::kBorderBottomLeftRadiusProperty));
+  }
+  | kBorderBottomRightRadiusToken {
+    $$ = TrivialStringPiece::FromCString(
+            cssom::GetPropertyName(cssom::kBorderBottomRightRadiusProperty));
+  }
   | kBorderBottomToken {
     $$ = TrivialStringPiece::FromCString(
             cssom::GetPropertyName(cssom::kBorderBottomProperty));
@@ -1393,6 +1408,14 @@
     $$ = TrivialStringPiece::FromCString(
             cssom::GetPropertyName(cssom::kBorderTopColorProperty));
   }
+  | kBorderTopLeftRadiusToken {
+    $$ = TrivialStringPiece::FromCString(
+            cssom::GetPropertyName(cssom::kBorderTopLeftRadiusProperty));
+  }
+  | kBorderTopRightRadiusToken {
+    $$ = TrivialStringPiece::FromCString(
+          cssom::GetPropertyName(cssom::kBorderTopRightRadiusProperty));
+  }
   | kBorderTopStyleToken {
     $$ = TrivialStringPiece::FromCString(
             cssom::GetPropertyName(cssom::kBorderTopStyleProperty));
@@ -3461,11 +3484,44 @@
 // The radii of a quarter ellipse that defines the shape of the corner
 // of the outer border edge.
 //   https://www.w3.org/TR/css3-background/#the-border-radius
-border_radius_property_value:
+border_radius_element:
     positive_length_percent_property_value { $$ = $1; }
+  ;
+border_radius_element_with_common_values:
+    border_radius_element
   | common_values
   ;
 
+border_radius_property_list:
+    /* empty */ {
+    $$ = new cssom::PropertyListValue::Builder();
+  }
+  | border_radius_property_list border_radius_element {
+    scoped_ptr<cssom::PropertyListValue::Builder> property_value($1);
+    property_value->push_back(MakeScopedRefPtrAndRelease($2));
+    $$ = property_value.release();
+  }
+  ;
+
+border_radius_property_value:
+    border_radius_property_list {
+    scoped_ptr<cssom::PropertyListValue::Builder> property_value($1);
+    if (property_value->size() > 0u &&
+        property_value->size() <= 4u) {
+      $$ = AddRef(new cssom::PropertyListValue(property_value.Pass()));
+    } else {
+      parser_impl->LogWarning(@1, "invalid number of border radius values");
+      $$ = NULL;
+    }
+  }
+  | common_values {
+    scoped_ptr<cssom::PropertyListValue::Builder> property_value(
+        new cssom::PropertyListValue::Builder());
+    property_value->push_back($1);
+    $$ = AddRef(new cssom::PropertyListValue(property_value.Pass()));
+  }
+  ;
+
 box_shadow_property_element:
     length {
     if ($<shadow_info>0->length_vector.size() == 2) {
@@ -5610,6 +5666,18 @@
       $$ = NULL;
     }
   }
+  | kBorderBottomLeftRadiusToken maybe_whitespace colon border_radius_element_with_common_values
+      maybe_important {
+    $$ = $4 ? new PropertyDeclaration(cssom::kBorderBottomLeftRadiusProperty,
+                                      MakeScopedRefPtrAndRelease($4), $5)
+            : NULL;
+  }
+  | kBorderBottomRightRadiusToken maybe_whitespace colon border_radius_element_with_common_values
+      maybe_important {
+    $$ = $4 ? new PropertyDeclaration(cssom::kBorderBottomRightRadiusProperty,
+                                      MakeScopedRefPtrAndRelease($4), $5)
+            : NULL;
+  }
   | kBorderBottomToken maybe_whitespace colon border_or_outline_property_value
       maybe_important {
     scoped_ptr<BorderOrOutlineShorthand> border($4);
@@ -5733,9 +5801,38 @@
   }
   | kBorderRadiusToken maybe_whitespace colon border_radius_property_value
       maybe_important {
-    $$ = $4 ? new PropertyDeclaration(cssom::kBorderRadiusProperty,
-                                      MakeScopedRefPtrAndRelease($4), $5)
-            : NULL;
+    scoped_refptr<cssom::PropertyValue> property_list_value(
+        MakeScopedRefPtrAndRelease($4));
+    if (property_list_value) {
+      BorderShorthandToLonghand shorthand_to_longhand;
+      shorthand_to_longhand.Assign4BordersBasedOnPropertyList(
+            property_list_value);
+
+      scoped_ptr<PropertyDeclaration> property_declaration(
+          new PropertyDeclaration($5));
+
+      // Unpack border radius.
+      property_declaration->property_values.push_back(
+          PropertyDeclaration::PropertyKeyValuePair(
+              cssom::kBorderTopLeftRadiusProperty,
+              shorthand_to_longhand.border_top));
+      property_declaration->property_values.push_back(
+          PropertyDeclaration::PropertyKeyValuePair(
+              cssom::kBorderTopRightRadiusProperty,
+              shorthand_to_longhand.border_right));
+      property_declaration->property_values.push_back(
+          PropertyDeclaration::PropertyKeyValuePair(
+              cssom::kBorderBottomRightRadiusProperty,
+              shorthand_to_longhand.border_bottom));
+      property_declaration->property_values.push_back(
+          PropertyDeclaration::PropertyKeyValuePair(
+              cssom::kBorderBottomLeftRadiusProperty,
+              shorthand_to_longhand.border_left));
+
+      $$ = property_declaration.release();
+    } else {
+      $$ = NULL;
+    }
   }
   | kBorderRightToken maybe_whitespace colon border_or_outline_property_value
       maybe_important {
@@ -5846,6 +5943,18 @@
                                       MakeScopedRefPtrAndRelease($4), $5)
             : NULL;
   }
+  | kBorderTopLeftRadiusToken maybe_whitespace colon border_radius_element_with_common_values
+      maybe_important {
+    $$ = $4 ? new PropertyDeclaration(cssom::kBorderTopLeftRadiusProperty,
+                                      MakeScopedRefPtrAndRelease($4), $5)
+            : NULL;
+  }
+  | kBorderTopRightRadiusToken maybe_whitespace colon border_radius_element_with_common_values
+      maybe_important {
+    $$ = $4 ? new PropertyDeclaration(cssom::kBorderTopRightRadiusProperty,
+                                      MakeScopedRefPtrAndRelease($4), $5)
+            : NULL;
+  }
   | kBorderTopStyleToken maybe_whitespace colon line_style_with_common_values
       maybe_important {
     $$ = $4 ? new PropertyDeclaration(cssom::kBorderTopStyleProperty,
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index 4a7a792..8af545c 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -3869,29 +3869,268 @@
   EXPECT_FALSE(style->GetPropertyValue(cssom::kBorderLeftWidthProperty));
 }
 
-TEST_F(ParserTest, ParsesBorderRadiusLength) {
+TEST_F(ParserTest, ParsesBorderRadiusSingleLength) {
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
       parser_.ParseStyleDeclarationList("border-radius: 0.2em;",
                                         source_location_);
 
-  scoped_refptr<cssom::LengthValue> border_radius =
+  scoped_refptr<cssom::LengthValue> border_top_left_radius =
       dynamic_cast<cssom::LengthValue*>(
-          style->GetPropertyValue(cssom::kBorderRadiusProperty).get());
-  ASSERT_TRUE(border_radius);
-  EXPECT_FLOAT_EQ(0.2f, border_radius->value());
-  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_radius->unit());
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_top_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_left_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_top_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_top_right_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_bottom_right_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_bottom_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_bottom_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_bottom_left_radius->unit());
 }
 
-TEST_F(ParserTest, ParsesBorderRadiusPercentage) {
+TEST_F(ParserTest, ParsesBorderRadiusSinglePercentage) {
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
       parser_.ParseStyleDeclarationList("border-radius: 50%;",
                                         source_location_);
 
-  scoped_refptr<cssom::PercentageValue> border_radius =
+  scoped_refptr<cssom::PercentageValue> border_top_left_radius =
       dynamic_cast<cssom::PercentageValue*>(
-          style->GetPropertyValue(cssom::kBorderRadiusProperty).get());
-  ASSERT_TRUE(border_radius);
-  EXPECT_FLOAT_EQ(0.5f, border_radius->value());
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(0.5f, border_top_left_radius->value());
+
+  scoped_refptr<cssom::PercentageValue> border_top_right_radius =
+      dynamic_cast<cssom::PercentageValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(0.5f, border_top_right_radius->value());
+
+  scoped_refptr<cssom::PercentageValue> border_bottom_right_radius =
+      dynamic_cast<cssom::PercentageValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(0.5f, border_bottom_right_radius->value());
+
+  scoped_refptr<cssom::PercentageValue> border_bottom_left_radius =
+      dynamic_cast<cssom::PercentageValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(0.5f, border_bottom_left_radius->value());
+}
+
+TEST_F(ParserTest, ParsesBorderRadiusWithTwoLengths) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-radius: .8em 20px;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_top_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_top_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_left_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_top_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(20.0f, border_top_right_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_top_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_bottom_right_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_bottom_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(20.0f, border_bottom_left_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_bottom_left_radius->unit());
+}
+
+TEST_F(ParserTest, ParsesBorderRadiusWithLengthAndPercentage) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-radius: .8em 20%;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_top_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_top_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_left_radius->unit());
+
+  scoped_refptr<cssom::PercentageValue> border_top_right_radius =
+      dynamic_cast<cssom::PercentageValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_top_right_radius->value());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_bottom_right_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_bottom_right_radius->unit());
+
+  scoped_refptr<cssom::PercentageValue> border_bottom_left_radius =
+      dynamic_cast<cssom::PercentageValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_bottom_left_radius->value());
+}
+
+TEST_F(ParserTest, ParsesBorderRadiusWithThreeLengths) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-radius: .8em 20px 10px;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_top_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_top_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_left_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_top_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(20.0f, border_top_right_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_top_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(10.0f, border_bottom_right_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_bottom_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(20.0f, border_bottom_left_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_bottom_left_radius->unit());
+}
+
+TEST_F(ParserTest, ParsesBorderRadiusWithFourLengths) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-radius: .8em 20px 10px 5px;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_top_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_top_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_left_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_top_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(20.0f, border_top_right_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_top_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(10.0f, border_bottom_right_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_bottom_right_radius->unit());
+
+  scoped_refptr<cssom::LengthValue> border_bottom_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(5.0f, border_bottom_left_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_bottom_left_radius->unit());
+}
+
+TEST_F(ParserTest, ParsesBorderTopLeftRadius) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-top-left-radius: 20px;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_top_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopLeftRadiusProperty).get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(20.0f, border_top_left_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_top_left_radius->unit());
+}
+
+TEST_F(ParserTest, ParsesBorderTopRightRadius) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-top-right-radius: .8em;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_top_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderTopRightRadiusProperty).get());
+  ASSERT_TRUE(border_top_right_radius);
+  EXPECT_FLOAT_EQ(0.8f, border_top_right_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_top_right_radius->unit());
+}
+
+TEST_F(ParserTest, ParsesBorderBottomRightRadius) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-bottom-right-radius: 50px;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_bottom_right_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomRightRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_right_radius);
+  EXPECT_FLOAT_EQ(50.0f, border_bottom_right_radius->value());
+  EXPECT_EQ(cssom::kPixelsUnit, border_bottom_right_radius->unit());
+}
+
+TEST_F(ParserTest, ParsesBorderBottomLeftRadius) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList("border-bottom-left-radius: .2em;",
+                                        source_location_);
+
+  scoped_refptr<cssom::LengthValue> border_bottom_left_radius =
+      dynamic_cast<cssom::LengthValue*>(
+          style->GetPropertyValue(cssom::kBorderBottomLeftRadiusProperty)
+              .get());
+  ASSERT_TRUE(border_bottom_left_radius);
+  EXPECT_FLOAT_EQ(0.2f, border_bottom_left_radius->value());
+  EXPECT_EQ(cssom::kFontSizesAkaEmUnit, border_bottom_left_radius->unit());
 }
 
 TEST_F(ParserTest, ParsesBoxShadowWithNone) {
diff --git a/src/cobalt/css_parser/scanner.cc b/src/cobalt/css_parser/scanner.cc
index b03a7d8..06f6eaa 100644
--- a/src/cobalt/css_parser/scanner.cc
+++ b/src/cobalt/css_parser/scanner.cc
@@ -1856,6 +1856,24 @@
       }
       return false;
 
+    case 22:
+      if (IsEqualToCssIdentifier(
+              name.begin,
+              cssom::GetPropertyName(cssom::kBorderTopLeftRadiusProperty))) {
+        *property_name_token = kBorderTopLeftRadiusToken;
+        return true;
+      }
+      return false;
+
+    case 23:
+      if (IsEqualToCssIdentifier(
+              name.begin,
+              cssom::GetPropertyName(cssom::kBorderTopRightRadiusProperty))) {
+        *property_name_token = kBorderTopRightRadiusToken;
+        return true;
+      }
+      return false;
+
     case 25:
       if (IsEqualToCssIdentifier(
               name.begin, cssom::GetPropertyName(
@@ -1869,6 +1887,12 @@
         *property_name_token = kAnimationTimingFunctionToken;
         return true;
       }
+      if (IsEqualToCssIdentifier(
+              name.begin,
+              cssom::GetPropertyName(cssom::kBorderBottomLeftRadiusProperty))) {
+        *property_name_token = kBorderBottomLeftRadiusToken;
+        return true;
+      }
       return false;
 
     case 26:
@@ -1878,6 +1902,12 @@
         *property_name_token = kTransitionTimingFunctionToken;
         return true;
       }
+      if (IsEqualToCssIdentifier(
+              name.begin, cssom::GetPropertyName(
+                              cssom::kBorderBottomRightRadiusProperty))) {
+        *property_name_token = kBorderBottomRightRadiusToken;
+        return true;
+      }
       return false;
   }
 
diff --git a/src/cobalt/cssom/computed_style.cc b/src/cobalt/cssom/computed_style.cc
index c26d8e1..a59143a 100644
--- a/src/cobalt/cssom/computed_style.cc
+++ b/src/cobalt/cssom/computed_style.cc
@@ -2933,7 +2933,10 @@
       (*value)->Accept(&background_size_provider);
       *value = background_size_provider.computed_background_size();
     } break;
-    case kBorderRadiusProperty: {
+    case kBorderBottomLeftRadiusProperty:
+    case kBorderBottomRightRadiusProperty:
+    case kBorderTopLeftRadiusProperty:
+    case kBorderTopRightRadiusProperty: {
       ComputedBorderRadiusProvider border_radius_provider(
           GetFontSize(), GetRootFontSize(), GetViewportSizeOnePercent());
       (*value)->Accept(&border_radius_provider);
@@ -3013,6 +3016,7 @@
     case kBorderColorProperty:
     case kBorderLeftProperty:
     case kBorderProperty:
+    case kBorderRadiusProperty:
     case kBorderRightProperty:
     case kBorderStyleProperty:
     case kBorderTopProperty:
@@ -3076,6 +3080,8 @@
     case kBackgroundSizeProperty:
     case kBorderBottomProperty:
     case kBorderBottomColorProperty:
+    case kBorderBottomLeftRadiusProperty:
+    case kBorderBottomRightRadiusProperty:
     case kBorderBottomWidthProperty:
     case kBorderColorProperty:
     case kBorderLeftProperty:
@@ -3088,6 +3094,8 @@
     case kBorderStyleProperty:
     case kBorderTopProperty:
     case kBorderTopColorProperty:
+    case kBorderTopLeftRadiusProperty:
+    case kBorderTopRightRadiusProperty:
     case kBorderTopWidthProperty:
     case kBorderProperty:
     case kBorderWidthProperty:
diff --git a/src/cobalt/cssom/computed_style_test.cc b/src/cobalt/cssom/computed_style_test.cc
index 26e0064..cfa0183 100644
--- a/src/cobalt/cssom/computed_style_test.cc
+++ b/src/cobalt/cssom/computed_style_test.cc
@@ -774,7 +774,8 @@
 TEST(PromoteToComputedStyle, BorderRadiusEmToPixel) {
   scoped_refptr<CSSComputedStyleData> computed_style(
       new CSSComputedStyleData());
-  computed_style->set_border_radius(new LengthValue(3, kFontSizesAkaEmUnit));
+  computed_style->set_border_top_left_radius(
+      new LengthValue(3, kFontSizesAkaEmUnit));
 
   scoped_refptr<CSSComputedStyleData> parent_computed_style(
       new CSSComputedStyleData());
@@ -784,10 +785,12 @@
   PromoteToComputedStyle(computed_style, parent_computed_style_declaration,
                          parent_computed_style, math::Size(), NULL);
 
-  LengthValue* border_radius = base::polymorphic_downcast<LengthValue*>(
-      computed_style->border_radius().get());
-  EXPECT_FLOAT_EQ(48.0f, border_radius->value());
-  EXPECT_EQ(kPixelsUnit, border_radius->unit());
+  LengthValue* border_top_left_radius =
+      base::polymorphic_downcast<LengthValue*>(
+          computed_style->border_top_left_radius().get());
+  ASSERT_TRUE(border_top_left_radius);
+  EXPECT_FLOAT_EQ(48.0f, border_top_left_radius->value());
+  EXPECT_EQ(kPixelsUnit, border_top_left_radius->unit());
 }
 
 TEST(PromoteToComputedStyle, BorderColorWithInitialValue) {
diff --git a/src/cobalt/cssom/css_computed_style_data.cc b/src/cobalt/cssom/css_computed_style_data.cc
index e0d75a2..492e046 100644
--- a/src/cobalt/cssom/css_computed_style_data.cc
+++ b/src/cobalt/cssom/css_computed_style_data.cc
@@ -167,19 +167,23 @@
     case kBackgroundProperty:
     case kBackgroundRepeatProperty:
     case kBackgroundSizeProperty:
-    case kBorderBottomStyleProperty:
+    case kBorderBottomLeftRadiusProperty:
     case kBorderBottomProperty:
+    case kBorderBottomRightRadiusProperty:
+    case kBorderBottomStyleProperty:
     case kBorderColorProperty:
     case kBorderLeftProperty:
     case kBorderLeftStyleProperty:
     case kBorderProperty:
+    case kBorderRadiusProperty:
     case kBorderRightProperty:
     case kBorderRightStyleProperty:
     case kBorderStyleProperty:
+    case kBorderTopLeftRadiusProperty:
     case kBorderTopProperty:
+    case kBorderTopRightRadiusProperty:
     case kBorderTopStyleProperty:
     case kBorderWidthProperty:
-    case kBorderRadiusProperty:
     case kBottomProperty:
     case kBoxShadowProperty:
     case kColorProperty:
diff --git a/src/cobalt/cssom/css_computed_style_data.h b/src/cobalt/cssom/css_computed_style_data.h
index ab2c0fc..88d90a6 100644
--- a/src/cobalt/cssom/css_computed_style_data.h
+++ b/src/cobalt/cssom/css_computed_style_data.h
@@ -275,11 +275,38 @@
     SetPropertyValue(kBorderLeftWidthProperty, border_left_width);
   }
 
-  const scoped_refptr<PropertyValue>& border_radius() const {
-    return GetPropertyValueReference(kBorderRadiusProperty);
+  const scoped_refptr<PropertyValue>& border_top_left_radius() const {
+    return GetPropertyValueReference(kBorderTopLeftRadiusProperty);
   }
-  void set_border_radius(const scoped_refptr<PropertyValue>& border_radius) {
-    SetPropertyValue(kBorderRadiusProperty, border_radius);
+  void set_border_top_left_radius(
+      const scoped_refptr<PropertyValue>& border_top_left_radius) {
+    SetPropertyValue(kBorderTopLeftRadiusProperty, border_top_left_radius);
+  }
+
+  const scoped_refptr<PropertyValue>& border_top_right_radius() const {
+    return GetPropertyValueReference(kBorderTopRightRadiusProperty);
+  }
+  void set_border_top_right_radius(
+      const scoped_refptr<PropertyValue>& border_top_right_radius) {
+    SetPropertyValue(kBorderTopRightRadiusProperty, border_top_right_radius);
+  }
+
+  const scoped_refptr<PropertyValue>& border_bottom_right_radius() const {
+    return GetPropertyValueReference(kBorderBottomRightRadiusProperty);
+  }
+  void set_border_bottom_right_radius(
+      const scoped_refptr<PropertyValue>& border_bottom_right_radius) {
+    SetPropertyValue(kBorderBottomRightRadiusProperty,
+                     border_bottom_right_radius);
+  }
+
+  const scoped_refptr<PropertyValue>& border_bottom_left_radius() const {
+    return GetPropertyValueReference(kBorderBottomLeftRadiusProperty);
+  }
+  void set_border_bottom_left_radius(
+      const scoped_refptr<PropertyValue>& border_bottom_left_radius) {
+    SetPropertyValue(kBorderBottomLeftRadiusProperty,
+                     border_bottom_left_radius);
   }
 
   const scoped_refptr<PropertyValue>& bottom() const {
diff --git a/src/cobalt/cssom/css_computed_style_data_test.cc b/src/cobalt/cssom/css_computed_style_data_test.cc
index f7b4f0c..de9ac9d 100644
--- a/src/cobalt/cssom/css_computed_style_data_test.cc
+++ b/src/cobalt/cssom/css_computed_style_data_test.cc
@@ -127,20 +127,69 @@
 TEST(CSSComputedStyleDataTest, BorderRadiusSettersAndGettersAreConsistent) {
   scoped_refptr<CSSComputedStyleData> style = new CSSComputedStyleData();
 
-  EXPECT_EQ(GetPropertyInitialValue(kBorderRadiusProperty),
-            style->border_radius());
-  EXPECT_EQ(style->border_radius(),
-            style->GetPropertyValue(kBorderRadiusProperty));
+  EXPECT_EQ(GetPropertyInitialValue(kBorderTopLeftRadiusProperty),
+            style->border_top_left_radius());
+  EXPECT_EQ(style->border_top_left_radius(),
+            style->GetPropertyValue(kBorderTopLeftRadiusProperty));
 
-  style->set_border_radius(KeywordValue::GetInitial());
-  EXPECT_EQ(KeywordValue::GetInitial(), style->border_radius());
+  style->set_border_top_left_radius(KeywordValue::GetInitial());
+  EXPECT_EQ(KeywordValue::GetInitial(), style->border_top_left_radius());
   EXPECT_EQ(KeywordValue::GetInitial(),
-            style->GetPropertyValue(kBorderRadiusProperty));
+            style->GetPropertyValue(kBorderTopLeftRadiusProperty));
 
-  style->SetPropertyValue(kBorderRadiusProperty, KeywordValue::GetInherit());
-  EXPECT_EQ(KeywordValue::GetInherit(), style->border_radius());
+  style->SetPropertyValue(kBorderTopLeftRadiusProperty,
+                          KeywordValue::GetInherit());
+  EXPECT_EQ(KeywordValue::GetInherit(), style->border_top_left_radius());
   EXPECT_EQ(KeywordValue::GetInherit(),
-            style->GetPropertyValue(kBorderRadiusProperty));
+            style->GetPropertyValue(kBorderTopLeftRadiusProperty));
+
+  EXPECT_EQ(GetPropertyInitialValue(kBorderTopRightRadiusProperty),
+            style->border_top_right_radius());
+  EXPECT_EQ(style->border_top_right_radius(),
+            style->GetPropertyValue(kBorderTopRightRadiusProperty));
+
+  style->set_border_top_right_radius(KeywordValue::GetInitial());
+  EXPECT_EQ(KeywordValue::GetInitial(), style->border_top_right_radius());
+  EXPECT_EQ(KeywordValue::GetInitial(),
+            style->GetPropertyValue(kBorderTopRightRadiusProperty));
+
+  style->SetPropertyValue(kBorderTopRightRadiusProperty,
+                          KeywordValue::GetInherit());
+  EXPECT_EQ(KeywordValue::GetInherit(), style->border_top_right_radius());
+  EXPECT_EQ(KeywordValue::GetInherit(),
+            style->GetPropertyValue(kBorderTopRightRadiusProperty));
+
+  EXPECT_EQ(GetPropertyInitialValue(kBorderBottomRightRadiusProperty),
+            style->border_bottom_right_radius());
+  EXPECT_EQ(style->border_bottom_right_radius(),
+            style->GetPropertyValue(kBorderBottomRightRadiusProperty));
+
+  style->set_border_bottom_right_radius(KeywordValue::GetInitial());
+  EXPECT_EQ(KeywordValue::GetInitial(), style->border_bottom_right_radius());
+  EXPECT_EQ(KeywordValue::GetInitial(),
+            style->GetPropertyValue(kBorderBottomRightRadiusProperty));
+
+  style->SetPropertyValue(kBorderBottomRightRadiusProperty,
+                          KeywordValue::GetInherit());
+  EXPECT_EQ(KeywordValue::GetInherit(), style->border_bottom_right_radius());
+  EXPECT_EQ(KeywordValue::GetInherit(),
+            style->GetPropertyValue(kBorderBottomRightRadiusProperty));
+
+  EXPECT_EQ(GetPropertyInitialValue(kBorderBottomLeftRadiusProperty),
+            style->border_bottom_left_radius());
+  EXPECT_EQ(style->border_bottom_left_radius(),
+            style->GetPropertyValue(kBorderBottomLeftRadiusProperty));
+
+  style->set_border_bottom_left_radius(KeywordValue::GetInitial());
+  EXPECT_EQ(KeywordValue::GetInitial(), style->border_bottom_left_radius());
+  EXPECT_EQ(KeywordValue::GetInitial(),
+            style->GetPropertyValue(kBorderBottomLeftRadiusProperty));
+
+  style->SetPropertyValue(kBorderBottomLeftRadiusProperty,
+                          KeywordValue::GetInherit());
+  EXPECT_EQ(KeywordValue::GetInherit(), style->border_bottom_left_radius());
+  EXPECT_EQ(KeywordValue::GetInherit(),
+            style->GetPropertyValue(kBorderBottomLeftRadiusProperty));
 }
 
 TEST(CSSComputedStyleDataTest, BorderTopColorSettersAndGettersAreConsistent) {
diff --git a/src/cobalt/cssom/css_declared_style_data_test.cc b/src/cobalt/cssom/css_declared_style_data_test.cc
index ae9a990..b9b0a5e 100644
--- a/src/cobalt/cssom/css_declared_style_data_test.cc
+++ b/src/cobalt/cssom/css_declared_style_data_test.cc
@@ -85,12 +85,33 @@
 TEST(CSSDeclaredStyleDataTest, BorderRadiusSettersAndGettersAreConsistent) {
   scoped_refptr<CSSDeclaredStyleData> style = new CSSDeclaredStyleData();
 
-  EXPECT_FALSE(style->GetPropertyValue(kBorderRadiusProperty));
+  EXPECT_FALSE(style->GetPropertyValue(kBorderTopLeftRadiusProperty));
 
-  style->SetPropertyValueAndImportance(kBorderRadiusProperty,
+  style->SetPropertyValueAndImportance(kBorderTopLeftRadiusProperty,
                                        KeywordValue::GetInherit(), false);
   EXPECT_EQ(KeywordValue::GetInherit(),
-            style->GetPropertyValue(kBorderRadiusProperty));
+            style->GetPropertyValue(kBorderTopLeftRadiusProperty));
+
+  EXPECT_FALSE(style->GetPropertyValue(kBorderTopRightRadiusProperty));
+
+  style->SetPropertyValueAndImportance(kBorderTopRightRadiusProperty,
+                                       KeywordValue::GetInherit(), false);
+  EXPECT_EQ(KeywordValue::GetInherit(),
+            style->GetPropertyValue(kBorderTopRightRadiusProperty));
+
+  EXPECT_FALSE(style->GetPropertyValue(kBorderBottomRightRadiusProperty));
+
+  style->SetPropertyValueAndImportance(kBorderBottomRightRadiusProperty,
+                                       KeywordValue::GetInherit(), false);
+  EXPECT_EQ(KeywordValue::GetInherit(),
+            style->GetPropertyValue(kBorderBottomRightRadiusProperty));
+
+  EXPECT_FALSE(style->GetPropertyValue(kBorderBottomLeftRadiusProperty));
+
+  style->SetPropertyValueAndImportance(kBorderBottomLeftRadiusProperty,
+                                       KeywordValue::GetInherit(), false);
+  EXPECT_EQ(KeywordValue::GetInherit(),
+            style->GetPropertyValue(kBorderBottomLeftRadiusProperty));
 }
 
 TEST(CSSDeclaredStyleDataTest, BorderTopColorSettersAndGettersAreConsistent) {
diff --git a/src/cobalt/cssom/css_declared_style_declaration_test.cc b/src/cobalt/cssom/css_declared_style_declaration_test.cc
index 00eb1f1..1975f98 100644
--- a/src/cobalt/cssom/css_declared_style_declaration_test.cc
+++ b/src/cobalt/cssom/css_declared_style_declaration_test.cc
@@ -183,6 +183,38 @@
   style->set_border_bottom_color(border_bottom_color, NULL);
 }
 
+TEST(CSSDeclaredStyleDeclarationTest, BorderBottomLeftRadiusSetter) {
+  testing::MockCSSParser css_parser;
+  scoped_refptr<CSSDeclaredStyleDeclaration> style =
+      new CSSDeclaredStyleDeclaration(&css_parser);
+
+  const std::string border_bottom_left_radius = "0.2em";
+  MockMutationObserver observer;
+  style->set_mutation_observer(&observer);
+
+  EXPECT_CALL(css_parser, ParsePropertyIntoDeclarationData(
+                              GetPropertyName(kBorderBottomLeftRadiusProperty),
+                              border_bottom_left_radius, _, _));
+  EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+  style->set_border_bottom_left_radius(border_bottom_left_radius, NULL);
+}
+
+TEST(CSSDeclaredStyleDeclarationTest, BorderBottomRightRadiusSetter) {
+  testing::MockCSSParser css_parser;
+  scoped_refptr<CSSDeclaredStyleDeclaration> style =
+      new CSSDeclaredStyleDeclaration(&css_parser);
+
+  const std::string border_bottom_right_radius = "0.2em";
+  MockMutationObserver observer;
+  style->set_mutation_observer(&observer);
+
+  EXPECT_CALL(css_parser, ParsePropertyIntoDeclarationData(
+                              GetPropertyName(kBorderBottomRightRadiusProperty),
+                              border_bottom_right_radius, _, _));
+  EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+  style->set_border_bottom_right_radius(border_bottom_right_radius, NULL);
+}
+
 TEST(CSSDeclaredStyleDeclarationTest, BorderBottomStyleSetter) {
   testing::MockCSSParser css_parser;
   scoped_refptr<CSSDeclaredStyleDeclaration> style =
@@ -391,6 +423,38 @@
   style->set_border_style(border_style, NULL);
 }
 
+TEST(CSSDeclaredStyleDeclarationTest, BorderTopLeftRadiusSetter) {
+  testing::MockCSSParser css_parser;
+  scoped_refptr<CSSDeclaredStyleDeclaration> style =
+      new CSSDeclaredStyleDeclaration(&css_parser);
+
+  const std::string border_top_left_radius = "0.2em";
+  MockMutationObserver observer;
+  style->set_mutation_observer(&observer);
+
+  EXPECT_CALL(css_parser, ParsePropertyIntoDeclarationData(
+                              GetPropertyName(kBorderTopLeftRadiusProperty),
+                              border_top_left_radius, _, _));
+  EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+  style->set_border_top_left_radius(border_top_left_radius, NULL);
+}
+
+TEST(CSSDeclaredStyleDeclarationTest, BorderTopRightRadiusSetter) {
+  testing::MockCSSParser css_parser;
+  scoped_refptr<CSSDeclaredStyleDeclaration> style =
+      new CSSDeclaredStyleDeclaration(&css_parser);
+
+  const std::string border_top_right_radius = "0.2em";
+  MockMutationObserver observer;
+  style->set_mutation_observer(&observer);
+
+  EXPECT_CALL(css_parser, ParsePropertyIntoDeclarationData(
+                              GetPropertyName(kBorderTopRightRadiusProperty),
+                              border_top_right_radius, _, _));
+  EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+  style->set_border_top_right_radius(border_top_right_radius, NULL);
+}
+
 TEST(CSSDeclaredStyleDeclarationTest, BorderTopSetter) {
   testing::MockCSSParser css_parser;
   scoped_refptr<CSSDeclaredStyleDeclaration> style =
diff --git a/src/cobalt/cssom/css_style_declaration.cc b/src/cobalt/cssom/css_style_declaration.cc
index 7736040..e2e62fd 100644
--- a/src/cobalt/cssom/css_style_declaration.cc
+++ b/src/cobalt/cssom/css_style_declaration.cc
@@ -428,6 +428,54 @@
                               exception_state);
 }
 
+std::string CSSStyleDeclaration::border_top_left_radius(
+    script::ExceptionState* /*exception_state*/) const {
+  return GetDeclaredPropertyValueStringByKey(kBorderTopLeftRadiusProperty);
+}
+
+void CSSStyleDeclaration::set_border_top_left_radius(
+    const std::string& border_top_left_radius,
+    script::ExceptionState* exception_state) {
+  SetPropertyValueStringByKey(kBorderTopLeftRadiusProperty,
+                              border_top_left_radius, exception_state);
+}
+
+std::string CSSStyleDeclaration::border_top_right_radius(
+    script::ExceptionState* /*exception_state*/) const {
+  return GetDeclaredPropertyValueStringByKey(kBorderTopRightRadiusProperty);
+}
+
+void CSSStyleDeclaration::set_border_top_right_radius(
+    const std::string& border_top_right_radius,
+    script::ExceptionState* exception_state) {
+  SetPropertyValueStringByKey(kBorderTopRightRadiusProperty,
+                              border_top_right_radius, exception_state);
+}
+
+std::string CSSStyleDeclaration::border_bottom_right_radius(
+    script::ExceptionState* /*exception_state*/) const {
+  return GetDeclaredPropertyValueStringByKey(kBorderBottomRightRadiusProperty);
+}
+
+void CSSStyleDeclaration::set_border_bottom_right_radius(
+    const std::string& border_bottom_right_radius,
+    script::ExceptionState* exception_state) {
+  SetPropertyValueStringByKey(kBorderBottomRightRadiusProperty,
+                              border_bottom_right_radius, exception_state);
+}
+
+std::string CSSStyleDeclaration::border_bottom_left_radius(
+    script::ExceptionState* /*exception_state*/) const {
+  return GetDeclaredPropertyValueStringByKey(kBorderBottomLeftRadiusProperty);
+}
+
+void CSSStyleDeclaration::set_border_bottom_left_radius(
+    const std::string& border_bottom_left_radius,
+    script::ExceptionState* exception_state) {
+  SetPropertyValueStringByKey(kBorderBottomLeftRadiusProperty,
+                              border_bottom_left_radius, exception_state);
+}
+
 std::string CSSStyleDeclaration::bottom(
     script::ExceptionState* /*exception_state*/) const {
   return GetDeclaredPropertyValueStringByKey(kBottomProperty);
diff --git a/src/cobalt/cssom/css_style_declaration.h b/src/cobalt/cssom/css_style_declaration.h
index 42d159d..0cda61a 100644
--- a/src/cobalt/cssom/css_style_declaration.h
+++ b/src/cobalt/cssom/css_style_declaration.h
@@ -116,6 +116,18 @@
   void set_border_bottom_color(const std::string& border_bottom_color,
                                script::ExceptionState* exception_state);
 
+  std::string border_bottom_left_radius(
+      script::ExceptionState* exception_state) const;
+  void set_border_bottom_left_radius(
+      const std::string& border_bottom_left_radius,
+      script::ExceptionState* exception_state);
+
+  std::string border_bottom_right_radius(
+      script::ExceptionState* exception_state) const;
+  void set_border_bottom_right_radius(
+      const std::string& border_bottom_right_radius,
+      script::ExceptionState* exception_state);
+
   std::string border_bottom_style(
       script::ExceptionState* exception_state) const;
   void set_border_bottom_style(const std::string& border_bottom_style,
@@ -174,6 +186,16 @@
   void set_border_top_color(const std::string& border_top_color,
                             script::ExceptionState* exception_state);
 
+  std::string border_top_left_radius(
+      script::ExceptionState* exception_state) const;
+  void set_border_top_left_radius(const std::string& border_top_left_radius,
+                                  script::ExceptionState* exception_state);
+
+  std::string border_top_right_radius(
+      script::ExceptionState* exception_state) const;
+  void set_border_top_right_radius(const std::string& border_top_right_radius,
+                                   script::ExceptionState* exception_state);
+
   std::string border_top_style(script::ExceptionState* exception_state) const;
   void set_border_top_style(const std::string& border_top_style,
                             script::ExceptionState* exception_state);
diff --git a/src/cobalt/cssom/css_style_declaration.idl b/src/cobalt/cssom/css_style_declaration.idl
index 3a92ed1..25101cf 100644
--- a/src/cobalt/cssom/css_style_declaration.idl
+++ b/src/cobalt/cssom/css_style_declaration.idl
@@ -33,6 +33,8 @@
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString border;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderBottom;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderBottomColor;
+  [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderBottomLeftRadius;
+  [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderBottomRightRadius;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderBottomStyle;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderBottomWidth;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderColor;
@@ -48,6 +50,8 @@
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderStyle;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderTop;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderTopColor;
+  [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderTopLeftRadius;
+  [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderTopRightRadius;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderTopStyle;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderTopWidth;
   [TreatNullAs=EmptyString, RaisesException] attribute DOMString borderWidth;
diff --git a/src/cobalt/cssom/property_definitions.cc b/src/cobalt/cssom/property_definitions.cc
index ac7a1cf..eced1da 100644
--- a/src/cobalt/cssom/property_definitions.cc
+++ b/src/cobalt/cssom/property_definitions.cc
@@ -344,12 +344,30 @@
                         kImpactsBoxSizesYes, kImpactsBoxCrossReferencesNo,
                         new LengthValue(3, kPixelsUnit));
 
-  // Cobalt only support a single length value that applies to all borders.
   //   https://www.w3.org/TR/css3-background/#the-border-radius
+  SetPropertyDefinition(kBorderTopLeftRadiusProperty, "border-top-left-radius",
+                        kInheritedNo, kAnimatableNo,
+                        kImpactsChildDeclaredStyleNo, kImpactsBoxGenerationNo,
+                        kImpactsBoxSizesNo, kImpactsBoxCrossReferencesNo,
+                        new LengthValue(0, kPixelsUnit));
+
+  SetPropertyDefinition(kBorderTopRightRadiusProperty,
+                        "border-top-right-radius", kInheritedNo, kAnimatableNo,
+                        kImpactsChildDeclaredStyleNo, kImpactsBoxGenerationNo,
+                        kImpactsBoxSizesNo, kImpactsBoxCrossReferencesNo,
+                        new LengthValue(0, kPixelsUnit));
+
   SetPropertyDefinition(
-      kBorderRadiusProperty, "border-radius", kInheritedNo, kAnimatableNo,
-      kImpactsChildDeclaredStyleNo, kImpactsBoxGenerationNo, kImpactsBoxSizesNo,
-      kImpactsBoxCrossReferencesNo, new LengthValue(0, kPixelsUnit));
+      kBorderBottomRightRadiusProperty, "border-bottom-right-radius",
+      kInheritedNo, kAnimatableNo, kImpactsChildDeclaredStyleNo,
+      kImpactsBoxGenerationNo, kImpactsBoxSizesNo, kImpactsBoxCrossReferencesNo,
+      new LengthValue(0, kPixelsUnit));
+
+  SetPropertyDefinition(
+      kBorderBottomLeftRadiusProperty, "border-bottom-left-radius",
+      kInheritedNo, kAnimatableNo, kImpactsChildDeclaredStyleNo,
+      kImpactsBoxGenerationNo, kImpactsBoxSizesNo, kImpactsBoxCrossReferencesNo,
+      new LengthValue(0, kPixelsUnit));
 
   // https://www.w3.org/TR/CSS2/visuren.html#propdef-bottom
   SetPropertyDefinition(kBottomProperty, "bottom", kInheritedNo, kAnimatableNo,
@@ -780,6 +798,15 @@
   SetShorthandPropertyDefinition(kBorderWidthProperty, "border-width",
                                  border_width_longhand_properties);
 
+  //   https://www.w3.org/TR/css3-background/#border-radius
+  LonghandPropertySet border_radius_longhand_properties;
+  border_radius_longhand_properties.insert(kBorderTopLeftRadiusProperty);
+  border_radius_longhand_properties.insert(kBorderTopRightRadiusProperty);
+  border_radius_longhand_properties.insert(kBorderBottomRightRadiusProperty);
+  border_radius_longhand_properties.insert(kBorderBottomLeftRadiusProperty);
+  SetShorthandPropertyDefinition(kBorderRadiusProperty, "border-radius",
+                                 border_radius_longhand_properties);
+
   //   https://www.w3.org/TR/css3-background/#border
   LonghandPropertySet border_longhand_properties;
   border_longhand_properties.insert(kBorderColorProperty);
@@ -1423,6 +1450,20 @@
       }
       return kNoneProperty;
 
+    case 22:
+      if (LowerCaseEqualsASCII(property_name,
+                               GetPropertyName(kBorderTopLeftRadiusProperty))) {
+        return kBorderTopLeftRadiusProperty;
+      }
+      return kNoneProperty;
+
+    case 23:
+      if (LowerCaseEqualsASCII(
+              property_name, GetPropertyName(kBorderTopRightRadiusProperty))) {
+        return kBorderTopRightRadiusProperty;
+      }
+      return kNoneProperty;
+
     case 25:
       if (LowerCaseEqualsASCII(
               property_name,
@@ -1434,11 +1475,21 @@
               GetPropertyName(kAnimationTimingFunctionProperty))) {
         return kAnimationTimingFunctionProperty;
       }
+      if (LowerCaseEqualsASCII(
+              property_name,
+              GetPropertyName(kBorderBottomLeftRadiusProperty))) {
+        return kBorderBottomLeftRadiusProperty;
+      }
       return kNoneProperty;
 
     case 26:
       if (LowerCaseEqualsASCII(
               property_name,
+              GetPropertyName(kBorderBottomRightRadiusProperty))) {
+        return kBorderBottomRightRadiusProperty;
+      }
+      if (LowerCaseEqualsASCII(
+              property_name,
               GetPropertyName(kTransitionTimingFunctionProperty))) {
         return kTransitionTimingFunctionProperty;
       }
diff --git a/src/cobalt/cssom/property_definitions.h b/src/cobalt/cssom/property_definitions.h
index 8fbddbe..3ba699a 100644
--- a/src/cobalt/cssom/property_definitions.h
+++ b/src/cobalt/cssom/property_definitions.h
@@ -53,16 +53,19 @@
   kBackgroundRepeatProperty,
   kBackgroundSizeProperty,
   kBorderBottomColorProperty,
+  kBorderBottomLeftRadiusProperty,
+  kBorderBottomRightRadiusProperty,
   kBorderBottomStyleProperty,
   kBorderBottomWidthProperty,
   kBorderLeftColorProperty,
   kBorderLeftStyleProperty,
   kBorderLeftWidthProperty,
-  kBorderRadiusProperty,
   kBorderRightColorProperty,
   kBorderRightStyleProperty,
   kBorderRightWidthProperty,
   kBorderTopColorProperty,
+  kBorderTopLeftRadiusProperty,
+  kBorderTopRightRadiusProperty,
   kBorderTopStyleProperty,
   kBorderTopWidthProperty,
   kBottomProperty,
@@ -135,6 +138,7 @@
   kBorderColorProperty,
   kBorderLeftProperty,
   kBorderProperty,
+  kBorderRadiusProperty,
   kBorderRightProperty,
   kBorderStyleProperty,
   kBorderTopProperty,
diff --git a/src/cobalt/doc/splash_screen.md b/src/cobalt/doc/splash_screen.md
index 201d5e1..9122555 100644
--- a/src/cobalt/doc/splash_screen.md
+++ b/src/cobalt/doc/splash_screen.md
@@ -76,6 +76,15 @@
 self-contained splash screen document. The document must not violate the Content
 Security Policy. The splash screen is treated as a script resource by the CSP.
 
+### Caching implementation requirements
+
+In order to cache the application-provided splash screen, Cobalt will attempt
+to create directories and write files into the directory returned from a call to
+`SbSystemGetPath(kSbSystemPathCacheDirectory, ...)`.  Cobalt will expect the
+data that it writes into that directory to persist across process instances.
+Cobalt will also need to read the cached splash screen from the cache directory
+when starting up.
+
 ## Application-specific splash screens
 
 On systems that plan to support multiple Cobalt-based applications, an
@@ -96,5 +105,3 @@
   * `h5vcc-embedded://black_splash_screen.html` - a black splash screen
   * `h5vcc-embedded://cobalt_splash_screen.html` - a splash screen showing the
     Cobalt logo
-  * `h5vcc-embedded://youtube_splash_screen.html` - a splash screen showing the
-    YouTube logo
diff --git a/src/cobalt/dom/csp_delegate.cc b/src/cobalt/dom/csp_delegate.cc
index fc9d41f..0bcf6f0 100644
--- a/src/cobalt/dom/csp_delegate.cc
+++ b/src/cobalt/dom/csp_delegate.cc
@@ -25,9 +25,10 @@
 
 CspDelegateSecure::CspDelegateSecure(
     scoped_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    const std::string& location_policy,
+    const std::string& location_policy, csp::CSPHeaderPolicy require_csp,
     const base::Closure& policy_changed_callback) {
   location_policy_ = location_policy;
+  require_csp_ = require_csp;
   was_header_received_ = false;
   policy_changed_callback_ = policy_changed_callback;
 
@@ -53,10 +54,15 @@
   // we check our default navigation policy, to permit navigation to
   // and from the main site and error pages, and disallow everything else.
   if (!was_header_received_) {
+    bool should_allow = false;
     if (type == kLocation) {
-      return csp_->AllowNavigateToSource(url, redirect_status);
+      should_allow = csp_->AllowNavigateToSource(url, redirect_status);
+    }
+    if (require_csp_ == csp::kCSPRequired || should_allow) {
+      return should_allow;
     } else {
-      return false;
+      DLOG(WARNING) << "Page must include Content-Security-Policy header, it "
+                       "will fail to load in production builds of Cobalt!";
     }
   }
 
diff --git a/src/cobalt/dom/csp_delegate.h b/src/cobalt/dom/csp_delegate.h
index 2660ea7..17a797c 100644
--- a/src/cobalt/dom/csp_delegate.h
+++ b/src/cobalt/dom/csp_delegate.h
@@ -105,6 +105,7 @@
  public:
   CspDelegateSecure(scoped_ptr<CspViolationReporter> violation_reporter,
                     const GURL& url, const std::string& location_policy,
+                    csp::CSPHeaderPolicy require_csp,
                     const base::Closure& policy_changed_callback);
   ~CspDelegateSecure();
 
@@ -156,6 +157,9 @@
   // in a <meta> tag.
   base::Closure policy_changed_callback_;
 
+  // Whether Cobalt is forbidden to render without receiving CSP header.
+  csp::CSPHeaderPolicy require_csp_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CspDelegateSecure);
 };
diff --git a/src/cobalt/dom/csp_delegate_factory.cc b/src/cobalt/dom/csp_delegate_factory.cc
index 55a4f15..e20c061 100644
--- a/src/cobalt/dom/csp_delegate_factory.cc
+++ b/src/cobalt/dom/csp_delegate_factory.cc
@@ -46,6 +46,7 @@
 CspDelegate* CreateInsecureDelegate(
     scoped_ptr<CspViolationReporter> /*violation_reporter*/,
     const GURL& /*url*/, const std::string& /*location_policy*/,
+    csp::CSPHeaderPolicy /*requre_csp*/,
     const base::Closure& /*policy_changed_callback*/,
     int insecure_allowed_token) {
   if (InsecureAllowed(insecure_allowed_token)) {
@@ -58,11 +59,11 @@
 
 CspDelegate* CreateSecureDelegate(
     scoped_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    const std::string& location_policy,
+    const std::string& location_policy, csp::CSPHeaderPolicy require_csp,
     const base::Closure& policy_changed_callback,
     int /*insecure_allowed_token*/) {
   return new CspDelegateSecure(violation_reporter.Pass(), url, location_policy,
-                               policy_changed_callback);
+                               require_csp, policy_changed_callback);
 }
 }  // namespace
 
@@ -86,11 +87,11 @@
 scoped_ptr<CspDelegate> CspDelegateFactory::Create(
     CspEnforcementType type,
     scoped_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    const std::string& location_policy,
+    const std::string& location_policy, csp::CSPHeaderPolicy require_csp,
     const base::Closure& policy_changed_callback, int insecure_allowed_token) {
-  scoped_ptr<CspDelegate> delegate(
-      method_[type](violation_reporter.Pass(), url, location_policy,
-                    policy_changed_callback, insecure_allowed_token));
+  scoped_ptr<CspDelegate> delegate(method_[type](
+      violation_reporter.Pass(), url, location_policy, require_csp,
+      policy_changed_callback, insecure_allowed_token));
   return delegate.Pass();
 }
 
diff --git a/src/cobalt/dom/csp_delegate_factory.h b/src/cobalt/dom/csp_delegate_factory.h
index d7f3275..c732d37 100644
--- a/src/cobalt/dom/csp_delegate_factory.h
+++ b/src/cobalt/dom/csp_delegate_factory.h
@@ -21,6 +21,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
+#include "cobalt/csp/content_security_policy.h"
 #include "cobalt/dom/csp_delegate_type.h"
 #include "googleurl/src/gurl.h"
 
@@ -43,13 +44,13 @@
   scoped_ptr<CspDelegate> Create(
       CspEnforcementType type,
       scoped_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-      const std::string& location_policy,
+      const std::string& location_policy, csp::CSPHeaderPolicy require_csp,
       const base::Closure& policy_changed_callback,
       int insecure_allowed_token = 0);
 
   typedef CspDelegate* (*CspDelegateCreator)(
       scoped_ptr<CspViolationReporter> violation_reporter, const GURL&,
-      const std::string&, const base::Closure&, int);
+      const std::string&, csp::CSPHeaderPolicy, const base::Closure&, int);
 
 #if !defined(COBALT_FORCE_CSP)
   // Allow tests to have the factory create a different delegate type.
diff --git a/src/cobalt/dom/csp_delegate_test.cc b/src/cobalt/dom/csp_delegate_test.cc
index fccdcaf..f1df142 100644
--- a/src/cobalt/dom/csp_delegate_test.cc
+++ b/src/cobalt/dom/csp_delegate_test.cc
@@ -107,8 +107,9 @@
   mock_reporter_ = new StrictMock<MockViolationReporter>();
   scoped_ptr<CspViolationReporter> reporter(mock_reporter_);
 
-  csp_delegate_.reset(new CspDelegateSecure(
-      reporter.Pass(), origin, default_navigation_policy, base::Closure()));
+  csp_delegate_.reset(
+      new CspDelegateSecure(reporter.Pass(), origin, default_navigation_policy,
+                            csp::kCSPRequired, base::Closure()));
   std::string policy =
       base::StringPrintf("default-src none; %s 'self'", GetParam().directive);
   csp_delegate_->OnReceiveHeader(policy, csp::kHeaderTypeEnforce,
@@ -138,7 +139,7 @@
 TEST(CspDelegateFactoryTest, Secure) {
   scoped_ptr<CspDelegate> delegate = CspDelegateFactory::GetInstance()->Create(
       kCspEnforcementEnable, scoped_ptr<CspViolationReporter>(), GURL(),
-      std::string(), base::Closure());
+      std::string(), csp::kCSPRequired, base::Closure());
   EXPECT_TRUE(delegate != NULL);
 }
 
@@ -151,7 +152,7 @@
     scoped_ptr<CspDelegate> delegate =
         CspDelegateFactory::GetInstance()->Create(
             kCspEnforcementDisable, scoped_ptr<CspViolationReporter>(), GURL(),
-            std::string(), base::Closure());
+            std::string(), csp::kCSPRequired, base::Closure());
 
     scoped_ptr<CspDelegate> empty_delegate;
     EXPECT_EQ(empty_delegate, delegate.get());
@@ -165,7 +166,7 @@
   int token = CspDelegateFactory::GetInstance()->GetInsecureAllowedToken();
   scoped_ptr<CspDelegate> delegate = CspDelegateFactory::GetInstance()->Create(
       kCspEnforcementDisable, scoped_ptr<CspViolationReporter>(), GURL(),
-      std::string(), base::Closure(), token);
+      std::string(), csp::kCSPRequired, base::Closure(), token);
   EXPECT_TRUE(delegate != NULL);
 }
 
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
index d8b4c42..65c85c1 100644
--- a/src/cobalt/dom/custom_event_test.cc
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -70,13 +70,12 @@
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
-            std::string() /* default security policy */, kCspEnforcementEnable,
-            base::Closure() /* csp_policy_changed */,
+            std::string() /* default security policy */, csp::kCSPRequired,
+            kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
             base::Closure() /* ran_animation_frame_callbacks */,
-            base::Closure() /* window_close */,
+            dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */, NULL, NULL)) {
-    engine_ = script::JavaScriptEngine::CreateEngine(
-        script::JavaScriptEngine::Options());
+    engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index fa07b55..211b221 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -107,7 +107,7 @@
   csp_delegate_ =
       CspDelegateFactory::GetInstance()
           ->Create(options.csp_enforcement_mode, violation_reporter.Pass(),
-                   options.url, options.location_policy,
+                   options.url, options.location_policy, options.require_csp,
                    options.csp_policy_changed_callback,
                    options.csp_insecure_allowed_token)
           .Pass();
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 9a6c527..42c824f 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -111,6 +111,7 @@
             network_bridge::CookieJar* cookie_jar,
             const network_bridge::PostSender& post_sender,
             const std::string& location_policy,
+            csp::CSPHeaderPolicy require_csp,
             CspEnforcementType csp_enforcement_mode,
             const base::Closure& csp_policy_changed_callback,
             int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0)
@@ -124,6 +125,7 @@
           cookie_jar(cookie_jar),
           post_sender(post_sender),
           location_policy(location_policy),
+          require_csp(require_csp),
           csp_enforcement_mode(csp_enforcement_mode),
           csp_policy_changed_callback(csp_policy_changed_callback),
           csp_insecure_allowed_token(csp_insecure_allowed_token),
@@ -139,6 +141,7 @@
     network_bridge::CookieJar* cookie_jar;
     network_bridge::PostSender post_sender;
     std::string location_policy;
+    csp::CSPHeaderPolicy require_csp;
     CspEnforcementType csp_enforcement_mode;
     base::Closure csp_policy_changed_callback;
     int csp_insecure_allowed_token;
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
index b8ead45..40a51d2 100644
--- a/src/cobalt/dom/error_event_test.cc
+++ b/src/cobalt/dom/error_event_test.cc
@@ -70,13 +70,12 @@
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
-            std::string() /* default security policy */, kCspEnforcementEnable,
-            base::Closure() /* csp_policy_changed */,
+            std::string() /* default security policy */, csp::kCSPRequired,
+            kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
             base::Closure() /* ran_animation_frame_callbacks */,
-            base::Closure() /* window_close */,
+            dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */, NULL, NULL)) {
-    engine_ = script::JavaScriptEngine::CreateEngine(
-        script::JavaScriptEngine::Options());
+    engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
     global_environment_->CreateGlobalObject(window_,
                                             environment_settings_.get());
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index 2b01566..32ef4f9 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -59,10 +59,10 @@
         dom_stat_tracker_.get(), url_, "", "en-US",
         base::Callback<void(const GURL&)>(), base::Bind(&StubErrorCallback),
         NULL, network_bridge::PostSender(),
-        std::string() /* default security policy */, dom::kCspEnforcementEnable,
-        base::Closure() /* csp_policy_changed */,
+        std::string() /* default security policy */, csp::kCSPRequired,
+        dom::kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
-        base::Closure() /* window_close */,
+        dom::Window::CloseCallback() /* window_close */,
         base::Closure() /* window_minimize */, NULL, NULL);
     global_environment_->CreateGlobalObject(window_, &environment_settings_);
   }
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 07b2f6d..25af450 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -99,10 +99,11 @@
                network_bridge::CookieJar* cookie_jar,
                const network_bridge::PostSender& post_sender,
                const std::string& default_security_policy,
+               csp::CSPHeaderPolicy require_csp,
                CspEnforcementType csp_enforcement_mode,
                const base::Closure& csp_policy_changed_callback,
                const base::Closure& ran_animation_frame_callbacks_callback,
-               const base::Closure& window_close_callback,
+               const CloseCallback& window_close_callback,
                const base::Closure& window_minimize_callback,
                const scoped_refptr<input::Camera3D>& camera_3d,
                const scoped_refptr<MediaSession>& media_session,
@@ -138,7 +139,7 @@
               performance_->timing()->GetNavigationStartClock(),
               navigation_callback, ParseUserAgentStyleSheet(css_parser),
               math::Size(width_, height_), cookie_jar, post_sender,
-              default_security_policy, csp_enforcement_mode,
+              default_security_policy, require_csp, csp_enforcement_mode,
               csp_policy_changed_callback, csp_insecure_allowed_token,
               dom_max_element_depth)))),
       document_loader_(NULL),
@@ -202,7 +203,8 @@
 void Window::Close() {
   LOG(INFO) << __func__;
   if (!window_close_callback_.is_null()) {
-    window_close_callback_.Run();
+    window_close_callback_.Run(
+        performance_->timing()->GetNavigationStartClock()->Now());
   }
 }
 
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 49c5135..48f44ff 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -102,6 +102,10 @@
       const base::SourceLocation&, const base::Closure&,
       const base::Callback<void(const std::string&)>&)>
       HTMLDecoderCreatorCallback;
+  // Callback that will be called when window.close() is called.  The
+  // base::TimeDelta parameter will contain the document's timeline time when
+  // close() was called.
+  typedef base::Callback<void(base::TimeDelta)> CloseCallback;
   typedef UrlRegistry<MediaSource> MediaSourceRegistry;
   enum ClockType {
     kClockTypeTestRunner,
@@ -134,10 +138,11 @@
       network_bridge::CookieJar* cookie_jar,
       const network_bridge::PostSender& post_sender,
       const std::string& default_security_policy,
+      csp::CSPHeaderPolicy require_csp,
       dom::CspEnforcementType csp_enforcement_mode,
       const base::Closure& csp_policy_changed_callback,
       const base::Closure& ran_animation_frame_callbacks_callback,
-      const base::Closure& window_close_callback,
+      const CloseCallback& window_close_callback,
       const base::Closure& window_minimize_callback,
       const scoped_refptr<input::Camera3D>& camera_3d,
       const scoped_refptr<cobalt::media_session::MediaSession>& media_session,
@@ -393,7 +398,7 @@
   scoped_refptr<Screen> screen_;
 
   const base::Closure ran_animation_frame_callbacks_callback_;
-  const base::Closure window_close_callback_;
+  const CloseCallback window_close_callback_;
   const base::Closure window_minimize_callback_;
 
   base::Callback<bool(const std::string&)> splash_screen_cache_callback_;
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index a35d5bc..65a8c1d 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -59,10 +59,10 @@
             base::Bind(&MockErrorCallback::Run,
                        base::Unretained(&mock_error_callback_)),
             NULL, network_bridge::PostSender(),
-            std::string() /* default security policy */, kCspEnforcementEnable,
-            base::Closure() /* csp_policy_changed */,
+            std::string() /* default security policy */, csp::kCSPRequired,
+            kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
             base::Closure() /* ran_animation_frame_callbacks */,
-            base::Closure() /* window_close */,
+            dom::Window::CloseCallback() /* window_close */,
             base::Closure() /* window_minimize */, NULL, NULL)) {}
 
   ~WindowTest() OVERRIDE {}
diff --git a/src/cobalt/dom_parser/html_decoder.cc b/src/cobalt/dom_parser/html_decoder.cc
index 1609e9d..7dba201 100644
--- a/src/cobalt/dom_parser/html_decoder.cc
+++ b/src/cobalt/dom_parser/html_decoder.cc
@@ -29,13 +29,14 @@
     const int dom_max_element_depth, const base::SourceLocation& input_location,
     const base::Closure& done_callback,
     const base::Callback<void(const std::string&)>& error_callback,
-    const bool should_run_scripts)
+    const bool should_run_scripts, const csp::CSPHeaderPolicy require_csp)
     : libxml_html_parser_wrapper_(new LibxmlHTMLParserWrapper(
           document, parent_node, reference_node, dom_max_element_depth,
           input_location, error_callback, should_run_scripts)),
       document_(document),
       done_callback_(done_callback),
-      should_run_scripts_(should_run_scripts) {}
+      should_run_scripts_(should_run_scripts),
+      require_csp_(require_csp) {}
 
 HTMLDecoder::~HTMLDecoder() {}
 
@@ -61,7 +62,8 @@
   }
 
   csp::ResponseHeaders csp_headers(headers);
-  if (document_->csp_delegate()->OnReceiveHeaders(csp_headers)) {
+  if (document_->csp_delegate()->OnReceiveHeaders(csp_headers) ||
+      require_csp_ == csp::kCSPOptional) {
     return loader::kLoadResponseContinue;
   } else {
     DLOG(ERROR) << "Failure receiving Content Security Policy headers";
diff --git a/src/cobalt/dom_parser/html_decoder.h b/src/cobalt/dom_parser/html_decoder.h
index 043ad4f..d9ca8b2 100644
--- a/src/cobalt/dom_parser/html_decoder.h
+++ b/src/cobalt/dom_parser/html_decoder.h
@@ -49,7 +49,8 @@
               const base::SourceLocation& input_location,
               const base::Closure& done_callback,
               const base::Callback<void(const std::string&)>& error_callback,
-              const bool should_run_scripts);
+              const bool should_run_scripts,
+              const csp::CSPHeaderPolicy require_csp);
 
   ~HTMLDecoder();
 
@@ -75,6 +76,9 @@
 
   const bool should_run_scripts_;
 
+  // If Cobalt user forbids rendering Cobalt without csp headers.
+  const csp::CSPHeaderPolicy require_csp_;
+
   DISALLOW_COPY_AND_ASSIGN(HTMLDecoder);
 };
 
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index d4e89bb..851aea3 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -77,11 +77,12 @@
 
 TEST_F(HTMLDecoderTest, CanParseEmptyDocument) {
   const std::string input = "";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -100,11 +101,12 @@
 TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlyNulls) {
   unsigned char temp[] = {0x0, 0x0};
 
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(reinterpret_cast<char*>(temp), sizeof(temp));
   html_decoder_->Finish();
 
@@ -119,11 +121,12 @@
 
 TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlySpaces) {
   const std::string input = "   ";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -138,11 +141,12 @@
 
 TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlyHTML) {
   const std::string input = "<html>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -157,11 +161,12 @@
 
 TEST_F(HTMLDecoderTest, CanParseDocumentWithOnlyBody) {
   const std::string input = "<body>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -176,11 +181,12 @@
 
 TEST_F(HTMLDecoderTest, DecodingWholeDocumentShouldAddImpliedTags) {
   const std::string input = "<p></p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -200,11 +206,12 @@
 
 TEST_F(HTMLDecoderTest, DecodingDocumentFragmentShouldNotAddImpliedTags) {
   const std::string input = "<p></p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -216,11 +223,12 @@
 
 TEST_F(HTMLDecoderTest, CanParseAttributesWithAndWithoutValue) {
   const std::string input = "<div a b=2 c d></div>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -241,11 +249,12 @@
 
 TEST_F(HTMLDecoderTest, CanParseIncompleteAttributesAssignment) {
   const std::string input = "<div a= b=2 c=></div>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -262,11 +271,12 @@
 
 TEST_F(HTMLDecoderTest, CanParseSelfClosingTags) {
   const std::string input = "<p><p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -281,11 +291,12 @@
 
 TEST_F(HTMLDecoderTest, CanParseNormalCharacters) {
   const std::string input = "<p>text</p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -301,11 +312,12 @@
 TEST_F(HTMLDecoderTest, ShouldIgnoreNonUTF8Input) {
   unsigned char temp[] = {0xff, 0xff};
 
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(reinterpret_cast<char*>(temp), sizeof(temp));
   html_decoder_->Finish();
 
@@ -315,11 +327,12 @@
 // Test a decimal and hex escaped supplementary (not in BMP) character.
 TEST_F(HTMLDecoderTest, CanParseEscapedCharacters) {
   const std::string input = "<p>&#128169;</p><p>&#x1f4a9;</p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -343,11 +356,12 @@
 // Test an escaped invalid Unicode character.
 TEST_F(HTMLDecoderTest, CanParseEscapedInvalidUnicodeCharacters) {
   const std::string input = "<p>&#xe62b;</p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -363,11 +377,12 @@
 // Test a UTF8 encoded supplementary (not in BMP) character.
 TEST_F(HTMLDecoderTest, CanParseUTF8EncodedSupplementaryCharacters) {
   const std::string input = "<p>💩</p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -386,11 +401,12 @@
   for (size_t first_chunk_size = 0; first_chunk_size < input.length();
        first_chunk_size++) {
     root_ = new dom::Element(document_, base::Token("element"));
-    html_decoder_.reset(new HTMLDecoder(
-        document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-        base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                    base::Unretained(&mock_error_callback_)),
-        true));
+    html_decoder_.reset(
+        new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                        source_location_, base::Closure(),
+                        base::Bind(&MockErrorCallback::Run,
+                                   base::Unretained(&mock_error_callback_)),
+                        true, csp::kCSPRequired));
 
     // This could cut the input in the middle of a UTF8 character.
     html_decoder_->DecodeChunk(input.c_str(), first_chunk_size);
@@ -414,11 +430,12 @@
 // The current version DOES NOT handle the error as outlined in the link above.
 TEST_F(HTMLDecoderTest, CanParseMisnestedTags1) {
   const std::string input = "<p>1<b>2<i>3</b>4</i>5</p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -441,11 +458,12 @@
 // The current version DOES NOT handle the error as outlined in the link above.
 TEST_F(HTMLDecoderTest, CanParseMisnestedTags2) {
   const std::string input = "<b>1<p>2</b>3</p>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -460,11 +478,12 @@
 
 TEST_F(HTMLDecoderTest, TagNamesShouldBeCaseInsensitive) {
   const std::string input = "<DIV></DIV>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -475,11 +494,12 @@
 
 TEST_F(HTMLDecoderTest, AttributesShouldBeCaseInsensitive) {
   const std::string input = "<div A></div>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, root_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, root_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
@@ -499,11 +519,12 @@
       "</head>"
       "<body>陈绮贞</body>"
       "</html>";
-  html_decoder_.reset(new HTMLDecoder(
-      document_, document_, NULL, kDOMMaxElementDepth, source_location_,
-      base::Closure(), base::Bind(&MockErrorCallback::Run,
-                                  base::Unretained(&mock_error_callback_)),
-      true));
+  html_decoder_.reset(
+      new HTMLDecoder(document_, document_, NULL, kDOMMaxElementDepth,
+                      source_location_, base::Closure(),
+                      base::Bind(&MockErrorCallback::Run,
+                                 base::Unretained(&mock_error_callback_)),
+                      true, csp::kCSPRequired));
   html_decoder_->DecodeChunk(input.c_str(), input.length());
   html_decoder_->Finish();
 
diff --git a/src/cobalt/dom_parser/parser.cc b/src/cobalt/dom_parser/parser.cc
index 03150ea..65e4473 100644
--- a/src/cobalt/dom_parser/parser.cc
+++ b/src/cobalt/dom_parser/parser.cc
@@ -31,7 +31,7 @@
       new dom::Document(html_element_context);
   HTMLDecoder html_decoder(document, document, NULL, dom_max_element_depth_,
                            input_location, base::Closure(), error_callback_,
-                           false);
+                           false, require_csp_);
   html_decoder.DecodeChunk(input.c_str(), input.length());
   html_decoder.Finish();
   return document;
@@ -57,9 +57,9 @@
     const scoped_refptr<dom::Node>& parent_node,
     const scoped_refptr<dom::Node>& reference_node,
     const base::SourceLocation& input_location) {
-  HTMLDecoder html_decoder(document, parent_node, reference_node,
-                           dom_max_element_depth_, input_location,
-                           base::Closure(), error_callback_, false);
+  HTMLDecoder html_decoder(
+      document, parent_node, reference_node, dom_max_element_depth_,
+      input_location, base::Closure(), error_callback_, false, require_csp_);
   html_decoder.DecodeChunk(input.c_str(), input.length());
   html_decoder.Finish();
 }
@@ -81,9 +81,9 @@
 scoped_ptr<loader::Decoder> Parser::ParseDocumentAsync(
     const scoped_refptr<dom::Document>& document,
     const base::SourceLocation& input_location) {
-  return scoped_ptr<loader::Decoder>(
-      new HTMLDecoder(document, document, NULL, dom_max_element_depth_,
-                      input_location, base::Closure(), error_callback_, true));
+  return scoped_ptr<loader::Decoder>(new HTMLDecoder(
+      document, document, NULL, dom_max_element_depth_, input_location,
+      base::Closure(), error_callback_, true, require_csp_));
 }
 
 scoped_ptr<loader::Decoder> Parser::ParseXMLDocumentAsync(
diff --git a/src/cobalt/dom_parser/parser.h b/src/cobalt/dom_parser/parser.h
index 0368850..d4cb9fc 100644
--- a/src/cobalt/dom_parser/parser.h
+++ b/src/cobalt/dom_parser/parser.h
@@ -19,6 +19,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "cobalt/csp/content_security_policy.h"
 #include "cobalt/dom/parser.h"
 
 namespace cobalt {
@@ -29,15 +30,19 @@
   Parser()
       : dom_max_element_depth_(kDefaultDOMMaxElementDepth),
         ALLOW_THIS_IN_INITIALIZER_LIST(error_callback_(
-            base::Bind(&Parser::ErrorCallback, base::Unretained(this)))) {}
+            base::Bind(&Parser::ErrorCallback, base::Unretained(this)))),
+        require_csp_(csp::kCSPRequired) {}
   explicit Parser(
       const base::Callback<void(const std::string&)>& error_callback)
       : dom_max_element_depth_(kDefaultDOMMaxElementDepth),
-        error_callback_(error_callback) {}
+        error_callback_(error_callback),
+        require_csp_(csp::kCSPRequired) {}
   Parser(const int dom_max_element_depth,
-         const base::Callback<void(const std::string&)>& error_callback)
+         const base::Callback<void(const std::string&)>& error_callback,
+         csp::CSPHeaderPolicy require_csp)
       : dom_max_element_depth_(dom_max_element_depth),
-        error_callback_(error_callback) {}
+        error_callback_(error_callback),
+        require_csp_(require_csp) {}
   ~Parser() OVERRIDE {}
 
   // From dom::Parser.
@@ -79,6 +84,10 @@
 
   void ErrorCallback(const std::string& error);
 
+  // Cobalt user can specify if they want to forbid Cobalt rendering without csp
+  // headers.
+  csp::CSPHeaderPolicy require_csp_;
+
   DISALLOW_COPY_AND_ASSIGN(Parser);
 };
 
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index ff4edae..38c644f 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -52,6 +52,7 @@
 using cobalt::render_tree::MatrixTransformNode;
 using cobalt::render_tree::OpacityFilter;
 using cobalt::render_tree::RectNode;
+using cobalt::render_tree::RoundedCorner;
 using cobalt::render_tree::RoundedCorners;
 using cobalt::render_tree::ViewportFilter;
 using cobalt::render_tree::animations::Animation;
@@ -546,24 +547,21 @@
   render_tree::CompositionNode::Builder border_node_builder(border_box_offset);
   AnimateNode::Builder animate_node_builder;
 
-  UsedBorderRadiusProvider border_radius_provider(GetBorderBoxSize());
-  computed_style()->border_radius()->Accept(&border_radius_provider);
+  base::optional<RoundedCorners> rounded_corners = ComputeRoundedCorners();
 
   // If we have rounded corners and a non-zero border, then we need to compute
   // the "inner" rounded corners, as the ones specified by CSS apply to the
   // outer border edge.
   base::optional<RoundedCorners> padding_rounded_corners_if_different;
-  if (border_radius_provider.rounded_corners() && !border_insets_.zero()) {
-    padding_rounded_corners_if_different =
-        border_radius_provider.rounded_corners()->Inset(math::InsetsF(
-            border_insets_.left().toFloat(), border_insets_.top().toFloat(),
-            border_insets_.right().toFloat(),
-            border_insets_.bottom().toFloat()));
+  if (rounded_corners && !border_insets_.zero()) {
+    padding_rounded_corners_if_different = rounded_corners->Inset(math::InsetsF(
+        border_insets_.left().toFloat(), border_insets_.top().toFloat(),
+        border_insets_.right().toFloat(), border_insets_.bottom().toFloat()));
   }
   const base::optional<RoundedCorners>& padding_rounded_corners =
       padding_rounded_corners_if_different
           ? padding_rounded_corners_if_different
-          : border_radius_provider.rounded_corners();
+          : rounded_corners;
 
   // The painting order is:
   // - background color.
@@ -594,10 +592,10 @@
     if (background_image_result.node) {
       border_node_builder.AddChild(background_image_result.node);
     }
-    RenderAndAnimateBorder(border_radius_provider.rounded_corners(),
-                           &border_node_builder, &animate_node_builder);
-    RenderAndAnimateBoxShadow(border_radius_provider.rounded_corners(),
-                              &border_node_builder, &animate_node_builder);
+    RenderAndAnimateBorder(rounded_corners, &border_node_builder,
+                           &animate_node_builder);
+    RenderAndAnimateBoxShadow(rounded_corners, &border_node_builder,
+                              &animate_node_builder);
   }
 
   const bool overflow_hidden =
@@ -1227,6 +1225,55 @@
 
 }  // namespace
 
+base::optional<render_tree::RoundedCorners> Box::ComputeRoundedCorners() {
+  UsedBorderRadiusProvider border_radius_provider(GetBorderBoxSize());
+  render_tree::RoundedCorner border_top_left_radius;
+  render_tree::RoundedCorner border_top_right_radius;
+  render_tree::RoundedCorner border_bottom_right_radius;
+  render_tree::RoundedCorner border_bottom_left_radius;
+
+  computed_style()->border_top_left_radius()->Accept(&border_radius_provider);
+  if (border_radius_provider.rounded_corner()) {
+    border_top_left_radius = render_tree::RoundedCorner(
+        border_radius_provider.rounded_corner()->horizontal,
+        border_radius_provider.rounded_corner()->vertical);
+  }
+
+  computed_style()->border_top_right_radius()->Accept(&border_radius_provider);
+  if (border_radius_provider.rounded_corner()) {
+    border_top_right_radius = render_tree::RoundedCorner(
+        border_radius_provider.rounded_corner()->horizontal,
+        border_radius_provider.rounded_corner()->vertical);
+  }
+
+  computed_style()->border_bottom_right_radius()->Accept(
+      &border_radius_provider);
+  if (border_radius_provider.rounded_corner()) {
+    border_bottom_right_radius = render_tree::RoundedCorner(
+        border_radius_provider.rounded_corner()->horizontal,
+        border_radius_provider.rounded_corner()->vertical);
+  }
+  computed_style()->border_bottom_left_radius()->Accept(
+      &border_radius_provider);
+  if (border_radius_provider.rounded_corner()) {
+    border_bottom_left_radius = render_tree::RoundedCorner(
+        border_radius_provider.rounded_corner()->horizontal,
+        border_radius_provider.rounded_corner()->vertical);
+  }
+
+  base::optional<RoundedCorners> rounded_corners;
+  if (!border_top_left_radius.IsSquare() ||
+      !border_top_right_radius.IsSquare() ||
+      !border_bottom_right_radius.IsSquare() ||
+      !border_bottom_left_radius.IsSquare()) {
+    rounded_corners.emplace(border_top_left_radius, border_top_right_radius,
+                            border_bottom_right_radius,
+                            border_bottom_left_radius);
+  }
+
+  return rounded_corners;
+}
+
 void Box::RenderAndAnimateBoxShadow(
     const base::optional<RoundedCorners>& rounded_corners,
     CompositionNode::Builder* border_node_builder,
@@ -1501,21 +1548,18 @@
 scoped_refptr<render_tree::Node> Box::RenderAndAnimateOverflow(
     const scoped_refptr<render_tree::Node>& content_node,
     const math::Vector2dF& border_offset) {
-  UsedBorderRadiusProvider border_radius_provider(GetBorderBoxSize());
-  computed_style()->border_radius()->Accept(&border_radius_provider);
+  base::optional<RoundedCorners> rounded_corners = ComputeRoundedCorners();
 
   base::optional<RoundedCorners> padding_rounded_corners_if_different;
-  if (border_radius_provider.rounded_corners() && !border_insets_.zero()) {
-    padding_rounded_corners_if_different =
-        border_radius_provider.rounded_corners()->Inset(math::InsetsF(
-            border_insets_.left().toFloat(), border_insets_.top().toFloat(),
-            border_insets_.right().toFloat(),
-            border_insets_.bottom().toFloat()));
+  if (rounded_corners && !border_insets_.zero()) {
+    padding_rounded_corners_if_different = rounded_corners->Inset(math::InsetsF(
+        border_insets_.left().toFloat(), border_insets_.top().toFloat(),
+        border_insets_.right().toFloat(), border_insets_.bottom().toFloat()));
   }
   const base::optional<RoundedCorners>& padding_rounded_corners =
       padding_rounded_corners_if_different
           ? padding_rounded_corners_if_different
-          : border_radius_provider.rounded_corners();
+          : rounded_corners;
 
   return RenderAndAnimateOverflow(padding_rounded_corners, content_node, NULL,
                                   border_offset);
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index e90a9f1..67056ab 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -699,6 +699,9 @@
   // Updates used values of "padding" properties.
   void UpdatePaddings(const LayoutParams& layout_params);
 
+  // Computes the rounded corners (if there are any) from the border radii.
+  base::optional<render_tree::RoundedCorners> ComputeRoundedCorners();
+
   // Called after TryPlaceEllipsisOrProcessPlacedEllipsis() determines that the
   // box is impacted by the ellipsis. This handles both determining the location
   // of the ellipsis, if it has not already been placed, and updating the
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index 2191cc9..323425d 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -1212,15 +1212,19 @@
 
 void UsedBorderRadiusProvider::VisitLength(cssom::LengthValue* length) {
   if (length->value() > 0) {
-    rounded_corners_.emplace(length->value(), length->value());
+    rounded_corner_.emplace(length->value(), length->value());
+  } else {
+    rounded_corner_ = base::nullopt;
   }
 }
 
 void UsedBorderRadiusProvider::VisitPercentage(
     cssom::PercentageValue* percentage) {
   if (percentage->value() > 0) {
-    rounded_corners_.emplace(percentage->value() * frame_size_.width(),
-                             percentage->value() * frame_size_.height());
+    rounded_corner_.emplace(percentage->value() * frame_size_.width(),
+                            percentage->value() * frame_size_.height());
+  } else {
+    rounded_corner_ = base::nullopt;
   }
 }
 
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h
index f2ec746..2f90762 100644
--- a/src/cobalt/layout/used_style.h
+++ b/src/cobalt/layout/used_style.h
@@ -269,12 +269,12 @@
   void VisitLength(cssom::LengthValue* length) OVERRIDE;
   void VisitPercentage(cssom::PercentageValue* percentage) OVERRIDE;
 
-  const base::optional<render_tree::RoundedCorners>& rounded_corners() const {
-    return rounded_corners_;
+  const base::optional<render_tree::RoundedCorner>& rounded_corner() const {
+    return rounded_corner_;
   }
 
  private:
-  base::optional<render_tree::RoundedCorners> rounded_corners_;
+  base::optional<render_tree::RoundedCorner> rounded_corner_;
 
   const math::SizeF frame_size_;
 
diff --git a/src/cobalt/layout_tests/layout_snapshot.cc b/src/cobalt/layout_tests/layout_snapshot.cc
index de1f5ec..1fe1c7e 100644
--- a/src/cobalt/layout_tests/layout_snapshot.cc
+++ b/src/cobalt/layout_tests/layout_snapshot.cc
@@ -61,7 +61,7 @@
   network::NetworkModule::Options net_options;
   // Some layout tests test Content Security Policy; allow HTTP so we
   // don't interfere.
-  net_options.require_https = false;
+  net_options.https_requirement = network::kHTTPSOptional;
   network::NetworkModule network_module(net_options);
 
   // We do not support a media module in this mode.
@@ -86,7 +86,7 @@
       base::Bind(&WebModuleOnRenderTreeProducedCallback, &results, &run_loop,
                  MessageLoop::current()),
       base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
-      base::Closure() /* window_close_callback */,
+      browser::WebModule::CloseCallback() /* window_close_callback */,
       base::Closure() /* window_minimize_callback */, stub_media_module.get(),
       &network_module, viewport_size, 1.f, resource_provider, 60.0f,
       web_module_options);
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index f377d78..55d0ea7 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -94,7 +94,6 @@
 
   bool results =
       pixel_tester.TestTree(animated_tree, GetParam().base_file_path);
-  EXPECT_TRUE(results);
 
   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kRebaseline) ||
       (!results &&
@@ -102,6 +101,8 @@
            switches::kRebaselineFailedTests))) {
     pixel_tester.Rebaseline(animated_tree, GetParam().base_file_path);
   }
+
+  EXPECT_TRUE(results);
 }
 
 // Cobalt-specific test cases.
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-and-inset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-and-inset-shadow.html
new file mode 100644
index 0000000..fff02ae
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-and-inset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 2 values for the border radius for a box with a background color
+ | and an inset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 25px 100px;
+      box-shadow: 10px 10px #FF7F50 inset;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-and-outset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-and-outset-shadow.html
new file mode 100644
index 0000000..ed2dc0e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-and-outset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 2 values for the border radius for a box with a background color
+ | and an outset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 25px 100px;
+      box-shadow: 10px 10px #FF7F50;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-expected.png
new file mode 100644
index 0000000..6f420eb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color.html
new file mode 100644
index 0000000..9dc4581
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-background-color.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ | Setting 2 values for the border radius for a box with a background color.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 25px 100px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-border-only-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-border-only-expected.png
new file mode 100644
index 0000000..6bcf3b9
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-border-only-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-border-only.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-border-only.html
new file mode 100644
index 0000000..0b16b13
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-2-values-with-border-only.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ | Setting 2 values for the border radius for a box with a border.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      border: 25px #6495ED solid;
+      border-radius: 50px 100px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-and-inset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-and-inset-shadow.html
new file mode 100644
index 0000000..d453c0f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-and-inset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 3 values for the border radius for a box with a background color
+ | and an inset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 50px;
+      box-shadow: 10px 10px #FF7F50 inset;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-and-outset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-and-outset-shadow.html
new file mode 100644
index 0000000..dac58e5
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-and-outset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 3 values for the border radius for a box with a background color
+ | and an outset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 50px;
+      box-shadow: 10px 10px #FF7F50;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-expected.png
new file mode 100644
index 0000000..5e019e4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color.html
new file mode 100644
index 0000000..47866c0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-background-color.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ | Setting 3 values for the border radius for a box with a background color.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 50px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-border-only-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-border-only-expected.png
new file mode 100644
index 0000000..8b5f44d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-border-only-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-border-only.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-border-only.html
new file mode 100644
index 0000000..56de16a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-3-values-with-border-only.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ | Setting 3 values for the border radius for a box with a border.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      border: 25px #6495ED solid;
+      border-radius: 10px 120px 50px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-and-inset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-and-inset-shadow.html
new file mode 100644
index 0000000..c443b15
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-and-inset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a background color
+ | and an inset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 20px 70px;
+      box-shadow: 10px 10px #FF7F50 inset;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-and-outset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-and-outset-shadow.html
new file mode 100644
index 0000000..c9bfeb8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-and-outset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a background color
+ | and an outset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 20px 70px;
+      box-shadow: 10px 10px #FF7F50;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-expected.png
new file mode 100644
index 0000000..d424893
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color.html
new file mode 100644
index 0000000..035a589
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-background-color.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a background color.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 20px 70px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-border-only-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-border-only-expected.png
new file mode 100644
index 0000000..296cc7d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-border-only-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-border-only.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-border-only.html
new file mode 100644
index 0000000..5cf86bb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-4-values-with-border-only.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a border.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      border: 25px #6495ED solid;
+      border-radius: 50px 120px 40px 70px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-and-inset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-and-inset-shadow.html
new file mode 100644
index 0000000..30a32f8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-and-inset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a background color
+ | and an inset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 0px 70px;
+      box-shadow: 10px 10px #FF7F50 inset;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-and-outset-shadow.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-and-outset-shadow.html
new file mode 100644
index 0000000..9f4781d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-and-outset-shadow.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a background color
+ | and an outset shadow.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 10px 120px 0px 70px;
+      box-shadow: 10px 10px #FF7F50;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-expected.png
new file mode 100644
index 0000000..a9c2039
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color.html
new file mode 100644
index 0000000..6a23bd8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-background-color.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a background color.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      background-color: rgb(176,176,176);
+      border-radius: 25px 100px 0px 75px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-border-only-expected.png b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-border-only-expected.png
new file mode 100644
index 0000000..f0eb38f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-border-only-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-border-only.html b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-border-only.html
new file mode 100644
index 0000000..5cfffb1
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/5-0-border-radius-with-zero-value-and-border-only.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+ | Setting 4 values for the border radius for a box with a border.
+ | Note that this will not look the exact same in Chrome since Cobalt
+ | normalizes opposing corners and Chrome does not.
+ |   https://www.w3.org/TR/css3-background/#border-radius
+ -->
+<html>
+<head>
+  <style>
+    div {
+      border: 25px #6495ED solid;
+      border-radius: 40px 100px 0px 75px;
+      height: 200px;
+      width: 100px;
+    }
+  </style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
index 2c12a56..03230b9 100644
--- a/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
@@ -73,6 +73,12 @@
 4-0-border-width-with-solid-border-style
 4-0-different-border-styles-with-different-border-widths
 4-0-different-border-widths-with-solid-border-style
+5-0-border-radius-2-values-with-background-color
+5-0-border-radius-2-values-with-border-only
+5-0-border-radius-3-values-with-background-color
+5-0-border-radius-3-values-with-border-only
+5-0-border-radius-4-values-with-background-color
+5-0-border-radius-4-values-with-border-only
 5-0-border-radius-circle-with-background-color-and-border
 5-0-border-radius-circle-with-background-image-and-border
 5-0-border-radius-with-background-color
@@ -80,6 +86,8 @@
 5-0-border-radius-with-background-image
 5-0-border-radius-with-background-image-and-border
 5-0-border-radius-with-border-only
+5-0-border-radius-with-zero-value-and-background-color
+5-0-border-radius-with-zero-value-and-border-only
 7-1-1-box-shadow-with-inset
 7-1-1-box-shadow-with-inset-and-blur-and-spread
 7-1-1-box-shadow-with-spread
diff --git a/src/cobalt/layout_tests/web_platform_test_parser.cc b/src/cobalt/layout_tests/web_platform_test_parser.cc
index 50b6c1e..04f894e 100644
--- a/src/cobalt/layout_tests/web_platform_test_parser.cc
+++ b/src/cobalt/layout_tests/web_platform_test_parser.cc
@@ -101,8 +101,7 @@
     // Evaluate the javascript precondition. Enumerate the web platform tests
     // only if the precondition is true.
     scoped_ptr<script::JavaScriptEngine> engine =
-        script::JavaScriptEngine::CreateEngine(
-            script::JavaScriptEngine::Options());
+        script::JavaScriptEngine::CreateEngine();
     scoped_refptr<script::GlobalEnvironment> global_environment =
         engine->CreateGlobalEnvironment();
     global_environment->CreateGlobalObject();
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 01788fd..ed6e5c4 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -46,22 +46,23 @@
  public:
   CspDelegatePermissive(
       scoped_ptr<dom::CspViolationReporter> violation_reporter, const GURL& url,
-      const std::string& location_policy,
+      const std::string& location_policy, csp::CSPHeaderPolicy require_csp,
       const base::Closure& policy_changed_callback)
       : dom::CspDelegateSecure(violation_reporter.Pass(), url, location_policy,
-                               policy_changed_callback) {
+                               require_csp, policy_changed_callback) {
     // Lies, but some checks in our parent require this.
     was_header_received_ = true;
   }
 
   static CspDelegate* Create(
       scoped_ptr<dom::CspViolationReporter> violation_reporter, const GURL& url,
-      const std::string& location_policy,
+      const std::string& location_policy, csp::CSPHeaderPolicy require_csp,
       const base::Closure& policy_changed_callback,
       int insecure_allowed_token) {
     UNREFERENCED_PARAMETER(insecure_allowed_token);
     return new CspDelegatePermissive(violation_reporter.Pass(), url,
-                                     location_policy, policy_changed_callback);
+                                     location_policy, require_csp,
+                                     policy_changed_callback);
   }
 
   bool OnReceiveHeaders(const csp::ResponseHeaders& headers) OVERRIDE {
@@ -150,7 +151,7 @@
 
   // Network module
   network::NetworkModule::Options net_options;
-  net_options.require_https = false;
+  net_options.https_requirement = network::kHTTPSOptional;
   network::NetworkModule network_module(net_options);
 
   // Media module
@@ -177,7 +178,7 @@
       base::Bind(&WebModuleOnRenderTreeProducedCallback, &results, &run_loop,
                  MessageLoop::current()),
       base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
-      base::Closure() /* window_close_callback */,
+      browser::WebModule::CloseCallback() /* window_close_callback */,
       base::Closure() /* window_minimize_callback */, media_module.get(),
       &network_module, kDefaultViewportSize, 1.f, &resource_provider, 60.0f,
       web_module_options);
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index cf037bf..2b3ecf1 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -184,8 +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.html',
         '<(input_directory)/splash_screen.js',
+        '<!@(["python", "<(DEPTH)/starboard/tools/find_private_files.py", "<(DEPTH)", "*.html", "cobalt/loader/embedded_resources"])',
       ],
       'actions': [
         {
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index 336d137..024cc55 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -510,10 +510,14 @@
 
   base::ThreadChecker resource_cache_thread_checker_;
 
-  base::CVal<base::cval::SizeInBytes, base::CValPublic> size_in_bytes_;
-  base::CVal<base::cval::SizeInBytes, base::CValPublic> capacity_in_bytes_;
-  base::CVal<int> count_requested_resources_;
-  base::CVal<int> count_loading_resources_;
+  base::CVal<base::cval::SizeInBytes, base::CValPublic> memory_size_in_bytes_;
+  base::CVal<base::cval::SizeInBytes, base::CValPublic>
+      memory_capacity_in_bytes_;
+  base::CVal<base::cval::SizeInBytes> memory_resources_loaded_in_bytes_;
+
+  base::CVal<int> count_resources_requested_;
+  base::CVal<int> count_resources_loading_;
+  base::CVal<int> count_resources_loaded_;
   base::CVal<int> count_pending_callbacks_;
 
   DISALLOW_COPY_AND_ASSIGN(ResourceCache);
@@ -532,20 +536,28 @@
       create_loader_function_(create_loader_function),
       is_processing_pending_callbacks_(false),
       are_callbacks_disabled_(false),
-      size_in_bytes_(base::StringPrintf("Memory.%s.Size", name_.c_str()), 0,
-                     "Total number of bytes currently used by the cache."),
-      capacity_in_bytes_(
+      memory_size_in_bytes_(
+          base::StringPrintf("Memory.%s.Size", name_.c_str()), 0,
+          "Total number of bytes currently used by the cache."),
+      memory_capacity_in_bytes_(
           base::StringPrintf("Memory.%s.Capacity", name_.c_str()),
           cache_capacity_,
           "The capacity, in bytes, of the resource cache.  "
           "Exceeding this results in *unused* resources being "
           "purged."),
-      count_requested_resources_(
-          base::StringPrintf("Count.%s.RequestedResources", name_.c_str()), 0,
+      memory_resources_loaded_in_bytes_(
+          base::StringPrintf("Memory.%s.Resource.Loaded", name_.c_str()), 0,
+          "Combined size in bytes of all resources that have been loaded by "
+          "the cache."),
+      count_resources_requested_(
+          base::StringPrintf("Count.%s.Resource.Requested", name_.c_str()), 0,
           "The total number of resources that have been requested."),
-      count_loading_resources_(
-          base::StringPrintf("Count.%s.LoadingResources", name_.c_str()), 0,
-          "The number of loading resources that are still outstanding."),
+      count_resources_loading_(
+          base::StringPrintf("Count.%s.Resource.Loading", name_.c_str()), 0,
+          "The number of resources that are currently loading."),
+      count_resources_loaded_(
+          base::StringPrintf("Count.%s.Resource.Loaded", name_.c_str()), 0,
+          "The total number of resources that have been successfully loaded."),
       count_pending_callbacks_(
           base::StringPrintf("Count.%s.PendingCallbacks", name_.c_str()), 0,
           "The number of loading completed resources that have pending "
@@ -580,7 +592,7 @@
   }
 
   // If we reach this point, then the resource doesn't exist yet.
-  ++count_requested_resources_;
+  ++count_resources_requested_;
 
   // Add the resource to a loading set. If no current resources have pending
   // callbacks, then this resource will block callbacks until it is decoded.
@@ -593,7 +605,7 @@
   } else {
     non_callback_blocking_loading_resource_set_.insert(url.spec());
   }
-  ++count_loading_resources_;
+  ++count_resources_loading_;
 
   // Create the cached resource and fetch its resource based on the url.
   scoped_refptr<CachedResourceType> cached_resource(new CachedResourceType(
@@ -613,7 +625,7 @@
 void ResourceCache<CacheType>::SetCapacity(uint32 capacity) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
   cache_capacity_ = capacity;
-  capacity_in_bytes_ = capacity;
+  memory_capacity_in_bytes_ = capacity;
   ReclaimMemoryAndMaybeProcessPendingCallbacks(cache_capacity_);
 }
 
@@ -637,8 +649,12 @@
   const std::string& url = cached_resource->url().spec();
 
   if (cached_resource->TryGetResource()) {
-    size_in_bytes_ +=
+    uint32 estimated_size_in_bytes =
         CacheType::GetEstimatedSizeInBytes(cached_resource->TryGetResource());
+    memory_size_in_bytes_ += estimated_size_in_bytes;
+    memory_resources_loaded_in_bytes_ += estimated_size_in_bytes;
+
+    ++count_resources_loaded_;
   }
 
   // Remove the resource from its loading set. It should exist in exactly one
@@ -659,7 +675,7 @@
   // incremented first to ensure that the total of the two counts always remains
   // above 0.
   ++count_pending_callbacks_;
-  --count_loading_resources_;
+  --count_resources_loading_;
 
   ProcessPendingCallbacksIfUnblocked();
   ReclaimMemoryAndMaybeProcessPendingCallbacks(cache_capacity_);
@@ -690,10 +706,10 @@
     DCHECK(non_callback_blocking_loading_resource_set_.find(url) ==
            non_callback_blocking_loading_resource_set_.end());
     DCHECK(pending_callback_map_.find(url) == pending_callback_map_.end());
-    --count_loading_resources_;
+    --count_resources_loading_;
   } else if (non_callback_blocking_loading_resource_set_.erase(url)) {
     DCHECK(pending_callback_map_.find(url) == pending_callback_map_.end());
-    --count_loading_resources_;
+    --count_resources_loading_;
   } else if (pending_callback_map_.erase(url)) {
     --count_pending_callbacks_;
   }
@@ -717,7 +733,7 @@
   // pending callbacks and try again. References to the cached resources are
   // potentially being held until the callbacks run, so processing them may
   // enable more memory to be reclaimed.
-  if (size_in_bytes_ > bytes_to_reclaim_down_to) {
+  if (memory_size_in_bytes_ > bytes_to_reclaim_down_to) {
     ProcessPendingCallbacks();
     ReclaimMemory(bytes_to_reclaim_down_to, true /*log_warning_if_over*/);
   }
@@ -728,7 +744,7 @@
                                              bool log_warning_if_over) {
   DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
 
-  while (size_in_bytes_ > bytes_to_reclaim_down_to &&
+  while (memory_size_in_bytes_ > bytes_to_reclaim_down_to &&
          !unreference_cached_resource_map_.empty()) {
     // The first element is the earliest-inserted element.
     scoped_refptr<ResourceType> resource =
@@ -739,15 +755,15 @@
     // in linked_hash_map. Add that function and related unit test.
     unreference_cached_resource_map_.erase(
         unreference_cached_resource_map_.begin());
-    size_in_bytes_ -= first_resource_size;
+    memory_size_in_bytes_ -= first_resource_size;
   }
 
   if (log_warning_if_over) {
     // Log a warning if we're still over |bytes_to_reclaim_down_to| after
     // attempting to reclaim memory. This can occur validly when the size of
     // the referenced images exceeds the target size.
-    DLOG_IF(WARNING, size_in_bytes_ > bytes_to_reclaim_down_to)
-        << "cached size: " << size_in_bytes_
+    DLOG_IF(WARNING, memory_size_in_bytes_ > bytes_to_reclaim_down_to)
+        << "cached size: " << memory_size_in_bytes_
         << ", target size: " << bytes_to_reclaim_down_to;
   }
 }
diff --git a/src/cobalt/media/sandbox/media_sandbox.cc b/src/cobalt/media/sandbox/media_sandbox.cc
index 19345f9..59edcc0 100644
--- a/src/cobalt/media/sandbox/media_sandbox.cc
+++ b/src/cobalt/media/sandbox/media_sandbox.cc
@@ -72,7 +72,7 @@
                          const FilePath& trace_log_path) {
   trace_to_file_.reset(new trace_event::ScopedTraceToFile(trace_log_path));
   network::NetworkModule::Options network_options;
-  network_options.require_https = false;
+  network_options.https_requirement = network::kHTTPSOptional;
 
   network_module_.reset(new network::NetworkModule(network_options));
   fetcher_factory_.reset(new loader::FetcherFactory(network_module_.get()));
diff --git a/src/cobalt/network/network_delegate.cc b/src/cobalt/network/network_delegate.cc
index 2755569..4ebee31 100644
--- a/src/cobalt/network/network_delegate.cc
+++ b/src/cobalt/network/network_delegate.cc
@@ -23,10 +23,10 @@
 namespace network {
 
 NetworkDelegate::NetworkDelegate(net::StaticCookiePolicy::Type cookie_policy,
-                                 bool require_https)
+                                 network::HTTPSRequirement https_requirement)
     : cookie_policy_(cookie_policy),
       cookies_enabled_(true),
-      require_https_(require_https) {}
+      https_requirement_(https_requirement) {}
 
 NetworkDelegate::~NetworkDelegate() {}
 
@@ -37,16 +37,13 @@
   UNREFERENCED_PARAMETER(callback);
   UNREFERENCED_PARAMETER(new_url);
 
-#if defined(COBALT_FORCE_HTTPS)
-  const bool require_https = true;
-#else
-  bool require_https = require_https_;
-#endif
-
   const GURL& url = request->url();
-  if (!require_https) {
+  if (url.SchemeIsSecure() || url.SchemeIs("data")) {
     return net::OK;
-  } else if (url.SchemeIsSecure() || url.SchemeIs("data")) {
+  } else if (https_requirement_ == kHTTPSOptional) {
+    DLOG(WARNING)
+        << "Page must be served over secure scheme, it will fail to load "
+           "in production builds of Cobalt.";
     return net::OK;
   }
 
diff --git a/src/cobalt/network/network_delegate.h b/src/cobalt/network/network_delegate.h
index 344a0ea..f61049f 100644
--- a/src/cobalt/network/network_delegate.h
+++ b/src/cobalt/network/network_delegate.h
@@ -23,6 +23,13 @@
 
 namespace cobalt {
 namespace network {
+
+// Used to tell if HTTPS scheme has to be used.
+enum HTTPSRequirement {
+  kHTTPSRequired,
+  kHTTPSOptional,
+};
+
 // A NetworkDelegate receives callbacks when network events occur.
 // Each override can specify custom behavior or just add additional logging.
 // We do nothing for most events, but our network delegate
@@ -30,7 +37,7 @@
 class NetworkDelegate : public net::NetworkDelegate {
  public:
   NetworkDelegate(net::StaticCookiePolicy::Type cookie_policy,
-                  bool require_https);
+                  network::HTTPSRequirement https_requirement);
   ~NetworkDelegate() OVERRIDE;
 
   // For debugging, we allow blocking all cookies.
@@ -84,7 +91,7 @@
  private:
   net::StaticCookiePolicy::Type cookie_policy_;
   bool cookies_enabled_;
-  bool require_https_;
+  network::HTTPSRequirement https_requirement_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkDelegate);
 };
diff --git a/src/cobalt/network/network_module.cc b/src/cobalt/network/network_module.cc
index f9fb043..059ced9 100644
--- a/src/cobalt/network/network_module.cc
+++ b/src/cobalt/network/network_module.cc
@@ -151,7 +151,7 @@
       new URLRequestContext(storage_manager_, options_.custom_proxy, net_log,
                             options_.ignore_certificate_errors));
   network_delegate_.reset(
-      new NetworkDelegate(options_.cookie_policy, options_.require_https));
+      new NetworkDelegate(options_.cookie_policy, options_.https_requirement));
   url_request_context_->set_http_user_agent_settings(
       http_user_agent_settings_.get());
   url_request_context_->set_network_delegate(network_delegate_.get());
diff --git a/src/cobalt/network/network_module.h b/src/cobalt/network/network_module.h
index 222a05c..2b51e12 100644
--- a/src/cobalt/network/network_module.h
+++ b/src/cobalt/network/network_module.h
@@ -60,11 +60,11 @@
         : cookie_policy(
               net::StaticCookiePolicy::BLOCK_SETTING_THIRD_PARTY_COOKIES),
           ignore_certificate_errors(false),
-          require_https(true),
+          https_requirement(network::kHTTPSRequired),
           preferred_language("en-US") {}
     net::StaticCookiePolicy::Type cookie_policy;
     bool ignore_certificate_errors;
-    bool require_https;
+    HTTPSRequirement https_requirement;
     std::string preferred_language;
     std::string custom_proxy;
   };
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 8827ef4..4fb1ee3 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -35,11 +35,6 @@
 namespace renderer {
 
 namespace {
-// In order to put a bound on memory we set a maximum submission queue size that
-// is empirically found to be a nice balance between animation smoothing and
-// memory usage.
-const size_t kMaxSubmissionQueueSize = 4u;
-
 // How quickly the renderer time adjusts to changing submission times.
 // 500ms is chosen as a default because it is fast enough that the user will not
 // usually notice input lag from a slow timeline renderer, but slow enough that
@@ -84,6 +79,7 @@
           submit_even_if_render_tree_is_unchanged),
       last_did_rasterize_(false),
       last_animations_expired_(true),
+      last_stat_tracked_animations_expired_(true),
       rasterize_periodic_timer_("Renderer.Rasterize.Duration",
                                 kRasterizePeriodicTimerEntriesPerUpdate,
                                 false /*enable_entry_list_c_val*/),
@@ -233,14 +229,40 @@
                render_target_->GetSize());
 }
 
+void Pipeline::TimeFence(base::TimeDelta time_fence) {
+  TRACK_MEMORY_SCOPE("Renderer");
+  TRACE_EVENT0("cobalt::renderer", "Pipeline::TimeFence()");
+
+  if (MessageLoop::current() != rasterizer_thread_.message_loop()) {
+    rasterizer_thread_.message_loop()->PostTask(
+        FROM_HERE,
+        base::Bind(&Pipeline::TimeFence, base::Unretained(this), time_fence));
+    return;
+  }
+
+  if (!time_fence_) {
+    time_fence_ = time_fence;
+  } else {
+    LOG(ERROR) << "Attempting to set a time fence while one was already set.";
+  }
+}
+
 void Pipeline::SetNewRenderTree(const Submission& render_tree_submission) {
   DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
   DCHECK(render_tree_submission.render_tree.get());
 
   TRACE_EVENT0("cobalt::renderer", "Pipeline::SetNewRenderTree()");
 
-  submission_queue_->PushSubmission(render_tree_submission,
-                                    base::TimeTicks::Now());
+  // If a time fence is active, save the submission to be queued only after
+  // we pass the time fence.  Overwrite any existing waiting submission in this
+  // case.
+  if (time_fence_) {
+    post_fence_submission_ = render_tree_submission;
+    post_fence_receipt_time_ = base::TimeTicks::Now();
+    return;
+  }
+
+  QueueSubmission(render_tree_submission, base::TimeTicks::Now());
 
   // Start the rasterization timer if it is not yet started.
   if (!rasterize_timer_) {
@@ -260,7 +282,7 @@
   DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
   TRACE_EVENT0("cobalt::renderer", "Pipeline::ClearCurrentRenderTree()");
 
-  submission_queue_->Reset();
+  ResetSubmissionQueue();
   rasterize_timer_ = base::nullopt;
 }
 
@@ -280,37 +302,55 @@
   // 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_update_pending_ &&
-      !submit_even_if_render_tree_is_unchanged_ && !has_render_tree_changed) {
-    return;
+  if (fps_overlay_update_pending_ || submit_even_if_render_tree_is_unchanged_ ||
+      has_render_tree_changed) {
+    // Check whether the animations in the render tree that is being rasterized
+    // are active.
+    render_tree::animations::AnimateNode* animate_node =
+        base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
+            submission.render_tree.get());
+
+    // Rasterize the last submitted render tree.
+    bool did_rasterize =
+        RasterizeSubmissionToRenderTarget(submission, render_target_);
+
+    bool animations_expired = animate_node->expiry() <= submission.time_offset;
+    bool stat_tracked_animations_expired =
+        animate_node->depends_on_time_expiry() <= submission.time_offset;
+
+    UpdateRasterizeStats(did_rasterize, stat_tracked_animations_expired,
+                         is_new_render_tree, start_rasterize_time,
+                         base::TimeTicks::Now());
+
+    last_did_rasterize_ = did_rasterize;
+    last_animations_expired_ = animations_expired;
+    last_stat_tracked_animations_expired_ = stat_tracked_animations_expired;
   }
 
-  // Check whether the animations in the render tree that is being rasterized
-  // are active.
-  render_tree::animations::AnimateNode* animate_node =
-      base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
-          submission.render_tree.get());
+  if (time_fence_ && submission_queue_->submission_time(
+                         base::TimeTicks::Now()) >= *time_fence_) {
+    // A time fence was active and we just crossed it, so reset it.
+    time_fence_ = base::nullopt;
 
-  // 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;
+    if (post_fence_submission_) {
+      // A submission was waiting to be queued once we passed the time fence,
+      // so go ahead and queue it now.
+      QueueSubmission(*post_fence_submission_, *post_fence_receipt_time_);
+      post_fence_submission_ = base::nullopt;
+      post_fence_receipt_time_ = base::nullopt;
+    }
+  }
 }
 
-void Pipeline::UpdateRasterizeStats(bool did_rasterize, bool animations_expired,
+void Pipeline::UpdateRasterizeStats(bool did_rasterize,
+                                    bool are_stat_tracked_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;
+      !last_stat_tracked_animations_expired_ && last_did_rasterize_;
+  bool animations_active =
+      !are_stat_tracked_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
@@ -463,11 +503,7 @@
       base::Thread::Options(MessageLoop::TYPE_DEFAULT, kRendererThreadStackSize,
                             base::kThreadPriority_High));
 
-  submission_queue_.emplace(
-      kMaxSubmissionQueueSize,
-      base::TimeDelta::FromMillisecondsD(kTimeToConvergeInMS),
-      base::Bind(&DestructSubmissionOnMessageLoop,
-                 submission_disposal_thread_.message_loop()));
+  ResetSubmissionQueue();
 }
 
 void Pipeline::ShutdownSubmissionQueue() {
@@ -622,5 +658,32 @@
   }
 }
 
+void Pipeline::ResetSubmissionQueue() {
+  TRACK_MEMORY_SCOPE("Renderer");
+  TRACE_EVENT0("cobalt::renderer", "Pipeline::ResetSubmissionQueue()");
+  submission_queue_ = base::nullopt;
+  submission_queue_.emplace(
+      current_timeline_info_.max_submission_queue_size,
+      base::TimeDelta::FromMillisecondsD(kTimeToConvergeInMS),
+      current_timeline_info_.allow_latency_reduction,
+      base::Bind(&DestructSubmissionOnMessageLoop,
+                 submission_disposal_thread_.message_loop()));
+}
+
+void Pipeline::QueueSubmission(const Submission& submission,
+                               base::TimeTicks receipt_time) {
+  TRACK_MEMORY_SCOPE("Renderer");
+  TRACE_EVENT0("cobalt::renderer", "Pipeline::QueueSubmission()");
+  // Upon each submission, check if the timeline has changed.  If it has,
+  // reset our submission queue (possibly with a new configuration specified
+  // within |timeline_info|.
+  if (submission.timeline_info.id != current_timeline_info_.id) {
+    current_timeline_info_ = submission.timeline_info;
+    ResetSubmissionQueue();
+  }
+
+  submission_queue_->PushSubmission(submission, receipt_time);
+}
+
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 49308c9..f2c3663 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -93,6 +93,16 @@
   void RasterizeToRGBAPixels(const Submission& render_tree_submission,
                              const RasterizationCompleteCallback& complete);
 
+  // Inserts a fence that ensures the rasterizer rasterizes up until the
+  // submission time proceeding queuing additional submissions.  This is useful
+  // when switching timelines in order to ensure that an old timeline plays out
+  // completely before resetting the submission queue for a timeline change.
+  // Upon passing the fence, we will immediately queue the latest submission
+  // submitted after TimeFence() was called.  If a time fence is set while
+  // an existing time fence already exists, the new time fence is ignored (and
+  // an error is logged).
+  void TimeFence(base::TimeDelta time_fence);
+
   // Returns a thread-safe object from which one can produce renderer resources
   // like images and fonts which can be referenced by render trees that are
   // subsequently submitted to this pipeline.
@@ -125,7 +135,8 @@
 
   // 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,
+  void UpdateRasterizeStats(bool did_rasterize,
+                            bool are_stat_tracked_animations_expired,
                             bool is_new_render_tree, base::TimeTicks start_time,
                             base::TimeTicks end_time);
 
@@ -160,6 +171,17 @@
   void FrameStatsOnFlushCallback(
       const base::CValCollectionTimerStatsFlushResults& flush_results);
 
+  // Resets the submission queue, effecitvely emptying it and restarting it
+  // with the configuration specified by |current_timeline_info_| applied to it.
+  void ResetSubmissionQueue();
+
+  // Pushes the specified submission into the submission queue, where it will
+  // then be picked up by subsequent rasterizations.  If the submission's
+  // timeline id is different from the current timeline id (in
+  // |current_timeline_info_|), then the submission queue will be reset.
+  void QueueSubmission(const Submission& submission,
+                       base::TimeTicks receipt_time);
+
   base::WaitableEvent rasterizer_created_event_;
 
   // The render_target that all submitted render trees will be rasterized to.
@@ -214,6 +236,9 @@
   // allows us to skip rasterizing that render tree if we see it again and it
   // did have expired animations.
   bool last_animations_expired_;
+  // Keep track of whether the last rendered tree had animations that we're
+  // tracking stats on.
+  bool last_stat_tracked_animations_expired_;
 
   // Did a rasterization take place in the last frame?
   bool last_did_rasterize_;
@@ -277,6 +302,18 @@
 
   // True if the overlay has been updated and it needs to be re-rasterized.
   bool fps_overlay_update_pending_;
+
+  // Time fence data that records if a time fence is active, at what time, and
+  // what submission if any is waiting to be queued once we pass the time fence.
+  base::optional<base::TimeDelta> time_fence_;
+  base::optional<Submission> post_fence_submission_;
+  base::optional<base::TimeTicks> post_fence_receipt_time_;
+
+  // Information about the current timeline.  Each incoming submission
+  // identifies with a particular timeline, and if that ever changes, we assume
+  // a discontinuity in animations and reset our submission queue, possibly
+  // with new configuration parameters specified in the new |TimelineInfo|.
+  Submission::TimelineInfo current_timeline_info_;
 };
 
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.cc b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
index 24ec5d0..2e84809 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
@@ -73,8 +73,12 @@
       rx(init.rx * kRCornerGradientScale),
       ry(init.ry * kRCornerGradientScale) {}
 
+DrawObject::DrawObject()
+    : merge_type_(base::GetTypeId<DrawObject>()) {}
+
 DrawObject::DrawObject(const BaseState& base_state)
-    : base_state_(base_state) {}
+    : base_state_(base_state),
+      merge_type_(base::GetTypeId<DrawObject>()) {}
 
 math::Vector2dF DrawObject::GetScale() const {
   float m00 = base_state_.transform(0, 0);
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.h b/src/cobalt/renderer/rasterizer/egl/draw_object.h
index 19af454..21ac722 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.h
@@ -71,6 +71,11 @@
 
   virtual ~DrawObject() {}
 
+  // Certain draw objects can be merged with others to reduce the number of
+  // draw calls issued. If TryMerge returns true, then |other| can be discarded.
+  base::TypeId GetMergeTypeId() const { return merge_type_; }
+  virtual bool TryMerge(DrawObject* other) { return false; }
+
   // This stage is used to update the vertex buffer for the rasterize
   // stage. Vertex data is handled by the GraphicsState to minimize the number
   // of vertex buffers needed. Once this stage is executed, the rasterizer will
@@ -107,7 +112,7 @@
     RCorner rcorner;
   };
 
-  DrawObject() {}
+  DrawObject();
   explicit DrawObject(const BaseState& base_state);
 
   // Extract the scale vector from this object's transform.
@@ -147,6 +152,10 @@
 
   BaseState base_state_;
 
+  // Provide type information for use with TryMerge. Only DrawObjects that may
+  // be merged need to set this.
+  base::TypeId merge_type_;
+
  private:
   // Return the RCorner values for the given rounded rect, and the normalized
   // rect and corner values used.
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
index e39eaaf..d12c4bc 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
@@ -44,6 +44,19 @@
     BlendType blend_type, base::TypeId draw_type,
     const backend::RenderTarget* render_target,
     const math::RectF& draw_bounds) {
+  // See if this draw object can be merged with the last one.
+  if (!onscreen_draws_.empty()) {
+    DrawInfo& last_draw = onscreen_draws_.back();
+    if (last_draw.render_target == render_target &&
+        last_draw.draw_type == draw_type &&
+        last_draw.blend_type == blend_type &&
+        last_draw.draw_object->TryMerge(draw_object.get())) {
+      last_draw.draw_bounds.Union(draw_bounds);
+      last_draw.draw_id = ++current_draw_id_;
+      return current_draw_id_;
+    }
+  }
+
   onscreen_draws_.emplace_back(draw_object.Pass(), draw_type, blend_type,
       render_target, draw_bounds, ++current_draw_id_);
   return current_draw_id_;
@@ -53,6 +66,19 @@
     BlendType blend_type, base::TypeId draw_type,
     const backend::RenderTarget* render_target,
     const math::RectF& draw_bounds) {
+  // See if this draw object can be merged with the last one.
+  if (!offscreen_draws_.empty()) {
+    DrawInfo& last_draw = offscreen_draws_.back();
+    if (last_draw.render_target == render_target &&
+        last_draw.draw_type == draw_type &&
+        last_draw.blend_type == blend_type &&
+        last_draw.draw_object->TryMerge(draw_object.get())) {
+      last_draw.draw_bounds.Union(draw_bounds);
+      last_draw.draw_id = ++current_draw_id_;
+      return current_draw_id_;
+    }
+  }
+
   offscreen_draws_.emplace_back(draw_object.Pass(), draw_type, blend_type,
       render_target, draw_bounds, ++current_draw_id_);
   return current_draw_id_;
@@ -65,7 +91,7 @@
   RemoveDraws(&external_offscreen_draws_, last_valid_draw_id);
 }
 
-void DrawObjectManager::RemoveDraws(std::vector<DrawInfo>* draw_list,
+void DrawObjectManager::RemoveDraws(DrawList* draw_list,
     uint32_t last_valid_draw_id) {
   // Objects in the draw list should have ascending draw IDs at this point.
   auto iter = draw_list->end();
@@ -117,13 +143,13 @@
 
 void DrawObjectManager::ExecuteOffscreenRasterize(GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  SortOffscreenDraws(&external_offscreen_draws_);
+  SortOffscreenDraws(external_offscreen_draws_,
+                     &sorted_external_offscreen_draws_);
 
   // Process draws handled by an external rasterizer.
   {
     TRACE_EVENT0("cobalt::renderer", "OffscreenExternalRasterizer");
-    for (auto draw = external_offscreen_draws_.begin();
-         draw != external_offscreen_draws_.end(); ++draw) {
+    for (const DrawInfo* draw : sorted_external_offscreen_draws_) {
       // Batched external draws should not have any dependencies.
       DCHECK_EQ(draw->dependencies, 0);
       draw->draw_object->ExecuteRasterize(graphics_state, program_manager);
@@ -133,8 +159,8 @@
     }
   }
 
-  SortOffscreenDraws(&offscreen_draws_);
-  SortOnscreenDraws(&onscreen_draws_);
+  SortOffscreenDraws(offscreen_draws_, &sorted_offscreen_draws_);
+  SortOnscreenDraws(onscreen_draws_, &sorted_onscreen_draws_);
 
   // Update the vertex buffer for all draws.
   {
@@ -145,19 +171,17 @@
   // Process the native offscreen draws.
   {
     TRACE_EVENT0("cobalt::renderer", "OffscreenNativeRasterizer");
-    Rasterize(offscreen_draws_, graphics_state, program_manager);
+    Rasterize(sorted_offscreen_draws_, graphics_state, program_manager);
   }
 }
 
 void DrawObjectManager::ExecuteUpdateVertexBuffer(
     GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
-  for (auto draw = offscreen_draws_.begin(); draw != offscreen_draws_.end();
-       ++draw) {
+  for (const DrawInfo* draw : sorted_offscreen_draws_) {
     draw->draw_object->ExecuteUpdateVertexBuffer(
         graphics_state, program_manager);
   }
-  for (auto draw = onscreen_draws_.begin(); draw != onscreen_draws_.end();
-       ++draw) {
+  for (const DrawInfo* draw : sorted_onscreen_draws_) {
     draw->draw_object->ExecuteUpdateVertexBuffer(
         graphics_state, program_manager);
   }
@@ -166,10 +190,10 @@
 
 void DrawObjectManager::ExecuteOnscreenRasterize(GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  Rasterize(onscreen_draws_, graphics_state, program_manager);
+  Rasterize(sorted_onscreen_draws_, graphics_state, program_manager);
 }
 
-void DrawObjectManager::Rasterize(const std::vector<DrawInfo>& draw_list,
+void DrawObjectManager::Rasterize(const SortedDrawList& sorted_draw_list,
     GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
   const backend::RenderTarget* current_target = nullptr;
   bool using_native_rasterizer = true;
@@ -177,7 +201,7 @@
   // Starting from an unknown state.
   graphics_state->SetDirty();
 
-  for (auto draw = draw_list.begin(); draw != draw_list.end(); ++draw) {
+  for (const DrawInfo* draw : sorted_draw_list) {
     bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
 
     if (draw_uses_native_rasterizer) {
@@ -217,94 +241,131 @@
   }
 }
 
-void DrawObjectManager::SortOffscreenDraws(std::vector<DrawInfo>* draw_list) {
+void DrawObjectManager::SortOffscreenDraws(const DrawList& draw_list,
+    SortedDrawList* sorted_draw_list) {
   TRACE_EVENT0("cobalt::renderer", "SortOffscreenDraws");
 
   // Sort offscreen draws to minimize GPU state changes.
-  for (auto iter = draw_list->begin(); iter != draw_list->end(); ++iter) {
+  sorted_draw_list->reserve(draw_list.size());
+  for (size_t draw_pos = 0; draw_pos < draw_list.size(); ++draw_pos) {
+    auto* draw = &draw_list[draw_pos];
+    bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
+    bool next_uses_native_rasterizer = draw_pos + 1 < draw_list.size() &&
+        draw_list[draw_pos + 1].blend_type != kBlendExternal;
     auto dependencies =
-        dependency_count_.find(iter->render_target->GetSerialNumber());
-    iter->dependencies =
+        dependency_count_.find(draw->render_target->GetSerialNumber());
+    draw->dependencies =
         dependencies != dependency_count_.end() ? dependencies->second : 0;
 
-    for (auto current_draw = iter; current_draw != draw_list->begin();
-         std::swap(*current_draw, *(current_draw - 1)), current_draw--) {
-      auto prev_draw = current_draw - 1;
+    // Find an appropriate sort position for the current draw.
+    auto sort_pos = draw_pos;
+    for (; sort_pos > 0; --sort_pos) {
+      auto* prev_draw = sorted_draw_list->at(sort_pos - 1);
 
       // Unlike onscreen draws, offscreen draws should be grouped by render
       // target. Ensure that render targets with fewer dependencies are first
       // in the draw list.
-      if (prev_draw->dependencies > current_draw->dependencies) {
+      if (prev_draw->dependencies > draw->dependencies) {
         continue;
-      } else if (prev_draw->dependencies < current_draw->dependencies) {
+      } else if (prev_draw->dependencies < draw->dependencies) {
         break;
       }
-      if (prev_draw->render_target > current_draw->render_target) {
+      if (prev_draw->render_target > draw->render_target) {
         continue;
-      } else if (prev_draw->render_target < current_draw->render_target) {
+      } else if (prev_draw->render_target < draw->render_target) {
         break;
       }
 
-      if (prev_draw->draw_bounds.Intersects(current_draw->draw_bounds)) {
-        break;
-      }
-
-      if (prev_draw->draw_type > current_draw->draw_type) {
-        continue;
-      } else if (prev_draw->draw_type < current_draw->draw_type) {
-        break;
-      }
-
-      if (prev_draw->blend_type <= current_draw->blend_type) {
-        break;
-      }
-    }
-  }
-}
-
-void DrawObjectManager::SortOnscreenDraws(std::vector<DrawInfo>* draw_list) {
-  TRACE_EVENT0("cobalt::renderer", "SortOnscreenDraws");
-
-  // Sort onscreen draws to minimize GPU state changes.
-  for (auto iter = draw_list->begin(); iter != draw_list->end(); ++iter) {
-    for (auto current_draw = iter; current_draw != draw_list->begin();
-         std::swap(*current_draw, *(current_draw - 1)), current_draw--) {
-      auto prev_draw = current_draw - 1;
-
-      // Do not sort across different render targets since their contents may
-      // be generated just before consumed by a subsequent draw.
-      if (prev_draw->render_target != current_draw->render_target) {
-        break;
-      }
-
-      if (prev_draw->draw_bounds.Intersects(current_draw->draw_bounds)) {
+      // The rest of the sorting logic is the same between onscreen and
+      // offscreen draws.
+      if (prev_draw->draw_bounds.Intersects(draw->draw_bounds)) {
         break;
       }
 
       // Group native vs. non-native draws together.
-      bool next_uses_same_rasterizer = current_draw + 1 != draw_list->end() &&
-          ((current_draw + 1)->blend_type == kBlendExternal) ==
-          (current_draw->blend_type == kBlendExternal);
-      bool prev_uses_same_rasterizer =
-          (prev_draw->blend_type == kBlendExternal) ==
-          (current_draw->blend_type == kBlendExternal);
-      if (!next_uses_same_rasterizer && !prev_uses_same_rasterizer) {
+      bool prev_uses_native_rasterizer =
+          prev_draw->blend_type != kBlendExternal;
+      if (draw_uses_native_rasterizer != next_uses_native_rasterizer &&
+          draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
+        next_uses_native_rasterizer = prev_uses_native_rasterizer;
         continue;
       }
-      if (next_uses_same_rasterizer && !prev_uses_same_rasterizer) {
+      if (draw_uses_native_rasterizer == next_uses_native_rasterizer &&
+          draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
+        break;
+      }
+      next_uses_native_rasterizer = prev_uses_native_rasterizer;
+
+      if (prev_draw->draw_type > draw->draw_type) {
+        continue;
+      } else if (prev_draw->draw_type < draw->draw_type) {
         break;
       }
 
-      if (prev_draw->draw_type > current_draw->draw_type) {
-        continue;
-      } else if (prev_draw->draw_type < current_draw->draw_type) {
-        break;
-      }
-
-      if (prev_draw->blend_type <= current_draw->blend_type) {
+      if (prev_draw->blend_type <= draw->blend_type) {
         break;
       }
     }
+
+    sorted_draw_list->insert(sorted_draw_list->begin() + sort_pos, draw);
+  }
+}
+
+void DrawObjectManager::SortOnscreenDraws(const DrawList& draw_list,
+    SortedDrawList* sorted_draw_list) {
+  TRACE_EVENT0("cobalt::renderer", "SortOnscreenDraws");
+
+  // Sort onscreen draws to minimize GPU state changes.
+  sorted_draw_list->reserve(draw_list.size());
+  for (size_t draw_pos = 0; draw_pos < draw_list.size(); ++draw_pos) {
+    auto* draw = &draw_list[draw_pos];
+    bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
+    bool next_uses_native_rasterizer = draw_pos + 1 < draw_list.size() &&
+        draw_list[draw_pos + 1].blend_type != kBlendExternal;
+
+    // Find an appropriate sort position for the current draw.
+    auto sort_pos = draw_pos;
+    for (; sort_pos > 0; --sort_pos) {
+      auto* prev_draw = sorted_draw_list->at(sort_pos - 1);
+
+      // Do not sort across different render targets since their contents may
+      // be generated just before consumed by a subsequent draw.
+      if (prev_draw->render_target != draw->render_target) {
+        break;
+      }
+
+      // The rest of the sorting logic is the same between onscreen and
+      // offscreen draws.
+      if (prev_draw->draw_bounds.Intersects(draw->draw_bounds)) {
+        break;
+      }
+
+      // Group native vs. non-native draws together.
+      bool prev_uses_native_rasterizer =
+          prev_draw->blend_type != kBlendExternal;
+      if (draw_uses_native_rasterizer != next_uses_native_rasterizer &&
+          draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
+        next_uses_native_rasterizer = prev_uses_native_rasterizer;
+        continue;
+      }
+      if (draw_uses_native_rasterizer == next_uses_native_rasterizer &&
+          draw_uses_native_rasterizer != prev_uses_native_rasterizer) {
+        break;
+      }
+      next_uses_native_rasterizer = prev_uses_native_rasterizer;
+
+      if (prev_draw->draw_type > draw->draw_type) {
+        continue;
+      } else if (prev_draw->draw_type < draw->draw_type) {
+        break;
+      }
+
+      if (prev_draw->blend_type <= draw->blend_type) {
+        break;
+      }
+    }
+
+    sorted_draw_list->insert(sorted_draw_list->begin() + sort_pos, draw);
   }
 }
 
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
index 10b0f17..beb941e 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
@@ -117,7 +117,7 @@
     BlendType blend_type;
     union {
       uint32_t draw_id;
-      uint32_t dependencies;
+      mutable uint32_t dependencies;
     };
   };
 
@@ -130,25 +130,33 @@
     const backend::RenderTarget* required_target;
   };
 
+  typedef std::vector<DrawInfo> DrawList;
+  typedef std::vector<const DrawInfo*> SortedDrawList;
+
   void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager);
 
-  void Rasterize(const std::vector<DrawInfo>& draw_list,
+  void Rasterize(const SortedDrawList& draw_list,
                  GraphicsState* graphics_state,
                  ShaderProgramManager* program_manager);
 
-  void RemoveDraws(std::vector<DrawInfo>* draw_list,
-                   uint32_t last_valid_draw_id);
+  void RemoveDraws(DrawList* draw_list, uint32_t last_valid_draw_id);
 
-  void SortOffscreenDraws(std::vector<DrawInfo>* draw_list);
-  void SortOnscreenDraws(std::vector<DrawInfo>* draw_list);
+  void SortOffscreenDraws(const DrawList& draw_list,
+                          SortedDrawList* sorted_draw_list);
+  void SortOnscreenDraws(const DrawList& draw_list,
+                         SortedDrawList* sorted_draw_list);
 
   base::Closure reset_external_rasterizer_;
   base::Closure flush_external_offscreen_draws_;
 
-  std::vector<DrawInfo> onscreen_draws_;
-  std::vector<DrawInfo> offscreen_draws_;
-  std::vector<DrawInfo> external_offscreen_draws_;
+  DrawList onscreen_draws_;
+  DrawList offscreen_draws_;
+  DrawList external_offscreen_draws_;
+
+  SortedDrawList sorted_onscreen_draws_;
+  SortedDrawList sorted_offscreen_draws_;
+  SortedDrawList sorted_external_offscreen_draws_;
 
   std::vector<RenderTargetDependency> draw_dependencies_;
   std::unordered_map<int32_t, uint32_t> dependency_count_;
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
index d56c717..eda1511 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
@@ -16,6 +16,8 @@
 
 #include <GLES2/gl2.h>
 
+#include "base/logging.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/renderer/backend/egl/utils.h"
 #include "egl/generated_shader_impl.h"
 #include "starboard/memory.h"
@@ -29,32 +31,77 @@
     const BaseState& base_state, const math::RectF& rect,
     const render_tree::ColorRGBA& color)
     : DrawObject(base_state),
-      vertex_buffer_(NULL) {
+      index_buffer_(nullptr),
+      vertex_buffer_(nullptr) {
+  merge_type_ = base::GetTypeId<DrawPolyColor>();
+
   attributes_.reserve(4);
-  AddRect(rect, GetGLRGBA(GetDrawColor(color) * base_state_.opacity));
+  AddRectVertices(rect, GetGLRGBA(GetDrawColor(color) * base_state_.opacity));
+  indices_.reserve(6);
+  AddRectIndices(0, 1, 2, 3);
+
   graphics_state->ReserveVertexData(
-      attributes_.size() * sizeof(VertexAttributes));
+      attributes_.size() * sizeof(attributes_[0]));
+  graphics_state->ReserveVertexIndices(indices_.size());
 }
 
 DrawPolyColor::DrawPolyColor(const BaseState& base_state)
     : DrawObject(base_state),
-      vertex_buffer_(NULL) {
+      index_buffer_(nullptr),
+      vertex_buffer_(nullptr) {
+  merge_type_ = base::GetTypeId<DrawPolyColor>();
+}
+
+bool DrawPolyColor::TryMerge(DrawObject* other) {
+  if (merge_type_ != other->GetMergeTypeId()) {
+    return false;
+  }
+
+  // If the merge types match, then the objects should use the same shaders.
+  // Otherwise, ensure the merge types are different.
+  DCHECK(GetTypeId() == other->GetTypeId());
+
+  DrawPolyColor* merge = base::polymorphic_downcast<DrawPolyColor*>(other);
+  if (!PrepareForMerge() || !merge->PrepareForMerge()) {
+    return false;
+  }
+
+  // Since the draws for these objects already use indexed triangles, just
+  // concatenate the vertex attributes and indices. Keep in mind the indices
+  // for the |other| object need to be fixed up.
+  uint16_t index_offset = static_cast<uint16_t>(attributes_.size());
+  attributes_.insert(attributes_.end(),
+                     merge->attributes_.begin(), merge->attributes_.end());
+  for (uint16_t index : merge->indices_) {
+    indices_.emplace_back(index + index_offset);
+  }
+
+  base_state_.scissor.Union(merge->base_state_.scissor);
+  return true;
 }
 
 void DrawPolyColor::ExecuteUpdateVertexBuffer(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  vertex_buffer_ = graphics_state->AllocateVertexData(
-      attributes_.size() * sizeof(VertexAttributes));
-  SbMemoryCopy(vertex_buffer_, &attributes_[0],
-               attributes_.size() * sizeof(VertexAttributes));
+  if (attributes_.size() > 0) {
+    vertex_buffer_ = graphics_state->AllocateVertexData(
+        attributes_.size() * sizeof(attributes_[0]));
+    SbMemoryCopy(vertex_buffer_, &attributes_[0],
+                 attributes_.size() * sizeof(attributes_[0]));
+    index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size());
+    SbMemoryCopy(index_buffer_, &indices_[0],
+                 indices_.size() * sizeof(indices_[0]));
+  }
 }
 
 void DrawPolyColor::ExecuteRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  SetupShader(graphics_state, program_manager);
-  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+  if (attributes_.size() > 0) {
+    SetupShader(graphics_state, program_manager);
+    GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
+        graphics_state->GetVertexIndexPointer(index_buffer_)));
+  }
 }
 
 base::TypeId DrawPolyColor::GetTypeId() const {
@@ -86,16 +133,55 @@
   graphics_state->VertexAttribFinish();
 }
 
-void DrawPolyColor::AddRect(const math::RectF& rect, uint32_t color) {
-  AddVertex(rect.x(), rect.y(), color);
-  AddVertex(rect.x(), rect.bottom(), color);
-  AddVertex(rect.right(), rect.y(), color);
-  AddVertex(rect.right(), rect.bottom(), color);
+void DrawPolyColor::AddRectVertices(const math::RectF& rect, uint32_t color) {
+  // Beware that child classes may depend on the order in which these vertices
+  // are added.
+  attributes_.emplace_back(rect.x(), rect.y(), color);
+  attributes_.emplace_back(rect.right(), rect.y(), color);
+  attributes_.emplace_back(rect.x(), rect.bottom(), color);
+  attributes_.emplace_back(rect.right(), rect.bottom(), color);
 }
 
-void DrawPolyColor::AddVertex(float x, float y, uint32_t color) {
-  VertexAttributes attribute = { { x, y }, color };
-  attributes_.push_back(attribute);
+void DrawPolyColor::AddRectIndices(uint16_t top_left, uint16_t top_right,
+    uint16_t bottom_left, uint16_t bottom_right) {
+  indices_.emplace_back(top_left);
+  indices_.emplace_back(top_right);
+  indices_.emplace_back(bottom_left);
+  indices_.emplace_back(top_right);
+  indices_.emplace_back(bottom_left);
+  indices_.emplace_back(bottom_right);
+}
+
+bool DrawPolyColor::PrepareForMerge() {
+  if (can_merge_) {
+    return *can_merge_;
+  }
+
+  // Since a single draw can only have one transform and one scissor, draws
+  // can be merged only if they use the same transform and the vertices are
+  // in their respective scissors.
+
+  // Rounded scissors are too expensive to check for containment.
+  if (base_state_.rounded_scissor_corners) {
+    can_merge_ = false;
+    return *can_merge_;
+  }
+
+  math::RectF scissor(base_state_.scissor);
+  bool in_scissor = true;
+
+  // Transform the vertices and check that they are within the scissor.
+  for (auto& vert : attributes_) {
+    math::PointF pos = base_state_.transform *
+        math::PointF(vert.position[0], vert.position[1]);
+    vert.position[0] = pos.x();
+    vert.position[1] = pos.y();
+    in_scissor = in_scissor && scissor.Contains(pos);
+  }
+
+  base_state_.transform = math::Matrix3F::Identity();
+  can_merge_ = in_scissor;
+  return *can_merge_;
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
index cfce106..356ebf5 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
@@ -17,6 +17,7 @@
 
 #include <vector>
 
+#include "base/optional.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/color_rgba.h"
 #include "cobalt/renderer/rasterizer/egl/draw_object.h"
@@ -34,6 +35,7 @@
                 const math::RectF& rect,
                 const render_tree::ColorRGBA& color);
 
+  bool TryMerge(DrawObject* other) OVERRIDE;
   void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
   void ExecuteRasterize(GraphicsState* graphics_state,
@@ -44,16 +46,26 @@
   explicit DrawPolyColor(const BaseState& base_state);
   void SetupShader(GraphicsState* graphics_state,
                    ShaderProgramManager* program_manager);
-  void AddRect(const math::RectF& rect, uint32_t color);
-  void AddVertex(float x, float y, uint32_t color);
+  void AddRectVertices(const math::RectF& rect, uint32_t color);
+  void AddRectIndices(uint16_t top_left, uint16_t top_right,
+                      uint16_t bottom_left, uint16_t bottom_right);
+  bool PrepareForMerge();
 
   struct VertexAttributes {
+    VertexAttributes(float x, float y, uint32_t color32) {
+      position[0] = x;
+      position[1] = y;
+      color = color32;
+    }
     float position[2];
     uint32_t color;
   };
   std::vector<VertexAttributes> attributes_;
+  std::vector<uint16_t> indices_;
 
+  uint16_t* index_buffer_;
   uint8_t* vertex_buffer_;
+  base::optional<bool> can_merge_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
index f3d38c7..e24392d 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
@@ -32,20 +32,24 @@
 // antialiased inner area, and the solid area in between.
 const int kRegionCount = 3;
 
-// Each region consists of 2 rects which use 4 vertices each. Each region is
-// drawn with 8 triangles, and indices are used to minimize vertex duplication.
-const int kVertexCountPerRegion = 4 * 2;
-const int kIndexCountPerRegion = 8 * 3;
+// Each region consists of an outer and inner rectangle. However, since regions
+// are adjacent to each other, many of these rectangles are shared.
+const int kVertexCount = (kRegionCount + 1) * 4;
 
-const int kVertexCount = kRegionCount * kVertexCountPerRegion;
-const int kIndexCount = kRegionCount * kIndexCountPerRegion;
+// The draw object may draw the content rect as well. If so, two triangles are
+// used to draw the content rect.
+const int kIndexCountForContentRect = 2 * 3;
+
+// Each region has 4 rectangular areas corresponding to the possible borders.
+// Each rectangular area is drawn using 2 triangles.
+const int kIndexCount = kRegionCount * (4 * 2 * 3) + kIndexCountForContentRect;
 }  // namespace
 
 DrawRectBorder::DrawRectBorder(GraphicsState* graphics_state,
     const BaseState& base_state,
     const scoped_refptr<render_tree::RectNode>& node)
     : DrawPolyColor(base_state),
-      index_buffer_(NULL) {
+      draw_content_rect_(false) {
   DCHECK(node->data().border);
 
   const render_tree::Border& border = *(node->data().border);
@@ -99,17 +103,24 @@
                (num_borders == 2 && uniform_opposing_borders) ||
                (num_borders == 4 && uniform_borders));
 
-  // If a background brush is used, then only solid colored ones are supported.
-  // This simplifies blending the inner-antialiased border with the content.
+  // If the background brush is solid-colored, then this object can handle the
+  // content rect as well. Otherwise, don't draw the inner antialiased edge to
+  // avoid having to blend with an unknown color.
   render_tree::ColorRGBA content_color(0);
-  if (is_valid_ && node->data().background_brush) {
-    is_valid_ = node->data().background_brush->GetTypeId() ==
-                base::GetTypeId<render_tree::SolidColorBrush>();
-    if (is_valid_) {
-      const render_tree::SolidColorBrush* solid_brush =
-          base::polymorphic_downcast<const render_tree::SolidColorBrush*>
-              (node->data().background_brush.get());
-      content_color = GetDrawColor(solid_brush->color()) * base_state_.opacity;
+  if (is_valid_) {
+    if (node->data().background_brush) {
+      draw_content_rect_ = node->data().background_brush->GetTypeId() ==
+                           base::GetTypeId<render_tree::SolidColorBrush>();
+      if (draw_content_rect_) {
+        const render_tree::SolidColorBrush* solid_brush =
+            base::polymorphic_downcast<const render_tree::SolidColorBrush*>
+                (node->data().background_brush.get());
+        content_color = GetDrawColor(solid_brush->color()) *
+                        base_state_.opacity;
+      }
+    } else {
+      // No background brush is the same as a totally transparent background.
+      draw_content_rect_ = true;
     }
   }
 
@@ -127,37 +138,13 @@
                                   border_color, content_color);
       if (is_valid_ && attributes_.size() > 0) {
         graphics_state->ReserveVertexData(
-            attributes_.size() * sizeof(VertexAttributes));
+            attributes_.size() * sizeof(attributes_[0]));
         graphics_state->ReserveVertexIndices(indices_.size());
       }
     }
   }
 }
 
-void DrawRectBorder::ExecuteUpdateVertexBuffer(
-    GraphicsState* graphics_state,
-    ShaderProgramManager* program_manager) {
-  if (attributes_.size() > 0) {
-    vertex_buffer_ = graphics_state->AllocateVertexData(
-        attributes_.size() * sizeof(VertexAttributes));
-    SbMemoryCopy(vertex_buffer_, &attributes_[0],
-                 attributes_.size() * sizeof(VertexAttributes));
-    index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size());
-    SbMemoryCopy(index_buffer_, &indices_[0],
-                 indices_.size() * sizeof(indices_[0]));
-  }
-}
-
-void DrawRectBorder::ExecuteRasterize(
-    GraphicsState* graphics_state,
-    ShaderProgramManager* program_manager) {
-  if (attributes_.size() > 0) {
-    SetupShader(graphics_state, program_manager);
-    GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
-        graphics_state->GetVertexIndexPointer(index_buffer_)));
-  }
-}
-
 bool DrawRectBorder::SetSquareBorder(const render_tree::Border& border,
     const math::RectF& border_rect, const math::RectF& content_rect,
     const render_tree::ColorRGBA& border_color,
@@ -196,17 +183,41 @@
   math::RectF outer_rect(border_rect);
   math::RectF inner_rect(content_rect);
   outer_rect.Inset(insets.Scale(0.5f * pixel_size_x, 0.5f * pixel_size_y));
-  inner_rect.Inset(insets.Scale(-0.5f * pixel_size_x, -0.5f * pixel_size_y));
+  if (draw_content_rect_) {
+    inner_rect.Inset(insets.Scale(-0.5f * pixel_size_x, -0.5f * pixel_size_y));
+  }
   math::RectF outer_outer(outer_rect);
   math::RectF inner_inner(inner_rect);
   outer_outer.Inset(insets.Scale(-pixel_size_x, -pixel_size_y));
-  inner_inner.Inset(insets.Scale(pixel_size_x, pixel_size_y));
+  if (draw_content_rect_) {
+    inner_inner.Inset(insets.Scale(pixel_size_x, pixel_size_y));
+  }
 
-  uint32_t border_color32 = GetGLRGBA(border_color);
-  uint32_t content_color32 = GetGLRGBA(content_color);
-  AddRegion(outer_outer, 0, outer_rect, border_color32);
-  AddRegion(outer_rect, border_color32, inner_rect, border_color32);
-  AddRegion(inner_rect, border_color32, inner_inner, content_color32);
+  // Add the vertex attributes for the rectangles that will be used.
+  uint16_t outer_outer_verts = static_cast<uint16_t>(attributes_.size());
+  AddRectVertices(outer_outer, 0);
+  uint16_t outer_rect_verts = static_cast<uint16_t>(attributes_.size());
+  AddRectVertices(outer_rect, GetGLRGBA(border_color));
+  uint16_t inner_rect_verts = static_cast<uint16_t>(attributes_.size());
+  AddRectVertices(inner_rect, GetGLRGBA(border_color));
+  uint16_t inner_inner_verts = inner_rect_verts;
+  if (draw_content_rect_) {
+    inner_inner_verts = static_cast<uint16_t>(attributes_.size());
+    AddRectVertices(inner_inner, GetGLRGBA(content_color));
+  }
+
+  // Add indices to draw the borders using the vertex attributes added.
+  AddBorders(border, outer_outer_verts, outer_rect_verts);
+  AddBorders(border, outer_rect_verts, inner_rect_verts);
+  if (draw_content_rect_) {
+    AddBorders(border, inner_rect_verts, inner_inner_verts);
+  }
+
+  // Draw the content rect as appropriate.
+  if (draw_content_rect_ && content_color.a() > 0.0f) {
+    AddRectIndices(inner_inner_verts, inner_inner_verts + 1,
+                   inner_inner_verts + 2, inner_inner_verts + 3);
+  }
 
   // Update the content and node bounds to account for the antialiasing edges.
   node_bounds_ = outer_outer;
@@ -214,34 +225,27 @@
   return true;
 }
 
-void DrawRectBorder::AddRegion(
-    const math::RectF& outer_rect, uint32_t outer_color,
-    const math::RectF& inner_rect, uint32_t inner_color) {
-  // Add triangles to render the area between the two rects.
-  uint16_t first_vertex = static_cast<uint16_t>(attributes_.size());
-  AddVertex(outer_rect.x(), outer_rect.y(), outer_color);
-  AddVertex(inner_rect.x(), inner_rect.y(), inner_color);
-  AddVertex(outer_rect.right(), outer_rect.y(), outer_color);
-  AddVertex(inner_rect.right(), inner_rect.y(), inner_color);
-  AddVertex(outer_rect.right(), outer_rect.bottom(), outer_color);
-  AddVertex(inner_rect.right(), inner_rect.bottom(), inner_color);
-  AddVertex(outer_rect.x(), outer_rect.bottom(), outer_color);
-  AddVertex(inner_rect.x(), inner_rect.bottom(), inner_color);
-
-  // Use indices to minimize duplication of vertex data. The last two triangles
-  // use the first one or two vertices of the region.
-  uint16_t wrap_start = static_cast<uint16_t>(attributes_.size()) - 2;
-  for (uint16_t i = first_vertex; i < wrap_start; ++i) {
-    indices_.push_back(i);
-    indices_.push_back(i + 1);
-    indices_.push_back(i + 2);
+void DrawRectBorder::AddBorders(const render_tree::Border& border,
+    uint16_t outer_verts, uint16_t inner_verts) {
+  // Draw the area between those two rectangles using triangle primitives.
+  // The vertices for the rectangles were added as top-left, top-right,
+  // bottom-left, and bottom-right. See DrawPolyColor::AddRectVertices().
+  if (border.left.style != render_tree::kBorderStyleNone) {
+    AddRectIndices(outer_verts, inner_verts,
+                   outer_verts + 2, inner_verts + 2);
   }
-  indices_.push_back(wrap_start);
-  indices_.push_back(wrap_start + 1);
-  indices_.push_back(first_vertex);
-  indices_.push_back(wrap_start + 1);
-  indices_.push_back(first_vertex);
-  indices_.push_back(first_vertex + 1);
+  if (border.top.style != render_tree::kBorderStyleNone) {
+    AddRectIndices(outer_verts, outer_verts + 1,
+                   inner_verts, inner_verts + 1);
+  }
+  if (border.right.style != render_tree::kBorderStyleNone) {
+    AddRectIndices(inner_verts + 1, outer_verts + 1,
+                   inner_verts + 3, outer_verts + 3);
+  }
+  if (border.bottom.style != render_tree::kBorderStyleNone) {
+    AddRectIndices(inner_verts + 2, inner_verts + 3,
+                   outer_verts + 2, outer_verts + 3);
+  }
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.h
index 5139fff..2944611 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.h
@@ -35,12 +35,8 @@
                  const BaseState& base_state,
                  const scoped_refptr<render_tree::RectNode>& node);
 
-  void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
-      ShaderProgramManager* program_manager) OVERRIDE;
-  void ExecuteRasterize(GraphicsState* graphics_state,
-      ShaderProgramManager* program_manager) OVERRIDE;
-
   bool IsValid() const { return is_valid_; }
+  bool DrawsContentRect() const { return draw_content_rect_; }
   const math::RectF& GetContentRect() const { return content_rect_; }
   const math::RectF& GetBounds() const { return node_bounds_; }
 
@@ -50,14 +46,13 @@
                        const math::RectF& content_rect,
                        const render_tree::ColorRGBA& border_color,
                        const render_tree::ColorRGBA& content_color);
-  void AddRegion(const math::RectF& outer_rect, uint32_t outer_color,
-                 const math::RectF& inner_rect, uint32_t inner_color);
+  void AddBorders(const render_tree::Border& border,
+                  uint16_t outer_verts, uint16_t inner_verts);
 
   math::RectF content_rect_;
   math::RectF node_bounds_;
-  std::vector<uint16_t> indices_;
-  uint16_t* index_buffer_;
   bool is_valid_;
+  bool draw_content_rect_;
 };
 
 }  // namespace egl
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 49b9243..fad5a3b 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -520,7 +520,11 @@
   }
 
   if (draw_border) {
+    bool content_rect_drawn = draw_border->DrawsContentRect();
     AddTransparentDraw(draw_border.PassAs<DrawObject>(), node_bounds);
+    if (content_rect_drawn) {
+      return;
+    }
   }
 
   // Handle drawing the content.
@@ -537,11 +541,9 @@
     } else {
       scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
           draw_state_, content_rect, solid_brush->color()));
-      if (IsOpaque(draw_state_.opacity * solid_brush->color().a())) {
-        AddOpaqueDraw(draw.Pass(), node_bounds);
-      } else {
-        AddTransparentDraw(draw.Pass(), node_bounds);
-      }
+      // Match the blending mode used by other rect node draws to allow
+      // merging of the draw objects if possible.
+      AddTransparentDraw(draw.Pass(), node_bounds);
     }
   } else if (brush_is_linear_and_supported) {
     const render_tree::LinearGradientBrush* linear_brush =
diff --git a/src/cobalt/renderer/rasterizer/lib/exported/graphics.h b/src/cobalt/renderer/rasterizer/lib/exported/graphics.h
index 0163934..7543fcf 100644
--- a/src/cobalt/renderer/rasterizer/lib/exported/graphics.h
+++ b/src/cobalt/renderer/rasterizer/lib/exported/graphics.h
@@ -31,6 +31,12 @@
 extern "C" {
 #endif
 
+typedef struct CbLibSize {
+  CbLibSize(int width, int height) : width(width), height(height) {}
+  int width;
+  int height;
+} CbLibSize;
+
 typedef void (*CbLibGraphicsContextCreatedCallback)(void* context);
 typedef void (*CbLibGraphicsBeginRenderFrameCallback)(void* context);
 typedef void (*CbLibGraphicsEndRenderFrameCallback)(void* context);
@@ -59,6 +65,13 @@
 // rasterization thread only.
 SB_EXPORT_PLATFORM intptr_t CbLibGrapicsGetMainTextureHandle();
 
+// Sets the target main texture buffer size (in pixels) to use when rendering
+// out HTML content. The specified size is a target and is not guaranteed. In
+// general, the updated size will be picked up and used at the beginning of the
+// next frame.
+SB_EXPORT_PLATFORM void CbLibGraphicsSetTargetMainTextureSize(
+    const CbLibSize& target_render_size);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
index 1ac6887..4838ed8 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -34,6 +34,8 @@
 #include "cobalt/renderer/rasterizer/skia/hardware_mesh.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
 #include "starboard/shared/gles/gl_call.h"
+#include "third_party/glm/glm/gtc/matrix_transform.hpp"
+#include "third_party/glm/glm/gtc/type_ptr.hpp"
 
 COMPILE_ASSERT(
     cobalt::render_tree::kMono == kCbLibVideoStereoModeMono &&
@@ -49,7 +51,11 @@
 
 namespace {
 
-const float kMaxRenderTargetSize = 15360.0f;
+static const float kMaxRenderTargetSize = 15360.0f;
+
+// The minimum amount of change required in the desired texture size to generate
+// a new offscreen render target for the quad texture.
+static const float kMinTextureSizeEpsilon = 20.0f;
 
 // Matches the signatures of the callback setter functions in exported/video.h
 // and exported/graphics.h.
@@ -126,6 +132,16 @@
 EndRenderFrame::LazyCallback g_end_render_frame_callback =
     LAZY_INSTANCE_INITIALIZER;
 
+bool ApproxEqual(const cobalt::math::Size& a, const cobalt::math::Size& b,
+                 float epsilon) {
+  return std::abs(a.width() - b.width()) < epsilon &&
+         std::abs(a.height() - b.height()) < epsilon;
+}
+
+cobalt::math::Size CobaltSizeFromCbLibSize(CbLibSize size) {
+  return cobalt::math::Size(size.width, size.height);
+}
+
 ExternalRasterizer::Impl* g_external_rasterizer_impl = nullptr;
 
 }  // namespace
@@ -152,9 +168,18 @@
 
   intptr_t GetMainTextureHandle();
 
+  // Sets the target size in pixels to use for the main render target buffer.
+  void SetTargetMainTextureSize(const cobalt::math::Size& target_render_size) {
+    target_main_render_target_size_ = target_render_size;
+  }
+
  private:
   void RenderOffscreenVideo(render_tree::FilterNode* map_to_mesh_filter_node);
 
+  scoped_refptr<render_tree::MatrixTransformNode> UpdateTextureSizeAndWrapNode(
+      const cobalt::math::Size& native_render_target_size,
+      const scoped_refptr<render_tree::Node>& render_tree);
+
   base::ThreadChecker thread_checker_;
 
   backend::GraphicsContextEGL* graphics_context_;
@@ -180,7 +205,11 @@
   scoped_refptr<skia::HardwareMesh> left_eye_video_mesh_;
   scoped_refptr<skia::HardwareMesh> right_eye_video_mesh_;
   render_tree::StereoMode video_stereo_mode_;
-  int video_texture_rgb_;
+  GLuint video_texture_rgb_;
+  // The 'target'/'ideal' size to use for the main RenderTarget. The actual size
+  // of the buffer for the main RenderTarget should aim to be within some small
+  // delta of this whenever a new RenderTree is rendered.
+  cobalt::math::Size target_main_render_target_size_;
 };
 
 ExternalRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
@@ -192,23 +221,23 @@
     : graphics_context_(
           base::polymorphic_downcast<backend::GraphicsContextEGL*>(
               graphics_context)),
-      hardware_rasterizer_(
-          graphics_context, skia_atlas_width, skia_atlas_height,
-          skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
-          rasterizer_gpu_cache_size_in_bytes,
-          purge_skia_font_caches_on_destruction),
+      hardware_rasterizer_(graphics_context, skia_atlas_width,
+                           skia_atlas_height, skia_cache_size_in_bytes,
+                           scratch_surface_cache_size_in_bytes,
+                           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) {
+      video_texture_rgb_(0),
+      target_main_render_target_size_(1, 1) {
   CHECK(!g_external_rasterizer_impl);
   g_external_rasterizer_impl = this;
   options_.flags = Rasterizer::kSubmitFlags_Clear;
   graphics_context_->MakeCurrent();
 
-  // TODO: Import the correct size for this and any other textures from the lib
-  // client and re-generate the size as appropriate.
   main_offscreen_render_target_ =
-      graphics_context_->CreateOffscreenRenderTarget(math::Size(1920, 1080));
+      graphics_context_->CreateOffscreenRenderTarget(
+        target_main_render_target_size_);
   main_texture_.reset(new backend::TextureEGL(
       graphics_context_,
       make_scoped_refptr(base::polymorphic_downcast<backend::RenderTargetEGL*>(
@@ -321,12 +350,11 @@
     }
   }
 
-  backend::RenderTargetEGL* main_texture_render_target_egl =
-      base::polymorphic_downcast<backend::RenderTargetEGL*>(
-          main_offscreen_render_target_.get());
-  hardware_rasterizer_.Submit(map_to_mesh_search.replaced_tree,
-                              main_offscreen_render_target_, options_);
-
+  const scoped_refptr<render_tree::MatrixTransformNode> scaled_main_node =
+      UpdateTextureSizeAndWrapNode(render_target->GetSize(),
+                                   map_to_mesh_search.replaced_tree);
+  hardware_rasterizer_.Submit(scaled_main_node, main_offscreen_render_target_,
+                              options_);
   // TODO: Allow clients to specify arbitrary subtrees to render into
   // different textures?
   g_begin_render_frame_callback.Get().Run();
@@ -334,6 +362,52 @@
   g_end_render_frame_callback.Get().Run();
 }
 
+// TODO: Share this logic with the ComponentRenderer.
+scoped_refptr<render_tree::MatrixTransformNode>
+ExternalRasterizer::Impl::UpdateTextureSizeAndWrapNode(
+    const cobalt::math::Size& native_render_target_size,
+    const scoped_refptr<render_tree::Node>& render_tree) {
+  // Create a new offscreen render target if the exist one's size is far enough
+  // off from the target/ideal size.
+  if (!main_offscreen_render_target_ ||
+      !ApproxEqual(main_offscreen_render_target_->GetSize(),
+                   target_main_render_target_size_, kMinTextureSizeEpsilon)) {
+    LOG(INFO) << "Creating a new offscreen render target of size "
+              << target_main_render_target_size_;
+    main_offscreen_render_target_ =
+        graphics_context_->CreateOffscreenRenderTarget(
+            target_main_render_target_size_);
+    // Note: The TextureEGL this pointer references must first be destroyed by
+    // calling reset() before a new TextureEGL can be constructed.
+    main_texture_.reset();
+    main_texture_.reset(new backend::TextureEGL(
+        graphics_context_,
+        make_scoped_refptr(
+            base::polymorphic_downcast<backend::RenderTargetEGL*>(
+                main_offscreen_render_target_.get()))));
+  }
+
+  DCHECK(native_render_target_size.width());
+  DCHECK(native_render_target_size.height());
+  // We wrap the RenderTree in a MatrixTransformNode to scale the RenderTree so
+  // that its scale relative to our RenderTarget matches its original scale
+  // relative to native_render_target_size. This makes the texture cropped to
+  // fit our RenderTarget the same amount it would be for the original
+  // RenderTarget.
+  const float texture_x_scale =
+      static_cast<float>(main_offscreen_render_target_->GetSize().width()) /
+      native_render_target_size.width();
+  const float texture_y_scale =
+      static_cast<float>(main_offscreen_render_target_->GetSize().height()) /
+      native_render_target_size.height();
+  const glm::mat3 scale_mat(glm::scale(
+      glm::mat4(1.0f), glm::vec3(texture_x_scale, texture_y_scale, 1)));
+  const cobalt::math::Matrix3F root_transform_matrix =
+      cobalt::math::Matrix3F::FromArray(glm::value_ptr(scale_mat));
+  return scoped_refptr<render_tree::MatrixTransformNode>(
+      new render_tree::MatrixTransformNode(render_tree, root_transform_matrix));
+}
+
 render_tree::ResourceProvider* ExternalRasterizer::Impl::GetResourceProvider() {
   return hardware_rasterizer_.GetResourceProvider();
 }
@@ -504,3 +578,15 @@
 
   return g_external_rasterizer_impl->GetMainTextureHandle();
 }
+
+void CbLibGraphicsSetTargetMainTextureSize(
+    const CbLibSize& target_render_size) {
+  DCHECK(g_external_rasterizer_impl);
+  if (!g_external_rasterizer_impl) {
+    LOG(WARNING) << __FUNCTION__
+                 << "ExternalRasterizer not yet created; unable to progress.";
+    return;
+  }
+  const cobalt::math::Size size = CobaltSizeFromCbLibSize(target_render_size);
+  g_external_rasterizer_impl->SetTargetMainTextureSize(size);
+}
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index e62c930..d9a85e7 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -700,6 +700,55 @@
   TestTree(new CompositionNode(composition_builder.Pass()));
 }
 
+TEST_F(PixelTest, RectDrawOrder) {
+  // This test ensures that relative draw order is preserved for overlapping
+  // rectangles.
+
+  // Use a linear congruential generator (std::minstd_rand) to generate
+  // deterministic pseudo-random numbers.
+  struct SimpleRand {
+    SimpleRand() : seed(10222016) {}
+    int32_t operator()() {
+      seed = static_cast<int32_t>(
+          (static_cast<int64_t>(seed) * 48271) % 2147483647);
+      return seed;
+    }
+    int32_t seed;
+  } simple_rand;
+
+  const int kPositionScale = 10;
+  math::Size rand_area(output_surface_size().width() / kPositionScale + 1,
+                       output_surface_size().height() / kPositionScale + 1);
+
+  // Add a bunch of random rectangles with varying colors and opacity. Limit
+  // opacity to be less than 100% so that previous rectangles are not totally
+  // overwritten. Also leave a gap at the edges of the rectangles so that
+  // adjacent rects are not considered intersecting.
+  CompositionNode::Builder composition_builder;
+  for (int i = 0; i < 400; ++i) {
+    // The evaluation order of function call parameters is not guaranteed.
+    // To maintain determinism, explicitly calculate the parameters before
+    // calling the relevant functions.
+    int x1 = simple_rand() % rand_area.width();
+    int x2 = simple_rand() % rand_area.width();
+    int y1 = simple_rand() % rand_area.height();
+    int y2 = simple_rand() % rand_area.height();
+    float r = (simple_rand() % 256) / 255.0f;
+    float g = (simple_rand() % 256) / 255.0f;
+    float b = (simple_rand() % 256) / 255.0f;
+    float a = (simple_rand() % 5) * 0.1f + 0.1f;
+    composition_builder.AddChild(new RectNode(
+        math::RectF(
+            std::min(x1, x2) * kPositionScale + 0.1f,
+            std::min(y1, y2) * kPositionScale + 0.1f,
+            std::abs(x1 - x2) * kPositionScale - 0.2f,
+            std::abs(y1 - y2) * kPositionScale - 0.2f),
+        scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(r, g, b, a)))));
+  }
+
+  TestTree(new CompositionNode(composition_builder.Pass()));
+}
+
 namespace {
 
 // Creates a texture containing a 3x3 grid of colors each of the provided
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
index 4ad07b9..ae68696 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
@@ -54,6 +54,10 @@
   return stream_->duplicate();
 }
 
+size_t SkTypeface_CobaltStream::GetStreamLength() const {
+  return stream_->getLength();
+}
+
 SkTypeface_CobaltStreamProvider::SkTypeface_CobaltStreamProvider(
     SkFileMemoryChunkStreamProvider* stream_provider, int face_index,
     Style style, bool is_fixed_pitch, const SkString& family_name,
@@ -83,3 +87,11 @@
   *face_index = face_index_;
   return stream_provider_->OpenStream();
 }
+
+size_t SkTypeface_CobaltStreamProvider::GetStreamLength() const {
+  DLOG(WARNING)
+      << "Requesting stream length of SkTypeface_CobaltStreamProvider. "
+         "This requires a file load and should be used sparingly.";
+  SkAutoTUnref<SkFileMemoryChunkStream> stream(stream_provider_->OpenStream());
+  return stream->getLength();
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
index 2ac66ab..5e30052 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
@@ -28,10 +28,12 @@
   SkTypeface_Cobalt(int face_index, Style style, bool is_fixed_pitch,
                     const SkString& family_name);
 
+  virtual size_t GetStreamLength() const = 0;
+
   bool synthesizes_bold() const { return synthesizes_bold_; }
 
  protected:
-  virtual void onGetFamilyName(SkString* family_name) const SK_OVERRIDE;
+  void onGetFamilyName(SkString* family_name) const SK_OVERRIDE;
 
   int face_index_;
   SkString family_name_;
@@ -46,10 +48,12 @@
   SkTypeface_CobaltStream(SkStreamAsset* stream, int face_index, Style style,
                           bool is_fixed_pitch, const SkString& family_name);
 
-  virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
-                                   bool* serialize) const SK_OVERRIDE;
+  void onGetFontDescriptor(SkFontDescriptor* descriptor,
+                           bool* serialize) const SK_OVERRIDE;
 
-  virtual SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+  SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+
+  size_t GetStreamLength() const SK_OVERRIDE;
 
  private:
   typedef SkTypeface_Cobalt INHERITED;
@@ -64,10 +68,12 @@
       Style style, bool is_fixed_pitch, const SkString& family_name,
       bool disable_synthetic_bolding);
 
-  virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
-                                   bool* serialize) const SK_OVERRIDE;
+  void onGetFontDescriptor(SkFontDescriptor* descriptor,
+                           bool* serialize) const SK_OVERRIDE;
 
-  virtual SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+  SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+
+  size_t GetStreamLength() const SK_OVERRIDE;
 
  private:
   typedef SkTypeface_Cobalt INHERITED;
diff --git a/src/cobalt/renderer/rasterizer/skia/typeface.cc b/src/cobalt/renderer/rasterizer/skia/typeface.cc
index e4396fe..6e0866d 100644
--- a/src/cobalt/renderer/rasterizer/skia/typeface.cc
+++ b/src/cobalt/renderer/rasterizer/skia/typeface.cc
@@ -18,12 +18,6 @@
 
 #include "third_party/skia/include/core/SkPaint.h"
 
-namespace {
-
-const uint32 kEstimatedBytesPerGlyph = 256;
-
-}  // namespace
-
 namespace cobalt {
 namespace renderer {
 namespace rasterizer {
@@ -43,7 +37,7 @@
 }
 
 uint32 SkiaTypeface::GetEstimatedSizeInBytes() const {
-  return typeface_->countGlyphs() * kEstimatedBytesPerGlyph;
+  return static_cast<uint32>(typeface_->GetStreamLength());
 }
 
 scoped_refptr<render_tree::Font> SkiaTypeface::CreateFontWithSize(
diff --git a/src/cobalt/renderer/rasterizer/testdata/RectDrawOrder-expected.png b/src/cobalt/renderer/rasterizer/testdata/RectDrawOrder-expected.png
new file mode 100644
index 0000000..bd6f522
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/RectDrawOrder-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/submission.h b/src/cobalt/renderer/submission.h
index f9cdd79..8d912f6 100644
--- a/src/cobalt/renderer/submission.h
+++ b/src/cobalt/renderer/submission.h
@@ -60,6 +60,39 @@
   // If non-null, |on_rasterized_callback| will be called every time this
   // submission is rasterized.
   base::Closure on_rasterized_callback;
+
+  // Information about the specific timeline that this submission is intended
+  // to run on.  The most important part of TimelineInfo is TimelineInfo::id,
+  // which the renderer pipeline will check to see if it is equal to the
+  // submissions timeline, and if so assume animation continuity.  If not, it
+  // will reset its submission queue, and possibly apply any animation playback
+  // configuration changes specified by the other fields in this structure.
+  struct TimelineInfo {
+    // An id of -1 is valid, and acts as the default id.
+    TimelineInfo()
+        : id(-1), allow_latency_reduction(true), max_submission_queue_size(4) {}
+
+    // A number identifying this timeline and used to check for timeline
+    // continuity between submissions.  If this changes, the renderer pipeline
+    // will reset its submission queue.
+    int id;
+
+    // If true, allows the vector from renderer time to submission time to
+    // increase over time, in effect reducing latency between when a submission
+    // is submitted to when it appears on the screen.  This is typically
+    // desirable for interactive applications, but not as necessary for
+    // non-interactive content (and in this case can result in some frames
+    // being skipped).
+    bool allow_latency_reduction;
+
+    // In order to put a bound on memory we set a maximum submission queue size.
+    // The queue size refers to how many submissions which the renderer has
+    // not caught up to rendering yet will be stored.  If latency reduction
+    // is disallowed, this will likely need to be higher to accommodate for
+    // the larger latency between submission and render.
+    int max_submission_queue_size;
+  };
+  TimelineInfo timeline_info;
 };
 
 }  // namespace renderer
diff --git a/src/cobalt/renderer/submission_queue.cc b/src/cobalt/renderer/submission_queue.cc
index b7b861e..923fc0d 100644
--- a/src/cobalt/renderer/submission_queue.cc
+++ b/src/cobalt/renderer/submission_queue.cc
@@ -34,6 +34,7 @@
 
 SubmissionQueue::SubmissionQueue(
     size_t max_queue_size, base::TimeDelta time_to_converge,
+    bool allow_latency_reduction,
     const DisposeSubmissionFunction& dispose_function)
     : max_queue_size_(max_queue_size),
       dispose_function_(dispose_function),
@@ -47,13 +48,16 @@
       queue_size_(
           "Renderer.SubmissionQueueSize", 0,
           "The current size of the renderer submission queue.  Each item in "
-          "queue contains a render tree and associated animations.") {}
+          "queue contains a render tree and associated animations."),
+      allow_latency_reduction_(allow_latency_reduction) {}
 
 void SubmissionQueue::PushSubmission(const Submission& submission,
                                      const base::TimeTicks& now) {
   TRACE_EVENT0("cobalt::renderer", "SubmissionQueue::PushSubmission()");
 
-  CheckThatNowIsMonotonicallyIncreasing(now);
+  if (!submission_queue_.empty()) {
+    CheckThatNowIsMonotonicallyIncreasing(now);
+  }
 
   if (submission_queue_.size() >= max_queue_size_) {
     // If we are at capacity, then make room for the new submission by erasing
@@ -78,7 +82,12 @@
   double latest_to_submission_time_in_ms =
       latest_to_submission_time.InMillisecondsF();
 
-  to_submission_time_in_ms_.SetTarget(latest_to_submission_time_in_ms, now);
+  // Update our mapping from render time to submission time.
+  if (allow_latency_reduction_ || submission_queue_.empty() ||
+      to_submission_time_in_ms_.GetValueAtTime(now) >
+          latest_to_submission_time_in_ms) {
+    to_submission_time_in_ms_.SetTarget(latest_to_submission_time_in_ms, now);
+  }
 
   // Snap time to the new submission if no existing animations are playing both
   // currently and during the time that we are snapping to.
diff --git a/src/cobalt/renderer/submission_queue.h b/src/cobalt/renderer/submission_queue.h
index f30a0eb..4ca20ca 100644
--- a/src/cobalt/renderer/submission_queue.h
+++ b/src/cobalt/renderer/submission_queue.h
@@ -105,6 +105,7 @@
   // used to allow the Submission/render tree to be disposed/destroyed on a
   // separate thread.
   SubmissionQueue(size_t max_queue_size, base::TimeDelta time_to_converge,
+                  bool allow_latency_reduction = true,
                   const DisposeSubmissionFunction& dispose_function =
                       DisposeSubmissionFunction());
 
@@ -116,19 +117,16 @@
   // timing information already setup.  Time must be monotonically increasing.
   Submission GetCurrentSubmission(const base::TimeTicks& now);
 
-  // Resets the submission queue.
-  void Reset() { submission_queue_.clear(); }
-
- private:
-  typedef std::list<Submission> SubmissionQueueInternal;
+  // Returns the corresponding submission time for a given TimeTicks
+  // "real world" system value.
+  base::TimeDelta submission_time(const base::TimeTicks& time);
 
   // Returns the corresponding renderer time for a given TimeTicks value
   // (e.g. base::TimeTicks::Now()).
   base::TimeDelta render_time(const base::TimeTicks& time);
 
-  // Returns the corresponding submission time for a given TimeTicks
-  // "real world" system value.
-  base::TimeDelta submission_time(const base::TimeTicks& time);
+ private:
+  typedef std::list<Submission> SubmissionQueueInternal;
 
   void PurgeStaleSubmissionsFromQueue(const base::TimeTicks& time);
 
@@ -168,6 +166,12 @@
 
   base::CVal<base::TimeDelta> to_submission_time_cval_;
   base::CVal<size_t> queue_size_;
+
+  // If false, we will only ever allow to_submission_time_cval_ to move
+  // backwards ensuring that animations never speed up during playback (at the
+  // cost of increased and non-recoverable input latency).  This is good for
+  // non-interactive content.
+  const bool allow_latency_reduction_;
 };
 
 }  // namespace renderer
diff --git a/src/cobalt/renderer/submission_queue_test.cc b/src/cobalt/renderer/submission_queue_test.cc
index c3d7a3e..494b131 100644
--- a/src/cobalt/renderer/submission_queue_test.cc
+++ b/src/cobalt/renderer/submission_queue_test.cc
@@ -152,6 +152,53 @@
   EXPECT_DOUBLE_EQ(7.5, current.time_offset.InSecondsF());
 }
 
+TEST(SubmissionQueueTest,
+     TimeDoesNotSkewTowardsFasterOffsetsWhenLatencyReductionIsDisabled) {
+  SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1), false);
+
+  Submission first = MakeSubmissionWithUniqueRenderTree(1.0);
+  Submission second = MakeSubmissionWithUniqueRenderTree(5.0);
+
+  // Offset of 1.0 from render tree time.
+  queue.PushSubmission(first, SecondsToTime(2.0));
+  // Offset of 0.5 from render tree time.
+  queue.PushSubmission(second, SecondsToTime(5.5));
+
+  // Check that the first submission has up until this point had its time
+  // advanced at the same rate as the renderer.
+  Submission current = queue.GetCurrentSubmission(SecondsToTime(5.5));
+  EXPECT_EQ(first.render_tree, current.render_tree);
+  EXPECT_DOUBLE_EQ(4.5, current.time_offset.InSecondsF());
+
+  // At this point we are half-way through the transition.  Ordinarily the
+  // submission queue would try to transition from the initial offset of 1.0
+  // to the new offset of 0.5, but because latency reduction is disabled, the
+  // offset should never increase and it should stay at 1.0.
+  current = queue.GetCurrentSubmission(SecondsToTime(6.0));
+  EXPECT_EQ(second.render_tree, current.render_tree);
+  EXPECT_NEAR(5, current.time_offset.InSecondsF(), 0.001);
+
+  // After 1 second later, we should still be unchanged from offset 1.0
+  current = queue.GetCurrentSubmission(SecondsToTime(8.0));
+  EXPECT_EQ(second.render_tree, current.render_tree);
+  EXPECT_DOUBLE_EQ(7, current.time_offset.InSecondsF());
+
+  // A third submission that increases the latency should indeed still affect
+  // the submission queue transitioned time.
+  Submission third = MakeSubmissionWithUniqueRenderTree(9.5);
+  queue.PushSubmission(third, SecondsToTime(11.0));
+
+  // Nothing should have changed yet...
+  current = queue.GetCurrentSubmission(SecondsToTime(11.0));
+  EXPECT_EQ(third.render_tree, current.render_tree);
+  EXPECT_NEAR(10, current.time_offset.InSecondsF(), 0.001);
+
+  // We should be transitioning to a larger offset now...
+  current = queue.GetCurrentSubmission(SecondsToTime(11.5));
+  EXPECT_EQ(third.render_tree, current.render_tree);
+  EXPECT_NEAR(10.25, current.time_offset.InSecondsF(), 0.001);
+}
+
 // Check that inserting a submission older than what the renderer thinks the
 // submission time is will cause the SubmissionQueue to jump back in time to
 // make to accomodate the newly pushed old submission.
diff --git a/src/cobalt/script/mozjs-45/mozjs.cc b/src/cobalt/script/mozjs-45/mozjs.cc
index 53edb1f..b17937f 100644
--- a/src/cobalt/script/mozjs-45/mozjs.cc
+++ b/src/cobalt/script/mozjs-45/mozjs.cc
@@ -60,8 +60,7 @@
 }
 
 int MozjsMain(int argc, char** argv) {
-  JavaScriptEngine::Options js_options;
-  cobalt::script::StandaloneJavascriptRunner standalone_runner(js_options);
+  cobalt::script::StandaloneJavascriptRunner standalone_runner();
   MozjsGlobalEnvironment* global_environment =
       static_cast<MozjsGlobalEnvironment*>(
           standalone_runner.global_environment().get());
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc
index 364d359..f637f47 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -142,7 +142,7 @@
     : context_(nullptr), accumulated_extra_memory_cost_(0), options_(options) {
   TRACE_EVENT0("cobalt::script", "MozjsEngine::MozjsEngine()");
   SbOnce(&g_js_init_once_control, CallInitAndRegisterShutDownOnce);
-  runtime_ = JS_NewRuntime(options_.js_options.gc_threshold_bytes);
+  runtime_ = JS_NewRuntime(options_.gc_threshold_bytes);
   CHECK(runtime_);
 
   // Sets the size of the native stack that should not be exceeded.
@@ -207,7 +207,7 @@
 scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment() {
   TRACE_EVENT0("cobalt::script", "MozjsEngine::CreateGlobalEnvironment()");
   DCHECK(thread_checker_.CalledOnValidThread());
-  return new MozjsGlobalEnvironment(runtime_, options_.js_options);
+  return new MozjsGlobalEnvironment(runtime_);
 }
 
 void MozjsEngine::CollectGarbage() {
@@ -221,7 +221,7 @@
   accumulated_extra_memory_cost_ += bytes;
 
   const bool do_collect_garbage =
-      accumulated_extra_memory_cost_ > options_.js_options.gc_threshold_bytes;
+      accumulated_extra_memory_cost_ > options_.gc_threshold_bytes;
   if (do_collect_garbage) {
     accumulated_extra_memory_cost_ = 0;
     CollectGarbage();
@@ -326,8 +326,7 @@
 scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine(
     const JavaScriptEngine::Options& options) {
   TRACE_EVENT0("cobalt::script", "JavaScriptEngine::CreateEngine()");
-  mozjs::MozjsEngine::Options moz_options(options);
-  return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(moz_options));
+  return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(options));
 }
 
 // static
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.h b/src/cobalt/script/mozjs-45/mozjs_engine.h
index b432000..a4a2314 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.h
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.h
@@ -27,12 +27,6 @@
 
 class MozjsEngine : public JavaScriptEngine {
  public:
-  struct Options {
-    explicit Options(const JavaScriptEngine::Options& js_options)
-        : js_options(js_options) {}
-    JavaScriptEngine::Options js_options;  // Generic settings.
-  };
-
   explicit MozjsEngine(const Options& options);
   ~MozjsEngine() OVERRIDE;
 
@@ -70,7 +64,7 @@
   // Used to handle javascript errors.
   ErrorHandler error_handler_;
 
-  Options options_;
+  JavaScriptEngine::Options options_;
 };
 }  // namespace mozjs
 }  // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 086ef37..f551025 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -120,8 +120,7 @@
 static base::LazyInstance<MozjsStubHandler> proxy_handler;
 }  // namespace
 
-MozjsGlobalEnvironment::MozjsGlobalEnvironment(
-    JSRuntime* runtime, const JavaScriptEngine::Options& options)
+MozjsGlobalEnvironment::MozjsGlobalEnvironment(JSRuntime* runtime)
     : context_(NULL),
       garbage_collection_count_(0),
       context_destructor_(&context_),
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index a7bccae..ad99d14 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -45,8 +45,7 @@
 class MozjsGlobalEnvironment : public GlobalEnvironment,
                                public Wrappable::CachedWrapperAccessor {
  public:
-  MozjsGlobalEnvironment(JSRuntime* runtime,
-                         const JavaScriptEngine::Options& options);
+  explicit MozjsGlobalEnvironment(JSRuntime* runtime);
   ~MozjsGlobalEnvironment() OVERRIDE;
 
   void CreateGlobalObject() OVERRIDE;
diff --git a/src/cobalt/script/standalone_javascript_runner.cc b/src/cobalt/script/standalone_javascript_runner.cc
index 528f379..a71d2d1 100644
--- a/src/cobalt/script/standalone_javascript_runner.cc
+++ b/src/cobalt/script/standalone_javascript_runner.cc
@@ -24,8 +24,8 @@
 namespace script {
 
 StandaloneJavascriptRunner::StandaloneJavascriptRunner(
-    const JavaScriptEngine::Options& options) {
-  CommonInitialization(options);
+    const JavaScriptEngine::Options& javascript_engine_options) {
+  CommonInitialization(javascript_engine_options);
   global_environment_->CreateGlobalObject();
 }
 
@@ -54,8 +54,8 @@
 }
 
 void StandaloneJavascriptRunner::CommonInitialization(
-    const JavaScriptEngine::Options& options) {
-  engine_ = JavaScriptEngine::CreateEngine(options);
+    const JavaScriptEngine::Options& javascript_engine_options) {
+  engine_ = JavaScriptEngine::CreateEngine(javascript_engine_options);
   global_environment_ = engine_->CreateGlobalEnvironment();
   environment_settings_.reset(new EnvironmentSettings());
 }
diff --git a/src/cobalt/script/standalone_javascript_runner.h b/src/cobalt/script/standalone_javascript_runner.h
index 16526df..a7ffb7b 100644
--- a/src/cobalt/script/standalone_javascript_runner.h
+++ b/src/cobalt/script/standalone_javascript_runner.h
@@ -30,13 +30,14 @@
 class StandaloneJavascriptRunner {
  public:
   StandaloneJavascriptRunner(
-      const JavaScriptEngine::Options& options = JavaScriptEngine::Options());
+      const JavaScriptEngine::Options& javascript_engine_options =
+          JavaScriptEngine::Options());
 
   template <typename GlobalInterface>
   StandaloneJavascriptRunner(
-      const JavaScriptEngine::Options& options,
+      const JavaScriptEngine::Options& javascript_engine_options,
       const scoped_refptr<GlobalInterface>& global_object) {
-    CommonInitialization(options);
+    CommonInitialization(javascript_engine_options);
     global_environment_->CreateGlobalObject(global_object,
                                             environment_settings_.get());
   }
@@ -53,7 +54,8 @@
   }
 
  private:
-  void CommonInitialization(const JavaScriptEngine::Options& options);
+  void CommonInitialization(
+      const JavaScriptEngine::Options& javascript_engine_options);
   void ExecuteAndPrintResult(const base::SourceLocation& source_location,
                              const std::string& script);
 
diff --git a/src/cobalt/speech/sandbox/speech_sandbox.cc b/src/cobalt/speech/sandbox/speech_sandbox.cc
index 8638535..29b09c5 100644
--- a/src/cobalt/speech/sandbox/speech_sandbox.cc
+++ b/src/cobalt/speech/sandbox/speech_sandbox.cc
@@ -33,7 +33,7 @@
   trace_to_file_.reset(new trace_event::ScopedTraceToFile(trace_log_path));
 
   network::NetworkModule::Options network_options;
-  network_options.require_https = false;
+  network_options.https_requirement = network::kHTTPSOptional;
   network_module_.reset(new network::NetworkModule(network_options));
 
   GURL url(file_path_string);
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
index 70cf32b..8b846dd 100644
--- a/src/nb/analytics/memory_tracker_helpers.h
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -162,7 +162,7 @@
       AllocationRecord,
       std::less<const void*>,  // required, when specifying allocator.
       nb::StdAllocator<
-          std::pair<const void*, AllocationRecord>,
+          std::pair<const void* const, AllocationRecord>,
           NoReportAllocator> > PointerMap;
 
   PointerMap pointer_map_;
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index fa69e65..4b010c4 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -70,6 +70,7 @@
   * Color keys
   * Closed Caption key
   * Application launch key
+  * Channel Up/Down keys
 
 ### `kSbEventTypeLowMemory`
 
diff --git a/src/starboard/client_porting/poem/assert_poem.h b/src/starboard/client_porting/poem/assert_poem.h
index 9d01a43..587d807 100644
--- a/src/starboard/client_porting/poem/assert_poem.h
+++ b/src/starboard/client_porting/poem/assert_poem.h
@@ -23,6 +23,7 @@
 
 #if !defined(POEM_NO_EMULATION)
 
+#undef assert
 // On one line so that the assert macros do not interfere with reporting of line
 // numbers in compiler error messages.
 #define assert(x)                                                            \
diff --git a/src/starboard/client_porting/poem/inet_poem.h b/src/starboard/client_porting/poem/inet_poem.h
index 55cf47a..f575106 100644
--- a/src/starboard/client_porting/poem/inet_poem.h
+++ b/src/starboard/client_porting/poem/inet_poem.h
@@ -19,9 +19,16 @@
 
 #include "starboard/byte_swap.h"
 
+#undef htonl
 #define htonl(x) SB_HOST_TO_NET_U32(x)
+
+#undef htons
 #define htons(x) SB_HOST_TO_NET_U16(x)
+
+#undef ntohl
 #define ntohl(x) SB_NET_TO_HOST_U32(x)
+
+#undef ntohs
 #define ntohs(x) SB_NET_TO_HOST_U16(x)
 
 #endif  // STARBOARD_CLIENT_PORTING_POEM_INET_POEM_H_
diff --git a/src/starboard/key.h b/src/starboard/key.h
index 462dcaf..73a1cb6 100644
--- a/src/starboard/key.h
+++ b/src/starboard/key.h
@@ -214,6 +214,8 @@
   kSbKeyYellow = 0x195,
   kSbKeyBlue = 0x196,
 
+  kSbKeyChannelUp = 0x1AB,
+  kSbKeyChannelDown = 0x1AC,
   kSbKeySubtitle = 0x1CC,
   kSbKeyClosedCaption = kSbKeySubtitle,
 
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py
index 3b476d1..fa70644 100644
--- a/src/starboard/linux/shared/launcher.py
+++ b/src/starboard/linux/shared/launcher.py
@@ -38,30 +38,26 @@
 class Launcher(abstract_launcher.AbstractLauncher):
   """Class for launching Cobalt/tools on Linux."""
 
-  def __init__(self, platform, target_name, config, device_id, args):
+  def __init__(self, platform, target_name, config, device_id, args,
+               output_file, out_directory):
     super(Launcher, self).__init__(platform, target_name, config, device_id,
-                                   args)
+                                   args, output_file, out_directory)
     if not self.device_id:
       if socket.has_ipv6:  #  If the device supports IPv6:
         self.device_id = "::1"  #  Use the only IPv6 loopback address
       else:
         self.device_id = socket.gethostbyname("localhost")
 
-    executable_dir = "{}_{}".format(self.platform, self.config)
-    self.executable = os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                                   os.pardir, os.pardir,
-                                                   os.pardir, "out",
-                                                   executable_dir,
-                                                   target_name))
+    self.executable = abstract_launcher.GetDefaultTargetPath(
+        platform, config, target_name)
 
     self.pid = None
 
   def Run(self):
     """Runs launcher's executable."""
 
-    signal.signal(signal.SIGTERM, lambda signum, frame: self.Kill())
-    signal.signal(signal.SIGINT, lambda signum, frame: self.Kill())
-    proc = subprocess.Popen([self.executable] + self.target_command_line_params)
+    proc = subprocess.Popen([self.executable] + self.target_command_line_params,
+                            stdout=self.output_file, stderr=self.output_file)
     self.pid = proc.pid
     proc.wait()
     return proc.returncode
@@ -71,5 +67,5 @@
     if self.pid:
       try:
         os.kill(self.pid, signal.SIGTERM)
-      except OSError:
+      except OSError:  # Process is already dead
         raise OSError("Process already closed.")
diff --git a/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi b/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi
index fd25c54..2e7df1d 100644
--- a/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi
@@ -70,6 +70,8 @@
       '-Wno-unused-local-typedefs',
       # Disable warning: 'narrowing conversion'
       '-Wno-narrowing',
+      # Do not remove null this checks.
+      '-fno-delete-null-pointer-checks',
     ],
     'conditions': [
       ['cobalt_fastbuild==0', {
diff --git a/src/starboard/nplb/key_test.cc b/src/starboard/nplb/key_test.cc
index cbfd240..f3644cd 100644
--- a/src/starboard/nplb/key_test.cc
+++ b/src/starboard/nplb/key_test.cc
@@ -30,6 +30,8 @@
   EXPECT_NE(kSbKeyUnknown, kSbKeyYellow);
   EXPECT_NE(kSbKeyUnknown, kSbKeyBlue);
   EXPECT_NE(kSbKeyUnknown, kSbKeySubtitle);
+  EXPECT_NE(kSbKeyUnknown, kSbKeyChannelUp);
+  EXPECT_NE(kSbKeyUnknown, kSbKeyChannelDown);
   EXPECT_NE(kSbKeyUnknown, kSbKeyClosedCaption);
   EXPECT_NE(kSbKeyUnknown, kSbKeyLaunchThisApplication);
 #endif  //  SB_API_VERSION >= SB_NEW_KEYCODES_API_VERSION
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index eb9b8cd..ec4439d 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -249,6 +249,8 @@
         'time_zone_get_current_test.cc',
         'time_zone_get_dst_name_test.cc',
         'time_zone_get_name_test.cc',
+        'undefined_behavior_test.cc',
+        'unsafe_math_test.cc',
         'user_get_current_test.cc',
         'user_get_property_test.cc',
         'user_get_signed_in_test.cc',
diff --git a/src/starboard/nplb/undefined_behavior_test.cc b/src/starboard/nplb/undefined_behavior_test.cc
new file mode 100644
index 0000000..8081c3b
--- /dev/null
+++ b/src/starboard/nplb/undefined_behavior_test.cc
@@ -0,0 +1,60 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+class Object {
+ public:
+  SB_C_NOINLINE bool ThisPointerIsNull() {
+    auto* pointer = this;
+    // Intentionally not "return !pointer", and with a side effect for one
+    // branch, in order to more closely match the form that this undefined
+    // behavior is observed to take in the wild.
+    if (!pointer) {
+      return true;
+    }
+    member_ = 42;
+    return false;
+  }
+
+ private:
+  int member_ = 0;
+};
+
+TEST(SbUndefinedBehaviorTest, CallThisPointerIsNullSunnyDay) {
+  auto* object = new Object();
+  EXPECT_FALSE(object->ThisPointerIsNull());
+  delete object;
+}
+
+TEST(SbUndefinedBehaviorTest, CallThisPointerIsNullRainyDay) {
+  auto* object = static_cast<Object*>(nullptr);
+  auto* object_copy = object;
+  EXPECT_TRUE(object_copy->ThisPointerIsNull());
+  // WARNING!  Failure of this test indicates that your toolchain believes
+  // that removing a NULL check on a this pointer is a valid optimization.
+  // While from a pure C++ standard perspective this is true, Starboard client
+  // applications, such as Cobalt, depend on third party code bases in which
+  // this assumption is not safe.  If you fail this test and are using GCC or
+  // Clang (especially with a GCC version >= 6), then it is highly recommended
+  // that you add the "-fno-delete-null-pointer-checks" flag.
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/unsafe_math_test.cc b/src/starboard/nplb/unsafe_math_test.cc
new file mode 100644
index 0000000..7eab0cc
--- /dev/null
+++ b/src/starboard/nplb/unsafe_math_test.cc
@@ -0,0 +1,151 @@
+// 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 <cmath>
+#include <limits>
+
+#include "starboard/double.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+TEST(SbUnsafeMathTest, NaNDoubleSunnyDay) {
+  auto a = std::numeric_limits<double>::quiet_NaN();
+  auto b = std::numeric_limits<double>::quiet_NaN();
+  auto c = 42.5;
+  auto infinity = std::numeric_limits<double>::infinity();
+
+  // A NaN is a NaN.
+  EXPECT_TRUE(SbDoubleIsNan(a));
+  EXPECT_TRUE(SbDoubleIsNan(b));
+
+  // However, NaN is not ordered.  It is not greater than, less than, or
+  // equal to anything, including itself.
+  EXPECT_FALSE(a == a);
+  EXPECT_FALSE(a > a);
+  EXPECT_FALSE(a < a);
+  EXPECT_FALSE(a >= a);
+  EXPECT_FALSE(a <= a);
+
+  EXPECT_FALSE(a == b);
+  EXPECT_FALSE(a > b);
+  EXPECT_FALSE(a < b);
+  EXPECT_FALSE(a >= b);
+  EXPECT_FALSE(a <= b);
+
+  EXPECT_FALSE(a == c);
+  EXPECT_FALSE(a > c);
+  EXPECT_FALSE(a < c);
+  EXPECT_FALSE(a >= c);
+  EXPECT_FALSE(a <= c);
+
+  EXPECT_FALSE(c == a);
+  EXPECT_FALSE(c > a);
+  EXPECT_FALSE(c < a);
+  EXPECT_FALSE(c >= a);
+  EXPECT_FALSE(c <= a);
+
+  // All operations involving a NaN result in a NaN.
+  EXPECT_TRUE(SbDoubleIsNan(a + b));
+  EXPECT_TRUE(SbDoubleIsNan(a * b));
+  EXPECT_TRUE(SbDoubleIsNan(a - b));
+  EXPECT_TRUE(SbDoubleIsNan(a / b));
+
+  EXPECT_TRUE(SbDoubleIsNan(a + c));
+  EXPECT_TRUE(SbDoubleIsNan(a * c));
+  EXPECT_TRUE(SbDoubleIsNan(a - c));
+  EXPECT_TRUE(SbDoubleIsNan(a / c));
+
+  EXPECT_TRUE(SbDoubleIsNan(c + a));
+  EXPECT_TRUE(SbDoubleIsNan(c * a));
+  EXPECT_TRUE(SbDoubleIsNan(c - a));
+  EXPECT_TRUE(SbDoubleIsNan(c / a));
+
+  // (+/- infinity / +/- infinity) results in a NaN.
+  EXPECT_TRUE(SbDoubleIsNan(infinity / infinity));
+  EXPECT_TRUE(SbDoubleIsNan(-infinity / infinity));
+  EXPECT_TRUE(SbDoubleIsNan(infinity / -infinity));
+  EXPECT_TRUE(SbDoubleIsNan(-infinity / -infinity));
+
+  // Infinity minus infinity (and signed equivalents) result in a NaN.
+  EXPECT_TRUE(SbDoubleIsNan(infinity + -infinity));
+  EXPECT_TRUE(SbDoubleIsNan(-infinity + infinity));
+  EXPECT_TRUE(SbDoubleIsNan(infinity - infinity));
+  EXPECT_TRUE(SbDoubleIsNan(-infinity - -infinity));
+
+  EXPECT_TRUE(SbDoubleIsNan(std::sqrt(-c)));
+}
+
+TEST(SbUnsafeMathTest, InfinityDoubleSunnyDay) {
+  auto a = std::numeric_limits<double>::infinity();
+  auto b = std::numeric_limits<double>::infinity();
+
+  // Infinity is equal to itself.
+  EXPECT_TRUE(a == b);
+  EXPECT_TRUE(-a == -b);
+
+  // Infinity is greater than the maximium double.
+  EXPECT_TRUE(a > std::numeric_limits<double>::max());
+  EXPECT_TRUE(a >= std::numeric_limits<double>::max());
+
+  // Negative infinity is less than the minimum double.
+  EXPECT_TRUE(-a < std::numeric_limits<double>::min());
+  EXPECT_TRUE(-a <= std::numeric_limits<double>::min());
+}
+
+TEST(SbUnsafeMathTest, SignedZeroDoubleSunnyDay) {
+  auto a = 42.5;
+  auto positive_zero = 0.0;
+  auto negative_zero = -0.0;
+  auto infinity = std::numeric_limits<double>::infinity();
+
+  EXPECT_TRUE(positive_zero == negative_zero);
+
+  EXPECT_TRUE(negative_zero / a == -positive_zero);
+
+  EXPECT_TRUE(negative_zero * negative_zero == positive_zero);
+
+  EXPECT_TRUE(a + positive_zero == a);
+  EXPECT_TRUE(a + negative_zero == a);
+
+  EXPECT_TRUE(negative_zero + negative_zero == negative_zero - positive_zero);
+  EXPECT_TRUE(negative_zero - positive_zero == negative_zero);
+
+  EXPECT_TRUE(positive_zero + positive_zero == positive_zero - negative_zero);
+  EXPECT_TRUE(positive_zero - negative_zero == positive_zero);
+
+  EXPECT_TRUE(a - a == a + (-a));
+  EXPECT_TRUE(a + (-a) == positive_zero);
+
+  EXPECT_TRUE(std::sqrt(negative_zero) == negative_zero);
+  EXPECT_TRUE(negative_zero / -infinity == positive_zero);
+
+  EXPECT_TRUE(SbDoubleAbsolute(a) / negative_zero == -infinity);
+
+  EXPECT_TRUE(SbDoubleIsNan(positive_zero * infinity));
+  EXPECT_TRUE(SbDoubleIsNan(-positive_zero * infinity));
+  EXPECT_TRUE(SbDoubleIsNan(positive_zero * -infinity));
+  EXPECT_TRUE(SbDoubleIsNan(-positive_zero * -infinity));
+
+  EXPECT_TRUE(SbDoubleIsNan(positive_zero / positive_zero));
+  EXPECT_TRUE(SbDoubleIsNan(-positive_zero / positive_zero));
+  EXPECT_TRUE(SbDoubleIsNan(positive_zero / -positive_zero));
+  EXPECT_TRUE(SbDoubleIsNan(-positive_zero / -positive_zero));
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/raspi/shared/application_dispmanx.cc b/src/starboard/raspi/shared/application_dispmanx.cc
index 4f9dddf..55e6adc 100644
--- a/src/starboard/raspi/shared/application_dispmanx.cc
+++ b/src/starboard/raspi/shared/application_dispmanx.cc
@@ -85,6 +85,7 @@
 
 void ApplicationDispmanx::AcceptFrame(SbPlayer player,
                                       const scoped_refptr<VideoFrame>& frame,
+                                      int z_index,
                                       int x,
                                       int y,
                                       int width,
diff --git a/src/starboard/raspi/shared/application_dispmanx.h b/src/starboard/raspi/shared/application_dispmanx.h
index a3469c2..37f0b61 100644
--- a/src/starboard/raspi/shared/application_dispmanx.h
+++ b/src/starboard/raspi/shared/application_dispmanx.h
@@ -50,6 +50,7 @@
   void Teardown() SB_OVERRIDE;
   void AcceptFrame(SbPlayer player,
                    const scoped_refptr<VideoFrame>& frame,
+                   int z_index,
                    int x,
                    int y,
                    int width,
diff --git a/src/starboard/shared/pthread/thread_create.cc b/src/starboard/shared/pthread/thread_create.cc
index 6cc9028..8432041 100644
--- a/src/starboard/shared/pthread/thread_create.cc
+++ b/src/starboard/shared/pthread/thread_create.cc
@@ -61,12 +61,14 @@
 
   delete thread_params;
 
+#if !SB_HAS_QUIRK(THREAD_AFFINITY_UNSUPPORTED)
   if (SbThreadIsValidAffinity(affinity)) {
     cpu_set_t cpu_set;
     CPU_ZERO(&cpu_set);
     CPU_SET(affinity, &cpu_set);
     sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
   }
+#endif
 
   return entry_point(real_context);
 }
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index d287112..71235ab 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -156,11 +156,12 @@
 #if SB_HAS(PLAYER)
 void Application::HandleFrame(SbPlayer player,
                               const scoped_refptr<VideoFrame>& frame,
+                              int z_index,
                               int x,
                               int y,
                               int width,
                               int height) {
-  AcceptFrame(player, frame, x, y, width, height);
+  AcceptFrame(player, frame, z_index, x, y, width, height);
 }
 #endif  // SB_HAS(PLAYER)
 
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index d4ec732..df00670 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -228,6 +228,7 @@
   // video manually (should be rare). Will be called from an external thread.
   void HandleFrame(SbPlayer player,
                    const scoped_refptr<VideoFrame>& frame,
+                   int z_index,
                    int x,
                    int y,
                    int width,
@@ -265,6 +266,7 @@
   // system. Will be called from an external thread.
   virtual void AcceptFrame(SbPlayer /* player */,
                            const scoped_refptr<VideoFrame>& /* frame */,
+                           int /* z_index */,
                            int /* x */,
                            int /* y */,
                            int /* width */,
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 758f4cb..276c6c5 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -174,10 +174,16 @@
         if (!SbDrmSystemIsValid(drm_system_)) {
           return false;
         }
-        if (drm_system_->Decrypt(input_buffer) == SbDrmSystemPrivate::kRetry) {
+        SbDrmSystemPrivate::DecryptStatus decrypt_status =
+          drm_system_->Decrypt(input_buffer);
+        if (decrypt_status == SbDrmSystemPrivate::kRetry) {
           *written = false;
           return true;
         }
+        if (decrypt_status == SbDrmSystemPrivate::kFailure) {
+          *written = false;
+          return false;
+        }
       }
       audio_renderer_->WriteSample(input_buffer);
     }
@@ -194,10 +200,16 @@
         if (!SbDrmSystemIsValid(drm_system_)) {
           return false;
         }
-        if (drm_system_->Decrypt(input_buffer) == SbDrmSystemPrivate::kRetry) {
+        SbDrmSystemPrivate::DecryptStatus decrypt_status =
+          drm_system_->Decrypt(input_buffer);
+        if (decrypt_status == SbDrmSystemPrivate::kRetry) {
           *written = false;
           return true;
         }
+        if (decrypt_status == SbDrmSystemPrivate::kFailure) {
+          *written = false;
+          return false;
+        }
       }
       video_renderer_->WriteSample(input_buffer);
     }
@@ -317,7 +329,8 @@
           audio_renderer_->GetCurrentTime(),
           audio_renderer_->IsEndOfStreamPlayed());
       shared::starboard::Application::Get()->HandleFrame(
-          player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
+          player_, frame, bounds_.z_index, bounds_.x, bounds_.y, bounds_.width,
+          bounds_.height);
     }
 
     player_worker_->UpdateDroppedVideoFrames(
@@ -347,7 +360,7 @@
   if (IsPunchoutMode()) {
     // Clear the video frame as we terminate.
     shared::starboard::Application::Get()->HandleFrame(
-        player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0);
+        player_, VideoFrame::CreateEOSFrame(), 0, 0, 0, 0, 0);
   }
 }
 
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index 7e017cc..5b8feed 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -90,8 +90,12 @@
   worker_->WriteEndOfStream(stream_type);
 }
 
-void SbPlayerPrivate::SetBounds(int x, int y, int width, int height) {
-  PlayerWorker::Bounds bounds = {x, y, width, height};
+void SbPlayerPrivate::SetBounds(int z_index,
+                                int x,
+                                int y,
+                                int width,
+                                int height) {
+  PlayerWorker::Bounds bounds = {z_index, x, y, width, height};
   worker_->SetBounds(bounds);
   // TODO: Wait until a frame is rendered with the updated bounds.
 }
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index 67ce3bb..0a0aa8a 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -46,7 +46,7 @@
                    const SbMediaVideoSampleInfo* video_sample_info,
                    const SbDrmSampleInfo* sample_drm_info);
   void WriteEndOfStream(SbMediaType stream_type);
-  void SetBounds(int x, int y, int width, int height);
+  void SetBounds(int z_index, int x, int y, int width, int height);
 
   void GetInfo(SbPlayerInfo* out_player_info);
   void SetPause(bool pause);
diff --git a/src/starboard/shared/starboard/player/player_set_bounds.cc b/src/starboard/shared/starboard/player/player_set_bounds.cc
index c275c82..0c0921c 100644
--- a/src/starboard/shared/starboard/player/player_set_bounds.cc
+++ b/src/starboard/shared/starboard/player/player_set_bounds.cc
@@ -18,7 +18,7 @@
 #include "starboard/shared/starboard/player/player_internal.h"
 
 void SbPlayerSetBounds(SbPlayer player,
-                       int /*z_index*/,
+                       int z_index,
                        int x,
                        int y,
                        int width,
@@ -27,5 +27,5 @@
     SB_DLOG(WARNING) << "player is invalid.";
     return;
   }
-  player->SetBounds(x, y, width, height);
+  player->SetBounds(z_index, x, y, width, height);
 }
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 30da877..5237fa1 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -51,6 +51,7 @@
   };
 
   struct Bounds {
+    int z_index;
     int x;
     int y;
     int width;
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
index acc7057..ec9f438 100644
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -84,6 +84,9 @@
 
 namespace {
 
+const HdcpProtection kHDCPProtectionMode =
+    HdcpProtection::OnWithTypeEnforcement;
+
 const int kWinSockVersionMajor = 2;
 const int kWinSockVersionMinor = 2;
 
@@ -615,7 +618,7 @@
 bool ApplicationUwp::IsHdcpOn() {
   ::starboard::ScopedLock lock(hdcp_session_mutex_);
 
-  return GetHdcpSession()->IsEffectiveProtectionAtLeast(HdcpProtection::On);
+  return GetHdcpSession()->IsEffectiveProtectionAtLeast(kHDCPProtectionMode);
 }
 
 bool ApplicationUwp::TurnOnHdcp() {
@@ -623,9 +626,8 @@
   {
     ::starboard::ScopedLock lock(hdcp_session_mutex_);
 
-    protection_result =
-      WaitForResult(GetHdcpSession()->SetDesiredMinProtectionAsync(
-        HdcpProtection::On));
+    protection_result = WaitForResult(
+        GetHdcpSession()->SetDesiredMinProtectionAsync(kHDCPProtectionMode));
   }
 
   if (IsHdcpOn()) {
@@ -660,28 +662,6 @@
   return success;
 }
 
-void ApplicationUwp::AcceptFrame(SbPlayer player,
-                                 const scoped_refptr<VideoFrame>& frame,
-                                 int x,
-                                 int y,
-                                 int width,
-                                 int height) {
-  SB_UNREFERENCED_PARAMETER(player);
-  SB_UNREFERENCED_PARAMETER(frame);
-  SB_UNREFERENCED_PARAMETER(x);
-  SB_UNREFERENCED_PARAMETER(y);
-  SB_UNREFERENCED_PARAMETER(width);
-  SB_UNREFERENCED_PARAMETER(height);
-
-  if (frame->IsEndOfStream()) {
-    // TODO: Implement.
-  } else {
-    ID3D11Texture2D* dx_texture =
-        static_cast<ID3D11Texture2D*>(frame->native_texture());
-    SB_UNREFERENCED_PARAMETER(dx_texture);
-  }
-}
-
 }  // namespace uwp
 }  // namespace shared
 }  // namespace starboard
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h
index e62305c..3ded30a 100644
--- a/src/starboard/shared/uwp/application_uwp.h
+++ b/src/starboard/shared/uwp/application_uwp.h
@@ -117,13 +117,6 @@
   TimedEvent* GetNextDueTimedEvent() SB_OVERRIDE;
   SbTimeMonotonic GetNextTimedEventTargetTime() SB_OVERRIDE;
 
-  void AcceptFrame(SbPlayer player,
-                   const scoped_refptr<VideoFrame>& frame,
-                   int x,
-                   int y,
-                   int width,
-                   int height) SB_OVERRIDE;
-
   // These two functions should only be called while holding
   // |hdcp_session_mutex_|.
   Windows::Media::Protection::HdcpSession^ GetHdcpSession();
diff --git a/src/starboard/shared/uwp/system_get_property.cc b/src/starboard/shared/uwp/system_get_property.cc
index 1f4924a..8249fb5 100644
--- a/src/starboard/shared/uwp/system_get_property.cc
+++ b/src/starboard/shared/uwp/system_get_property.cc
@@ -120,7 +120,7 @@
         friendly_name = "XboxOne";

       } else if (sku == "XBOX_ONE_ED") {

         friendly_name = "XboxOne S";

-      } else if (sku == "XBOX_ONE_CH") {

+      } else if (sku == "XBOX_ONE_CH" || sku == "XBOX_ONE_SC") {

         friendly_name = "XboxOne X";

       } else {

         friendly_name = "XboxOne " + sku;

diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index c4a5cb6..387cb37 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -696,7 +696,7 @@
       wm_delete_atom_(None),
       composite_event_id_(kSbEventIdInvalid),
       frame_read_index_(0),
-      frame_written_(false),
+      frames_updated_(false),
       display_(NULL),
       paste_buffer_key_release_pending_(false) {
   SbAudioSinkPrivate::Initialize();
@@ -754,30 +754,32 @@
   if (!windows_.empty()) {
     SbWindow window = windows_[0];
     if (SbWindowIsValid(window)) {
-      int index = -1;
+      std::map<int, FrameInfo> frame_infos;
       {
         ScopedLock lock(frame_mutex_);
-        if (frame_written_) {
-          // Clear the old frame, now that we are done with it.
-          frame_infos_[frame_read_index_].frame = NULL;
-
+        if (frames_updated_) {
           // Increment the index to the next frame, which has been written.
           frame_read_index_ = (frame_read_index_ + 1) % kNumFrames;
+          frame_infos.swap(frame_infos_[frame_read_index_]);
 
           // Clear the frame written flag, so we will not advance frames until
           // the next frame is written.
-          frame_written_ = false;
+          frames_updated_ = false;
         }
-        index = frame_read_index_;
       }
-      FrameInfo& frame_info = frame_infos_[frame_read_index_];
+      window->BeginComposite();
+      for (auto& iter : frame_infos) {
+        FrameInfo& frame_info = iter.second;
 
-      if (frame_info.frame && !frame_info.frame->IsEndOfStream() &&
-          frame_info.frame->format() != VideoFrame::kBGRA32) {
-        frame_info.frame = frame_info.frame->ConvertTo(VideoFrame::kBGRA32);
+        if (frame_info.frame && !frame_info.frame->IsEndOfStream() &&
+            frame_info.frame->format() != VideoFrame::kBGRA32) {
+          frame_info.frame = frame_info.frame->ConvertTo(VideoFrame::kBGRA32);
+        }
+        window->CompositeVideoFrame(frame_info.x, frame_info.y,
+                                    frame_info.width, frame_info.height,
+                                    frame_info.frame);
       }
-      window->Composite(frame_info.x, frame_info.y, frame_info.width,
-                        frame_info.height, frame_info.frame);
+      window->EndComposite();
     }
   }
   composite_event_id_ =
@@ -786,34 +788,34 @@
 
 void ApplicationX11::AcceptFrame(SbPlayer player,
                                  const scoped_refptr<VideoFrame>& frame,
+                                 int z_index,
                                  int x,
                                  int y,
                                  int width,
                                  int height) {
-  int write_index = -1;
-  {
-    ScopedLock lock(frame_mutex_);
-    // Always write ahead 1 frame of the current read frame.
-    write_index = (frame_read_index_ + 1) % kNumFrames;
+  ScopedLock lock(frame_mutex_);
+  // Always write ahead 1 frame of the current read frame.
+  int write_index = (frame_read_index_ + 1) % kNumFrames;
 
-    // Since we are about to modify the next frame, we need to ensure that the
-    // reader will not try to advance frames concurrently, so we clear the flag
-    // stating the frame has been written.
-    frame_written_ = false;
+  for (auto iter = frame_infos_[write_index].begin();
+       iter != frame_infos_[write_index].end(); ++iter) {
+    if (iter->second.player == player) {
+      frame_infos_[write_index].erase(iter);
+      break;
+    }
   }
 
   // Copy the frame.
-  frame_infos_[write_index].frame = frame;
-  frame_infos_[write_index].x = x;
-  frame_infos_[write_index].y = y;
-  frame_infos_[write_index].width = width;
-  frame_infos_[write_index].height = height;
+  FrameInfo& frame_info = frame_infos_[write_index][z_index];
+  frame_info.player = player;
+  frame_info.frame = frame;
+  frame_info.z_index = z_index;
+  frame_info.x = x;
+  frame_info.y = y;
+  frame_info.width = width;
+  frame_info.height = height;
 
-  {
-    ScopedLock lock(frame_mutex_);
-    // The next frame is now ready to be read.
-    frame_written_ = true;
-  }
+  frames_updated_ = true;
 }
 
 void ApplicationX11::Initialize() {
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index cadfc90..48d32d7 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -17,6 +17,7 @@
 
 #include <X11/Xlib.h>
 
+#include <map>
 #include <queue>
 #include <vector>
 
@@ -52,6 +53,7 @@
  protected:
   void AcceptFrame(SbPlayer player,
                    const scoped_refptr<VideoFrame>& frame,
+                   int z_index,
                    int x,
                    int y,
                    int width,
@@ -76,7 +78,9 @@
   typedef std::vector<SbWindow> SbWindowVector;
 
   struct FrameInfo {
+    SbPlayer player;
     scoped_refptr<VideoFrame> frame;
+    int z_index;
     int x;
     int y;
     int width;
@@ -107,9 +111,11 @@
   SbEventId composite_event_id_;
   Mutex frame_mutex_;
   int frame_read_index_;
-  bool frame_written_;
+  bool frames_updated_;
+
   static const int kNumFrames = 2;
-  FrameInfo frame_infos_[kNumFrames];
+  // Video frames from different videos sorted by their z indices.
+  std::map<int, FrameInfo> frame_infos_[kNumFrames];
 
   Display* display_;
   SbWindowVector windows_;
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc
index a8d42e4..9f4436d 100644
--- a/src/starboard/shared/x11/window_internal.cc
+++ b/src/starboard/shared/x11/window_internal.cc
@@ -140,12 +140,7 @@
   XDestroyWindow(display, window);
 }
 
-void SbWindowPrivate::Composite(
-    int bounds_x,
-    int bounds_y,
-    int bounds_width,
-    int bounds_height,
-    const starboard::scoped_refptr<VideoFrame>& frame) {
+void SbWindowPrivate::BeginComposite() {
   XSynchronize(display, True);
   XWindowAttributes window_attributes;
   XGetWindowAttributes(display, window, &window_attributes);
@@ -174,7 +169,14 @@
   XRenderColor black = {0x0000, 0x0000, 0x0000, 0xFFFF};
   XRenderFillRectangle(display, PictOpSrc, composition_picture, &black, 0, 0,
                        width, height);
+}
 
+void SbWindowPrivate::CompositeVideoFrame(
+    int bounds_x,
+    int bounds_y,
+    int bounds_width,
+    int bounds_height,
+    const starboard::scoped_refptr<VideoFrame>& frame) {
   if (frame != NULL && frame->format() == VideoFrame::kBGRA32 &&
       frame->GetPlaneCount() > 0 && frame->width() > 0 && frame->height() > 0) {
     if (frame->width() != video_pixmap_width ||
@@ -248,7 +250,9 @@
                      composition_picture, 0, 0, 0, 0, dest_x, dest_y,
                      video_width, video_height);
   }
+}
 
+void SbWindowPrivate::EndComposite() {
   // Composite (with blending) the GL output on top of the composition pixmap
   // that already has the current video frame if video is playing.
   XRenderComposite(display, PictOpOver, gl_picture, None, composition_picture,
diff --git a/src/starboard/shared/x11/window_internal.h b/src/starboard/shared/x11/window_internal.h
index 6740a9e..7c97758 100644
--- a/src/starboard/shared/x11/window_internal.h
+++ b/src/starboard/shared/x11/window_internal.h
@@ -31,15 +31,19 @@
 
   typedef ::starboard::shared::starboard::player::VideoFrame VideoFrame;
 
-  // Composites graphics and the given video frame video for this window. In
-  // kSbPlayerOutputModePunchOut mode, this is the only way any graphics or
-  // video is presented in the window.  The video frame will be rendered
-  // according to boundaries specified by the parameters.
-  void Composite(int bounds_x,
-                 int bounds_y,
-                 int bounds_width,
-                 int bounds_height,
-                 const starboard::scoped_refptr<VideoFrame>& frame);
+  // The following functions composite graphics and the given video frame video
+  // for this window. In kSbPlayerOutputModePunchOut mode, this is the only way
+  // any graphics or video is presented in the window.  The video frame will be
+  // rendered according to boundaries specified by the parameters.
+  // CompositeVideoFrame() can be called multiple times in between
+  // BeginComposite() and EndComposite() to display frames from multiple videos.
+  void BeginComposite();
+  void CompositeVideoFrame(int bounds_x,
+                           int bounds_y,
+                           int bounds_width,
+                           int bounds_height,
+                           const starboard::scoped_refptr<VideoFrame>& frame);
+  void EndComposite();
 
   // The cached XRender Picture that represents the window that is the
   // destination of the composition.
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index 1336a8f..e7129c6 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -102,6 +102,10 @@
 // following quirk.
 #undef SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES
 
+// Some platforms do not have thread affinity support. Platforms where this is
+// the case should define the following quirk.
+#undef SB_HAS_QUIRK_THREAD_AFFINITY_UNSUPPORTED
+
 // --- System Header Configuration -------------------------------------------
 
 // Any system headers listed here that are not provided by the platform will be
@@ -160,6 +164,15 @@
 #define SB_IS_WCHAR_T_SIGNED 1
 #endif
 
+// Some platforms have memset predefined in system headers. Platforms where this
+// is the case should define the following quirk.
+#undef SB_HAS_QUIRK_MEMSET_IN_SYSTEM_HEADERS
+
+// This quirk is used to switch the headers included in
+// starboard/shared/linux/socket_get_interface_address.cc for darwin system
+// headers. It may be removed at some point in favor of a different solution.
+#undef SB_HAS_QUIRK_SOCKET_BSD_HEADERS
+
 // --- Compiler Configuration ------------------------------------------------
 
 // The platform's annotation for forcing a C function to be inlined.
diff --git a/src/starboard/tools/abstract_launcher.py b/src/starboard/tools/abstract_launcher.py
index 47e5a69..3f2454b 100644
--- a/src/starboard/tools/abstract_launcher.py
+++ b/src/starboard/tools/abstract_launcher.py
@@ -32,47 +32,107 @@
 import starboard.tools.platform as platform_module
 
 
-def _GetLauncherForPlatform(platform_path):
+def GetGypModuleForPlatform(platform):
+  """Gets the module containing a platform's GYP configuration.
+
+  Args:
+    platform:  Platform on which the app will be run, ex. "linux-x64x11".
+
+  Returns:
+    The module containing the platform's GYP configuration.
+
+  Raises:
+    RuntimeError:  The specified platform does not exist.
+  """
+  platform_dict = platform_module.GetAllPorts()
+  if platform in platform_dict:
+    platform_path = platform_dict[platform]
+    if platform_path not in sys.path:
+      sys.path.append(platform_path)
+    gyp_module = importlib.import_module("gyp_configuration")
+    return gyp_module
+  else:
+    raise RuntimeError("Specified platform does not exist.")
+
+
+def _GetLauncherForPlatform(platform):
   """Gets the module containing a platform's concrete launcher implementation.
 
   Args:
-    platform_path:  Path to the location of a valid starboard platform.
+    platform: Platform on which the app will be run, ex. "linux-x64x11".
 
   Returns:
     The module containing the platform's launcher implementation.
   """
-  if platform_path not in sys.path:
-    sys.path.append(platform_path)
-  gyp_module = importlib.import_module("gyp_configuration")
+
+  gyp_module = GetGypModuleForPlatform(platform)
   return gyp_module.CreatePlatformConfig().GetLauncher()
 
 
-def LauncherFactory(platform, target_name, config, device_id, args):
+def DynamicallyBuildOutDirectory(platform, config):
+  """Constructs the location used to store executable targets/their components.
+
+  Args:
+    platform: The platform to run the executable on, ex. "linux-x64x11".
+    config: The build configuration, ex. "qa".
+
+  Returns:
+    The path to the directory containing executables and/or their components.
+  """
+  path = os.path.abspath(
+      os.path.join(os.path.dirname(__file__),
+                   os.pardir, os.pardir, "out",
+                   "{}_{}".format(platform, config)))
+  return path
+
+
+def GetDefaultTargetPath(platform, config, target_name):
+  """Constructs the default path to an executable target.
+
+  The default path takes the form of:
+
+    "/path/to/out/<platform>_<config>/target_name"
+
+  Args:
+    platform: The platform to run the executable on, ex. "linux-x64x11".
+    config: The build configuration, ex. "qa".
+    target_name:  The name of the executable target, ex. "cobalt"
+
+  Returns:
+    The path to an executable target.
+  """
+  return os.path.join(DynamicallyBuildOutDirectory(platform, config),
+                      target_name)
+
+
+def LauncherFactory(platform, target_name, config, device_id, args,
+                    output_file=sys.stdout, out_directory=None):
   """Creates the proper launcher based upon command line args.
 
   Args:
-    platform:  The platform on which the app will run
-    target_name:  The name of the executable target (ex. "cobalt")
-    config:  Type of configuration used by the launcher (ex. "qa", "devel")
-    device_id:  The identifier for the devkit being used
-    args:  Any extra arguments to be passed on a platform-specific basis
+    platform:  The platform on which the app will run.
+    target_name:  The name of the executable target (ex. "cobalt").
+    config:  Type of configuration used by the launcher (ex. "qa", "devel").
+    device_id:  The identifier for the devkit being used.
+    args:  Any extra arguments to be passed on a platform-specific basis.
+    output_file:  The open file to which the launcher should write its output.
+    out_directory:  Path to directory where tool/test targets and/or their
+      components are stored.
 
   Returns:
-    An instance of the concrete launcher class for the desired platform
+    An instance of the concrete launcher class for the desired platform.
 
   Raises:
     RuntimeError: The platform does not exist, or there is no project root.
   """
 
+  if not out_directory:
+    out_directory = DynamicallyBuildOutDirectory(platform, config)
+
   #  Creates launcher for provided platform if the platform has a valid port
-  platform_dict = platform_module.GetAllPorts()
-  if platform in platform_dict:
-    platform_path = platform_dict[platform]
-    launcher_module = _GetLauncherForPlatform(platform_path)
-    return launcher_module.Launcher(platform, target_name, config, device_id,
-                                    args)
-  else:
-    raise RuntimeError("Specified platform does not exist.")
+  launcher_module = _GetLauncherForPlatform(platform)
+  return launcher_module.Launcher(platform, target_name, config, device_id,
+                                  args, output_file, out_directory)
 
 
 class AbstractLauncher(object):
@@ -80,16 +140,23 @@
 
   __metaclass__ = abc.ABCMeta
 
-  def __init__(self, platform, target_name, config, device_id, args):
+  def __init__(self, platform, target_name, config, device_id,
+               args, output_file, out_directory):
     self.platform = platform
     self.target_name = target_name
     self.config = config
     self.device_id = device_id
     self.args = args
+    self.out_directory = out_directory
+    self.output_file = output_file
     self.target_command_line_params = []
     if "target_params" in args:
       self.target_command_line_params.extend(args["target_params"])
 
+    # Launchers that need different startup timeout times should reassign
+    # this variable during initialization.
+    self.startup_timeout_seconds = 2 * 60
+
   @abc.abstractmethod
   def Run(self):
     """Runs the launcher's executable.  Must be implemented in subclasses.
@@ -104,6 +171,10 @@
     """Kills the launcher. Must be implemented in subclasses."""
     pass
 
+  def GetStartupTimeout(self):
+    """Gets the number of seconds to wait before assuming a launcher timeout."""
+    return self.startup_timeout_seconds
+
   def GetHostAndPortGivenPort(self, port):
     """Creates a host/port tuple for use on the target device.
 
diff --git a/src/starboard/tools/command_line.py b/src/starboard/tools/command_line.py
new file mode 100644
index 0000000..0c86290
--- /dev/null
+++ b/src/starboard/tools/command_line.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+#
+# 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.
+"""Common command line parser for all Starboard tools.
+
+Arguments can be added to the parser returned from CreateParser() on a
+per-tool basis.
+"""
+
+import argparse
+
+
+def CreateParser():
+  """Returns an argparse.ArgumentParser object set up for Starboard tools."""
+  arg_parser = argparse.ArgumentParser(
+      description="Runs application/tool executables.")
+  arg_parser.add_argument(
+      "-p",
+      "--platform",
+      help="Device platform, eg 'linux-x64x11'.")
+  arg_parser.add_argument(
+      "-c",
+      "--config",
+      choices=["debug", "devel", "qa", "gold"],
+      help="Build config (eg, 'qa' or 'devel')")
+  arg_parser.add_argument(
+      "-d",
+      "--device_id",
+      help="Devkit or IP address for the target device.")
+  arg_parser.add_argument(
+      "-t",
+      "--target_name",
+      help="Name of executable target.")
+  arg_parser.add_argument(
+      "--target_params",
+      help="Command line arguments to pass to the executable."
+           " Because different executables could have differing command"
+           " line syntax, list all arguments exactly as you would to the"
+           " executable between a set of double quotation marks.")
+  arg_parser.add_argument(
+      "-o",
+      "--out_directory",
+      help="Directory containing tool binaries or their components."
+           " Automatically derived if absent.")
+  return arg_parser
diff --git a/src/starboard/tools/example/app_launcher_client.py b/src/starboard/tools/example/app_launcher_client.py
index b240697..8df7546 100644
--- a/src/starboard/tools/example/app_launcher_client.py
+++ b/src/starboard/tools/example/app_launcher_client.py
@@ -28,39 +28,13 @@
     sys.path.append(env_path)
   environment = importlib.import_module("environment")
 
-import argparse
-
 from starboard.tools import abstract_launcher
-
-arg_parser = argparse.ArgumentParser(
-    description="Runs application/tool executables.")
-arg_parser.add_argument(
-    "-p",
-    "--platform",
-    help="Device platform, eg 'linux-x64x11'.")
-arg_parser.add_argument(
-    "-t",
-    "--target_name",
-    help="Name of executable.")
-arg_parser.add_argument(
-    "-c",
-    "--config",
-    choices=["debug", "devel", "qa", "gold"],
-    help="Build config (eg, 'qa' or 'devel')")
-arg_parser.add_argument(
-    "-d",
-    "--device_id",
-    help="Devkit or IP address for the target device.")
-arg_parser.add_argument(
-    "--target_params",
-    help="Command line arguments to pass to the executable."
-         " Because different executables could have differing command"
-         " line syntax, list all arguments exactly as you would to the"
-         " executable between a set of double quotation marks.")
+from starboard.tools import command_line
 
 
 def main():
-  args = arg_parser.parse_args()
+  parser = command_line.CreateParser()
+  args = parser.parse_args()
   extra_args = {}
 
   if not args.device_id:
@@ -70,9 +44,9 @@
   if args.target_params:
     extra_args["target_params"] = args.target_params.split(" ")
 
-  launcher = abstract_launcher.LauncherFactory(args.platform,
-                                               args.target_name, args.config,
-                                               args.device_id, extra_args)
+  launcher = abstract_launcher.LauncherFactory(
+      args.platform, args.target_name, args.config,
+      args.device_id, extra_args, out_directory=args.out_directory)
   return launcher.Run()
 
 if __name__ == "__main__":
diff --git a/src/starboard/tools/find_private_files.py b/src/starboard/tools/find_private_files.py
index bbb36f4..f9dd9ee 100644
--- a/src/starboard/tools/find_private_files.py
+++ b/src/starboard/tools/find_private_files.py
@@ -1,61 +1,66 @@
-#!/usr/bin/env python

-# 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.

-'''Gyp helper to find private files if present.

-

-The first argument is the result of "<(DEPTH)" from gyp, the second argument

-is the path/pattern from <(DEPTH)/starboard/private/. Patterns should be given

-using Unix path style '/'.

-

-python find_private_files.py ".." "*.h"

-Would return files matching ../starboard/private/*.h if any exist.

-

-python find_private_files.py ".." "nplb/*_test.cc"

-Would return files matching ../starboard/private/nplb/*_test.cc if any exist.

-

-NOTE: gyp errors often produce no warnings. Be sure to structure usages of this

-script by gyp files like the line below, where the'<!@' is a gyp command

-expansion that will process the results into a list of returned file paths.

-Quoting arguments protects against wildcard expansion and other undesirable

-gyp/shell behavior.

-'<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "*.h")',

-'''

-

-import glob

-import os

-import sys

-

-_PRIVATE_DIR_PATH = 'starboard/private'

-

-def find_private_files(depth, target_pattern):

-  '''Assembles search glob and finds files matching the target pattern.

-

-  Args:

-    depth: The string result of "<(DEPTH)"" from gyp.

-    target_pattern: The string path/pattern from <(DEPTH)/starboard/private/.

-  '''

-  path = os.path.normpath(os.path.join(

-      depth, _PRIVATE_DIR_PATH, target_pattern))

-  for f in glob.iglob(path):

-    # Switch to Unix style '/' for gyp.

-    print f.replace('\\', '/')

-

-

-if __name__ == '__main__':

-  depth_arg = sys.argv[1]

-  target_pattern_arg = sys.argv[2]

-

-  find_private_files(depth_arg, target_pattern_arg)

-

-  sys.exit(0)

+#!/usr/bin/env python
+# 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.
+"""Gyp helper to find private files if present.
+
+The first argument is the result of "<(DEPTH)" from gyp, the second argument
+is the path/pattern from <(DEPTH)/starboard/private/. Patterns should be given
+using Unix path style '/'.
+
+python find_private_files.py "../.." "*.h"
+Would return files matching ../../starboard/private/*.h if any exist.
+
+python find_private_files.py "../.." "nplb/*_test.cc"
+Would return files matching ../../starboard/private/nplb/*_test.cc if any exist.
+
+NOTE: gyp errors often produce no warnings. Be sure to structure usages of this
+script by gyp files like the line below, where the'<!@' is a gyp command
+expansion that will process the results into a list of returned file paths.
+Quoting arguments protects against wildcard expansion and other undesirable
+gyp/shell behavior.
+'<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "*.h")',
+"""
+
+import glob
+import os
+import sys
+
+
+def find_private_files(depth,
+                       target_pattern,
+                       private_dir_path='starboard/private'):
+  """Assembles search glob and finds files matching the target pattern.
+
+  Args:
+    depth: The string result of "<(DEPTH)"" from gyp.
+    target_pattern: The string path/pattern from <(DEPTH)/|private_dir_path|
+    private_dir_path: Optional The path to the private directory, which
+      defaults to 'starboard/private'.
+  """
+  path = os.path.normpath(os.path.join(depth, private_dir_path, target_pattern))
+  for f in glob.iglob(path):
+    # Switch to Unix style '/' for gyp.
+    print f.replace('\\', '/')
+
+
+if __name__ == '__main__':
+  depth_arg = sys.argv[1]
+  target_pattern_arg = sys.argv[2]
+  if len(sys.argv) > 3:
+    private_dir_path_arg = sys.argv[3]
+    find_private_files(depth_arg, target_pattern_arg, private_dir_path_arg)
+  else:
+    find_private_files(depth_arg, target_pattern_arg)
+
+  sys.exit(0)
diff --git a/src/starboard/tools/toolchain/abstract.py b/src/starboard/tools/toolchain/abstract.py
index c1d940b..3f9f3d3 100644
--- a/src/starboard/tools/toolchain/abstract.py
+++ b/src/starboard/tools/toolchain/abstract.py
@@ -213,6 +213,35 @@
     """
     pass
 
+class ObjectiveCxxCompiler(Tool):
+  """Compiles Objective-C++ sources."""
+
+  def IsPlatformAgnostic(self):
+    return False
+
+  def GetRuleName(self):
+    return 'compile_objective_cxx'
+
+  @abc.abstractmethod
+  def GetFlags(self, defines, include_dirs, cflags):
+    """Returns tool flags specific to a target.
+
+    This method translates platform-agnostic concepts into a command line
+    arguments understood by a tool.
+
+    Args:
+      defines: A list of preprocessor defines in "NAME=VALUE" format.
+      include_dirs: A list of header search directories.
+      cflags: A list of GCC-style command-line flags. See
+        https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC for
+        details.
+
+    Returns:
+      A list of unquoted strings, one for each flag. It is a responsibility of a
+      caller to quote flags that contain special characters (as determined by a
+      shell) before passing to a tool.
+    """
+    pass
 
 class AssemblerWithCPreprocessor(Tool):
   """Compiles assembler sources that contain C preprocessor directives."""
diff --git a/src/starboard/tools/toolchain/clang.py b/src/starboard/tools/toolchain/clang.py
index 68bedf1..1cf467f 100644
--- a/src/starboard/tools/toolchain/clang.py
+++ b/src/starboard/tools/toolchain/clang.py
@@ -90,6 +90,26 @@
     ]
     return define_flags + include_dir_flags + cflags
 
+class ObjectiveCxxCompiler(CompilerBase, abstract.ObjectiveCxxCompiler):
+  """Compiles Objective-C++ sources using Clang."""
+
+  def __init__(self, **kwargs):
+    super(ObjectiveCxxCompiler, self).__init__(**kwargs)
+
+  def GetCommand(self, path, extra_flags, flags):
+    return '{0} -x objective-c++ -MMD -MF $out.d {1} {2} -c $in -o $out'.format(
+        path, extra_flags, flags)
+
+  def GetDescription(self):
+    return 'OBJCXX $out'
+
+  def GetFlags(self, defines, include_dirs, cflags):
+    define_flags = ['-D{0}'.format(define) for define in defines]
+    include_dir_flags = [
+        '-I{0}'.format(include_dir) for include_dir in include_dirs
+    ]
+    return define_flags + include_dir_flags + cflags
+
 
 class AssemblerWithCPreprocessor(CompilerBase,
                                  abstract.AssemblerWithCPreprocessor):
diff --git a/src/starboard/win/lib/gyp_configuration.gypi b/src/starboard/win/lib/gyp_configuration.gypi
deleted file mode 100644
index 9a3622e..0000000
--- a/src/starboard/win/lib/gyp_configuration.gypi
+++ /dev/null
@@ -1,52 +0,0 @@
-# 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': {
-    'javascript_engine': 'mozjs-45',
-    'cobalt_enable_jit': 0,
-    # TODO: In theory, there are tools that can combine static libraries into
-    # thick static libraries with all their transitive dependencies. Using
-    # shared_library here can have unexpected consequences. Explore building
-    # this into a thick static library instead.
-    'final_executable_type': 'shared_library',
-    'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
-    'sb_enable_lib': 1,
-    'enable_map_to_mesh': 1,
-    'angle_build_winrt': 0,
-    'winrt': 0,
-    'enable_d3d11_feature_level_11': 1,
-  },
-  'includes': [
-    '../shared/gyp_configuration.gypi',
-  ],
-  'target_defaults': {
-    'default_configuration': 'win-lib_debug',
-    'configurations': {
-
-      'win-lib_debug': {
-        'inherit_from': ['win32_base', 'msvs_debug'],
-      },
-      'win-lib_devel': {
-       'inherit_from': ['win32_base', 'msvs_devel'],
-      },
-      'win-lib_qa': {
-        'inherit_from': ['win32_base', 'msvs_qa'],
-      },
-      'win-lib_gold': {
-        'inherit_from': ['win32_base', 'msvs_gold'],
-      },
-    },  # end of configurations
-  },
-}
diff --git a/src/starboard/win/lib/starboard_platform.gyp b/src/starboard/win/lib/starboard_platform.gyp
deleted file mode 100644
index 986d8b6..0000000
--- a/src/starboard/win/lib/starboard_platform.gyp
+++ /dev/null
@@ -1,41 +0,0 @@
-# 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',
-  ],
-  'variables': {
-    'starboard_platform_dependent_files': [
-      'atomic_public.h',
-      'configuration_public.h',
-      'thread_types_public.h',
-      '../shared/system_get_path.cc',
-      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-      '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
-      '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-      '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
-      '<@(uwp_incompatible_win32)',
-      '<@(win32_media_player_files)',
-      '<@(win32_shared_drm_files)',
-      '<@(win32_shared_media_player_files)',
-    ],
-  },
-}
diff --git a/src/starboard/win/win32/lib/gyp_configuration.gypi b/src/starboard/win/win32/lib/gyp_configuration.gypi
index 7d8ddb2..426c515 100644
--- a/src/starboard/win/win32/lib/gyp_configuration.gypi
+++ b/src/starboard/win/win32/lib/gyp_configuration.gypi
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'javascript_engine': 'mozjs',
+    'javascript_engine': 'mozjs-45',
     'cobalt_enable_jit': 0,
     # TODO: In theory, there are tools that can combine static libraries into
     # thick static libraries with all their transitive dependencies. Using
@@ -23,6 +23,7 @@
     'final_executable_type': 'shared_library',
     'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
     'sb_enable_lib': 1,
+    'enable_map_to_mesh': 1,
     'angle_build_winrt': 0,
     'winrt': 0,
     'enable_d3d11_feature_level_11': 1,
diff --git a/src/third_party/libevent/kqueue.c b/src/third_party/libevent/kqueue.c
index bcad213..9f9e671 100644
--- a/src/third_party/libevent/kqueue.c
+++ b/src/third_party/libevent/kqueue.c
@@ -92,7 +92,9 @@
 	struct kevent *changes;
 	int nchanges;
 	struct kevent *events;
+#ifndef STARBOARD
 	struct event_list evsigevents[NSIG];
+#endif
 	int nevents;
 	int kq;
 	pid_t pid;
@@ -129,7 +131,7 @@
 		return (NULL);
 
 	/* Initalize the kernel queue */
-	
+
 	if ((kq = kqueue()) == -1) {
 		event_warn("kqueue");
 		free (kqueueop);
@@ -154,16 +156,18 @@
 	}
 	kqueueop->nevents = NEVENT;
 
+#ifndef STARBOARD
 	/* we need to keep track of multiple events per signal */
 	for (i = 0; i < NSIG; ++i) {
 		TAILQ_INIT(&kqueueop->evsigevents[i]);
 	}
+#endif
 
 	/* Check for Mac OS X kqueue bug. */
 	kqueueop->changes[0].ident = -1;
 	kqueueop->changes[0].filter = EVFILT_READ;
 	kqueueop->changes[0].flags = EV_ADD;
-	/* 
+	/*
 	 * If kqueue works, then kevent will succeed, and it will
 	 * stick an error in events[0].  If kqueue is broken, then
 	 * kevent will fail.
@@ -222,7 +226,7 @@
 	memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
 
 	event_debug(("%s: fd %d %s%s",
-		__func__, (int)kev->ident, 
+		__func__, (int)kev->ident,
 		kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
 		kev->flags == EV_DELETE ? " (del)" : ""));
 
@@ -267,7 +271,7 @@
 		int which = 0;
 
 		if (events[i].flags & EV_ERROR) {
-			/* 
+			/*
 			 * Error messages that can happen, when a delete fails.
 			 *   EBADF happens when the file discriptor has been
 			 *   closed,
@@ -324,25 +328,26 @@
 	struct kqop *kqop = arg;
 	struct kevent kev;
 
+#ifndef STARBOARD
 	if (ev->ev_events & EV_SIGNAL) {
 		int nsignal = EVENT_SIGNAL(ev);
 
 		assert(nsignal >= 0 && nsignal < NSIG);
 		if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) {
 			struct timespec timeout = { 0, 0 };
-			
+
 			memset(&kev, 0, sizeof(kev));
 			kev.ident = nsignal;
 			kev.filter = EVFILT_SIGNAL;
 			kev.flags = EV_ADD;
 			kev.udata = PTR_TO_UDATA(&kqop->evsigevents[nsignal]);
-			
+
 			/* Be ready for the signal if it is sent any
 			 * time between now and the next call to
 			 * kq_dispatch. */
 			if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
 				return (-1);
-			
+
 			if (_evsignal_set_handler(ev->ev_base, nsignal,
 				kq_sighandler) == -1)
 				return (-1);
@@ -353,6 +358,7 @@
 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
 		return (0);
 	}
+#endif
 
 	if (ev->ev_events & EV_READ) {
  		memset(&kev, 0, sizeof(kev));
@@ -366,7 +372,7 @@
 		if (!(ev->ev_events & EV_PERSIST))
 			kev.flags |= EV_ONESHOT;
 		kev.udata = PTR_TO_UDATA(ev);
-		
+
 		if (kq_insert(kqop, &kev) == -1)
 			return (-1);
 
@@ -381,7 +387,7 @@
 		if (!(ev->ev_events & EV_PERSIST))
 			kev.flags |= EV_ONESHOT;
 		kev.udata = PTR_TO_UDATA(ev);
-		
+
 		if (kq_insert(kqop, &kev) == -1)
 			return (-1);
 
@@ -400,6 +406,7 @@
 	if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
 		return (0);
 
+#ifndef STARBOARD
 	if (ev->ev_events & EV_SIGNAL) {
 		int nsignal = EVENT_SIGNAL(ev);
 		struct timespec timeout = { 0, 0 };
@@ -411,7 +418,7 @@
 			kev.ident = nsignal;
 			kev.filter = EVFILT_SIGNAL;
 			kev.flags = EV_DELETE;
-		
+
 			/* Because we insert signal events
 			 * immediately, we need to delete them
 			 * immediately, too */
@@ -426,13 +433,14 @@
 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
 		return (0);
 	}
+#endif
 
 	if (ev->ev_events & EV_READ) {
  		memset(&kev, 0, sizeof(kev));
 		kev.ident = ev->ev_fd;
 		kev.filter = EVFILT_READ;
 		kev.flags = EV_DELETE;
-		
+
 		if (kq_insert(kqop, &kev) == -1)
 			return (-1);
 
@@ -444,7 +452,7 @@
 		kev.ident = ev->ev_fd;
 		kev.filter = EVFILT_WRITE;
 		kev.flags = EV_DELETE;
-		
+
 		if (kq_insert(kqop, &kev) == -1)
 			return (-1);
 
diff --git a/src/third_party/libevent/libevent.gyp b/src/third_party/libevent/libevent.gyp
index d62a6c9..612fdc2 100644
--- a/src/third_party/libevent/libevent.gyp
+++ b/src/third_party/libevent/libevent.gyp
@@ -87,7 +87,7 @@
                   'include_dirs': [ 'starboard/linux' ],
                   }
                 ],
-                [ 'target_os == "ios"', {
+                [ 'target_os in ("tvos", "ios", "mac")', {
                   'include_dirs': [ 'starboard/darwin' ],
                   }
                 ],
diff --git a/src/third_party/libwebp/libwebp.gyp b/src/third_party/libwebp/libwebp.gyp
index 9b659e9..f2a17f3 100644
--- a/src/third_party/libwebp/libwebp.gyp
+++ b/src/third_party/libwebp/libwebp.gyp
@@ -73,8 +73,9 @@
         },{  # "target_arch != "arm" or arm_version < 7"
           'type': 'none',
         }],
-        ['target_arch == "arm" and arm_version >= 8 and clang == 1', {
-          # NEON is implicit on ARMv8, and clang doesn't like the redundant flag
+        ['target_arch == "arm" and arm_version >= 8', {
+          # NEON is implicit on ARMv8, and both clang and gcc don't like the
+          # redundant flag.
           'cflags!': [ '-mfpu=neon' ],
         }],
       ],
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h b/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h
index b84117b..1447a97 100644
--- a/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h
@@ -100,7 +100,15 @@
 
 // Use AtomicWord for a machine-sized pointer.  It will use the Atomic32 or
 // Atomic64 routines below, depending on your architecture.
+#if defined(STARBOARD)
+#if SB_HAS(64_BIT_POINTERS)
+typedef SbAtomic64 AtomicWord;
+#else
+typedef SbAtomic32 AtomicWord;
+#endif
+#else
 typedef intptr_t AtomicWord;
+#endif
 
 // Atomically execute:
 //      result = *ptr;
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/port.h b/src/third_party/protobuf/src/google/protobuf/stubs/port.h
index d6189dc..e751fb1 100644
--- a/src/third_party/protobuf/src/google/protobuf/stubs/port.h
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/port.h
@@ -51,6 +51,15 @@
 // This workaround is for protoc auto-generated files which use memset.
 #ifndef memset
 #define memset SbMemorySet
+#if SB_HAS_QUIRK(MEMSET_IN_SYSTEM_HEADERS)
+  namespace std {
+    inline namespace __1 {
+      void *SbMemorySet(void* destination, int byte_value, size_t count) {
+        return ::SbMemorySet(destination, byte_value, count);
+      }
+    }
+  }
+#endif  // SB_HAS_QUIRK(MEMSET_IN_SYSTEM_HEADERS)
 #endif  // memset
 #endif
 
diff --git a/src/third_party/sqlite/amalgamation/sqlite3.c b/src/third_party/sqlite/amalgamation/sqlite3.c
index f89fabc..e7f4e39 100644
--- a/src/third_party/sqlite/amalgamation/sqlite3.c
+++ b/src/third_party/sqlite/amalgamation/sqlite3.c
@@ -1,3 +1,7 @@
+// TODO: Undefine this globally in
+// starboard/darwin/tvos/simulator/gyp_configuration.gypi
+#undef __APPLE__
+
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
 ** version 3.7.6.3.  By combining all the individual C code files into this 
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 1724d6f..9f75af1 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -913,6 +913,7 @@
       cflags = GetConfigFlags(config, self.toolset, 'cflags')
       cflags_c = GetConfigFlags(config, self.toolset, 'cflags_c')
       cflags_cc = GetConfigFlags(config, self.toolset, 'cflags_cc')
+      cflags_mm = GetConfigFlags(config, self.toolset, 'cflags_mm')
 
       c_compiler = FindFirstInstanceOf(abstract.CCompiler, toolchain)
       if c_compiler:
@@ -930,6 +931,16 @@
             '{0}_flags'.format(GetNinjaRuleName(cxx_compiler, self.toolset)),
             JoinShellArguments(shell, cxx_compiler_flags))
 
+      objcxx_compiler = FindFirstInstanceOf(abstract.ObjectiveCxxCompiler,
+                                            toolchain)
+      if objcxx_compiler:
+        objcxx_compiler_flags = objcxx_compiler.GetFlags(defines, include_dirs,
+                                                         cflags + cflags_cc +
+                                                         cflags_mm)
+        self.ninja.variable(
+            '{0}_flags'.format(GetNinjaRuleName(objcxx_compiler, self.toolset)),
+            JoinShellArguments(shell, objcxx_compiler_flags))
+
       assembler = FindFirstInstanceOf(abstract.AssemblerWithCPreprocessor,
                                       toolchain)
       if assembler:
@@ -954,6 +965,11 @@
                                 'to build {0} for {1} platform.').format(
                                     source, self.toolset)
           rule_name = GetNinjaRuleName(cxx_compiler, self.toolset)
+        elif extension in ['.mm']:
+          assert objcxx_compiler, ('Toolchain must provide Objective-C++ '
+                                   'compiler in order to build {0} for {1} '
+                                   'platform.').format(source, self.toolset)
+          rule_name = GetNinjaRuleName(objcxx_compiler, self.toolset)
         elif extension in ['.S', '.s']:
           assert assembler, ('Toolchain must provide assembler in order to '
                              'build {0} for {1} platform.').format(
@@ -1192,7 +1208,8 @@
         self.ninja.variable('{0}_flags'.format(rule_name),
                             JoinShellArguments(shell, executable_linker_flags))
       else:
-        raise Exception('Target type {0} is not supported.'.format(target_type))
+        raise Exception('Target type {0} is not supported for target {1}.'
+            .format(target_type, spec['target_name']))
 
       order_only_deps = set()