Import Cobalt 13.93830
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 9473e36..891c4a6 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -43,6 +43,7 @@
     MacroOpWithNameOnly(animationend)                                \
     MacroOpWithNameOnly(assertive)                                   \
     MacroOpWithNameOnly(attributes)                                  \
+    MacroOpWithNameOnly(beforeunload)                                \
     MacroOpWithNameOnly(blur)                                        \
     MacroOpWithNameOnly(boundary)                                    \
     MacroOpWithNameOnly(canplay)                                     \
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 9f855ca..2ac5506 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -93,16 +93,16 @@
 #endif  // ENABLE_REMOTE_DEBUGGING
 
 #if defined(ENABLE_WEBDRIVER)
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 int GetWebDriverPort() {
   // The default port on which the webdriver server should listen for incoming
   // connections.
 #if defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
   const int kDefaultWebDriverPort = SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT;
 #else
-  const int kDefaultWebDriverPort = 9515;
+  const int kDefaultWebDriverPort = 4444;
 #endif  // defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
   int webdriver_port = kDefaultWebDriverPort;
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kWebDriverPort)) {
     if (!base::StringToInt(
@@ -114,22 +114,24 @@
       webdriver_port = kDefaultWebDriverPort;
     }
   }
+#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
   return webdriver_port;
 }
 
 std::string GetWebDriverListenIp() {
-  // The default port on which the webdriver server should listen for incoming
+  // The default IP on which the webdriver server should listen for incoming
   // connections.
   std::string webdriver_listen_ip =
       webdriver::WebDriverModule::kDefaultListenIp;
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kWebDriverListenIp)) {
     webdriver_listen_ip =
         command_line->GetSwitchValueASCII(switches::kWebDriverListenIp);
   }
+#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
   return webdriver_listen_ip;
 }
-#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 #endif  // ENABLE_WEBDRIVER
 
 GURL GetInitialURL() {
@@ -625,7 +627,12 @@
 
 #if defined(ENABLE_WEBDRIVER)
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-  if (command_line->HasSwitch(switches::kEnableWebDriver)) {
+  bool create_webdriver_module =
+      !command_line->HasSwitch(switches::kDisableWebDriver);
+#else
+  bool create_webdriver_module = true;
+#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+  if (create_webdriver_module) {
     web_driver_module_.reset(new webdriver::WebDriverModule(
         GetWebDriverPort(), GetWebDriverListenIp(),
         base::Bind(&BrowserModule::CreateSessionDriver,
@@ -636,7 +643,6 @@
                    base::Unretained(browser_module_.get())),
         base::Bind(&Application::Quit, base::Unretained(this))));
   }
-#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 #endif  // ENABLE_WEBDRIVER
 
 #if defined(ENABLE_REMOTE_DEBUGGING)
@@ -724,6 +730,9 @@
     case kSbEventTypeUnpause:
     case kSbEventTypeSuspend:
     case kSbEventTypeResume:
+#if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
+    case kSbEventTypeLowMemory:
+#endif  // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
       OnApplicationEvent(starboard_event->type);
       break;
     case kSbEventTypeNetworkConnect:
@@ -739,11 +748,9 @@
       DispatchEventInternal(new base::DeepLinkEvent(link));
       break;
     }
-#if SB_API_VERSION >= 4
     case kSbEventTypeAccessiblitySettingsChanged:
       DispatchEventInternal(new base::AccessibilitySettingsChangedEvent());
       break;
-#endif  // SB_API_VERSION >= 4
     default:
       DLOG(WARNING) << "Unhandled Starboard event of type: "
                     << starboard_event->type;
@@ -815,6 +822,13 @@
       browser_module_->Resume();
       DLOG(INFO) << "Finished resuming.";
       break;
+#if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
+    case kSbEventTypeLowMemory:
+      DLOG(INFO) << "Got low memory event.";
+      browser_module_->ReduceMemory();
+      DLOG(INFO) << "Finished reducing memory usage.";
+      break;
+#endif  // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
     default:
       NOTREACHED() << "Unexpected event type: " << event_type;
       return;
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index cf2ba72..41f4937 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -65,6 +65,7 @@
         '../dom/comment.idl',
         '../dom/console.idl',
         '../dom/crypto.idl',
+        '../dom/custom_event.idl',
         '../dom/data_view.idl',
         '../dom/device_orientation_event.idl',
         '../dom/document.idl',
@@ -79,6 +80,7 @@
         '../dom/dom_string_map.idl',
         '../dom/dom_token_list.idl',
         '../dom/element.idl',
+        '../dom/error_event.idl',
         '../dom/event.idl',
         '../dom/event_listener.idl',
         '../dom/event_target.idl',
@@ -219,9 +221,11 @@
         '../audio/audio_node_channel_count_mode.idl',
         '../audio/audio_node_channel_interpretation.idl',
         '../dom/blob_property_bag.idl',
+        '../dom/custom_event_init.idl',
         '../dom/device_orientation_event_init.idl',
         '../dom/document_ready_state.idl',
         '../dom/dom_parser_supported_type.idl',
+        '../dom/error_event_init.idl',
         '../dom/event_init.idl',
         '../dom/event_modifier_init.idl',
         '../dom/focus_event_init.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index a9eb520..cfc4a45 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -111,8 +111,13 @@
 
 // TODO: Subscribe to viewport size changes.
 
+const int kMainWebModuleZIndex = 1;
+const int kSplashScreenZIndex = 2;
+
 #if defined(ENABLE_DEBUG_CONSOLE)
 
+const int kDebugConsoleZIndex = 3;
+
 const char kFuzzerToggleCommand[] = "fuzzer_toggle";
 const char kFuzzerToggleCommandShortHelp[] = "Toggles the input fuzzer on/off.";
 const char kFuzzerToggleCommandLongHelp[] =
@@ -211,9 +216,7 @@
       storage_manager_(make_scoped_ptr(new StorageUpgradeHandler(url))
                            .PassAs<storage::StorageManager::UpgradeHandler>(),
                        options_.storage_manager_options),
-#if defined(OS_STARBOARD)
       is_rendered_(false),
-#endif  // OS_STARBOARD
 #if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
       array_buffer_allocator_(
           new ResourceProviderArrayBufferAllocator(GetResourceProvider())),
@@ -251,7 +254,8 @@
 #endif
       will_quit_(false),
       application_state_(initial_application_state),
-      splash_screen_cache_(new SplashScreenCache()) {
+      splash_screen_cache_(new SplashScreenCache()),
+      produced_render_tree_(false) {
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
   on_error_triggered_count_ = 0;
@@ -296,9 +300,7 @@
     OnFuzzerToggle(std::string());
   }
   if (command_line->HasSwitch(switches::kSuspendFuzzer)) {
-#if SB_API_VERSION >= 4
     suspend_fuzzer_.emplace();
-#endif
   }
 #endif  // ENABLE_DEBUG_CONSOLE && ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
@@ -403,13 +405,18 @@
   base::optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
   if (fallback_splash_screen_url_ ||
       (key && splash_screen_cache_->IsSplashScreenCached(*key))) {
+    // Create the splash screen layer.
+    splash_screen_layer_ =
+        render_tree_combiner_->CreateLayer(kSplashScreenZIndex);
+
     splash_screen_.reset(new SplashScreen(
         application_state_,
-        base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
+        base::Bind(&BrowserModule::QueueOnSplashScreenRenderTreeProduced,
                    base::Unretained(this)),
         &network_module_, viewport_size, GetResourceProvider(),
         kLayoutMaxRefreshFrequencyInHz, *fallback_splash_screen_url_, url,
-        splash_screen_cache_.get()));
+        splash_screen_cache_.get(),
+        base::Bind(&BrowserModule::DestroySplashScreen, weak_this_)));
     lifecycle_observers_.AddObserver(splash_screen_.get());
   }
 
@@ -473,8 +480,6 @@
     return;
   }
 
-  DestroySplashScreen();
-
   // This log is relied on by the webdriver benchmark tests, so it shouldn't be
   // changed unless the corresponding benchmark logic is changed as well.
   LOG(INFO) << "Loaded WebModule";
@@ -519,26 +524,67 @@
       base::Bind(&BrowserModule::ProcessRenderTreeSubmissionQueue, weak_this_));
 }
 
+void BrowserModule::QueueOnSplashScreenRenderTreeProduced(
+    const browser::WebModule::LayoutResults& layout_results) {
+  TRACE_EVENT0("cobalt::browser",
+               "BrowserModule::QueueOnSplashScreenRenderTreeProduced()");
+  render_tree_submission_queue_.AddMessage(
+      base::Bind(&BrowserModule::OnSplashScreenRenderTreeProduced,
+                 base::Unretained(this), layout_results));
+  self_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&BrowserModule::ProcessRenderTreeSubmissionQueue, weak_this_));
+}
+
 void BrowserModule::OnRenderTreeProduced(
     const browser::WebModule::LayoutResults& layout_results) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnRenderTreeProduced()");
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+
+  if (splash_screen_ && !produced_render_tree_) {
+    splash_screen_->Shutdown();
+  }
+  produced_render_tree_ = true;
+
   if (application_state_ == base::kApplicationStatePreloading ||
-      !render_tree_combiner_) {
+      !render_tree_combiner_ || !main_web_module_layer_) {
+    return;
+  }
+  renderer::Submission renderer_submission(layout_results.render_tree,
+                                           layout_results.layout_time);
+  renderer_submission.on_rasterized_callback = base::Bind(
+      &BrowserModule::OnRendererSubmissionRasterized, base::Unretained(this));
+  main_web_module_layer_->Submit(renderer_submission, true /* receive_time */);
+
+#if defined(ENABLE_SCREENSHOT)
+  screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
+      layout_results.render_tree, layout_results.layout_time));
+#endif
+}
+
+void BrowserModule::OnSplashScreenRenderTreeProduced(
+    const browser::WebModule::LayoutResults& layout_results) {
+  TRACE_EVENT0("cobalt::browser",
+               "BrowserModule::OnSplashScreenRenderTreeProduced()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+
+  if (application_state_ == base::kApplicationStatePreloading ||
+      !render_tree_combiner_ || !splash_screen_layer_) {
     return;
   }
 
   renderer::Submission renderer_submission(layout_results.render_tree,
                                            layout_results.layout_time);
-#if defined(OS_STARBOARD)
   renderer_submission.on_rasterized_callback = base::Bind(
       &BrowserModule::OnRendererSubmissionRasterized, base::Unretained(this));
-#endif  // OS_STARBOARD
-  render_tree_combiner_->UpdateMainRenderTree(renderer_submission);
+  splash_screen_layer_->Submit(renderer_submission, false /* receive_time */);
 
 #if defined(ENABLE_SCREENSHOT)
-  screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
-      layout_results.render_tree, layout_results.layout_time));
+// TODO: write screen shot using render_tree_combinder_ (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
 }
 
@@ -549,11 +595,7 @@
   }
 #endif
 
-#if defined(OS_STARBOARD)
   SbSystemRequestStop(0);
-#else
-  LOG(WARNING) << "window.close() is not supported on this platform.";
-#endif
 }
 
 void BrowserModule::OnWindowMinimize() {
@@ -563,11 +605,7 @@
   }
 #endif
 
-#if defined(OS_STARBOARD) && SB_API_VERSION >= 4
   SbSystemRequestSuspend();
-#else
-  LOG(WARNING) << "window.minimize() is not supported on this platform.";
-#endif
 }
 
 #if defined(ENABLE_DEBUG_CONSOLE)
@@ -632,16 +670,16 @@
                "BrowserModule::OnDebugConsoleRenderTreeProduced()");
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   if (application_state_ == base::kApplicationStatePreloading ||
-      !render_tree_combiner_) {
+      !render_tree_combiner_ || !debug_console_layer_) {
     return;
   }
 
   if (debug_console_->GetMode() == debug::DebugHub::kDebugConsoleOff) {
-    render_tree_combiner_->UpdateDebugConsoleRenderTree(base::nullopt);
+    debug_console_layer_->Submit(base::nullopt);
     return;
   }
 
-  render_tree_combiner_->UpdateDebugConsoleRenderTree(renderer::Submission(
+  debug_console_layer_->Submit(renderer::Submission(
       layout_results.render_tree, layout_results.layout_time));
 }
 
@@ -804,9 +842,15 @@
 
 void BrowserModule::DestroySplashScreen() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::DestroySplashScreen()");
+  if (MessageLoop::current() != self_message_loop_) {
+    self_message_loop_->PostTask(
+        FROM_HERE, base::Bind(&BrowserModule::DestroySplashScreen, weak_this_));
+    return;
+  }
   if (splash_screen_) {
     lifecycle_observers_.RemoveObserver(splash_screen_.get());
   }
+  splash_screen_layer_.reset(NULL);
   splash_screen_.reset(NULL);
 }
 
@@ -912,6 +956,22 @@
   application_state_ = base::kApplicationStatePaused;
 }
 
+void BrowserModule::ReduceMemory() {
+  if (splash_screen_) {
+    splash_screen_->ReduceMemory();
+  }
+
+#if defined(ENABLE_DEBUG_CONSOLE)
+  if (debug_console_) {
+    debug_console_->ReduceMemory();
+  }
+#endif  // defined(ENABLE_DEBUG_CONSOLE)
+
+  if (web_module_) {
+    web_module_->ReduceMemory();
+  }
+}
+
 void BrowserModule::CheckMemory(
     const int64_t& used_cpu_memory,
     const base::optional<int64_t>& used_gpu_memory) {
@@ -923,7 +983,6 @@
                                      used_gpu_memory);
 }
 
-#if defined(OS_STARBOARD)
 void BrowserModule::OnRendererSubmissionRasterized() {
   TRACE_EVENT0("cobalt::browser",
                "BrowserModule::OnRendererSubmissionRasterized()");
@@ -933,7 +992,6 @@
     SbSystemHideSplashScreen();
   }
 }
-#endif  // OS_STARBOARD
 
 #if defined(COBALT_CHECK_RENDER_TIMEOUT)
 void BrowserModule::OnPollForRenderTimeout(const GURL& url) {
@@ -1021,11 +1079,14 @@
 
   render_tree_combiner_.reset(
       new RenderTreeCombiner(renderer_module_.get(), GetViewportSize()));
-
-  // Always render the debug console. It will draw nothing if disabled.
-  // This setting is ignored if ENABLE_DEBUG_CONSOLE is not defined.
-  // TODO: Render tree combiner should probably be refactored.
-  render_tree_combiner_->set_render_debug_console(true);
+  // Create the main web module layer.
+  main_web_module_layer_ =
+      render_tree_combiner_->CreateLayer(kMainWebModuleZIndex);
+// Create the debug console layer.
+#if defined(ENABLE_DEBUG_CONSOLE)
+  debug_console_layer_ =
+      render_tree_combiner_->CreateLayer(kDebugConsoleZIndex);
+#endif
 
 #if defined(ENABLE_SCREENSHOT)
   screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
@@ -1085,8 +1146,14 @@
 
   // Clear out the render tree combiner so that it doesn't hold on to any
   // render tree resources either.
-  if (render_tree_combiner_) {
-    render_tree_combiner_->Reset();
+  if (main_web_module_layer_) {
+    main_web_module_layer_->Reset();
+  }
+  if (splash_screen_layer_) {
+    splash_screen_layer_->Reset();
+  }
+  if (debug_console_layer_) {
+    debug_console_layer_->Reset();
   }
 
 #if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index ba3861d..386eb0e 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -142,6 +142,10 @@
   void Suspend();
   void Resume();
 
+  // Attempt to reduce overall memory consumption. Called in response to a
+  // system indication that memory usage is nearing a critical level.
+  void ReduceMemory();
+
   void CheckMemory(const int64_t& used_cpu_memory,
                    const base::optional<int64_t>& used_gpu_memory);
 
@@ -173,6 +177,13 @@
   void OnRenderTreeProduced(
       const browser::WebModule::LayoutResults& layout_results);
 
+  // Glue function to deal with the production of the splash screen render tree,
+  // and will manage handing it off to the renderer.
+  void QueueOnSplashScreenRenderTreeProduced(
+      const browser::WebModule::LayoutResults& layout_results);
+  void OnSplashScreenRenderTreeProduced(
+      const browser::WebModule::LayoutResults& layout_results);
+
   // Saves/loads the debug console mode to/from local storage so we can
   // persist the user's preference.
   void SaveDebugConsoleMode();
@@ -357,8 +368,11 @@
   // Sets up the network component for requesting internet resources.
   network::NetworkModule network_module_;
 
-  // Manages the two render trees, combines and renders them.
+  // Manages the three render trees, combines and renders them.
   scoped_ptr<RenderTreeCombiner> render_tree_combiner_;
+  scoped_ptr<RenderTreeCombiner::Layer> main_web_module_layer_;
+  scoped_ptr<RenderTreeCombiner::Layer> debug_console_layer_;
+  scoped_ptr<RenderTreeCombiner::Layer> splash_screen_layer_;
 
 #if defined(ENABLE_SCREENSHOT)
   // Helper object to create screen shots of the last layout tree.
@@ -459,6 +473,9 @@
 
   // The splash screen cache.
   scoped_ptr<SplashScreenCache> splash_screen_cache_;
+
+  // Whether or not the main WebModule has produced any render trees yet.
+  bool produced_render_tree_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h
index 9ecbef5..6ed291d 100644
--- a/src/cobalt/browser/debug_console.h
+++ b/src/cobalt/browser/debug_console.h
@@ -79,6 +79,8 @@
     web_module_->Resume(resource_provider);
   }
 
+  void ReduceMemory() { web_module_->ReduceMemory(); }
+
  private:
   void OnError(const GURL& /* url */, const std::string& error) {
     LOG(ERROR) << error;
diff --git a/src/cobalt/browser/debug_console/console_values.js b/src/cobalt/browser/debug_console/console_values.js
index 2ed232e..444ca48 100644
--- a/src/cobalt/browser/debug_console/console_values.js
+++ b/src/cobalt/browser/debug_console/console_values.js
@@ -20,8 +20,9 @@
   this.DEFAULT_KEY = 'default';
   // Reduced space-separated list of CVal prefixes to display at start-up.
   this.DEFAULT_ACTIVE_SET =
-      'Cobalt DevTools Memory.CPU Memory.MainWebModule Memory.JS Memory.Font ' +
-      'Event.Duration.MainWebModule.KeyDown Renderer.Rasterize.Duration';
+      'Cobalt DevTools WebDriver Memory.CPU Memory.MainWebModule Memory.JS ' +
+      'Memory.Font Event.Duration.MainWebModule.KeyDown ' +
+      'Renderer.Rasterize.Duration';
 
   var names = window.debugHub.getConsoleValueNames();
   this.allCVals = names.split(' ');
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
index f2a92bb..65de06f 100644
--- a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
@@ -30,7 +30,6 @@
 #include "cobalt/browser/memory_tracker/tool/params.h"
 #include "cobalt/browser/memory_tracker/tool/tool_thread.h"
 #include "cobalt/browser/memory_tracker/tool/util.h"
-#include "cobalt/script/mozjs/util/stack_trace_helpers.h"
 #include "nb/analytics/memory_tracker.h"
 #include "nb/analytics/memory_tracker_helpers.h"
 #include "nb/concurrent_map.h"
diff --git a/src/cobalt/browser/render_tree_combiner.cc b/src/cobalt/browser/render_tree_combiner.cc
index 171fbc3..78fe4be 100644
--- a/src/cobalt/browser/render_tree_combiner.cc
+++ b/src/cobalt/browser/render_tree_combiner.cc
@@ -14,99 +14,97 @@
 
 #include "cobalt/browser/render_tree_combiner.h"
 
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
+#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 {
 namespace browser {
 
-#if defined(ENABLE_DEBUG_CONSOLE)
+RenderTreeCombiner::Layer::Layer(RenderTreeCombiner* render_tree_combiner)
+    : render_tree_combiner_(render_tree_combiner),
+      render_tree_(base::nullopt),
+      receipt_time_(base::nullopt) {}
+
+RenderTreeCombiner::Layer::~Layer() {
+  DCHECK(render_tree_combiner_);
+  render_tree_combiner_->RemoveLayer(this);
+}
+
+void RenderTreeCombiner::Layer::Submit(
+    const base::optional<renderer::Submission>& render_tree_submission,
+    bool receive_time) {
+  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();
+}
+
 RenderTreeCombiner::RenderTreeCombiner(
     renderer::RendererModule* renderer_module, const math::Size& viewport_size)
-    : render_debug_console_(true),
-      renderer_module_(renderer_module),
-      viewport_size_(viewport_size) {}
+    : renderer_module_(renderer_module), viewport_size_(viewport_size) {}
 
-RenderTreeCombiner::~RenderTreeCombiner() {}
+scoped_ptr<RenderTreeCombiner::Layer> RenderTreeCombiner::CreateLayer(
+    int z_index) {
+  if (layers_.count(z_index) > 0) {
+    return scoped_ptr<RenderTreeCombiner::Layer>(NULL);
+  }
+  RenderTreeCombiner::Layer* layer = new Layer(this);
+  layers_[z_index] = layer;
 
-void RenderTreeCombiner::Reset() {
-  main_render_tree_ = base::nullopt;
-  debug_console_render_tree_ = base::nullopt;
-  main_render_tree_receipt_time_ = base::nullopt;
+  return scoped_ptr<RenderTreeCombiner::Layer>(layers_[z_index]);
 }
 
-void RenderTreeCombiner::UpdateMainRenderTree(
-    const renderer::Submission& render_tree_submission) {
-  main_render_tree_ = render_tree_submission;
-  main_render_tree_receipt_time_ = base::TimeTicks::HighResNow();
-  SubmitToRenderer();
-}
-
-void RenderTreeCombiner::UpdateDebugConsoleRenderTree(
-    const base::optional<renderer::Submission>& render_tree_submission) {
-  debug_console_render_tree_ = render_tree_submission;
-  SubmitToRenderer();
+void RenderTreeCombiner::RemoveLayer(const Layer* layer) {
+  for (auto it = layers_.begin(); it != layers_.end(); /* no increment */) {
+    if (it->second == layer) {
+      it = layers_.erase(it);
+    } else {
+      ++it;
+    }
+  }
 }
 
 void RenderTreeCombiner::SubmitToRenderer() {
-  if (render_debug_console_ && debug_console_render_tree_) {
-    if (main_render_tree_) {
-      render_tree::CompositionNode::Builder builder;
-      builder.AddChild(main_render_tree_->render_tree);
-      builder.AddChild(debug_console_render_tree_->render_tree);
-      scoped_refptr<render_tree::Node> combined_tree =
-          new render_tree::CompositionNode(builder);
+  render_tree::CompositionNode::Builder builder;
 
-      // Setup time to be based off of the main submitted tree only.
-      // TODO: Setup a "layers" interface on the Pipeline so that
-      // trees can be combined and animated there, properly.
-      renderer::Submission combined_submission(*main_render_tree_);
-      combined_submission.render_tree = combined_tree;
-      combined_submission.time_offset =
-          main_render_tree_->time_offset +
-          (base::TimeTicks::HighResNow() - *main_render_tree_receipt_time_);
-
-      renderer_module_->pipeline()->Submit(combined_submission);
-    } else {
-      // If we are rendering the debug console by itself, give it a solid black
-      // background to it.
-      render_tree::CompositionNode::Builder builder;
-      builder.AddChild(new render_tree::RectNode(
-          math::RectF(viewport_size_),
-          scoped_ptr<render_tree::Brush>(new render_tree::SolidColorBrush(
-              render_tree::ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)))));
-      builder.AddChild(debug_console_render_tree_->render_tree);
-
-      renderer::Submission combined_submission(*debug_console_render_tree_);
-      combined_submission.render_tree =
-          new render_tree::CompositionNode(builder);
-      renderer_module_->pipeline()->Submit(combined_submission);
+  // Add children for all layers in order.
+  base::optional<renderer::Submission> first_tree = base::nullopt;
+  base::optional<renderer::Submission> combined_submission = base::nullopt;
+  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_);
+      }
     }
-  } else if (main_render_tree_) {
-    renderer_module_->pipeline()->Submit(*main_render_tree_);
   }
+  if (!first_tree) {
+    return;
+  }
+  if (!combined_submission) {
+    // None of the layers store the time.
+    combined_submission = renderer::Submission(*first_tree);
+  }
+
+  combined_submission->render_tree = new render_tree::CompositionNode(builder);
+  renderer_module_->pipeline()->Submit(*combined_submission);
 }
-#else   // ENABLE_DEBUG_CONSOLE
-RenderTreeCombiner::RenderTreeCombiner(
-    renderer::RendererModule* renderer_module, const math::Size& viewport_size)
-    : renderer_module_(renderer_module) {
-  UNREFERENCED_PARAMETER(viewport_size);
-}
-
-RenderTreeCombiner::~RenderTreeCombiner() {}
-
-void RenderTreeCombiner::Reset() {}
-
-void RenderTreeCombiner::UpdateMainRenderTree(
-    const renderer::Submission& render_tree_submission) {
-  renderer_module_->pipeline()->Submit(render_tree_submission);
-}
-
-void RenderTreeCombiner::UpdateDebugConsoleRenderTree(
-    const base::optional<renderer::Submission>& render_tree_submission) {
-  UNREFERENCED_PARAMETER(render_tree_submission);
-}
-#endif  // ENABLE_DEBUG_CONSOLE
-
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/render_tree_combiner.h b/src/cobalt/browser/render_tree_combiner.h
index 76b1a22..65f3127 100644
--- a/src/cobalt/browser/render_tree_combiner.h
+++ b/src/cobalt/browser/render_tree_combiner.h
@@ -15,49 +15,72 @@
 #ifndef COBALT_BROWSER_RENDER_TREE_COMBINER_H_
 #define COBALT_BROWSER_RENDER_TREE_COMBINER_H_
 
+#include <map>
+
+#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 {
 namespace browser {
 
-// Combines the main and debug console render trees. Caches the individual
-// trees as they are produced. Re-renders when either tree changes.
-// This class is only fully implemented when ENABLE_DEBUG_CONSOLE is defined,
-// otherwise (e.g. in release builds) a stub implementation is used.
+// Combines rendering layers (such as the main, splash screen, and
+// debug console). Caches the individual trees as they are produced.
+// Re-renders when any tree changes.
 class RenderTreeCombiner {
  public:
+  // Layer represents the render layer corresponding to the main web
+  // module, the splash screen, or the debug console and are used to
+  // create and submit a combined tree to the RendererModule's
+  // pipeline. Layers are combined in order of the |z_index| specifed
+  // at the Layers' creation. The RenderTreeCombiner stores pointers
+  // to Layers. The Layers are owned by the caller of
+  // RenderTreeCombiner::CreateLayer.
+  class Layer {
+   public:
+    ~Layer();
+
+    void Reset() {
+      render_tree_ = base::nullopt;
+      receipt_time_ = base::nullopt;
+    }
+
+    // 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);
+
+   private:
+    friend class RenderTreeCombiner;
+
+    explicit Layer(RenderTreeCombiner* render_tree_combiner = NULL);
+
+    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() {}
 
-  void Reset();
-
-  // Update the main web module render tree.
-  void UpdateMainRenderTree(const renderer::Submission& render_tree_submission);
-
-  // Update the debug console render tree.
-  void UpdateDebugConsoleRenderTree(
-      const base::optional<renderer::Submission>& render_tree_submission);
-
-#if defined(ENABLE_DEBUG_CONSOLE)
-  bool render_debug_console() const { return render_debug_console_; }
-  void set_render_debug_console(bool render_debug_console) {
-    render_debug_console_ = render_debug_console;
-  }
-#else   // ENABLE_DEBUG_CONSOLE
-  bool render_debug_console() const { return false; }
-  void set_render_debug_console(bool render_debug_console) {
-    UNREFERENCED_PARAMETER(render_debug_console);
-  }
-#endif  // ENABLE_DEBUG_CONSOLE
+  // 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);
 
  private:
-#if defined(ENABLE_DEBUG_CONSOLE)
-  // Combines the two cached render trees (main/debug) and renders the result.
-  void SubmitToRenderer();
+  // The layers keyed on their z_index.
+  std::map<int, Layer*> layers_;
 
-  bool render_debug_console_;
+  // 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.
@@ -65,22 +88,6 @@
 
   // The size of the output viewport.
   math::Size viewport_size_;
-
-  // Local references to the main and debug console render trees/animation maps
-  // so we can combine them.
-  base::optional<renderer::Submission> main_render_tree_;
-
-  // This is the time that we received the last main render tree submission.
-  // used so that we know what time to forward the submission to the pipeline
-  // with.
-  base::optional<base::TimeTicks> main_render_tree_receipt_time_;
-
-  // The debug console render tree submission.
-  base::optional<renderer::Submission> debug_console_render_tree_;
-#else   // ENABLE_DEBUG_CONSOLE
-  // Use this local reference even in release builds to submit the main tree.
-  renderer::RendererModule* renderer_module_;
-#endif  // ENABLE_DEBUG_CONSOLE
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index 8c78ce2..2999b41 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -17,25 +17,51 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/callback.h"
+#include "base/cancelable_callback.h"
 #include "base/threading/platform_thread.h"
+#include "base/time.h"
 #include "cobalt/browser/splash_screen_cache.h"
 #include "cobalt/loader/cache_fetcher.h"
 
 namespace cobalt {
 namespace browser {
+namespace {
 
-SplashScreen::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 GURL& fallback_splash_screen_url,
-                           const GURL& initial_main_web_module_url,
-                           SplashScreenCache* splash_screen_cache)
+const int kSplashShutdownSeconds = 2;
+
+void PostCallbackToMessageLoop(const base::Closure& callback,
+                               MessageLoop* message_loop) {
+  DCHECK(message_loop);
+  message_loop->PostTask(FROM_HERE, callback);
+}
+
+// TODO: consolidate definitions of BindToLoop / BindToCurrentLoop
+// from here and media in base.
+base::Closure BindToLoop(const base::Closure& callback,
+                         MessageLoop* message_loop) {
+  return base::Bind(&PostCallbackToMessageLoop, callback, message_loop);
+}
+
+void OnError(const GURL& /* url */, const std::string& error) {
+  LOG(ERROR) << error;
+}
+
+}  // namespace
+
+SplashScreen::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 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)
     : render_tree_produced_callback_(render_tree_produced_callback),
-      is_ready_(true, false) {
+      self_message_loop_(MessageLoop::current()),
+      on_splash_screen_shutdown_complete_(on_splash_screen_shutdown_complete) {
   WebModule::Options web_module_options;
   web_module_options.name = "SplashScreenWebModule";
 
@@ -57,11 +83,14 @@
     web_module_options.splash_screen_cache = splash_screen_cache;
   }
 
+  base::Callback<void()> 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_.reset(new WebModule(
-      url_to_pass, initial_application_state,
-      base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
-      base::Bind(&SplashScreen::OnError, base::Unretained(this)),
-      base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
+      url_to_pass, initial_application_state, render_tree_produced_callback_,
+      base::Bind(&OnError), on_window_close,
       base::Closure(),  // window_minimize_callback
       &stub_media_module_, network_module, window_dimensions,
       1.f /*video_pixel_ratio*/, resource_provider, layout_refresh_rate,
@@ -69,23 +98,23 @@
 }
 
 SplashScreen::~SplashScreen() {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   // Destroy the web module first to prevent our callbacks from being called
   // (from another thread) while member objects are being destroyed.
   web_module_.reset();
+  // Cancel any pending run of the splash screen shutdown callback.
+  on_splash_screen_shutdown_complete_.Cancel();
 }
 
-void SplashScreen::WaitUntilReady() {
-  is_ready_.Wait();
-}
-
-void SplashScreen::OnRenderTreeProduced(
-    const browser::WebModule::LayoutResults& layout_results) {
-  is_ready_.Signal();
-  render_tree_produced_callback_.Run(layout_results);
-}
-
-void SplashScreen::OnWindowClosed() {
-  is_ready_.Signal();
+void SplashScreen::Shutdown() {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  DCHECK(web_module_);
+  if (!on_splash_screen_shutdown_complete_.callback().is_null()) {
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, on_splash_screen_shutdown_complete_.callback(),
+        base::TimeDelta::FromSeconds(kSplashShutdownSeconds));
+  }
+  web_module_->InjectBeforeUnloadEvent();
 }
 
 }  // namespace browser
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index 0e07915..73db062 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -33,16 +33,17 @@
 //
 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 GURL& fallback_splash_screen_url,
-               const GURL& initial_main_web_module_url,
-               cobalt::browser::SplashScreenCache* splash_screen_cache);
+  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 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();
 
   void SetSize(const math::Size& window_dimensions, float video_pixel_ratio) {
@@ -61,29 +62,32 @@
     web_module_->Resume(resource_provider);
   }
 
-  // Block the caller until the splash screen is ready to be rendered.
-  void WaitUntilReady();
+  void ReduceMemory() { web_module_->ReduceMemory(); }
+
+  // This dispatches event beforeunload in the WebModule. If
+  // beforeunload has any handlers or listeners, Shutdown waits for
+  // window.close to be called or a maximum of kSplashShutdownSeconds
+  // before running |on_splash_screen_shutdown_complete_|. If beforeunload has
+  // no handlers, |on_splash_screen_shutdown_complete_| is run immediately.
+  void Shutdown();
 
  private:
-  void OnRenderTreeProduced(
-      const browser::WebModule::LayoutResults& layout_results);
-
-  void OnError(const GURL& /* url */, const std::string& error) {
-    is_ready_.Signal();
-    LOG(ERROR) << error;
-  }
-
+  // Run when window.close() is called by the WebModule.
   void OnWindowClosed();
+  void OnWindowClosedInternal();
 
   media::MediaModuleStub stub_media_module_;
 
   WebModule::OnRenderTreeProducedCallback render_tree_produced_callback_;
 
-  // Signalled once the splash screen has produced its first render tree or
-  // an error occurred.
-  base::WaitableEvent is_ready_;
-
   scoped_ptr<WebModule> web_module_;
+
+  // The splash screen runs on this message loop.
+  MessageLoop* const self_message_loop_;
+
+  // This is called by Shutdown (via window.close) or after
+  // the time limit has been exceeded.
+  base::CancelableClosure on_splash_screen_shutdown_complete_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/suspend_fuzzer.cc b/src/cobalt/browser/suspend_fuzzer.cc
index b94158b..51a7265 100644
--- a/src/cobalt/browser/suspend_fuzzer.cc
+++ b/src/cobalt/browser/suspend_fuzzer.cc
@@ -43,20 +43,13 @@
 
 void SuspendFuzzer::DoStep() {
   DCHECK(MessageLoop::current() == thread_.message_loop());
-#if SB_API_VERSION < 4
-  NOTREACHED() << "Cannot run suspend_fuzzer on SB_API_VERSION < 4.";
-#endif
   if (step_type_ == kShouldRequestSuspend) {
     SB_DLOG(INFO) << "suspend_fuzzer: Requesting suspend.";
-#if SB_API_VERSION >= 4
     SbSystemRequestSuspend();
-#endif
     step_type_ = kShouldRequestUnpause;
   } else if (step_type_ == kShouldRequestUnpause) {
     SB_DLOG(INFO) << "suspend_fuzzer: Requesting unpause.";
-#if SB_API_VERSION >= 4
     SbSystemRequestUnpause();
-#endif
     step_type_ = kShouldRequestSuspend;
   } else {
     NOTREACHED();
diff --git a/src/cobalt/browser/suspend_fuzzer.h b/src/cobalt/browser/suspend_fuzzer.h
index 4aa97cc..d1681cf 100644
--- a/src/cobalt/browser/suspend_fuzzer.h
+++ b/src/cobalt/browser/suspend_fuzzer.h
@@ -22,7 +22,7 @@
 namespace browser {
 
 // Repeatedly switch off between calling |SbSystemRequestSuspend| and
-// |SbSystemRequestUnpause|, or just no-op if on an SB_API_VERSION < 4.
+// |SbSystemRequestUnpause|.
 class SuspendFuzzer {
  public:
   SuspendFuzzer();
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 2c39eed..11e98c3 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -34,12 +34,12 @@
 // Switches different debug console modes: on | hud | off
 const char kDebugConsoleMode[] = "debug_console";
 
+// Do not create the WebDriver server.
+const char kDisableWebDriver[] = "disable_webdriver";
+
 // Disable webm/vp9.
 const char kDisableWebmVp9[] = "disable_webm_vp9";
 
-// Create WebDriver server.
-const char kEnableWebDriver[] = "enable_webdriver";
-
 // Additional base directory for accessing web files via file://.
 const char kExtraWebFileDir[] = "web_file_path";
 
@@ -83,8 +83,7 @@
 const char kStubImageDecoder[] = "stub_image_decoder";
 
 // If this flag is set, alternating calls to |SbSystemRequestSuspend| and
-// |SbSystemRequestUnpause| will be made periodically. Requires
-// SB_API_VERSION >= 4, and will otherwise just no-op.
+// |SbSystemRequestUnpause| will be made periodically.
 const char kSuspendFuzzer[] = "suspend_fuzzer";
 
 // If this is set, then a trace (see base/debug/trace_eventh.h) is started on
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 4470c4e..4dc02ef 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -24,8 +24,8 @@
 extern const char kAudioDecoderStub[];
 extern const char kCspMode[];
 extern const char kDebugConsoleMode[];
+extern const char kDisableWebDriver[];
 extern const char kDisableWebmVp9[];
-extern const char kEnableWebDriver[];
 extern const char kExtraWebFileDir[];
 extern const char kFakeMicrophone[];
 extern const char kIgnoreCertificateErrors[];
diff --git a/src/cobalt/browser/testdata/splash_screen/beforeunload.html b/src/cobalt/browser/testdata/splash_screen/beforeunload.html
new file mode 100644
index 0000000..5b6ecf9
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/beforeunload.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+.box {
+    width: 100px;
+    height: 100px;
+    background-color: red;
+    color : yellow;
+    font-size: 20px;
+    left: 0px;
+    top: 0px;
+    position: absolute;
+    transition: background-color .25s;
+    transition-timing-function: ease;
+}
+.box1{
+    width: 100px;
+    height: 100px;
+    background-color: blue;
+    color: yellow;
+    font-size: 20px;
+    left: 0px;
+    top: 0px;
+    position:absolute;
+    transition: background-color .25s;
+    transition-timing-function: ease;
+}
+    </style>
+  </head>
+<body style="background-color: #1f52a5;">
+<div class="box" id="box">Sample</div>
+<div id="immediately" style="display:block;">
+THIS SHOWS IMMEDIATELY
+</div>
+
+<div id="beforeUnload" style="display:none;">
+THIS SHOWS AT BEFOREUNLOAD
+</div>
+
+<div id="transitionEnd" style="display:none;">
+THIS SHOWS AT TRANSITIONEND
+</div>
+
+
+<script>
+  console.log('Running the script in beforeunload.html');
+
+  function updateTransition() {
+    var el = document.getElementById("box");
+    if (el.className == "box") {
+      el.className = "box1";
+    } else {
+      el.className = "box";
+    }
+  }
+
+  function transitionEndFunction() {
+    console.log("transitionend event");
+    // Set this to true to simulate an unresponsive splash screen which does
+    // not call window.close().
+    var unresponsive = true;
+    // This style change is only shown if unresponsive.
+    document.getElementById("transitionEnd").style.display="block";
+    // Comment this out to test out an unresponsive
+    // window.ontransitionend function.
+    if (!unresponsive) {
+      window.close();
+    }
+  }
+  // Either event handler or event listeners should work.
+  window.ontransitionend = transitionEndFunction;
+  //window.addEventListener("transitionend", transitionEndFunction, true);
+
+  function beforeUnloadFunction() {
+    console.log("beforeunload event");
+    document.getElementById("immediately").style.display="none";
+    document.getElementById("beforeUnload").style.display="block";
+    window.updateTransition();
+    // Returning a string shows a confirmation dialog in Chrome.
+    return "returning text is futile";
+  };
+  // Either event handler or event listeners should work.
+  // window.onbeforeunload = beforeUnloadFunction;
+  window.addEventListener("beforeunload", beforeUnloadFunction, true);
+
+  console.log("Ran the script in beforeunload.html");
+</script>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/testdata/splash_screen/block_render_tree_head_body_display_none.html b/src/cobalt/browser/testdata/splash_screen/block_render_tree_head_body_display_none.html
new file mode 100644
index 0000000..f481c4d
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/block_render_tree_head_body_display_none.html
@@ -0,0 +1,441 @@
+<!DOCTYPE html>
+<html>
+
+<head style="display : none">
+  <meta http-equiv="Content-Security-Policy" content="
+    default-src 'unsafe-inline';
+    style-src 'unsafe-inline';
+    script-src 'unsafe-inline';">
+</head>
+
+<script>
+  window.setTimeout(function() {
+    document.getElementsByTagName('body')[0].style.display = 'block';
+  }, 5000);
+</script>
+
+<body style="background-color: #1f52a5; display: none">
+<h1>Heading</h1>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html b/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html
new file mode 100644
index 0000000..d5f4552
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html
@@ -0,0 +1,440 @@
+<!DOCTYPE html>
+<html style="display: none">
+
+<head>
+  <meta http-equiv="Content-Security-Policy" content="
+    default-src 'unsafe-inline';
+    style-src 'unsafe-inline';
+    script-src 'unsafe-inline';">
+</head>
+
+<script>
+  window.setTimeout(function() {
+    document.getElementsByTagName('html')[0].style.display = 'block';
+  }, 5000);
+</script>
+<body style="background-color: #1f52a5;">
+<h1>Heading</h1>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 87fc9f2..729697f 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -54,6 +54,7 @@
 #include "cobalt/loader/image/animated_image_tracker.h"
 #include "cobalt/media_session/media_session_client.h"
 #include "cobalt/page_visibility/visibility_state.h"
+#include "cobalt/script/error_report.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/storage/storage_manager.h"
 #include "starboard/accessibility.h"
@@ -143,6 +144,12 @@
   void InjectWheelEvent(scoped_refptr<dom::Element> element, base::Token type,
                         const dom::WheelEventInit& event);
 
+  // Called to inject a beforeunload event into the web module. If
+  // this event is not handled by the web application,
+  // on_before_unload_fired_but_not_handled will be called. The event
+  // is not directed at a specific element.
+  void InjectBeforeUnloadEvent();
+
   // Called to execute JavaScript in this WebModule. Sets the |result|
   // output parameter and signals |got_result|.
   void ExecuteJavascript(const std::string& script_utf8,
@@ -197,8 +204,10 @@
   void Unpause();
   void Resume(render_tree::ResourceProvider* resource_provider);
 
-  void ReportScriptError(const base::SourceLocation& source_location,
-                         const std::string& error_message);
+  void ReduceMemory();
+
+  void LogScriptError(const base::SourceLocation& source_location,
+                      const std::string& error_message);
 
  private:
   class DocumentLoadedObserver;
@@ -233,6 +242,10 @@
     error_callback_.Run(window_->location()->url(), error);
   }
 
+  // Report an error encountered while running JS.
+  // Returns whether or not the error was handled.
+  bool ReportScriptError(const script::ErrorReport& error_report);
+
   // Inject the DOM event object into the window or the element.
   void InjectInputEvent(scoped_refptr<dom::Element> element,
                         const scoped_refptr<dom::Event>& event);
@@ -358,6 +371,8 @@
   scoped_ptr<media_session::MediaSessionClient> media_session_client_;
 
   scoped_ptr<layout::TopmostEventTarget> topmost_event_target_;
+
+  base::Closure on_before_unload_fired_but_not_handled;
 };
 
 class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -422,6 +437,9 @@
                    base::Unretained(data.options.splash_screen_cache));
   }
 
+  on_before_unload_fired_but_not_handled =
+      data.options.on_before_unload_fired_but_not_handled;
+
   fetcher_factory_.reset(new loader::FetcherFactory(
       data.network_module, data.options.extra_web_file_dir,
       dom::URL::MakeBlobResolverCallback(blob_registry_.get()),
@@ -475,7 +493,7 @@
 
 #if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
   script::JavaScriptEngine::ErrorHandler error_handler =
-      base::Bind(&WebModule::Impl::ReportScriptError, base::Unretained(this));
+      base::Bind(&WebModule::Impl::LogScriptError, base::Unretained(this));
   javascript_engine_->RegisterErrorHandler(error_handler);
 #endif
 
@@ -578,6 +596,9 @@
       base::Bind(&dom::CspDelegate::ReportEval,
                  base::Unretained(window_->document()->csp_delegate())));
 
+  global_environment_->SetReportErrorCallback(
+      base::Bind(&WebModule::Impl::ReportScriptError, base::Unretained(this)));
+
   InjectCustomWindowAttributes(data.options.injected_window_attributes);
 
   if (!data.options.loaded_callbacks.empty()) {
@@ -594,6 +615,8 @@
   DCHECK(is_running_);
   is_running_ = false;
   global_environment_->SetReportEvalCallback(base::Closure());
+  global_environment_->SetReportErrorCallback(
+      script::GlobalEnvironment::ReportErrorCallback());
   window_->DispatchEvent(new dom::Event(base::Tokens::unload()));
   document_load_observer_.reset();
   media_session_client_.reset();
@@ -750,6 +773,14 @@
   }
 }
 
+bool WebModule::Impl::ReportScriptError(
+    const script::ErrorReport& error_report) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(is_running_);
+  DCHECK(window_);
+  return window_->ReportScriptError(error_report);
+}
+
 #if defined(ENABLE_WEBDRIVER)
 void WebModule::Impl::CreateWindowDriver(
     const webdriver::protocol::WindowId& window_id,
@@ -925,7 +956,23 @@
   SetApplicationState(base::kApplicationStatePaused);
 }
 
-void WebModule::Impl::ReportScriptError(
+void WebModule::Impl::ReduceMemory() {
+  TRACE_EVENT0("cobalt::browser", "WebModule::Impl::ReduceMemory()");
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!is_running_) {
+    return;
+  }
+
+  PurgeResourceCaches();
+  window_->document()->PurgeCachedResources();
+
+  // Force garbage collection in |javascript_engine_|.
+  if (javascript_engine_) {
+    javascript_engine_->CollectGarbage();
+  }
+}
+
+void WebModule::Impl::LogScriptError(
     const base::SourceLocation& source_location,
     const std::string& error_message) {
   std::string file_name =
@@ -944,6 +991,15 @@
   SbLogRaw(ss.str().c_str());
 }
 
+void WebModule::Impl::InjectBeforeUnloadEvent() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (window_ && window_->HasEventListener(base::Tokens::beforeunload())) {
+    window_->DispatchEvent(new dom::Event(base::Tokens::beforeunload()));
+  } else if (!on_before_unload_fired_but_not_handled.is_null()) {
+    on_before_unload_fired_but_not_handled.Run();
+  }
+}
+
 void WebModule::Impl::PurgeResourceCaches() {
   image_cache_->Purge();
   remote_typeface_cache_->Purge();
@@ -1097,6 +1153,15 @@
                             scoped_refptr<dom::Element>(), type, event));
 }
 
+void WebModule::InjectBeforeUnloadEvent() {
+  TRACE_EVENT0("cobalt::browser", "WebModule::InjectBeforeUnloadEvent()");
+  DCHECK(message_loop());
+  DCHECK(impl_);
+  message_loop()->PostTask(FROM_HERE,
+                           base::Bind(&WebModule::Impl::InjectBeforeUnloadEvent,
+                                      base::Unretained(impl_.get())));
+}
+
 std::string WebModule::ExecuteJavascript(
     const std::string& script_utf8,
     const base::SourceLocation& script_location,
@@ -1287,6 +1352,17 @@
                             base::Unretained(impl_.get()), resource_provider));
 }
 
+void WebModule::ReduceMemory() {
+  // Must only be called by a thread external from the WebModule thread.
+  DCHECK_NE(MessageLoop::current(), message_loop());
+
+  // We block here so that we block the Low Memory event handler until we have
+  // reduced our memory consumption.
+  message_loop()->PostBlockingTask(
+      FROM_HERE, base::Bind(&WebModule::Impl::ReduceMemory,
+                            base::Unretained(impl_.get())));
+}
+
 void WebModule::Impl::HandlePointerEvents() {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::HandlePointerEvents");
   const scoped_refptr<dom::Document>& document = window_->document();
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index b296d6f..ba23707 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -185,6 +185,14 @@
     // The splash screen cache object, owned by the BrowserModule.
     SplashScreenCache* splash_screen_cache;
 
+    // The beforeunload event can give a web page a chance to shut
+    // itself down softly and ultimately call window.close(), however
+    // if it is not handled by the web application, we indicate this
+    // situation externally by calling this callback, so that if the
+    // beforeunload event was generated it can be known that there is
+    // no window.close() call pending.
+    base::Closure on_before_unload_fired_but_not_handled;
+
     // Whether or not the WebModule is allowed to fetch from cache via
     // h5vcc-cache://.
     bool can_fetch_cache;
@@ -222,6 +230,11 @@
   // represents the event name, for example 'wheel'.
   void InjectWheelEvent(base::Token type, const dom::WheelEventInit& event);
 
+  // Call this to inject a beforeunload event into the web module. If
+  // this event is not handled by the web application,
+  // on_before_unload_fired_but_not_handled will be called.
+  void InjectBeforeUnloadEvent();
+
   // Call this to execute Javascript code in this web module.  The calling
   // thread will block until the JavaScript has executed and the output results
   // are available.
@@ -262,6 +275,10 @@
   void Suspend() OVERRIDE;
   void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE;
 
+  // Attempt to reduce overall memory consumption. Called in response to a
+  // system indication that memory usage is nearing a critical level.
+  void ReduceMemory();
+
  private:
   // Data required to construct a WebModule, initialized in the constructor and
   // passed to |Initialize|.
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 3c54c85..248bc70 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-90790
\ No newline at end of file
+93830
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index a3243c1..02c5ec2 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -790,10 +790,9 @@
   # Clients must copy over all content; to avoid having to copy over extra data, we
   # omit the test data
   'conditions': [
-    ['cobalt_config != "gold" and cobalt_enable_lib == 0', {
+    ['cobalt_config != "gold"', {
       'variables' : {
         'cobalt_copy_debug_console': 1,
-        'cobalt_copy_test_data': 1,
         'enable_about_scheme': 1,
         'enable_fake_microphone': 1,
         'enable_file_scheme': 1,
@@ -807,7 +806,6 @@
     {
       'variables' : {
         'cobalt_copy_debug_console': 0,
-        'cobalt_copy_test_data': 0,
         'enable_about_scheme': 0,
         'enable_fake_microphone': 0,
         'enable_file_scheme': 0,
@@ -818,5 +816,15 @@
         'sb_allows_memory_tracking': 0,
       },
     }],
+    ['cobalt_config != "gold" and cobalt_enable_lib == 0', {
+      'variables' : {
+        'cobalt_copy_test_data': 1,
+      },
+    },
+    {
+      'variables' : {
+        'cobalt_copy_test_data': 0,
+      },
+    }],
   ],
 }
diff --git a/src/cobalt/build/config/starboard.py b/src/cobalt/build/config/starboard.py
index ecba441..2af50cd 100644
--- a/src/cobalt/build/config/starboard.py
+++ b/src/cobalt/build/config/starboard.py
@@ -81,9 +81,6 @@
         # Cobalt uses OpenSSL on all platforms.
         'use_openssl': 1,
         'clang': use_clang,
-        # Cobalt relies on the Starboard implementation for DRM on all Starboard
-        # platforms.
-        'use_widevine': 0,
         # Whether to build with clang's Address Sanitizer instrumentation.
         'use_asan': use_asan,
         # Whether to build with clang's Thread Sanitizer instrumentation.
diff --git a/src/cobalt/debug/debug_web_server.cc b/src/cobalt/debug/debug_web_server.cc
index 6ffd152..baa0fb9 100644
--- a/src/cobalt/debug/debug_web_server.cc
+++ b/src/cobalt/debug/debug_web_server.cc
@@ -30,12 +30,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/tcp_listen_socket.h"
 #include "net/server/http_server_request_info.h"
-
-#if defined(__LB_SHELL__)
-#include "lb_network_helpers.h"  // NOLINT[build/include]
-#elif defined(OS_STARBOARD)
 #include "starboard/socket.h"
-#endif
 
 namespace cobalt {
 namespace debug {
@@ -84,7 +79,6 @@
   net::IPEndPoint ip_addr;
   SbSocketAddress local_ip;
   SbMemorySet(&local_ip, 0, sizeof(local_ip));
-#if SB_API_VERSION >= 4
   bool result = false;
 
   // Prefer IPv4 addresses, as they're easier to type for debugging.
@@ -109,19 +103,6 @@
     DLOG(WARNING) << "Unable to get a local interface address.";
     return base::nullopt;
   }
-#else
-  bool result = SbSocketGetLocalInterfaceAddress(&local_ip);
-  if (!result) {
-    DLOG(WARNING) << "Unable to get a local interface address.";
-    return base::nullopt;
-  }
-
-  result = ip_addr.FromSbSocketAddress(&local_ip);
-  if (!result) {
-    LOG(WARNING) << "Got invalid local interface address.";
-    return base::nullopt;
-  }
-#endif  // SB_API_VERSION >= 4
 
   return ip_addr.ToStringWithoutPort();
 }
diff --git a/src/cobalt/dom/custom_event.h b/src/cobalt/dom/custom_event.h
new file mode 100644
index 0000000..615eb6a
--- /dev/null
+++ b/src/cobalt/dom/custom_event.h
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_CUSTOM_EVENT_H_
+#define COBALT_DOM_CUSTOM_EVENT_H_
+
+#include <string>
+
+#include "cobalt/dom/custom_event_init.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/script/value_handle.h"
+
+namespace cobalt {
+namespace dom {
+
+// Events using the CustomEvent interface can be used to carry custom data.
+//   https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
+class CustomEvent : public Event {
+ public:
+  explicit CustomEvent(const std::string& type) : Event(type) {}
+  CustomEvent(const std::string& type, const CustomEventInit& init_dict)
+      : Event(type, init_dict) {
+    set_detail(init_dict.detail());
+  }
+
+  // Creates an event with its "initialized flag" unset.
+  explicit CustomEvent(UninitializedFlag uninitialized_flag)
+      : Event(uninitialized_flag) {}
+
+  // Web API: CustomEvent
+  //
+  void InitCustomEvent(const std::string& type, bool bubbles, bool cancelable,
+                       const script::ValueHandleHolder& detail) {
+    InitEvent(type, bubbles, cancelable);
+    set_detail(&detail);
+  }
+
+  void set_detail(const script::ValueHandleHolder* detail) {
+    if (detail) {
+      detail_.reset(new script::ValueHandleHolder::Reference(this, *detail));
+    } else {
+      detail_.reset();
+    }
+  }
+
+  const script::ValueHandleHolder* detail() const {
+    if (!detail_) {
+      return NULL;
+    }
+
+    return &(detail_->referenced_value());
+  }
+
+  DEFINE_WRAPPABLE_TYPE(CustomEvent);
+
+ protected:
+  ~CustomEvent() OVERRIDE {}
+
+  scoped_ptr<script::ValueHandleHolder::Reference> detail_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_CUSTOM_EVENT_H_
diff --git a/src/cobalt/dom/custom_event.idl b/src/cobalt/dom/custom_event.idl
new file mode 100644
index 0000000..cd4f555
--- /dev/null
+++ b/src/cobalt/dom/custom_event.idl
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
+[Constructor(DOMString type, optional CustomEventInit eventInitDict)]
+interface CustomEvent : Event {
+  readonly attribute any detail;
+
+  void initCustomEvent(DOMString type,
+                       boolean bubbles,
+                       boolean cancelable,
+                       any detail);
+};
diff --git a/src/cobalt/dom/custom_event_init.idl b/src/cobalt/dom/custom_event_init.idl
new file mode 100644
index 0000000..2967077
--- /dev/null
+++ b/src/cobalt/dom/custom_event_init.idl
@@ -0,0 +1,19 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/dom/#customeventinit
+
+dictionary CustomEventInit : EventInit {
+  any detail = null;
+};
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
new file mode 100644
index 0000000..d8b4c42
--- /dev/null
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -0,0 +1,198 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/custom_event.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/custom_event_init.h"
+#include "cobalt/dom/local_storage_database.h"
+#include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/dom_parser/parser.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/script/source_code.h"
+#include "cobalt/script/testing/fake_script_value.h"
+#include "cobalt/script/value_handle.h"
+#include "cobalt/script/wrappable.h"
+#include "nb/pointer_arithmetic.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+using ::cobalt::script::testing::FakeScriptValue;
+
+class MockErrorCallback : public base::Callback<void(const std::string&)> {
+ public:
+  MOCK_METHOD1(Run, void(const std::string&));
+};
+
+namespace {
+class CustomEventTest : public ::testing::Test {
+ public:
+  CustomEventTest()
+      : environment_settings_(new script::EnvironmentSettings),
+        message_loop_(MessageLoop::TYPE_DEFAULT),
+        css_parser_(css_parser::Parser::Create()),
+        dom_parser_(new dom_parser::Parser(mock_error_callback_)),
+        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        local_storage_database_(NULL),
+        stub_media_module_(new media::MediaModuleStub()),
+        url_("about:blank"),
+        window_(new Window(
+            1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+            dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
+            NULL, NULL, &local_storage_database_, stub_media_module_.get(),
+            stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
+            "en-US", base::Callback<void(const GURL&)>(),
+            base::Bind(&MockErrorCallback::Run,
+                       base::Unretained(&mock_error_callback_)),
+            NULL, network_bridge::PostSender(),
+            std::string() /* default security policy */, kCspEnforcementEnable,
+            base::Closure() /* csp_policy_changed */,
+            base::Closure() /* ran_animation_frame_callbacks */,
+            base::Closure() /* window_close */,
+            base::Closure() /* window_minimize */, NULL, NULL)) {
+    engine_ = script::JavaScriptEngine::CreateEngine(
+        script::JavaScriptEngine::Options());
+    global_environment_ = engine_->CreateGlobalEnvironment();
+    global_environment_->CreateGlobalObject(window_,
+                                            environment_settings_.get());
+  }
+
+  bool EvaluateScript(const std::string& js_code, std::string* result);
+
+ private:
+  scoped_ptr<script::JavaScriptEngine> engine_;
+  scoped_refptr<script::GlobalEnvironment> global_environment_;
+
+  const scoped_ptr<script::EnvironmentSettings> environment_settings_;
+  MessageLoop message_loop_;
+  MockErrorCallback mock_error_callback_;
+  scoped_ptr<css_parser::Parser> css_parser_;
+  scoped_ptr<dom_parser::Parser> dom_parser_;
+  network::NetworkModule network_module_;
+  scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+  dom::LocalStorageDatabase local_storage_database_;
+  scoped_ptr<media::MediaModule> stub_media_module_;
+  GURL url_;
+  const scoped_refptr<Window> window_;
+};
+
+bool CustomEventTest::EvaluateScript(const std::string& js_code,
+                                     std::string* result) {
+  DCHECK(global_environment_);
+  DCHECK(result);
+  scoped_refptr<script::SourceCode> source_code =
+      script::SourceCode::CreateSourceCode(
+          js_code, base::SourceLocation(__FILE__, __LINE__, 1));
+
+  global_environment_->EnableEval();
+  global_environment_->SetReportEvalCallback(base::Closure());
+  bool succeeded = global_environment_->EvaluateScript(source_code, result);
+  return succeeded;
+}
+}  // namespace
+
+TEST_F(CustomEventTest, ConstructorWithEventTypeString) {
+  scoped_refptr<CustomEvent> event = new CustomEvent("mytestevent");
+
+  EXPECT_EQ("mytestevent", event->type());
+  EXPECT_EQ(NULL, event->target());
+  EXPECT_EQ(NULL, event->current_target());
+  EXPECT_EQ(Event::kNone, event->event_phase());
+  EXPECT_FALSE(event->bubbles());
+  EXPECT_FALSE(event->cancelable());
+  EXPECT_FALSE(event->default_prevented());
+  EXPECT_FALSE(event->IsBeingDispatched());
+  EXPECT_FALSE(event->propagation_stopped());
+  EXPECT_FALSE(event->immediate_propagation_stopped());
+  EXPECT_EQ(NULL, event->detail());
+}
+
+TEST_F(CustomEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
+  CustomEventInit init;
+  scoped_refptr<CustomEvent> event = new CustomEvent("mytestevent", init);
+
+  EXPECT_EQ("mytestevent", event->type());
+  EXPECT_EQ(NULL, event->target());
+  EXPECT_EQ(NULL, event->current_target());
+  EXPECT_EQ(Event::kNone, event->event_phase());
+  EXPECT_FALSE(event->bubbles());
+  EXPECT_FALSE(event->cancelable());
+  EXPECT_FALSE(event->default_prevented());
+  EXPECT_FALSE(event->IsBeingDispatched());
+  EXPECT_FALSE(event->propagation_stopped());
+  EXPECT_FALSE(event->immediate_propagation_stopped());
+  EXPECT_EQ(NULL, event->detail());
+}
+
+TEST_F(CustomEventTest, ConstructorWithEventTypeAndCustomInitDict) {
+  std::string result;
+  bool success = EvaluateScript(
+      "var event = new CustomEvent('dog', "
+      "    {'bubbles':true, "
+      "     'cancelable':true, "
+      "     'detail':{'cobalt':'rulez'}});"
+      "if (event.type == 'dog' &&"
+      "    event.bubbles == true &&"
+      "    event.cancelable == true) "
+      "    event.detail.cobalt;",
+      &result);
+  EXPECT_EQ("rulez", result);
+
+  if (!success) {
+    DLOG(ERROR) << "Failed to evaluate test: "
+                << "\"" << result << "\"";
+  } else {
+    LOG(INFO) << "Test result : "
+              << "\"" << result << "\"";
+  }
+}
+
+TEST_F(CustomEventTest, InitCustomEvent) {
+  std::string result;
+  bool success = EvaluateScript(
+      "var event = new CustomEvent('cat');\n"
+      "event.initCustomEvent('dog', true, true, {cobalt:'rulez'});"
+      "if (event.type == 'dog' &&"
+      "    event.detail &&"
+      "    event.bubbles == true &&"
+      "    event.cancelable == true) "
+      "    event.detail.cobalt;",
+      &result);
+  EXPECT_EQ("rulez", result);
+
+  if (!success) {
+    DLOG(ERROR) << "Failed to evaluate test: "
+                << "\"" << result << "\"";
+  } else {
+    LOG(INFO) << "Test result : "
+              << "\"" << result << "\"";
+  }
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index ff262c6..ec42f43 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -34,6 +34,7 @@
 #include "cobalt/dom/comment.h"
 #include "cobalt/dom/csp_delegate.h"
 #include "cobalt/dom/csp_delegate_factory.h"
+#include "cobalt/dom/custom_event.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/dom_implementation.h"
 #include "cobalt/dom/element.h"
@@ -240,6 +241,8 @@
   } else if (base::strcasecmp(interface_name.c_str(), "uievent") == 0 ||
              base::strcasecmp(interface_name.c_str(), "uievents") == 0) {
     return new UIEvent(Event::Uninitialized);
+  } else if (base::strcasecmp(interface_name.c_str(), "customevent") == 0) {
+    return new CustomEvent(Event::Uninitialized);
   }
 
   DOMException::Raise(
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 2a4ee06..7e4c391 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -19,6 +19,7 @@
 #include "cobalt/cssom/css_style_sheet.h"
 #include "cobalt/dom/attr.h"
 #include "cobalt/dom/comment.h"
+#include "cobalt/dom/custom_event.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/dom_implementation.h"
 #include "cobalt/dom/dom_stat_tracker.h"
@@ -172,6 +173,19 @@
   EXPECT_FALSE(event->initialized_flag());
 }
 
+TEST_F(DocumentTest, CreateEventCustomEvent) {
+  StrictMock<MockExceptionState> exception_state;
+  scoped_refptr<script::ScriptException> exception;
+  scoped_refptr<Document> document = new Document(&html_element_context_);
+
+  // Create an Event, the name is case insensitive.
+  scoped_refptr<Event> event =
+      document->CreateEvent("CuStOmEvEnT", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<CustomEvent*>(event.get()));
+}
+
 TEST_F(DocumentTest, CreateEventUIEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 88ef23d..27a8f29 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -62,6 +62,7 @@
         'css_animations_adapter.h',
         'css_transitions_adapter.cc',
         'css_transitions_adapter.h',
+        'custom_event.h',
         'data_view.cc',
         'data_view.h',
         'device_orientation_event.cc',
@@ -93,8 +94,10 @@
         'dom_token_list.h',
         'element.cc',
         'element.h',
+        'error_event.h',
         'event.cc',
         'event.h',
+        'event_init.h',
         'event_listener.cc',
         'event_listener.h',
         'event_queue.cc',
@@ -105,6 +108,7 @@
         'float64_array.h',
         'focus_event.cc',
         'focus_event.h',
+        'focus_event_init.h',
         'font_cache.cc',
         'font_cache.h',
         'font_face.cc',
@@ -164,6 +168,7 @@
         'initial_computed_style.h',
         'keyboard_event.cc',
         'keyboard_event.h',
+        'keyboard_event_init.h',
         'keycode.h',
         'keyframes_map_updater.cc',
         'keyframes_map_updater.h',
@@ -182,6 +187,7 @@
         'mime_type_array.h',
         'mouse_event.cc',
         'mouse_event.h',
+        'mouse_event_init.h',
         'mutation_observer.cc',
         'mutation_observer.h',
         'mutation_observer_init.h',
@@ -212,6 +218,7 @@
         'plugin_array.h',
         'pointer_event.cc',
         'pointer_event.h',
+        'pointer_event_init.h',
         'pointer_state.cc',
         'pointer_state.h',
         'progress_event.cc',
@@ -248,6 +255,7 @@
         'typed_array.h',
         'ui_event.cc',
         'ui_event.h',
+        'ui_event_init.h',
         'ui_event_with_key_state.cc',
         'ui_event_with_key_state.h',
         'uint8_array.h',
@@ -260,6 +268,7 @@
         'video_track_list.h',
         'wheel_event.cc',
         'wheel_event.h',
+        'wheel_event_init.h',
         'window.cc',
         'window.h',
         'window_timers.cc',
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 2c41b83..8cf266a 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -28,6 +28,7 @@
         'comment_test.cc',
         'crypto_test.cc',
         'csp_delegate_test.cc',
+        'custom_event_test.cc',
         'data_view_test.cc',
         'document_test.cc',
         'document_type_test.cc',
@@ -37,6 +38,7 @@
         'dom_string_map_test.cc',
         'dom_token_list_test.cc',
         'element_test.cc',
+        'error_event_test.cc',
         'event_queue_test.cc',
         'event_target_test.cc',
         'event_test.cc',
diff --git a/src/cobalt/dom/error_event.h b/src/cobalt/dom/error_event.h
new file mode 100644
index 0000000..d3b3b71
--- /dev/null
+++ b/src/cobalt/dom/error_event.h
@@ -0,0 +1,90 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_ERROR_EVENT_H_
+#define COBALT_DOM_ERROR_EVENT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/error_event_init.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/script/value_handle.h"
+
+namespace cobalt {
+namespace dom {
+
+// Whenever an uncaught runtime script error occurs in one of the scripts
+// associated with a Document, the user agent must report the error for the
+// relevant script.
+//   https://www.w3.org/TR/html5/webappapis.html#errorevent
+class ErrorEvent : public Event {
+ public:
+  explicit ErrorEvent(const std::string& type)
+      : Event(type), lineno_(0), colno_(0) {}
+  ErrorEvent(const std::string& type, const ErrorEventInit& init_dict)
+      : Event(type, init_dict),
+        message_(init_dict.message()),
+        filename_(init_dict.filename()),
+        lineno_(init_dict.lineno()),
+        colno_(init_dict.colno()) {
+    InitError(init_dict);
+  }
+  ErrorEvent(base::Token type, const ErrorEventInit& init_dict)
+      : Event(type, init_dict),
+        message_(init_dict.message()),
+        filename_(init_dict.filename()),
+        lineno_(init_dict.lineno()),
+        colno_(init_dict.colno()) {
+    InitError(init_dict);
+  }
+
+  // Web API: ErrorEvent
+  //
+  std::string message() const { return message_; }
+  std::string filename() const { return filename_; }
+  uint32 lineno() const { return lineno_; }
+  uint32 colno() const { return colno_; }
+
+  const script::ValueHandleHolder* error() const {
+    if (!error_) {
+      return NULL;
+    }
+    return &(error_->referenced_value());
+  }
+
+  DEFINE_WRAPPABLE_TYPE(ErrorEvent);
+
+ protected:
+  ~ErrorEvent() OVERRIDE {}
+
+ private:
+  void InitError(const ErrorEventInit& init_dict) {
+    const script::ValueHandleHolder* error = init_dict.error();
+    if (error) {
+      error_.reset(new script::ValueHandleHolder::Reference(this, *error));
+    }
+  }
+
+  std::string message_;
+  std::string filename_;
+  uint32 lineno_;
+  uint32 colno_;
+  scoped_ptr<script::ValueHandleHolder::Reference> error_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_ERROR_EVENT_H_
diff --git a/src/cobalt/dom/error_event.idl b/src/cobalt/dom/error_event.idl
new file mode 100644
index 0000000..10c4699
--- /dev/null
+++ b/src/cobalt/dom/error_event.idl
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/html5/webappapis.html#errorevent
+
+[Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
+interface ErrorEvent : Event {
+  readonly attribute DOMString message;
+  readonly attribute DOMString filename;
+  readonly attribute unsigned long lineno;
+  readonly attribute unsigned long colno;
+  readonly attribute any error;
+};
diff --git a/src/cobalt/dom/error_event_init.idl b/src/cobalt/dom/error_event_init.idl
new file mode 100644
index 0000000..77e80c0
--- /dev/null
+++ b/src/cobalt/dom/error_event_init.idl
@@ -0,0 +1,23 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/html5/webappapis.html#erroreventinit
+
+dictionary ErrorEventInit : EventInit {
+  DOMString message = "";
+  DOMString filename = "";
+  unsigned long lineno = 0;
+  unsigned long colno = 0;
+  any error = null;
+};
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
new file mode 100644
index 0000000..b8ead45
--- /dev/null
+++ b/src/cobalt/dom/error_event_test.cc
@@ -0,0 +1,191 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/error_event.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/error_event_init.h"
+#include "cobalt/dom/local_storage_database.h"
+#include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/dom_parser/parser.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/script/source_code.h"
+#include "cobalt/script/testing/fake_script_value.h"
+#include "cobalt/script/value_handle.h"
+#include "cobalt/script/wrappable.h"
+#include "nb/pointer_arithmetic.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+using ::cobalt::script::testing::FakeScriptValue;
+
+class MockErrorCallback : public base::Callback<void(const std::string&)> {
+ public:
+  MOCK_METHOD1(Run, void(const std::string&));
+};
+
+namespace {
+class ErrorEventTest : public ::testing::Test {
+ public:
+  ErrorEventTest()
+      : environment_settings_(new script::EnvironmentSettings),
+        message_loop_(MessageLoop::TYPE_DEFAULT),
+        css_parser_(css_parser::Parser::Create()),
+        dom_parser_(new dom_parser::Parser(mock_error_callback_)),
+        fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+        local_storage_database_(NULL),
+        stub_media_module_(new media::MediaModuleStub()),
+        url_("about:blank"),
+        window_(new Window(
+            1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+            dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
+            NULL, NULL, &local_storage_database_, stub_media_module_.get(),
+            stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
+            "en-US", base::Callback<void(const GURL&)>(),
+            base::Bind(&MockErrorCallback::Run,
+                       base::Unretained(&mock_error_callback_)),
+            NULL, network_bridge::PostSender(),
+            std::string() /* default security policy */, kCspEnforcementEnable,
+            base::Closure() /* csp_policy_changed */,
+            base::Closure() /* ran_animation_frame_callbacks */,
+            base::Closure() /* window_close */,
+            base::Closure() /* window_minimize */, NULL, NULL)) {
+    engine_ = script::JavaScriptEngine::CreateEngine(
+        script::JavaScriptEngine::Options());
+    global_environment_ = engine_->CreateGlobalEnvironment();
+    global_environment_->CreateGlobalObject(window_,
+                                            environment_settings_.get());
+  }
+
+  bool EvaluateScript(const std::string& js_code, std::string* result);
+
+ private:
+  scoped_ptr<script::JavaScriptEngine> engine_;
+  scoped_refptr<script::GlobalEnvironment> global_environment_;
+
+  const scoped_ptr<script::EnvironmentSettings> environment_settings_;
+  MessageLoop message_loop_;
+  MockErrorCallback mock_error_callback_;
+  scoped_ptr<css_parser::Parser> css_parser_;
+  scoped_ptr<dom_parser::Parser> dom_parser_;
+  network::NetworkModule network_module_;
+  scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+  dom::LocalStorageDatabase local_storage_database_;
+  scoped_ptr<media::MediaModule> stub_media_module_;
+  GURL url_;
+  const scoped_refptr<Window> window_;
+};
+
+bool ErrorEventTest::EvaluateScript(const std::string& js_code,
+                                    std::string* result) {
+  DCHECK(global_environment_);
+  DCHECK(result);
+  scoped_refptr<script::SourceCode> source_code =
+      script::SourceCode::CreateSourceCode(
+          js_code, base::SourceLocation(__FILE__, __LINE__, 1));
+
+  global_environment_->EnableEval();
+  global_environment_->SetReportEvalCallback(base::Closure());
+  bool succeeded = global_environment_->EvaluateScript(source_code, result);
+  return succeeded;
+}
+}  // namespace
+
+TEST_F(ErrorEventTest, ConstructorWithEventTypeString) {
+  scoped_refptr<ErrorEvent> event = new ErrorEvent("mytestevent");
+
+  EXPECT_EQ("mytestevent", event->type());
+  EXPECT_EQ(NULL, event->target());
+  EXPECT_EQ(NULL, event->current_target());
+  EXPECT_EQ(Event::kNone, event->event_phase());
+  EXPECT_FALSE(event->bubbles());
+  EXPECT_FALSE(event->cancelable());
+  EXPECT_FALSE(event->default_prevented());
+  EXPECT_FALSE(event->IsBeingDispatched());
+  EXPECT_FALSE(event->propagation_stopped());
+  EXPECT_FALSE(event->immediate_propagation_stopped());
+  EXPECT_EQ("", event->message());
+  EXPECT_EQ("", event->filename());
+  EXPECT_EQ(0, event->lineno());
+  EXPECT_EQ(0, event->colno());
+  EXPECT_EQ(NULL, event->error());
+}
+
+TEST_F(ErrorEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
+  ErrorEventInit init;
+  scoped_refptr<ErrorEvent> event = new ErrorEvent("mytestevent", init);
+
+  EXPECT_EQ("mytestevent", event->type());
+  EXPECT_EQ(NULL, event->target());
+  EXPECT_EQ(NULL, event->current_target());
+  EXPECT_EQ(Event::kNone, event->event_phase());
+  EXPECT_FALSE(event->bubbles());
+  EXPECT_FALSE(event->cancelable());
+  EXPECT_FALSE(event->default_prevented());
+  EXPECT_FALSE(event->IsBeingDispatched());
+  EXPECT_FALSE(event->propagation_stopped());
+  EXPECT_FALSE(event->immediate_propagation_stopped());
+  EXPECT_EQ("", event->message());
+  EXPECT_EQ("", event->filename());
+  EXPECT_EQ(0, event->lineno());
+  EXPECT_EQ(0, event->colno());
+  EXPECT_EQ(NULL, event->error());
+}
+
+TEST_F(ErrorEventTest, ConstructorWithEventTypeAndErrorInitDict) {
+  std::string result;
+  bool success = EvaluateScript(
+      "var event = new ErrorEvent('dog', "
+      "    {'cancelable':true, "
+      "     'message':'error_message', "
+      "     'filename':'error_filename', "
+      "     'lineno':100, "
+      "     'colno':50, "
+      "     'error':{'cobalt':'rulez'}});"
+      "if (event.type == 'dog' &&"
+      "    event.bubbles == false &&"
+      "    event.cancelable == true &&"
+      "    event.message == 'error_message' &&"
+      "    event.filename == 'error_filename' &&"
+      "    event.lineno == 100 &&"
+      "    event.colno == 50) "
+      "    event.error.cobalt;",
+      &result);
+  EXPECT_EQ("rulez", result);
+
+  if (!success) {
+    DLOG(ERROR) << "Failed to evaluate test: "
+                << "\"" << result << "\"";
+  } else {
+    LOG(INFO) << "Test result : "
+              << "\"" << result << "\"";
+  }
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc
index ca6593c..b6a3a8c 100644
--- a/src/cobalt/dom/event_target.cc
+++ b/src/cobalt/dom/event_target.cc
@@ -78,7 +78,6 @@
   DCHECK(event->initialized_flag());
   TRACE_EVENT1("cobalt::dom", "EventTarget::DispatchEvent", "event",
                event->type().c_str());
-
   if (!event || event->IsBeingDispatched() || !event->initialized_flag()) {
     return false;
   }
@@ -223,6 +222,18 @@
       new EventListenerInfo(type, this, listener, use_capture, listener_type));
 }
 
+bool EventTarget::HasEventListener(base::Token type) {
+  TRACK_MEMORY_SCOPE("DOM");
+
+  for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
+       iter != event_listener_infos_.end(); ++iter) {
+    if ((*iter)->type == type) {
+      return true;
+    }
+  }
+  return false;
+}
+
 EventTarget::EventListenerInfo::EventListenerInfo(
     base::Token type, EventTarget* const event_target,
     const EventListenerScriptValue& listener, bool use_capture,
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index 896667c..4e269f3 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -78,6 +78,9 @@
       const tracked_objects::Location& location, base::Token event_name,
       const base::Closure& dispatched_callback);
 
+  // Check if target has event listener (atrtibute or not attribute).
+  bool HasEventListener(base::Token type);
+
   // Web API: GlobalEventHandlers (implements)
   // Many objects can have event handlers specified. These act as non-capture
   // event listeners for the object on which they are specified.
@@ -338,6 +341,20 @@
     SetAttributeEventListener(base::Tokens::timeupdate(), event_listener);
   }
 
+  const EventListenerScriptValue* onbeforeunload() {
+    return GetAttributeEventListener(base::Tokens::beforeunload());
+  }
+  void set_onbeforeunload(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::beforeunload(), event_listener);
+  }
+
+  const EventListenerScriptValue* ontransitionend() {
+    return GetAttributeEventListener(base::Tokens::transitionend());
+  }
+  void set_ontransitionend(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::transitionend(), event_listener);
+  }
+
   const EventListenerScriptValue* onunload() {
     return GetAttributeEventListener(base::Tokens::unload());
   }
diff --git a/src/cobalt/dom/global_event_handlers.idl b/src/cobalt/dom/global_event_handlers.idl
index 372f2f7..df852fc 100644
--- a/src/cobalt/dom/global_event_handlers.idl
+++ b/src/cobalt/dom/global_event_handlers.idl
@@ -44,6 +44,8 @@
 
   attribute EventHandler onresize;
 
+  attribute EventHandler ontransitionend;
+
   // Extensions for the Pointer Events recommendation.
   //  https://www.w3.org/TR/2015/REC-pointerevents-20150224/#extensions-to-the-globaleventhandlers-interface
   attribute EventHandler ongotpointercapture;
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index e62c335..5806cfc 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -272,6 +272,10 @@
   //   https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
   bool CanbeDesignatedByPointerIfDisplayed() const;
 
+  // Returns true if this node and all of its ancestors do NOT have display set
+  // to 'none'.
+  bool IsDisplayed() const;
+
   DEFINE_WRAPPABLE_TYPE(HTMLElement);
 
  protected:
@@ -330,10 +334,6 @@
   // Purge the cached background images on only this node.
   void PurgeCachedBackgroundImages();
 
-  // Returns true if this node and all of its ancestors do NOT have display set
-  // to 'none'.
-  bool IsDisplayed() const;
-
   bool locked_for_focus_;
 
   // The directionality of the html element is determined by the 'dir'
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 0a4a3a7..51200ba 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -207,6 +207,12 @@
 
 std::string HTMLMediaElement::CanPlayType(const std::string& mime_type,
                                           const std::string& key_system) {
+  if (!html_element_context()->can_play_type_handler()) {
+    DLOG(ERROR) << __FUNCTION__ << "(" << mime_type << ", " << key_system
+                << "): Media playback in PRELOADING is not supported.";
+    return "";
+  }
+
 #if defined(COBALT_MEDIA_SOURCE_2016)
   DLOG_IF(ERROR, !key_system.empty())
       << "CanPlayType() only accepts one parameter but (" << key_system
@@ -732,6 +738,11 @@
     }
   }
 
+  if (!html_element_context()->web_media_player_factory()) {
+    DLOG(ERROR) << "Media playback in PRELOADING is not supported.";
+    return;
+  }
+
   player_ =
       html_element_context()->web_media_player_factory()->CreateWebMediaPlayer(
           this);
@@ -1517,7 +1528,7 @@
   EndProcessingMediaPlayerCallback();
 }
 
-void HTMLMediaElement::TimeChanged() {
+void HTMLMediaElement::TimeChanged(bool eos_played) {
   DCHECK(player_);
   if (!player_) {
     return;
@@ -1543,8 +1554,9 @@
   // When the current playback position reaches the end of the media resource
   // when the direction of playback is forwards, then the user agent must follow
   // these steps:
-  if (!SbDoubleIsNan(dur) && (0.0f != dur) && now >= dur &&
-      playback_rate_ > 0) {
+  eos_played |=
+      !SbDoubleIsNan(dur) && (0.0f != dur) && now >= dur && playback_rate_ > 0;
+  if (eos_played) {
     // If the media element has a loop attribute specified and does not have a
     // current media controller,
     if (loop()) {
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index b3d1f0d..31314e3 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -231,7 +231,7 @@
   // WebMediaPlayerClient methods
   void NetworkStateChanged() OVERRIDE;
   void ReadyStateChanged() OVERRIDE;
-  void TimeChanged() OVERRIDE;
+  void TimeChanged(bool eos_played) OVERRIDE;
   void DurationChanged() OVERRIDE;
   void OutputModeChanged() OVERRIDE;
   void PlaybackStateChanged() OVERRIDE;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 0d4e62e..fab5c8c 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -28,6 +28,8 @@
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/element.h"
+#include "cobalt/dom/error_event.h"
+#include "cobalt/dom/error_event_init.h"
 #include "cobalt/dom/event.h"
 #include "cobalt/dom/history.h"
 #include "cobalt/dom/html_element.h"
@@ -111,6 +113,7 @@
       height_(height),
       device_pixel_ratio_(device_pixel_ratio),
       is_resize_event_pending_(false),
+      is_reporting_script_error_(false),
 #if defined(ENABLE_TEST_RUNNER)
       test_runner_(new TestRunner()),
 #endif  // ENABLE_TEST_RUNNER
@@ -430,6 +433,65 @@
   html_element_context_->page_visibility_state()->SetApplicationState(state);
 }
 
+bool Window::ReportScriptError(const script::ErrorReport& error_report) {
+  // Runtime script errors: when the user agent is required to report an error
+  // for a particular script, it must run these steps, after which the error is
+  // either handled or not handled:
+  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+
+  // 1. If target is in error reporting mode, then abort these steps; the error
+  //    is not handled.
+  if (is_reporting_script_error_) {
+    return false;
+  }
+
+  // 2. Let target be in error reporting mode.
+  is_reporting_script_error_ = true;
+
+  // 7. Let event be a new trusted ErrorEvent object that does not bubble but is
+  //    cancelable, and which has the event name error.
+  // NOTE: Cobalt does not currently support trusted events.
+  ErrorEventInit error_event_init;
+  error_event_init.set_bubbles(false);
+  error_event_init.set_cancelable(true);
+
+  if (error_report.is_muted) {
+    // 6. If script has muted errors, then set message to "Script error.", set
+    //    location to the empty string, set line and col to 0, and set error
+    //    object to null.
+    error_event_init.set_message("Script error.");
+    error_event_init.set_filename("");
+    error_event_init.set_lineno(0);
+    error_event_init.set_colno(0);
+    error_event_init.set_error(NULL);
+  } else {
+    // 8. Initialize event's message attribute to message.
+    error_event_init.set_message(error_report.message);
+    // 9. Initialize event's filename attribute to location.
+    error_event_init.set_filename(error_report.filename);
+    // 10. Initialize event's lineno attribute to line.
+    error_event_init.set_lineno(error_report.line_number);
+    // 11. Initialize event's colno attribute to col.
+    error_event_init.set_colno(error_report.column_number);
+    // 12. Initialize event's error attribute to error object.
+    error_event_init.set_error(error_report.error ? error_report.error.get()
+                                                  : NULL);
+  }
+
+  scoped_refptr<ErrorEvent> error_event(
+      new ErrorEvent(base::Tokens::error(), error_event_init));
+
+  // 13. Dispatch event at target.
+  DispatchEvent(error_event);
+
+  // 14. Let target no longer be in error reporting mode.
+  is_reporting_script_error_ = false;
+
+  // 15. If event was canceled, then the error is handled. Otherwise, the error
+  //     is not handled.
+  return error_event->default_prevented();
+}
+
 void Window::SetSynchronousLayoutCallback(
     const base::Closure& synchronous_layout_callback) {
   document_->set_synchronous_layout_callback(synchronous_layout_callback);
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index e3a3f83..9cd4cb6 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -53,6 +53,7 @@
 #include "cobalt/page_visibility/page_visibility_state.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/environment_settings.h"
+#include "cobalt/script/error_report.h"
 #include "cobalt/script/execution_state.h"
 #include "cobalt/script/script_runner.h"
 #include "cobalt/script/script_value_factory.h"
@@ -310,6 +311,11 @@
   // precipitate events to be dispatched.
   void SetApplicationState(base::ApplicationState state);
 
+  // Performs the steps specified for runtime script errors:
+  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+  // Returns whether or not the script was handled.
+  bool ReportScriptError(const script::ErrorReport& error_report);
+
   // page_visibility::PageVisibilityState::Observer implementation.
   void OnWindowFocusChanged(bool has_focus) OVERRIDE;
   void OnVisibilityStateChanged(
@@ -347,6 +353,11 @@
   // visibility state changes to visible.
   bool is_resize_event_pending_;
 
+  // Whether or not the window is currently reporting a script error. This is
+  // used to prevent infinite recursion, because reporting the error causes an
+  // event to be dispatched, which can generate a new script error.
+  bool is_reporting_script_error_;
+
 #if defined(ENABLE_TEST_RUNNER)
   scoped_refptr<TestRunner> test_runner_;
 #endif  // ENABLE_TEST_RUNNER
diff --git a/src/cobalt/dom/window_event_handlers.idl b/src/cobalt/dom/window_event_handlers.idl
index 2cda344..bc2bb42 100644
--- a/src/cobalt/dom/window_event_handlers.idl
+++ b/src/cobalt/dom/window_event_handlers.idl
@@ -17,4 +17,5 @@
 [NoInterfaceObject]
 interface WindowEventHandlers {
   attribute EventHandler onunload;
+  attribute EventHandler onbeforeunload;
 };
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.cc b/src/cobalt/h5vcc/h5vcc_accessibility.cc
index 2f9ddb7..7ab5164 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.cc
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.cc
@@ -42,7 +42,6 @@
 
 #if SB_HAS(SPEECH_SYNTHESIS)
 bool IsTextToSpeechEnabled() {
-#if SB_API_VERSION >= 4
   // Check if the tts feature is enabled in Starboard.
   SbAccessibilityTextToSpeechSettings tts_settings = {0};
   // Check platform settings.
@@ -50,7 +49,7 @@
     return tts_settings.has_text_to_speech_setting &&
            tts_settings.is_text_to_speech_enabled;
   }
-#endif  // SB_API_VERSION >= 4
+
   return false;
 }
 #endif  // SB_HAS(SPEECH_SYNTHESIS)
@@ -102,7 +101,6 @@
 }
 
 bool H5vccAccessibility::high_contrast_text() const {
-#if SB_API_VERSION >= 4
   SbAccessibilityDisplaySettings settings;
   SbMemorySet(&settings, 0, sizeof(settings));
 
@@ -111,13 +109,9 @@
   }
 
   return settings.is_high_contrast_text_enabled;
-#else   // SB_API_VERSION >= 4
-  return  false;
-#endif  // SB_API_VERSION >= 4
 }
 
 bool H5vccAccessibility::text_to_speech() const {
-#if SB_API_VERSION >= 4
   SbAccessibilityTextToSpeechSettings settings;
   SbMemorySet(&settings, 0, sizeof(settings));
 
@@ -127,9 +121,6 @@
 
   return settings.has_text_to_speech_setting &&
       settings.is_text_to_speech_enabled;
-#else   // SB_API_VERSION >= 4
-  return  false;
-#endif  // SB_API_VERSION >= 4
 }
 
 void H5vccAccessibility::AddHighContrastTextListener(
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index b1e6b70..34a3853 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -650,10 +650,10 @@
           RenderAndAnimateOverflow(padding_rounded_corners, border_node,
                                    &animate_node_builder, border_box_offset);
     }
-    border_node = RenderAndAnimateOpacity(border_node, &animate_node_builder,
-                                          opacity, opacity_animated);
     border_node = RenderAndAnimateTransform(border_node, &animate_node_builder,
                                             border_box_offset);
+    border_node = RenderAndAnimateOpacity(border_node, &animate_node_builder,
+                                          opacity, opacity_animated);
 
     cached_render_tree_node_info_->node_ =
         animate_node_builder.empty()
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 5f9c536..dc00344 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -64,7 +64,6 @@
 scoped_refptr<render_tree::Image> GetVideoFrame(
     const scoped_refptr<ShellVideoFrameProvider>& frame_provider,
     render_tree::ResourceProvider* resource_provider) {
-#if SB_API_VERSION >= 4
   SbDecodeTarget decode_target = frame_provider->GetCurrentSbDecodeTarget();
   if (SbDecodeTargetIsValid(decode_target)) {
 #if SB_HAS(GRAPHICS)
@@ -72,12 +71,8 @@
 #else  // SB_HAS(GRAPHICS)
     UNREFERENCED_PARAMETER(resource_provider);
     return NULL;
-#endif
+#endif  // SB_HAS(GRAPHICS)
   } else {
-#else  // SB_API_VERSION >= 4
-  UNREFERENCED_PARAMETER(resource_provider);
-  {
-#endif
     DCHECK(frame_provider);
     scoped_refptr<VideoFrame> video_frame = frame_provider->GetCurrentFrame();
     if (video_frame && video_frame->texture_id()) {
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index e9b8319..7f0cc65 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -24,7 +24,9 @@
 #include "base/timer.h"
 #include "cobalt/cssom/cascade_precedence.h"
 #include "cobalt/dom/camera_3d.h"
+#include "cobalt/dom/html_body_element.h"
 #include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/html_head_element.h"
 #include "cobalt/dom/html_html_element.h"
 #include "cobalt/layout/benchmark_stat_names.h"
 #include "cobalt/layout/block_formatting_block_container_box.h"
@@ -77,6 +79,8 @@
   const OnLayoutCallback on_layout_callback_;
   const LayoutTrigger layout_trigger_;
 
+  bool produced_render_tree_;
+
   // Setting these flags triggers an update of the layout box tree and the
   // generation of a new render tree at a regular interval (e.g. 60Hz). Events
   // such as DOM mutations cause them to be set to true. While the render tree
@@ -167,6 +171,7 @@
       on_render_tree_produced_callback_(on_render_tree_produced),
       on_layout_callback_(on_layout),
       layout_trigger_(layout_trigger),
+      produced_render_tree_(false),
       are_computed_styles_and_box_tree_dirty_(true),
       is_render_tree_pending_(
           StringPrintf("%s.Layout.IsRenderTreePending", name.c_str()), true,
@@ -366,11 +371,32 @@
       are_computed_styles_and_box_tree_dirty_ = false;
     }
 
+    // If no render tree has been produced yet, check if html, head, and
+    // body display should block the first render tree.
+    if (!produced_render_tree_) {
+      bool displayed_html = document->html()->IsDisplayed();
+      if (!displayed_html) {
+        return;
+      }
+      bool displayed_head = true;
+      if (document->head()) {
+        displayed_head = document->head();
+      }
+      bool displayed_body = true;
+      if (document->body()) {
+        displayed_body = document->body();
+      }
+      if (!displayed_head && !displayed_body) {
+        return;
+      }
+    }
+
     scoped_refptr<render_tree::Node> render_tree_root =
         layout::GenerateRenderTreeFromBoxTree(used_style_provider_.get(),
                                               layout_stat_tracker_,
                                               &initial_containing_block_);
     bool run_on_render_tree_produced_callback = true;
+    produced_render_tree_ = true;
 #if defined(ENABLE_TEST_RUNNER)
     if (layout_trigger_ == kTestRunnerMode &&
         window_->test_runner()->should_wait()) {
diff --git a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements-expected.png b/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements-expected.png
deleted file mode 100644
index b16b41a..0000000
--- a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements.html b/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements.html
deleted file mode 100644
index 4d9cc35..0000000
--- a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!--
- | Applying display: none to all elements.
- -->
-<html>
-<head></head>
-<body></body>
-<style>
-* {
-  display: none;
-}
-</style>
-
-</html>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
index 03292a0..9509933 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -4,7 +4,6 @@
 changing-css-text-triggers-layout
 cobalt-oxide, file:///cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.html
 console-trace-should-not-crash
-display_none_set_to_all_elements
 divs-with-background-color-and-text
 fixed-width-divs-with-background-color
 font-weight
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
index 13a4626..87dab78 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
@@ -170,7 +170,7 @@
 send-redirect-infinite.htm,PASS
 send-redirect-infinite-sync.htm,FAIL
 send-redirect-no-location.htm,FAIL
-send-redirect-to-cors.htm,DISABLE
+send-redirect-to-cors.htm,PASS
 send-redirect-to-non-cors.htm,FAIL
 send-response-event-order.htm,FAIL
 send-response-upload-event-loadend.htm,PASS
diff --git a/src/cobalt/loader/error_fetcher.cc b/src/cobalt/loader/error_fetcher.cc
new file mode 100644
index 0000000..caf00e2
--- /dev/null
+++ b/src/cobalt/loader/error_fetcher.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/loader/error_fetcher.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+
+namespace cobalt {
+namespace loader {
+
+ErrorFetcher::ErrorFetcher(Handler* handler, const std::string& error_message)
+    : Fetcher(handler),
+      error_message_(error_message),
+      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&ErrorFetcher::Fetch, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ErrorFetcher::Fetch() {
+  handler()->OnError(this, error_message_);
+}
+
+}  // namespace loader
+}  // namespace cobalt
diff --git a/src/cobalt/loader/error_fetcher.h b/src/cobalt/loader/error_fetcher.h
new file mode 100644
index 0000000..8f7738e
--- /dev/null
+++ b/src/cobalt/loader/error_fetcher.h
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_LOADER_ERROR_FETCHER_H_
+#define COBALT_LOADER_ERROR_FETCHER_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "cobalt/loader/fetcher.h"
+
+namespace cobalt {
+namespace loader {
+
+// Always returns an error.
+class ErrorFetcher : public Fetcher {
+ public:
+  ErrorFetcher(Handler* handler, const std::string& error_message_);
+
+ private:
+  void Fetch();
+
+  std::string error_message_;
+  base::WeakPtrFactory<ErrorFetcher> weak_ptr_factory_;
+};
+
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // COBALT_LOADER_ERROR_FETCHER_H_
diff --git a/src/cobalt/loader/fetcher_factory.cc b/src/cobalt/loader/fetcher_factory.cc
index 1e70cf4..9776365 100644
--- a/src/cobalt/loader/fetcher_factory.cc
+++ b/src/cobalt/loader/fetcher_factory.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/loader/fetcher_factory.h"
 
+#include <sstream>
 #include <string>
 
 #include "base/bind.h"
@@ -25,6 +26,7 @@
 #include "cobalt/loader/blob_fetcher.h"
 #include "cobalt/loader/cache_fetcher.h"
 #include "cobalt/loader/embedded_fetcher.h"
+#include "cobalt/loader/error_fetcher.h"
 #include "cobalt/loader/file_fetcher.h"
 #include "cobalt/loader/net_fetcher.h"
 #include "cobalt/network/network_module.h"
@@ -37,6 +39,7 @@
 const char kAboutScheme[] = "about";
 #endif
 
+#if defined(COBALT_ENABLE_FILE_SCHEME)
 bool FileURLToFilePath(const GURL& url, FilePath* file_path) {
   DCHECK(url.is_valid() && url.SchemeIsFile());
   std::string path = url.path();
@@ -45,6 +48,7 @@
   *file_path = FilePath(path);
   return !file_path->empty();
 }
+#endif
 
 std::string ClipUrl(const GURL& url, size_t length) {
   const std::string& spec = url.possibly_invalid_spec();
@@ -94,59 +98,67 @@
 scoped_ptr<Fetcher> FetcherFactory::CreateSecureFetcher(
     const GURL& url, const csp::SecurityCallback& url_security_callback,
     Fetcher::Handler* handler) {
+  DLOG(INFO) << "Fetching: " << ClipUrl(url, 60);
+
   if (!url.is_valid()) {
-    LOG(ERROR) << "URL is invalid: " << url;
-    return scoped_ptr<Fetcher>(NULL);
+    std::stringstream error_message;
+    error_message << "URL is invalid: " << url;
+    return scoped_ptr<Fetcher>(new ErrorFetcher(handler, error_message.str()));
   }
 
-  DLOG(INFO) << "Fetching: " << ClipUrl(url, 60);
-  scoped_ptr<Fetcher> fetcher;
+  if ((url.SchemeIs("https") || url.SchemeIs("http") ||
+       url.SchemeIs("data")) &&
+      network_module_) {
+    NetFetcher::Options options;
+    return scoped_ptr<Fetcher>(new NetFetcher(url, url_security_callback,
+                                              handler, network_module_,
+                                              options));
+  }
+
+  if (url.SchemeIs("blob") && !blob_resolver_.is_null()) {
+    return scoped_ptr<Fetcher>(new BlobFetcher(url, handler, blob_resolver_));
+  }
+
   if (url.SchemeIs(kEmbeddedScheme)) {
     EmbeddedFetcher::Options options;
-    fetcher.reset(
-        new EmbeddedFetcher(url, url_security_callback, handler, options));
-  } else if (url.SchemeIsFile()) {
+    return scoped_ptr<Fetcher>(new EmbeddedFetcher(url, url_security_callback,
+                                                   handler, options));
+  }
+
+  // h5vcc-cache: scheme requires read_cache_callback_ which is not available
+  // in the main WebModule.
+  if (url.SchemeIs(kCacheScheme) && !read_cache_callback_.is_null()) {
+    return scoped_ptr<Fetcher>(new CacheFetcher(url, url_security_callback,
+                                                handler,
+                                                read_cache_callback_));
+  }
+
+#if defined(COBALT_ENABLE_FILE_SCHEME)
+  if (url.SchemeIsFile()) {
     FilePath file_path;
-    if (FileURLToFilePath(url, &file_path)) {
-      FileFetcher::Options options;
-      options.message_loop_proxy = file_thread_.message_loop_proxy();
-      options.extra_search_dir = extra_search_dir_;
-      fetcher.reset(new FileFetcher(file_path, handler, options));
-    } else {
-      LOG(ERROR) << "File URL cannot be converted to file path: " << url;
-    }
-  }
-#if defined(ENABLE_ABOUT_SCHEME)
-  else if (url.SchemeIs(kAboutScheme)) {  // NOLINT(readability/braces)
-    fetcher.reset(new AboutFetcher(handler));
-  }
-#endif
-  else if (url.SchemeIs("blob")) {  // NOLINT(readability/braces)
-    if (!blob_resolver_.is_null()) {
-      fetcher.reset(new BlobFetcher(url, handler, blob_resolver_));
-    } else {
-      LOG(ERROR) << "Fetcher factory not provided the blob registry, "
-                    "could not fetch the URL: "
-                 << url;
-    }
-  } else if (url.SchemeIs(kCacheScheme)) {
-    if (read_cache_callback_.is_null()) {
-      LOG(ERROR) << "read_cache_callback_ must be provided to CacheFetcher for "
-                    "accessing h5vcc-cache:// . This is not available in the "
-                    "main WebModule.";
-      DCHECK(!read_cache_callback_.is_null());
-      return fetcher.Pass();
+    if (!FileURLToFilePath(url, &file_path)) {
+      std::stringstream error_message;
+      error_message << "File URL cannot be converted to file path: " << url;
+      return scoped_ptr<Fetcher>(new ErrorFetcher(handler,
+                                                  error_message.str()));
     }
 
-    fetcher.reset(new CacheFetcher(url, url_security_callback, handler,
-                                   read_cache_callback_));
-  } else {  // NOLINT(readability/braces)
-    DCHECK(network_module_) << "Network module required.";
-    NetFetcher::Options options;
-    fetcher.reset(new NetFetcher(url, url_security_callback, handler,
-                                 network_module_, options));
+    FileFetcher::Options options;
+    options.message_loop_proxy = file_thread_.message_loop_proxy();
+    options.extra_search_dir = extra_search_dir_;
+    return scoped_ptr<Fetcher>(new FileFetcher(file_path, handler, options));
   }
-  return fetcher.Pass();
+#endif
+
+#if defined(ENABLE_ABOUT_SCHEME)
+  if (url.SchemeIs(kAboutScheme)) {
+    return scoped_ptr<Fetcher>(new AboutFetcher(handler));
+  }
+#endif
+
+  std::stringstream error_message;
+  error_message << "Scheme " << url.scheme() << ": is not supported";
+  return scoped_ptr<Fetcher>(new ErrorFetcher(handler, error_message.str()));
 }
 
 }  // namespace loader
diff --git a/src/cobalt/loader/fetcher_factory_test.cc b/src/cobalt/loader/fetcher_factory_test.cc
index 7e525e1..e39d0a4 100644
--- a/src/cobalt/loader/fetcher_factory_test.cc
+++ b/src/cobalt/loader/fetcher_factory_test.cc
@@ -14,6 +14,7 @@
 
 #include <string>
 
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/file_fetcher.h"
@@ -26,32 +27,32 @@
 class StubFetcherHandler : public Fetcher::Handler {
  public:
   explicit StubFetcherHandler(base::RunLoop* run_loop)
-      : fetcher_(NULL), run_loop_(run_loop) {}
+      : run_loop_(run_loop), fetcher_(NULL) {}
 
   // From Fetcher::Handler.
   void OnReceived(Fetcher* fetcher, const char* data, size_t size) OVERRIDE {
     UNREFERENCED_PARAMETER(data);
     UNREFERENCED_PARAMETER(size);
-    CheckFetcher(fetcher);
+    CheckSameFetcher(fetcher);
   }
   void OnDone(Fetcher* fetcher) OVERRIDE {
-    CheckFetcher(fetcher);
-    if (run_loop_) {
-      MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
-    }
+    CheckSameFetcher(fetcher);
+    MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
   }
-  void OnError(Fetcher* fetcher, const std::string& error) OVERRIDE {
-    UNREFERENCED_PARAMETER(error);
-    CheckFetcher(fetcher);
-    if (run_loop_) {
-      MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
-    }
+  void OnError(Fetcher* fetcher, const std::string& error_message) OVERRIDE {
+    CheckSameFetcher(fetcher);
+    error_message_ = error_message;
+    MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
   }
 
   Fetcher* fetcher() const { return fetcher_; }
 
+  const base::optional<std::string>& error_message() const {
+    return error_message_;
+  }
+
  private:
-  void CheckFetcher(Fetcher* fetcher) {
+  void CheckSameFetcher(Fetcher* fetcher) {
     EXPECT_TRUE(fetcher);
     if (fetcher_ == NULL) {
       fetcher_ = fetcher;
@@ -60,8 +61,9 @@
     EXPECT_EQ(fetcher_, fetcher);
   }
 
-  Fetcher* fetcher_;
   base::RunLoop* run_loop_;
+  Fetcher* fetcher_;
+  base::optional<std::string> error_message_;
 };
 
 }  // namespace
@@ -79,27 +81,42 @@
 };
 
 TEST_F(FetcherFactoryTest, InvalidURL) {
-  StubFetcherHandler stub_fetcher_handler(NULL);
+  base::RunLoop run_loop;
+  StubFetcherHandler stub_fetcher_handler(&run_loop);
+
   fetcher_ = fetcher_factory_.CreateFetcher(GURL("invalid-url"),
                                             &stub_fetcher_handler);
-  EXPECT_FALSE(fetcher_.get());
-  EXPECT_FALSE(stub_fetcher_handler.fetcher());
+  EXPECT_TRUE(fetcher_);
+
+  run_loop.Run();
+  EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
+  EXPECT_TRUE(stub_fetcher_handler.error_message().has_engaged());
 }
 
 TEST_F(FetcherFactoryTest, EmptyFileURL) {
-  StubFetcherHandler stub_fetcher_handler(NULL);
+  base::RunLoop run_loop;
+  StubFetcherHandler stub_fetcher_handler(&run_loop);
+
   fetcher_ =
       fetcher_factory_.CreateFetcher(GURL("file:///"), &stub_fetcher_handler);
-  EXPECT_FALSE(fetcher_.get());
-  EXPECT_FALSE(stub_fetcher_handler.fetcher());
+  EXPECT_TRUE(fetcher_);
+
+  run_loop.Run();
+  EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
+  EXPECT_TRUE(stub_fetcher_handler.error_message().has_engaged());
 }
 
 TEST_F(FetcherFactoryTest, FileURLCannotConvertToFilePath) {
-  StubFetcherHandler stub_fetcher_handler(NULL);
+  base::RunLoop run_loop;
+  StubFetcherHandler stub_fetcher_handler(&run_loop);
+
   fetcher_ = fetcher_factory_.CreateFetcher(GURL("file://file.txt"),
                                             &stub_fetcher_handler);
-  EXPECT_FALSE(fetcher_.get());
-  EXPECT_FALSE(stub_fetcher_handler.fetcher());
+  EXPECT_TRUE(fetcher_);
+
+  run_loop.Run();
+  EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
+  EXPECT_TRUE(stub_fetcher_handler.error_message().has_engaged());
 }
 
 TEST_F(FetcherFactoryTest, MultipleCreations) {
diff --git a/src/cobalt/loader/image/image_data_decoder.h b/src/cobalt/loader/image/image_data_decoder.h
index 2d648b5..432942a 100644
--- a/src/cobalt/loader/image/image_data_decoder.h
+++ b/src/cobalt/loader/image/image_data_decoder.h
@@ -46,7 +46,7 @@
   }
 
 #if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
   // Starboard version 3 adds support for hardware accelerated image decoding.
   // In order to make use of this feature, subclasses of ImageDataDecoder may
   // override this method in order to return an SbDecodeTarget, rather than a
@@ -59,7 +59,7 @@
   virtual SbDecodeTarget RetrieveSbDecodeTarget() {
     return kSbDecodeTargetInvalid;
   }
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 #endif  // defined(STARBOARD)
 
   void DecodeChunk(const uint8* data, size_t size);
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 739c523..50f1699 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -29,9 +29,7 @@
 #include "cobalt/loader/image/webp_image_decoder.h"
 #include "net/base/mime_util.h"
 #include "net/http/http_status_code.h"
-#if defined(STARBOARD)
 #include "starboard/image.h"
-#endif
 
 namespace cobalt {
 namespace loader {
@@ -161,15 +159,13 @@
       DCHECK(decoder_);
       if (decoder_->FinishWithSuccess()) {
         if (!decoder_->has_animation()) {
-#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
           SbDecodeTarget target = decoder_->RetrieveSbDecodeTarget();
           if (SbDecodeTargetIsValid(target)) {
             success_callback_.Run(new StaticImage(
                 resource_provider_->CreateImageFromSbDecodeTarget(target)));
           } else  // NOLINT
 #endif
-#endif
           {
             scoped_ptr<render_tree::ImageData> image_data =
                 decoder_->RetrieveImageData();
@@ -271,8 +267,7 @@
 }
 
 namespace {
-#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
 const char* GetMimeTypeFromImageType(ImageDecoder::ImageType image_type) {
   switch (image_type) {
     case ImageDecoder::kImageTypeJPEG:
@@ -331,8 +326,7 @@
   }
   return scoped_ptr<ImageDataDecoder>();
 }
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
-#endif  // defined(STARBOARD)
+#endif  // SB_HAS(GRAPHICS)
 
 scoped_ptr<ImageDataDecoder> CreateImageDecoderFromImageType(
     ImageDecoder::ImageType image_type,
@@ -383,12 +377,10 @@
     image_type_ = DetermineImageType(signature_cache_.data);
   }
 
-#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
   decoder_ =
       MaybeCreateStarboardDecoder(mime_type_, image_type_, resource_provider_);
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
-#endif  // defined(STARBOARD)
+#endif  // SB_HAS(GRAPHICS)
 
   if (!decoder_) {
     decoder_ = CreateImageDecoderFromImageType(image_type_, resource_provider_);
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc
index a0518c0..1183eea 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.cc
+++ b/src/cobalt/loader/image/image_decoder_starboard.cc
@@ -23,7 +23,7 @@
 #include "starboard/decode_target.h"
 #include "starboard/image.h"
 
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_TRUE && SB_HAS(GRAPHICS)
 
 namespace cobalt {
 namespace loader {
@@ -35,11 +35,11 @@
     : ImageDataDecoder(resource_provider),
       mime_type_(mime_type),
       format_(format),
-#if SB_API_VERSION >= 4
+#if SB_TRUE
       provider_(resource_provider->GetSbDecodeTargetGraphicsContextProvider()),
-#else   // #if SB_API_VERSION >= 4
+#else   // #if SB_TRUE
       provider_(resource_provider->GetSbDecodeTargetProvider()),
-#endif  // #if SB_API_VERSION >= 4
+#endif  // #if SB_TRUE
       target_(kSbDecodeTargetInvalid) {
   TRACE_EVENT0("cobalt::loader::image",
                "ImageDecoderStarboard::ImageDecoderStarboard()");
@@ -75,6 +75,6 @@
 }  // namespace loader
 }  // namespace cobalt
 
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif  // SB_TRUE && SB_HAS(GRAPHICS)
 
 #endif  // #if defined(STARBOARD)
diff --git a/src/cobalt/loader/image/image_decoder_starboard.h b/src/cobalt/loader/image/image_decoder_starboard.h
index 39b6cbe..14e386d 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.h
+++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -15,8 +15,6 @@
 #ifndef COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
 #define COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
 
-#if defined(STARBOARD)
-
 #include <string>
 #include <vector>
 
@@ -25,7 +23,7 @@
 #include "cobalt/loader/image/image_data_decoder.h"
 #include "starboard/decode_target.h"
 
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
 
 namespace cobalt {
 namespace loader {
@@ -51,11 +49,7 @@
   const char* mime_type_;
   SbDecodeTargetFormat format_;
   std::vector<uint8> buffer_;
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider* provider_;
-#else   // #if SB_API_VERSION >= 4
-  SbDecodeTargetProvider* provider_;
-#endif  // #if SB_API_VERSION >= 4
   SbDecodeTarget target_;
 };
 
@@ -63,8 +57,6 @@
 }  // namespace loader
 }  // namespace cobalt
 
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
-
-#endif  // defined(STARBOARD)
+#endif  // SB_HAS(GRAPHICS)
 
 #endif  // COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 2fad2ec..6a07f01 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -28,29 +28,31 @@
         'decoder.h',
         'embedded_fetcher.cc',
         'embedded_fetcher.h',
-        'fetcher.cc',
-        'fetcher.h',
+        'error_fetcher.cc',
+        'error_fetcher.h',
         'fetcher_factory.cc',
         'fetcher_factory.h',
+        'fetcher.cc',
+        'fetcher.h',
         'file_fetcher.cc',
         'file_fetcher.h',
         'font/remote_typeface_cache.h',
         'font/typeface_decoder.cc',
         'font/typeface_decoder.h',
+        'image/animated_image_tracker.cc',
+        'image/animated_image_tracker.h',
         'image/animated_webp_image.cc',
         'image/animated_webp_image.h',
         'image/dummy_gif_image_decoder.cc',
         'image/dummy_gif_image_decoder.h',
-        'image/image.h',
         'image/image_cache.h',
         'image/image_data_decoder.cc',
         'image/image_data_decoder.h',
-        'image/image_decoder.cc',
-        'image/image_decoder.h',
         'image/image_decoder_starboard.cc',
         'image/image_decoder_starboard.h',
-        'image/animated_image_tracker.cc',
-        'image/animated_image_tracker.h',
+        'image/image_decoder.cc',
+        'image/image_decoder.h',
+        'image/image.h',
         'image/jpeg_image_decoder.cc',
         'image/jpeg_image_decoder.h',
         'image/png_image_decoder.cc',
@@ -60,11 +62,11 @@
         'image/threaded_image_decoder_proxy.h',
         'image/webp_image_decoder.cc',
         'image/webp_image_decoder.h',
-        'loader.cc',
-        'loader.h',
         'loader_factory.cc',
         'loader_factory.h',
         'loader_types.h',
+        'loader.cc',
+        'loader.h',
         'mesh/mesh_cache.h',
         'mesh/mesh_decoder.cc',
         'mesh/mesh_decoder.h',
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index 00dfa4a..b0a26ff 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -26,10 +26,6 @@
 #include "base/optional.h"
 #include "starboard/drm.h"
 
-#if SB_API_VERSION < 4
-#error "Cobalt media stack requires Starboard 4 or above."
-#endif  // SB_API_VERSION < 4
-
 namespace cobalt {
 namespace media {
 
@@ -97,7 +93,7 @@
             ,
             SessionUpdateKeyStatusesCallback update_key_statuses_callback
 #endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
-            );
+            );  // NOLINT(whitespace/parens)
     void set_id(const std::string& id) { id_ = id; }
     const SessionUpdateRequestGeneratedCallback&
     update_request_generated_callback() const {
@@ -133,7 +129,7 @@
 #if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
       SessionUpdateKeyStatusesCallback session_update_key_statuses_callback
 #endif  // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
-      );
+      );  // NOLINT(whitespace/parens)
 
  private:
   // Stores context of |GenerateSessionUpdateRequest|.
diff --git a/src/cobalt/media/base/shell_media_platform.h b/src/cobalt/media/base/shell_media_platform.h
index 73510fd..1e6c711 100644
--- a/src/cobalt/media/base/shell_media_platform.h
+++ b/src/cobalt/media/base/shell_media_platform.h
@@ -63,14 +63,10 @@
     return NULL;
   }
 
-#if SB_API_VERSION >= 4
   virtual SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() {
     return NULL;
   }
-#elif SB_API_VERSION >= 3
-  virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
-#endif  // SB_API_VERSION >= 3
 
   // This function is called before the decoder buffer leaves the demuxer and
   // is being sent to the media pipeline for decrypting and decoding. The
diff --git a/src/cobalt/media/base/shell_video_frame_provider.h b/src/cobalt/media/base/shell_video_frame_provider.h
index a5bd969..e2b1569 100644
--- a/src/cobalt/media/base/shell_video_frame_provider.h
+++ b/src/cobalt/media/base/shell_video_frame_provider.h
@@ -49,9 +49,7 @@
 
   ShellVideoFrameProvider() : output_mode_(kOutputModeInvalid) {}
 
-#if SB_API_VERSION >= 4
   typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
-#endif  // SB_API_VERSION >= 4
 
   scoped_refptr<VideoFrame> GetCurrentFrame() { return NULL; }
 
@@ -65,7 +63,6 @@
     return output_mode_;
   }
 
-#if SB_API_VERSION >= 4
   // For Starboard platforms that have a decode-to-texture player, we enable
   // this ShellVideoFrameProvider to act as a bridge for Cobalt code to query
   // for the current SbDecodeTarget.  In effect, we bypass all of
@@ -91,15 +88,12 @@
       return get_current_sb_decode_target_function_.Run();
     }
   }
-#endif  // SB_API_VERSION >= 4
 
  private:
   mutable base::Lock lock_;
 
   OutputMode output_mode_;
-#if SB_API_VERSION >= 4
   GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
-#endif  // SB_API_VERSION >= 4
 
   DISALLOW_COPY_AND_ASSIGN(ShellVideoFrameProvider);
 };
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index cb8749c..9cf382a 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -96,11 +96,9 @@
   DCHECK(host_);
   DCHECK(set_bounds_helper_);
 
-#if SB_API_VERSION >= 4
   output_mode_ = ComputeSbPlayerOutputMode(
       MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
       prefer_decode_to_texture);
-#endif  // SB_API_VERSION >= 4
 
   CreatePlayer();
 
@@ -118,11 +116,9 @@
 
   ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
       ShellVideoFrameProvider::kOutputModeInvalid);
-#if SB_API_VERSION >= 4
   ShellMediaPlatform::Instance()
       ->GetVideoFrameProvider()
       ->ResetGetCurrentSbDecodeTargetFunction();
-#endif  // SB_API_VERSION >= 4
 
   if (SbPlayerIsValid(player_)) {
     SbPlayerDestroy(player_);
@@ -211,13 +207,9 @@
   }
 
   DCHECK(SbPlayerIsValid(player_));
-#if SB_API_VERSION >= 4
   const int kZIndex = 0;
   SbPlayerSetBounds(player_, kZIndex, rect.x(), rect.y(), rect.width(),
                     rect.height());
-#else   // SB_API_VERSION >= 4
-  SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
-#endif  // SB_API_VERSION >= 4
 }
 
 void StarboardPlayer::PrepareForSeek() {
@@ -231,11 +223,7 @@
   }
 
   ++ticket_;
-#if SB_API_VERSION < 4
-  SbPlayerSetPause(player_, true);
-#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.f);
-#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::Seek(base::TimeDelta time) {
@@ -260,11 +248,7 @@
   ++ticket_;
   SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
   seek_pending_ = false;
-#if SB_API_VERSION < 4
-  SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else  // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::SetVolume(float volume) {
@@ -295,11 +279,7 @@
     return;
   }
 
-#if SB_API_VERSION < 4
-  SbPlayerSetPause(player_, playback_rate == 0.0);
-#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, playback_rate);
-#endif  // SB_API_VERSION < 4
 }
 
 void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
@@ -349,11 +329,7 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
-#if SB_API_VERSION < 4
-  SbPlayerSetPause(player_, true);
-#else   // SB_API_VERSION < 4
   SbPlayerSetPlaybackRate(player_, 0.0);
-#endif  // SB_API_VERSION < 4
 
   base::AutoLock auto_lock(lock_);
 
@@ -391,7 +367,6 @@
 }
 
 namespace {
-#if SB_API_VERSION >= 4
 ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
     SbPlayerOutputMode output_mode) {
   switch (output_mode) {
@@ -406,7 +381,6 @@
   NOTREACHED();
   return ShellVideoFrameProvider::kOutputModeInvalid;
 }
-#endif  // #if SB_API_VERSION >= 4
 }  // namespace
 
 void StarboardPlayer::CreatePlayer() {
@@ -446,26 +420,17 @@
   SbMediaVideoCodec video_codec =
       MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
 
-#if SB_API_VERSION >= 4
   DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
-#endif  // SB_API_VERSION >= 4
 
-  player_ = SbPlayerCreate(
-      window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
-      &audio_header, &StarboardPlayer::DeallocateSampleCB,
-      &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION >= 4
-      ,
-      output_mode_,
-      ShellMediaPlatform::Instance()->GetSbDecodeTargetGraphicsContextProvider()
-#elif SB_API_VERSION >= 3
-      ,
-      ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider()  // provider
-#endif  // SB_API_VERSION >= 3
-          );
+  player_ = SbPlayerCreate(window_, video_codec, audio_codec,
+                           SB_PLAYER_NO_DURATION, drm_system_, &audio_header,
+                           &StarboardPlayer::DeallocateSampleCB,
+                           &StarboardPlayer::DecoderStatusCB,
+                           &StarboardPlayer::PlayerStatusCB, this, output_mode_,
+                           ShellMediaPlatform::Instance()
+                               ->GetSbDecodeTargetGraphicsContextProvider());
   DCHECK(SbPlayerIsValid(player_));
 
-#if SB_API_VERSION >= 4
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     // If the player is setup to decode to texture, then provide Cobalt with
     // a method of querying that texture.
@@ -477,10 +442,6 @@
   }
   ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
       ToVideoFrameProviderOutputMode(output_mode_));
-#else   // SB_API_VERSION >= 4
-  ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
-      ShellVideoFrameProvider::kOutputModePunchOut);
-#endif  // SB_API_VERSION >= 4
 
   set_bounds_helper_->SetPlayer(this);
 
@@ -490,14 +451,13 @@
   }
 }
 
-#if SB_API_VERSION >= 4
 SbDecodeTarget StarboardPlayer::GetCurrentSbDecodeTarget() {
   return SbPlayerGetCurrentFrame(player_);
 }
+
 SbPlayerOutputMode StarboardPlayer::GetSbPlayerOutputMode() {
   return output_mode_;
 }
-#endif  // SB_API_VERSION >= 4
 
 void StarboardPlayer::ClearDecoderBufferCache() {
   DCHECK(message_loop_->BelongsToCurrentThread());
@@ -572,11 +532,7 @@
     }
     SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
     SetVolume(volume_);
-#if SB_API_VERSION < 4
-    SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else  // SB_API_VERSION < 4
     SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif  // SB_API_VERSION < 4
     return;
   }
   host_->OnPlayerStatus(state);
@@ -628,7 +584,6 @@
                  helper->callback_helper_, sample_buffer));
 }
 
-#if SB_API_VERSION >= 4
 // static
 SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
     SbMediaVideoCodec codec, SbDrmSystem drm_system,
@@ -658,7 +613,6 @@
 
   return output_mode;
 }
-#endif  // SB_API_VERSION >= 4
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index d2c02f1..ad1a31a 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -73,10 +73,8 @@
   void Suspend();
   void Resume();
 
-#if SB_API_VERSION >= 4
   SbDecodeTarget GetCurrentSbDecodeTarget();
   SbPlayerOutputMode GetSbPlayerOutputMode();
-#endif  // SB_API_VERSION >= 4
 
  private:
   enum State {
@@ -127,13 +125,11 @@
   static void DeallocateSampleCB(SbPlayer player, void* context,
                                  const void* sample_buffer);
 
-#if SB_API_VERSION >= 4
   // Returns the output mode that should be used for a video with the given
   // specifications.
   static SbPlayerOutputMode ComputeSbPlayerOutputMode(
       SbMediaVideoCodec codec, SbDrmSystem drm_system,
       bool prefer_decode_to_texture);
-#endif  // SB_API_VERSION >= 4
 
   // The following variables are initialized in the ctor and never changed.
   const scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -171,10 +167,8 @@
   uint32 cached_video_frames_dropped_;
   base::TimeDelta preroll_timestamp_;
 
-#if SB_API_VERSION >= 4
   // Keep track of the output mode we are supposed to output to.
   SbPlayerOutputMode output_mode_;
-#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace media
diff --git a/src/cobalt/media/base/starboard_utils.cc b/src/cobalt/media/base/starboard_utils.cc
index 72c4c72..bc6905a 100644
--- a/src/cobalt/media/base/starboard_utils.cc
+++ b/src/cobalt/media/base/starboard_utils.cc
@@ -128,7 +128,6 @@
   }
 }
 
-#if SB_API_VERSION >= 4
 // Ensure that the enums in starboard/media.h match enums in gfx::ColorSpace.
 #define ENUM_EQ(a, b) \
   COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums)
@@ -279,7 +278,6 @@
 
   return sb_media_color_metadata;
 }
-#endif  // SB_API_VERSION >= 3
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/starboard_utils.h b/src/cobalt/media/base/starboard_utils.h
index 0cb43c1..04196b5 100644
--- a/src/cobalt/media/base/starboard_utils.h
+++ b/src/cobalt/media/base/starboard_utils.h
@@ -39,10 +39,8 @@
                        SbDrmSampleInfo* drm_info,
                        SbDrmSubSampleMapping* subsample_mapping);
 
-#if SB_API_VERSION >= 4
 SbMediaColorMetadata MediaToSbMediaColorMetadata(
     const WebMColorMetadata& webm_color_metadata);
-#endif
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index b3b5cb6..beee2aa 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -194,7 +194,7 @@
  public:
   virtual void NetworkStateChanged() = 0;
   virtual void ReadyStateChanged() = 0;
-  virtual void TimeChanged() = 0;
+  virtual void TimeChanged(bool eos_played) = 0;
   virtual void DurationChanged() = 0;
   virtual void OutputModeChanged() = 0;
   virtual void PlaybackStateChanged() = 0;
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index bd0d14b..c8cf80a 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -590,7 +590,8 @@
   // Update our paused time.
   if (state_.paused) state_.paused_time = pipeline_->GetMediaTime();
 
-  GetClient()->TimeChanged();
+  const bool eos_played = false;
+  GetClient()->TimeChanged(eos_played);
 }
 
 void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) {
@@ -599,7 +600,9 @@
     OnPipelineError(status);
     return;
   }
-  GetClient()->TimeChanged();
+
+  const bool eos_played = true;
+  GetClient()->TimeChanged(eos_played);
 }
 
 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index 58618a2..1909a5c 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -35,7 +35,7 @@
   // WebMediaPlayerClient methods
   void NetworkStateChanged() OVERRIDE {}
   void ReadyStateChanged() OVERRIDE {}
-  void TimeChanged() OVERRIDE {}
+  void TimeChanged(bool) OVERRIDE {}
   void DurationChanged() OVERRIDE {}
   void OutputModeChanged() OVERRIDE {}
   void PlaybackStateChanged() OVERRIDE {}
@@ -48,9 +48,8 @@
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
   std::string SourceURL() const OVERRIDE { return ""; }
 #if defined(COBALT_MEDIA_SOURCE_2016)
-  void EncryptedMediaInitDataEncountered(EmeInitDataType init_data_type,
-                                         const unsigned char* init_data,
-                                         unsigned init_data_length) OVERRIDE {}
+  void EncryptedMediaInitDataEncountered(EmeInitDataType, const unsigned char*,
+                                         unsigned) OVERRIDE {}
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 };
 
diff --git a/src/cobalt/media/shell_media_platform_starboard.h b/src/cobalt/media/shell_media_platform_starboard.h
index 0119293..8bb5c4c 100644
--- a/src/cobalt/media/shell_media_platform_starboard.h
+++ b/src/cobalt/media/shell_media_platform_starboard.h
@@ -44,7 +44,6 @@
     return video_frame_provider_;
   }
 
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
 #if SB_HAS(GRAPHICS)
@@ -53,15 +52,6 @@
     return NULL;
 #endif  // SB_HAS(GRAPHICS)
   }
-#elif SB_API_VERSION >= 3
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE {
-#if SB_HAS(GRAPHICS)
-    return resource_provider_->GetSbDecodeTargetProvider();
-#else   // SB_HAS(GRAPHICS)
-    return NULL;
-#endif  // SB_HAS(GRAPHICS)
-  }
-#endif  // SB_API_VERSION >= 4
 
   void Suspend() OVERRIDE { resource_provider_ = NULL; }
   void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
@@ -134,7 +124,6 @@
       const scoped_refptr<DecoderBuffer>& buffer) OVERRIDE;
   bool IsOutputProtected() OVERRIDE;
 
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
 #if SB_HAS(GRAPHICS)
@@ -143,15 +132,6 @@
     return NULL;
 #endif  // SB_HAS(GRAPHICS)
   }
-#elif SB_API_VERSION >= 3
-  virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() {
-#if SB_HAS(GRAPHICS)
-    return resource_provider_->GetSbDecodeTargetProvider();
-#else   // SB_HAS(GRAPHICS)
-    return NULL;
-#endif  // SB_HAS(GRAPHICS)
-  }
-#endif  // SB_API_VERSION >= 4
 
   void Suspend() OVERRIDE { resource_provider_ = NULL; }
   void Resume(
diff --git a/src/cobalt/network/local_network.cc b/src/cobalt/network/local_network.cc
index 725f3e4..964a54e 100644
--- a/src/cobalt/network/local_network.cc
+++ b/src/cobalt/network/local_network.cc
@@ -23,7 +23,6 @@
 
 namespace {
 
-#if SB_API_VERSION >= 4
 bool CompareNBytesOfAddress(const SbSocketAddress& ip,
                             const SbSocketAddress& source_address,
                             const SbSocketAddress& netmask,
@@ -57,22 +56,15 @@
   }
 }
 
-#endif  // SB_API_VERSION >= 4
-
 }  // namespace
 
 bool IsIPInLocalNetwork(const SbSocketAddress& destination) {
-#if SB_API_VERSION >= 4
   SbSocketAddress source_address;
   SbSocketAddress netmask;
   if (!(SbSocketGetInterfaceAddress(&destination, &source_address, &netmask))) {
     return false;
   }
   return IsLocalIP(destination, source_address, netmask);
-#else
-  UNREFERENCED_PARAMETER(destination);
-  return false;
-#endif
 }
 
 bool IsIPInPrivateRange(const SbSocketAddress& ip) {
diff --git a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
index c82b19d..3487dd7 100644
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
@@ -41,9 +41,7 @@
     case kSbSystemDeviceTypeOverTheTopBox:
     case kSbSystemDeviceTypeSetTopBox:
     case kSbSystemDeviceTypeTV:
-#if SB_API_VERSION >= 4
     case kSbSystemDeviceTypeAndroidTV:
-#endif  // SB_API_VERSION >= 4
       return true;
     case kSbSystemDeviceTypeDesktopPC:
     case kSbSystemDeviceTypeUnknown:
@@ -107,11 +105,9 @@
       case kSbSystemDeviceTypeTV:
         youtube_tv_info_->device_type = YouTubeTVInfo::kTV;
         break;
-#if SB_API_VERSION >= 4
       case kSbSystemDeviceTypeAndroidTV:
         youtube_tv_info_->device_type = YouTubeTVInfo::kAndroidTV;
         break;
-#endif  // SB_API_VERSION >= 4
       case kSbSystemDeviceTypeDesktopPC:
       default:
         youtube_tv_info_->device_type = YouTubeTVInfo::kInvalidDeviceType;
diff --git a/src/cobalt/network/user_agent_string_factory.cc b/src/cobalt/network/user_agent_string_factory.cc
index 7ad310b..8bd7815 100644
--- a/src/cobalt/network/user_agent_string_factory.cc
+++ b/src/cobalt/network/user_agent_string_factory.cc
@@ -39,6 +39,28 @@
 #error Unknown build configuration.
 #endif
 
+struct SanitizeReplacements {
+  const char* replace_chars;
+  const char* replace_with;
+} kSanitizeReplacements[] = {
+  { ",", u8"\uFF0C" },  // fullwidth comma
+  { "_", u8"\u2E0F" },  // paragraphos
+  { "/", u8"\u2215" },  // division slash
+  { "(", u8"\uFF08" },  // fullwidth left paren
+  { ")", u8"\uFF09" },  // fullwidth right paren
+};
+
+// Replace reserved characters with Unicode homoglyphs
+std::string Sanitize(const std::string& str) {
+  std::string clean(str);
+  for (size_t i=0; i < arraysize(kSanitizeReplacements); i++) {
+    const SanitizeReplacements* replacement = kSanitizeReplacements + i;
+    ReplaceChars(
+        clean, replacement->replace_chars, replacement->replace_with, &clean);
+  }
+  return clean;
+}
+
 }  // namespace
 
 std::string UserAgentStringFactory::CreateUserAgentString() {
@@ -66,11 +88,12 @@
   if (youtube_tv_info_) {
     base::StringAppendF(
         &user_agent, ", %s_%s_%s/%s (%s, %s, %s)",
-        youtube_tv_info_->network_operator.value_or("").c_str(),
+        Sanitize(youtube_tv_info_->network_operator.value_or("")).c_str(),
         CreateDeviceTypeString().c_str(),
-        youtube_tv_info_->chipset_model_number.value_or("").c_str(),
-        youtube_tv_info_->firmware_version.value_or("").c_str(),
-        youtube_tv_info_->brand.c_str(), youtube_tv_info_->model.c_str(),
+        Sanitize(youtube_tv_info_->chipset_model_number.value_or("")).c_str(),
+        Sanitize(youtube_tv_info_->firmware_version.value_or("")).c_str(),
+        Sanitize(youtube_tv_info_->brand).c_str(),
+        Sanitize(youtube_tv_info_->model).c_str(),
         CreateConnectionTypeString().c_str());
   }
 
@@ -112,10 +135,8 @@
       return "STB";
     case YouTubeTVInfo::kTV:
       return "TV";
-#if SB_API_VERSION >= 4
     case YouTubeTVInfo::kAndroidTV:
       return "ATV";
-#endif  // SB_API_VERSION >= 4
     case YouTubeTVInfo::kInvalidDeviceType:
     default:
       NOTREACHED();
diff --git a/src/cobalt/network/user_agent_string_factory.h b/src/cobalt/network/user_agent_string_factory.h
index 5869855..7f7686f 100644
--- a/src/cobalt/network/user_agent_string_factory.h
+++ b/src/cobalt/network/user_agent_string_factory.h
@@ -47,9 +47,7 @@
   struct YouTubeTVInfo {
     enum DeviceType {
       kInvalidDeviceType,
-#if SB_API_VERSION >= 4
       kAndroidTV,
-#endif  // SB_API_VERSION >= 4
       kBlueRayDiskPlayer,
       kGameConsole,
       kOverTheTopBox,
diff --git a/src/cobalt/network/user_agent_string_factory_test.cc b/src/cobalt/network/user_agent_string_factory_test.cc
index 6e3a669..80171b8 100644
--- a/src/cobalt/network/user_agent_string_factory_test.cc
+++ b/src/cobalt/network/user_agent_string_factory_test.cc
@@ -82,23 +82,35 @@
 class UserAgentStringFactoryWithYouTubeTVInfo : public UserAgentStringFactory {
  public:
   UserAgentStringFactoryWithYouTubeTVInfo() {
+    // There are deliberately a variety of underscores, commas, slashes, and
+    // parentheses in the strings below to ensure they get sanitized.
     os_name_and_version_ = "GLaDOS 3.11";
     youtube_tv_info_ = YouTubeTVInfo();
-    youtube_tv_info_->network_operator = "ApertureLaboratories";
+    youtube_tv_info_->network_operator = "Aperture_Science_Innovators";
     youtube_tv_info_->device_type = YouTubeTVInfo::kOverTheTopBox;
-    youtube_tv_info_->chipset_model_number = "Wheatley";
-    youtube_tv_info_->firmware_version = "0.01";
-    youtube_tv_info_->brand = "Aperture Science";
+    youtube_tv_info_->chipset_model_number = "P-body/Orange_Atlas/Blue";
+    youtube_tv_info_->firmware_version = "0,01";
+    youtube_tv_info_->brand = "Aperture Science (Labs)";
     youtube_tv_info_->model = "GLaDOS";
   }
 };
 
+// Look-alike replacements expected from sanitizing fields
+#define COMMA  u8"\uFF0C"  // fullwidth comma
+#define UNDER  u8"\u2E0F"  // paragraphos
+#define SLASH  u8"\u2215"  // division slash
+#define LPAREN u8"\uFF08"  // fullwidth left paren
+#define RPAREN u8"\uFF09"  // fullwidth right paren
+
 TEST(UserAgentStringFactoryTest, WithYouTubeTVInfo) {
   std::string user_agent_string =
       UserAgentStringFactoryWithYouTubeTVInfo().CreateUserAgentString();
-  EXPECT_NE(std::string::npos,
-            user_agent_string.find("ApertureLaboratories_OTT_Wheatley/0.01 "
-                                   "(Aperture Science, GLaDOS, )"));
+  const char* tv_info_str =
+      "Aperture" UNDER "Science" UNDER "Innovators"
+      "_OTT_"
+      "P-body" SLASH "Orange" UNDER "Atlas" SLASH "Blue"
+      "/0" COMMA "01 (Aperture Science " LPAREN "Labs" RPAREN ", GLaDOS, )";
+  EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
 }
 
 class UserAgentStringFactoryWithWiredConnection
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
index 1d13b6c..d30b9fc 100644
--- a/src/cobalt/render_tree/mock_resource_provider.h
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -94,23 +94,17 @@
 
 #if SB_HAS(GRAPHICS)
 
-#if SB_API_VERSION >= 3
   scoped_refptr<Image> CreateImageFromSbDecodeTarget(SbDecodeTarget target) {
     UNREFERENCED_PARAMETER(target);
     return NULL;
   }
 
   bool SupportsSbDecodeTarget() { return false; }
-#endif  // SB_API_VERSION >= 3
 
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() {
     return NULL;
   }
-#elif SB_API_VERSION >= 3
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
-#endif  // SB_API_VERSION >= 4
 
 #endif  // SB_HAS(GRAPHICS)
 
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index 4a648d1..acbbb21 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -27,9 +27,7 @@
 #include "cobalt/render_tree/mesh.h"
 #include "cobalt/render_tree/node.h"
 #include "cobalt/render_tree/typeface.h"
-#if defined(STARBOARD)
 #include "starboard/decode_target.h"
-#endif  // defined(STARBOARD)
 
 namespace cobalt {
 namespace render_tree {
@@ -76,7 +74,6 @@
       scoped_ptr<ImageData> pixel_data) = 0;
 
 #if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 3
   // This function will consume an SbDecodeTarget object produced by
   // SbDecodeTargetCreate(), wrap it in a render_tree::Image that can be used
   // in a render tree, and return it to the caller.
@@ -85,19 +82,12 @@
 
   // Whether SbDecodeTargetIsSupported or not.
   virtual bool SupportsSbDecodeTarget() = 0;
-#endif  // SB_API_VERSION >= 3
 
-#if SB_API_VERSION >= 4
   // Return the SbDecodeTargetGraphicsContextProvider associated with the
   // ResourceProvider, if it exists.  Returns NULL if SbDecodeTarget is not
   // supported.
   virtual SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() = 0;
-#elif SB_API_VERSION >= 3
-  // Return the associated SbDecodeTargetProvider with the ResourceProvider,
-  // if it exists.  Returns NULL if SbDecodeTarget is not supported.
-  virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() = 0;
-#endif  // SB__API_VERSION >= 4
 #endif  // SB_HAS(GRAPHICS)
 
   // Returns a raw chunk of memory that can later be passed into a function like
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index aee3f69..397d8cb 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -247,30 +247,22 @@
     return make_scoped_refptr(new ImageStub(skia_source_data.Pass()));
   }
 
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
   scoped_refptr<Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE {
     NOTREACHED();
-#if SB_API_VERSION < 4
-    SbDecodeTargetDestroy(decode_target);
-#else   // 4
     SbDecodeTargetRelease(decode_target);
-#endif  // 4
     return NULL;
   }
 
   bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 
 #if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
     return NULL;
   }
-#elif SB_API_VERSION >= 3
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-#endif  // SB_API_VERSION >= 4
 #endif  // SB_HAS(GRAPHICS)
 
   scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
diff --git a/src/cobalt/render_tree/rounded_corners.cc b/src/cobalt/render_tree/rounded_corners.cc
index b0a3331..7eacb19 100644
--- a/src/cobalt/render_tree/rounded_corners.cc
+++ b/src/cobalt/render_tree/rounded_corners.cc
@@ -17,6 +17,17 @@
 namespace cobalt {
 namespace render_tree {
 
+RoundedCorners RoundedCorners::Scale(float sx, float sy) const {
+  return RoundedCorners(RoundedCorner(top_left.horizontal * sx,
+                                      top_left.vertical * sy),
+                        RoundedCorner(top_right.horizontal * sx,
+                                      top_right.vertical * sy),
+                        RoundedCorner(bottom_right.horizontal * sx,
+                                      bottom_right.vertical * sy),
+                        RoundedCorner(bottom_left.horizontal * sx,
+                                      bottom_left.vertical * sy));
+}
+
 RoundedCorners RoundedCorners::Normalize(const math::RectF& rect) const {
   float scale = 1.0f;
   float size;
@@ -48,14 +59,7 @@
     scale = std::min(rect.height() / size, scale);
   }
 
-  return RoundedCorners(RoundedCorner(top_left.horizontal * scale,
-                                      top_left.vertical * scale),
-                        RoundedCorner(top_right.horizontal * scale,
-                                      top_right.vertical * scale),
-                        RoundedCorner(bottom_right.horizontal * scale,
-                                      bottom_right.vertical * scale),
-                        RoundedCorner(bottom_left.horizontal * scale,
-                                      bottom_left.vertical * scale));
+  return Scale(scale, scale);
 }
 
 bool RoundedCorners::IsNormalized(const math::RectF& rect) const {
diff --git a/src/cobalt/render_tree/rounded_corners.h b/src/cobalt/render_tree/rounded_corners.h
index cf1d03e..7646f65 100644
--- a/src/cobalt/render_tree/rounded_corners.h
+++ b/src/cobalt/render_tree/rounded_corners.h
@@ -91,6 +91,8 @@
     return Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
   }
 
+  RoundedCorners Scale(float sx, float sy) const;
+
   // Ensure the rounded corners' radii do not exceed the length of the
   // corresponding edge of the given rect.
   RoundedCorners Normalize(const math::RectF& rect) const;
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
new file mode 100644
index 0000000..168df28
--- /dev/null
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
@@ -0,0 +1,33 @@
+#version 100

+precision mediump float;

+uniform sampler2D uSampler0_Stage0;

+uniform sampler2D uSampler0_Stage1;

+uniform vec4 uTexDom_Stage1;

+varying vec4 vColor;

+varying vec2 vMatrixCoord_Stage0;

+varying vec2 vMatrixCoord_Stage1;

+

+void main() 

+{

+  vec4 output_Stage0;

+  {

+    // Stage 0: Texture

+    output_Stage0 =

+        (vColor * texture2D(uSampler0_Stage0, vMatrixCoord_Stage0).aaaa);

+  }

+  vec4 output_Stage1;

+  {

+    // Stage 1: TextureDomain

+    {

+      bvec4 outside;

+      outside.xy = lessThan(vMatrixCoord_Stage1, uTexDom_Stage1.xy);

+      outside.zw = greaterThan(vMatrixCoord_Stage1, uTexDom_Stage1.zw);

+      output_Stage1 =

+          any(outside) ?

+              vec4(0.0, 0.0, 0.0, 0.0) :

+              (output_Stage0 * texture2D(uSampler0_Stage1,

+                                         vMatrixCoord_Stage1));

+    }

+  }

+  gl_FragColor = output_Stage1;

+}

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi b/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
index 1997d06..d38588c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
+++ b/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
@@ -78,6 +78,7 @@
       'fragment_skia_texture_domain.glsl',
       'fragment_skia_texture_domain_masked_texture_domain.glsl',
       'fragment_skia_texture_masked_texture.glsl',
+      'fragment_skia_texture_masked_texture_domain.glsl',
       'fragment_skia_yuv.glsl',
       'fragment_textured_vbo_rgba.glsl',
       'fragment_textured_vbo_uyvy_1plane.glsl',
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 1da3c9e..e66f6e5 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -41,9 +41,7 @@
     : device_(device),
       skia_resource_provider_(skia_resource_provider),
       submit_offscreen_callback_(submit_offscreen_callback) {
-#if SB_API_VERSION >= 4
   decode_target_graphics_context_provider_.device = device;
-#endif  // SB_API_VERSION >= 4
 }
 
 bool ResourceProvider::PixelFormatSupported(PixelFormat pixel_format) {
@@ -83,25 +81,10 @@
   return make_scoped_refptr(new SinglePlaneImage(blitter_source_data.Pass()));
 }
 
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
 
 scoped_refptr<render_tree::Image>
 ResourceProvider::CreateImageFromSbDecodeTarget(SbDecodeTarget decode_target) {
-#if SB_API_VERSION < 4
-  SbDecodeTargetFormat format = SbDecodeTargetGetFormat(decode_target);
-  if (format == kSbDecodeTargetFormat1PlaneRGBA) {
-    SbBlitterSurface surface =
-        SbDecodeTargetGetPlane(decode_target, kSbDecodeTargetPlaneRGBA);
-    DCHECK(SbBlitterIsSurfaceValid(surface));
-    bool is_opaque = SbDecodeTargetIsOpaque(decode_target);
-
-    // Now that we have the surface it contained, we are free to delete
-    // |decode_target|.
-    SbDecodeTargetDestroy(decode_target);
-    return make_scoped_refptr(
-        new SinglePlaneImage(surface, is_opaque, base::Closure()));
-  }
-#else   // SB_API_VERSION < 4
   SbDecodeTargetInfo info;
   SbMemorySet(&info, 0, sizeof(info));
   CHECK(SbDecodeTargetGetInfo(decode_target, &info));
@@ -121,19 +104,14 @@
         plane.surface, info.is_opaque,
         base::Bind(&SbDecodeTargetRelease, decode_target)));
   }
-#endif  // SB_API_VERSION < 4
 
   NOTREACHED()
       << "Only format kSbDecodeTargetFormat1PlaneRGBA is currently supported.";
-#if SB_API_VERSION < 4
-  SbDecodeTargetDestroy(decode_target);
-#else   // SB_API_VERSION < 4
   SbDecodeTargetRelease(decode_target);
-#endif  // SB_API_VERSION < 4
   return NULL;
 }
 
-#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 
 scoped_ptr<render_tree::RawImageMemory>
 ResourceProvider::AllocateRawImageMemory(size_t size_in_bytes,
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index 3f01c64..92315fe 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -43,21 +43,15 @@
 
   void Finish() OVERRIDE {}
 
-#if SB_API_VERSION >= 3
   scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE;
 
   bool SupportsSbDecodeTarget() OVERRIDE { return true; }
-#endif  // SB_API_VERSION >= 3
 
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider*
   GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
     return &decode_target_graphics_context_provider_;
   }
-#elif SB_API_VERSION >= 3
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-#endif  // SB_API_VERSION >= 4
 
   bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
   bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
@@ -123,10 +117,8 @@
 
   SubmitOffscreenCallback submit_offscreen_callback_;
 
-#if SB_API_VERSION >= 4
   SbDecodeTargetGraphicsContextProvider
       decode_target_graphics_context_provider_;
-#endif  // SB_API_VERSION >= 4
 };
 
 }  // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.cc b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
index c2084cf..24ec5d0 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <limits>
 
+#include "cobalt/math/transform_2d.h"
 #include "cobalt/renderer/backend/egl/utils.h"
 
 namespace cobalt {
@@ -24,6 +25,34 @@
 namespace rasterizer {
 namespace egl {
 
+namespace {
+// To accommodate large radius values for rounded corners while using mediump
+// floats in the fragment shader, scale 1 / radius.xy by kRCornerGradientScale.
+// The fragment shader will take this into account.
+const float kRCornerGradientScale = 16.0f;
+
+// Get the midpoint of the given rounded rect. A normalized rounded rect
+// should have at least one point which the corners do not cross.
+math::PointF GetRRectCenter(const math::RectF& rect,
+    const render_tree::RoundedCorners& corners) {
+  return math::PointF(
+      0.5f * (rect.x() + std::max(corners.top_left.horizontal,
+                                  corners.bottom_left.horizontal) +
+              rect.right() - std::max(corners.top_right.horizontal,
+                                      corners.bottom_right.horizontal)),
+      0.5f * (rect.y() + std::max(corners.top_left.vertical,
+                                  corners.top_right.vertical) +
+              rect.bottom() - std::max(corners.bottom_left.vertical,
+                                       corners.bottom_right.vertical)));
+}
+
+math::PointF ClampToBounds(const math::RectF& bounds, float x, float y) {
+  return math::PointF(
+      std::min(std::max(bounds.x(), x), bounds.right()),
+      std::min(std::max(bounds.y(), y), bounds.bottom()));
+}
+}  // namespace
+
 DrawObject::BaseState::BaseState()
     : transform(math::Matrix3F::Identity()),
       scissor(0, 0,
@@ -38,9 +67,35 @@
       rounded_scissor_corners(other.rounded_scissor_corners),
       opacity(other.opacity) {}
 
+DrawObject::RCorner::RCorner(const float (&position)[2], const RCorner& init)
+    : x((position[0] - init.x) * init.rx),
+      y((position[1] - init.y) * init.ry),
+      rx(init.rx * kRCornerGradientScale),
+      ry(init.ry * kRCornerGradientScale) {}
+
 DrawObject::DrawObject(const BaseState& base_state)
     : base_state_(base_state) {}
 
+math::Vector2dF DrawObject::GetScale() const {
+  float m00 = base_state_.transform(0, 0);
+  float m01 = base_state_.transform(0, 1);
+  float m10 = base_state_.transform(1, 0);
+  float m11 = base_state_.transform(1, 1);
+  return math::Vector2dF(std::sqrt(m00 * m00 + m10 * m10),
+                         std::sqrt(m01 * m01 + m11 * m11));
+}
+
+math::Vector2dF DrawObject::RemoveScaleFromTransform() {
+  // Avoid division by zero.
+  const float kEpsilon = 0.00001f;
+
+  math::Vector2dF scale = GetScale();
+  base_state_.transform = base_state_.transform *
+      math::ScaleMatrix(1.0f / std::max(scale.x(), kEpsilon),
+                        1.0f / std::max(scale.y(), kEpsilon));
+  return scale;
+}
+
 // static
 uint32_t DrawObject::GetGLRGBA(float r, float g, float b, float a) {
   // Ensure color bytes represent RGBA, regardless of endianness.
@@ -56,74 +111,130 @@
 }
 
 // static
-void DrawObject::SetRRectUniforms(GLint rect_uniform, GLint corners_uniform,
-    const math::RectF& rect, const render_tree::RoundedCorners& corners,
-    float inset) {
-  math::RectF inset_rect(rect);
-  inset_rect.Inset(inset, inset);
-  render_tree::RoundedCorners inset_corners =
-      corners.Inset(inset, inset, inset, inset);
+void DrawObject::GetRRectAttributes(const math::RectF& bounds,
+    math::RectF rect, render_tree::RoundedCorners corners,
+    RRectAttributes (&out_attributes)[4]) {
+  GetRCornerValues(&rect, &corners, out_attributes);
 
-  // Tweak corners that are square-ish so they have values that play
-  // nicely with the shader. Interpolating x^2 / a^2 + y^2 / b^2 does not
-  // work well when |a| or |b| are very small.
-  if (inset_corners.top_left.horizontal <= 0.5f ||
-      inset_corners.top_left.vertical <= 0.5f) {
-    inset_corners.top_left.horizontal = 0.0f;
-    inset_corners.top_left.vertical = 0.0f;
-  }
-  if (inset_corners.top_right.horizontal <= 0.5f ||
-      inset_corners.top_right.vertical <= 0.5f) {
-    inset_corners.top_right.horizontal = 0.0f;
-    inset_corners.top_right.vertical = 0.0f;
-  }
-  if (inset_corners.bottom_left.horizontal <= 0.5f ||
-      inset_corners.bottom_left.vertical <= 0.5f) {
-    inset_corners.bottom_left.horizontal = 0.0f;
-    inset_corners.bottom_left.vertical = 0.0f;
-  }
-  if (inset_corners.bottom_right.horizontal <= 0.5f ||
-      inset_corners.bottom_right.vertical <= 0.5f) {
-    inset_corners.bottom_right.horizontal = 0.0f;
-    inset_corners.bottom_right.vertical = 0.0f;
-  }
+  // Calculate the bounds for each patch. Four patches will be used to cover
+  // the entire bounded area.
+  math::PointF center = GetRRectCenter(rect, corners);
+  center = ClampToBounds(bounds, center.x(), center.y());
 
+  out_attributes[0].bounds.SetRect(bounds.x(), bounds.y(),
+      center.x() - bounds.x(), center.y() - bounds.y());
+  out_attributes[1].bounds.SetRect(center.x(), bounds.y(),
+      bounds.right() - center.x(), center.y() - bounds.y());
+  out_attributes[2].bounds.SetRect(bounds.x(), center.y(),
+      center.x() - bounds.x(), bounds.bottom() - center.y());
+  out_attributes[3].bounds.SetRect(center.x(), center.y(),
+      bounds.right() - center.x(), bounds.bottom() - center.y());
+}
+
+// static
+void DrawObject::GetRRectAttributes(const math::RectF& bounds,
+    math::RectF rect, render_tree::RoundedCorners corners,
+    RRectAttributes (&out_attributes)[8]) {
+  GetRCornerValues(&rect, &corners, out_attributes);
+  out_attributes[4].rcorner = out_attributes[0].rcorner;
+  out_attributes[5].rcorner = out_attributes[1].rcorner;
+  out_attributes[6].rcorner = out_attributes[2].rcorner;
+  out_attributes[7].rcorner = out_attributes[3].rcorner;
+
+  // Given an ellipse with radii A and B, the largest inscribed rectangle has
+  // dimensions sqrt(2) * A and sqrt(2) * B. To accommodate the antialiased
+  // edge, inset the inscribed rect by a pixel on each side.
+  const float kInsetScale = 0.2929f;    // 1 - sqrt(2) / 2
+
+  // Calculate the bounds for each patch. Eight patches will be used to exclude
+  // the inscribed rect:
+  //   +---+-----+-----+---+
+  //   |   |  4  |  5  |   |
+  //   | 0 +-----+-----+ 1 |
+  //   |   |           |   |
+  //   +---+     C     +---+    C = center point
+  //   |   |           |   |
+  //   | 2 +-----+-----+ 3 |
+  //   |   |  6  |  7  |   |
+  //   +---+-----+-----+---+
+  math::PointF center = GetRRectCenter(rect, corners);
+  center = ClampToBounds(bounds, center.x(), center.y());
+  math::PointF inset0 = ClampToBounds(bounds,
+      rect.x() + kInsetScale * corners.top_left.horizontal + 1.0f,
+      rect.y() + kInsetScale * corners.top_left.vertical + 1.0f);
+  math::PointF inset1 = ClampToBounds(bounds,
+      rect.right() - kInsetScale * corners.top_right.horizontal - 1.0f,
+      rect.y() + kInsetScale * corners.top_right.vertical + 1.0f);
+  math::PointF inset2 = ClampToBounds(bounds,
+      rect.x() + kInsetScale * corners.bottom_left.horizontal + 1.0f,
+      rect.bottom() - kInsetScale * corners.bottom_left.vertical - 1.0f);
+  math::PointF inset3 = ClampToBounds(bounds,
+      rect.right() - kInsetScale * corners.bottom_right.horizontal - 1.0f,
+      rect.bottom() - kInsetScale * corners.bottom_right.vertical - 1.0f);
+
+  out_attributes[0].bounds.SetRect(bounds.x(), bounds.y(),
+      inset0.x() - bounds.x(), center.y() - bounds.y());
+  out_attributes[1].bounds.SetRect(inset1.x(), bounds.y(),
+      bounds.right() - inset1.x(), center.y() - bounds.y());
+  out_attributes[2].bounds.SetRect(bounds.x(), center.y(),
+      inset2.x() - bounds.x(), bounds.bottom() - center.y());
+  out_attributes[3].bounds.SetRect(inset3.x(), center.y(),
+      bounds.right() - inset3.x(), bounds.bottom() - center.y());
+  out_attributes[4].bounds.SetRect(inset0.x(), bounds.y(),
+      center.x() - inset0.x(), inset0.y() - bounds.y());
+  out_attributes[5].bounds.SetRect(center.x(), bounds.y(),
+      inset1.x() - center.x(), inset1.y() - bounds.y());
+  out_attributes[6].bounds.SetRect(inset2.x(), inset2.y(),
+      center.x() - inset2.x(), bounds.bottom() - inset2.y());
+  out_attributes[7].bounds.SetRect(center.x(), inset3.y(),
+      inset3.x() - center.x(), bounds.bottom() - inset3.y());
+}
+
+// static
+void DrawObject::GetRCornerValues(math::RectF* rect,
+    render_tree::RoundedCorners* corners, RRectAttributes out_rcorners[4]) {
   // Ensure corner sizes are non-zero to allow generic handling of square and
-  // rounded corners.
-  const float kMinCornerSize = 0.01f;
-  inset_rect.Outset(kMinCornerSize, kMinCornerSize);
-  inset_corners = inset_corners.Inset(-kMinCornerSize, -kMinCornerSize,
-      -kMinCornerSize, -kMinCornerSize);
-  inset_corners = inset_corners.Normalize(inset_rect);
+  // rounded corners. Corner radii must be at least 1 pixel for antialiasing
+  // to work well.
+  const float kMinCornerSize = 1.0f;
 
-  // The rect data is a vec4 representing (min.xy, max.xy).
-  float rect_data[4] = {
-    inset_rect.x(), inset_rect.y(), inset_rect.right(), inset_rect.bottom(),
-  };
-  GL_CALL(glUniform4fv(rect_uniform, 1, rect_data));
+  // First inset to make room for the minimum corner size. Then outset to
+  // enforce the minimum corner size. Be careful not to inset more than the
+  // rect size, otherwise the outset rect will be off-centered.
+  rect->Inset(std::min(rect->width() * 0.5f, kMinCornerSize),
+              std::min(rect->height() * 0.5f, kMinCornerSize));
+  *corners = corners->Inset(kMinCornerSize, kMinCornerSize, kMinCornerSize,
+                            kMinCornerSize);
+  *corners = corners->Normalize(*rect);
+  rect->Outset(kMinCornerSize, kMinCornerSize);
+  *corners = corners->Inset(-kMinCornerSize, -kMinCornerSize, -kMinCornerSize,
+                            -kMinCornerSize);
 
-  // The corners data is a mat4 with each vector representing a corner
-  // (ordered top left, top right, bottom left, bottom right). Each corner
-  // vec4 represents (start.xy, radius.xy).
-  float corners_data[16] = {
-    inset_rect.x() + inset_corners.top_left.horizontal,
-    inset_rect.y() + inset_corners.top_left.vertical,
-    inset_corners.top_left.horizontal,
-    inset_corners.top_left.vertical,
-    inset_rect.right() - inset_corners.top_right.horizontal,
-    inset_rect.y() + inset_corners.top_right.vertical,
-    inset_corners.top_right.horizontal,
-    inset_corners.top_right.vertical,
-    inset_rect.x() + inset_corners.bottom_left.horizontal,
-    inset_rect.bottom() - inset_corners.bottom_left.vertical,
-    inset_corners.bottom_left.horizontal,
-    inset_corners.bottom_left.vertical,
-    inset_rect.right() - inset_corners.bottom_right.horizontal,
-    inset_rect.bottom() - inset_corners.bottom_right.vertical,
-    inset_corners.bottom_right.horizontal,
-    inset_corners.bottom_right.vertical,
-  };
-  GL_CALL(glUniformMatrix4fv(corners_uniform, 1, false, corners_data));
+  // |rcorner| describes (start.xy, 1 / radius.xy) for the relevant corner.
+  // The sign of the radius component is used to facilitate the calculation:
+  //   vec2 scaled_offset = (position - corner.xy) * corner.zw
+  // Such that |scaled_offset| is in the first quadrant when the pixel is
+  // in the given rounded corner.
+  COMPILE_ASSERT(sizeof(RCorner) == sizeof(float) * 4, struct_should_be_vec4);
+  out_rcorners[0].rcorner.x = rect->x() + corners->top_left.horizontal;
+  out_rcorners[0].rcorner.y = rect->y() + corners->top_left.vertical;
+  out_rcorners[0].rcorner.rx = -1.0f / corners->top_left.horizontal;
+  out_rcorners[0].rcorner.ry = -1.0f / corners->top_left.vertical;
+
+  out_rcorners[1].rcorner.x = rect->right() - corners->top_right.horizontal;
+  out_rcorners[1].rcorner.y = rect->y() + corners->top_right.vertical;
+  out_rcorners[1].rcorner.rx = 1.0f / corners->top_right.horizontal;
+  out_rcorners[1].rcorner.ry = -1.0f / corners->top_right.vertical;
+
+  out_rcorners[2].rcorner.x = rect->x() + corners->bottom_left.horizontal;
+  out_rcorners[2].rcorner.y = rect->bottom() - corners->bottom_left.vertical;
+  out_rcorners[2].rcorner.rx = -1.0f / corners->bottom_left.horizontal;
+  out_rcorners[2].rcorner.ry = 1.0f / corners->bottom_left.vertical;
+
+  out_rcorners[3].rcorner.x = rect->right() - corners->bottom_right.horizontal;
+  out_rcorners[3].rcorner.y = rect->bottom() - corners->bottom_right.vertical;
+  out_rcorners[3].rcorner.rx = 1.0f / corners->bottom_right.horizontal;
+  out_rcorners[3].rcorner.ry = 1.0f / corners->bottom_right.vertical;
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.h b/src/cobalt/renderer/rasterizer/egl/draw_object.h
index 1e2fb12..19af454 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.h
@@ -90,9 +90,32 @@
   virtual base::TypeId GetTypeId() const = 0;
 
  protected:
+  // Structures describing vertex data for rendering rounded rectangles.
+  struct RCorner {
+    // Constructor to transform a RCorner value into a format which the
+    // shader function IsOutsideRCorner() expects. This expresses the current
+    // vertex position as a scaled offset relevant for the corner, and provides
+    // scalars to assist in calculating the antialiased edge. For more details,
+    // see function_is_outside_rcorner.inc.
+    RCorner(const float (&position)[2], const RCorner& init);
+    RCorner() {}
+    float x, y;
+    float rx, ry;
+  };
+  struct RRectAttributes {
+    math::RectF bounds;   // The region in which to use the rcorner data.
+    RCorner rcorner;
+  };
+
   DrawObject() {}
   explicit DrawObject(const BaseState& base_state);
 
+  // Extract the scale vector from this object's transform.
+  math::Vector2dF GetScale() const;
+
+  // Remove scale from the transform, and return the scale vector.
+  math::Vector2dF RemoveScaleFromTransform();
+
   // Utility function to get the render color for the blend modes that will
   // be used. These modes expect alpha to be pre-multiplied.
   static render_tree::ColorRGBA GetDrawColor(
@@ -108,14 +131,28 @@
     return GetGLRGBA(color.r(), color.g(), color.b(), color.a());
   }
 
-  // Set shader uniforms for a rounded rect. Specify a non-zero inset if
-  // the rect will be used with anti-aliasing (e.g. 0.5 inset for a 1-pixel
-  // anti-aliasing border).
-  static void SetRRectUniforms(GLint rect_uniform, GLint corners_uniform,
-      const math::RectF& rect, const render_tree::RoundedCorners& corners,
-      float inset);
+  // Get the vertex attributes to use to draw the given rounded rect. Each
+  // corner uses a different attribute. These RCorner values must be transformed
+  // before being passed to the shader. (See RCorner constructor.)
+  static void GetRRectAttributes(const math::RectF& bounds,
+      math::RectF rect, render_tree::RoundedCorners corners,
+      RRectAttributes (&out_attributes)[4]);
+
+  // Get the vertex attributes to draw the given rounded rect excluding the
+  // inscribed rect. These RCorner values must be transformed before being
+  // passed to the shader. (See RCorner constructor.)
+  static void GetRRectAttributes(const math::RectF& bounds,
+      math::RectF rect, render_tree::RoundedCorners corners,
+      RRectAttributes (&out_attributes)[8]);
 
   BaseState base_state_;
+
+ private:
+  // Return the RCorner values for the given rounded rect, and the normalized
+  // rect and corner values used.
+  static void GetRCornerValues(math::RectF* rect,
+      render_tree::RoundedCorners* corners,
+      RRectAttributes out_rcorners[4]);
 };
 
 }  // 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 cdc1d88..829ef32 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
@@ -121,26 +121,18 @@
     const math::RectF& border_rect, const math::RectF& content_rect,
     const render_tree::ColorRGBA& border_color,
     const render_tree::ColorRGBA& content_color) {
-  // Extract scale from the transform matrix. This can be reliably done if no
-  // rotations are involved.
-  const float kEpsilon = 0.0001f;
-  if (std::abs(base_state_.transform(0, 1)) >= kEpsilon ||
-      std::abs(base_state_.transform(1, 0)) >= kEpsilon) {
-    return false;
-  }
-
-  // If the scale is 0 in either direction, then there's nothing to render.
-  float scale_x = std::abs(base_state_.transform(0, 0));
-  float scale_y = std::abs(base_state_.transform(1, 1));
-  if (scale_x <= kEpsilon || scale_y <= kEpsilon) {
+  // If the scaled border rect is too small, then don't bother rendering.
+  math::Vector2dF scale = GetScale();
+  if (border_rect.width() * scale.x() < 1.0f ||
+      border_rect.height() * scale.y() < 1.0f) {
     return true;
   }
 
   // Antialiased subpixel borders are not supported at this time. It can be
   // done by attenuating the alpha, but this can get complicated if the borders
   // are of different widths.
-  float pixel_size_x = 1.0f / scale_x;
-  float pixel_size_y = 1.0f / scale_y;
+  float pixel_size_x = 1.0f / scale.x();
+  float pixel_size_y = 1.0f / scale.y();
   if (border.left.width < pixel_size_x || border.right.width < pixel_size_x ||
       border.top.width < pixel_size_y || border.bottom.width < pixel_size_y) {
     return false;
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
index 13401c9..fcabd2d 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -80,128 +80,340 @@
 }
 }  // namespace
 
+DrawRectShadowBlur::VertexAttributesSquare::VertexAttributesSquare(
+    float x, float y, float offset_scale) {
+  position[0] = x;
+  position[1] = y;
+  offset[0] = x * offset_scale;
+  offset[1] = y * offset_scale;
+}
+
+DrawRectShadowBlur::VertexAttributesRound::VertexAttributesRound(
+    float x, float y, const RCorner& init) {
+  position[0] = x;
+  position[1] = y;
+  rcorner_scissor = RCorner(position, init);
+}
+
 DrawRectShadowBlur::DrawRectShadowBlur(GraphicsState* graphics_state,
     const BaseState& base_state, const math::RectF& base_rect,
     const OptionalRoundedCorners& base_corners, const math::RectF& spread_rect,
     const OptionalRoundedCorners& spread_corners,
     const render_tree::ColorRGBA& color, float blur_sigma, bool inset)
-    : DrawRectShadowSpread(graphics_state, base_state),
+    : DrawObject(base_state),
       spread_rect_(spread_rect),
       spread_corners_(spread_corners),
       blur_sigma_(blur_sigma),
-      is_inset_(inset) {
-  const float kBlurExtentInPixels = kBlurExtentInSigmas * blur_sigma;
+      is_inset_(inset),
+      vertex_buffer_(nullptr),
+      index_buffer_(nullptr) {
+  color_ = GetDrawColor(color) * base_state_.opacity;
 
-  if (inset) {
-    outer_rect_ = base_rect;
-    outer_corners_ = base_corners;
-    inner_rect_ = spread_rect;
-    inner_rect_.Inset(kBlurExtentInPixels, kBlurExtentInPixels);
-    if (inner_rect_.IsEmpty()) {
-      inner_rect_.set_origin(spread_rect.CenterPoint());
-    }
-    if (spread_corners) {
-      inner_corners_ = spread_corners->Inset(kBlurExtentInPixels,
-          kBlurExtentInPixels, kBlurExtentInPixels, kBlurExtentInPixels);
-    }
-  } else {
-    inner_rect_ = base_rect;
-    inner_corners_ = base_corners;
-    outer_rect_ = spread_rect;
-    outer_rect_.Outset(kBlurExtentInPixels, kBlurExtentInPixels);
-    if (spread_corners) {
-      outer_corners_ = spread_corners->Inset(-kBlurExtentInPixels,
-          -kBlurExtentInPixels, -kBlurExtentInPixels, -kBlurExtentInPixels);
-    }
+  // Extract scale from the transform and move it into the vertex attributes
+  // so that the anti-aliased edges remain 1 pixel wide.
+  math::Vector2dF scale = RemoveScaleFromTransform();
+  math::RectF scaled_base_rect(base_rect);
+  scaled_base_rect.Scale(scale.x(), scale.y());
+  OptionalRoundedCorners scaled_base_corners(base_corners);
+  if (scaled_base_corners) {
+    scaled_base_corners = scaled_base_corners->Scale(scale.x(), scale.y());
+    scaled_base_corners = scaled_base_corners->Normalize(scaled_base_rect);
+  }
+  spread_rect_.Scale(scale.x(), scale.y());
+  if (spread_corners_) {
+    spread_corners_ = spread_corners_->Scale(scale.x(), scale.y());
+    spread_corners_ = spread_corners_->Normalize(spread_rect_);
   }
 
-  if (base_corners || spread_corners) {
-    // If rounded rects are specified, then both the base and spread rects
-    // must have rounded corners.
-    DCHECK(inner_corners_);
-    DCHECK(outer_corners_);
-    DCHECK(spread_corners_);
-  } else {
-    // Non-rounded rects specify vertex offset in terms of sigma from the
-    // center of the spread rect.
-    offset_scale_ = kBlurDistance / kBlurExtentInPixels;
-    offset_center_ = spread_rect_.CenterPoint();
-  }
+  // The blur algorithms used by the shaders do not produce good results with
+  // separate x and y blur sigmas. Select a single blur sigma to approximate
+  // the desired blur.
+  blur_sigma_ *= std::sqrt(scale.x() * scale.y());
 
-  color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
+  SetGeometry(graphics_state, scaled_base_rect, scaled_base_corners);
+}
+
+void DrawRectShadowBlur::ExecuteUpdateVertexBuffer(
+    GraphicsState* graphics_state,
+    ShaderProgramManager* program_manager) {
+  if (attributes_square_.size() > 0) {
+    vertex_buffer_ = graphics_state->AllocateVertexData(
+        attributes_square_.size() * sizeof(attributes_square_[0]));
+    SbMemoryCopy(vertex_buffer_, &attributes_square_[0],
+        attributes_square_.size() * sizeof(attributes_square_[0]));
+  } else if (attributes_round_.size() > 0) {
+    vertex_buffer_ = graphics_state->AllocateVertexData(
+        attributes_round_.size() * sizeof(attributes_round_[0]));
+    SbMemoryCopy(vertex_buffer_, &attributes_round_[0],
+        attributes_round_.size() * sizeof(attributes_round_[0]));
+    index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size());
+    SbMemoryCopy(index_buffer_, &indices_[0],
+        indices_.size() * sizeof(indices_[0]));
+  }
 }
 
 void DrawRectShadowBlur::ExecuteRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
+  if (vertex_buffer_ == nullptr) {
+    return;
+  }
+
   // Draw the blurred shadow.
-  if (inner_corners_) {
-    ShaderProgram<CommonVertexShader,
+  if (spread_corners_) {
+    ShaderProgram<ShaderVertexOffsetRcorner,
                   ShaderFragmentColorBlurRrects>* program;
     program_manager->GetProgram(&program);
     graphics_state->UseProgram(program->GetHandle());
-    SetupShader(program->GetVertexShader(), graphics_state);
-
+    SetupVertexShader(graphics_state, program->GetVertexShader());
+    SetFragmentUniforms(program->GetFragmentShader().u_color(),
+                        program->GetFragmentShader().u_scale_add());
     float sigma_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
     GL_CALL(glUniform2f(program->GetFragmentShader().u_sigma_scale(),
                         sigma_scale, sigma_scale));
-
     // Pre-calculate the scale values to calculate the normalized gaussian.
     GL_CALL(glUniform2f(program->GetFragmentShader().u_gaussian_scale(),
                         -1.0f / (2.0f * blur_sigma_ * blur_sigma_),
                         1.0f / (kSqrt2 * kSqrtPi * blur_sigma_)));
-    if (is_inset_) {
-      // Set the outer rect to be an inclusive scissor, and invert the shadow.
-      SetRRectUniforms(program->GetFragmentShader().u_scissor_rect(),
-                       program->GetFragmentShader().u_scissor_corners(),
-                       outer_rect_, *outer_corners_, 0.5f);
-      GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
-                          -1.0f, 1.0f));
-    } else {
-      // Set the inner rect to be an exclusive scissor.
-      SetRRectUniforms(program->GetFragmentShader().u_scissor_rect(),
-                       program->GetFragmentShader().u_scissor_corners(),
-                       inner_rect_, *inner_corners_, 0.5f);
-      GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
-                          1.0f, 0.0f));
-    }
     SetBlurRRectUniforms(program->GetFragmentShader(),
                          spread_rect_, *spread_corners_, blur_sigma_);
+    GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
+        graphics_state->GetVertexIndexPointer(index_buffer_)));
   } else {
-    ShaderProgram<CommonVertexShader,
+    ShaderProgram<ShaderVertexOffset,
                   ShaderFragmentColorBlur>* program;
     program_manager->GetProgram(&program);
     graphics_state->UseProgram(program->GetHandle());
-    SetupShader(program->GetVertexShader(), graphics_state);
-    if (is_inset_) {
-      // Invert the shadow.
-      GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
-                          -1.0f, 1.0f));
-    } else {
-      // Keep the normal (outset) shadow.
-      GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
-                          1.0f, 0.0f));
-    }
+    SetupVertexShader(graphics_state, program->GetVertexShader());
+    SetFragmentUniforms(program->GetFragmentShader().u_color(),
+                        program->GetFragmentShader().u_scale_add());
     GL_CALL(glUniform4f(program->GetFragmentShader().u_blur_rect(),
-        (spread_rect_.x() - offset_center_.x()) * offset_scale_,
-        (spread_rect_.y() - offset_center_.y()) * offset_scale_,
-        (spread_rect_.right() - offset_center_.x()) * offset_scale_,
-        (spread_rect_.bottom() - offset_center_.y()) * offset_scale_));
+                        spread_rect_.x(), spread_rect_.y(),
+                        spread_rect_.right(), spread_rect_.bottom()));
+    GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_square_.size()));
   }
-
-  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count_));
 }
 
 base::TypeId DrawRectShadowBlur::GetTypeId() const {
-  if (inner_corners_) {
-    return ShaderProgram<CommonVertexShader,
+  if (spread_corners_) {
+    return ShaderProgram<ShaderVertexOffsetRcorner,
                          ShaderFragmentColorBlurRrects>::GetTypeId();
   } else {
-    return ShaderProgram<CommonVertexShader,
+    return ShaderProgram<ShaderVertexOffset,
                          ShaderFragmentColorBlur>::GetTypeId();
   }
 }
 
+void DrawRectShadowBlur::SetupVertexShader(GraphicsState* graphics_state,
+    const ShaderVertexOffset& shader) {
+  graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
+  graphics_state->UpdateTransformMatrix(shader.u_view_matrix(),
+      base_state_.transform);
+  graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+      base_state_.scissor.width(), base_state_.scissor.height());
+  graphics_state->VertexAttribPointer(
+      shader.a_position(), 2, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesSquare), vertex_buffer_ +
+      offsetof(VertexAttributesSquare, position));
+  graphics_state->VertexAttribPointer(
+      shader.a_offset(), 2, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesSquare), vertex_buffer_ +
+      offsetof(VertexAttributesSquare, offset));
+  graphics_state->VertexAttribFinish();
+}
+
+void DrawRectShadowBlur::SetupVertexShader(GraphicsState* graphics_state,
+    const ShaderVertexOffsetRcorner& shader) {
+  graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
+  graphics_state->UpdateTransformMatrix(shader.u_view_matrix(),
+      base_state_.transform);
+  graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+      base_state_.scissor.width(), base_state_.scissor.height());
+  graphics_state->VertexAttribPointer(
+      shader.a_position(), 2, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesRound), vertex_buffer_ +
+      offsetof(VertexAttributesRound, position));
+  graphics_state->VertexAttribPointer(
+      shader.a_rcorner(), 4, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesRound), vertex_buffer_ +
+      offsetof(VertexAttributesRound, rcorner_scissor));
+  graphics_state->VertexAttribFinish();
+}
+
+void DrawRectShadowBlur::SetFragmentUniforms(
+    GLint color_uniform, GLint scale_add_uniform) {
+  GL_CALL(glUniform4f(color_uniform,
+                      color_.r(), color_.g(), color_.b(), color_.a()));
+  if (is_inset_) {
+    // Invert the shadow.
+    GL_CALL(glUniform2f(scale_add_uniform, -1.0f, 1.0f));
+  } else {
+    // Keep the normal (outset) shadow.
+    GL_CALL(glUniform2f(scale_add_uniform, 1.0f, 0.0f));
+  }
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+    const math::RectF& base_rect, const OptionalRoundedCorners& base_corners) {
+  const float kBlurExtentInPixels = kBlurExtentInSigmas * blur_sigma_;
+
+  if (base_corners || spread_corners_) {
+    // If rounded rects are specified, then both the base and spread rects
+    // must have rounded corners.
+    DCHECK(base_corners);
+    DCHECK(spread_corners_);
+
+    if (is_inset_) {
+      // Extend the outer rect to include the antialiased edge.
+      math::RectF outer_rect(base_rect);
+      outer_rect.Outset(1.0f, 1.0f);
+      RRectAttributes rrect_outer[4];
+      GetRRectAttributes(outer_rect, base_rect, *base_corners, rrect_outer);
+      // Inset the spread rect by the blur extent. Use that as the inner bounds.
+      RRectAttributes rrect_inner[8];
+      math::RectF inner_rect(spread_rect_);
+      inner_rect.Inset(kBlurExtentInPixels, kBlurExtentInPixels);
+      if (!inner_rect.IsEmpty()) {
+        // Get the inner bounds excluding the inscribed rect.
+        render_tree::RoundedCorners inner_corners = spread_corners_->Inset(
+            kBlurExtentInPixels, kBlurExtentInPixels, kBlurExtentInPixels,
+            kBlurExtentInPixels);
+        inner_corners = inner_corners.Normalize(inner_rect);
+        GetRRectAttributes(outer_rect, inner_rect, inner_corners, rrect_inner);
+      } else {
+        // The blur covers everything inside the outer rect.
+        rrect_inner[0].bounds = outer_rect;
+      }
+      SetGeometry(graphics_state, rrect_outer, rrect_inner);
+    } else {
+      // Extend the outer rect to include the blur.
+      math::RectF outer_rect(spread_rect_);
+      outer_rect.Outset(kBlurExtentInPixels, kBlurExtentInPixels);
+      // Exclude the inscribed rect of the base rounded rect.
+      RRectAttributes rrect[8];
+      GetRRectAttributes(outer_rect, base_rect, *base_corners, rrect);
+      SetGeometry(graphics_state, rrect);
+    }
+  } else {
+    // Handle box shadow with square corners.
+    if (is_inset_) {
+      math::RectF inner_rect(spread_rect_);
+      inner_rect.Inset(kBlurExtentInPixels, kBlurExtentInPixels);
+      SetGeometry(graphics_state, inner_rect, base_rect);
+    } else {
+      math::RectF outer_rect(spread_rect_);
+      outer_rect.Outset(kBlurExtentInPixels, kBlurExtentInPixels);
+      SetGeometry(graphics_state, base_rect, outer_rect);
+    }
+  }
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+    const math::RectF& inner_rect, const math::RectF& outer_rect) {
+  // Express offset in terms of blur sigma for the shader.
+  float offset_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
+
+  // The spread rect should also be expressed in terms of sigma.
+  spread_rect_.Scale(offset_scale, offset_scale);
+
+  // The box shadow is a triangle strip covering the area between outer rect
+  // and inner rect.
+  if (inner_rect.IsEmpty()) {
+    attributes_square_.reserve(4);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.bottom(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.bottom(), offset_scale);
+  } else {
+    math::RectF inside_rect(inner_rect);
+    inside_rect.Intersect(outer_rect);
+    attributes_square_.reserve(10);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        inside_rect.x(), inside_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        inside_rect.right(), inside_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.bottom(), offset_scale);
+    attributes_square_.emplace_back(
+        inside_rect.right(), inside_rect.bottom(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.bottom(), offset_scale);
+    attributes_square_.emplace_back(
+        inside_rect.x(), inside_rect.bottom(), offset_scale);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.y(), offset_scale);
+    attributes_square_.emplace_back(
+        inside_rect.x(), inside_rect.y(), offset_scale);
+  }
+
+  graphics_state->ReserveVertexData(
+      attributes_square_.size() * sizeof(attributes_square_[0]));
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+    const RRectAttributes (&rrect)[8]) {
+  // The shadowed area is already split into quads.
+  for (int i = 0; i < arraysize(rrect); ++i) {
+    uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+    const math::RectF& bounds = rrect[i].bounds;
+    const RCorner& rcorner = rrect[i].rcorner;
+    attributes_round_.emplace_back(bounds.x(), bounds.y(), rcorner);
+    attributes_round_.emplace_back(bounds.right(), bounds.y(), rcorner);
+    attributes_round_.emplace_back(bounds.x(), bounds.bottom(), rcorner);
+    attributes_round_.emplace_back(bounds.right(), bounds.bottom(), rcorner);
+    indices_.emplace_back(vert);
+    indices_.emplace_back(vert + 1);
+    indices_.emplace_back(vert + 2);
+    indices_.emplace_back(vert + 1);
+    indices_.emplace_back(vert + 2);
+    indices_.emplace_back(vert + 3);
+  }
+
+  graphics_state->ReserveVertexData(
+      attributes_round_.size() * sizeof(attributes_round_[0]));
+  graphics_state->ReserveVertexIndices(indices_.size());
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+    const RRectAttributes (&rrect_outer)[4],
+    const RRectAttributes (&rrect_inner)[8]) {
+  // Draw the area between the inner rect and outer rect using the outer rect's
+  // rounded corners. The inner quads already exclude the inscribed rectangle.
+  for (int i = 0; i < arraysize(rrect_inner); ++i) {
+    for (int o = 0; o < arraysize(rrect_outer); ++o) {
+      math::RectF rect = math::IntersectRects(
+          rrect_inner[i].bounds, rrect_outer[o].bounds);
+      if (!rect.IsEmpty()) {
+        // Use two triangles to draw the intersection.
+        const RCorner& rcorner = rrect_outer[o].rcorner;
+        uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+        attributes_round_.emplace_back(rect.x(), rect.y(), rcorner);
+        attributes_round_.emplace_back(rect.right(), rect.y(), rcorner);
+        attributes_round_.emplace_back(rect.x(), rect.bottom(), rcorner);
+        attributes_round_.emplace_back(rect.right(), rect.bottom(), rcorner);
+        indices_.emplace_back(vert);
+        indices_.emplace_back(vert + 1);
+        indices_.emplace_back(vert + 2);
+        indices_.emplace_back(vert + 1);
+        indices_.emplace_back(vert + 2);
+        indices_.emplace_back(vert + 3);
+      }
+    }
+  }
+
+  graphics_state->ReserveVertexData(
+      attributes_round_.size() * sizeof(attributes_round_[0]));
+  graphics_state->ReserveVertexIndices(indices_.size());
+}
+
 }  // namespace egl
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
index 8d36722..107af85 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
@@ -15,7 +15,12 @@
 #ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
 #define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
 
-#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h"
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
+#include "egl/generated_shader_impl.h"
 
 namespace cobalt {
 namespace renderer {
@@ -40,12 +45,7 @@
 
 // Handles drawing a box shadow with blur. This uses a gaussian kernel to fade
 // the "blur" region.
-//
-// This uses a shader to mimic skia's SkBlurMask.cpp.
-// See also http://stereopsis.com/shadowrect/ as reference for the formula
-// used to approximate the gaussian integral (which controls the opacity of
-// the shadow).
-class DrawRectShadowBlur : public DrawRectShadowSpread {
+class DrawRectShadowBlur : public DrawObject {
  public:
   // Draw a blurred box shadow.
   // The box shadow exists in the area between |base_rect| and |spread_rect|
@@ -59,16 +59,55 @@
                      const render_tree::ColorRGBA& color,
                      float blur_sigma, bool inset);
 
+  void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+      ShaderProgramManager* program_manager) OVERRIDE;
   void ExecuteRasterize(GraphicsState* graphics_state,
       ShaderProgramManager* program_manager) OVERRIDE;
   base::TypeId GetTypeId() const OVERRIDE;
 
  private:
+  struct VertexAttributesSquare {
+    VertexAttributesSquare(float x, float y, float offset_scale);
+    float position[2];
+    float offset[2];
+  };
+
+  struct VertexAttributesRound {
+    VertexAttributesRound(float x, float y, const RCorner& init);
+    float position[2];
+    RCorner rcorner_scissor;
+  };
+
+  void SetupVertexShader(GraphicsState* graphics_state,
+                         const ShaderVertexOffset& shader);
+  void SetupVertexShader(GraphicsState* graphics_state,
+                         const ShaderVertexOffsetRcorner& shader);
+  void SetFragmentUniforms(GLint color_uniform, GLint scale_add_uniform);
+
+  void SetGeometry(GraphicsState* graphics_state,
+                   const math::RectF& base_rect,
+                   const OptionalRoundedCorners& base_corners);
+  void SetGeometry(GraphicsState* graphics_state,
+                   const math::RectF& inner_rect,
+                   const math::RectF& outer_rect);
+  void SetGeometry(GraphicsState* graphics_state,
+                   const RRectAttributes (&rrect)[8]);
+  void SetGeometry(GraphicsState* graphics_state,
+                   const RRectAttributes (&rrect_outer)[4],
+                   const RRectAttributes (&rrect_inner)[8]);
+
   math::RectF spread_rect_;
   OptionalRoundedCorners spread_corners_;
-
+  render_tree::ColorRGBA color_;
   float blur_sigma_;
   bool is_inset_;
+
+  std::vector<VertexAttributesSquare> attributes_square_;
+  std::vector<VertexAttributesRound> attributes_round_;
+  std::vector<uint16_t> indices_;
+
+  uint8_t* vertex_buffer_;
+  uint16_t* index_buffer_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
index 9dba0d3..faa7286 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
@@ -26,8 +26,19 @@
 namespace rasterizer {
 namespace egl {
 
-namespace {
-const int kVertexCount = 10;
+DrawRectShadowSpread::VertexAttributesSquare::VertexAttributesSquare(
+    float x, float y, uint32_t in_color) {
+  position[0] = x;
+  position[1] = y;
+  color = in_color;
+}
+
+DrawRectShadowSpread::VertexAttributesRound::VertexAttributesRound(
+    float x, float y, const RCorner& inner, const RCorner& outer) {
+  position[0] = x;
+  position[1] = y;
+  rcorner_inner = RCorner(position, inner);
+  rcorner_outer = RCorner(position, outer);
 }
 
 DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
@@ -36,121 +47,94 @@
     const OptionalRoundedCorners& outer_corners,
     const render_tree::ColorRGBA& color)
     : DrawObject(base_state),
-      inner_rect_(inner_rect),
-      outer_rect_(outer_rect),
-      inner_corners_(inner_corners),
-      outer_corners_(outer_corners),
-      offset_scale_(1.0f),
       vertex_buffer_(nullptr),
-      vertex_count_(0) {
-  color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
-  if (inner_corners_ || outer_corners_) {
+      index_buffer_(nullptr) {
+  color_ = GetDrawColor(color) * base_state_.opacity;
+
+  // Extract scale from the transform and move it into the vertex attributes
+  // so that the anti-aliased edges remain 1 pixel wide.
+  math::Vector2dF scale = RemoveScaleFromTransform();
+  math::RectF inside_rect(inner_rect);
+  math::RectF outside_rect(outer_rect);
+  inside_rect.Scale(scale.x(), scale.y());
+  outside_rect.Scale(scale.x(), scale.y());
+
+  if (inner_corners || outer_corners) {
     // If using rounded corners, then both inner and outer rects must have
     // rounded corner definitions.
-    DCHECK(inner_corners_);
-    DCHECK(outer_corners_);
+    DCHECK(inner_corners);
+    DCHECK(outer_corners);
+    render_tree::RoundedCorners inside_corners =
+        inner_corners->Scale(scale.x(), scale.y());
+    render_tree::RoundedCorners outside_corners =
+        outer_corners->Scale(scale.x(), scale.y());
+    SetGeometry(graphics_state,
+                inside_rect, inside_corners,
+                outside_rect, outside_corners);
+  } else {
+    SetGeometry(graphics_state, inside_rect, outside_rect);
   }
-  graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
-}
-
-DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
-    const BaseState& base_state)
-    : DrawObject(base_state),
-      offset_scale_(1.0f),
-      vertex_buffer_(nullptr),
-      vertex_count_(0) {
-  graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
 }
 
 void DrawRectShadowSpread::ExecuteUpdateVertexBuffer(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  // Draw the box shadow's spread. This is a triangle strip covering the area
-  // between outer rect and inner rect.
-  math::RectF inside_rect(inner_rect_);
-  math::RectF outside_rect(outer_rect_);
-  VertexAttributes attributes[kVertexCount];
-
-  if (inner_corners_) {
-    // Inset the inside rect to include the rounded corners.
-    inside_rect.Inset(
-        std::max(inner_corners_->bottom_left.horizontal,
-                 inner_corners_->top_left.horizontal),
-        std::max(inner_corners_->top_left.vertical,
-                 inner_corners_->top_right.vertical),
-        std::max(inner_corners_->top_right.horizontal,
-                 inner_corners_->bottom_right.horizontal),
-        std::max(inner_corners_->bottom_right.vertical,
-                 inner_corners_->bottom_left.vertical));
-
-    // Add a 1 pixel border to the outer rect for anti-aliasing.
-    outside_rect.Outset(1.0f, 1.0f);
+  if (attributes_square_.size() > 0) {
+    vertex_buffer_ = graphics_state->AllocateVertexData(
+        attributes_square_.size() * sizeof(attributes_square_[0]));
+    SbMemoryCopy(vertex_buffer_, &attributes_square_[0],
+        attributes_square_.size() * sizeof(attributes_square_[0]));
+  } else if (attributes_round_.size() > 0) {
+    vertex_buffer_ = graphics_state->AllocateVertexData(
+        attributes_round_.size() * sizeof(attributes_round_[0]));
+    SbMemoryCopy(vertex_buffer_, &attributes_round_[0],
+        attributes_round_.size() * sizeof(attributes_round_[0]));
+    index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size());
+    SbMemoryCopy(index_buffer_, &indices_[0],
+        indices_.size() * sizeof(indices_[0]));
   }
-
-  // Only pixels inside the outer rect should be touched.
-  if (inside_rect.IsEmpty()) {
-    vertex_count_ = 4;
-    SetVertex(&attributes[0], outside_rect.x(), outside_rect.y());
-    SetVertex(&attributes[1], outside_rect.right(), outside_rect.y());
-    SetVertex(&attributes[2], outside_rect.x(), outside_rect.bottom());
-    SetVertex(&attributes[3], outside_rect.right(), outside_rect.bottom());
-  } else {
-    inside_rect.Intersect(outside_rect);
-    vertex_count_ = 10;
-    SetVertex(&attributes[0], outside_rect.x(), outside_rect.y());
-    SetVertex(&attributes[1], inside_rect.x(), inside_rect.y());
-    SetVertex(&attributes[2], outside_rect.right(), outside_rect.y());
-    SetVertex(&attributes[3], inside_rect.right(), inside_rect.y());
-    SetVertex(&attributes[4], outside_rect.right(), outside_rect.bottom());
-    SetVertex(&attributes[5], inside_rect.right(), inside_rect.bottom());
-    SetVertex(&attributes[6], outside_rect.x(), outside_rect.bottom());
-    SetVertex(&attributes[7], inside_rect.x(), inside_rect.bottom());
-    SetVertex(&attributes[8], outside_rect.x(), outside_rect.y());
-    SetVertex(&attributes[9], inside_rect.x(), inside_rect.y());
-  }
-
-  vertex_buffer_ = graphics_state->AllocateVertexData(
-      vertex_count_ * sizeof(VertexAttributes));
-  SbMemoryCopy(vertex_buffer_, attributes,
-      vertex_count_ * sizeof(VertexAttributes));
 }
 
 void DrawRectShadowSpread::ExecuteRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  if (inner_corners_) {
-    ShaderProgram<CommonVertexShader,
-                  ShaderFragmentColorBetweenRrects>* program;
-    program_manager->GetProgram(&program);
-    graphics_state->UseProgram(program->GetHandle());
-    SetupShader(program->GetVertexShader(), graphics_state);
-
-    SetRRectUniforms(program->GetFragmentShader().u_inner_rect(),
-                     program->GetFragmentShader().u_inner_corners(),
-                     inner_rect_, *inner_corners_, 0.5f);
-    SetRRectUniforms(program->GetFragmentShader().u_outer_rect(),
-                     program->GetFragmentShader().u_outer_corners(),
-                     outer_rect_, *outer_corners_, 0.5f);
-  } else {
-    ShaderProgram<CommonVertexShader, ShaderFragmentColorInclude>* program;
-    program_manager->GetProgram(&program);
-    graphics_state->UseProgram(program->GetHandle());
-    SetupShader(program->GetVertexShader(), graphics_state);
-
-    float include[4] = {
-      outer_rect_.x(),
-      outer_rect_.y(),
-      outer_rect_.right(),
-      outer_rect_.bottom()
-    };
-    GL_CALL(glUniform4fv(program->GetFragmentShader().u_include(), 1, include));
+  if (vertex_buffer_ == nullptr) {
+    return;
   }
 
-  GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count_));
+  // Draw the box shadow.
+  if (attributes_square_.size() > 0) {
+    ShaderProgram<ShaderVertexColor,
+                  ShaderFragmentColor>* program;
+    program_manager->GetProgram(&program);
+    graphics_state->UseProgram(program->GetHandle());
+    SetupVertexShader(graphics_state, program->GetVertexShader());
+    GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_square_.size()));
+  } else {
+    ShaderProgram<ShaderVertexRcorner2,
+                  ShaderFragmentRcorner2Color>* program;
+    program_manager->GetProgram(&program);
+    graphics_state->UseProgram(program->GetHandle());
+    SetupVertexShader(graphics_state, program->GetVertexShader());
+    GL_CALL(glUniform4f(program->GetFragmentShader().u_color(),
+        color_.r(), color_.g(), color_.b(), color_.a()));
+    GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
+        graphics_state->GetVertexIndexPointer(index_buffer_)));
+  }
 }
 
-void DrawRectShadowSpread::SetupShader(const CommonVertexShader& shader,
-    GraphicsState* graphics_state) {
+base::TypeId DrawRectShadowSpread::GetTypeId() const {
+  if (attributes_square_.size() > 0) {
+    return ShaderProgram<ShaderVertexColor,
+                         ShaderFragmentColor>::GetTypeId();
+  } else {
+    return ShaderProgram<ShaderVertexRcorner2,
+                         ShaderFragmentRcorner2Color>::GetTypeId();
+  }
+}
+
+void DrawRectShadowSpread::SetupVertexShader(GraphicsState* graphics_state,
+    const ShaderVertexColor& shader) {
   graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
   graphics_state->UpdateTransformMatrix(shader.u_view_matrix(),
       base_state_.transform);
@@ -158,36 +142,134 @@
       base_state_.scissor.width(), base_state_.scissor.height());
   graphics_state->VertexAttribPointer(
       shader.a_position(), 2, GL_FLOAT, GL_FALSE,
-      sizeof(VertexAttributes), vertex_buffer_ +
-      offsetof(VertexAttributes, position));
+      sizeof(VertexAttributesSquare), vertex_buffer_ +
+      offsetof(VertexAttributesSquare, position));
   graphics_state->VertexAttribPointer(
       shader.a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
-      sizeof(VertexAttributes), vertex_buffer_ +
-      offsetof(VertexAttributes, color));
-  graphics_state->VertexAttribPointer(
-      shader.a_offset(), 2, GL_FLOAT, GL_FALSE,
-      sizeof(VertexAttributes), vertex_buffer_ +
-      offsetof(VertexAttributes, offset));
+      sizeof(VertexAttributesSquare), vertex_buffer_ +
+      offsetof(VertexAttributesSquare, color));
   graphics_state->VertexAttribFinish();
 }
 
-base::TypeId DrawRectShadowSpread::GetTypeId() const {
-  if (inner_corners_) {
-    return ShaderProgram<CommonVertexShader,
-                         ShaderFragmentColorBetweenRrects>::GetTypeId();
-  } else {
-    return ShaderProgram<CommonVertexShader,
-                         ShaderFragmentColorInclude>::GetTypeId();
-  }
+void DrawRectShadowSpread::SetupVertexShader(GraphicsState* graphics_state,
+    const ShaderVertexRcorner2& shader) {
+  graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
+  graphics_state->UpdateTransformMatrix(shader.u_view_matrix(),
+      base_state_.transform);
+  graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+      base_state_.scissor.width(), base_state_.scissor.height());
+  graphics_state->VertexAttribPointer(
+      shader.a_position(), 2, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesRound), vertex_buffer_ +
+      offsetof(VertexAttributesRound, position));
+  graphics_state->VertexAttribPointer(
+      shader.a_rcorner_inner(), 4, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesRound), vertex_buffer_ +
+      offsetof(VertexAttributesRound, rcorner_inner));
+  graphics_state->VertexAttribPointer(
+      shader.a_rcorner_outer(), 4, GL_FLOAT, GL_FALSE,
+      sizeof(VertexAttributesRound), vertex_buffer_ +
+      offsetof(VertexAttributesRound, rcorner_outer));
+  graphics_state->VertexAttribFinish();
 }
 
-void DrawRectShadowSpread::SetVertex(VertexAttributes* vertex,
-                                     float x, float y) {
-  vertex->position[0] = x;
-  vertex->position[1] = y;
-  vertex->offset[0] = (x - offset_center_.x()) * offset_scale_;
-  vertex->offset[1] = (y - offset_center_.y()) * offset_scale_;
-  vertex->color = color_;
+void DrawRectShadowSpread::SetGeometry(GraphicsState* graphics_state,
+    const math::RectF& inner_rect, const math::RectF& outer_rect) {
+  // Draw the box shadow's spread. This is a triangle strip covering the area
+  // between outer rect and inner rect.
+  uint32_t color = GetGLRGBA(color_);
+
+  if (inner_rect.IsEmpty()) {
+    attributes_square_.reserve(4);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.y(), color);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.y(), color);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.bottom(), color);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.bottom(), color);
+  } else {
+    math::RectF inside_rect(inner_rect);
+    inside_rect.Intersect(outer_rect);
+    attributes_square_.reserve(10);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.y(), color);
+    attributes_square_.emplace_back(
+        inside_rect.x(), inside_rect.y(), color);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.y(), color);
+    attributes_square_.emplace_back(
+        inside_rect.right(), inside_rect.y(), color);
+    attributes_square_.emplace_back(
+        outer_rect.right(), outer_rect.bottom(), color);
+    attributes_square_.emplace_back(
+        inside_rect.right(), inside_rect.bottom(), color);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.bottom(), color);
+    attributes_square_.emplace_back(
+        inside_rect.x(), inside_rect.bottom(), color);
+    attributes_square_.emplace_back(
+        outer_rect.x(), outer_rect.y(), color);
+    attributes_square_.emplace_back(
+        inside_rect.x(), inside_rect.y(), color);
+  }
+
+  graphics_state->ReserveVertexData(
+      attributes_square_.size() * sizeof(attributes_square_[0]));
+}
+
+void DrawRectShadowSpread::SetGeometry(
+    GraphicsState* graphics_state,
+    const math::RectF& inner_rect,
+    const render_tree::RoundedCorners& inner_corners,
+    const math::RectF& outer_rect,
+    const render_tree::RoundedCorners& outer_corners) {
+  // Draw the area between the inner rounded rect and outer rounded rect. Add
+  // a 1-pixel border to include antialiasing.
+  math::RectF bounds(outer_rect);
+  bounds.Outset(1.0f, 1.0f);
+
+  // Get the render quads for the inner rounded rect excluding its inscribed
+  // rectangle.
+  RRectAttributes rrect_inner[8];
+  GetRRectAttributes(bounds, inner_rect, inner_corners, rrect_inner);
+
+  // Get the render quads for the outer rounded rect.
+  RRectAttributes rrect_outer[4];
+  GetRRectAttributes(bounds, outer_rect, outer_corners, rrect_outer);
+
+  // Add geometry to draw the area between the inner rrect and outer rrect.
+  for (int i = 0; i < arraysize(rrect_inner); ++i) {
+    for (int o = 0; o < arraysize(rrect_outer); ++o) {
+      math::RectF intersection = math::IntersectRects(
+          rrect_inner[i].bounds, rrect_outer[o].bounds);
+      if (!intersection.IsEmpty()) {
+        // Use two triangles to draw the intersection.
+        const RCorner& inner = rrect_inner[i].rcorner;
+        const RCorner& outer = rrect_outer[o].rcorner;
+        uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+        attributes_round_.emplace_back(
+            intersection.x(), intersection.y(), inner, outer);
+        attributes_round_.emplace_back(
+            intersection.right(), intersection.y(), inner, outer);
+        attributes_round_.emplace_back(
+            intersection.x(), intersection.bottom(), inner, outer);
+        attributes_round_.emplace_back(
+            intersection.right(), intersection.bottom(), inner, outer);
+        indices_.emplace_back(vert);
+        indices_.emplace_back(vert + 1);
+        indices_.emplace_back(vert + 2);
+        indices_.emplace_back(vert + 1);
+        indices_.emplace_back(vert + 2);
+        indices_.emplace_back(vert + 3);
+      }
+    }
+  }
+
+  graphics_state->ReserveVertexData(
+      attributes_round_.size() * sizeof(attributes_round_[0]));
+  graphics_state->ReserveVertexIndices(indices_.size());
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
index 1e4d590..6df79ca 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
@@ -15,6 +15,8 @@
 #ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
 #define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
 
+#include <vector>
+
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/color_rgba.h"
 #include "cobalt/renderer/rasterizer/egl/draw_object.h"
@@ -58,31 +60,43 @@
       ShaderProgramManager* program_manager) OVERRIDE;
   base::TypeId GetTypeId() const OVERRIDE;
 
- protected:
-  typedef ShaderVertexColorOffset CommonVertexShader;
-
-  struct VertexAttributes {
+ private:
+  struct VertexAttributesSquare {
+    VertexAttributesSquare(float x, float y, uint32_t color);
     float position[2];
-    float offset[2];
     uint32_t color;
   };
 
-  DrawRectShadowSpread(GraphicsState* graphics_state,
-                       const BaseState& base_state);
-  void SetupShader(const CommonVertexShader& shader,
-                   GraphicsState* graphics_state);
-  void SetVertex(VertexAttributes* vertex, float x, float y);
+  struct VertexAttributesRound {
+    VertexAttributesRound(float x, float y,
+        const RCorner& inner, const RCorner& outer);
+    float position[2];
+    RCorner rcorner_inner;
+    RCorner rcorner_outer;
+  };
 
-  math::RectF inner_rect_;
-  math::RectF outer_rect_;
-  OptionalRoundedCorners inner_corners_;
-  OptionalRoundedCorners outer_corners_;
-  math::PointF offset_center_;
-  float offset_scale_;
-  uint32_t color_;
+  void SetupVertexShader(GraphicsState* graphics_state,
+                         const ShaderVertexColor& shader);
+  void SetupVertexShader(GraphicsState* graphics_state,
+                         const ShaderVertexRcorner2& shader);
+
+  void SetGeometry(GraphicsState* graphics_state,
+                   const math::RectF& inner_rect,
+                   const math::RectF& outer_rect);
+  void SetGeometry(GraphicsState* graphics_state,
+                   const math::RectF& inner_rect,
+                   const render_tree::RoundedCorners& inner_corners,
+                   const math::RectF& outer_rect,
+                   const render_tree::RoundedCorners& outer_corners);
+
+  render_tree::ColorRGBA color_;
+
+  std::vector<VertexAttributesSquare> attributes_square_;
+  std::vector<VertexAttributesRound> attributes_round_;
+  std::vector<uint16_t> indices_;
 
   uint8_t* vertex_buffer_;
-  int vertex_count_;
+  uint16_t* index_buffer_;
 };
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
index f12e7ed..2443bbe 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
@@ -26,21 +26,7 @@
 namespace egl {
 
 namespace {
-const int kVertexCount = 4;
-
-struct VertexAttributes {
-  float position[2];
-  float offset[2];
-  uint32_t color;
-};
-
-void SetVertex(VertexAttributes* vertex, float x, float y, uint32_t color) {
-  vertex->position[0] = x;
-  vertex->position[1] = y;
-  vertex->offset[0] = x;
-  vertex->offset[1] = y;
-  vertex->color = color;
-}
+const int kVertexCount = 4 * 6;
 }  // namespace
 
 DrawRRectColor::DrawRRectColor(GraphicsState* graphics_state,
@@ -51,8 +37,14 @@
       rect_(rect),
       corners_(corners),
       vertex_buffer_(NULL) {
-  color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
+  color_ = GetDrawColor(color) * base_state_.opacity;
   graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
+
+  // Extract scale from the transform and move it into the vertex attributes
+  // so that the anti-aliased edges remain 1 pixel wide.
+  math::Vector2dF scale = RemoveScaleFromTransform();
+  rect_.Scale(scale.x(), scale.y());
+  corners_ = corners_.Scale(scale.x(), scale.y());
 }
 
 void DrawRRectColor::ExecuteUpdateVertexBuffer(
@@ -63,10 +55,31 @@
   VertexAttributes attributes[kVertexCount];
   math::RectF outer_rect(rect_);
   outer_rect.Outset(1.0f, 1.0f);
-  SetVertex(&attributes[0], outer_rect.x(), outer_rect.y(), color_);
-  SetVertex(&attributes[1], outer_rect.right(), outer_rect.y(), color_);
-  SetVertex(&attributes[2], outer_rect.right(), outer_rect.bottom(), color_);
-  SetVertex(&attributes[3], outer_rect.x(), outer_rect.bottom(), color_);
+
+  RRectAttributes rrect[4];
+  GetRRectAttributes(outer_rect, rect_, corners_, rrect);
+  for (int r = 0, v = 0; r < arraysize(rrect); ++r) {
+    attributes[v  ].position[0] = rrect[r].bounds.x();
+    attributes[v  ].position[1] = rrect[r].bounds.y();
+    attributes[v  ].rcorner =
+        RCorner(attributes[v  ].position, rrect[r].rcorner);
+    attributes[v+1].position[0] = rrect[r].bounds.right();
+    attributes[v+1].position[1] = rrect[r].bounds.y();
+    attributes[v+1].rcorner =
+        RCorner(attributes[v+1].position, rrect[r].rcorner);
+    attributes[v+2].position[0] = rrect[r].bounds.x();
+    attributes[v+2].position[1] = rrect[r].bounds.bottom();
+    attributes[v+2].rcorner =
+        RCorner(attributes[v+2].position, rrect[r].rcorner);
+    attributes[v+3].position[0] = rrect[r].bounds.right();
+    attributes[v+3].position[1] = rrect[r].bounds.bottom();
+    attributes[v+3].rcorner =
+        RCorner(attributes[v+3].position, rrect[r].rcorner);
+    attributes[v+4] = attributes[v+1];
+    attributes[v+5] = attributes[v+2];
+    v += 6;
+  }
+
   vertex_buffer_ = graphics_state->AllocateVertexData(sizeof(attributes));
   SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
 }
@@ -74,8 +87,8 @@
 void DrawRRectColor::ExecuteRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  ShaderProgram<ShaderVertexColorOffset,
-                ShaderFragmentColorRrect>* program;
+  ShaderProgram<ShaderVertexRcorner,
+                ShaderFragmentRcornerColor>* program;
   program_manager->GetProgram(&program);
   graphics_state->UseProgram(program->GetHandle());
   graphics_state->UpdateClipAdjustment(
@@ -90,23 +103,19 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, position));
   graphics_state->VertexAttribPointer(
-      program->GetVertexShader().a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
+      program->GetVertexShader().a_rcorner(), 4, GL_FLOAT, GL_FALSE,
       sizeof(VertexAttributes), vertex_buffer_ +
-      offsetof(VertexAttributes, color));
-  graphics_state->VertexAttribPointer(
-      program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
-      sizeof(VertexAttributes), vertex_buffer_ +
-      offsetof(VertexAttributes, offset));
+      offsetof(VertexAttributes, rcorner));
   graphics_state->VertexAttribFinish();
-  SetRRectUniforms(program->GetFragmentShader().u_rect(),
-                   program->GetFragmentShader().u_corners(),
-                   rect_, corners_, 0.5f);
-  GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, kVertexCount));
+
+  GL_CALL(glUniform4f(program->GetFragmentShader().u_color(),
+                      color_.r(), color_.g(), color_.b(), color_.a()));
+  GL_CALL(glDrawArrays(GL_TRIANGLES, 0, kVertexCount));
 }
 
 base::TypeId DrawRRectColor::GetTypeId() const {
-  return ShaderProgram<ShaderVertexColorOffset,
-                       ShaderFragmentColorRrect>::GetTypeId();
+  return ShaderProgram<ShaderVertexRcorner,
+                       ShaderFragmentRcornerColor>::GetTypeId();
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h
index c5cd055..de71e9e 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h
@@ -41,9 +41,14 @@
   base::TypeId GetTypeId() const OVERRIDE;
 
  private:
+  struct VertexAttributes {
+    float position[2];
+    RCorner rcorner;
+  };
+
   math::RectF rect_;
   render_tree::RoundedCorners corners_;
-  uint32_t color_;
+  render_tree::ColorRGBA color_;
 
   uint8_t* vertex_buffer_;
 };
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc
index 7ec422b..dbcbec1 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc
@@ -27,22 +27,7 @@
 namespace egl {
 
 namespace {
-const int kVertexCount = 4;
-
-struct VertexAttributes {
-  float position[2];
-  float offset[2];
-  float texcoord[2];
-};
-
-void SetVertex(VertexAttributes* vertex, float x, float y, float u, float v) {
-  vertex->position[0] = x;
-  vertex->position[1] = y;
-  vertex->offset[0] = x;
-  vertex->offset[1] = y;
-  vertex->texcoord[0] = u;
-  vertex->texcoord[1] = v;
-}
+const int kVertexCount = 4 * 6;
 }  // namespace
 
 DrawRRectColorTexture::DrawRRectColorTexture(GraphicsState* graphics_state,
@@ -60,25 +45,58 @@
   DCHECK(base_state_.rounded_scissor_corners);
   color_ = GetDrawColor(color) * base_state_.opacity;
   graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
+
+  // Extract scale from the transform and move it into the vertex attributes
+  // so that the anti-aliased edges remain 1 pixel wide.
+  math::Vector2dF scale = RemoveScaleFromTransform();
+  rect_.Scale(scale.x(), scale.y());
+  base_state_.rounded_scissor_rect.Scale(scale.x(), scale.y());
+  base_state_.rounded_scissor_corners =
+      base_state_.rounded_scissor_corners->Scale(scale.x(), scale.y());
 }
 
 void DrawRRectColorTexture::ExecuteUpdateVertexBuffer(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
+  const float kWidthScale = 1.0f / rect_.width();
+  const float kHeightScale = 1.0f / rect_.height();
+
   VertexAttributes attributes[kVertexCount];
-  SetVertex(&attributes[0], rect_.x(), rect_.y(),
-      texcoord_transform_(0, 2), texcoord_transform_(1, 2));    // uv = (0,0)
-  SetVertex(&attributes[1], rect_.right(), rect_.y(),
-      texcoord_transform_(0, 0) + texcoord_transform_(0, 2),    // uv = (1,0)
-      texcoord_transform_(1, 0) + texcoord_transform_(1, 2));
-  SetVertex(&attributes[2], rect_.right(), rect_.bottom(),
-      texcoord_transform_(0, 0) + texcoord_transform_(0, 1) +   // uv = (1,1)
-          texcoord_transform_(0, 2),
-      texcoord_transform_(1, 0) + texcoord_transform_(1, 1) +
-          texcoord_transform_(1, 2));
-  SetVertex(&attributes[3], rect_.x(), rect_.bottom(),
-      texcoord_transform_(0, 1) + texcoord_transform_(0, 2),    // uv = (0,1)
-      texcoord_transform_(1, 1) + texcoord_transform_(1, 2));
+  RRectAttributes rrect[4];
+  GetRRectAttributes(rect_, base_state_.rounded_scissor_rect,
+                     *base_state_.rounded_scissor_corners, rrect);
+  for (int r = 0, v = 0; r < arraysize(rrect); ++r) {
+    attributes[v  ].position[0] = rrect[r].bounds.x();
+    attributes[v  ].position[1] = rrect[r].bounds.y();
+    attributes[v  ].rcorner =
+        RCorner(attributes[v  ].position, rrect[r].rcorner);
+    attributes[v+1].position[0] = rrect[r].bounds.right();
+    attributes[v+1].position[1] = rrect[r].bounds.y();
+    attributes[v+1].rcorner =
+        RCorner(attributes[v+1].position, rrect[r].rcorner);
+    attributes[v+2].position[0] = rrect[r].bounds.x();
+    attributes[v+2].position[1] = rrect[r].bounds.bottom();
+    attributes[v+2].rcorner =
+        RCorner(attributes[v+2].position, rrect[r].rcorner);
+    attributes[v+3].position[0] = rrect[r].bounds.right();
+    attributes[v+3].position[1] = rrect[r].bounds.bottom();
+    attributes[v+3].rcorner =
+        RCorner(attributes[v+3].position, rrect[r].rcorner);
+
+    for (int t = v; t < v + 4; ++t) {
+      math::PointF texcoord(
+          (attributes[t].position[0] - rect_.x()) * kWidthScale,
+          (attributes[t].position[1] - rect_.y()) * kHeightScale);
+      texcoord = texcoord_transform_ * texcoord;
+      attributes[t].texcoord[0] = texcoord.x();
+      attributes[t].texcoord[1] = texcoord.y();
+    }
+
+    attributes[v+4] = attributes[v+1];
+    attributes[v+5] = attributes[v+2];
+    v += 6;
+  }
+
   vertex_buffer_ = graphics_state->AllocateVertexData(sizeof(attributes));
   SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
 
@@ -119,8 +137,8 @@
 void DrawRRectColorTexture::ExecuteRasterize(
     GraphicsState* graphics_state,
     ShaderProgramManager* program_manager) {
-  ShaderProgram<ShaderVertexOffsetTexcoord,
-                ShaderFragmentTexcoordColorRrect>* program;
+  ShaderProgram<ShaderVertexRcornerTexcoord,
+                ShaderFragmentRcornerTexcoordColor>* program;
   program_manager->GetProgram(&program);
   graphics_state->UseProgram(program->GetHandle());
   graphics_state->UpdateClipAdjustment(
@@ -135,19 +153,15 @@
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, position));
   graphics_state->VertexAttribPointer(
-      program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
+      program->GetVertexShader().a_rcorner(), 4, GL_FLOAT, GL_FALSE,
       sizeof(VertexAttributes), vertex_buffer_ +
-      offsetof(VertexAttributes, offset));
+      offsetof(VertexAttributes, rcorner));
   graphics_state->VertexAttribPointer(
       program->GetVertexShader().a_texcoord(), 2, GL_FLOAT, GL_FALSE,
       sizeof(VertexAttributes), vertex_buffer_ +
       offsetof(VertexAttributes, texcoord));
   graphics_state->VertexAttribFinish();
 
-  SetRRectUniforms(program->GetFragmentShader().u_rect(),
-                   program->GetFragmentShader().u_corners(),
-                   base_state_.rounded_scissor_rect,
-                   *base_state_.rounded_scissor_corners, 0.5f);
   GL_CALL(glUniform4f(program->GetFragmentShader().u_color(),
                       color_.r(), color_.g(), color_.b(), color_.a()));
   GL_CALL(glUniform4fv(program->GetFragmentShader().u_texcoord_clamp(), 1,
@@ -157,7 +171,7 @@
     graphics_state->ActiveBindTexture(
         program->GetFragmentShader().u_texture_texunit(),
         texture_->GetTarget(), texture_->gl_handle(), GL_REPEAT);
-    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, kVertexCount));
+    GL_CALL(glDrawArrays(GL_TRIANGLES, 0, kVertexCount));
     graphics_state->ActiveBindTexture(
         program->GetFragmentShader().u_texture_texunit(),
         texture_->GetTarget(), texture_->gl_handle(), GL_CLAMP_TO_EDGE);
@@ -165,13 +179,13 @@
     graphics_state->ActiveBindTexture(
         program->GetFragmentShader().u_texture_texunit(),
         texture_->GetTarget(), texture_->gl_handle());
-    GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, kVertexCount));
+    GL_CALL(glDrawArrays(GL_TRIANGLES, 0, kVertexCount));
   }
 }
 
 base::TypeId DrawRRectColorTexture::GetTypeId() const {
-  return ShaderProgram<ShaderVertexOffsetTexcoord,
-                       ShaderFragmentTexcoordColorRrect>::GetTypeId();
+  return ShaderProgram<ShaderVertexRcornerTexcoord,
+                       ShaderFragmentRcornerTexcoordColor>::GetTypeId();
 }
 
 }  // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h
index c65347e..98a5c84 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h
@@ -44,6 +44,12 @@
   base::TypeId GetTypeId() const OVERRIDE;
 
  private:
+  struct VertexAttributes {
+    float position[2];
+    float texcoord[2];
+    RCorner rcorner;
+  };
+
   math::Matrix3F texcoord_transform_;
   math::RectF rect_;
   render_tree::ColorRGBA color_;
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
index 0418992..74aba4b 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
@@ -17,6 +17,7 @@
 #include <GLES2/gl2.h>
 
 #include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
 
 namespace cobalt {
 namespace renderer {
@@ -24,6 +25,13 @@
 namespace egl {
 
 ShaderProgramManager::ShaderProgramManager() {
+  // These are shaders that get instantiated during video playback when the
+  // users starts interacting with the transport controls. They are preloaded
+  // to prevent UI-hiccups.
+  // These shaders are generated from egl/generated_shader_impl.h
+  Preload<ShaderVertexOffsetRcorner, ShaderFragmentColorBlurRrects>();
+  Preload<ShaderVertexColorOffset, ShaderFragmentColorInclude>();
+  Preload<ShaderVertexRcornerTexcoord, ShaderFragmentRcornerTexcoordColor>();
 }
 
 ShaderProgramManager::~ShaderProgramManager() {
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h
index f9c396e..7184e57 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h
@@ -44,6 +44,9 @@
   ShaderProgramBase* FindProgram(base::TypeId program_type_id);
   void AddProgram(base::TypeId program_type_id, ShaderProgramBase* program);
 
+  template <typename VertextShaderT, typename FragmentShaderT>
+  void Preload();
+
   typedef base::linked_hash_map<base::TypeId, ShaderProgramBase*> ProgramMap;
   ProgramMap program_map_;
 };
@@ -59,6 +62,12 @@
   *out_program = base::polymorphic_downcast<ShaderProgramType*>(program);
 }
 
+template <typename VertextShaderT, typename FragmentShaderT>
+inline void ShaderProgramManager::Preload() {
+  ShaderProgram<VertextShaderT, FragmentShaderT>* program;
+  GetProgram(&program);
+}
+
 }  // namespace egl
 }  // namespace rasterizer
 }  // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl
deleted file mode 100644
index f2897ad..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl
+++ /dev/null
@@ -1,35 +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.

-

-precision mediump float;

-

-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy), and a

-// matrix of corners. Each vector in the matrix represents a corner (order:

-// top left, top right, bottom left, bottom right). Each corner vec4 represents

-// (start.xy, radius.xy).

-uniform vec4 u_inner_rect;

-uniform mat4 u_inner_corners;

-uniform vec4 u_outer_rect;

-uniform mat4 u_outer_corners;

-

-varying vec2 v_offset;

-varying vec4 v_color;

-

-#include "function_is_outside_rrect.inc"

-

-void main() {

-  float inner_scale = IsOutsideRRect(v_offset, u_inner_rect, u_inner_corners);

-  float outer_scale = IsOutsideRRect(v_offset, u_outer_rect, u_outer_corners);

-  gl_FragColor = v_color * (inner_scale * (1.0 - outer_scale));

-}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
index 50ebd6a..4f15571 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
@@ -13,11 +13,12 @@
 // limitations under the License.

 

 precision mediump float;

+

+uniform vec4 u_color;

 uniform vec4 u_blur_rect;

 uniform vec2 u_scale_add;

 

 varying vec2 v_offset;

-varying vec4 v_color;

 

 #include "function_gaussian_integral.inc"

 

@@ -27,5 +28,5 @@
   float integral = GaussianIntegral(u_blur_rect.xz - v_offset.xx) *

                    GaussianIntegral(u_blur_rect.yw - v_offset.yy);

   float blur_scale = integral * u_scale_add.x + u_scale_add.y;

-  gl_FragColor = v_color * blur_scale;

+  gl_FragColor = u_color * blur_scale;

 }

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
index ee8c4ae..efc8747 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
@@ -14,13 +14,6 @@
 

 precision mediump float;

 

-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy)

-// and a matrix of corners. Each vector in the matrix represents a corner

-// (order: top left, top right, bottom left, bottom right). Each corner vec4

-// represents (start.xy, radius.xy).

-uniform vec4 u_scissor_rect;

-uniform mat4 u_scissor_corners;

-

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

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

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

@@ -49,14 +42,16 @@
 // inset shadow with scissor rect behaving as an inclusive scissor.

 uniform vec2 u_scale_add;

 

+uniform vec4 u_color;

+

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

 // translate pixel distances into sigma distances.

 uniform vec2 u_sigma_scale;

 

 varying vec2 v_offset;

-varying vec4 v_color;

+varying vec4 v_rcorner;

 

-#include "function_is_outside_rrect.inc"

+#include "function_is_outside_rcorner.inc"

 #include "function_gaussian_integral.inc"

 

 vec2 GetXExtents(float y) {

@@ -126,8 +121,7 @@
 

 void main() {

   float scissor_scale =

-      IsOutsideRRect(v_offset, u_scissor_rect, u_scissor_corners) *

-      u_scale_add.x + u_scale_add.y;

+      IsOutsideRCorner(v_rcorner) * u_scale_add.x + u_scale_add.y;

   float blur_scale = GetBlur(v_offset) * u_scale_add.x + u_scale_add.y;

-  gl_FragColor = v_color * (blur_scale * scissor_scale);

+  gl_FragColor = u_color * (blur_scale * scissor_scale);

 }

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl
deleted file mode 100644
index 26549ed..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl
+++ /dev/null
@@ -1,32 +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.

-

-precision mediump float;

-

-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy), and a

-// matrix of corners. Each vector in the matrix represents a corner (order:

-// top left, top right, bottom left, bottom right). Each corner vec4 represents

-// (start.xy, radius.xy).

-uniform vec4 u_rect;

-uniform mat4 u_corners;

-

-varying vec2 v_offset;

-varying vec4 v_color;

-

-#include "function_is_outside_rrect.inc"

-

-void main() {

-  float scale = IsOutsideRRect(v_offset, u_rect, u_corners);

-  gl_FragColor = v_color * (1.0 - scale);

-}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl
new file mode 100644
index 0000000..c7a8601
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+precision mediump float;

+

+uniform vec4 u_color;

+varying vec4 v_rcorner_inner;

+varying vec4 v_rcorner_outer;

+

+#include "function_is_outside_rcorner.inc"

+

+void main() {

+  float inner_scale = IsOutsideRCorner(v_rcorner_inner);

+  float outer_scale = 1.0 - IsOutsideRCorner(v_rcorner_outer);

+  gl_FragColor = u_color * (inner_scale * outer_scale);

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl
new file mode 100644
index 0000000..02c2425
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+precision mediump float;

+

+uniform vec4 u_color;

+varying vec4 v_rcorner;

+

+#include "function_is_outside_rcorner.inc"

+

+void main() {

+  float scale = IsOutsideRCorner(v_rcorner);

+  gl_FragColor = u_color * (1.0 - scale);

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl
new file mode 100644
index 0000000..4676317
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+precision mediump float;

+

+uniform vec4 u_color;

+uniform vec4 u_texcoord_clamp;

+uniform sampler2D u_texture;

+

+varying vec4 v_rcorner;

+varying vec2 v_texcoord;

+

+#include "function_is_outside_rcorner.inc"

+

+void main() {

+  float scale = IsOutsideRCorner(v_rcorner);

+  gl_FragColor = u_color * (1.0 - scale) * texture2D(u_texture,

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

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl
deleted file mode 100644
index 34a25ad..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl
+++ /dev/null
@@ -1,37 +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.

-

-precision mediump float;

-

-uniform vec4 u_color;

-uniform vec4 u_texcoord_clamp;

-uniform sampler2D u_texture;

-

-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy), and a

-// matrix of corners. Each vector in the matrix represents a corner (order:

-// top left, top right, bottom left, bottom right). Each corner vec4 represents

-// (start.xy, radius.xy).

-uniform vec4 u_rect;

-uniform mat4 u_corners;

-

-varying vec2 v_offset;

-varying vec2 v_texcoord;

-

-#include "function_is_outside_rrect.inc"

-

-void main() {

-  float scale = IsOutsideRRect(v_offset, u_rect, u_corners);

-  gl_FragColor = u_color * (1.0 - scale) * texture2D(u_texture,

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

-}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc b/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc
new file mode 100644
index 0000000..5233ae9
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc
@@ -0,0 +1,37 @@
+// 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.

+

+// Return 0 if the given position is inside the rounded corner, or scale

+// towards 1 as it goes outside a 1-pixel anti-aliasing border.

+// |rcorner| is a vec4 representing (scaled.xy, 1 / radius.xy) with scaled.xy

+//   representing the offset of the current position in terms of radius.xy

+//   (i.e. offset.xy / radius.xy). The scaled.xy values can be negative if the

+//   current position is outside the corner start.

+float IsOutsideRCorner(vec4 rcorner) {

+  // Estimate the distance to an implicit function using

+  //   dist = f(x,y) / length(gradient(f(x,y)))

+  // For an ellipse, f(x,y) = x^2 / a^2 + y^2 / b^2 - 1.

+  vec2 scaled = max(rcorner.xy, 0.0);

+  float implicit = dot(scaled, scaled) - 1.0;

+

+  // NOTE: To accommodate large radius values using mediump floats, rcorner.zw

+  //   was scaled by kRCornerGradientScale in the vertex attribute data.

+  //   Multiply inv_gradient by kRCornerGradientScale to undo that scaling.

+  const float kRCornerGradientScale = 16.0;

+  vec2 gradient = 2.0 * scaled * rcorner.zw;

+  float inv_gradient = kRCornerGradientScale *

+      inversesqrt(max(dot(gradient, gradient), 0.0001));

+

+  return clamp(0.5 + implicit * inv_gradient, 0.0, 1.0);

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc b/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc
deleted file mode 100644
index e3e58b9..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc
+++ /dev/null
@@ -1,51 +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.

-

-// Return 0 if the given point is inside the rounded rect, or scale towards 1

-// as it goes outside a 1-pixel anti-aliasing border.

-// |rect| represents (min.xy, max.xy) of the encompassing rectangle.

-// |corners| is a matrix with each vec4 representing (start.xy, radius.xy) of

-//   a corner. The order is top left, top right, bottom left, bottom right.

-float IsOutsideRRect(vec2 point, vec4 rect, mat4 corners) {

-  vec4 select_corner = vec4(

-      step(point.x, corners[0].x) * step(point.y, corners[0].y),

-      step(corners[1].x, point.x) * step(point.y, corners[1].y),

-      step(point.x, corners[2].x) * step(corners[2].y, point.y),

-      step(corners[3].x, point.x) * step(corners[3].y, point.y));

-  if (dot(select_corner, vec4(1.0)) > 0.5) {

-    // Estimate the amount of anti-aliasing that should be used by comparing

-    // x^2 / a^2 + y^2 / b^2 for the ellipse and ellipse + 1 pixel.

-    vec4 corner = corners * select_corner;

-    vec2 pixel_offset = point - corner.xy;

-

-    if (abs(corner.z - corner.w) < 0.1) {

-      // This is a square or round corner.

-      return clamp(length(pixel_offset) - corner.z, 0.0, 1.0);

-    }

-

-    vec2 offset_min = pixel_offset / corner.zw;

-    vec2 offset_max = pixel_offset / (corner.zw + vec2(1.0));

-    float result_min = dot(offset_min, offset_min);

-    float result_max = dot(offset_max, offset_max);

-

-    // Return 1.0 if outside, or interpolate if in the border, or 0 if inside.

-    return (result_max >= 1.0) ? 1.0 :

-        max(result_min - 1.0, 0.0) / (result_min - result_max);

-  }

-

-  return clamp(rect.x - point.x, 0.0, 1.0) +

-         clamp(point.x - rect.z, 0.0, 1.0) +

-         clamp(rect.y - point.y, 0.0, 1.0) +

-         clamp(point.y - rect.w, 0.0, 1.0);

-}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
index 9d00899..2ebc954 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
@@ -20,22 +20,25 @@
     'generate_class_script': '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/generate_shader_impl.py',
     'shader_sources': [
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color.glsl',
-      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_include.glsl',
-      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_opacity_texcoord1d.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord.glsl',
-      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_gaussian_integral.inc',
-      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_offset.glsl',
-      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl',
+      '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl',
       '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl',
     ],
   },
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
new file mode 100644
index 0000000..3d9ff46
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+uniform vec4 u_clip_adjustment;

+uniform mat3 u_view_matrix;

+attribute vec2 a_position;

+attribute vec4 a_rcorner;

+varying vec2 v_offset;

+varying vec4 v_rcorner;

+

+void main() {

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

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

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

+  v_offset = a_position;

+  v_rcorner = a_rcorner;

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl
deleted file mode 100644
index df81d24..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl
+++ /dev/null
@@ -1,29 +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.

-

-uniform vec4 u_clip_adjustment;

-uniform mat3 u_view_matrix;

-attribute vec2 a_position;

-attribute vec2 a_offset;

-attribute vec2 a_texcoord;

-varying vec2 v_offset;

-varying vec2 v_texcoord;

-

-void main() {

-  vec3 pos2d = u_view_matrix * vec3(a_position, 1);

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

-                     u_clip_adjustment.zw, 0, pos2d.z);

-  v_offset = a_offset;

-  v_texcoord = a_texcoord;

-}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl
new file mode 100644
index 0000000..a84d4f2
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+uniform vec4 u_clip_adjustment;

+uniform mat3 u_view_matrix;

+attribute vec2 a_position;

+attribute vec4 a_rcorner;

+varying vec4 v_rcorner;

+

+void main() {

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

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

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

+  v_rcorner = a_rcorner;

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl
new file mode 100644
index 0000000..fa4d155
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+uniform vec4 u_clip_adjustment;

+uniform mat3 u_view_matrix;

+attribute vec2 a_position;

+attribute vec4 a_rcorner_inner;

+attribute vec4 a_rcorner_outer;

+varying vec4 v_rcorner_inner;

+varying vec4 v_rcorner_outer;

+

+void main() {

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

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

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

+  v_rcorner_inner = a_rcorner_inner;

+  v_rcorner_outer = a_rcorner_outer;

+}

diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl
new file mode 100644
index 0000000..103d1ab
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.

+//

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

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

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

+//

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

+//

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

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

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

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

+// limitations under the License.

+

+uniform vec4 u_clip_adjustment;

+uniform mat3 u_view_matrix;

+attribute vec2 a_position;

+attribute vec4 a_rcorner;

+attribute vec2 a_texcoord;

+varying vec4 v_rcorner;

+varying vec2 v_texcoord;

+

+void main() {

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

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

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

+  v_rcorner = a_rcorner;

+  v_texcoord = a_texcoord;

+}

diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index a3f66eb..760bb4d 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -216,6 +216,44 @@
           RotateMatrix(static_cast<float>(M_PI) / 6.0f)));
 }
 
+TEST_F(PixelTest, ScaledThenRotatedRectWithDifferentRoundedCorners) {
+  RoundedCorner top_left(6, 15);
+  RoundedCorner top_right(0, 0);
+  RoundedCorner bottom_right(6, 25);
+  RoundedCorner bottom_left(2, 25);
+
+  scoped_ptr<RoundedCorners> rounded_corners(
+      new RoundedCorners(top_left, top_right, bottom_right, bottom_left));
+
+  TestTree(new MatrixTransformNode(
+      new RectNode(RectF(-7, -25, 14, 50),
+                   scoped_ptr<Brush>(
+                       new SolidColorBrush(ColorRGBA(1, 1, 1, 1))),
+                   rounded_corners.Pass()),
+      TranslateMatrix(100.0f, 100.0f) *
+      RotateMatrix(static_cast<float>(M_PI) / 3.0f) *
+      ScaleMatrix(-10.0f, 2.0f)));
+}
+
+TEST_F(PixelTest, RotatedThenScaledRectWithDifferentRoundedCorners) {
+  RoundedCorner top_left(4, 7);
+  RoundedCorner top_right(0, 0);
+  RoundedCorner bottom_right(10, 2);
+  RoundedCorner bottom_left(5, 3);
+
+  scoped_ptr<RoundedCorners> rounded_corners(
+      new RoundedCorners(top_left, top_right, bottom_right, bottom_left));
+
+  TestTree(new MatrixTransformNode(
+      new RectNode(RectF(-10, -7, 20, 14),
+                   scoped_ptr<Brush>(
+                       new SolidColorBrush(ColorRGBA(1, 1, 1, 1))),
+                   rounded_corners.Pass()),
+      TranslateMatrix(100.0f, 100.0f) *
+      ScaleMatrix(6.0f, 9.0f) *
+      RotateMatrix(static_cast<float>(M_PI) / 6.0f)));
+}
+
 TEST_F(PixelTest, RedRectWithDifferentRoundedCornersOnTopLeftOfSurface) {
   RoundedCorner top_left(10, 10);
   RoundedCorner top_right(20, 20);
@@ -1952,6 +1990,19 @@
       new ImageNode(image)));
 }
 
+TEST_F(PixelTest, ScaledThenRotatedRoundedCornersViewportOverImage) {
+  scoped_refptr<Image> image =
+      CreateColoredCheckersImage(GetResourceProvider(), output_surface_size());
+
+  TestTree(new MatrixTransformNode(
+      new FilterNode(
+          ViewportFilter(RectF(25, 5, 150, 10), RoundedCorners(25, 2)),
+          new ImageNode(image)),
+      TranslateMatrix(-30, 130) *
+      RotateMatrix(static_cast<float>(M_PI / 3.0f)) *
+      ScaleMatrix(1.0f, 10.0f)));
+}
+
 TEST_F(PixelTest, RoundedCornersViewportOverWrappingImage) {
   scoped_refptr<Image> image =
       CreateColoredCheckersImage(GetResourceProvider(), output_surface_size());
@@ -2829,6 +2880,14 @@
       Shadow(Vector2dF(0.0f, 0.0f), 100.0f, ColorRGBA(0, 0, 0, 1))));
 }
 
+TEST_F(PixelTest, ScaledBoxShadowWithSpreadAndBlurCentered) {
+  TestTree(new MatrixTransformNode(CreateShadowRectWithBackground(
+      output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+      ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), RectF(50, 8, 100, 4),
+      Shadow(Vector2dF(0.0f, 0.0f), 3.0f, ColorRGBA(0, 0, 0, 1)), false, 5.0f),
+      ScaleMatrix(1.0f, 10.0f)));
+}
+
 TEST_F(PixelTest, TransparentBoxShadowBlurOnGreenBackgroundCentered) {
   TestTree(CreateShadowRectWithBackground(
       output_surface_size(), ColorRGBA(0.3f, 0.8f, 0.3f, 1.0f),
@@ -2916,6 +2975,15 @@
       RoundedCorners(25, 25)));
 }
 
+TEST_F(PixelTest, ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners) {
+  TestTree(new MatrixTransformNode(CreateShadowRectWithBackground(
+      output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+      ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), RectF(6, 25, 2, 100),
+      Shadow(Vector2dF(0.0f, 0.0f), 0.0f, ColorRGBA(0, 0, 0, 1)), false, 5.0f,
+      RoundedCorners(1, 50)),
+      ScaleMatrix(15.0f, 1.0f)));
+}
+
 TEST_F(PixelTest, BoxShadowCircleWithInset25pxSpread1pxBlurAndRoundedCorners) {
   TestTree(CreateShadowRectWithBackground(
       output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
@@ -3034,6 +3102,16 @@
 }
 
 TEST_F(PixelTest,
+       ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners) {
+  TestTree(new MatrixTransformNode(CreateShadowRectWithBackground(
+      output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+      ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), RectF(20, 5, 140, 10),
+      Shadow(Vector2dF(8.0f, 1.0f), 3.0f, ColorRGBA(0, 0, 0, 1)), false, 4.0f,
+      RoundedCorners(70, 5)),
+      ScaleMatrix(1.0f, 10.0f)));
+}
+
+TEST_F(PixelTest,
        BoxShadowEllipseWithInset25pxSpread50pxBlurAndRoundedCorners) {
   TestTree(CreateShadowRectWithBackground(
       output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 576969c..ce39cca 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -60,7 +60,7 @@
   // on multiple threads simultaneously later.
   SkSafeUnref(SkFontMgr::RefDefault());
 
-#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
   decode_target_graphics_context_provider_.egl_display =
       cobalt_context_->system_egl()->GetDisplay();
   decode_target_graphics_context_provider_.egl_context =
@@ -68,7 +68,7 @@
   decode_target_graphics_context_provider_.gles_context_runner =
       &HardwareResourceProvider::GraphicsContextRunner;
   decode_target_graphics_context_provider_.gles_context_runner_context = this;
-#endif  // SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 }
 
 HardwareResourceProvider::~HardwareResourceProvider() {
@@ -146,7 +146,7 @@
       self_message_loop_));
 }
 
-#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
 namespace {
 
 #if SB_API_VERSION < SB_DECODE_TARGET_PLANES_FOR_FORMAT
@@ -378,7 +378,7 @@
   }
 }
 
-#endif  // SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 
 scoped_ptr<RawImageMemory> HardwareResourceProvider::AllocateRawImageMemory(
     size_t size_in_bytes, size_t alignment) {
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 33ed4d2..5ad0c4a 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -56,7 +56,6 @@
       scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
 
 #if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 4
 
   scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE;
@@ -71,24 +70,6 @@
   // Whether SbDecodeTargetIsSupported or not.
   bool SupportsSbDecodeTarget() OVERRIDE { return true; }
 
-#elif SB_API_VERSION >= 3
-
-  scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
-      SbDecodeTarget decode_target) OVERRIDE {
-    NOTREACHED()
-        << "CreateImageFromSbDecodeTarget is not supported on EGL yet.";
-    SbDecodeTargetDestroy(decode_target);
-    return NULL;
-  }
-
-  // Return the associated SbDecodeTargetProvider with the ResourceProvider,
-  // if it exists.  Returns NULL if SbDecodeTarget is not supported.
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-
-  // Whether SbDecodeTargetIsSupported or not.
-  bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-
-#endif  // SB_API_VERSION >= 4
 #endif  // SB_HAS(GRAPHICS)
 
   scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
@@ -147,7 +128,7 @@
   TextShaper text_shaper_;
   int max_texture_size_;
 
-#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
   static void GraphicsContextRunner(
       SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
       SbDecodeTargetGlesContextRunnerTarget target_function,
@@ -155,7 +136,7 @@
 
   SbDecodeTargetGraphicsContextProvider
       decode_target_graphics_context_provider_;
-#endif  // SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#endif  // SB_HAS(GRAPHICS)
 
   // We keep a handle to the message loop that this resource provider was
   // created on.  This message loop is used whenever we need to issue graphics
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
index 0f2159b..918a60e 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -949,6 +949,11 @@
   SkiaBrushVisitor brush_visitor(&paint, *draw_state);
   brush->Accept(&brush_visitor);
 
+  if (!draw_state->render_target->getTotalMatrix().preservesAxisAlignment()) {
+    // Enable anti-aliasing if we're rendering a rotated or skewed box.
+    paint.setAntiAlias(true);
+  }
+
   draw_state->render_target->drawRect(
       SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()), paint);
 }
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index 67e8e2d..492d3ab 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -47,7 +47,6 @@
       scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
 
 #if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 4
   scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
       SbDecodeTarget decode_target) OVERRIDE {
     NOTREACHED();
@@ -61,18 +60,6 @@
   }
 
   bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-#elif SB_API_VERSION >= 3
-  scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
-      SbDecodeTarget decode_target) OVERRIDE {
-    NOTREACHED();
-    SbDecodeTargetDestroy(decode_target);
-    return NULL;
-  }
-
-  SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-
-  bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-#endif  // SB_API_VERSION >= 4
 #endif  // SB_HAS(GRAPHICS)
 
   scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
diff --git a/src/cobalt/renderer/rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png
new file mode 100644
index 0000000..b1725a0
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png
new file mode 100644
index 0000000..94a9b2f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png
new file mode 100644
index 0000000..26f22df
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png
new file mode 100644
index 0000000..9c2b5b6
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png
new file mode 100644
index 0000000..9b62917
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png
new file mode 100644
index 0000000..55c2d7d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png
Binary files differ
diff --git a/src/cobalt/script/error_report.h b/src/cobalt/script/error_report.h
new file mode 100644
index 0000000..82c1b12
--- /dev/null
+++ b/src/cobalt/script/error_report.h
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SCRIPT_ERROR_REPORT_H_
+#define COBALT_SCRIPT_ERROR_REPORT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/value_handle.h"
+
+namespace cobalt {
+namespace script {
+
+struct ErrorReport {
+ public:
+  ErrorReport() : line_number(0), column_number(0), is_muted(false) {}
+
+  std::string message;
+  std::string filename;
+  uint32 line_number;
+  uint32 column_number;
+  scoped_ptr<script::ValueHandleHolder> error;
+  bool is_muted;
+};
+
+}  // namespace script
+}  // namespace cobalt
+
+#endif  // COBALT_SCRIPT_ERROR_REPORT_H_
diff --git a/src/cobalt/script/fake_global_environment.h b/src/cobalt/script/fake_global_environment.h
index ca8c3fa..391c5e7 100644
--- a/src/cobalt/script/fake_global_environment.h
+++ b/src/cobalt/script/fake_global_environment.h
@@ -49,6 +49,8 @@
   void EnableEval() OVERRIDE {}
   void DisableJit() OVERRIDE {}
   void SetReportEvalCallback(const base::Closure& /*report_eval*/) OVERRIDE {}
+  void SetReportErrorCallback(
+      const ReportErrorCallback& /*report_eval*/) OVERRIDE {}
   void Bind(const std::string& /*identifier*/,
             const scoped_refptr<Wrappable>& /*impl*/) OVERRIDE {}
   ScriptValueFactory* script_value_factory() { return NULL; }
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h
index 794cc22..039f05d 100644
--- a/src/cobalt/script/global_environment.h
+++ b/src/cobalt/script/global_environment.h
@@ -19,6 +19,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
+#include "cobalt/script/error_report.h"
 #include "cobalt/script/opaque_handle.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/script_value_factory.h"
@@ -35,6 +36,9 @@
 // Manages a handle to a JavaScript engine's global object.
 class GlobalEnvironment : public base::RefCounted<GlobalEnvironment> {
  public:
+  typedef base::Callback<bool(const ErrorReport& error_report)>
+      ReportErrorCallback;
+
   // Create a new global object with bindings as defined for the definition of
   // the GlobalInterface type. The IDL for this interface must have the
   // PrimaryGlobal or Global extended attribute.
@@ -95,6 +99,10 @@
   // constructor is used.
   virtual void SetReportEvalCallback(const base::Closure& report_eval) = 0;
 
+  // Set a callback that will be fired whenever a JavaScript error occurs.
+  virtual void SetReportErrorCallback(
+      const ReportErrorCallback& report_error) = 0;
+
   // Dynamically bind a cpp object to the javascript global object with the
   // supplied identifier.
   // This method is useful for testing and debug purposes, as well as for
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index c850506..2673778 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -339,6 +339,12 @@
   report_eval_ = report_eval;
 }
 
+void MozjsGlobalEnvironment::SetReportErrorCallback(
+    const ReportErrorCallback& report_error_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  report_error_callback_ = report_error_callback;
+}
+
 void MozjsGlobalEnvironment::Bind(const std::string& identifier,
                                   const scoped_refptr<Wrappable>& impl) {
   TRACK_MEMORY_SCOPE("Javascript");
@@ -456,19 +462,54 @@
 
 void MozjsGlobalEnvironment::ReportError(const char* message,
                                          JSErrorReport* report) {
-  std::string error_message;
+  JS::RootedValue exception(context_);
+  ::JS_GetPendingException(context_, &exception);
+
+  // Note: we must do this before running any more code on context.
+  ::JS_ClearPendingException(context_);
+
+  // Populate the error report.
+  ErrorReport error_report;
   if (report->errorNumber == JSMSG_CSP_BLOCKED_EVAL) {
-    error_message = eval_disabled_message_.value_or(message);
+    error_report.message = eval_disabled_message_.value_or(message);
   } else {
-    error_message = message;
+    error_report.message = message;
+  }
+  error_report.filename =
+      report->filename ? report->filename : "<internal exception>";
+  error_report.line_number = report->lineno;
+  error_report.column_number = report->column;
+  // Let error object be the object that represents the error: in the case of
+  // an uncaught exception, that would be the object that was thrown; in the
+  // case of a JavaScript error that would be an Error object. If there is no
+  // corresponding object, then the null value must be used instead.
+  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+  if (exception.isObject()) {
+    error_report.error.reset(
+        new MozjsValueHandleHolder(exception, context_, wrapper_factory()));
+  }
+  error_report.is_muted = report->isMuted;
+
+  // If this isn't simply a warning, and the error wasn't caused by JS running
+  // out of memory (in which case the callback will fail as well), then run
+  // the callback. In the case that it returns that the script was handled,
+  // simply return; the error should only be reported to the user if it wasn't
+  // handled.
+  if (!JSREPORT_IS_WARNING(report->flags) &&
+      report->errorNumber != JSMSG_OUT_OF_MEMORY &&
+      !report_error_callback_.is_null() &&
+      report_error_callback_.Run(error_report)) {
+    return;
   }
 
+  // If the error is not handled, then the error may be reported to the user.
+  //   https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors-in-documents
   if (last_error_message_) {
-    *last_error_message_ = error_message;
+    *last_error_message_ = error_report.message;
   } else {
-    const char* filename = report->filename ? report->filename : "(none)";
-    LOG(ERROR) << "JS Error: " << filename << ":" << report->lineno << ":"
-               << report->column << ": " << error_message;
+    LOG(ERROR) << "JS Error: " << error_report.filename << ":"
+               << error_report.line_number << ":" << error_report.column_number
+               << ": " << error_report.message;
   }
 }
 
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index e34eabe..a7bccae 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -75,6 +75,9 @@
 
   void SetReportEvalCallback(const base::Closure& report_eval) OVERRIDE;
 
+  void SetReportErrorCallback(
+      const ReportErrorCallback& report_error_callback) OVERRIDE;
+
   void Bind(const std::string& identifier,
             const scoped_refptr<Wrappable>& impl) OVERRIDE;
 
@@ -182,6 +185,7 @@
   bool eval_enabled_;
   base::optional<std::string> eval_disabled_message_;
   base::Closure report_eval_;
+  ReportErrorCallback report_error_callback_;
 
   friend class GlobalObjectProxy;
 };
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index 71b593d..52af1fe 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -344,6 +344,12 @@
   report_eval_ = report_eval;
 }
 
+void MozjsGlobalEnvironment::SetReportErrorCallback(
+    const ReportErrorCallback& report_error_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  report_error_callback_ = report_error_callback;
+}
+
 void MozjsGlobalEnvironment::Bind(const std::string& identifier,
                                   const scoped_refptr<Wrappable>& impl) {
   TRACK_MEMORY_SCOPE("Javascript");
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h
index 4914e18..e221a5d 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -74,6 +74,9 @@
 
   void SetReportEvalCallback(const base::Closure& report_eval) OVERRIDE;
 
+  void SetReportErrorCallback(
+      const ReportErrorCallback& report_error_callback) OVERRIDE;
+
   void Bind(const std::string& identifier,
             const scoped_refptr<Wrappable>& impl) OVERRIDE;
 
@@ -181,6 +184,7 @@
   bool eval_enabled_;
   base::optional<std::string> eval_disabled_message_;
   base::Closure report_eval_;
+  ReportErrorCallback report_error_callback_;
 
   friend class GlobalObjectProxy;
 };
diff --git a/src/cobalt/script/script.gyp b/src/cobalt/script/script.gyp
index a7e9192..34a7c4e 100644
--- a/src/cobalt/script/script.gyp
+++ b/src/cobalt/script/script.gyp
@@ -21,6 +21,7 @@
       'type': 'static_library',
       'sources': [
         'call_frame.h',
+        'error_report.h',
         'exception_message.cc',
         'exception_message.h',
         'execution_state.cc',
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
index 23dedfd..6fb7a34 100644
--- a/src/cobalt/speech/google_speech_service.cc
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -290,14 +290,12 @@
 
   const char* speech_api_key = "";
 #if defined(OS_STARBOARD)
-#if SB_API_VERSION >= 2
   const int kSpeechApiKeyLength = 100;
   char buffer[kSpeechApiKeyLength] = {0};
   bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer,
                                     SB_ARRAY_SIZE_INT(buffer));
   SB_DCHECK(result);
   speech_api_key = result ? buffer : "";
-#endif  // SB_API_VERSION >= 2
 #endif  // defined(OS_STARBOARD)
 
   up_url = AppendQueryParameter(up_url, "key", speech_api_key);
diff --git a/src/cobalt/speech/speech_configuration.h b/src/cobalt/speech/speech_configuration.h
index 8b9e0ba..c23ee5e 100644
--- a/src/cobalt/speech/speech_configuration.h
+++ b/src/cobalt/speech/speech_configuration.h
@@ -16,15 +16,14 @@
 #define COBALT_SPEECH_SPEECH_CONFIGURATION_H_
 
 #include "build/build_config.h"
-
-#if defined(OS_STARBOARD)
 #include "starboard/configuration.h"
-#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
+
+#if SB_HAS(MICROPHONE)
 #define SB_USE_SB_MICROPHONE 1
-#endif  // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif  // SB_HAS(MICROPHONE)
+
 #if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
 #define SB_USE_SB_SPEECH_RECOGNIZER 1
 #endif  // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
-#endif  // defined(OS_STARBOARD)
 
 #endif  // COBALT_SPEECH_SPEECH_CONFIGURATION_H_
diff --git a/src/cobalt/speech/speech_synthesis.cc b/src/cobalt/speech/speech_synthesis.cc
index 5e3e1bb..fb34eaa 100644
--- a/src/cobalt/speech/speech_synthesis.cc
+++ b/src/cobalt/speech/speech_synthesis.cc
@@ -100,12 +100,6 @@
     return;
   }
 
-#if SB_API_VERSION < 4
-  // DEPRECATED IN API VERSION 4
-  std::string language =
-      utterance->lang().empty() ? navigator_->language() : utterance->lang();
-  SbSpeechSynthesisSetLanguage(language.c_str());
-#endif
   SB_DLOG(INFO) << "Speaking: \"" << utterance->text() << "\" "
                 << utterance->lang();
   SbSpeechSynthesisSpeak(utterance->text().c_str());
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index e9daa78..88582da 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -15,6 +15,6 @@
 #define COBALT_VERSION_H_
 
 // Cobalt release number.
-#define COBALT_VERSION "12"
+#define COBALT_VERSION "13"
 
 #endif  // COBALT_VERSION_H_
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index 321c8c4..146b674 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -21,6 +21,8 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/string_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
 #include "net/base/tcp_listen_socket.h"
 #include "net/server/http_server_request_info.h"
 
@@ -177,11 +179,21 @@
 
 WebDriverServer::WebDriverServer(int port, const std::string& listen_ip,
                                  const HandleRequestCallback& callback)
-    : handle_request_callback_(callback) {
+    : handle_request_callback_(callback),
+      server_address_("WebDriver.Server",
+                      "Address to communicate with WebDriver.") {
   // Create http server
   factory_.reset(new net::TCPListenSocketFactory(listen_ip, port));
   server_ = new net::HttpServer(*factory_, this);
-  LOG(INFO) << "Starting WebDriver server on port " << port;
+  GURL address;
+  int result = GetLocalAddress(&address);
+  if (result == net::OK) {
+    LOG(INFO) << "Starting WebDriver server on port " << port;
+    server_address_ = address.spec();
+  } else {
+    LOG(WARNING) << "Could not start WebDriver server";
+    server_address_ = "<NOT RUNNING>";
+  }
 }
 
 void WebDriverServer::OnHttpRequest(int connection_id,
@@ -217,5 +229,14 @@
                                parameters.Pass(), response_handler.Pass());
 }
 
+int WebDriverServer::GetLocalAddress(GURL* out) const {
+  net::IPEndPoint ip_addr;
+  int result = server_->GetLocalAddress(&ip_addr);
+  if (result == net::OK) {
+    *out = GURL("http://" + ip_addr.ToString());
+  }
+  return result;
+}
+
 }  // namespace webdriver
 }  // namespace cobalt
diff --git a/src/cobalt/webdriver/server.h b/src/cobalt/webdriver/server.h
index 71a66fa..03ba2b4 100644
--- a/src/cobalt/webdriver/server.h
+++ b/src/cobalt/webdriver/server.h
@@ -22,7 +22,9 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/threading/thread.h"
 #include "base/values.h"
+#include "cobalt/base/c_val.h"
 #include "cobalt/webdriver/protocol/server_status.h"
+#include "googleurl/src/gurl.h"
 #include "net/base/stream_listen_socket.h"
 #include "net/server/http_server.h"
 
@@ -92,10 +94,13 @@
   void OnClose(int) OVERRIDE {}  // NOLINT(readability/casting)
 
  private:
+  int GetLocalAddress(GURL* out) const;
+
   base::ThreadChecker thread_checker_;
   HandleRequestCallback handle_request_callback_;
   scoped_ptr<net::StreamListenSocketFactory> factory_;
   scoped_refptr<net::HttpServer> server_;
+  base::CVal<std::string> server_address_;
 };
 
 }  // namespace webdriver
diff --git a/src/media/audio/android/audio_manager_android.cc b/src/media/audio/android/audio_manager_android.cc
deleted file mode 100644
index 0e4d6ba..0000000
--- a/src/media/audio/android/audio_manager_android.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/android/audio_manager_android.h"
-
-#include "base/logging.h"
-#include "media/audio/android/opensles_input.h"
-#include "media/audio/android/opensles_output.h"
-#include "media/audio/audio_manager.h"
-#include "media/audio/fake_audio_input_stream.h"
-
-namespace media {
-
-// Maximum number of output streams that can be open simultaneously.
-static const int kMaxOutputStreams = 10;
-
-AudioManager* CreateAudioManager() {
-  return new AudioManagerAndroid();
-}
-
-AudioManagerAndroid::AudioManagerAndroid() {
-  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
-}
-
-AudioManagerAndroid::~AudioManagerAndroid() {
-  Shutdown();
-}
-
-bool AudioManagerAndroid::HasAudioOutputDevices() {
-  return true;
-}
-
-bool AudioManagerAndroid::HasAudioInputDevices() {
-  return false;
-}
-
-AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream(
-      const AudioParameters& params) {
-  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
-  return new OpenSLESOutputStream(this, params);
-}
-
-AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
-      const AudioParameters& params) {
-  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
-  return new OpenSLESOutputStream(this, params);
-}
-
-AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
-    const AudioParameters& params, const std::string& device_id) {
-  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
-  return new OpenSLESInputStream(this, params);
-}
-
-AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
-    const AudioParameters& params, const std::string& device_id) {
-  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
-  return new OpenSLESInputStream(this, params);
-}
-
-}  // namespace media
diff --git a/src/media/audio/android/audio_manager_android.h b/src/media/audio/android/audio_manager_android.h
deleted file mode 100644
index 8f14808..0000000
--- a/src/media/audio/android/audio_manager_android.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
-#define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
-
-#include "media/audio/audio_manager_base.h"
-
-namespace media {
-
-// Android implemention of AudioManager.
-class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
- public:
-  AudioManagerAndroid();
-
-  // Implementation of AudioManager.
-  virtual bool HasAudioOutputDevices() OVERRIDE;
-  virtual bool HasAudioInputDevices() OVERRIDE;
-
-  // Implementation of AudioManagerBase.
-  virtual AudioOutputStream* MakeLinearOutputStream(
-      const AudioParameters& params) OVERRIDE;
-  virtual AudioOutputStream* MakeLowLatencyOutputStream(
-      const AudioParameters& params) OVERRIDE;
-  virtual AudioInputStream* MakeLinearInputStream(
-      const AudioParameters& params, const std::string& device_id) OVERRIDE;
-  virtual AudioInputStream* MakeLowLatencyInputStream(
-      const AudioParameters& params, const std::string& device_id) OVERRIDE;
-
- protected:
-  virtual ~AudioManagerAndroid();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AudioManagerAndroid);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
diff --git a/src/media/audio/android/opensles_input.cc b/src/media/audio/android/opensles_input.cc
deleted file mode 100644
index 0df5bc1..0000000
--- a/src/media/audio/android/opensles_input.cc
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/android/opensles_input.h"
-
-#include "base/logging.h"
-#include "media/audio/android/audio_manager_android.h"
-
-namespace media {
-
-OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
-                                         const AudioParameters& params)
-    : audio_manager_(audio_manager),
-      callback_(NULL),
-      recorder_(NULL),
-      simple_buffer_queue_(NULL),
-      active_queue_(0),
-      buffer_size_bytes_(0),
-      started_(false) {
-  format_.formatType = SL_DATAFORMAT_PCM;
-  format_.numChannels = static_cast<SLuint32>(params.channels());
-  // Provides sampling rate in milliHertz to OpenSLES.
-  format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
-  format_.bitsPerSample = params.bits_per_sample();
-  format_.containerSize = params.bits_per_sample();
-  format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
-  if (format_.numChannels == 1)
-    format_.channelMask = SL_SPEAKER_FRONT_CENTER;
-  else if (format_.numChannels == 2)
-    format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
-  else
-    NOTREACHED() << "Unsupported number of channels: " << format_.numChannels;
-
-  buffer_size_bytes_ = params.GetBytesPerBuffer();
-
-  memset(&audio_data_, 0, sizeof(audio_data_));
-}
-
-OpenSLESInputStream::~OpenSLESInputStream() {
-  DCHECK(!recorder_object_.Get());
-  DCHECK(!engine_object_.Get());
-  DCHECK(!recorder_);
-  DCHECK(!simple_buffer_queue_);
-  DCHECK(!audio_data_[0]);
-}
-
-bool OpenSLESInputStream::Open() {
-  if (engine_object_.Get())
-    return false;
-
-  if (!CreateRecorder())
-    return false;
-
-  SetupAudioBuffer();
-
-  return true;
-}
-
-void OpenSLESInputStream::Start(AudioInputCallback* callback) {
-  DCHECK(callback);
-  DCHECK(recorder_);
-  DCHECK(simple_buffer_queue_);
-  if (started_)
-    return;
-
-  // Enable the flags before streaming.
-  callback_ = callback;
-  active_queue_ = 0;
-  started_ = true;
-
-  SLresult err = SL_RESULT_UNKNOWN_ERROR;
-  // Enqueues |kNumOfQueuesInBuffer| zero buffers to get the ball rolling.
-  for (int i = 0; i < kNumOfQueuesInBuffer - 1; ++i) {
-    err = (*simple_buffer_queue_)->Enqueue(
-        simple_buffer_queue_,
-        audio_data_[i],
-        buffer_size_bytes_);
-    if (SL_RESULT_SUCCESS != err) {
-      HandleError(err);
-      return;
-    }
-  }
-
-  // Start the recording by setting the state to |SL_RECORDSTATE_RECORDING|.
-  err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_RECORDING);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    HandleError(err);
-}
-
-void OpenSLESInputStream::Stop() {
-  if (!started_)
-    return;
-
-  // Stop recording by setting the record state to |SL_RECORDSTATE_STOPPED|.
-  SLresult err = (*recorder_)->SetRecordState(recorder_,
-                                              SL_RECORDSTATE_STOPPED);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(WARNING) << "SetRecordState() failed to set the state to stop";
-  }
-
-  // Clear the buffer queue to get rid of old data when resuming recording.
-  err = (*simple_buffer_queue_)->Clear(simple_buffer_queue_);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(WARNING) << "Clear() failed to clear the buffer queue";
-  }
-
-  started_ = false;
-}
-
-void OpenSLESInputStream::Close() {
-  // Stop the stream if it is still recording.
-  Stop();
-
-  // Explicitly free the player objects and invalidate their associated
-  // interfaces. They have to be done in the correct order.
-  recorder_object_.Reset();
-  engine_object_.Reset();
-  simple_buffer_queue_ = NULL;
-  recorder_ = NULL;
-
-  ReleaseAudioBuffer();
-
-  audio_manager_->ReleaseInputStream(this);
-}
-
-double OpenSLESInputStream::GetMaxVolume() {
-  NOTIMPLEMENTED();
-  return 0.0;
-}
-
-void OpenSLESInputStream::SetVolume(double volume) {
-  NOTIMPLEMENTED();
-}
-
-double OpenSLESInputStream::GetVolume() {
-  NOTIMPLEMENTED();
-  return 0.0;
-}
-
-void OpenSLESInputStream::SetAutomaticGainControl(bool enabled) {
-  NOTIMPLEMENTED();
-}
-
-bool OpenSLESInputStream::GetAutomaticGainControl() {
-  NOTIMPLEMENTED();
-  return false;
-}
-
-bool OpenSLESInputStream::CreateRecorder() {
-  // Initializes the engine object with specific option. After working with the
-  // object, we need to free the object and its resources.
-  SLEngineOption option[] = {
-    { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }
-  };
-  SLresult err = slCreateEngine(engine_object_.Receive(),
-                                1,
-                                option,
-                                0,
-                                NULL,
-                                NULL);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Realize the SL engine object in synchronous mode.
-  err = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Get the SL engine interface which is implicit.
-  SLEngineItf engine;
-  err = engine_object_->GetInterface(
-      engine_object_.Get(), SL_IID_ENGINE, &engine);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Audio source configuration.
-  SLDataLocator_IODevice mic_locator = {
-    SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
-    SL_DEFAULTDEVICEID_AUDIOINPUT, NULL
-  };
-  SLDataSource audio_source = { &mic_locator, NULL };
-
-  // Audio sink configuration.
-  SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {
-    SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // Locator type.
-    static_cast<SLuint32>(kNumOfQueuesInBuffer)  // Number of buffers.
-  };
-  SLDataSink audio_sink = { &buffer_queue, &format_ };
-
-  // Create an audio recorder.
-  const SLuint32 number_of_interfaces = 1;
-  const SLInterfaceID interface_id[number_of_interfaces] = {
-    SL_IID_ANDROIDSIMPLEBUFFERQUEUE
-  };
-  const SLboolean interface_required[number_of_interfaces] = {
-    SL_BOOLEAN_TRUE
-  };
-  err = (*engine)->CreateAudioRecorder(engine,
-                                       recorder_object_.Receive(),
-                                       &audio_source,
-                                       &audio_sink,
-                                       number_of_interfaces,
-                                       interface_id,
-                                       interface_required);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(ERROR) << "CreateAudioRecorder failed with error code " << err;
-    return false;
-  }
-
-  // Realize the recorder object in synchronous mode.
-  err = recorder_object_->Realize(recorder_object_.Get(), SL_BOOLEAN_FALSE);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(ERROR) << "Recprder Realize() failed with error code " << err;
-    return false;
-  }
-
-  // Get an implicit recorder interface.
-  err = recorder_object_->GetInterface(recorder_object_.Get(),
-                                       SL_IID_RECORD,
-                                       &recorder_);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Get the simple buffer queue interface.
-  err = recorder_object_->GetInterface(recorder_object_.Get(),
-                                       SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
-                                       &simple_buffer_queue_);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Register the input callback for the simple buffer queue.
-  // This callback will be called when receiving new data from the device.
-  err = (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
-                                                  SimpleBufferQueueCallback,
-                                                  this);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-
-  return (SL_RESULT_SUCCESS == err);
-}
-
-void OpenSLESInputStream::SimpleBufferQueueCallback(
-    SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
-  OpenSLESInputStream* stream =
-      reinterpret_cast<OpenSLESInputStream*>(instance);
-  stream->ReadBufferQueue();
-}
-
-void OpenSLESInputStream::ReadBufferQueue() {
-  if (!started_)
-    return;
-
-  // Get the enqueued buffer from the soundcard.
-  SLresult err = (*simple_buffer_queue_)->Enqueue(
-      simple_buffer_queue_,
-      audio_data_[active_queue_],
-      buffer_size_bytes_);
-  if (SL_RESULT_SUCCESS != err)
-    HandleError(err);
-
-  // TODO(xians): Get an accurate delay estimation.
-  callback_->OnData(this,
-                    audio_data_[active_queue_],
-                    buffer_size_bytes_,
-                    buffer_size_bytes_,
-                    0.0);
-
-  active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer;
-}
-
-void OpenSLESInputStream::SetupAudioBuffer() {
-  DCHECK(!audio_data_[0]);
-  for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
-    audio_data_[i] = new uint8[buffer_size_bytes_];
-  }
-}
-
-void OpenSLESInputStream::ReleaseAudioBuffer() {
-  if (audio_data_[0]) {
-    for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
-      delete [] audio_data_[i];
-      audio_data_[i] = NULL;
-    }
-  }
-}
-
-void OpenSLESInputStream::HandleError(SLresult error) {
-  DLOG(FATAL) << "OpenSLES error " << error;
-  if (callback_)
-    callback_->OnError(this, error);
-}
-
-}  // namespace media
diff --git a/src/media/audio/android/opensles_input.h b/src/media/audio/android/opensles_input.h
deleted file mode 100644
index 0d18f9b..0000000
--- a/src/media/audio/android/opensles_input.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
-#define MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
-
-#include "base/compiler_specific.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/android/opensles_util.h"
-#include <SLES/OpenSLES_Android.h>
-
-namespace media {
-
-class AudioManagerAndroid;
-
-// Implements PCM audio input support for Android using the OpenSLES API.
-class OpenSLESInputStream : public AudioInputStream {
- public:
-  static const int kNumOfQueuesInBuffer = 2;
-
-  OpenSLESInputStream(AudioManagerAndroid* manager,
-                      const AudioParameters& params);
-
-  virtual ~OpenSLESInputStream();
-
-  // Implementation of AudioInputStream.
-  virtual bool Open() OVERRIDE;
-  virtual void Start(AudioInputCallback* callback) OVERRIDE;
-  virtual void Stop() OVERRIDE;
-  virtual void Close() OVERRIDE;
-  virtual double GetMaxVolume() OVERRIDE;
-  virtual void SetVolume(double volume) OVERRIDE;
-  virtual double GetVolume() OVERRIDE;
-  virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
-  virtual bool GetAutomaticGainControl() OVERRIDE;
-
- private:
-  bool CreateRecorder();
-
-  static void SimpleBufferQueueCallback(
-      SLAndroidSimpleBufferQueueItf buffer_queue, void* instance);
-
-  void ReadBufferQueue();
-
-  // Called in Open();
-  void SetupAudioBuffer();
-
-  // Called in Close();
-  void ReleaseAudioBuffer();
-
-  // If OpenSLES reports an error this function handles it and passes it to
-  // the attached AudioInputCallback::OnError().
-  void HandleError(SLresult error);
-
-  AudioManagerAndroid* audio_manager_;
-
-  AudioInputCallback* callback_;
-
-  // Shared engine interfaces for the app.
-  media::ScopedSLObjectItf recorder_object_;
-  media::ScopedSLObjectItf engine_object_;
-
-  SLRecordItf recorder_;
-
-  // Buffer queue recorder interface.
-  SLAndroidSimpleBufferQueueItf simple_buffer_queue_;
-
-  SLDataFormat_PCM format_;
-
-  // Audio buffers that are allocated in the constructor based on
-  // info from audio parameters.
-  uint8* audio_data_[kNumOfQueuesInBuffer];
-
-  int active_queue_;
-  int buffer_size_bytes_;
-
-  bool started_;
-
-  DISALLOW_COPY_AND_ASSIGN(OpenSLESInputStream);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
diff --git a/src/media/audio/android/opensles_output.cc b/src/media/audio/android/opensles_output.cc
deleted file mode 100644
index 26ae25b..0000000
--- a/src/media/audio/android/opensles_output.cc
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/android/opensles_output.h"
-
-#include "base/logging.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/android/audio_manager_android.h"
-
-namespace media {
-
-OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
-                                           const AudioParameters& params)
-    : audio_manager_(manager),
-      callback_(NULL),
-      player_(NULL),
-      simple_buffer_queue_(NULL),
-      active_queue_(0),
-      buffer_size_bytes_(0),
-      started_(false),
-      volume_(1.0) {
-  format_.formatType = SL_DATAFORMAT_PCM;
-  format_.numChannels = static_cast<SLuint32>(params.channels());
-  // Provides sampling rate in milliHertz to OpenSLES.
-  format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
-  format_.bitsPerSample = params.bits_per_sample();
-  format_.containerSize = params.bits_per_sample();
-  format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
-  if (format_.numChannels == 1)
-    format_.channelMask = SL_SPEAKER_FRONT_CENTER;
-  else if (format_.numChannels == 2)
-    format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
-  else
-    NOTREACHED() << "Unsupported number of channels: " << format_.numChannels;
-
-  buffer_size_bytes_ = params.GetBytesPerBuffer();
-  audio_bus_ = AudioBus::Create(params);
-
-  memset(&audio_data_, 0, sizeof(audio_data_));
-}
-
-OpenSLESOutputStream::~OpenSLESOutputStream() {
-  DCHECK(!engine_object_.Get());
-  DCHECK(!player_object_.Get());
-  DCHECK(!output_mixer_.Get());
-  DCHECK(!player_);
-  DCHECK(!simple_buffer_queue_);
-  DCHECK(!audio_data_[0]);
-}
-
-bool OpenSLESOutputStream::Open() {
-  if (engine_object_.Get())
-    return false;
-
-  if (!CreatePlayer())
-    return false;
-
-  SetupAudioBuffer();
-
-  return true;
-}
-
-void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
-  DCHECK(callback);
-  DCHECK(player_);
-  DCHECK(simple_buffer_queue_);
-  if (started_)
-    return;
-
-  // Enable the flags before streaming.
-  callback_ = callback;
-  active_queue_ = 0;
-  started_ = true;
-
-  // Avoid start-up glitches by filling up one buffer queue before starting
-  // the stream.
-  FillBufferQueue();
-
-  // Start streaming data by setting the play state to |SL_PLAYSTATE_PLAYING|.
-  SLresult err = (*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(WARNING) << "SetPlayState() failed to start playing";
-  }
-}
-
-void OpenSLESOutputStream::Stop() {
-  if (!started_)
-    return;
-
-  started_ = false;
-  // Stop playing by setting the play state to |SL_PLAYSTATE_STOPPED|.
-  SLresult err = (*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(WARNING) << "SetPlayState() failed to set the state to stop";
-  }
-
-  // Clear the buffer queue so that the old data won't be played when
-  // resuming playing.
-  err = (*simple_buffer_queue_)->Clear(simple_buffer_queue_);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(WARNING) << "Clear() failed to clear the buffer queue";
-  }
-}
-
-void OpenSLESOutputStream::Close() {
-  // Stop the stream if it is still playing.
-  Stop();
-
-  // Explicitly free the player objects and invalidate their associated
-  // interfaces. They have to be done in the correct order.
-  output_mixer_.Reset();
-  player_object_.Reset();
-  engine_object_.Reset();
-  simple_buffer_queue_ = NULL;
-  player_ = NULL;
-
-  ReleaseAudioBuffer();
-
-  audio_manager_->ReleaseOutputStream(this);
-}
-
-void OpenSLESOutputStream::SetVolume(double volume) {
-  float volume_float = static_cast<float>(volume);
-  if (volume_float < 0.0f || volume_float > 1.0f) {
-    return;
-  }
-  volume_ = volume_float;
-}
-
-void OpenSLESOutputStream::GetVolume(double* volume) {
-  *volume = static_cast<double>(volume_);
-}
-
-bool OpenSLESOutputStream::CreatePlayer() {
-  // Initializes the engine object with specific option. After working with the
-  // object, we need to free the object and its resources.
-  SLEngineOption option[] = {
-    { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }
-  };
-  SLresult err = slCreateEngine(engine_object_.Receive(), 1, option, 0,
-                                NULL, NULL);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Realize the SL engine object in synchronous mode.
-  err = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Get the SL engine interface which is implicit.
-  SLEngineItf engine;
-  err = engine_object_->GetInterface(engine_object_.Get(),
-                                     SL_IID_ENGINE,
-                                     &engine);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Create ouput mixer object to be used by the player.
-  // TODO(xians): Do we need the environmental reverb auxiliary effect?
-  err = (*engine)->CreateOutputMix(engine,
-                                   output_mixer_.Receive(),
-                                   0,
-                                   NULL,
-                                   NULL);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Realizing the output mix object in synchronous mode.
-  err = output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Audio source configuration.
-  SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = {
-    SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
-    static_cast<SLuint32>(kNumOfQueuesInBuffer)
-  };
-  SLDataSource audio_source = { &simple_buffer_queue, &format_ };
-
-  // Audio sink configuration.
-  SLDataLocator_OutputMix locator_output_mix = {
-    SL_DATALOCATOR_OUTPUTMIX, output_mixer_.Get()
-  };
-  SLDataSink audio_sink = { &locator_output_mix, NULL };
-
-  // Create an audio player.
-  const SLuint32 number_of_interfaces = 1;
-  const SLInterfaceID interface_id[number_of_interfaces] = {
-    SL_IID_BUFFERQUEUE
-  };
-  const SLboolean interface_required[number_of_interfaces] = {
-    SL_BOOLEAN_TRUE
-  };
-  err = (*engine)->CreateAudioPlayer(engine,
-                                     player_object_.Receive(),
-                                     &audio_source,
-                                     &audio_sink,
-                                     number_of_interfaces,
-                                     interface_id,
-                                     interface_required);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(ERROR) << "CreateAudioPlayer() failed with error code " << err;
-    return false;
-  }
-
-  // Realize the player object in synchronous mode.
-  err = player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err) {
-    DLOG(ERROR) << "Player Realize() failed with error code " << err;
-    return false;
-  }
-
-  // Get an implicit player interface.
-  err = player_object_->GetInterface(
-      player_object_.Get(), SL_IID_PLAY, &player_);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Get the simple buffer queue interface.
-  err = player_object_->GetInterface(player_object_.Get(),
-                                     SL_IID_BUFFERQUEUE,
-                                     &simple_buffer_queue_);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-  if (SL_RESULT_SUCCESS != err)
-    return false;
-
-  // Register the input callback for the simple buffer queue.
-  // This callback will be called when the soundcard needs data.
-  err = (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
-                                                  SimpleBufferQueueCallback,
-                                                  this);
-  DCHECK_EQ(SL_RESULT_SUCCESS, err);
-
-  return (SL_RESULT_SUCCESS == err);
-}
-
-void OpenSLESOutputStream::SimpleBufferQueueCallback(
-    SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
-  OpenSLESOutputStream* stream =
-      reinterpret_cast<OpenSLESOutputStream*>(instance);
-  stream->FillBufferQueue();
-}
-
-void OpenSLESOutputStream::FillBufferQueue() {
-  if (!started_)
-    return;
-
-  // Read data from the registered client source.
-  // TODO(xians): Get an accurate delay estimation.
-  uint32 hardware_delay = buffer_size_bytes_;
-  int frames_filled = callback_->OnMoreData(
-      audio_bus_.get(), AudioBuffersState(0, hardware_delay));
-  int num_filled_bytes =
-      frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8;
-  DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_);
-  // Note: If this ever changes to output raw float the data must be clipped and
-  // sanitized since it may come from an untrusted source such as NaCl.
-  audio_bus_->ToInterleaved(
-      frames_filled, format_.bitsPerSample / 8, audio_data_[active_queue_]);
-
-  // Perform in-place, software-volume adjustments.
-  media::AdjustVolume(audio_data_[active_queue_],
-                      num_filled_bytes,
-                      format_.numChannels,
-                      format_.bitsPerSample / 8,
-                      volume_);
-
-  // Enqueue the buffer for playback.
-  SLresult err = (*simple_buffer_queue_)->Enqueue(
-      simple_buffer_queue_,
-      audio_data_[active_queue_],
-      num_filled_bytes);
-  if (SL_RESULT_SUCCESS != err)
-    HandleError(err);
-
-  active_queue_ = (active_queue_  + 1) % kNumOfQueuesInBuffer;
-}
-
-void OpenSLESOutputStream::SetupAudioBuffer() {
-  DCHECK(!audio_data_[0]);
-  for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
-    audio_data_[i] = new uint8[buffer_size_bytes_];
-  }
-}
-
-void OpenSLESOutputStream::ReleaseAudioBuffer() {
-  if (audio_data_[0]) {
-    for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
-      delete [] audio_data_[i];
-      audio_data_[i] = NULL;
-    }
-  }
-}
-
-void OpenSLESOutputStream::HandleError(SLresult error) {
-  DLOG(FATAL) << "OpenSLES error " << error;
-  if (callback_)
-    callback_->OnError(this, error);
-}
-
-}  // namespace media
diff --git a/src/media/audio/android/opensles_output.h b/src/media/audio/android/opensles_output.h
deleted file mode 100644
index 9ecfb6c..0000000
--- a/src/media/audio/android/opensles_output.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
-#define MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
-
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "media/audio/android/opensles_util.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_parameters.h"
-#include <SLES/OpenSLES_Android.h>
-
-namespace media {
-
-class AudioManagerAndroid;
-
-// Implements PCM audio output support for Android using the OpenSLES API.
-class OpenSLESOutputStream : public AudioOutputStream {
- public:
-  static const int kNumOfQueuesInBuffer = 2;
-
-  OpenSLESOutputStream(AudioManagerAndroid* manager,
-                       const AudioParameters& params);
-
-  virtual ~OpenSLESOutputStream();
-
-  // Implementation of AudioOutputStream.
-  virtual bool Open() OVERRIDE;
-  virtual void Close() OVERRIDE;
-  virtual void Start(AudioSourceCallback* callback) OVERRIDE;
-  virtual void Stop() OVERRIDE;
-  virtual void SetVolume(double volume) OVERRIDE;
-  virtual void GetVolume(double* volume) OVERRIDE;
-
- private:
-  bool CreatePlayer();
-
-  static void SimpleBufferQueueCallback(
-      SLAndroidSimpleBufferQueueItf buffer_queue, void* instance);
-
-  void FillBufferQueue();
-
-  // Called in Open();
-  void SetupAudioBuffer();
-
-  // Called in Close();
-  void ReleaseAudioBuffer();
-
-  // If OpenSLES reports an error this function handles it and passes it to
-  // the attached AudioOutputCallback::OnError().
-  void HandleError(SLresult error);
-
-  AudioManagerAndroid* audio_manager_;
-
-  AudioSourceCallback* callback_;
-
-  // Shared engine interfaces for the app.
-  media::ScopedSLObjectItf engine_object_;
-  media::ScopedSLObjectItf player_object_;
-  media::ScopedSLObjectItf output_mixer_;
-
-  SLPlayItf player_;
-
-  // Buffer queue recorder interface.
-  SLAndroidSimpleBufferQueueItf simple_buffer_queue_;
-
-  SLDataFormat_PCM format_;
-
-  // Audio buffer arrays that are allocated in the constructor.
-  uint8* audio_data_[kNumOfQueuesInBuffer];
-
-  int active_queue_;
-  size_t buffer_size_bytes_;
-
-  bool started_;
-
-  // Volume level from 0 to 1.
-  float volume_;
-
-  // Container for retrieving data from AudioSourceCallback::OnMoreData().
-  scoped_ptr<AudioBus> audio_bus_;
-
-  DISALLOW_COPY_AND_ASSIGN(OpenSLESOutputStream);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
diff --git a/src/media/audio/android/opensles_util.h b/src/media/audio/android/opensles_util.h
deleted file mode 100644
index 4a028e2..0000000
--- a/src/media/audio/android/opensles_util.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
-#define MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
-
-#include "base/logging.h"
-#include <SLES/OpenSLES.h>
-
-namespace media {
-
-template <typename SLType, typename SLDerefType>
-class ScopedSLObject {
- public:
-  ScopedSLObject() : obj_(NULL) {}
-
-  ~ScopedSLObject() { Reset(); }
-
-  SLType* Receive() {
-    DCHECK(!obj_);
-    return &obj_;
-  }
-
-  SLDerefType operator->() { return *obj_; }
-
-  SLType Get() const { return obj_; }
-
-  void Reset() {
-    if (obj_) {
-      (*obj_)->Destroy(obj_);
-      obj_ = NULL;
-    }
-  }
-
- private:
-  SLType obj_;
-};
-
-typedef ScopedSLObject<SLObjectItf, const SLObjectItf_*> ScopedSLObjectItf;
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
diff --git a/src/media/audio/async_socket_io_handler.h b/src/media/audio/async_socket_io_handler.h
deleted file mode 100644
index d17e3d3..0000000
--- a/src/media/audio/async_socket_io_handler.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_
-#define MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_
-
-#include "base/message_loop.h"
-#include "base/sync_socket.h"
-#include "base/threading/non_thread_safe.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// The message loop callback interface is different based on platforms.
-#if defined(OS_WIN)
-typedef MessageLoopForIO::IOHandler MessageLoopIOHandler;
-#elif defined(OS_POSIX)
-typedef MessageLoopForIO::Watcher MessageLoopIOHandler;
-#endif
-
-// Extends the CancelableSyncSocket class to allow reading from a socket
-// asynchronously on a TYPE_IO message loop thread.  This makes it easy to share
-// a thread that uses a message loop (e.g. for IPC and other things) and not
-// require a separate thread to read from the socket.
-//
-// Example usage (also see the unit tests):
-//
-// class SocketReader {
-//  public:
-//   SocketReader(base::CancelableSyncSocket* socket)
-//       : socket_(socket), buffer_() {
-//     io_handler.Initialize(socket_->handle(),
-//                           base::Bind(&SocketReader::OnDataAvailable,
-//                                      base::Unretained(this));
-//   }
-//
-//   void AsyncRead() {
-//     CHECK(io_handler.Read(&buffer_[0], sizeof(buffer_)));
-//   }
-//
-//  private:
-//   void OnDataAvailable(int bytes_read) {
-//     if (ProcessData(&buffer_[0], bytes_read)) {
-//       // Issue another read.
-//       CHECK(io_handler.Read(&buffer_[0], sizeof(buffer_)));
-//     }
-//   }
-//
-//   media::AsyncSocketIoHandler io_handler;
-//   base::CancelableSyncSocket* socket_;
-//   char buffer_[kBufferSize];
-// };
-//
-class MEDIA_EXPORT AsyncSocketIoHandler
-    : public NON_EXPORTED_BASE(base::NonThreadSafe),
-      public NON_EXPORTED_BASE(MessageLoopIOHandler) {
- public:
-  AsyncSocketIoHandler();
-  virtual ~AsyncSocketIoHandler();
-
-  // Type definition for the callback. The parameter tells how many
-  // bytes were read and is 0 if an error occurred.
-  typedef base::Callback<void(int)> ReadCompleteCallback;
-
-  // Initializes the AsyncSocketIoHandler by hooking it up to the current
-  // thread's message loop (must be TYPE_IO), to do async reads from the socket
-  // on the current thread.  The |callback| will be invoked whenever a Read()
-  // has completed.
-  bool Initialize(base::SyncSocket::Handle socket,
-                  const ReadCompleteCallback& callback);
-
-  // Attempts to read from the socket.  The return value will be |false|
-  // if an error occurred and |true| if data was read or a pending read
-  // was issued.  Regardless of async or sync operation, the
-  // ReadCompleteCallback (see above) will be called when data is available.
-  bool Read(char* buffer, int buffer_len);
-
- private:
-#if defined(OS_WIN)
-  // Implementation of IOHandler on Windows.
-  virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
-                             DWORD bytes_transfered,
-                             DWORD error) OVERRIDE;
-#elif defined(OS_POSIX)
-  // Implementation of MessageLoopForIO::Watcher.
-  virtual void OnFileCanWriteWithoutBlocking(int socket) OVERRIDE {}
-  virtual void OnFileCanReadWithoutBlocking(int socket) OVERRIDE;
-
-  void EnsureWatchingSocket();
-#endif
-
-  base::SyncSocket::Handle socket_;
-#if defined(OS_WIN)
-  MessageLoopForIO::IOContext* context_;
-  bool is_pending_;
-#elif defined(OS_POSIX)
-  MessageLoopForIO::FileDescriptorWatcher socket_watcher_;
-  // |pending_buffer_| and |pending_buffer_len_| are valid only between
-  // Read() and OnFileCanReadWithoutBlocking().
-  char* pending_buffer_;
-  int pending_buffer_len_;
-  // |true| iff the message loop is watching the socket for IO events.
-  bool is_watching_;
-#endif
-  ReadCompleteCallback read_complete_;
-
-  DISALLOW_COPY_AND_ASSIGN(AsyncSocketIoHandler);
-};
-
-}  // namespace media.
-
-#endif  // MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_
diff --git a/src/media/audio/async_socket_io_handler_posix.cc b/src/media/audio/async_socket_io_handler_posix.cc
deleted file mode 100644
index eeec7c1..0000000
--- a/src/media/audio/async_socket_io_handler_posix.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/async_socket_io_handler.h"
-
-#include <fcntl.h>
-
-#include "base/posix/eintr_wrapper.h"
-
-namespace media {
-
-AsyncSocketIoHandler::AsyncSocketIoHandler()
-    : socket_(base::SyncSocket::kInvalidHandle),
-      pending_buffer_(NULL),
-      pending_buffer_len_(0),
-      is_watching_(false) {
-}
-
-AsyncSocketIoHandler::~AsyncSocketIoHandler() {
-  DCHECK(CalledOnValidThread());
-}
-
-void AsyncSocketIoHandler::OnFileCanReadWithoutBlocking(int socket) {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(socket, socket_);
-  DCHECK(!read_complete_.is_null());
-
-  if (pending_buffer_) {
-    int bytes_read = HANDLE_EINTR(read(socket_, pending_buffer_,
-                                       pending_buffer_len_));
-    DCHECK_GT(bytes_read, 0);
-    pending_buffer_ = NULL;
-    pending_buffer_len_ = 0;
-    read_complete_.Run(bytes_read > 0 ? bytes_read : 0);
-  } else {
-    // We're getting notifications that we can read from the socket while
-    // we're not waiting for data.  In order to not starve the message loop,
-    // let's stop watching the fd and restart the watch when Read() is called.
-    is_watching_ = false;
-    socket_watcher_.StopWatchingFileDescriptor();
-  }
-}
-
-bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) {
-  DCHECK(CalledOnValidThread());
-  DCHECK(!read_complete_.is_null());
-  DCHECK(!pending_buffer_);
-
-  EnsureWatchingSocket();
-
-  int bytes_read = HANDLE_EINTR(read(socket_, buffer, buffer_len));
-  if (bytes_read < 0) {
-    if (errno == EAGAIN) {
-      pending_buffer_ = buffer;
-      pending_buffer_len_ = buffer_len;
-    } else {
-      NOTREACHED() << "read(): " << errno;
-      return false;
-    }
-  } else {
-    read_complete_.Run(bytes_read);
-  }
-  return true;
-}
-
-bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket,
-                                      const ReadCompleteCallback& callback) {
-  DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle);
-
-  DetachFromThread();
-
-  socket_ = socket;
-  read_complete_ = callback;
-
-  // SyncSocket is blocking by default, so let's convert it to non-blocking.
-  int value = fcntl(socket, F_GETFL);
-  if (!(value & O_NONBLOCK)) {
-    // Set the socket to be non-blocking so we can do async reads.
-    if (fcntl(socket, F_SETFL, O_NONBLOCK) == -1) {
-      NOTREACHED();
-      return false;
-    }
-  }
-
-  return true;
-}
-
-void AsyncSocketIoHandler::EnsureWatchingSocket() {
-  DCHECK(CalledOnValidThread());
-  if (!is_watching_ && socket_ != base::SyncSocket::kInvalidHandle) {
-    is_watching_ = MessageLoopForIO::current()->WatchFileDescriptor(
-        socket_, true, MessageLoopForIO::WATCH_READ, &socket_watcher_, this);
-  }
-}
-
-}  // namespace media.
diff --git a/src/media/audio/async_socket_io_handler_unittest.cc b/src/media/audio/async_socket_io_handler_unittest.cc
deleted file mode 100644
index c7fa47b..0000000
--- a/src/media/audio/async_socket_io_handler_unittest.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/async_socket_io_handler.h"
-
-#include "base/bind.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-const char kAsyncSocketIoTestString[] = "Hello, AsyncSocketIoHandler";
-const size_t kAsyncSocketIoTestStringLength =
-    arraysize(kAsyncSocketIoTestString);
-
-class TestSocketReader {
- public:
-  // Set |number_of_reads_before_quit| to >0 when you expect a specific number
-  // of Read operations to complete.  Once that number is reached, the current
-  // message loop will be Quit().  Set |number_of_reads_before_quit| to -1 if
-  // callbacks should not be counted.
-  TestSocketReader(base::CancelableSyncSocket* socket,
-                   int number_of_reads_before_quit,
-                   bool issue_reads_from_callback)
-      : socket_(socket), buffer_(),
-        number_of_reads_before_quit_(number_of_reads_before_quit),
-        callbacks_received_(0),
-        issue_reads_from_callback_(issue_reads_from_callback) {
-    io_handler.Initialize(socket_->handle(),
-                          base::Bind(&TestSocketReader::OnRead,
-                                     base::Unretained(this)));
-  }
-  ~TestSocketReader() {}
-
-  bool IssueRead() {
-    return io_handler.Read(&buffer_[0], sizeof(buffer_));
-  }
-
-  const char* buffer() const { return &buffer_[0]; }
-
-  int callbacks_received() const { return callbacks_received_; }
-
- private:
-  void OnRead(int bytes_read) {
-    EXPECT_GT(bytes_read, 0);
-    ++callbacks_received_;
-    if (number_of_reads_before_quit_ == callbacks_received_) {
-      MessageLoop::current()->Quit();
-    } else if (issue_reads_from_callback_) {
-      IssueRead();
-    }
-  }
-
-  media::AsyncSocketIoHandler io_handler;
-  base::CancelableSyncSocket* socket_;  // Ownership lies outside the class.
-  char buffer_[kAsyncSocketIoTestStringLength];
-  int number_of_reads_before_quit_;
-  int callbacks_received_;
-  bool issue_reads_from_callback_;
-};
-
-// Workaround to be able to use a base::Closure for sending data.
-// Send() returns int but a closure must return void.
-void SendData(base::CancelableSyncSocket* socket,
-              const void* buffer,
-              size_t length) {
-  socket->Send(buffer, length);
-}
-
-}  // end namespace.
-
-// Tests doing a pending read from a socket and use an IO handler to get
-// notified of data.
-TEST(AsyncSocketIoHandlerTest, AsynchronousReadWithMessageLoop) {
-  MessageLoopForIO loop;
-
-  base::CancelableSyncSocket pair[2];
-  ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
-
-  TestSocketReader reader(&pair[0], 1, false);
-  EXPECT_TRUE(reader.IssueRead());
-
-  pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength);
-  MessageLoop::current()->Run();
-  EXPECT_EQ(strcmp(reader.buffer(), kAsyncSocketIoTestString), 0);
-  EXPECT_EQ(1, reader.callbacks_received());
-}
-
-// Tests doing a read from a socket when we know that there is data in the
-// socket.  Here we want to make sure that any async 'can read' notifications
-// won't trip us off and that the synchronous case works as well.
-TEST(AsyncSocketIoHandlerTest, SynchronousReadWithMessageLoop) {
-  MessageLoopForIO loop;
-
-  base::CancelableSyncSocket pair[2];
-  ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
-
-  TestSocketReader reader(&pair[0], -1, false);
-
-  pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength);
-  MessageLoop::current()->PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
-      base::TimeDelta::FromMilliseconds(100));
-  MessageLoop::current()->Run();
-
-  EXPECT_TRUE(reader.IssueRead());
-  EXPECT_EQ(strcmp(reader.buffer(), kAsyncSocketIoTestString), 0);
-  // We've now verified that the read happened synchronously, but it's not
-  // guaranteed that the callback has been issued since the callback will be
-  // called asynchronously even though the read may have been done.
-  // So we call RunUntilIdle() to allow any event notifications or APC's on
-  // Windows, to execute before checking the count of how many callbacks we've
-  // received.
-  MessageLoop::current()->RunUntilIdle();
-  EXPECT_EQ(1, reader.callbacks_received());
-}
-
-// Calls Read() from within a callback to test that simple read "loops" work.
-TEST(AsyncSocketIoHandlerTest, ReadFromCallback) {
-  MessageLoopForIO loop;
-
-  base::CancelableSyncSocket pair[2];
-  ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
-
-  const int kReadOperationCount = 10;
-  TestSocketReader reader(&pair[0], kReadOperationCount, true);
-  EXPECT_TRUE(reader.IssueRead());
-
-  // Issue sends on an interval to satisfy the Read() requirements.
-  int64 milliseconds = 0;
-  for (int i = 0; i < kReadOperationCount; ++i) {
-    MessageLoop::current()->PostDelayedTask(FROM_HERE,
-        base::Bind(&SendData, &pair[1], kAsyncSocketIoTestString,
-            kAsyncSocketIoTestStringLength),
-        base::TimeDelta::FromMilliseconds(milliseconds));
-    milliseconds += 10;
-  }
-
-  MessageLoop::current()->PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
-      base::TimeDelta::FromMilliseconds(100 + milliseconds));
-
-  MessageLoop::current()->Run();
-  EXPECT_EQ(kReadOperationCount, reader.callbacks_received());
-}
diff --git a/src/media/audio/async_socket_io_handler_win.cc b/src/media/audio/async_socket_io_handler_win.cc
deleted file mode 100644
index f83f405..0000000
--- a/src/media/audio/async_socket_io_handler_win.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/async_socket_io_handler.h"
-
-namespace media {
-
-AsyncSocketIoHandler::AsyncSocketIoHandler()
-    : socket_(base::SyncSocket::kInvalidHandle),
-      context_(NULL),
-      is_pending_(false) {}
-
-AsyncSocketIoHandler::~AsyncSocketIoHandler() {
-  // We need to be deleted on the correct thread to avoid racing with the
-  // message loop thread.
-  DCHECK(CalledOnValidThread());
-
-  if (context_) {
-    if (is_pending_) {
-      // Make the context be deleted by the message pump when done.
-      context_->handler = NULL;
-    } else {
-      delete context_;
-    }
-  }
-}
-
-// Implementation of IOHandler on Windows.
-void AsyncSocketIoHandler::OnIOCompleted(MessageLoopForIO::IOContext* context,
-                                         DWORD bytes_transfered,
-                                         DWORD error) {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(context_, context);
-  DCHECK(!read_complete_.is_null());
-  is_pending_ = false;
-  read_complete_.Run(error == ERROR_SUCCESS ? bytes_transfered : 0);
-}
-
-bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) {
-  DCHECK(CalledOnValidThread());
-  DCHECK(!read_complete_.is_null());
-  DCHECK(!is_pending_);
-  DCHECK_NE(socket_, base::SyncSocket::kInvalidHandle);
-
-  DWORD bytes_read = 0;
-  BOOL ok = ::ReadFile(socket_, buffer, buffer_len, &bytes_read,
-                       &context_->overlapped);
-  // The completion port will be signaled regardless of completing the read
-  // straight away or asynchronously (ERROR_IO_PENDING). OnIOCompleted() will
-  // be called regardless and we don't need to explicitly run the callback
-  // in the case where ok is FALSE and GLE==ERROR_IO_PENDING.
-  is_pending_ = !ok && (GetLastError() == ERROR_IO_PENDING);
-  return ok || is_pending_;
-}
-
-bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket,
-                                      const ReadCompleteCallback& callback) {
-  DCHECK(!context_);
-  DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle);
-
-  DetachFromThread();
-
-  socket_ = socket;
-  read_complete_ = callback;
-
-  MessageLoopForIO::current()->RegisterIOHandler(socket, this);
-
-  context_ = new MessageLoopForIO::IOContext();
-  context_->handler = this;
-  memset(&context_->overlapped, 0, sizeof(context_->overlapped));
-
-  return true;
-}
-
-}  // namespace media.
diff --git a/src/media/audio/audio_buffers_state.cc b/src/media/audio/audio_buffers_state.cc
deleted file mode 100644
index 6c4f950..0000000
--- a/src/media/audio/audio_buffers_state.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_buffers_state.h"
-
-namespace media {
-
-AudioBuffersState::AudioBuffersState()
-    : pending_bytes(0),
-      hardware_delay_bytes(0) {
-}
-
-AudioBuffersState::AudioBuffersState(int pending_bytes,
-                                     int hardware_delay_bytes)
-    : pending_bytes(pending_bytes),
-      hardware_delay_bytes(hardware_delay_bytes) {
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_buffers_state.h b/src/media/audio/audio_buffers_state.h
deleted file mode 100644
index 79244ae..0000000
--- a/src/media/audio/audio_buffers_state.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
-#define MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
-
-#include "media/base/media_export.h"
-
-namespace media {
-
-// AudioBuffersState struct stores current state of audio buffers.
-// It is used for audio synchronization.
-struct MEDIA_EXPORT AudioBuffersState {
-  AudioBuffersState();
-  AudioBuffersState(int pending_bytes, int hardware_delay_bytes);
-
-  int total_bytes() {
-    return pending_bytes + hardware_delay_bytes;
-  }
-
-  // Number of bytes we currently have in our software buffer.
-  int pending_bytes;
-
-  // Number of bytes that have been written to the device, but haven't
-  // been played yet.
-  int hardware_delay_bytes;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
diff --git a/src/media/audio/audio_device_name.cc b/src/media/audio/audio_device_name.cc
deleted file mode 100644
index 02bb03f..0000000
--- a/src/media/audio/audio_device_name.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_device_name.h"
-
-namespace media {
-
-AudioDeviceName::AudioDeviceName() {}
-
-AudioDeviceName::AudioDeviceName(const std::string& device_name,
-                                 const std::string& unique_id)
-    : device_name(device_name),
-      unique_id(unique_id) {
-}
-
-}  // namespace media
-
diff --git a/src/media/audio/audio_device_name.h b/src/media/audio/audio_device_name.h
deleted file mode 100644
index aa3cca0..0000000
--- a/src/media/audio/audio_device_name.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
-#define MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
-
-#include <list>
-#include <string>
-#include "media/base/media_export.h"
-
-namespace media {
-
-struct MEDIA_EXPORT AudioDeviceName {
-  AudioDeviceName();
-  AudioDeviceName(const std::string& device_name,
-                  const std::string& unique_id);
-
-  std::string device_name;  // Friendly name of the device.
-  std::string unique_id;    // Unique identifier for the device.
-};
-
-typedef std::list<AudioDeviceName> AudioDeviceNames;
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
diff --git a/src/media/audio/audio_device_thread.cc b/src/media/audio/audio_device_thread.cc
deleted file mode 100644
index 51d5ecd..0000000
--- a/src/media/audio/audio_device_thread.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_device_thread.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/aligned_memory.h"
-#include "base/message_loop.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "media/audio/audio_util.h"
-#include "media/base/audio_bus.h"
-
-using base::PlatformThread;
-
-namespace media {
-
-// The actual worker thread implementation.  It's very bare bones and much
-// simpler than SimpleThread (no synchronization in Start, etc) and supports
-// joining the thread handle asynchronously via a provided message loop even
-// after the Thread object itself has been deleted.
-class AudioDeviceThread::Thread
-    : public PlatformThread::Delegate,
-      public base::RefCountedThreadSafe<AudioDeviceThread::Thread> {
- public:
-  Thread(AudioDeviceThread::Callback* callback,
-         base::SyncSocket::Handle socket,
-         const char* thread_name);
-
-  void Start();
-
-  // Stops the thread.  If |loop_for_join| is non-NULL, the function posts
-  // a task to join (close) the thread handle later instead of waiting for
-  // the thread.  If loop_for_join is NULL, then the function waits
-  // synchronously for the thread to terminate.
-  void Stop(MessageLoop* loop_for_join);
-
- private:
-  friend class base::RefCountedThreadSafe<AudioDeviceThread::Thread>;
-  virtual ~Thread();
-
-  // Overrides from PlatformThread::Delegate.
-  virtual void ThreadMain() OVERRIDE;
-
-  // Runs the loop that reads from the socket.
-  void Run();
-
- private:
-  base::PlatformThreadHandle thread_;
-  AudioDeviceThread::Callback* callback_;
-  base::CancelableSyncSocket socket_;
-  base::Lock callback_lock_;
-  const char* thread_name_;
-
-  DISALLOW_COPY_AND_ASSIGN(Thread);
-};
-
-// AudioDeviceThread implementation
-
-AudioDeviceThread::AudioDeviceThread() {
-}
-
-AudioDeviceThread::~AudioDeviceThread() {
-  DCHECK(!thread_);
-}
-
-void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback,
-                              base::SyncSocket::Handle socket,
-                              const char* thread_name) {
-  base::AutoLock auto_lock(thread_lock_);
-  CHECK(thread_ == NULL);
-  thread_ = new AudioDeviceThread::Thread(callback, socket, thread_name);
-  thread_->Start();
-}
-
-void AudioDeviceThread::Stop(MessageLoop* loop_for_join) {
-  base::AutoLock auto_lock(thread_lock_);
-  if (thread_) {
-    thread_->Stop(loop_for_join);
-    thread_ = NULL;
-  }
-}
-
-bool AudioDeviceThread::IsStopped() {
-  base::AutoLock auto_lock(thread_lock_);
-  return thread_ == NULL;
-}
-
-// AudioDeviceThread::Thread implementation
-AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback,
-                                  base::SyncSocket::Handle socket,
-                                  const char* thread_name)
-    : thread_(base::kNullThreadHandle),
-      callback_(callback),
-      socket_(socket),
-      thread_name_(thread_name) {
-}
-
-AudioDeviceThread::Thread::~Thread() {
-  DCHECK_EQ(thread_, base::kNullThreadHandle) << "Stop wasn't called";
-}
-
-void AudioDeviceThread::Thread::Start() {
-  base::AutoLock auto_lock(callback_lock_);
-  DCHECK_EQ(thread_, base::kNullThreadHandle);
-  // This reference will be released when the thread exists.
-  AddRef();
-
-  PlatformThread::CreateWithPriority(0, this, &thread_,
-                                     base::kThreadPriority_RealtimeAudio);
-  CHECK(thread_ != base::kNullThreadHandle);
-}
-
-void AudioDeviceThread::Thread::Stop(MessageLoop* loop_for_join) {
-  socket_.Shutdown();
-
-  base::PlatformThreadHandle thread = base::kNullThreadHandle;
-
-  {  // NOLINT
-    base::AutoLock auto_lock(callback_lock_);
-    callback_ = NULL;
-    std::swap(thread, thread_);
-  }
-
-  if (thread != base::kNullThreadHandle) {
-    if (loop_for_join) {
-      loop_for_join->PostTask(FROM_HERE,
-          base::Bind(&base::PlatformThread::Join, thread));
-    } else {
-      base::PlatformThread::Join(thread);
-    }
-  }
-}
-
-void AudioDeviceThread::Thread::ThreadMain() {
-  PlatformThread::SetName(thread_name_);
-
-  // Singleton access is safe from this thread as long as callback is non-NULL.
-  // The callback is the only point where the thread calls out to 'unknown' code
-  // that might touch singletons and the lifetime of the callback is controlled
-  // by another thread on which singleton access is OK as well.
-  base::ThreadRestrictions::SetSingletonAllowed(true);
-
-  {  // NOLINT
-    base::AutoLock auto_lock(callback_lock_);
-    if (callback_)
-      callback_->InitializeOnAudioThread();
-  }
-
-  Run();
-
-  // Release the reference for the thread. Note that after this, the Thread
-  // instance will most likely be deleted.
-  Release();
-}
-
-void AudioDeviceThread::Thread::Run() {
-  while (true) {
-    int pending_data = 0;
-    size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data));
-    if (bytes_read != sizeof(pending_data)) {
-      DCHECK_EQ(bytes_read, 0U);
-      break;
-    }
-
-    base::AutoLock auto_lock(callback_lock_);
-    if (callback_)
-      callback_->Process(pending_data);
-  }
-}
-
-// AudioDeviceThread::Callback implementation
-
-AudioDeviceThread::Callback::Callback(
-    const AudioParameters& audio_parameters,
-    int input_channels,
-    base::SharedMemoryHandle memory, int memory_length)
-    : audio_parameters_(audio_parameters),
-      input_channels_(input_channels),
-      samples_per_ms_(audio_parameters.sample_rate() / 1000),
-      bytes_per_ms_(audio_parameters.channels() *
-                    (audio_parameters_.bits_per_sample() / 8) *
-                    samples_per_ms_),
-      shared_memory_(memory, false),
-      memory_length_(memory_length) {
-  CHECK_NE(bytes_per_ms_, 0);  // Catch division by zero early.
-  CHECK_NE(samples_per_ms_, 0);
-}
-
-AudioDeviceThread::Callback::~Callback() {}
-
-void AudioDeviceThread::Callback::InitializeOnAudioThread() {
-  MapSharedMemory();
-  DCHECK(shared_memory_.memory() != NULL);
-}
-
-}  // namespace media.
diff --git a/src/media/audio/audio_device_thread.h b/src/media/audio/audio_device_thread.h
deleted file mode 100644
index 44dbc3a..0000000
--- a/src/media/audio/audio_device_thread.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
-#define MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/shared_memory.h"
-#include "base/sync_socket.h"
-#include "base/synchronization/lock.h"
-#include "media/base/media_export.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/shared_memory_util.h"
-
-class MessageLoop;
-
-namespace media {
-class AudioBus;
-
-// Data transfer between browser and render process uses a combination
-// of sync sockets and shared memory. To read from the socket and render
-// data, we use a worker thread, a.k.a. the AudioDeviceThread, which reads
-// data from the browser via the socket and fills the shared memory from the
-// audio thread via the AudioDeviceThread::Callback interface/class.
-// For more details see the documentation in audio_device.h.
-//
-// TODO(tommi): Multiple audio input/output device instances should be able to
-// share the same thread instead of spinning one per instance.
-class MEDIA_EXPORT AudioDeviceThread {
- public:
-  // This is the callback interface/base class that Audio[Output|Input]Device
-  // implements to render input/output data. The callbacks run on the
-  // thread owned by AudioDeviceThread.
-  class Callback {
-   public:
-    Callback(const AudioParameters& audio_parameters,
-             int input_channels,
-             base::SharedMemoryHandle memory,
-             int memory_length);
-    virtual ~Callback();
-
-    // One time initialization for the callback object on the audio thread.
-    void InitializeOnAudioThread();
-
-    // Derived implementations must call shared_memory_.Map appropriately
-    // before Process can be called.
-    virtual void MapSharedMemory() = 0;
-
-    // Called whenever we receive notifications about pending data.
-    virtual void Process(int pending_data) = 0;
-
-   protected:
-    // Protected so that derived classes can access directly.
-    // The variables are 'const' since values are calculated/set in the
-    // constructor and must never change.
-    const AudioParameters audio_parameters_;
-    const int input_channels_;
-    const int samples_per_ms_;
-    const int bytes_per_ms_;
-
-    base::SharedMemory shared_memory_;
-    const int memory_length_;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Callback);
-  };
-
-  AudioDeviceThread();
-  ~AudioDeviceThread();
-
-  // Starts the audio thread. The thread must not already be running.
-  void Start(AudioDeviceThread::Callback* callback,
-             base::SyncSocket::Handle socket,
-             const char* thread_name);
-
-  // This tells the audio thread to stop and clean up the data.
-  // The method can stop the thread synchronously or asynchronously.
-  // In the latter case, the thread will still be running after Stop()
-  // returns, but the callback pointer is cleared so no further callbacks will
-  // be made (IOW after Stop() returns, it is safe to delete the callback).
-  // The |loop_for_join| parameter is required for asynchronous operation
-  // in order to join the worker thread and close the thread handle later via a
-  // posted task.
-  // If set to NULL, function will wait for the thread to exit before returning.
-  void Stop(MessageLoop* loop_for_join);
-
-  // Returns true if the thread is stopped or stopping.
-  bool IsStopped();
-
- private:
-  // Our own private SimpleThread override.  We implement this in a
-  // private class so that we get the following benefits:
-  // 1) AudioDeviceThread doesn't expose SimpleThread methods.
-  //    I.e. the caller can't call Start()/Stop() - which would be bad.
-  // 2) We override ThreadMain to add additional on-thread initialization
-  //    while still synchronized with SimpleThread::Start() to provide
-  //    reliable initialization.
-  class Thread;
-
-  base::Lock thread_lock_;
-  scoped_refptr<AudioDeviceThread::Thread> thread_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioDeviceThread);
-};
-
-}  // namespace media.
-
-#endif  // MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
diff --git a/src/media/audio/audio_input_controller.cc b/src/media/audio/audio_input_controller.cc
deleted file mode 100644
index 99bb420..0000000
--- a/src/media/audio/audio_input_controller.cc
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_input_controller.h"
-
-#include "base/bind.h"
-#include "base/threading/thread_restrictions.h"
-#include "media/base/limits.h"
-
-namespace {
-const int kMaxInputChannels = 2;
-const int kTimerResetIntervalSeconds = 1;
-#if defined(OS_IOS)
-// The first callback on iOS is received after the current background
-// audio has faded away.
-const int kTimerInitialIntervalSeconds = 4;
-#else
-const int kTimerInitialIntervalSeconds = 1;
-#endif  // defined(OS_IOS)
-}
-
-namespace media {
-
-// static
-AudioInputController::Factory* AudioInputController::factory_ = NULL;
-
-AudioInputController::AudioInputController(EventHandler* handler,
-                                           SyncWriter* sync_writer)
-    : creator_loop_(base::MessageLoopProxy::current()),
-      handler_(handler),
-      stream_(NULL),
-      data_is_active_(false),
-      state_(kEmpty),
-      sync_writer_(sync_writer),
-      max_volume_(0.0) {
-  DCHECK(creator_loop_);
-}
-
-AudioInputController::~AudioInputController() {
-  DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_);
-}
-
-// static
-scoped_refptr<AudioInputController> AudioInputController::Create(
-    AudioManager* audio_manager,
-    EventHandler* event_handler,
-    const AudioParameters& params) {
-  DCHECK(audio_manager);
-
-  if (!params.IsValid() || (params.channels() > kMaxInputChannels))
-    return NULL;
-
-  if (factory_)
-    return factory_->Create(audio_manager, event_handler, params);
-
-  scoped_refptr<AudioInputController> controller(new AudioInputController(
-      event_handler, NULL));
-
-  controller->message_loop_ = audio_manager->GetMessageLoop();
-
-  // Create and open a new audio input stream from the existing
-  // audio-device thread. Use the default audio-input device.
-  std::string device_id = AudioManagerBase::kDefaultDeviceId;
-  if (!controller->message_loop_->PostTask(FROM_HERE,
-          base::Bind(&AudioInputController::DoCreate, controller,
-                     base::Unretained(audio_manager), params, device_id))) {
-    controller = NULL;
-  }
-
-  return controller;
-}
-
-// static
-scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
-    AudioManager* audio_manager,
-    EventHandler* event_handler,
-    const AudioParameters& params,
-    const std::string& device_id,
-    SyncWriter* sync_writer) {
-  DCHECK(audio_manager);
-  DCHECK(sync_writer);
-
-  if (!params.IsValid() || (params.channels() > kMaxInputChannels))
-    return NULL;
-
-  // Create the AudioInputController object and ensure that it runs on
-  // the audio-manager thread.
-  scoped_refptr<AudioInputController> controller(new AudioInputController(
-      event_handler, sync_writer));
-  controller->message_loop_ = audio_manager->GetMessageLoop();
-
-  // Create and open a new audio input stream from the existing
-  // audio-device thread. Use the provided audio-input device.
-  if (!controller->message_loop_->PostTask(FROM_HERE,
-          base::Bind(&AudioInputController::DoCreate, controller,
-                     base::Unretained(audio_manager), params, device_id))) {
-    controller = NULL;
-  }
-
-  return controller;
-}
-
-void AudioInputController::Record() {
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioInputController::DoRecord, this));
-}
-
-void AudioInputController::Close(const base::Closure& closed_task) {
-  DCHECK(!closed_task.is_null());
-  DCHECK(creator_loop_->BelongsToCurrentThread());
-
-  message_loop_->PostTaskAndReply(
-      FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task);
-}
-
-void AudioInputController::SetVolume(double volume) {
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioInputController::DoSetVolume, this, volume));
-}
-
-void AudioInputController::SetAutomaticGainControl(bool enabled) {
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioInputController::DoSetAutomaticGainControl, this, enabled));
-}
-
-void AudioInputController::DoCreate(AudioManager* audio_manager,
-                                    const AudioParameters& params,
-                                    const std::string& device_id) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  stream_ = audio_manager->MakeAudioInputStream(params, device_id);
-
-  if (!stream_) {
-    // TODO(satish): Define error types.
-    handler_->OnError(this, 0);
-    return;
-  }
-
-  if (stream_ && !stream_->Open()) {
-    stream_->Close();
-    stream_ = NULL;
-    // TODO(satish): Define error types.
-    handler_->OnError(this, 0);
-    return;
-  }
-
-  DCHECK(!no_data_timer_.get());
-  // Create the data timer which will call DoCheckForNoData(). The timer
-  // is started in DoRecord() and restarted in each DoCheckForNoData() callback.
-  no_data_timer_.reset(new base::Timer(
-      FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds),
-      base::Bind(&AudioInputController::DoCheckForNoData,
-      base::Unretained(this)), false));
-  state_ = kCreated;
-  handler_->OnCreated(this);
-}
-
-void AudioInputController::DoRecord() {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  if (state_ != kCreated)
-    return;
-
-  {
-    base::AutoLock auto_lock(lock_);
-    state_ = kRecording;
-  }
-
-  // Start the data timer. Once |kTimerResetIntervalSeconds| have passed,
-  // a callback to DoCheckForNoData() is made.
-  no_data_timer_->Reset();
-
-  stream_->Start(this);
-  handler_->OnRecording(this);
-}
-
-void AudioInputController::DoClose() {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  // Delete the timer on the same thread that created it.
-  no_data_timer_.reset();
-
-  if (state_ != kClosed) {
-    DoStopCloseAndClearStream(NULL);
-    SetDataIsActive(false);
-
-    if (LowLatencyMode()) {
-      sync_writer_->Close();
-    }
-
-    state_ = kClosed;
-  }
-}
-
-void AudioInputController::DoReportError(int code) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  handler_->OnError(this, code);
-}
-
-void AudioInputController::DoSetVolume(double volume) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  DCHECK_GE(volume, 0);
-  DCHECK_LE(volume, 1.0);
-
-  if (state_ != kCreated && state_ != kRecording)
-    return;
-
-  // Only ask for the maximum volume at first call and use cached value
-  // for remaining function calls.
-  if (!max_volume_) {
-    max_volume_ = stream_->GetMaxVolume();
-  }
-
-  if (max_volume_ == 0.0) {
-    DLOG(WARNING) << "Failed to access input volume control";
-    return;
-  }
-
-  // Set the stream volume and scale to a range matched to the platform.
-  stream_->SetVolume(max_volume_ * volume);
-}
-
-void AudioInputController::DoSetAutomaticGainControl(bool enabled) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  DCHECK_NE(state_, kRecording);
-
-  // Ensure that the AGC state only can be modified before streaming starts.
-  if (state_ != kCreated || state_ == kRecording)
-    return;
-
-  stream_->SetAutomaticGainControl(enabled);
-}
-
-void AudioInputController::DoCheckForNoData() {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  if (!GetDataIsActive()) {
-    // The data-is-active marker will be false only if it has been more than
-    // one second since a data packet was recorded. This can happen if a
-    // capture device has been removed or disabled.
-    handler_->OnError(this, 0);
-    return;
-  }
-
-  // Mark data as non-active. The flag will be re-enabled in OnData() each
-  // time a data packet is received. Hence, under normal conditions, the
-  // flag will only be disabled during a very short period.
-  SetDataIsActive(false);
-
-  // Restart the timer to ensure that we check the flag again in
-  // |kTimerResetIntervalSeconds|.
-  no_data_timer_->Start(
-      FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds),
-      base::Bind(&AudioInputController::DoCheckForNoData,
-      base::Unretained(this)));
-}
-
-void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
-                                  uint32 size, uint32 hardware_delay_bytes,
-                                  double volume) {
-  {
-    base::AutoLock auto_lock(lock_);
-    if (state_ != kRecording)
-      return;
-  }
-
-  // Mark data as active to ensure that the periodic calls to
-  // DoCheckForNoData() does not report an error to the event handler.
-  SetDataIsActive(true);
-
-  // Use SyncSocket if we are in a low-latency mode.
-  if (LowLatencyMode()) {
-    sync_writer_->Write(data, size, volume);
-    sync_writer_->UpdateRecordedBytes(hardware_delay_bytes);
-    return;
-  }
-
-  handler_->OnData(this, data, size);
-}
-
-void AudioInputController::OnClose(AudioInputStream* stream) {
-  DVLOG(1) << "AudioInputController::OnClose()";
-  // TODO(satish): Sometimes the device driver closes the input stream without
-  // us asking for it (may be if the device was unplugged?). Check how to handle
-  // such cases here.
-}
-
-void AudioInputController::OnError(AudioInputStream* stream, int code) {
-  // Handle error on the audio-manager thread.
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioInputController::DoReportError, this, code));
-}
-
-void AudioInputController::DoStopCloseAndClearStream(
-    base::WaitableEvent *done) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  // Allow calling unconditionally and bail if we don't have a stream to close.
-  if (stream_ != NULL) {
-    stream_->Stop();
-    stream_->Close();
-    stream_ = NULL;
-  }
-
-  // Should be last in the method, do not touch "this" from here on.
-  if (done != NULL)
-    done->Signal();
-}
-
-void AudioInputController::SetDataIsActive(bool enabled) {
-  base::subtle::Release_Store(&data_is_active_, enabled);
-}
-
-bool AudioInputController::GetDataIsActive() {
-  return (base::subtle::Acquire_Load(&data_is_active_) != false);
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_controller.h b/src/media/audio/audio_input_controller.h
deleted file mode 100644
index 77e3e87..0000000
--- a/src/media/audio/audio_input_controller.h
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_
-
-#include <string>
-#include "base/atomicops.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "base/timer.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-
-// An AudioInputController controls an AudioInputStream and records data
-// from this input stream. The two main methods are Record() and Close() and
-// they are both executed on the audio thread which is injected by the two
-// alternative factory methods, Create() or CreateLowLatency().
-//
-// All public methods of AudioInputController are non-blocking.
-//
-// Here is a state diagram for the AudioInputController:
-//
-//                    .-->  [ Closed / Error ]  <--.
-//                    |                            |
-//                    |                            |
-//               [ Created ]  ---------->  [ Recording ]
-//                    ^
-//                    |
-//              *[  Empty  ]
-//
-// * Initial state
-//
-// State sequences (assuming low-latency):
-//
-//  [Creating Thread]                     [Audio Thread]
-//
-//      User               AudioInputController               EventHandler
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// CrateLowLatency() ==>        DoCreate()
-//                   AudioManager::MakeAudioInputStream()
-//                        AudioInputStream::Open()
-//                                  .- - - - - - - - - - - - ->   OnError()
-//                          create the data timer
-//                                  .------------------------->  OnCreated()
-//                               kCreated
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Record() ==>                 DoRecord()
-//                      AudioInputStream::Start()
-//                                  .------------------------->  OnRecording()
-//                          start the data timer
-//                              kRecording
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Close() ==>                  DoClose()
-//                        delete the data timer
-//                           state_ = kClosed
-//                        AudioInputStream::Stop()
-//                        AudioInputStream::Close()
-//                          SyncWriter::Close()
-// Closure::Run() <-----------------.
-// (closure-task)
-//
-// The audio thread itself is owned by the AudioManager that the
-// AudioInputController holds a reference to.  When performing tasks on the
-// audio thread, the controller must not add or release references to the
-// AudioManager or itself (since it in turn holds a reference to the manager).
-//
-namespace media {
-
-class MEDIA_EXPORT AudioInputController
-    : public base::RefCountedThreadSafe<AudioInputController>,
-      public AudioInputStream::AudioInputCallback {
- public:
-  // An event handler that receives events from the AudioInputController. The
-  // following methods are all called on the audio thread.
-  class MEDIA_EXPORT EventHandler {
-   public:
-    virtual void OnCreated(AudioInputController* controller) = 0;
-    virtual void OnRecording(AudioInputController* controller) = 0;
-    virtual void OnError(AudioInputController* controller, int error_code) = 0;
-    virtual void OnData(AudioInputController* controller, const uint8* data,
-                        uint32 size) = 0;
-
-   protected:
-    virtual ~EventHandler() {}
-  };
-
-  // A synchronous writer interface used by AudioInputController for
-  // synchronous writing.
-  class SyncWriter {
-   public:
-    virtual ~SyncWriter() {}
-
-    // Notify the synchronous writer about the number of bytes in the
-    // soundcard which has been recorded.
-    virtual void UpdateRecordedBytes(uint32 bytes) = 0;
-
-    // Write certain amount of data from |data|. This method returns
-    // number of written bytes.
-    virtual uint32 Write(const void* data, uint32 size, double volume) = 0;
-
-    // Close this synchronous writer.
-    virtual void Close() = 0;
-  };
-
-  // AudioInputController::Create() can use the currently registered Factory
-  // to create the AudioInputController. Factory is intended for testing only.
-  class Factory {
-   public:
-    virtual AudioInputController* Create(AudioManager* audio_manager,
-                                         EventHandler* event_handler,
-                                         AudioParameters params) = 0;
-   protected:
-    virtual ~Factory() {}
-  };
-
-  // Factory method for creating an AudioInputController.
-  // The audio device will be created on the audio thread, and when that is
-  // done, the event handler will receive an OnCreated() call from that same
-  // thread.
-  static scoped_refptr<AudioInputController> Create(
-      AudioManager* audio_manager,
-      EventHandler* event_handler,
-      const AudioParameters& params);
-
-  // Sets the factory used by the static method Create(). AudioInputController
-  // does not take ownership of |factory|. A value of NULL results in an
-  // AudioInputController being created directly.
-  static void set_factory_for_testing(Factory* factory) { factory_ = factory; }
-  AudioInputStream* stream_for_testing() { return stream_; }
-
-  // Factory method for creating an AudioInputController for low-latency mode.
-  // The audio device will be created on the audio thread, and when that is
-  // done, the event handler will receive an OnCreated() call from that same
-  // thread.
-  static scoped_refptr<AudioInputController> CreateLowLatency(
-      AudioManager* audio_manager,
-      EventHandler* event_handler,
-      const AudioParameters& params,
-      const std::string& device_id,
-      // External synchronous writer for audio controller.
-      SyncWriter* sync_writer);
-
-  // Starts recording using the created audio input stream.
-  // This method is called on the creator thread.
-  virtual void Record();
-
-  // Closes the audio input stream. The state is changed and the resources
-  // are freed on the audio thread. |closed_task| is then executed on the thread
-  // that called Close().
-  // Callbacks (EventHandler and SyncWriter) must exist until |closed_task|
-  // is called.
-  // It is safe to call this method more than once. Calls after the first one
-  // will have no effect.
-  // This method trampolines to the audio thread.
-  virtual void Close(const base::Closure& closed_task);
-
-  // Sets the capture volume of the input stream. The value 0.0 corresponds
-  // to muted and 1.0 to maximum volume.
-  virtual void SetVolume(double volume);
-
-  // Sets the Automatic Gain Control (AGC) state of the input stream.
-  // Changing the AGC state is not supported while recording is active.
-  virtual void SetAutomaticGainControl(bool enabled);
-
-  // AudioInputCallback implementation. Threading details depends on the
-  // device-specific implementation.
-  virtual void OnData(AudioInputStream* stream, const uint8* src, uint32 size,
-                      uint32 hardware_delay_bytes, double volume) OVERRIDE;
-  virtual void OnClose(AudioInputStream* stream) OVERRIDE;
-  virtual void OnError(AudioInputStream* stream, int code) OVERRIDE;
-
-  bool LowLatencyMode() const { return sync_writer_ != NULL; }
-
- protected:
-  friend class base::RefCountedThreadSafe<AudioInputController>;
-
-  // Internal state of the source.
-  enum State {
-    kEmpty,
-    kCreated,
-    kRecording,
-    kClosed,
-    kError
-  };
-
-  AudioInputController(EventHandler* handler, SyncWriter* sync_writer);
-  virtual ~AudioInputController();
-
-  // Methods called on the audio thread (owned by the AudioManager).
-  void DoCreate(AudioManager* audio_manager, const AudioParameters& params,
-                const std::string& device_id);
-  void DoRecord();
-  void DoClose();
-  void DoReportError(int code);
-  void DoSetVolume(double volume);
-  void DoSetAutomaticGainControl(bool enabled);
-
-  // Method which ensures that OnError() is triggered when data recording
-  // times out. Called on the audio thread.
-  void DoCheckForNoData();
-
-  // Helper method that stops, closes, and NULL:s |*stream_|.
-  // Signals event when done if the event is not NULL.
-  void DoStopCloseAndClearStream(base::WaitableEvent* done);
-
-  void SetDataIsActive(bool enabled);
-  bool GetDataIsActive();
-
-  // Gives access to the message loop of the creating thread.
-  scoped_refptr<base::MessageLoopProxy> creator_loop_;
-
-  // The message loop of audio-manager thread that this object runs on.
-  scoped_refptr<base::MessageLoopProxy> message_loop_;
-
-  // Contains the AudioInputController::EventHandler which receives state
-  // notifications from this class.
-  EventHandler* handler_;
-
-  // Pointer to the audio input stream object.
-  AudioInputStream* stream_;
-
-  // |no_data_timer_| is used to call OnError() when we stop receiving
-  // OnData() calls without an OnClose() call. This can occur
-  // when an audio input device is unplugged whilst recording on Windows.
-  // See http://crbug.com/79936 for details.
-  // This member is only touched by the audio thread.
-  scoped_ptr<base::Timer> no_data_timer_;
-
-  // This flag is used to signal that we are receiving OnData() calls, i.e,
-  // that data is active. It can be touched by the audio thread and by the
-  // low-level audio thread which calls OnData(). E.g. on Windows, the
-  // low-level audio thread is called wasapi_capture_thread.
-  base::subtle::Atomic32 data_is_active_;
-
-  // |state_| is written on the audio thread and is read on the hardware audio
-  // thread. These operations need to be locked. But lock is not required for
-  // reading on the audio input controller thread.
-  State state_;
-
-  base::Lock lock_;
-
-  // SyncWriter is used only in low-latency mode for synchronous writing.
-  SyncWriter* sync_writer_;
-
-  static Factory* factory_;
-
-  double max_volume_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioInputController);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_
diff --git a/src/media/audio/audio_input_controller_unittest.cc b/src/media/audio/audio_input_controller_unittest.cc
deleted file mode 100644
index 0a2a39b..0000000
--- a/src/media/audio/audio_input_controller_unittest.cc
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
-#include "media/audio/audio_input_controller.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::AtLeast;
-using ::testing::Exactly;
-using ::testing::InvokeWithoutArgs;
-using ::testing::NotNull;
-
-namespace media {
-
-static const int kSampleRate = AudioParameters::kAudioCDSampleRate;
-static const int kBitsPerSample = 16;
-static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
-static const int kSamplesPerPacket = kSampleRate / 10;
-
-// Posts MessageLoop::QuitClosure() on specified message loop.
-ACTION_P(QuitMessageLoop, loop_or_proxy) {
-  loop_or_proxy->PostTask(FROM_HERE, MessageLoop::QuitClosure());
-}
-
-// Posts MessageLoop::QuitClosure() on specified message loop after a certain
-// number of calls given by |limit|.
-ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop_or_proxy) {
-  if (++*count >= limit) {
-    loop_or_proxy->PostTask(FROM_HERE, MessageLoop::QuitClosure());
-  }
-}
-
-// Closes AudioOutputController synchronously.
-static void CloseAudioController(AudioInputController* controller) {
-  controller->Close(MessageLoop::QuitClosure());
-  MessageLoop::current()->Run();
-}
-
-class MockAudioInputControllerEventHandler
-    : public AudioInputController::EventHandler {
- public:
-  MockAudioInputControllerEventHandler() {}
-
-  MOCK_METHOD1(OnCreated, void(AudioInputController* controller));
-  MOCK_METHOD1(OnRecording, void(AudioInputController* controller));
-  MOCK_METHOD2(OnError, void(AudioInputController* controller, int error_code));
-  MOCK_METHOD3(OnData, void(AudioInputController* controller,
-                            const uint8* data, uint32 size));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockAudioInputControllerEventHandler);
-};
-
-// Test fixture.
-class AudioInputControllerTest : public testing::Test {
- public:
-  AudioInputControllerTest() {}
-  virtual ~AudioInputControllerTest() {}
-
- protected:
-  MessageLoop message_loop_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AudioInputControllerTest);
-};
-
-// Test AudioInputController for create and close without recording audio.
-TEST_F(AudioInputControllerTest, CreateAndClose) {
-  MockAudioInputControllerEventHandler event_handler;
-
-  // OnCreated() will be posted once.
-  EXPECT_CALL(event_handler, OnCreated(NotNull()))
-      .WillOnce(QuitMessageLoop(&message_loop_));
-
-  scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
-  AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
-                         kSampleRate, kBitsPerSample, kSamplesPerPacket);
-  scoped_refptr<AudioInputController> controller =
-      AudioInputController::Create(audio_manager.get(), &event_handler, params);
-  ASSERT_TRUE(controller.get());
-
-  // Wait for OnCreated() to fire.
-  message_loop_.Run();
-
-  // Close the AudioInputController synchronously.
-  CloseAudioController(controller);
-}
-
-// Test a normal call sequence of create, record and close.
-TEST_F(AudioInputControllerTest, RecordAndClose) {
-  MockAudioInputControllerEventHandler event_handler;
-  int count = 0;
-
-  // OnCreated() will be called once.
-  EXPECT_CALL(event_handler, OnCreated(NotNull()))
-      .Times(Exactly(1));
-
-  // OnRecording() will be called only once.
-  EXPECT_CALL(event_handler, OnRecording(NotNull()))
-      .Times(Exactly(1));
-
-  // OnData() shall be called ten times.
-  EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _))
-      .Times(AtLeast(10))
-      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10,
-          message_loop_.message_loop_proxy()));
-
-  scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
-  AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
-                         kSampleRate, kBitsPerSample, kSamplesPerPacket);
-
-  // Creating the AudioInputController should render an OnCreated() call.
-  scoped_refptr<AudioInputController> controller =
-      AudioInputController::Create(audio_manager.get(), &event_handler, params);
-  ASSERT_TRUE(controller.get());
-
-  // Start recording and trigger one OnRecording() call.
-  controller->Record();
-
-  // Record and wait until ten OnData() callbacks are received.
-  message_loop_.Run();
-
-  // Close the AudioInputController synchronously.
-  CloseAudioController(controller);
-}
-
-// Test that the AudioInputController reports an error when the input stream
-// stops without an OnClose() callback. This can happen when the underlying
-// audio layer stops feeding data as a result of a removed microphone device.
-TEST_F(AudioInputControllerTest, RecordAndError) {
-  MockAudioInputControllerEventHandler event_handler;
-  int count = 0;
-
-  // OnCreated() will be called once.
-  EXPECT_CALL(event_handler, OnCreated(NotNull()))
-      .Times(Exactly(1));
-
-  // OnRecording() will be called only once.
-  EXPECT_CALL(event_handler, OnRecording(NotNull()))
-      .Times(Exactly(1));
-
-  // OnData() shall be called ten times.
-  EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _))
-      .Times(AtLeast(10))
-      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10,
-          message_loop_.message_loop_proxy()));
-
-  // OnError() will be called after the data stream stops while the
-  // controller is in a recording state.
-  EXPECT_CALL(event_handler, OnError(NotNull(), 0))
-      .Times(Exactly(1))
-      .WillOnce(QuitMessageLoop(&message_loop_));
-
-  scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
-  AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
-                         kSampleRate, kBitsPerSample, kSamplesPerPacket);
-
-  // Creating the AudioInputController should render an OnCreated() call.
-  scoped_refptr<AudioInputController> controller =
-      AudioInputController::Create(audio_manager.get(), &event_handler, params);
-  ASSERT_TRUE(controller.get());
-
-  // Start recording and trigger one OnRecording() call.
-  controller->Record();
-
-  // Record and wait until ten OnData() callbacks are received.
-  message_loop_.Run();
-
-  // Stop the stream and verify that OnError() is posted.
-  AudioInputStream* stream = controller->stream_for_testing();
-  stream->Stop();
-  message_loop_.Run();
-
-  // Close the AudioInputController synchronously.
-  CloseAudioController(controller);
-}
-
-// Test that AudioInputController rejects insanely large packet sizes.
-TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) {
-  // Create an audio device with a very large packet size.
-  MockAudioInputControllerEventHandler event_handler;
-
-  // OnCreated() shall not be called in this test.
-  EXPECT_CALL(event_handler, OnCreated(NotNull()))
-    .Times(Exactly(0));
-
-  scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
-  AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
-                         kSampleRate, kBitsPerSample, kSamplesPerPacket * 1000);
-  scoped_refptr<AudioInputController> controller =
-      AudioInputController::Create(audio_manager.get(), &event_handler, params);
-  ASSERT_FALSE(controller);
-}
-
-// Test calling AudioInputController::Close multiple times.
-TEST_F(AudioInputControllerTest, CloseTwice) {
-  MockAudioInputControllerEventHandler event_handler;
-
-  // OnRecording() will be called only once.
-  EXPECT_CALL(event_handler, OnCreated(NotNull()));
-
-  // OnRecording() will be called only once.
-  EXPECT_CALL(event_handler, OnRecording(NotNull()))
-      .Times(Exactly(1));
-
-  scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
-  AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
-                         kSampleRate, kBitsPerSample, kSamplesPerPacket);
-  scoped_refptr<AudioInputController> controller =
-      AudioInputController::Create(audio_manager.get(), &event_handler, params);
-  ASSERT_TRUE(controller.get());
-
-  controller->Record();
-
-  controller->Close(MessageLoop::QuitClosure());
-  MessageLoop::current()->Run();
-
-  controller->Close(MessageLoop::QuitClosure());
-  MessageLoop::current()->Run();
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_device.cc b/src/media/audio/audio_input_device.cc
deleted file mode 100644
index 9edf6db..0000000
--- a/src/media/audio/audio_input_device.cc
+++ /dev/null
@@ -1,344 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_input_device.h"
-
-#include "base/bind.h"
-#include "base/message_loop.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/time.h"
-#include "media/audio/audio_manager_base.h"
-#include "media/base/audio_bus.h"
-
-namespace media {
-
-// Takes care of invoking the capture callback on the audio thread.
-// An instance of this class is created for each capture stream in
-// OnLowLatencyCreated().
-class AudioInputDevice::AudioThreadCallback
-    : public AudioDeviceThread::Callback {
- public:
-  AudioThreadCallback(const AudioParameters& audio_parameters,
-                      base::SharedMemoryHandle memory,
-                      int memory_length,
-                      CaptureCallback* capture_callback);
-  virtual ~AudioThreadCallback();
-
-  virtual void MapSharedMemory() OVERRIDE;
-
-  // Called whenever we receive notifications about pending data.
-  virtual void Process(int pending_data) OVERRIDE;
-
- private:
-  CaptureCallback* capture_callback_;
-  scoped_ptr<AudioBus> audio_bus_;
-  DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
-};
-
-AudioInputDevice::AudioInputDevice(
-    AudioInputIPC* ipc,
-    const scoped_refptr<base::MessageLoopProxy>& io_loop)
-    : ScopedLoopObserver(io_loop),
-      callback_(NULL),
-      event_handler_(NULL),
-      ipc_(ipc),
-      stream_id_(0),
-      session_id_(0),
-      pending_device_ready_(false),
-      agc_is_enabled_(false) {
-  CHECK(ipc_);
-}
-
-void AudioInputDevice::Initialize(const AudioParameters& params,
-                                  CaptureCallback* callback,
-                                  CaptureEventHandler* event_handler) {
-  DCHECK(!callback_);
-  DCHECK(!event_handler_);
-  audio_parameters_ = params;
-  callback_ = callback;
-  event_handler_ = event_handler;
-}
-
-void AudioInputDevice::SetDevice(int session_id) {
-  DVLOG(1) << "SetDevice (session_id=" << session_id << ")";
-  message_loop()->PostTask(FROM_HERE,
-      base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id));
-}
-
-void AudioInputDevice::Start() {
-  DVLOG(1) << "Start()";
-  message_loop()->PostTask(FROM_HERE,
-      base::Bind(&AudioInputDevice::InitializeOnIOThread, this));
-}
-
-void AudioInputDevice::Stop() {
-  DVLOG(1) << "Stop()";
-
-  {
-    base::AutoLock auto_lock(audio_thread_lock_);
-    audio_thread_.Stop(MessageLoop::current());
-  }
-
-  message_loop()->PostTask(FROM_HERE,
-      base::Bind(&AudioInputDevice::ShutDownOnIOThread, this));
-}
-
-void AudioInputDevice::SetVolume(double volume) {
-  if (volume < 0 || volume > 1.0) {
-    DLOG(ERROR) << "Invalid volume value specified";
-    return;
-  }
-
-  message_loop()->PostTask(FROM_HERE,
-      base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume));
-}
-
-void AudioInputDevice::SetAutomaticGainControl(bool enabled) {
-  DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")";
-  message_loop()->PostTask(FROM_HERE,
-      base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread,
-          this, enabled));
-}
-
-void AudioInputDevice::OnStreamCreated(
-    base::SharedMemoryHandle handle,
-    base::SyncSocket::Handle socket_handle,
-    int length) {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-#if defined(OS_WIN)
-  DCHECK(handle);
-  DCHECK(socket_handle);
-#elif defined(__LB_SHELL__) || defined(COBALT)
-  DCHECK(handle.get());
-#else
-  DCHECK_GE(handle.fd, 0);
-  DCHECK_GE(socket_handle, 0);
-#endif
-  DCHECK(length);
-  DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")";
-
-  // We should only get this callback if stream_id_ is valid.  If it is not,
-  // the IPC layer should have closed the shared memory and socket handles
-  // for us and not invoked the callback.  The basic assertion is that when
-  // stream_id_ is 0 the AudioInputDevice instance is not registered as a
-  // delegate and hence it should not receive callbacks.
-  DCHECK(stream_id_);
-
-  base::AutoLock auto_lock(audio_thread_lock_);
-
-  DCHECK(audio_thread_.IsStopped());
-  audio_callback_.reset(
-      new AudioInputDevice::AudioThreadCallback(audio_parameters_, handle,
-                                                length, callback_));
-  audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice");
-
-  MessageLoop::current()->PostTask(FROM_HERE,
-      base::Bind(&AudioInputDevice::StartOnIOThread, this));
-}
-
-void AudioInputDevice::OnVolume(double volume) {
-  NOTIMPLEMENTED();
-}
-
-void AudioInputDevice::OnStateChanged(
-    AudioInputIPCDelegate::State state) {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-
-  // Do nothing if the stream has been closed.
-  if (!stream_id_)
-    return;
-
-  switch (state) {
-    case AudioInputIPCDelegate::kStopped:
-      // TODO(xians): Should we just call ShutDownOnIOThread here instead?
-      ipc_->RemoveDelegate(stream_id_);
-
-      audio_thread_.Stop(MessageLoop::current());
-      audio_callback_.reset();
-
-      if (event_handler_)
-        event_handler_->OnDeviceStopped();
-
-      stream_id_ = 0;
-      pending_device_ready_ = false;
-      break;
-    case AudioInputIPCDelegate::kRecording:
-      NOTIMPLEMENTED();
-      break;
-    case AudioInputIPCDelegate::kError:
-      DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)";
-      // Don't dereference the callback object if the audio thread
-      // is stopped or stopping.  That could mean that the callback
-      // object has been deleted.
-      // TODO(tommi): Add an explicit contract for clearing the callback
-      // object.  Possibly require calling Initialize again or provide
-      // a callback object via Start() and clear it in Stop().
-      if (!audio_thread_.IsStopped())
-        callback_->OnCaptureError();
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
-void AudioInputDevice::OnDeviceReady(const std::string& device_id) {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")";
-
-  // Takes care of the case when Stop() is called before OnDeviceReady().
-  if (!pending_device_ready_)
-    return;
-
-  // If AudioInputDeviceManager returns an empty string, it means no device
-  // is ready for start.
-  if (device_id.empty()) {
-    ipc_->RemoveDelegate(stream_id_);
-    stream_id_ = 0;
-  } else {
-    ipc_->CreateStream(stream_id_, audio_parameters_, device_id,
-                       agc_is_enabled_);
-  }
-
-  pending_device_ready_ = false;
-  // Notify the client that the device has been started.
-  if (event_handler_)
-    event_handler_->OnDeviceStarted(device_id);
-}
-
-void AudioInputDevice::OnIPCClosed() {
-  ipc_ = NULL;
-}
-
-AudioInputDevice::~AudioInputDevice() {
-  // TODO(henrika): The current design requires that the user calls
-  // Stop before deleting this class.
-  CHECK_EQ(0, stream_id_);
-}
-
-void AudioInputDevice::InitializeOnIOThread() {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  // Make sure we don't call Start() more than once.
-  DCHECK_EQ(0, stream_id_);
-  if (stream_id_)
-    return;
-
-  stream_id_ = ipc_->AddDelegate(this);
-  // If |session_id_| is not specified, it will directly create the stream;
-  // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser
-  // and create the stream when getting a OnDeviceReady() callback.
-  if (!session_id_) {
-    ipc_->CreateStream(stream_id_, audio_parameters_,
-        AudioManagerBase::kDefaultDeviceId, agc_is_enabled_);
-  } else {
-    ipc_->StartDevice(stream_id_, session_id_);
-    pending_device_ready_ = true;
-  }
-}
-
-void AudioInputDevice::SetSessionIdOnIOThread(int session_id) {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  session_id_ = session_id;
-}
-
-void AudioInputDevice::StartOnIOThread() {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  if (stream_id_)
-    ipc_->RecordStream(stream_id_);
-}
-
-void AudioInputDevice::ShutDownOnIOThread() {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  // NOTE: |completion| may be NULL.
-  // Make sure we don't call shutdown more than once.
-  if (stream_id_) {
-    if (ipc_) {
-      ipc_->CloseStream(stream_id_);
-      ipc_->RemoveDelegate(stream_id_);
-    }
-
-    stream_id_ = 0;
-    session_id_ = 0;
-    pending_device_ready_ = false;
-    agc_is_enabled_ = false;
-  }
-
-  // We can run into an issue where ShutDownOnIOThread is called right after
-  // OnStreamCreated is called in cases where Start/Stop are called before we
-  // get the OnStreamCreated callback.  To handle that corner case, we call
-  // Stop(). In most cases, the thread will already be stopped.
-  // Another situation is when the IO thread goes away before Stop() is called
-  // in which case, we cannot use the message loop to close the thread handle
-  // and can't not rely on the main thread existing either.
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  audio_thread_.Stop(NULL);
-  audio_callback_.reset();
-}
-
-void AudioInputDevice::SetVolumeOnIOThread(double volume) {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  if (stream_id_)
-    ipc_->SetVolume(stream_id_, volume);
-}
-
-void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) {
-  DCHECK(message_loop()->BelongsToCurrentThread());
-  DCHECK_EQ(0, stream_id_) <<
-      "The AGC state can not be modified while capturing is active.";
-  if (stream_id_)
-    return;
-
-  // We simply store the new AGC setting here. This value will be used when
-  // a new stream is initialized and by GetAutomaticGainControl().
-  agc_is_enabled_ = enabled;
-}
-
-void AudioInputDevice::WillDestroyCurrentMessageLoop() {
-  LOG(ERROR) << "IO loop going away before the input device has been stopped";
-  ShutDownOnIOThread();
-}
-
-// AudioInputDevice::AudioThreadCallback
-AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
-    const AudioParameters& audio_parameters,
-    base::SharedMemoryHandle memory,
-    int memory_length,
-    CaptureCallback* capture_callback)
-    : AudioDeviceThread::Callback(audio_parameters, 0, memory, memory_length),
-      capture_callback_(capture_callback) {
-  audio_bus_ = AudioBus::Create(audio_parameters_);
-}
-
-AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
-}
-
-void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
-  shared_memory_.Map(memory_length_);
-}
-
-void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
-  // The shared memory represents parameters, size of the data buffer and the
-  // actual data buffer containing audio data. Map the memory into this
-  // structure and parse out parameters and the data area.
-  AudioInputBuffer* buffer =
-      reinterpret_cast<AudioInputBuffer*>(shared_memory_.memory());
-  DCHECK_EQ(buffer->params.size,
-            memory_length_ - sizeof(AudioInputBufferParameters));
-  double volume = buffer->params.volume;
-
-  int audio_delay_milliseconds = pending_data / bytes_per_ms_;
-  int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
-  const int bytes_per_sample = sizeof(memory[0]);
-
-  // Deinterleave each channel and convert to 32-bit floating-point
-  // with nominal range -1.0 -> +1.0.
-  audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample);
-
-  // Deliver captured data to the client in floating point format
-  // and update the audio-delay measurement.
-  capture_callback_->Capture(audio_bus_.get(),
-                             audio_delay_milliseconds, volume);
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_device.h b/src/media/audio/audio_input_device.h
deleted file mode 100644
index edefdf1..0000000
--- a/src/media/audio/audio_input_device.h
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Low-latency audio capturing class utilizing audio input stream provided
-// by a server (browser) process by use of an IPC interface.
-//
-// Relationship of classes:
-//
-//  AudioInputController                 AudioInputDevice
-//           ^                                  ^
-//           |                                  |
-//           v                  IPC             v
-// AudioInputRendererHost  <---------> AudioInputIPCDelegate
-//           ^                       (impl in AudioInputMessageFilter)
-//           |
-//           v
-// AudioInputDeviceManager
-//
-// Transportation of audio samples from the browser to the render process
-// is done by using shared memory in combination with a SyncSocket.
-// The AudioInputDevice user registers an AudioInputDevice::CaptureCallback by
-// calling Initialize().  The callback will be called with recorded audio from
-// the underlying audio layers.
-// The session ID is used by the AudioInputRendererHost to start the device
-// referenced by this ID.
-//
-// State sequences:
-//
-// Sequence where session_id has not been set using SetDevice():
-// ('<-' signifies callbacks, -> signifies calls made by AudioInputDevice)
-// Start -> InitializeOnIOThread -> CreateStream ->
-//       <- OnStreamCreated <-
-//       -> StartOnIOThread -> PlayStream ->
-//
-// Sequence where session_id has been set using SetDevice():
-// Start -> InitializeOnIOThread -> StartDevice ->
-//       <- OnDeviceReady <-
-//       -> CreateStream ->
-//       <- OnStreamCreated <-
-//       -> StartOnIOThread -> PlayStream ->
-//
-// AudioInputDevice::Capture => low latency audio transport on audio thread =>
-//                               |
-// Stop --> ShutDownOnIOThread ------>  CloseStream -> Close
-//
-// This class depends on two threads to function:
-//
-// 1. An IO thread.
-//    This thread is used to asynchronously process Start/Stop etc operations
-//    that are available via the public interface.  The public methods are
-//    asynchronous and simply post a task to the IO thread to actually perform
-//    the work.
-// 2. Audio transport thread.
-//    Responsible for calling the CaptureCallback and feed audio samples from
-//    the server side audio layer using a socket and shared memory.
-//
-// Implementation notes:
-// - The user must call Stop() before deleting the class instance.
-
-#ifndef MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
-
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/shared_memory.h"
-#include "media/audio/audio_device_thread.h"
-#include "media/audio/audio_input_ipc.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/scoped_loop_observer.h"
-#include "media/base/audio_capturer_source.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// TODO(henrika): This class is based on the AudioOutputDevice class and it has
-// many components in common. Investigate potential for re-factoring.
-// TODO(henrika): Add support for event handling (e.g. OnStateChanged,
-// OnCaptureStopped etc.) and ensure that we can deliver these notifications
-// to any clients using this class.
-class MEDIA_EXPORT AudioInputDevice
-    : NON_EXPORTED_BASE(public AudioCapturerSource),
-      NON_EXPORTED_BASE(public AudioInputIPCDelegate),
-      NON_EXPORTED_BASE(public ScopedLoopObserver) {
- public:
-  AudioInputDevice(AudioInputIPC* ipc,
-                   const scoped_refptr<base::MessageLoopProxy>& io_loop);
-
-  // AudioCapturerSource implementation.
-  virtual void Initialize(const AudioParameters& params,
-                          CaptureCallback* callback,
-                          CaptureEventHandler* event_handler) OVERRIDE;
-  virtual void Start() OVERRIDE;
-  virtual void Stop() OVERRIDE;
-  virtual void SetVolume(double volume) OVERRIDE;
-  virtual void SetDevice(int session_id) OVERRIDE;
-  virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
-
- protected:
-  // Methods called on IO thread ----------------------------------------------
-  // AudioInputIPCDelegate implementation.
-  virtual void OnStreamCreated(base::SharedMemoryHandle handle,
-                               base::SyncSocket::Handle socket_handle,
-                               int length) OVERRIDE;
-  virtual void OnVolume(double volume) OVERRIDE;
-  virtual void OnStateChanged(
-      AudioInputIPCDelegate::State state) OVERRIDE;
-  virtual void OnDeviceReady(const std::string& device_id) OVERRIDE;
-  virtual void OnIPCClosed() OVERRIDE;
-
-  friend class base::RefCountedThreadSafe<AudioInputDevice>;
-  virtual ~AudioInputDevice();
-
- private:
-  // Methods called on IO thread ----------------------------------------------
-  // The following methods are tasks posted on the IO thread that needs to
-  // be executed on that thread. They interact with AudioInputMessageFilter and
-  // sends IPC messages on that thread.
-  void InitializeOnIOThread();
-  void SetSessionIdOnIOThread(int session_id);
-  void StartOnIOThread();
-  void ShutDownOnIOThread();
-  void SetVolumeOnIOThread(double volume);
-  void SetAutomaticGainControlOnIOThread(bool enabled);
-
-  // MessageLoop::DestructionObserver implementation for the IO loop.
-  // If the IO loop dies before we do, we shut down the audio thread from here.
-  virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
-
-  AudioParameters audio_parameters_;
-
-  CaptureCallback* callback_;
-  CaptureEventHandler* event_handler_;
-
-  AudioInputIPC* ipc_;
-
-  // Our stream ID on the message filter. Only modified on the IO thread.
-  int stream_id_;
-
-  // The media session ID used to identify which input device to be started.
-  // Only modified on the IO thread.
-  int session_id_;
-
-  // State variable used to indicate it is waiting for a OnDeviceReady()
-  // callback. Only modified on the IO thread.
-  bool pending_device_ready_;
-
-  // Stores the Automatic Gain Control state. Default is false.
-  // Only modified on the IO thread.
-  bool agc_is_enabled_;
-
-  // Our audio thread callback class.  See source file for details.
-  class AudioThreadCallback;
-
-  // In order to avoid a race between OnStreamCreated and Stop(), we use this
-  // guard to control stopping and starting the audio thread.
-  base::Lock audio_thread_lock_;
-  AudioDeviceThread audio_thread_;
-  scoped_ptr<AudioInputDevice::AudioThreadCallback> audio_callback_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputDevice);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
diff --git a/src/media/audio/audio_input_device_unittest.cc b/src/media/audio/audio_input_device_unittest.cc
deleted file mode 100644
index dc211a4..0000000
--- a/src/media/audio/audio_input_device_unittest.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/environment.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/audio/audio_manager.h"
-#include "media/audio/audio_manager_base.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/audio_manager_win.h"
-#include "media/audio/win/wavein_input_win.h"
-#endif
-
-namespace media {
-
-// Test fixture which allows us to override the default enumeration API on
-// Windows.
-class AudioInputDeviceTest
-    : public ::testing::Test {
- protected:
-  AudioInputDeviceTest()
-      : audio_manager_(AudioManager::Create())
-#if defined(OS_WIN)
-      , com_init_(base::win::ScopedCOMInitializer::kMTA)
-#endif
-  {
-  }
-
-#if defined(OS_WIN)
-  bool SetMMDeviceEnumeration() {
-    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
-    // Windows Wave is used as default if Windows XP was detected =>
-    // return false since MMDevice is not supported on XP.
-    if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
-      return false;
-
-    amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
-    return true;
-  }
-
-  void SetWaveEnumeration() {
-    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
-    amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
-  }
-
-  std::string GetDeviceIdFromPCMWaveInAudioInputStream(
-      const std::string& device_id) {
-    AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
-    AudioParameters parameters(
-        AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
-        AudioParameters::kAudioCDSampleRate, 16,
-        1024);
-    scoped_ptr<PCMWaveInAudioInputStream> stream(
-        static_cast<PCMWaveInAudioInputStream*>(
-            amw->CreatePCMWaveInAudioInputStream(parameters, device_id)));
-    return stream.get() ? stream->device_id_ : std::string();
-  }
-#endif
-
-  // Helper method which verifies that the device list starts with a valid
-  // default record followed by non-default device names.
-  static void CheckDeviceNames(const AudioDeviceNames& device_names) {
-    if (!device_names.empty()) {
-      AudioDeviceNames::const_iterator it = device_names.begin();
-
-      // The first device in the list should always be the default device.
-      EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName),
-                it->device_name);
-      EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id);
-      ++it;
-
-      // Other devices should have non-empty name and id and should not contain
-      // default name or id.
-      while (it != device_names.end()) {
-        EXPECT_FALSE(it->device_name.empty());
-        EXPECT_FALSE(it->unique_id.empty());
-        EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
-                  it->device_name);
-        EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
-                  it->unique_id);
-        ++it;
-      }
-    } else {
-      // Log a warning so we can see the status on the build bots.  No need to
-      // break the test though since this does successfully test the code and
-      // some failure cases.
-      LOG(WARNING) << "No input devices detected";
-    }
-  }
-
-  bool CanRunAudioTest() {
-    return audio_manager_->HasAudioInputDevices();
-  }
-
-  scoped_ptr<AudioManager> audio_manager_;
-
-#if defined(OS_WIN)
-  // The MMDevice API requires COM to be initialized on the current thread.
-  base::win::ScopedCOMInitializer com_init_;
-#endif
-};
-
-// Test that devices can be enumerated.
-TEST_F(AudioInputDeviceTest, EnumerateDevices) {
-  if (!CanRunAudioTest())
-    return;
-
-  AudioDeviceNames device_names;
-  audio_manager_->GetAudioInputDeviceNames(&device_names);
-  CheckDeviceNames(device_names);
-}
-
-// Run additional tests for Windows since enumeration can be done using
-// two different APIs. MMDevice is default for Vista and higher and Wave
-// is default for XP and lower.
-#if defined(OS_WIN)
-
-// Override default enumeration API and force usage of Windows MMDevice.
-// This test will only run on Windows Vista and higher.
-TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) {
-  if (!CanRunAudioTest())
-    return;
-
-  AudioDeviceNames device_names;
-  if (!SetMMDeviceEnumeration()) {
-    // Usage of MMDevice will fail on XP and lower.
-    LOG(WARNING) << "MM device enumeration is not supported.";
-    return;
-  }
-  audio_manager_->GetAudioInputDeviceNames(&device_names);
-  CheckDeviceNames(device_names);
-}
-
-// Override default enumeration API and force usage of Windows Wave.
-// This test will run on Windows XP, Windows Vista and Windows 7.
-TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) {
-  if (!CanRunAudioTest())
-    return;
-
-  AudioDeviceNames device_names;
-  SetWaveEnumeration();
-  audio_manager_->GetAudioInputDeviceNames(&device_names);
-  CheckDeviceNames(device_names);
-}
-
-TEST_F(AudioInputDeviceTest, WinXPDeviceIdUnchanged) {
-  if (!CanRunAudioTest())
-    return;
-
-  AudioDeviceNames xp_device_names;
-  SetWaveEnumeration();
-  audio_manager_->GetAudioInputDeviceNames(&xp_device_names);
-  CheckDeviceNames(xp_device_names);
-
-  // Device ID should remain unchanged, including the default device ID.
-  for (AudioDeviceNames::iterator i = xp_device_names.begin();
-       i != xp_device_names.end(); ++i) {
-    EXPECT_EQ(i->unique_id,
-              GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id));
-  }
-}
-
-TEST_F(AudioInputDeviceTest, ConvertToWinXPDeviceId) {
-  if (!CanRunAudioTest())
-    return;
-
-  if (!SetMMDeviceEnumeration()) {
-    // Usage of MMDevice will fail on XP and lower.
-    LOG(WARNING) << "MM device enumeration is not supported.";
-    return;
-  }
-
-  AudioDeviceNames device_names;
-  audio_manager_->GetAudioInputDeviceNames(&device_names);
-  CheckDeviceNames(device_names);
-
-  for (AudioDeviceNames::iterator i = device_names.begin();
-       i != device_names.end(); ++i) {
-    std::string converted_id =
-        GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id);
-    if (i == device_names.begin()) {
-      // The first in the list is the default device ID, which should not be
-      // changed when passed to PCMWaveInAudioInputStream.
-      EXPECT_EQ(i->unique_id, converted_id);
-    } else {
-      // MMDevice-style device IDs should be converted to WaveIn-style device
-      // IDs.
-      EXPECT_NE(i->unique_id, converted_id);
-    }
-  }
-}
-
-#endif
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_ipc.cc b/src/media/audio/audio_input_ipc.cc
deleted file mode 100644
index 69253b0..0000000
--- a/src/media/audio/audio_input_ipc.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_input_ipc.h"
-
-namespace media {
-
-AudioInputIPCDelegate::~AudioInputIPCDelegate() {}
-
-AudioInputIPC::~AudioInputIPC() {}
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_ipc.h b/src/media/audio/audio_input_ipc.h
deleted file mode 100644
index eb4e72d..0000000
--- a/src/media/audio/audio_input_ipc.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
-
-#include "base/shared_memory.h"
-#include "base/sync_socket.h"
-#include "media/audio/audio_parameters.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// Contains IPC notifications for the state of the server side
-// (AudioInputController) audio state changes and when an AudioInputController
-// has been created.  Implemented by AudioInputDevice.
-class MEDIA_EXPORT AudioInputIPCDelegate {
- public:
-  // Valid states for the input stream.
-  enum State {
-    kRecording,
-    kStopped,
-    kError
-  };
-
-  // Called when an AudioInputController has been created.
-  // The shared memory |handle| points to a memory section that's used to
-  // transfer data between the AudioInputDevice and AudioInputController
-  // objects.  The implementation of OnStreamCreated takes ownership.
-  // The |socket_handle| is used by the AudioInputController to signal
-  // notifications that more data is available and can optionally provide
-  // parameter changes back.  The AudioInputDevice must read from this socket
-  // and process the shared memory whenever data is read from the socket.
-  virtual void OnStreamCreated(base::SharedMemoryHandle handle,
-                               base::SyncSocket::Handle socket_handle,
-                               int length) = 0;
-
-  // Called when state of an audio stream has changed.
-  virtual void OnStateChanged(State state) = 0;
-
-  // Called when the input stream volume has changed.
-  virtual void OnVolume(double volume) = 0;
-
-  // Called when a device has been started on the server side.
-  // If the device could not be started, |device_id| will be empty.
-  virtual void OnDeviceReady(const std::string& device_id) = 0;
-
-  // Called when the AudioInputIPC object is going away and/or when the
-  // IPC channel has been closed and no more IPC requests can be made.
-  // Implementations must clear any references to the AudioInputIPC object
-  // at this point.
-  virtual void OnIPCClosed() = 0;
-
- protected:
-  virtual ~AudioInputIPCDelegate();
-};
-
-// Provides IPC functionality for an AudioInputDevice.  The implementation
-// should asynchronously deliver the messages to an AudioInputController object
-// (or create one in the case of CreateStream()), that may live in a separate
-// process.
-class MEDIA_EXPORT AudioInputIPC {
- public:
-  // Registers an AudioInputIPCDelegate and returns a |stream_id| that
-  // must be used with all other IPC functions in this interface.
-  virtual int AddDelegate(AudioInputIPCDelegate* delegate) = 0;
-
-  // Unregisters a delegate that was previously registered via a call to
-  // AddDelegate().  The audio stream should be in a closed state prior to
-  // calling this function.
-  virtual void RemoveDelegate(int stream_id) = 0;
-
-  // Sends a request to create an AudioInputController object in the peer
-  // process, identify it by |stream_id| and configure it to use the specified
-  // audio |params|.  Once the stream has been created, the implementation must
-  // generate a notification to the AudioInputIPCDelegate and call
-  // OnStreamCreated().
-  virtual void CreateStream(int stream_id, const AudioParameters& params,
-      const std::string& device_id, bool automatic_gain_control) = 0;
-
-  // Starts the device on the server side.  Once the device has started,
-  // or failed to start, a callback to
-  // AudioInputIPCDelegate::OnDeviceReady() must be made.
-  virtual void StartDevice(int stream_id, int session_id) = 0;
-
-  // Corresponds to a call to AudioInputController::Record() on the server side.
-  virtual void RecordStream(int stream_id) = 0;
-
-  // Sets the volume of the audio stream.
-  virtual void SetVolume(int stream_id, double volume) = 0;
-
-  // Closes the audio stream and deletes the matching AudioInputController
-  // instance.  Prior to deleting the AudioInputController object, a call to
-  // AudioInputController::Close must be made.
-  virtual void CloseStream(int stream_id) = 0;
-
- protected:
-  virtual ~AudioInputIPC();
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
diff --git a/src/media/audio/audio_input_stream_impl.cc b/src/media/audio/audio_input_stream_impl.cc
deleted file mode 100644
index f68317c..0000000
--- a/src/media/audio/audio_input_stream_impl.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "media/audio/audio_input_stream_impl.h"
-
-namespace media {
-
-static const int kMinIntervalBetweenVolumeUpdatesMs = 1000;
-
-AudioInputStreamImpl::AudioInputStreamImpl()
-    : agc_is_enabled_(false),
-      max_volume_(0.0),
-      normalized_volume_(0.0) {
-}
-
-AudioInputStreamImpl::~AudioInputStreamImpl() {}
-
-void AudioInputStreamImpl::SetAutomaticGainControl(bool enabled) {
-  agc_is_enabled_ = enabled;
-}
-
-bool AudioInputStreamImpl::GetAutomaticGainControl() {
-  return agc_is_enabled_;
-}
-
-void AudioInputStreamImpl::UpdateAgcVolume() {
-  base::AutoLock lock(lock_);
-
-  // We take new volume samples once every second when the AGC is enabled.
-  // To ensure that a new setting has an immediate effect, the new volume
-  // setting is cached here. It will ensure that the next OnData() callback
-  // will contain a new valid volume level. If this approach was not taken,
-  // we could report invalid volume levels to the client for a time period
-  // of up to one second.
-  if (agc_is_enabled_) {
-    GetNormalizedVolume();
-  }
-}
-
-void AudioInputStreamImpl::QueryAgcVolume(double* normalized_volume) {
-  base::AutoLock lock(lock_);
-
-  // Only modify the |volume| output reference if AGC is enabled and if
-  // more than one second has passed since the volume was updated the last time.
-  if (agc_is_enabled_) {
-    base::Time now = base::Time::Now();
-    if ((now - last_volume_update_time_).InMilliseconds() >
-      kMinIntervalBetweenVolumeUpdatesMs) {
-        GetNormalizedVolume();
-        last_volume_update_time_ = now;
-    }
-    *normalized_volume = normalized_volume_;
-  }
-}
-
-void AudioInputStreamImpl::GetNormalizedVolume() {
-  if (max_volume_ == 0.0) {
-    // Cach the maximum volume if this is the first time we ask for it.
-    max_volume_ = GetMaxVolume();
-  }
-
-  if (max_volume_ != 0.0) {
-    // Retrieve the current volume level by asking the audio hardware.
-    // Range is normalized to [0.0,1.0] or [0.0, 1.5] on Linux.
-    normalized_volume_ = GetVolume() / max_volume_;
-  }
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_stream_impl.h b/src/media/audio/audio_input_stream_impl.h
deleted file mode 100644
index 64980a9..0000000
--- a/src/media/audio/audio_input_stream_impl.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_INPUT_STREAM_IMPL_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_STREAM_IMPL_H_
-
-#include "base/compiler_specific.h"
-#include "base/synchronization/lock.h"
-#include "base/time.h"
-#include "media/audio/audio_io.h"
-
-namespace media {
-
-// AudioInputStreamImpl implements platform-independent parts of the
-// AudioInputStream interface. Each platform dependent implementation
-// should derive from this class.
-// TODO(henrika): we can probably break out more parts from our current
-// AudioInputStream implementation and move out to this class.
-class MEDIA_EXPORT AudioInputStreamImpl : public AudioInputStream {
- public:
-  AudioInputStreamImpl();
-  virtual ~AudioInputStreamImpl();
-
-  // Sets the automatic gain control (AGC) to on or off. When AGC is enabled,
-  // the microphone volume is queried periodically and the volume level is
-  // provided in each AudioInputCallback::OnData() callback and fed to the
-  // render-side AGC.
-  virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
-
-  // Gets the current automatic gain control state.
-  virtual bool GetAutomaticGainControl() OVERRIDE;
-
- protected:
-  // Stores a new volume level by asking the audio hardware.
-  // This method only has an effect if AGC is enabled.
-  void UpdateAgcVolume();
-
-  // Gets the latest stored volume level if AGC is enabled and if
-  // more than one second has passed since the volume was updated the last time.
-  void QueryAgcVolume(double* normalized_volume);
-
- private:
-  // Takes a volume sample and stores it in |normalized_volume_|.
-  void GetNormalizedVolume();
-
-  // True when automatic gain control is enabled, false otherwise.
-  // Guarded by |lock_|.
-  bool agc_is_enabled_;
-
-  // Stores the maximum volume which is used for normalization to a volume
-  // range of [0.0, 1.0].
-  double max_volume_;
-
-  // Contains last result of internal call to GetVolume(). We save resources
-  // but not querying the capture volume for each callback. Guarded by |lock_|.
-  // The range is normalized to [0.0, 1.0].
-  double normalized_volume_;
-
-  // Protects |agc_is_enabled_| and |volume_| .
-  base::Lock lock_;
-
-  // Keeps track of the last time the microphone volume level was queried.
-  base::Time last_volume_update_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioInputStreamImpl);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_INPUT_STREAM_IMPL_H_
diff --git a/src/media/audio/audio_input_unittest.cc b/src/media/audio/audio_input_unittest.cc
deleted file mode 100644
index 5a02323..0000000
--- a/src/media/audio/audio_input_unittest.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/environment.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop.h"
-#include "base/threading/platform_thread.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace media {
-
-static const int kSamplingRate = 8000;
-static const int kSamplesPerPacket = kSamplingRate / 20;
-
-// This class allows to find out if the callbacks are occurring as
-// expected and if any error has been reported.
-class TestInputCallback : public AudioInputStream::AudioInputCallback {
- public:
-  explicit TestInputCallback(int max_data_bytes)
-      : callback_count_(0),
-        had_error_(0),
-        max_data_bytes_(max_data_bytes) {
-  }
-  virtual void OnData(AudioInputStream* stream, const uint8* data,
-                      uint32 size, uint32 hardware_delay_bytes, double volume) {
-    ++callback_count_;
-    // Read the first byte to make sure memory is good.
-    if (size) {
-      ASSERT_LE(static_cast<int>(size), max_data_bytes_);
-      int value = data[0];
-      EXPECT_GE(value, 0);
-    }
-  }
-  virtual void OnClose(AudioInputStream* stream) {}
-  virtual void OnError(AudioInputStream* stream, int code) {
-    ++had_error_;
-  }
-  // Returns how many times OnData() has been called.
-  int callback_count() const {
-    return callback_count_;
-  }
-  // Returns how many times the OnError callback was called.
-  int had_error() const {
-    return had_error_;
-  }
-
- private:
-  int callback_count_;
-  int had_error_;
-  int max_data_bytes_;
-};
-
-static bool CanRunAudioTests(AudioManager* audio_man) {
-  bool has_input = audio_man->HasAudioInputDevices();
-
-  if (!has_input)
-    LOG(WARNING) << "No input devices detected";
-
-  return has_input;
-}
-
-static AudioInputStream* CreateTestAudioInputStream(AudioManager* audio_man) {
-  AudioInputStream* ais = audio_man->MakeAudioInputStream(
-      AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
-                      kSamplingRate, 16, kSamplesPerPacket),
-                      AudioManagerBase::kDefaultDeviceId);
-  EXPECT_TRUE(NULL != ais);
-  return ais;
-}
-
-// Test that AudioInputStream rejects out of range parameters.
-TEST(AudioInputTest, SanityOnMakeParams) {
-  scoped_ptr<AudioManager> audio_man(AudioManager::Create());
-  if (!CanRunAudioTests(audio_man.get()))
-    return;
-
-  AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR;
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_7_1, 8000, 16,
-                      kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 1024 * 1024, 16,
-                      kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80,
-                      kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80,
-                      1000 * kSamplesPerPacket),
-                      AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16,
-                      kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, -8000, 16,
-                      kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, -16,
-                      kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
-  EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
-      AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 16, -1024),
-      AudioManagerBase::kDefaultDeviceId));
-}
-
-// Test create and close of an AudioInputStream without recording audio.
-TEST(AudioInputTest, CreateAndClose) {
-  scoped_ptr<AudioManager> audio_man(AudioManager::Create());
-  if (!CanRunAudioTests(audio_man.get()))
-    return;
-  AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
-  ais->Close();
-}
-
-// Test create, open and close of an AudioInputStream without recording audio.
-TEST(AudioInputTest, OpenAndClose) {
-  scoped_ptr<AudioManager> audio_man(AudioManager::Create());
-  if (!CanRunAudioTests(audio_man.get()))
-    return;
-  AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
-  EXPECT_TRUE(ais->Open());
-  ais->Close();
-}
-
-// Test create, open, stop and close of an AudioInputStream without recording.
-TEST(AudioInputTest, OpenStopAndClose) {
-  scoped_ptr<AudioManager> audio_man(AudioManager::Create());
-  if (!CanRunAudioTests(audio_man.get()))
-    return;
-  AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
-  EXPECT_TRUE(ais->Open());
-  ais->Stop();
-  ais->Close();
-}
-
-// Test a normal recording sequence using an AudioInputStream.
-TEST(AudioInputTest, Record) {
-  scoped_ptr<AudioManager> audio_man(AudioManager::Create());
-  if (!CanRunAudioTests(audio_man.get()))
-    return;
-  MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
-  AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
-  EXPECT_TRUE(ais->Open());
-
-  TestInputCallback test_callback(kSamplesPerPacket * 4);
-  ais->Start(&test_callback);
-  // Verify at least 500ms worth of audio was recorded, after giving sufficient
-  // extra time.
-  message_loop.PostDelayedTask(
-      FROM_HERE,
-      MessageLoop::QuitClosure(),
-      base::TimeDelta::FromMilliseconds(690));
-  message_loop.Run();
-  EXPECT_GE(test_callback.callback_count(), 1);
-  EXPECT_FALSE(test_callback.had_error());
-
-  ais->Stop();
-  ais->Close();
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_input_volume_unittest.cc b/src/media/audio/audio_input_volume_unittest.cc
deleted file mode 100644
index 8f754cc..0000000
--- a/src/media/audio/audio_input_volume_unittest.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <cmath>
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-#include "media/audio/audio_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/core_audio_util_win.h"
-#endif
-
-namespace media {
-
-class AudioInputVolumeTest : public ::testing::Test {
- protected:
-  AudioInputVolumeTest()
-      : audio_manager_(AudioManager::Create())
-#if defined(OS_WIN)
-       , com_init_(base::win::ScopedCOMInitializer::kMTA)
-#endif
-  {
-  }
-
-  bool CanRunAudioTests() {
-#if defined(OS_WIN)
-    // TODO(henrika): add support for volume control on Windows XP as well.
-    // For now, we might as well signal false already here to avoid running
-    // these tests on Windows XP.
-    if (!CoreAudioUtil::IsSupported())
-      return false;
-#endif
-    if (!audio_manager_.get())
-      return false;
-
-    return audio_manager_->HasAudioInputDevices();
-  }
-
-  // Helper method which checks if the stream has volume support.
-  bool HasDeviceVolumeControl(AudioInputStream* stream) {
-    if (!stream)
-      return false;
-
-    return (stream->GetMaxVolume() != 0.0);
-  }
-
-  AudioInputStream* CreateAndOpenStream(const std::string& device_id) {
-    AudioParameters::Format format = AudioParameters::AUDIO_PCM_LOW_LATENCY;
-    ChannelLayout channel_layout =
-        media::GetAudioInputHardwareChannelLayout(device_id);
-    int bits_per_sample = 16;
-    int sample_rate =
-        static_cast<int>(media::GetAudioInputHardwareSampleRate(device_id));
-    int samples_per_packet = 0;
-#if defined(OS_MACOSX)
-    samples_per_packet = (sample_rate / 100);
-#elif defined(OS_LINUX) || defined(OS_OPENBSD)
-    samples_per_packet = (sample_rate / 100);
-#elif defined(OS_WIN)
-    if (sample_rate == 44100)
-      samples_per_packet = 448;
-    else
-      samples_per_packet = (sample_rate / 100);
-#else
-#error Unsupported platform
-#endif
-    AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
-        AudioParameters(format, channel_layout, sample_rate, bits_per_sample,
-                        samples_per_packet),
-        device_id);
-    EXPECT_TRUE(NULL != ais);
-
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-    // Some linux devices do not support our settings, we may fail to open
-    // those devices.
-    if (!ais->Open()) {
-      // Default device should always be able to be opened.
-      EXPECT_TRUE(AudioManagerBase::kDefaultDeviceId != device_id);
-      ais->Close();
-      ais = NULL;
-    }
-#elif defined(OS_WIN) || defined(OS_MACOSX)
-    EXPECT_TRUE(ais->Open());
-#endif
-
-    return ais;
-  }
-
-  scoped_ptr<AudioManager> audio_manager_;
-
-#if defined(OS_WIN)
-  base::win::ScopedCOMInitializer com_init_;
-#endif
-};
-
-TEST_F(AudioInputVolumeTest, InputVolumeTest) {
-  if (!CanRunAudioTests())
-    return;
-
-  // Retrieve a list of all available input devices.
-  AudioDeviceNames device_names;
-  audio_manager_->GetAudioInputDeviceNames(&device_names);
-  if (device_names.empty()) {
-    LOG(WARNING) << "Could not find any available input device";
-    return;
-  }
-
-  // Scan all available input devices and repeat the same test for all of them.
-  for (AudioDeviceNames::const_iterator it = device_names.begin();
-       it != device_names.end();
-       ++it) {
-    AudioInputStream* ais = CreateAndOpenStream(it->unique_id);
-    if (!ais) {
-      DLOG(WARNING) << "Failed to open stream for device " << it->unique_id;
-      continue;
-    }
-
-    if (!HasDeviceVolumeControl(ais)) {
-      DLOG(WARNING) << "Device: " << it->unique_id
-                    << ", does not have volume control.";
-      ais->Close();
-      continue;
-    }
-
-    double max_volume = ais->GetMaxVolume();
-    EXPECT_GT(max_volume, 0.0);
-
-    // Store the current input-device volume level.
-    double original_volume = ais->GetVolume();
-    EXPECT_GE(original_volume, 0.0);
-#if defined(OS_WIN) || defined(OS_MACOSX)
-    // Note that |original_volume| can be higher than |max_volume| on Linux.
-    EXPECT_LE(original_volume, max_volume);
-#endif
-
-    // Set the volume to the maxiumum level..
-    ais->SetVolume(max_volume);
-    double current_volume = ais->GetVolume();
-    EXPECT_EQ(max_volume, current_volume);
-
-    // Set the volume to the mininum level (=0).
-    double new_volume = 0.0;
-    ais->SetVolume(new_volume);
-    current_volume = ais->GetVolume();
-    EXPECT_EQ(new_volume, current_volume);
-
-    // Set the volume to the mid level (50% of max).
-    // Verify that the absolute error is small enough.
-    new_volume = max_volume / 2;
-    ais->SetVolume(new_volume);
-    current_volume = ais->GetVolume();
-    EXPECT_LT(current_volume, max_volume);
-    EXPECT_GT(current_volume, 0);
-    EXPECT_NEAR(current_volume, new_volume, 0.25 * max_volume);
-
-    // Restores the volume to the original value.
-    ais->SetVolume(original_volume);
-    current_volume = ais->GetVolume();
-    EXPECT_EQ(original_volume, current_volume);
-
-    ais->Close();
-  }
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_io.h b/src/media/audio/audio_io.h
deleted file mode 100644
index e7b9a36..0000000
--- a/src/media/audio/audio_io.h
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_IO_H_
-#define MEDIA_AUDIO_AUDIO_IO_H_
-
-#include "base/basictypes.h"
-#include "media/audio/audio_buffers_state.h"
-#include "media/base/audio_bus.h"
-
-// Low-level audio output support. To make sound there are 3 objects involved:
-// - AudioSource : produces audio samples on a pull model. Implements
-//   the AudioSourceCallback interface.
-// - AudioOutputStream : uses the AudioSource to render audio on a given
-//   channel, format and sample frequency configuration. Data from the
-//   AudioSource is delivered in a 'pull' model.
-// - AudioManager : factory for the AudioOutputStream objects, manager
-//   of the hardware resources and mixer control.
-//
-// The number and configuration of AudioOutputStream does not need to match the
-// physically available hardware resources. For example you can have:
-//
-//  MonoPCMSource1 --> MonoPCMStream1 --> |       | --> audio left channel
-//  StereoPCMSource -> StereoPCMStream -> | mixer |
-//  MonoPCMSource2 --> MonoPCMStream2 --> |       | --> audio right channel
-//
-// This facility's objective is mix and render audio with low overhead using
-// the OS basic audio support, abstracting as much as possible the
-// idiosyncrasies of each platform. Non-goals:
-// - Positional, 3d audio
-// - Dependence on non-default libraries such as DirectX 9, 10, XAudio
-// - Digital signal processing or effects
-// - Extra features if a specific hardware is installed (EAX, X-fi)
-//
-// The primary client of this facility is audio coming from several tabs.
-// Specifically for this case we avoid supporting complex formats such as MP3
-// or WMA. Complex format decoding should be done by the renderers.
-
-
-// Models an audio stream that gets rendered to the audio hardware output.
-// Because we support more audio streams than physically available channels
-// a given AudioOutputStream might or might not talk directly to hardware.
-// An audio stream allocates several buffers for audio data and calls
-// AudioSourceCallback::OnMoreData() periodically to fill these buffers,
-// as the data is written to the audio device. Size of each packet is determined
-// by |samples_per_packet| specified in AudioParameters  when the stream is
-// created.
-
-namespace media {
-
-class MEDIA_EXPORT AudioOutputStream {
- public:
-  // Audio sources must implement AudioSourceCallback. This interface will be
-  // called in a random thread which very likely is a high priority thread. Do
-  // not rely on using this thread TLS or make calls that alter the thread
-  // itself such as creating Windows or initializing COM.
-  class MEDIA_EXPORT AudioSourceCallback {
-   public:
-    // Provide more data by fully filling |dest|.  The source will return
-    // the number of frames it filled.  |buffers_state| contains current state
-    // of the buffers, and can be used by the source to calculate delay.
-    virtual int OnMoreData(AudioBus* dest,
-                           AudioBuffersState buffers_state) = 0;
-
-    virtual int OnMoreIOData(AudioBus* source,
-                             AudioBus* dest,
-                             AudioBuffersState buffers_state) = 0;
-
-    // There was an error while playing a buffer. Audio source cannot be
-    // destroyed yet. No direct action needed by the AudioStream, but it is
-    // a good place to stop accumulating sound data since is is likely that
-    // playback will not continue. |code| is an error code that is platform
-    // specific.
-    virtual void OnError(AudioOutputStream* stream, int code) = 0;
-
-    // Deprecated.  DO NOT USE.  Waits until data becomes available.  Used only
-    // by Windows' WaveOut clients which may be extremely laggy.  Will yield the
-    // current thread until the renderer client has written its audio data or
-    // 1.5 seconds have elapsed.
-    virtual void WaitTillDataReady() {}
-
-   protected:
-    virtual ~AudioSourceCallback() {}
-  };
-
-  virtual ~AudioOutputStream() {}
-
-  // Open the stream. false is returned if the stream cannot be opened.  Open()
-  // must always be followed by a call to Close() even if Open() fails.
-  virtual bool Open() = 0;
-
-  // Starts playing audio and generating AudioSourceCallback::OnMoreData().
-  // Since implementor of AudioOutputStream may have internal buffers, right
-  // after calling this method initial buffers are fetched.
-  //
-  // The output stream does not take ownership of this callback.
-  virtual void Start(AudioSourceCallback* callback) = 0;
-
-  // Stops playing audio. Effect might not be instantaneous as the hardware
-  // might have locked audio data that is processing.
-  virtual void Stop() = 0;
-
-  // Sets the relative volume, with range [0.0, 1.0] inclusive.
-  virtual void SetVolume(double volume) = 0;
-
-  // Gets the relative volume, with range [0.0, 1.0] inclusive.
-  virtual void GetVolume(double* volume) = 0;
-
-  // Close the stream. This also generates AudioSourceCallback::OnClose().
-  // After calling this method, the object should not be used anymore.
-  virtual void Close() = 0;
-};
-
-// Models an audio sink receiving recorded audio from the audio driver.
-class MEDIA_EXPORT AudioInputStream {
- public:
-  class MEDIA_EXPORT AudioInputCallback {
-   public:
-    // Called by the audio recorder when a full packet of audio data is
-    // available. This is called from a special audio thread and the
-    // implementation should return as soon as possible.
-    virtual void OnData(AudioInputStream* stream, const uint8* src,
-                        uint32 size, uint32 hardware_delay_bytes,
-                        double volume) = 0;
-
-    // The stream is done with this callback, the last call received by this
-    // audio sink.
-    virtual void OnClose(AudioInputStream* stream) = 0;
-
-    // There was an error while recording audio. The audio sink cannot be
-    // destroyed yet. No direct action needed by the AudioInputStream, but it
-    // is a good place to stop accumulating sound data since is is likely that
-    // recording will not continue. |code| is an error code that is platform
-    // specific.
-    virtual void OnError(AudioInputStream* stream, int code) = 0;
-
-   protected:
-    virtual ~AudioInputCallback() {}
-  };
-
-  virtual ~AudioInputStream() {}
-
-  // Open the stream and prepares it for recording. Call Start() to actually
-  // begin recording.
-  virtual bool Open() = 0;
-
-  // Starts recording audio and generating AudioInputCallback::OnData().
-  // The input stream does not take ownership of this callback.
-  virtual void Start(AudioInputCallback* callback) = 0;
-
-  // Stops recording audio. Effect might not be instantaneous as there could be
-  // pending audio callbacks in the queue which will be issued first before
-  // recording stops.
-  virtual void Stop() = 0;
-
-  // Close the stream. This also generates AudioInputCallback::OnClose(). This
-  // should be the last call made on this object.
-  virtual void Close() = 0;
-
-  // Returns the maximum microphone analog volume or 0.0 if device does not
-  // have volume control.
-  virtual double GetMaxVolume() = 0;
-
-  // Sets the microphone analog volume, with range [0, max_volume] inclusive.
-  virtual void SetVolume(double volume) = 0;
-
-  // Returns the microphone analog volume, with range [0, max_volume] inclusive.
-  virtual double GetVolume() = 0;
-
-  // Sets the Automatic Gain Control (AGC) state.
-  virtual void SetAutomaticGainControl(bool enabled) = 0;
-
-  // Returns the Automatic Gain Control (AGC) state.
-  virtual bool GetAutomaticGainControl() = 0;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_IO_H_
diff --git a/src/media/audio/audio_low_latency_input_output_unittest.cc b/src/media/audio/audio_low_latency_input_output_unittest.cc
deleted file mode 100644
index 463321a..0000000
--- a/src/media/audio/audio_low_latency_input_output_unittest.cc
+++ /dev/null
@@ -1,464 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/basictypes.h"
-#include "base/environment.h"
-#include "base/file_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop.h"
-#include "base/path_service.h"
-#include "base/synchronization/lock.h"
-#include "base/test/test_timeouts.h"
-#include "base/time.h"
-#include "build/build_config.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-#include "media/audio/audio_util.h"
-#include "media/base/seekable_buffer.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-#include "media/audio/linux/audio_manager_linux.h"
-#elif defined(OS_MACOSX)
-#include "media/audio/mac/audio_manager_mac.h"
-#elif defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/audio_manager_win.h"
-#include "media/audio/win/core_audio_util_win.h"
-#elif defined(OS_ANDROID)
-#include "media/audio/android/audio_manager_android.h"
-#endif
-
-namespace media {
-
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-typedef AudioManagerLinux AudioManagerAnyPlatform;
-#elif defined(OS_MACOSX)
-typedef AudioManagerMac AudioManagerAnyPlatform;
-#elif defined(OS_WIN)
-typedef AudioManagerWin AudioManagerAnyPlatform;
-#elif defined(OS_ANDROID)
-typedef AudioManagerAndroid AudioManagerAnyPlatform;
-#endif
-
-// Limits the number of delay measurements we can store in an array and
-// then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
-static const size_t kMaxDelayMeasurements = 1000;
-
-// Name of the output text file. The output file will be stored in the
-// directory containing media_unittests.exe.
-// Example: \src\build\Debug\audio_delay_values_ms.txt.
-// See comments for the WASAPIAudioInputOutputFullDuplex test for more details
-// about the file format.
-static const char* kDelayValuesFileName = "audio_delay_values_ms.txt";
-
-// Contains delay values which are reported during the full-duplex test.
-// Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
-struct AudioDelayState {
-  AudioDelayState()
-      : delta_time_ms(0),
-        buffer_delay_ms(0),
-        input_delay_ms(0),
-        output_delay_ms(0) {
-  }
-
-  // Time in milliseconds since last delay report. Typical value is ~10 [ms].
-  int delta_time_ms;
-
-  // Size of internal sync buffer. Typical value is ~0 [ms].
-  int buffer_delay_ms;
-
-  // Reported capture/input delay. Typical value is ~10 [ms].
-  int input_delay_ms;
-
-  // Reported render/output delay. Typical value is ~40 [ms].
-  int output_delay_ms;
-};
-
-// This class mocks the platform specific audio manager and overrides
-// the GetMessageLoop() method to ensure that we can run our tests on
-// the main thread instead of the audio thread.
-class MockAudioManager : public AudioManagerAnyPlatform {
- public:
-  MockAudioManager() {}
-  virtual ~MockAudioManager() {}
-
-  virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
-    return MessageLoop::current()->message_loop_proxy();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
-};
-
-// Test fixture class.
-class AudioLowLatencyInputOutputTest : public testing::Test {
- protected:
-  AudioLowLatencyInputOutputTest() {}
-
-  virtual ~AudioLowLatencyInputOutputTest() {}
-
-  AudioManager* audio_manager() { return &mock_audio_manager_; }
-  MessageLoopForUI* message_loop() { return &message_loop_; }
-
-  // Convenience method which ensures that we are not running on the build
-  // bots and that at least one valid input and output device can be found.
-  bool CanRunAudioTests() {
-    bool input = audio_manager()->HasAudioInputDevices();
-    bool output = audio_manager()->HasAudioOutputDevices();
-    LOG_IF(WARNING, !input) << "No input device detected.";
-    LOG_IF(WARNING, !output) << "No output device detected.";
-    return input && output;
-  }
-
- private:
-  MessageLoopForUI message_loop_;
-  MockAudioManager mock_audio_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
-};
-
-// This audio source/sink implementation should be used for manual tests
-// only since delay measurements are stored on an output text file.
-// All incoming/recorded audio packets are stored in an intermediate media
-// buffer which the renderer reads from when it needs audio for playout.
-// The total effect is that recorded audio is played out in loop back using
-// a sync buffer as temporary storage.
-class FullDuplexAudioSinkSource
-    : public AudioInputStream::AudioInputCallback,
-      public AudioOutputStream::AudioSourceCallback {
- public:
-  FullDuplexAudioSinkSource(int sample_rate,
-                            int samples_per_packet,
-                            int channels)
-    : sample_rate_(sample_rate),
-      samples_per_packet_(samples_per_packet),
-      channels_(channels),
-      input_elements_to_write_(0),
-      output_elements_to_write_(0),
-      previous_write_time_(base::Time::Now()) {
-    // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
-    frame_size_ = (16 / 8) * channels_;
-
-    // Start with the smallest possible buffer size. It will be increased
-    // dynamically during the test if required.
-    buffer_.reset(
-        new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
-
-    frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
-    delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
-  }
-
-  virtual ~FullDuplexAudioSinkSource() {
-    // Get complete file path to output file in the directory containing
-    // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
-    FilePath file_name;
-    EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
-    file_name = file_name.AppendASCII(kDelayValuesFileName);
-
-    FILE* text_file = file_util::OpenFile(file_name, "wt");
-    DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
-    LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
-
-    // Write the array which contains time-stamps, buffer size and
-    // audio delays values to a text file.
-    size_t elements_written = 0;
-    while (elements_written <
-        std::min(input_elements_to_write_, output_elements_to_write_)) {
-      const AudioDelayState state = delay_states_[elements_written];
-      fprintf(text_file, "%d %d %d %d\n",
-              state.delta_time_ms,
-              state.buffer_delay_ms,
-              state.input_delay_ms,
-              state.output_delay_ms);
-      ++elements_written;
-    }
-
-    file_util::CloseFile(text_file);
-  }
-
-  // AudioInputStream::AudioInputCallback.
-  virtual void OnData(AudioInputStream* stream,
-                      const uint8* src, uint32 size,
-                      uint32 hardware_delay_bytes,
-                      double volume) OVERRIDE {
-    base::AutoLock lock(lock_);
-
-    // Update three components in the AudioDelayState for this recorded
-    // audio packet.
-    base::Time now_time = base::Time::Now();
-    int diff = (now_time - previous_write_time_).InMilliseconds();
-    previous_write_time_ = now_time;
-    if (input_elements_to_write_ < kMaxDelayMeasurements) {
-      delay_states_[input_elements_to_write_].delta_time_ms = diff;
-      delay_states_[input_elements_to_write_].buffer_delay_ms =
-          BytesToMilliseconds(buffer_->forward_bytes());
-      delay_states_[input_elements_to_write_].input_delay_ms =
-          BytesToMilliseconds(hardware_delay_bytes);
-      ++input_elements_to_write_;
-    }
-
-    // Store the captured audio packet in a seekable media buffer.
-    if (!buffer_->Append(src, size)) {
-      // An attempt to write outside the buffer limits has been made.
-      // Double the buffer capacity to ensure that we have a buffer large
-      // enough to handle the current sample test scenario.
-      buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
-      buffer_->Clear();
-    }
-  }
-
-  virtual void OnClose(AudioInputStream* stream) OVERRIDE {}
-  virtual void OnError(AudioInputStream* stream, int code) OVERRIDE {}
-
-  // AudioOutputStream::AudioSourceCallback.
-  virtual int OnMoreData(AudioBus* audio_bus,
-                         AudioBuffersState buffers_state) OVERRIDE {
-    base::AutoLock lock(lock_);
-
-    // Update one component in the AudioDelayState for the packet
-    // which is about to be played out.
-    if (output_elements_to_write_ < kMaxDelayMeasurements) {
-      int output_delay_bytes = buffers_state.hardware_delay_bytes;
-#if defined(OS_WIN)
-      // Special fix for Windows in combination with Wave where the
-      // pending bytes field of the audio buffer state is used to
-      // report the delay.
-      if (!CoreAudioUtil::IsSupported()) {
-        output_delay_bytes = buffers_state.pending_bytes;
-      }
-#endif
-      delay_states_[output_elements_to_write_].output_delay_ms =
-          BytesToMilliseconds(output_delay_bytes);
-      ++output_elements_to_write_;
-    }
-
-    int size;
-    const uint8* source;
-    // Read the data from the seekable media buffer which contains
-    // captured data at the same size and sample rate as the output side.
-    if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
-      EXPECT_EQ(channels_, audio_bus->channels());
-      size = std::min(audio_bus->frames() * frame_size_, size);
-      EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U);
-      audio_bus->FromInterleaved(
-          source, size / frame_size_, frame_size_ / channels_);
-      buffer_->Seek(size);
-      return size / frame_size_;
-    }
-
-    return 0;
-  }
-
-  virtual int OnMoreIOData(AudioBus* source,
-                           AudioBus* dest,
-                           AudioBuffersState buffers_state) OVERRIDE {
-    NOTREACHED();
-    return 0;
-  }
-
-  virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE {}
-  virtual void WaitTillDataReady() OVERRIDE {}
-
- protected:
-  // Converts from bytes to milliseconds taking the sample rate and size
-  // of an audio frame into account.
-  int BytesToMilliseconds(uint32 delay_bytes) const {
-    return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
-  }
-
- private:
-  base::Lock lock_;
-  scoped_ptr<media::SeekableBuffer> buffer_;
-  int sample_rate_;
-  int samples_per_packet_;
-  int channels_;
-  int frame_size_;
-  double frames_to_ms_;
-  scoped_array<AudioDelayState> delay_states_;
-  size_t input_elements_to_write_;
-  size_t output_elements_to_write_;
-  base::Time previous_write_time_;
-};
-
-class AudioInputStreamTraits {
- public:
-  typedef AudioInputStream StreamType;
-
-  static int HardwareSampleRate() {
-    return static_cast<int>(media::GetAudioInputHardwareSampleRate(
-        AudioManagerBase::kDefaultDeviceId));
-  }
-
-  // TODO(henrika): add support for GetAudioInputHardwareBufferSize in media.
-  static int HardwareBufferSize() {
-    return static_cast<int>(media::GetAudioHardwareBufferSize());
-  }
-
-  static StreamType* CreateStream(AudioManager* audio_manager,
-      const AudioParameters& params) {
-    return audio_manager->MakeAudioInputStream(params,
-      AudioManagerBase::kDefaultDeviceId);
-  }
-};
-
-class AudioOutputStreamTraits {
- public:
-  typedef AudioOutputStream StreamType;
-
-  static int HardwareSampleRate() {
-    return static_cast<int>(media::GetAudioHardwareSampleRate());
-  }
-
-  static int HardwareBufferSize() {
-    return static_cast<int>(media::GetAudioHardwareBufferSize());
-  }
-
-  static StreamType* CreateStream(AudioManager* audio_manager,
-      const AudioParameters& params) {
-    return audio_manager->MakeAudioOutputStream(params);
-  }
-};
-
-// Traits template holding a trait of StreamType. It encapsulates
-// AudioInputStream and AudioOutputStream stream types.
-template <typename StreamTraits>
-class StreamWrapper {
- public:
-  typedef typename StreamTraits::StreamType StreamType;
-
-  explicit StreamWrapper(AudioManager* audio_manager)
-      :
-#if defined(OS_WIN)
-        com_init_(base::win::ScopedCOMInitializer::kMTA),
-#endif
-        audio_manager_(audio_manager),
-        format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
-#if defined(OS_ANDROID)
-        channel_layout_(CHANNEL_LAYOUT_MONO),
-#else
-        channel_layout_(CHANNEL_LAYOUT_STEREO),
-#endif
-        bits_per_sample_(16) {
-    // Use the preferred sample rate.
-    sample_rate_ = StreamTraits::HardwareSampleRate();
-
-    // Use the preferred buffer size. Note that the input side uses the same
-    // size as the output side in this implementation.
-    samples_per_packet_ = StreamTraits::HardwareBufferSize();
-  }
-
-  virtual ~StreamWrapper() {}
-
-  // Creates an Audio[Input|Output]Stream stream object using default
-  // parameters.
-  StreamType* Create() {
-    return CreateStream();
-  }
-
-  int channels() const {
-    return ChannelLayoutToChannelCount(channel_layout_);
-  }
-  int bits_per_sample() const { return bits_per_sample_; }
-  int sample_rate() const { return sample_rate_; }
-  int samples_per_packet() const { return samples_per_packet_; }
-
- private:
-  StreamType* CreateStream() {
-    StreamType* stream = StreamTraits::CreateStream(audio_manager_,
-        AudioParameters(format_, channel_layout_, sample_rate_,
-            bits_per_sample_, samples_per_packet_));
-    EXPECT_TRUE(stream);
-    return stream;
-  }
-
-#if defined(OS_WIN)
-  base::win::ScopedCOMInitializer com_init_;
-#endif
-
-  AudioManager* audio_manager_;
-  AudioParameters::Format format_;
-  ChannelLayout channel_layout_;
-  int bits_per_sample_;
-  int sample_rate_;
-  int samples_per_packet_;
-};
-
-typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
-typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
-
-// This test is intended for manual tests and should only be enabled
-// when it is required to make a real-time test of audio in full duplex and
-// at the same time create a text file which contains measured delay values.
-// The file can later be analyzed off line using e.g. MATLAB.
-// MATLAB example:
-//   D=load('audio_delay_values_ms.txt');
-//   x=cumsum(D(:,1));
-//   plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
-//   axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
-//   legend('buffer delay','input delay','output delay','total delay');
-//   xlabel('time [msec]')
-//   ylabel('delay [msec]')
-//   title('Full-duplex audio delay measurement');
-TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
-  if (!CanRunAudioTests())
-    return;
-
-  AudioInputStreamWrapper aisw(audio_manager());
-  AudioInputStream* ais = aisw.Create();
-  EXPECT_TRUE(ais);
-
-  AudioOutputStreamWrapper aosw(audio_manager());
-  AudioOutputStream* aos = aosw.Create();
-  EXPECT_TRUE(aos);
-
-  // This test only supports identical parameters in both directions.
-  // TODO(henrika): it is possible to cut delay here by using different
-  // buffer sizes for input and output.
-  if (aisw.sample_rate() != aosw.sample_rate() ||
-      aisw.samples_per_packet() != aosw.samples_per_packet() ||
-      aisw.channels()!= aosw.channels() ||
-      aisw.bits_per_sample() != aosw.bits_per_sample()) {
-    LOG(ERROR) << "This test requires symmetric input and output parameters. "
-        "Ensure that sample rate and number of channels are identical in "
-        "both directions";
-    aos->Close();
-    ais->Close();
-    return;
-  }
-
-  EXPECT_TRUE(ais->Open());
-  EXPECT_TRUE(aos->Open());
-
-  FullDuplexAudioSinkSource full_duplex(
-      aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
-
-  LOG(INFO) << ">> You should now be able to hear yourself in loopback...";
-  DLOG(INFO) << "   sample_rate       : " << aisw.sample_rate();
-  DLOG(INFO) << "   samples_per_packet: " << aisw.samples_per_packet();
-  DLOG(INFO) << "   channels          : " << aisw.channels();
-
-  ais->Start(&full_duplex);
-  aos->Start(&full_duplex);
-
-  // Wait for approximately 10 seconds. The user shall hear his own voice
-  // in loop back during this time. At the same time, delay recordings are
-  // performed and stored in the output text file.
-  message_loop()->PostDelayedTask(FROM_HERE,
-      MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
-  message_loop()->Run();
-
-  aos->Stop();
-  ais->Stop();
-
-  // All Close() operations that run on the mocked audio thread,
-  // should be synchronous and not post additional close tasks to
-  // mocked the audio thread. Hence, there is no need to call
-  // message_loop()->RunUntilIdle() after the Close() methods.
-  aos->Close();
-  ais->Close();
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_manager.cc b/src/media/audio/audio_manager.cc
deleted file mode 100644
index 9372d08..0000000
--- a/src/media/audio/audio_manager.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_manager.h"
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
-
-namespace media {
-
-// Forward declaration of the platform specific AudioManager factory function.
-AudioManager* CreateAudioManager();
-
-AudioManager::AudioManager() {
-}
-
-AudioManager::~AudioManager() {
-}
-
-// static
-AudioManager* AudioManager::Create() {
-  return CreateAudioManager();
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_manager.h b/src/media/audio/audio_manager.h
deleted file mode 100644
index ca4c468..0000000
--- a/src/media/audio/audio_manager.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_MANAGER_H_
-#define MEDIA_AUDIO_AUDIO_MANAGER_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/string16.h"
-#include "media/audio/audio_device_name.h"
-#include "media/audio/audio_parameters.h"
-
-class MessageLoop;
-
-namespace base {
-class MessageLoopProxy;
-}
-
-namespace media {
-
-class AudioInputStream;
-class AudioOutputStream;
-
-// Manages all audio resources. In particular it owns the AudioOutputStream
-// objects. Provides some convenience functions that avoid the need to provide
-// iterators over the existing streams.
-class MEDIA_EXPORT AudioManager {
- public:
-  virtual ~AudioManager();
-
-  // Use to construct the audio manager.
-  // NOTE: There should only be one instance.
-  static AudioManager* Create();
-
-  // Returns true if the OS reports existence of audio devices. This does not
-  // guarantee that the existing devices support all formats and sample rates.
-  virtual bool HasAudioOutputDevices() = 0;
-
-  // Returns true if the OS reports existence of audio recording devices. This
-  // does not guarantee that the existing devices support all formats and
-  // sample rates.
-  virtual bool HasAudioInputDevices() = 0;
-
-  // Returns a human readable string for the model/make of the active audio
-  // input device for this computer.
-  virtual string16 GetAudioInputDeviceModel() = 0;
-
-  // Returns true if the platform specific audio input settings UI is known
-  // and can be shown.
-  virtual bool CanShowAudioInputSettings() = 0;
-
-  // Opens the platform default audio input settings UI.
-  // Note: This could invoke an external application/preferences pane, so
-  // ideally must not be called from the UI thread or other time sensitive
-  // threads to avoid blocking the rest of the application.
-  virtual void ShowAudioInputSettings() = 0;
-
-  // Appends a list of available input devices. It is not guaranteed that
-  // all the devices in the list support all formats and sample rates for
-  // recording.
-  virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) = 0;
-
-  // Factory for all the supported stream formats. |params| defines parameters
-  // of the audio stream to be created.
-  //
-  // |params.sample_per_packet| is the requested buffer allocation which the
-  // audio source thinks it can usually fill without blocking. Internally two
-  // or three buffers are created, one will be locked for playback and one will
-  // be ready to be filled in the call to AudioSourceCallback::OnMoreData().
-  //
-  // Returns NULL if the combination of the parameters is not supported, or if
-  // we have reached some other platform specific limit.
-  //
-  // |params.format| can be set to AUDIO_PCM_LOW_LATENCY and that has two
-  // effects:
-  // 1- Instead of triple buffered the audio will be double buffered.
-  // 2- A low latency driver or alternative audio subsystem will be used when
-  //    available.
-  //
-  // Do not free the returned AudioOutputStream. It is owned by AudioManager.
-  virtual AudioOutputStream* MakeAudioOutputStream(
-      const AudioParameters& params) = 0;
-
-  // Creates new audio output proxy. A proxy implements
-  // AudioOutputStream interface, but unlike regular output stream
-  // created with MakeAudioOutputStream() it opens device only when a
-  // sound is actually playing.
-  virtual AudioOutputStream* MakeAudioOutputStreamProxy(
-      const AudioParameters& params) = 0;
-
-  // Factory to create audio recording streams.
-  // |channels| can be 1 or 2.
-  // |sample_rate| is in hertz and can be any value supported by the platform.
-  // |bits_per_sample| can be any value supported by the platform.
-  // |samples_per_packet| is in hertz as well and can be 0 to |sample_rate|,
-  // with 0 suggesting that the implementation use a default value for that
-  // platform.
-  // Returns NULL if the combination of the parameters is not supported, or if
-  // we have reached some other platform specific limit.
-  //
-  // Do not free the returned AudioInputStream. It is owned by AudioManager.
-  // When you are done with it, call |Stop()| and |Close()| to release it.
-  virtual AudioInputStream* MakeAudioInputStream(
-      const AudioParameters& params, const std::string& device_id) = 0;
-
-  // Used to determine if something else is currently making use of audio input.
-  virtual bool IsRecordingInProcess() = 0;
-
-  // Returns message loop used for audio IO.
-  virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() = 0;
-
-  // Allows clients to listen for device state changes; e.g. preferred sample
-  // rate or channel layout changes.  The typical response to receiving this
-  // callback is to recreate the stream.
-  class AudioDeviceListener {
-   public:
-    virtual void OnDeviceChange() = 0;
-  };
-
-  virtual void AddOutputDeviceChangeListener(AudioDeviceListener* listener) = 0;
-  virtual void RemoveOutputDeviceChangeListener(
-      AudioDeviceListener* listener) = 0;
-
- protected:
-  AudioManager();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AudioManager);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_MANAGER_H_
diff --git a/src/media/audio/audio_manager_base.cc b/src/media/audio/audio_manager_base.cc
deleted file mode 100644
index 6333039..0000000
--- a/src/media/audio/audio_manager_base.cc
+++ /dev/null
@@ -1,397 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_manager_base.h"
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/message_loop_proxy.h"
-#include "base/threading/thread.h"
-#include "media/audio/audio_output_dispatcher_impl.h"
-#include "media/audio/audio_output_proxy.h"
-#include "media/audio/audio_output_resampler.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/fake_audio_input_stream.h"
-#include "media/audio/fake_audio_output_stream.h"
-#include "media/audio/virtual_audio_input_stream.h"
-#include "media/audio/virtual_audio_output_stream.h"
-#include "media/base/media_switches.h"
-
-// TODO(dalecurtis): Temporarily disabled while switching pipeline to use float,
-// http://crbug.com/114700
-#if defined(ENABLE_AUDIO_MIXER)
-#include "media/audio/audio_output_mixer.h"
-#endif
-
-namespace media {
-
-static const int kStreamCloseDelaySeconds = 5;
-
-// Default maximum number of output streams that can be open simultaneously
-// for all platforms.
-static const int kDefaultMaxOutputStreams = 16;
-
-// Default maximum number of input streams that can be open simultaneously
-// for all platforms.
-static const int kDefaultMaxInputStreams = 16;
-
-static const int kMaxInputChannels = 2;
-
-const char AudioManagerBase::kDefaultDeviceName[] = "Default";
-const char AudioManagerBase::kDefaultDeviceId[] = "default";
-
-AudioManagerBase::AudioManagerBase()
-    : num_active_input_streams_(0),
-      max_num_output_streams_(kDefaultMaxOutputStreams),
-      max_num_input_streams_(kDefaultMaxInputStreams),
-      num_output_streams_(0),
-      num_input_streams_(0),
-      audio_thread_(new base::Thread("AudioThread")),
-      virtual_audio_input_stream_(NULL) {
-#if defined(OS_WIN)
-  audio_thread_->init_com_with_mta(true);
-#endif
-  CHECK(audio_thread_->Start());
-  message_loop_ = audio_thread_->message_loop_proxy();
-}
-
-AudioManagerBase::~AudioManagerBase() {
-  // The platform specific AudioManager implementation must have already
-  // stopped the audio thread. Otherwise, we may destroy audio streams before
-  // stopping the thread, resulting an unexpected behavior.
-  // This way we make sure activities of the audio streams are all stopped
-  // before we destroy them.
-  CHECK(!audio_thread_.get());
-  // All the output streams should have been deleted.
-  DCHECK_EQ(0, num_output_streams_);
-  // All the input streams should have been deleted.
-  DCHECK_EQ(0, num_input_streams_);
-}
-
-string16 AudioManagerBase::GetAudioInputDeviceModel() {
-  return string16();
-}
-
-scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() {
-  return message_loop_;
-}
-
-AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
-    const AudioParameters& params) {
-  if (!params.IsValid()) {
-    DLOG(ERROR) << "Audio parameters are invalid";
-    return NULL;
-  }
-
-  // Limit the number of audio streams opened. This is to prevent using
-  // excessive resources for a large number of audio streams. More
-  // importantly it prevents instability on certain systems.
-  // See bug: http://crbug.com/30242.
-  if (num_output_streams_ >= max_num_output_streams_) {
-    DLOG(ERROR) << "Number of opened output audio streams "
-                << num_output_streams_
-                << " exceed the max allowed number "
-                << max_num_output_streams_;
-    return NULL;
-  }
-
-  // If there are no audio output devices we should use a FakeAudioOutputStream
-  // to ensure video playback continues to work.
-  bool audio_output_disabled =
-      params.format() == AudioParameters::AUDIO_FAKE ||
-      !HasAudioOutputDevices();
-
-  AudioOutputStream* stream = NULL;
-  if (virtual_audio_input_stream_) {
-#if defined(OS_IOS)
-    // We do not currently support iOS. It does not link.
-    NOTIMPLEMENTED();
-    return NULL;
-#else
-    stream = VirtualAudioOutputStream::MakeStream(this, params, message_loop_,
-        virtual_audio_input_stream_);
-#endif
-  } else if (audio_output_disabled) {
-    stream = FakeAudioOutputStream::MakeFakeStream(this, params);
-  } else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) {
-    stream = MakeLinearOutputStream(params);
-  } else if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
-    stream = MakeLowLatencyOutputStream(params);
-  }
-
-  if (stream)
-    ++num_output_streams_;
-
-  return stream;
-}
-
-AudioInputStream* AudioManagerBase::MakeAudioInputStream(
-    const AudioParameters& params, const std::string& device_id) {
-  if (!params.IsValid() || (params.channels() > kMaxInputChannels) ||
-      device_id.empty()) {
-    DLOG(ERROR) << "Audio parameters are invalid for device " << device_id;
-    return NULL;
-  }
-
-  if (num_input_streams_ >= max_num_input_streams_) {
-    DLOG(ERROR) << "Number of opened input audio streams "
-                << num_input_streams_
-                << " exceed the max allowed number " << max_num_input_streams_;
-    return NULL;
-  }
-
-  AudioInputStream* stream = NULL;
-  if (params.format() == AudioParameters::AUDIO_VIRTUAL) {
-#if defined(OS_IOS)
-    // We do not currently support iOS.
-    NOTIMPLEMENTED();
-    return NULL;
-#else
-    // TODO(justinlin): Currently, audio mirroring will only work for the first
-    // request. Subsequent requests will not get audio.
-    if (!virtual_audio_input_stream_) {
-      virtual_audio_input_stream_ =
-          VirtualAudioInputStream::MakeStream(this, params, message_loop_);
-      stream = virtual_audio_input_stream_;
-      DVLOG(1) << "Virtual audio input stream created.";
-
-      // Make all current output streams recreate themselves as
-      // VirtualAudioOutputStreams that will attach to the above
-      // VirtualAudioInputStream.
-      message_loop_->PostTask(FROM_HERE, base::Bind(
-          &AudioManagerBase::NotifyAllOutputDeviceChangeListeners,
-          base::Unretained(this)));
-    } else {
-      stream = NULL;
-    }
-#endif
-  } else if (params.format() == AudioParameters::AUDIO_FAKE) {
-    stream = FakeAudioInputStream::MakeFakeStream(this, params);
-  } else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) {
-    stream = MakeLinearInputStream(params, device_id);
-  } else if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
-    stream = MakeLowLatencyInputStream(params, device_id);
-  }
-
-  if (stream)
-    ++num_input_streams_;
-
-  return stream;
-}
-
-AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy(
-    const AudioParameters& params) {
-#if defined(OS_IOS)
-  // IOS implements audio input only.
-  NOTIMPLEMENTED();
-  return NULL;
-#else
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  bool use_audio_output_resampler =
-      !CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableAudioOutputResampler) &&
-      params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY;
-
-  // If we're not using AudioOutputResampler our output parameters are the same
-  // as our input parameters.
-  AudioParameters output_params = params;
-  if (use_audio_output_resampler) {
-    output_params = GetPreferredLowLatencyOutputStreamParameters(params);
-
-    // Ensure we only pass on valid output parameters.
-    if (!output_params.IsValid()) {
-      // We've received invalid audio output parameters, so switch to a mock
-      // output device based on the input parameters.  This may happen if the OS
-      // provided us junk values for the hardware configuration.
-      LOG(ERROR) << "Invalid audio output parameters received; using fake "
-                 << "audio path. Channels: " << output_params.channels() << ", "
-                 << "Sample Rate: " << output_params.sample_rate() << ", "
-                 << "Bits Per Sample: " << output_params.bits_per_sample()
-                 << ", Frames Per Buffer: "
-                 << output_params.frames_per_buffer();
-
-      // Tell the AudioManager to create a fake output device.
-      output_params = AudioParameters(
-          AudioParameters::AUDIO_FAKE, params.channel_layout(),
-          params.sample_rate(), params.bits_per_sample(),
-          params.frames_per_buffer());
-    }
-  }
-
-  std::pair<AudioParameters, AudioParameters> dispatcher_key =
-      std::make_pair(params, output_params);
-  AudioOutputDispatchersMap::iterator it =
-      output_dispatchers_.find(dispatcher_key);
-  if (it != output_dispatchers_.end())
-    return new AudioOutputProxy(it->second);
-
-  base::TimeDelta close_delay =
-      base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds);
-
-  if (use_audio_output_resampler &&
-      output_params.format() != AudioParameters::AUDIO_FAKE) {
-    scoped_refptr<AudioOutputDispatcher> dispatcher =
-        new AudioOutputResampler(this, params, output_params, close_delay);
-    output_dispatchers_[dispatcher_key] = dispatcher;
-    return new AudioOutputProxy(dispatcher);
-  }
-
-#if defined(ENABLE_AUDIO_MIXER)
-  // TODO(dalecurtis): Browser side mixing has a couple issues that must be
-  // fixed before it can be turned on by default: http://crbug.com/138098 and
-  // http://crbug.com/140247
-  if (cmd_line->HasSwitch(switches::kEnableAudioMixer)) {
-    scoped_refptr<AudioOutputDispatcher> dispatcher =
-        new AudioOutputMixer(this, params, close_delay);
-    output_dispatchers_[dispatcher_key] = dispatcher;
-    return new AudioOutputProxy(dispatcher);
-  }
-#endif
-
-  scoped_refptr<AudioOutputDispatcher> dispatcher =
-      new AudioOutputDispatcherImpl(this, output_params, close_delay);
-  output_dispatchers_[dispatcher_key] = dispatcher;
-  return new AudioOutputProxy(dispatcher);
-#endif  // defined(OS_IOS)
-}
-
-bool AudioManagerBase::CanShowAudioInputSettings() {
-  return false;
-}
-
-void AudioManagerBase::ShowAudioInputSettings() {
-}
-
-void AudioManagerBase::GetAudioInputDeviceNames(
-    media::AudioDeviceNames* device_names) {
-}
-
-void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
-  DCHECK(stream);
-  // TODO(xians) : Have a clearer destruction path for the AudioOutputStream.
-  // For example, pass the ownership to AudioManager so it can delete the
-  // streams.
-  num_output_streams_--;
-  delete stream;
-}
-
-void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) {
-  DCHECK(stream);
-  // TODO(xians) : Have a clearer destruction path for the AudioInputStream.
-
-  if (virtual_audio_input_stream_ == stream) {
-    DVLOG(1) << "Virtual audio input stream stopping.";
-    virtual_audio_input_stream_->Stop();
-    virtual_audio_input_stream_ = NULL;
-
-    // Make all VirtualAudioOutputStreams unregister from the
-    // VirtualAudioInputStream and recreate themselves as regular audio streams
-    // to return sound to hardware.
-    NotifyAllOutputDeviceChangeListeners();
-  }
-
-  num_input_streams_--;
-  delete stream;
-}
-
-void AudioManagerBase::IncreaseActiveInputStreamCount() {
-  base::AtomicRefCountInc(&num_active_input_streams_);
-}
-
-void AudioManagerBase::DecreaseActiveInputStreamCount() {
-  DCHECK(IsRecordingInProcess());
-  base::AtomicRefCountDec(&num_active_input_streams_);
-}
-
-bool AudioManagerBase::IsRecordingInProcess() {
-  return !base::AtomicRefCountIsZero(&num_active_input_streams_);
-}
-
-void AudioManagerBase::Shutdown() {
-  // To avoid running into deadlocks while we stop the thread, shut it down
-  // via a local variable while not holding the audio thread lock.
-  scoped_ptr<base::Thread> audio_thread;
-  {
-    base::AutoLock lock(audio_thread_lock_);
-    audio_thread_.swap(audio_thread);
-  }
-
-  if (!audio_thread.get())
-    return;
-
-  CHECK_NE(MessageLoop::current(), audio_thread->message_loop());
-
-  // We must use base::Unretained since Shutdown might have been called from
-  // the destructor and we can't alter the refcount of the object at that point.
-  audio_thread->message_loop()->PostTask(FROM_HERE, base::Bind(
-      &AudioManagerBase::ShutdownOnAudioThread,
-      base::Unretained(this)));
-
-  // Stop() will wait for any posted messages to be processed first.
-  audio_thread->Stop();
-}
-
-void AudioManagerBase::ShutdownOnAudioThread() {
-// IOS implements audio input only.
-#if defined(OS_IOS)
-  return;
-#else
-  // This should always be running on the audio thread, but since we've cleared
-  // the audio_thread_ member pointer when we get here, we can't verify exactly
-  // what thread we're running on.  The method is not public though and only
-  // called from one place, so we'll leave it at that.
-  AudioOutputDispatchersMap::iterator it = output_dispatchers_.begin();
-  for (; it != output_dispatchers_.end(); ++it) {
-    scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it).second;
-    if (dispatcher) {
-      dispatcher->Shutdown();
-      // All AudioOutputProxies must have been freed before Shutdown is called.
-      // If they still exist, things will go bad.  They have direct pointers to
-      // both physical audio stream objects that belong to the dispatcher as
-      // well as the message loop of the audio thread that will soon go away.
-      // So, better crash now than later.
-      DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive";
-      dispatcher = NULL;
-    }
-  }
-
-  output_dispatchers_.clear();
-#endif  // defined(OS_IOS)
-}
-
-AudioParameters AudioManagerBase::GetPreferredLowLatencyOutputStreamParameters(
-    const AudioParameters& input_params) {
-#if defined(OS_IOS)
-  // IOS implements audio input only.
-  NOTIMPLEMENTED();
-  return AudioParameters();
-#else
-  // TODO(dalecurtis): This should include bits per channel and channel layout
-  // eventually.
-  return AudioParameters(
-      AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
-      GetAudioHardwareSampleRate(), 16, GetAudioHardwareBufferSize());
-#endif  // defined(OS_IOS)
-}
-
-void AudioManagerBase::AddOutputDeviceChangeListener(
-    AudioDeviceListener* listener) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  output_listeners_.AddObserver(listener);
-}
-
-void AudioManagerBase::RemoveOutputDeviceChangeListener(
-    AudioDeviceListener* listener) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  output_listeners_.RemoveObserver(listener);
-}
-
-void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  DVLOG(1) << "Firing OnDeviceChange() notifications.";
-  FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange());
-}
-
-}  // namespace media
diff --git a/src/media/audio/audio_manager_base.h b/src/media/audio/audio_manager_base.h
deleted file mode 100644
index 83ad98b..0000000
--- a/src/media/audio/audio_manager_base.h
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
-#define MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
-
-#include <map>
-#include <string>
-#include <utility>
-
-#include "base/atomic_ref_count.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/observer_list.h"
-#include "base/synchronization/lock.h"
-#include "media/audio/audio_manager.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#endif
-
-namespace base {
-class Thread;
-}
-
-namespace media {
-
-class AudioOutputDispatcher;
-class VirtualAudioInputStream;
-
-// AudioManagerBase provides AudioManager functions common for all platforms.
-class MEDIA_EXPORT AudioManagerBase : public AudioManager {
- public:
-  // Name of the generic "default" device.
-  static const char kDefaultDeviceName[];
-  // Unique Id of the generic "default" device.
-  static const char kDefaultDeviceId[];
-
-  virtual ~AudioManagerBase();
-
-  virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE;
-
-  virtual string16 GetAudioInputDeviceModel() OVERRIDE;
-
-  virtual bool CanShowAudioInputSettings() OVERRIDE;
-  virtual void ShowAudioInputSettings() OVERRIDE;
-
-  virtual void GetAudioInputDeviceNames(
-      media::AudioDeviceNames* device_names) OVERRIDE;
-
-  virtual AudioOutputStream* MakeAudioOutputStream(
-      const AudioParameters& params) OVERRIDE;
-
-  virtual AudioInputStream* MakeAudioInputStream(
-      const AudioParameters& params, const std::string& device_id) OVERRIDE;
-
-  virtual AudioOutputStream* MakeAudioOutputStreamProxy(
-      const AudioParameters& params) OVERRIDE;
-
-  virtual bool IsRecordingInProcess() OVERRIDE;
-
-  // Called internally by the audio stream when it has been closed.
-  virtual void ReleaseOutputStream(AudioOutputStream* stream);
-  virtual void ReleaseInputStream(AudioInputStream* stream);
-
-  void IncreaseActiveInputStreamCount();
-  void DecreaseActiveInputStreamCount();
-
-  // Creates the output stream for the |AUDIO_PCM_LINEAR| format. The legacy
-  // name is also from |AUDIO_PCM_LINEAR|.
-  virtual AudioOutputStream* MakeLinearOutputStream(
-      const AudioParameters& params) = 0;
-
-  // Creates the output stream for the |AUDIO_PCM_LOW_LATENCY| format.
-  virtual AudioOutputStream* MakeLowLatencyOutputStream(
-      const AudioParameters& params) = 0;
-
-  // Creates the input stream for the |AUDIO_PCM_LINEAR| format. The legacy
-  // name is also from |AUDIO_PCM_LINEAR|.
-  virtual AudioInputStream* MakeLinearInputStream(
-      const AudioParameters& params, const std::string& device_id) = 0;
-
-  // Creates the input stream for the |AUDIO_PCM_LOW_LATENCY| format.
-  virtual AudioInputStream* MakeLowLatencyInputStream(
-      const AudioParameters& params, const std::string& device_id) = 0;
-
-  // Returns the preferred hardware audio output parameters for opening output
-  // streams in the |AUDIO_PCM_LOW_LATENCY| format.
-  // TODO(dalecurtis): Retrieve the |channel_layout| value from hardware instead
-  // of accepting the value.
-  // TODO(dalecurtis): Each AudioManager should implement their own version, see
-  // http://crbug.com/137326
-  virtual AudioParameters GetPreferredLowLatencyOutputStreamParameters(
-      const AudioParameters& input_params);
-
-  // Listeners will be notified on the AudioManager::GetMessageLoop() loop.
-  virtual void AddOutputDeviceChangeListener(
-      AudioDeviceListener* listener) OVERRIDE;
-  virtual void RemoveOutputDeviceChangeListener(
-      AudioDeviceListener* listener) OVERRIDE;
-
- protected:
-  AudioManagerBase();
-
-  // TODO(dalecurtis): This must change to map both input and output parameters
-  // to a single dispatcher, otherwise on a device state change we'll just get
-  // the exact same invalid dispatcher.
-  typedef std::map<std::pair<AudioParameters, AudioParameters>,
-                   scoped_refptr<AudioOutputDispatcher> >
-      AudioOutputDispatchersMap;
-
-  // Shuts down the audio thread and releases all the audio output dispatchers
-  // on the audio thread.  All audio streams should be freed before Shutdown()
-  // is called.  This must be called in the destructor of every AudioManagerBase
-  // implementation.
-  void Shutdown();
-
-  void SetMaxOutputStreamsAllowed(int max) { max_num_output_streams_ = max; }
-
-  // Called by each platform specific AudioManager to notify output state change
-  // listeners that a state change has occurred.  Must be called from the audio
-  // thread.
-  void NotifyAllOutputDeviceChangeListeners();
-
-  // Map of cached AudioOutputDispatcher instances.  Must only be touched
-  // from the audio thread (no locking).
-  AudioOutputDispatchersMap output_dispatchers_;
-
- private:
-  // Called by Shutdown().
-  void ShutdownOnAudioThread();
-
-  // Counts the number of active input streams to find out if something else
-  // is currently recording in Chrome.
-  base::AtomicRefCount num_active_input_streams_;
-
-  // Max number of open output streams, modified by
-  // SetMaxOutputStreamsAllowed().
-  int max_num_output_streams_;
-
-  // Max number of open input streams.
-  int max_num_input_streams_;
-
-  // Number of currently open output streams.
-  int num_output_streams_;
-
-  // Number of currently open input streams.
-  int num_input_streams_;
-
-  // Track output state change listeners.
-  ObserverList<AudioDeviceListener> output_listeners_;
-
-  // Thread used to interact with audio streams created by this audio manager.
-  scoped_ptr<base::Thread> audio_thread_;
-  mutable base::Lock audio_thread_lock_;
-
-  // The message loop of the audio thread this object runs on. Used for internal
-  // tasks which run on the audio thread even after Shutdown() has been started
-  // and GetMessageLoop() starts returning NULL.
-  scoped_refptr<base::MessageLoopProxy> message_loop_;
-
-  // Currently active VirtualAudioInputStream. When this is set, we will
-  // create all audio output streams as virtual streams so as to redirect audio
-  // data to this virtual input stream.
-  VirtualAudioInputStream* virtual_audio_input_stream_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioManagerBase);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
diff --git a/src/media/audio/audio_output_controller.cc b/src/media/audio/audio_output_controller.cc
deleted file mode 100644
index 50850c9..0000000
--- a/src/media/audio/audio_output_controller.cc
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/audio_output_controller.h"
-
-#include "base/bind.h"
-#include "base/debug/trace_event.h"
-#include "base/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/time.h"
-#include "build/build_config.h"
-#include "media/audio/shared_memory_util.h"
-
-using base::Time;
-using base::TimeDelta;
-using base::WaitableEvent;
-
-namespace media {
-
-// Polling-related constants.
-const int AudioOutputController::kPollNumAttempts = 3;
-const int AudioOutputController::kPollPauseInMilliseconds = 3;
-
-AudioOutputController::AudioOutputController(AudioManager* audio_manager,
-                                             EventHandler* handler,
-                                             const AudioParameters& params,
-                                             SyncReader* sync_reader)
-    : audio_manager_(audio_manager),
-      handler_(handler),
-      stream_(NULL),
-      volume_(1.0),
-      state_(kEmpty),
-      sync_reader_(sync_reader),
-      message_loop_(audio_manager->GetMessageLoop()),
-      number_polling_attempts_left_(0),
-      params_(params),
-      ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
-}
-
-AudioOutputController::~AudioOutputController() {
-  DCHECK_EQ(kClosed, state_);
-
-  if (message_loop_->BelongsToCurrentThread()) {
-    DoStopCloseAndClearStream(NULL);
-  } else {
-    // http://crbug.com/120973
-    base::ThreadRestrictions::ScopedAllowWait allow_wait;
-    WaitableEvent completion(true /* manual reset */,
-                             false /* initial state */);
-    message_loop_->PostTask(FROM_HERE,
-        base::Bind(&AudioOutputController::DoStopCloseAndClearStream,
-                   base::Unretained(this),
-                   &completion));
-    completion.Wait();
-  }
-}
-
-// static
-scoped_refptr<AudioOutputController> AudioOutputController::Create(
-    AudioManager* audio_manager,
-    EventHandler* event_handler,
-    const AudioParameters& params,
-    SyncReader* sync_reader) {
-  DCHECK(audio_manager);
-  DCHECK(sync_reader);
-
-  if (!params.IsValid() || !audio_manager)
-    return NULL;
-
-  // Starts the audio controller thread.
-  scoped_refptr<AudioOutputController> controller(new AudioOutputController(
-      audio_manager, event_handler, params, sync_reader));
-
-  controller->message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioOutputController::DoCreate, controller));
-
-  return controller;
-}
-
-void AudioOutputController::Play() {
-  DCHECK(message_loop_);
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioOutputController::DoPlay, this));
-}
-
-void AudioOutputController::Pause() {
-  DCHECK(message_loop_);
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioOutputController::DoPause, this));
-}
-
-void AudioOutputController::Flush() {
-  DCHECK(message_loop_);
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioOutputController::DoFlush, this));
-}
-
-void AudioOutputController::Close(const base::Closure& closed_task) {
-  DCHECK(!closed_task.is_null());
-  DCHECK(message_loop_);
-  message_loop_->PostTaskAndReply(FROM_HERE, base::Bind(
-      &AudioOutputController::DoClose, this), closed_task);
-}
-
-void AudioOutputController::SetVolume(double volume) {
-  DCHECK(message_loop_);
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &AudioOutputController::DoSetVolume, this, volume));
-}
-
-void AudioOutputController::DoCreate() {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-
-  // Close() can be called before DoCreate() is executed.
-  if (state_ == kClosed)
-    return;
-  DCHECK(state_ == kEmpty || state_ == kRecreating) << state_;
-