Import Cobalt 4.12959
diff --git a/src/base/circular_buffer_shell.cc b/src/base/circular_buffer_shell.cc
index ae58e4b..ce64f42 100644
--- a/src/base/circular_buffer_shell.cc
+++ b/src/base/circular_buffer_shell.cc
@@ -15,7 +15,7 @@
 #include "starboard/memory.h"
 #define malloc SbMemoryAllocate
 #define realloc SbMemoryReallocate
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #endif
 
 static inline void* add_to_pointer(void* pointer, size_t amount) {
diff --git a/src/base/logging.cc b/src/base/logging.cc
index 6a135ea..d3a97e5 100644
--- a/src/base/logging.cc
+++ b/src/base/logging.cc
@@ -406,6 +406,25 @@
   return true;
 }
 
+#if defined(OS_STARBOARD)
+SbLogPriority LogLevelToStarboardLogPriority(int level) {
+  switch (level) {
+    case LOG_INFO:
+      return kSbLogPriorityInfo;
+    case LOG_WARNING:
+      return kSbLogPriorityWarning;
+    case LOG_ERROR:
+    case LOG_ERROR_REPORT:
+      return kSbLogPriorityError;
+    case LOG_FATAL:
+      return kSbLogPriorityFatal;
+    default:
+      NOTREACHED() << "Unrecognized log level.";
+      return kSbLogPriorityInfo;
+  }
+}
+#endif  // defined(OS_STARBOARD)
+
 }  // namespace
 
 
@@ -467,6 +486,11 @@
 
 void SetMinLogLevel(int level) {
   min_log_level = std::min(LOG_ERROR_REPORT, level);
+
+#if defined(OS_STARBOARD)
+  starboard::logging::SetMinLogLevel(
+      LogLevelToStarboardLogPriority(std::min(LOG_FATAL, level)));
+#endif
 }
 
 int GetMinLogLevel() {
@@ -650,23 +674,7 @@
   if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG ||
       logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) {
 #if defined(OS_STARBOARD)
-    SbLogPriority priority = kSbLogPriorityUnknown;
-    switch (severity_) {
-      case LOG_INFO:
-        priority = kSbLogPriorityInfo;
-        break;
-      case LOG_WARNING:
-        priority = kSbLogPriorityWarning;
-        break;
-      case LOG_ERROR:
-      case LOG_ERROR_REPORT:
-        priority = kSbLogPriorityError;
-        break;
-      case LOG_FATAL:
-        priority = kSbLogPriorityFatal;
-        break;
-    }
-    SbLog(priority, str_newline.c_str());
+    SbLog(LogLevelToStarboardLogPriority(severity_), str_newline.c_str());
 #elif defined(OS_WIN) || defined(COBALT_WIN)
     OutputDebugStringA(str_newline.c_str());
 #elif defined(OS_ANDROID) || defined(__LB_ANDROID__)
diff --git a/src/base/memory/aligned_memory.h b/src/base/memory/aligned_memory.h
index 040b56d..8ea96b7 100644
--- a/src/base/memory/aligned_memory.h
+++ b/src/base/memory/aligned_memory.h
@@ -99,7 +99,7 @@
 #if defined(COMPILER_MSVC)
   _aligned_free(ptr);
 #elif defined(OS_STARBOARD)
-  SbMemoryFreeAligned(ptr);
+  SbMemoryDeallocateAligned(ptr);
 #else
   free(ptr);
 #endif
diff --git a/src/base/memory/scoped_ptr.h b/src/base/memory/scoped_ptr.h
index 9ee780e..7f2d7dc 100644
--- a/src/base/memory/scoped_ptr.h
+++ b/src/base/memory/scoped_ptr.h
@@ -395,7 +395,7 @@
  public:
   inline void operator()(void* x) const {
 #if defined(OS_STARBOARD)
-    SbMemoryFree(x);
+    SbMemoryDeallocate(x);
 #else
     free(x);
 #endif
diff --git a/src/base/pickle.cc b/src/base/pickle.cc
index 43121c9..e5050b9 100644
--- a/src/base/pickle.cc
+++ b/src/base/pickle.cc
@@ -10,7 +10,7 @@
 #if defined(OS_STARBOARD)
 #include "starboard/memory.h"
 #define realloc SbMemoryReallocate
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #define memcpy SbMemoryCopy
 #else
 #include <stdlib.h>
diff --git a/src/base/shared_memory.h b/src/base/shared_memory.h
index bf77788..8589779 100644
--- a/src/base/shared_memory.h
+++ b/src/base/shared_memory.h
@@ -67,7 +67,7 @@
   }
   ~RefCountedMem() {
 #if defined(OS_STARBOARD)
-    SbMemoryFree(memory_);
+    SbMemoryDeallocate(memory_);
 #else
     free(memory_);
 #endif
diff --git a/src/base/third_party/dmg_fp/dtoa.cc b/src/base/third_party/dmg_fp/dtoa.cc
index 1339d3f..3c5d5a5 100644
--- a/src/base/third_party/dmg_fp/dtoa.cc
+++ b/src/base/third_party/dmg_fp/dtoa.cc
@@ -205,7 +205,7 @@
 #if defined(STARBOARD)
 #include "starboard/memory.h"
 #define MALLOC SbMemoryAllocate
-#define FREE SbMemoryFree
+#define FREE SbMemoryDeallocate
 #else
 #include "stdlib.h"
 #include "string.h"
diff --git a/src/base/tools_sanity_unittest.cc b/src/base/tools_sanity_unittest.cc
index a901f59..074a13e 100644
--- a/src/base/tools_sanity_unittest.cc
+++ b/src/base/tools_sanity_unittest.cc
@@ -15,7 +15,7 @@
 
 #if defined(OS_STARBOARD)
 #include "starboard/memory.h"
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #define malloc SbMemoryAllocate
 #endif
 
diff --git a/src/cobalt/base/console_commands.cc b/src/cobalt/base/console_commands.cc
index eecf31a..73e4f1b 100644
--- a/src/cobalt/base/console_commands.cc
+++ b/src/cobalt/base/console_commands.cc
@@ -50,10 +50,15 @@
                                           const std::string& message) const {
   DCHECK_GT(channel.length(), size_t(0));
   base::AutoLock auto_lock(lock_);
-  CommandHandlerMap::const_iterator iter = command_channel_map_.find(channel);
-  if (iter != command_channel_map_.end()) {
+  CommandHandlerMap::const_iterator iter =
+      command_channel_map_.lower_bound(channel);
+  bool handler_found = false;
+  while (iter != command_channel_map_.end() && iter->first == channel) {
+    handler_found = true;
     iter->second->callback().Run(message);
-  } else {
+    ++iter;
+  }
+  if (!handler_found) {
     DLOG(WARNING) << "No command handler registered for channel: " << channel;
   }
 }
@@ -71,8 +76,9 @@
 std::string ConsoleCommandManager::GetShortHelp(
     const std::string& channel) const {
   base::AutoLock auto_lock(lock_);
-  CommandHandlerMap::const_iterator iter = command_channel_map_.find(channel);
-  if (iter != command_channel_map_.end()) {
+  for (CommandHandlerMap::const_iterator iter =
+           command_channel_map_.lower_bound(channel);
+       iter != command_channel_map_.end() && iter->first == channel; ++iter) {
     return iter->second->short_help();
   }
   return "No help available for unregistered channel: " + channel;
@@ -81,8 +87,9 @@
 std::string ConsoleCommandManager::GetLongHelp(
     const std::string& channel) const {
   base::AutoLock auto_lock(lock_);
-  CommandHandlerMap::const_iterator iter = command_channel_map_.find(channel);
-  if (iter != command_channel_map_.end()) {
+  for (CommandHandlerMap::const_iterator iter =
+           command_channel_map_.lower_bound(channel);
+       iter != command_channel_map_.end() && iter->first == channel; ++iter) {
     return iter->second->long_help();
   }
   return "No help available for unregistered channel: " + channel;
@@ -92,14 +99,22 @@
     const CommandHandler* handler) {
   DCHECK_GT(handler->channel().length(), size_t(0));
   base::AutoLock auto_lock(lock_);
-  command_channel_map_[handler->channel()] = handler;
+  command_channel_map_.insert(std::make_pair(handler->channel(), handler));
 }
 
 void ConsoleCommandManager::UnregisterCommandHandler(
     const CommandHandler* handler) {
-  DCHECK_GT(handler->channel().length(), size_t(0));
+  const std::string& channel = handler->channel();
+  DCHECK_GT(channel.length(), size_t(0));
   base::AutoLock auto_lock(lock_);
-  command_channel_map_.erase(handler->channel());
+  for (CommandHandlerMap::iterator iter =
+           command_channel_map_.lower_bound(channel);
+       iter != command_channel_map_.end() && iter->first == channel; ++iter) {
+    if (iter->second == handler) {
+      command_channel_map_.erase(iter);
+      break;
+    }
+  }
 }
 
 #else   // ENABLE_DEBUG_CONSOLE
diff --git a/src/cobalt/base/console_commands.h b/src/cobalt/base/console_commands.h
index 47144d6..f12113e 100644
--- a/src/cobalt/base/console_commands.h
+++ b/src/cobalt/base/console_commands.h
@@ -89,7 +89,7 @@
 
 #if defined(ENABLE_DEBUG_CONSOLE)
   // Command handler map type.
-  typedef std::map<std::string, const CommandHandler*> CommandHandlerMap;
+  typedef std::multimap<std::string, const CommandHandler*> CommandHandlerMap;
 
   // Methods to register/unregister command handlers.
   // These are intended only to be called from the command handler objects.
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index fe26699..438692d 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -50,6 +50,10 @@
 #endif  // defined(__LB_SHELL__FOR_RELEASE__)
 #include "lbshell/src/lb_memory_pages.h"
 #endif  // defined(__LB_SHELL__)
+#if defined(OS_STARBOARD)
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#endif  // defined(OS_STARBOARD)
 
 namespace cobalt {
 namespace browser {
@@ -61,7 +65,12 @@
 
 #if defined(ENABLE_REMOTE_DEBUGGING)
 int GetRemoteDebuggingPort() {
+#if defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT)
+  const int kDefaultRemoteDebuggingPort =
+      SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT;
+#else
   const int kDefaultRemoteDebuggingPort = 9222;
+#endif  // defined(SB_OVERRIDE_DEFAULT_REMOTE_DEBUGGING_PORT)
   int remote_debugging_port = kDefaultRemoteDebuggingPort;
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -164,6 +173,31 @@
 }
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
+std::string GetMinLogLevelString() {
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kMinLogLevel)) {
+    return command_line->GetSwitchValueASCII(switches::kMinLogLevel);
+  }
+#endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+  return "info";
+}
+
+int StringToLogLevel(const std::string& log_level) {
+  if (log_level == "info") {
+    return logging::LOG_INFO;
+  } else if (log_level == "warning") {
+    return logging::LOG_WARNING;
+  } else if (log_level == "error") {
+    return logging::LOG_ERROR;
+  } else if (log_level == "fatal") {
+    return logging::LOG_FATAL;
+  } else {
+    NOTREACHED() << "Unrecognized logging level: " << log_level;
+    return logging::LOG_INFO;
+  }
+}
+
 void SetIntegerIfSwitchIsSet(const char* switch_name, int* output) {
   if (CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) {
     int32 out;
@@ -267,6 +301,9 @@
 
   RegisterUserLogs();
 
+  // Set the minimum logging level, if specified on the command line.
+  logging::SetMinLogLevel(StringToLogLevel(GetMinLogLevelString()));
+
   stats_update_timer_.Start(
       FROM_HERE, base::TimeDelta::FromMilliseconds(kStatUpdatePeriodMs),
       base::Bind(&Application::UpdatePeriodicStats, base::Unretained(this)));
diff --git a/src/cobalt/browser/browser_bindings.gyp b/src/cobalt/browser/browser_bindings.gyp
index cb9d50b..329d8e2 100644
--- a/src/cobalt/browser/browser_bindings.gyp
+++ b/src/cobalt/browser/browser_bindings.gyp
@@ -116,6 +116,7 @@
         '../dom/MediaKeyNeededEvent.idl',
         '../dom/MediaQueryList.idl',
         '../dom/MediaSource.idl',
+        '../dom/MemoryInfo.idl',
         '../dom/MimeTypeArray.idl',
         '../dom/NamedNodeMap.idl',
         '../dom/Navigator.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 1532cca..58405df 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -179,6 +179,8 @@
   h5vcc_settings.account_manager = account_manager;
   h5vcc_settings.event_dispatcher = system_window->event_dispatcher();
   h5vcc_settings.initial_deep_link = options.initial_deep_link;
+  h5vcc_settings.on_set_record_stats = base::Bind(
+      &BrowserModule::OnSetRecordStats, base::Unretained(this));
   web_module_options_.injected_window_attributes["h5vcc"] =
       base::Bind(&CreateH5VCC, h5vcc_settings);
 
@@ -219,6 +221,8 @@
 }
 
 void BrowserModule::Navigate(const GURL& url) {
+  web_module_loaded_.Reset();
+
   // Always post this as a task in case this is being called from the WebModule.
   self_message_loop_->PostTask(
       FROM_HERE, base::Bind(&BrowserModule::NavigateInternal, weak_this_, url));
@@ -235,8 +239,6 @@
 void BrowserModule::NavigateInternal(const GURL& url) {
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
 
-  web_module_loaded_.Reset();
-
   // First try the registered handlers (e.g. for h5vcc://). If one of these
   // handles the URL, we don't use the web module.
   if (TryURLHandlers(url)) {
@@ -431,6 +433,11 @@
                "BrowserModule::OnDebugConsoleRenderTreeProduced()");
   DCHECK_EQ(MessageLoop::current(), self_message_loop_);
 
+  if (debug_console_->GetMode() == debug::DebugHub::kDebugConsoleOff) {
+    render_tree_combiner_.UpdateDebugConsoleRenderTree(base::nullopt);
+    return;
+  }
+
   render_tree_combiner_.UpdateDebugConsoleRenderTree(renderer::Submission(
       layout_results.render_tree, layout_results.layout_time));
 }
@@ -653,6 +660,8 @@
   // render tree resources either.
   render_tree_combiner_.Reset();
 
+  media_module_->Suspend();
+
   // Place the renderer module into a suspended state where it releases all its
   // graphical resources.
   renderer_module_.Suspend();
@@ -666,6 +675,8 @@
 
   renderer_module_.Resume();
 
+  media_module_->Resume();
+
   // Note that at this point, it is probable that this resource provider is
   // different than the one that was managed in the associated call to
   // Suspend().
@@ -697,5 +708,11 @@
 }
 #endif  // OS_STARBOARD
 
+void BrowserModule::OnSetRecordStats(bool set) {
+  if (web_module_) {
+    web_module_->OnSetRecordStats(set);
+  }
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 82322b3..677aa32 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -210,6 +210,9 @@
   // Process all messages queued into the |render_tree_submission_queue_|.
   void ProcessRenderTreeSubmissionQueue();
 
+  // Called when h5vcc.system.record_stats is set
+  void OnSetRecordStats(bool set);
+
   // TODO:
   //     WeakPtr usage here can be avoided if BrowserModule has a thread to
   //     own where it can ensure that its tasks are all resolved when it is
diff --git a/src/cobalt/browser/debug_console/debug_console.css b/src/cobalt/browser/debug_console/debug_console.css
index afac884..3df005f 100644
--- a/src/cobalt/browser/debug_console/debug_console.css
+++ b/src/cobalt/browser/debug_console/debug_console.css
@@ -10,6 +10,7 @@
   right: 0;
   background-color: rgba(128, 128, 128, 0.6);
   color: #FFFFFF;
+  display: none;
 }
 
 #hud {
@@ -35,6 +36,7 @@
   background-color: rgba(128, 128, 128, 0.6);
   color: #FFFFFF;
   overflow: hidden;
+  display: none;
 }
 
 #messageContainerFrame {
diff --git a/src/cobalt/browser/render_tree_combiner.cc b/src/cobalt/browser/render_tree_combiner.cc
index 364158b..6a076e1 100644
--- a/src/cobalt/browser/render_tree_combiner.cc
+++ b/src/cobalt/browser/render_tree_combiner.cc
@@ -45,7 +45,7 @@
 }
 
 void RenderTreeCombiner::UpdateDebugConsoleRenderTree(
-    const renderer::Submission& render_tree_submission) {
+    const base::optional<renderer::Submission>& render_tree_submission) {
   debug_console_render_tree_ = render_tree_submission;
   SubmitToRenderer();
 }
@@ -105,7 +105,7 @@
 }
 
 void RenderTreeCombiner::UpdateDebugConsoleRenderTree(
-    const renderer::Submission& render_tree_submission) {
+    const base::optional<renderer::Submission>& render_tree_submission) {
   UNREFERENCED_PARAMETER(render_tree_submission);
 }
 #endif  // ENABLE_DEBUG_CONSOLE
diff --git a/src/cobalt/browser/render_tree_combiner.h b/src/cobalt/browser/render_tree_combiner.h
index 44f38d6..75bca9b 100644
--- a/src/cobalt/browser/render_tree_combiner.h
+++ b/src/cobalt/browser/render_tree_combiner.h
@@ -40,7 +40,7 @@
 
   // Update the debug console render tree.
   void UpdateDebugConsoleRenderTree(
-      const renderer::Submission& render_tree_submission);
+      const base::optional<renderer::Submission>& render_tree_submission);
 
 #if defined(ENABLE_DEBUG_CONSOLE)
   bool render_debug_console() const { return render_debug_console_; }
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index a402253..73b734a 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -52,6 +52,9 @@
 // taken from an external input device (like a controller).
 const char kInputFuzzer[] = "input_fuzzer";
 
+// Set the minimum logging level: info|warning|error|fatal.
+const char kMinLogLevel[] = "min_log_level";
+
 // Use the NullAudioStreamer. Audio will be decoded but will not play back. No
 // audio output library will be initialized or used.
 const char kNullAudioStreamer[] = "null_audio_streamer";
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 7d463d0..fe02027 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -31,6 +31,7 @@
 extern const char kExtraWebFileDir[];
 extern const char kIgnoreCertificateErrors[];
 extern const char kInputFuzzer[];
+extern const char kMinLogLevel[];
 extern const char kNullAudioStreamer[];
 extern const char kNullSavegame[];
 extern const char kPartialLayout[];
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 775f7e6..5ee42a0 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -23,6 +23,8 @@
 #include "base/message_loop_proxy.h"
 #include "base/optional.h"
 #include "base/stringprintf.h"
+#include "cobalt/base/c_val.h"
+#include "cobalt/base/poller.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/browser/switches.h"
 #include "cobalt/browser/web_module_stat_tracker.h"
@@ -37,6 +39,12 @@
 
 namespace {
 
+#if defined(COBALT_RELEASE)
+const int kPollerPeriodMs = 2000;
+#else   // #if defined(COBALT_RELEASE)
+const int kPollerPeriodMs = 20;
+#endif  // #if defined(COBALT_RELEASE)
+
 // The maximum number of element depth in the DOM tree. Elements at a level
 // deeper than this could be discarded, and will not be rendered.
 const int kDOMMaxElementDepth = 32;
@@ -62,6 +70,29 @@
     "To wipe the box tree and turn partial layout off.";
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 
+class JSEngineStats {
+ public:
+  JSEngineStats()
+      : js_reserved_memory_("Memory.JS", 0,
+                            "The total memory that is reserved by the engine, "
+                            "including the part that is actually occupied by "
+                            "JS objects, and the part that is not yet.") {}
+
+  static JSEngineStats* GetInstance() {
+    return Singleton<JSEngineStats,
+                     StaticMemorySingletonTraits<JSEngineStats> >::get();
+  }
+
+  void SetReservedMemory(size_t js_reserved_memory) {
+    js_reserved_memory_ = static_cast<uint64>(js_reserved_memory);
+  }
+
+ private:
+  // The total memory that is reserved by the engine, including the part that is
+  // actually occupied by JS objects, and the part that is not yet.
+  base::CVal<base::cval::SizeInBytes, base::CValPublic> js_reserved_memory_;
+};
+
 }  // namespace
 
 // Private WebModule implementation. Each WebModule owns a single instance of
@@ -80,7 +111,9 @@
 #endif  // ENABLE_DEBUG_CONSOLE
 
   // Called to inject a keyboard event into the web module.
-  void InjectKeyboardEvent(const dom::KeyboardEvent::Data& event);
+  void InjectKeyboardEvent(
+      scoped_refptr<dom::Element> element,
+      const dom::KeyboardEvent::Data& event);
 
   // Called to execute JavaScript in this WebModule. Sets the |result|
   // output parameter and signals |got_result|.
@@ -111,6 +144,10 @@
   void Suspend();
   void Resume(render_tree::ResourceProvider* resource_provider);
 
+  void OnSetRecordStats(bool set) {
+    web_module_stat_tracker_->OnSetRecordStats(set);
+  }
+
  private:
   class DocumentLoadedObserver;
 
@@ -135,6 +172,13 @@
     error_callback_.Run(window_->location()->url(), error);
   }
 
+  void UpdateJavaScriptEngineStats() {
+    if (javascript_engine_) {
+      JSEngineStats::GetInstance()->SetReservedMemory(
+          javascript_engine_->UpdateMemoryStatsAndReturnReserved());
+    }
+  }
+
   // Thread checker ensures all calls to the WebModule are made from the same
   // thread that it is created in.
   base::ThreadChecker thread_checker_;
@@ -183,6 +227,9 @@
   // JavaScript engine for the browser.
   scoped_ptr<script::JavaScriptEngine> javascript_engine_;
 
+  // Poller that updates javascript engine stats.
+  scoped_ptr<base::PollerWithThread> javascript_engine_poller_;
+
   // JavaScript Global Object for the browser. There should be one per window,
   // but since there is only one window, we can have one per browser.
   scoped_refptr<script::GlobalEnvironment> global_environment_;
@@ -305,6 +352,11 @@
   javascript_engine_ = script::JavaScriptEngine::CreateEngine();
   DCHECK(javascript_engine_);
 
+  javascript_engine_poller_.reset(new base::PollerWithThread(
+      base::Bind(&WebModule::Impl::UpdateJavaScriptEngineStats,
+                 base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(kPollerPeriodMs)));
+
   global_environment_ = javascript_engine_->CreateGlobalEnvironment();
   DCHECK(global_environment_);
 
@@ -413,6 +465,7 @@
   script_runner_.reset();
   execution_state_.reset();
   global_environment_ = NULL;
+  javascript_engine_poller_.reset();
   javascript_engine_.reset();
   web_module_stat_tracker_.reset();
   local_storage_database_.reset();
@@ -424,6 +477,7 @@
 }
 
 void WebModule::Impl::InjectKeyboardEvent(
+    scoped_refptr<dom::Element> element,
     const dom::KeyboardEvent::Data& event) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(is_running_);
@@ -438,7 +492,11 @@
   // injected.
   web_module_stat_tracker_->OnInjectEvent(keyboard_event);
 
-  window_->InjectEvent(keyboard_event);
+  if (element) {
+    element->DispatchEvent(keyboard_event);
+  } else {
+    window_->InjectEvent(keyboard_event);
+  }
 }
 
 void WebModule::Impl::ExecuteJavascript(
@@ -515,6 +573,7 @@
   window_driver_out->reset(new webdriver::WindowDriver(
       window_id, window_weak_,
       base::Bind(&WebModule::Impl::global_environment, base::Unretained(this)),
+      base::Bind(&WebModule::Impl::InjectKeyboardEvent, base::Unretained(this)),
       base::MessageLoopProxy::current()));
 }
 #endif  // defined(ENABLE_WEBDRIVER)
@@ -694,15 +753,27 @@
   impl_.reset(new Impl(data));
 }
 
-void WebModule::InjectKeyboardEvent(const dom::KeyboardEvent::Data& event) {
+void WebModule::InjectKeyboardEvent(
+    const dom::KeyboardEvent::Data& event) {
+  DCHECK(message_loop());
+  DCHECK(impl_);
+  message_loop()->PostTask(FROM_HERE,
+                           base::Bind(&WebModule::Impl::InjectKeyboardEvent,
+                                      base::Unretained(impl_.get()),
+                                      scoped_refptr<dom::Element>(),
+                                      event));
+}
+
+void WebModule::InjectKeyboardEvent(
+    scoped_refptr<dom::Element> element,
+    const dom::KeyboardEvent::Data& event) {
   TRACE_EVENT1("cobalt::browser", "WebModule::InjectKeyboardEvent()", "type",
                event.type);
   DCHECK(message_loop());
   DCHECK(impl_);
+  DCHECK_EQ(MessageLoop::current(), message_loop());
 
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&WebModule::Impl::InjectKeyboardEvent,
-                                      base::Unretained(impl_.get()), event));
+  impl_->InjectKeyboardEvent(element, event);
 }
 
 std::string WebModule::ExecuteJavascript(
@@ -808,5 +879,12 @@
                             base::Unretained(impl_.get()), resource_provider));
 }
 
+void WebModule::OnSetRecordStats(bool set) {
+  DCHECK(message_loop());
+  DCHECK(impl_);
+  DCHECK_EQ(MessageLoop::current(), message_loop());
+  impl_->OnSetRecordStats(set);
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 4987ebe..5b6c8c2 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -160,7 +160,16 @@
   ~WebModule();
 
   // Call this to inject a keyboard event into the web module.
-  void InjectKeyboardEvent(const dom::KeyboardEvent::Data& event);
+  // Event is directed at a specific element if the element is non-null.
+  // Otherwise, the currently focused element receives the event.
+  // If element is specified, we must be on the WebModule's message loop
+  void InjectKeyboardEvent(
+      scoped_refptr<dom::Element> element,
+      const dom::KeyboardEvent::Data& event);
+
+  // Call this to inject a keyboard event into the web module.
+  void InjectKeyboardEvent(
+      const dom::KeyboardEvent::Data& event);
 
   // Call this to execute Javascript code in this web module.  The calling
   // thread will block until the JavaScript has executed and the output results
@@ -190,6 +199,9 @@
   // can only be called if we have previously suspended the WebModule.
   void Resume(render_tree::ResourceProvider* resource_provider);
 
+  // Called when h5vcc.system.recordStats is set.
+  void OnSetRecordStats(bool set);
+
 #if defined(COBALT_BUILD_TYPE_DEBUG)
   // Non-optimized builds require a bigger stack size.
   static const size_t kBaseStackSize = 2 * 1024 * 1024;
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc
index 10bdf94..dc94278 100644
--- a/src/cobalt/browser/web_module_stat_tracker.cc
+++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -20,6 +20,9 @@
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/event.h"
 
+// The maximum allowed string size of any recorded stat
+const std::string::size_type kMaxRecordedStatsBytes = 64 * 1024;
+
 namespace cobalt {
 namespace browser {
 
@@ -28,7 +31,10 @@
     : dom_stat_tracker_(new dom::DomStatTracker(name)),
       layout_stat_tracker_(new layout::LayoutStatTracker(name)),
       should_track_event_stats_(should_track_event_stats),
-      current_event_type_(kEventTypeInvalid) {
+      current_event_type_(kEventTypeInvalid),
+      name_(name),
+      event_is_processing_(StringPrintf("Event.%s.IsProcessing", name.c_str()),
+          0, "Nonzero when an event is being processed.") {
   if (should_track_event_stats_) {
     event_stats_.reserve(kNumEventTypes);
     for (int i = 0; i < kNumEventTypes; ++i) {
@@ -56,6 +62,8 @@
 
   EndCurrentEvent(false);
 
+  event_is_processing_ = 1;
+
   if (event->type() == base::Tokens::keydown()) {
     current_event_type_ = kEventTypeKeyDown;
   } else if (event->type() == base::Tokens::keyup()) {
@@ -82,6 +90,17 @@
   layout_stat_tracker_->FlushPeriodicTracking();
 }
 
+void WebModuleStatTracker::OnSetRecordStats(bool set) {
+  record_stats_ = set;
+
+  // Every time this variable is set, we clear out our stats
+  for (ScopedVector<EventStats>::iterator it = event_stats_.begin();
+       it != event_stats_.end();
+       ++it) {
+    (*it)->event_durations = "[]";
+  }
+}
+
 WebModuleStatTracker::EventStats::EventStats(const std::string& name)
     : count_dom_html_elements_created(
           StringPrintf("Event.Count.%s.DOM.HtmlElement.Created", name.c_str()),
@@ -138,7 +157,11 @@
           StringPrintf("Event.Duration.%s.Layout.RenderAndAnimate",
                        name.c_str()),
           base::TimeDelta(),
-          "RenderAndAnimate duration for event (in microseconds).") {}
+          "RenderAndAnimate duration for event (in microseconds)."),
+      event_durations(StringPrintf("Event.Durations.%s", name.c_str()),
+                     "[]",
+                     "JSON array of all event durations (in microseconds) "
+                     "since reset.") {}
 
 bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; }
 
@@ -152,6 +175,8 @@
     return;
   }
 
+  event_is_processing_ = 0;
+
   stop_watch_durations_[kStopWatchTypeEvent] = base::TimeDelta();
   stop_watches_[kStopWatchTypeEvent].Stop();
   dom_stat_tracker_->DisableStopWatches();
@@ -184,9 +209,25 @@
   // misleading as it merely indicates how long the user waited to initiate the
   // next event. When this occurs, the injection duration provides a much more
   // accurate picture of how long the event takes.
-  event_stats->duration_total = was_render_tree_produced
+  base::TimeDelta duration_total = was_render_tree_produced
                                     ? stop_watch_durations_[kStopWatchTypeEvent]
                                     : event_injection_duration;
+  event_stats->duration_total = duration_total;
+
+  if (record_stats_) {
+    std::string prev_durations = event_stats->event_durations.value();
+    if (prev_durations.size() <= 2) {
+      event_stats->event_durations =
+          StringPrintf("[%ld]", duration_total.InMicroseconds());
+    } else if (prev_durations.size() < kMaxRecordedStatsBytes) {
+      event_stats->event_durations
+          = StringPrintf("%s,%ld]",
+                         prev_durations.substr(
+                             0, prev_durations.size() - 1).c_str(),
+                             duration_total.InMicroseconds());
+    }
+  }
+
   event_stats->duration_dom_inject_event = event_injection_duration;
   event_stats->duration_dom_update_computed_style =
       dom_stat_tracker_->GetStopWatchTypeDuration(
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h
index 8e9dbd3..8ebf73d 100644
--- a/src/cobalt/browser/web_module_stat_tracker.h
+++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -54,6 +54,9 @@
   // triggers flushing of periodic counts within the stat trackers.
   void OnRenderTreeProduced();
 
+  // Called when h5vcc.system.record_stats is set
+  void OnSetRecordStats(bool set);
+
  private:
   enum EventType {
     kEventTypeInvalid = -1,
@@ -90,6 +93,9 @@
         duration_layout_update_used_sizes;
     base::CVal<base::TimeDelta, base::CValPublic>
         duration_layout_render_and_animate;
+
+    // Time series-related
+    base::CVal<std::string, base::CValPublic> event_durations;
   };
 
   // From base::StopWatchOwner
@@ -115,6 +121,13 @@
   // Stop watch-related
   std::vector<base::StopWatch> stop_watches_;
   std::vector<base::TimeDelta> stop_watch_durations_;
+
+  // Time series-related
+  bool record_stats_;
+
+  std::string name_;
+
+  base::CVal<int, base::CValPublic> event_is_processing_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 66974b8..4860db9 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-12347
\ No newline at end of file
+12959
\ No newline at end of file
diff --git a/src/cobalt/content/fonts/10megabytes/fonts.xml b/src/cobalt/content/fonts/10megabytes/fonts.xml
index c0ffb0a..d066d4a 100644
--- a/src/cobalt/content/fonts/10megabytes/fonts.xml
+++ b/src/cobalt/content/fonts/10megabytes/fonts.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
+<familyset version="1">
 <!--
     NOTE: Families with a "fallback" value of "true" are added to the fallback
     list, regardless of whether or not they are named. Fallback fonts are chosen
@@ -13,10 +14,9 @@
     indexed, and each page contains 256 characters, so character 1000 would be
     contained within page 3.
 -->
-<familyset version="1">
     <!-- first font is default -->
     <family name="sans-serif">
-        <font weight="400" style="normal">Roboto-Regular.ttf</font>
+        <font font_name="Roboto Regular" postscript_name="Roboto-Regular" style="normal" weight="400">Roboto-Regular.ttf</font>
     </family>
     <!-- Note that aliases must come after the fonts they reference. -->
     <alias name="arial" to="sans-serif" />
@@ -27,187 +27,187 @@
     <alias name="courier" to="serif-monospace" />
     <alias name="courier new" to="serif-monospace" />
     <family name="sans-serif-smallcaps">
-        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+        <font font_name="Carrois Gothic SC" postscript_name="CarroisGothicSC-Regular" style="normal" weight="400">CarroisGothicSC-Regular.ttf</font>
     </family>
     <!-- fallback fonts -->
-    <family name="Noto Naskh Arabic UI" fallback="true" pages="0,6-8,32,37,46,251-254">
-        <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
+    <family fallback="true" name="Noto Naskh Arabic UI" pages="0,6-8,32,37,46,251-254">
+        <font font_name="Noto Naskh Arabic UI" postscript_name="NotoNaskhArabicUI" style="normal" weight="400">NotoNaskhArabicUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,18-19,45,171,254">
-        <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
+        <font font_name="Noto Sans Ethiopic" postscript_name="NotoSansEthiopic" style="normal" weight="400">NotoSansEthiopic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,5,32,37,251,254">
-        <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
+        <font font_name="Noto Sans Hebrew" postscript_name="NotoSansHebrew" style="normal" weight="400">NotoSansHebrew-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,2-3,14,32,37,254">
-        <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
+        <font font_name="Noto Sans Thai UI" postscript_name="NotoSansThaiUI" style="normal" weight="400">NotoSansThaiUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,5,251,254">
-        <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
+        <font font_name="Noto Sans Armenian" postscript_name="NotoSansArmenian" style="normal" weight="400">NotoSansArmenian-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,5,16,45,254">
-        <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
+        <font font_name="Noto Sans Georgian" postscript_name="NotoSansGeorgian" style="normal" weight="400">NotoSansGeorgian-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,2,9,28,32,34,37,168,254">
-        <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
+        <font font_name="Noto Sans Devanagari UI" postscript_name="NotoSansDevanagariUI" style="normal" weight="400">NotoSansDevanagariUI-Regular.ttf</font>
     </family>
     <!-- Gujarati should come after Devanagari -->
     <family fallback="true" pages="0,9-10,32,34,37,168,254">
-        <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
+        <font font_name="Noto Sans Gujarati UI" postscript_name="NotoSansGujaratiUI" style="normal" weight="400">NotoSansGujaratiUI-Regular.ttf</font>
     </family>
     <!-- Gurmukhi should come after Devanagari -->
     <family fallback="true" pages="0,9-10,32,34,37-38,168,254">
-        <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
+        <font font_name="Noto Sans Gurmukhi UI" postscript_name="NotoSansGurmukhiUI" style="normal" weight="400">NotoSansGurmukhiUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,11,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
+        <font font_name="Noto Sans Tamil UI" postscript_name="NotoSansTamilUI" style="normal" weight="400">NotoSansTamilUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,9,13,32,34,37,254">
-        <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
+        <font font_name="Noto Sans Malayalam UI" postscript_name="NotoSansMalayalamUI" style="normal" weight="400">NotoSansMalayalamUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,32,34,37,254">
-        <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
+        <font font_name="Noto Sans Bengali UI" postscript_name="NotoSansBengaliUI" style="normal" weight="400">NotoSansBengaliUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,12,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
+        <font font_name="Noto Sans Telugu UI" postscript_name="NotoSansTeluguUI" style="normal" weight="400">NotoSansTeluguUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,12,32,34,37,254">
-        <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
+        <font font_name="Noto Sans Kannada UI" postscript_name="NotoSansKannadaUI" style="normal" weight="400">NotoSansKannadaUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,11,32,34,37,254">
-        <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
+        <font font_name="Noto Sans Oriya UI" postscript_name="NotoSansOriyaUI" style="normal" weight="400">NotoSansOriyaUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,13,32,34,37,254">
-        <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
+        <font font_name="Noto Sans Sinhala" postscript_name="NotoSansSinhala" style="normal" weight="400">NotoSansSinhala-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,25,32,37">
-        <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
+        <font font_name="Noto Sans Khmer UI" postscript_name="NotoSansKhmerUI" style="normal" weight="400">NotoSansKhmerUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,14,32,37">
-        <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
+        <font font_name="Noto Sans Lao UI" postscript_name="NotoSansLaoUI" style="normal" weight="400">NotoSansLaoUI-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,6-7,32,37,253-254">
-        <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
+        <font font_name="Noto Sans Thaana" postscript_name="NotoSansThaana" style="normal" weight="400">NotoSansThaana-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,170">
-        <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
+        <font font_name="Noto Sans Cham" postscript_name="NotoSansCham" style="normal" weight="400">NotoSansCham-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,27,32,37,254">
-        <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
+        <font font_name="Noto Sans Balinese" postscript_name="NotoSansBalinese" style="normal" weight="400">NotoSansBalinese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,166,254,360-362">
-        <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+        <font font_name="Noto Sans Bamum" postscript_name="NotoSansBamum" style="normal" weight="400">NotoSansBamum-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,27,254">
-        <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
+        <font font_name="Noto Sans Batak" postscript_name="NotoSansBatak" style="normal" weight="400">NotoSansBatak-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,26,32,169,254">
-        <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
+        <font font_name="Noto Sans Buginese" postscript_name="NotoSansBuginese" style="normal" weight="400">NotoSansBuginese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
+        <font font_name="Noto Sans Buhid" postscript_name="NotoSansBuhid" style="normal" weight="400">NotoSansBuhid-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0-3,20-22,24,254">
-        <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+        <font font_name="Noto Sans Canadian Aboriginal" postscript_name="NotoSansCanadianAboriginal" style="normal" weight="400">NotoSansCanadianAboriginal-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,19,254">
-        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+        <font font_name="Noto Sans Cherokee" postscript_name="NotoSansCherokee" style="normal" weight="400">NotoSansCherokee-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0-3,29,44,254">
-        <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
+        <font font_name="Noto Sans Coptic" postscript_name="NotoSansCoptic" style="normal" weight="400">NotoSansCoptic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,44,254">
-        <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
+        <font font_name="Noto Sans Glagolitic" postscript_name="NotoSansGlagolitic" style="normal" weight="400">NotoSansGlagolitic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
+        <font font_name="Noto Sans Hanunoo" postscript_name="NotoSansHanunoo" style="normal" weight="400">NotoSansHanunoo-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,32,37,169,254">
-        <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
+        <font font_name="Noto Sans Javanese" postscript_name="NotoSansJavanese" style="normal" weight="400">NotoSansJavanese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,169,254">
-        <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
+        <font font_name="Noto Sans Kayah Li" postscript_name="NotoSansKayahLi" style="normal" weight="400">NotoSansKayahLi-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,28,37,254">
-        <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
+        <font font_name="Noto Sans Lepcha" postscript_name="NotoSansLepcha" style="normal" weight="400">NotoSansLepcha-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,25,254">
-        <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
+        <font font_name="Noto Sans Limbu" postscript_name="NotoSansLimbu" style="normal" weight="400">NotoSansLimbu-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,2,164,254">
-        <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+        <font font_name="Noto Sans Lisu" postscript_name="NotoSansLisu" style="normal" weight="400">NotoSansLisu-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,6,8,254">
-        <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+        <font font_name="Noto Sans Mandaic" postscript_name="NotoSansMandaic" style="normal" weight="400">NotoSansMandaic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,170-171,254">
-        <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
+        <font font_name="Noto Sans Meetei Mayek" postscript_name="NotoSansMeeteiMayek" style="normal" weight="400">NotoSansMeeteiMayek-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,25,254">
-        <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+        <font font_name="Noto Sans New Tai Lue" postscript_name="NotoSansNewTaiLue" style="normal" weight="400">NotoSansNewTaiLue-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,6-7,32,46,253-254">
-        <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+        <font font_name="Noto Sans NKo" postscript_name="NotoSansNKo" style="normal" weight="400">NotoSansNKo-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,28,254">
-        <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
+        <font font_name="Noto Sans Ol Chiki" postscript_name="NotoSansOlChiki" style="normal" weight="400">NotoSansOlChiki-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,169,254">
-        <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
+        <font font_name="Noto Sans Rejang" postscript_name="NotoSansRejang" style="normal" weight="400">NotoSansRejang-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,32,37,168,254">
-        <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
+        <font font_name="Noto Sans Saurashtra" postscript_name="NotoSansSaurashtra" style="normal" weight="400">NotoSansSaurashtra-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,27-28,254">
-        <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
+        <font font_name="Noto Sans Sundanese" postscript_name="NotoSansSundanese" style="normal" weight="400">NotoSansSundanese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,32,37,168,254">
-        <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
+        <font font_name="Noto Sans Syloti Nagri" postscript_name="NotoSansSylotiNagri" style="normal" weight="400">NotoSansSylotiNagri-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,6-7,32,34,37-38,254">
-        <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+        <font font_name="Noto Sans Syriac Estrangela" postscript_name="NotoSansSyriacEstrangela" style="normal" weight="400">NotoSansSyriacEstrangela-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
+        <font font_name="Noto Sans Tagbanwa" postscript_name="NotoSansTagbanwa" style="normal" weight="400">NotoSansTagbanwa-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,26,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
+        <font font_name="Noto Sans Tai Tham" postscript_name="NotoSansTaiTham" style="normal" weight="400">NotoSansTaiTham-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,32,37,167,170,254">
-        <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
+        <font font_name="Noto Sans Tai Viet" postscript_name="NotoSansTaiViet" style="normal" weight="400">NotoSansTaiViet-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,15,32,37,254">
-        <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
+        <font font_name="Noto Sans Tibetan" postscript_name="NotoSansTibetan" style="normal" weight="400">NotoSansTibetan-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,32,45,254">
-        <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
+        <font font_name="Noto Sans Tifinagh" postscript_name="NotoSansTifinagh" style="normal" weight="400">NotoSansTifinagh-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,165-166,254">
-        <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+        <font font_name="Noto Sans Vai" postscript_name="NotoSansVai" style="normal" weight="400">NotoSansVai-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,160-164,254">
-        <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+        <font font_name="Noto Sans Yi" postscript_name="NotoSansYi" style="normal" weight="400">NotoSansYi-Regular.ttf</font>
     </family>
     <family fallback="true" pages="32-43">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+        <font font_name="Noto Sans Symbols" postscript_name="NotoSansSymbols" style="normal" weight="400">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family fallback="true" lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
-        <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
+        <font font_name="Noto Sans JP Regular" postscript_name="NotoSansJP-Regular" style="normal" weight="400">NotoSansJP-Regular.otf</font>
     </family>
     <family fallback="true" pages="0,32-33,35-39,41,43,48,50,224,254-255,496-502,4068">
-        <font weight="400" style="normal">NotoEmoji-Regular.ttf</font>
+        <font font_name="Noto Emoji" postscript_name="NotoEmoji" style="normal" weight="400">NotoEmoji-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,14,17,32,48-51,77-159,172-215,249-250,254-255,260">
-        <font weight="400" style="normal">DroidSansFallback.ttf</font>
+        <font font_name="Droid Sans Fallback" postscript_name="DroidSansFallback" style="normal" weight="400">DroidSansFallback.ttf</font>
     </family>
     <!--
         Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
     -->
     <family fallback="true" pages="0,16,25,48,254">
-        <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
+        <font font_name="Noto Sans Tai Le" postscript_name="NotoSansTaiLe" style="normal" weight="400">NotoSansTaiLe-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,24,32,36-37,48,254">
-        <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+        <font font_name="Noto Sans Mongolian" postscript_name="NotoSansMongolian" style="normal" weight="400">NotoSansMongolian-Regular.ttf</font>
     </family>
 </familyset>
diff --git a/src/cobalt/content/fonts/minimal/fonts.xml b/src/cobalt/content/fonts/minimal/fonts.xml
index 1381b81..bd40ee3 100644
--- a/src/cobalt/content/fonts/minimal/fonts.xml
+++ b/src/cobalt/content/fonts/minimal/fonts.xml
@@ -1,11 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <familyset version="1">
+<!--
+    NOTE: Families with a "fallback" value of "true" are added to the fallback
+    list, regardless of whether or not they are named. Fallback fonts are chosen
+    based on a match: full BCP-47 language tag including script, then just
+    language, and finally order (the first font containing the glyph). Order of
+    appearance is also the tiebreaker for weight matching.
+
+    The pages attribute indicates which character pages are contained within
+    the font. It is used with character fallback to allow the system to quickly
+    determine that a character cannot appear in a font without requiring the
+    full character map to be loaded into memory. Character pages are zero
+    indexed, and each page contains 256 characters, so character 1000 would be
+    contained within page 3.
+-->
     <!-- Ideally, this font should only be used if there are no other fonts in
          our final image. -->
     <family name="Minimal Roboto">
-        <font weight="400" style="normal">MinimalRoboto.ttf</font>
+        <font font_name="Minimal Roboto" postscript_name="Minimal Roboto" style="normal" weight="400">MinimalRoboto.ttf</font>
     </family>
     <family name="sans-serif-smallcaps">
-        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+        <font font_name="Carrois Gothic SC" postscript_name="CarroisGothicSC-Regular" style="normal" weight="400">CarroisGothicSC-Regular.ttf</font>
     </family>
 </familyset>
diff --git a/src/cobalt/content/fonts/unlimited/fonts.xml b/src/cobalt/content/fonts/unlimited/fonts.xml
index 3e1ec53..25426c3 100644
--- a/src/cobalt/content/fonts/unlimited/fonts.xml
+++ b/src/cobalt/content/fonts/unlimited/fonts.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<familyset version="1">
 <!--
     NOTE: Families with a "fallback" value of "true" are added to the fallback
     list, regardless of whether or not they are named. Fallback fonts are chosen
@@ -13,13 +13,12 @@
     indexed, and each page contains 256 characters, so character 1000 would be
     contained within page 3.
 -->
-<familyset version="1">
     <!-- first font is default -->
     <family name="sans-serif">
-        <font weight="400" style="normal">Roboto-Regular.ttf</font>
-        <font weight="400" style="italic">Roboto-Italic.ttf</font>
-        <font weight="700" style="normal">Roboto-Bold.ttf</font>
-        <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
+        <font font_name="Roboto Regular" postscript_name="Roboto-Regular" style="normal" weight="400">Roboto-Regular.ttf</font>
+        <font font_name="Roboto Italic" postscript_name="Roboto-Italic" style="italic" weight="400">Roboto-Italic.ttf</font>
+        <font font_name="Roboto Bold" postscript_name="Roboto-Bold" style="normal" weight="700">Roboto-Bold.ttf</font>
+        <font font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic" style="italic" weight="700">Roboto-BoldItalic.ttf</font>
     </family>
     <!-- Note that aliases must come after the fonts they reference. -->
     <alias name="arial" to="sans-serif" />
@@ -30,13 +29,13 @@
     <!-- Ideally, this font should only be used if there are no other fonts in
          our final image. -->
     <family name="Minimal Roboto">
-        <font weight="400" style="normal">MinimalRoboto.ttf</font>
+        <font font_name="Minimal Roboto" postscript_name="Minimal Roboto" style="normal" weight="400">MinimalRoboto.ttf</font>
     </family>
     <family name="serif">
-        <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
-        <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
-        <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
-        <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
+        <font font_name="Noto Serif" postscript_name="NotoSerif" style="normal" weight="400">NotoSerif-Regular.ttf</font>
+        <font font_name="Noto Serif Italic" postscript_name="NotoSerif-Italic" style="italic" weight="400">NotoSerif-Italic.ttf</font>
+        <font font_name="Noto Serif Bold" postscript_name="NotoSerif-Bold" style="normal" weight="700">NotoSerif-Bold.ttf</font>
+        <font font_name="Noto Serif Bold Italic" postscript_name="NotoSerif-BoldItalic" style="italic" weight="700">NotoSerif-BoldItalic.ttf</font>
     </family>
     <alias name="times" to="serif" />
     <alias name="times new roman" to="serif" />
@@ -47,243 +46,243 @@
     <alias name="fantasy" to="serif" />
     <alias name="ITC Stone Serif" to="serif" />
     <family name="monospace">
-        <font weight="400" style="normal">DroidSansMono.ttf</font>
+        <font font_name="Droid Sans Mono" postscript_name="DroidSansMono" style="normal" weight="400">DroidSansMono.ttf</font>
     </family>
     <alias name="sans-serif-monospace" to="monospace" />
     <alias name="monaco" to="monospace" />
     <family name="serif-monospace">
-        <font weight="400" style="normal">CutiveMono.ttf</font>
+        <font font_name="Cutive Mono" postscript_name="CutiveMono-Regular" style="normal" weight="400">CutiveMono.ttf</font>
     </family>
     <alias name="courier" to="serif-monospace" />
     <alias name="courier new" to="serif-monospace" />
     <family name="casual">
-        <font weight="400" style="normal">ComingSoon.ttf</font>
+        <font font_name="Coming Soon" postscript_name="ComingSoon" style="normal" weight="400">ComingSoon.ttf</font>
     </family>
     <family name="cursive">
-        <font weight="400" style="normal">DancingScript-Regular.ttf</font>
-        <font weight="700" style="normal">DancingScript-Bold.ttf</font>
+        <font font_name="Dancing Script" postscript_name="DancingScript" style="normal" weight="400">DancingScript-Regular.ttf</font>
+        <font font_name="Dancing Script Bold" postscript_name="DancingScript-Bold" style="normal" weight="700">DancingScript-Bold.ttf</font>
     </family>
     <family name="sans-serif-smallcaps">
-        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+        <font font_name="Carrois Gothic SC" postscript_name="CarroisGothicSC-Regular" style="normal" weight="400">CarroisGothicSC-Regular.ttf</font>
     </family>
     <!-- fallback fonts -->
-    <family name="Noto Naskh Arabic UI" fallback="true" pages="0,6-8,32,37,46,251-254">
-        <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
+    <family fallback="true" name="Noto Naskh Arabic UI" pages="0,6-8,32,37,46,251-254">
+        <font font_name="Noto Naskh Arabic UI" postscript_name="NotoNaskhArabicUI" style="normal" weight="400">NotoNaskhArabicUI-Regular.ttf</font>
+        <font font_name="Noto Naskh Arabic UI Bold" postscript_name="NotoNaskhArabicUI-Bold" style="normal" weight="700">NotoNaskhArabicUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,18-19,45,171,254">
-        <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
+        <font font_name="Noto Sans Ethiopic" postscript_name="NotoSansEthiopic" style="normal" weight="400">NotoSansEthiopic-Regular.ttf</font>
+        <font font_name="Noto Sans Ethiopic Bold" postscript_name="NotoSansEthiopic-Bold" style="normal" weight="700">NotoSansEthiopic-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,5,32,37,251,254">
-        <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+        <font font_name="Noto Sans Hebrew" postscript_name="NotoSansHebrew" style="normal" weight="400">NotoSansHebrew-Regular.ttf</font>
+        <font font_name="Noto Sans Hebrew Bold" postscript_name="NotoSansHebrew-Bold" style="normal" weight="700">NotoSansHebrew-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,2-3,14,32,37,254">
-        <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
+        <font font_name="Noto Sans Thai UI" postscript_name="NotoSansThaiUI" style="normal" weight="400">NotoSansThaiUI-Regular.ttf</font>
+        <font font_name="Noto Sans Thai UI Bold" postscript_name="NotoSansThaiUI-Bold" style="normal" weight="700">NotoSansThaiUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,5,251,254">
-        <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
+        <font font_name="Noto Sans Armenian" postscript_name="NotoSansArmenian" style="normal" weight="400">NotoSansArmenian-Regular.ttf</font>
+        <font font_name="Noto Sans Armenian Bold" postscript_name="NotoSansArmenian-Bold" style="normal" weight="700">NotoSansArmenian-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,5,16,45,254">
-        <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
+        <font font_name="Noto Sans Georgian" postscript_name="NotoSansGeorgian" style="normal" weight="400">NotoSansGeorgian-Regular.ttf</font>
+        <font font_name="Noto Sans Georgian Bold" postscript_name="NotoSansGeorgian-Bold" style="normal" weight="700">NotoSansGeorgian-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,2,9,28,32,34,37,168,254">
-        <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansDevanagariUI-Bold.ttf</font>
+        <font font_name="Noto Sans Devanagari UI" postscript_name="NotoSansDevanagariUI" style="normal" weight="400">NotoSansDevanagariUI-Regular.ttf</font>
+        <font font_name="Noto Sans Devanagari UI Bold" postscript_name="NotoSansDevanagariUI-Bold" style="normal" weight="700">NotoSansDevanagariUI-Bold.ttf</font>
     </family>
     <!-- Gujarati should come after Devanagari -->
     <family fallback="true" pages="0,9-10,32,34,37,168,254">
-        <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
+        <font font_name="Noto Sans Gujarati UI" postscript_name="NotoSansGujaratiUI" style="normal" weight="400">NotoSansGujaratiUI-Regular.ttf</font>
+        <font font_name="Noto Sans Gujarati UI Bold" postscript_name="NotoSansGujaratiUI-Bold" style="normal" weight="700">NotoSansGujaratiUI-Bold.ttf</font>
     </family>
     <!-- Gurmukhi should come after Devanagari -->
     <family fallback="true" pages="0,9-10,32,34,37-38,168,254">
-        <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansGurmukhiUI-Bold.ttf</font>
+        <font font_name="Noto Sans Gurmukhi UI" postscript_name="NotoSansGurmukhiUI" style="normal" weight="400">NotoSansGurmukhiUI-Regular.ttf</font>
+        <font font_name="Noto Sans Gurmukhi UI Bold" postscript_name="NotoSansGurmukhiUI-Bold" style="normal" weight="700">NotoSansGurmukhiUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,9,11,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansTamilUI-Bold.ttf</font>
+        <font font_name="Noto Sans Tamil UI" postscript_name="NotoSansTamilUI" style="normal" weight="400">NotoSansTamilUI-Regular.ttf</font>
+        <font font_name="Noto Sans Tamil UI Bold" postscript_name="NotoSansTamilUI-Bold" style="normal" weight="700">NotoSansTamilUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,3,9,13,32,34,37,254">
-        <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansMalayalamUI-Bold.ttf</font>
+        <font font_name="Noto Sans Malayalam UI" postscript_name="NotoSansMalayalamUI" style="normal" weight="400">NotoSansMalayalamUI-Regular.ttf</font>
+        <font font_name="Noto Sans Malayalam UI Bold" postscript_name="NotoSansMalayalamUI-Bold" style="normal" weight="700">NotoSansMalayalamUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,9,32,34,37,254">
-        <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
+        <font font_name="Noto Sans Bengali UI" postscript_name="NotoSansBengaliUI" style="normal" weight="400">NotoSansBengaliUI-Regular.ttf</font>
+        <font font_name="Noto Sans Bengali UI Bold" postscript_name="NotoSansBengaliUI-Bold" style="normal" weight="700">NotoSansBengaliUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,9,12,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansTeluguUI-Bold.ttf</font>
+        <font font_name="Noto Sans Telugu UI" postscript_name="NotoSansTeluguUI" style="normal" weight="400">NotoSansTeluguUI-Regular.ttf</font>
+        <font font_name="Noto Sans Telugu UI Bold" postscript_name="NotoSansTeluguUI-Bold" style="normal" weight="700">NotoSansTeluguUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,9,12,32,34,37,254">
-        <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
+        <font font_name="Noto Sans Kannada UI" postscript_name="NotoSansKannadaUI" style="normal" weight="400">NotoSansKannadaUI-Regular.ttf</font>
+        <font font_name="Noto Sans Kannada UI Bold" postscript_name="NotoSansKannadaUI-Bold" style="normal" weight="700">NotoSansKannadaUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,9,11,32,34,37,254">
-        <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
+        <font font_name="Noto Sans Oriya UI" postscript_name="NotoSansOriyaUI" style="normal" weight="400">NotoSansOriyaUI-Regular.ttf</font>
+        <font font_name="Noto Sans Oriya UI Bold" postscript_name="NotoSansOriyaUI-Bold" style="normal" weight="700">NotoSansOriyaUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,9,13,32,34,37,254">
-        <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
+        <font font_name="Noto Sans Sinhala" postscript_name="NotoSansSinhala" style="normal" weight="400">NotoSansSinhala-Regular.ttf</font>
+        <font font_name="Noto Sans Sinhala Bold" postscript_name="NotoSansSinhala-Bold" style="normal" weight="700">NotoSansSinhala-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,23,25,32,37">
-        <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
+        <font font_name="Noto Sans Khmer UI" postscript_name="NotoSansKhmerUI" style="normal" weight="400">NotoSansKhmerUI-Regular.ttf</font>
+        <font font_name="Noto Sans Khmer UI Bold" postscript_name="NotoSansKhmerUI-Bold" style="normal" weight="700">NotoSansKhmerUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,3,14,32,37">
-        <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
+        <font font_name="Noto Sans Lao UI" postscript_name="NotoSansLaoUI" style="normal" weight="400">NotoSansLaoUI-Regular.ttf</font>
+        <font font_name="Noto Sans Lao UI Bold" postscript_name="NotoSansLaoUI-Bold" style="normal" weight="700">NotoSansLaoUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,16,32,37,169-170,254">
-        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
+        <font font_name="Noto Sans Myanmar UI" postscript_name="NotoSansMyanmarUI" style="normal" weight="400">NotoSansMyanmarUI-Regular.ttf</font>
+        <font font_name="Noto Sans Myanmar UI Bold" postscript_name="NotoSansMyanmarUI-Bold" style="normal" weight="700">NotoSansMyanmarUI-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,6-7,32,37,253-254">
-        <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
+        <font font_name="Noto Sans Thaana" postscript_name="NotoSansThaana" style="normal" weight="400">NotoSansThaana-Regular.ttf</font>
+        <font font_name="Noto Sans Thaana Bold" postscript_name="NotoSansThaana-Bold" style="normal" weight="700">NotoSansThaana-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,3,170">
-        <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
+        <font font_name="Noto Sans Cham" postscript_name="NotoSansCham" style="normal" weight="400">NotoSansCham-Regular.ttf</font>
+        <font font_name="Noto Sans Cham Bold" postscript_name="NotoSansCham-Bold" style="normal" weight="700">NotoSansCham-Bold.ttf</font>
     </family>
     <family fallback="true" pages="0,27,32,37,254">
-        <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
+        <font font_name="Noto Sans Balinese" postscript_name="NotoSansBalinese" style="normal" weight="400">NotoSansBalinese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,166,254,360-362">
-        <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+        <font font_name="Noto Sans Bamum" postscript_name="NotoSansBamum" style="normal" weight="400">NotoSansBamum-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,27,254">
-        <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
+        <font font_name="Noto Sans Batak" postscript_name="NotoSansBatak" style="normal" weight="400">NotoSansBatak-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,26,32,169,254">
-        <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
+        <font font_name="Noto Sans Buginese" postscript_name="NotoSansBuginese" style="normal" weight="400">NotoSansBuginese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
+        <font font_name="Noto Sans Buhid" postscript_name="NotoSansBuhid" style="normal" weight="400">NotoSansBuhid-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0-3,20-22,24,254">
-        <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+        <font font_name="Noto Sans Canadian Aboriginal" postscript_name="NotoSansCanadianAboriginal" style="normal" weight="400">NotoSansCanadianAboriginal-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,19,254">
-        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+        <font font_name="Noto Sans Cherokee" postscript_name="NotoSansCherokee" style="normal" weight="400">NotoSansCherokee-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0-3,29,44,254">
-        <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
+        <font font_name="Noto Sans Coptic" postscript_name="NotoSansCoptic" style="normal" weight="400">NotoSansCoptic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,44,254">
-        <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
+        <font font_name="Noto Sans Glagolitic" postscript_name="NotoSansGlagolitic" style="normal" weight="400">NotoSansGlagolitic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
+        <font font_name="Noto Sans Hanunoo" postscript_name="NotoSansHanunoo" style="normal" weight="400">NotoSansHanunoo-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,32,37,169,254">
-        <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
+        <font font_name="Noto Sans Javanese" postscript_name="NotoSansJavanese" style="normal" weight="400">NotoSansJavanese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,169,254">
-        <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
+        <font font_name="Noto Sans Kayah Li" postscript_name="NotoSansKayahLi" style="normal" weight="400">NotoSansKayahLi-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,28,37,254">
-        <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
+        <font font_name="Noto Sans Lepcha" postscript_name="NotoSansLepcha" style="normal" weight="400">NotoSansLepcha-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,25,254">
-        <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
+        <font font_name="Noto Sans Limbu" postscript_name="NotoSansLimbu" style="normal" weight="400">NotoSansLimbu-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,2,164,254">
-        <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+        <font font_name="Noto Sans Lisu" postscript_name="NotoSansLisu" style="normal" weight="400">NotoSansLisu-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,6,8,254">
-        <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+        <font font_name="Noto Sans Mandaic" postscript_name="NotoSansMandaic" style="normal" weight="400">NotoSansMandaic-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,170-171,254">
-        <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
+        <font font_name="Noto Sans Meetei Mayek" postscript_name="NotoSansMeeteiMayek" style="normal" weight="400">NotoSansMeeteiMayek-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,25,254">
-        <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+        <font font_name="Noto Sans New Tai Lue" postscript_name="NotoSansNewTaiLue" style="normal" weight="400">NotoSansNewTaiLue-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,6-7,32,46,253-254">
-        <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+        <font font_name="Noto Sans NKo" postscript_name="NotoSansNKo" style="normal" weight="400">NotoSansNKo-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,28,254">
-        <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
+        <font font_name="Noto Sans Ol Chiki" postscript_name="NotoSansOlChiki" style="normal" weight="400">NotoSansOlChiki-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,169,254">
-        <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
+        <font font_name="Noto Sans Rejang" postscript_name="NotoSansRejang" style="normal" weight="400">NotoSansRejang-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,32,37,168,254">
-        <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
+        <font font_name="Noto Sans Saurashtra" postscript_name="NotoSansSaurashtra" style="normal" weight="400">NotoSansSaurashtra-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,27-28,254">
-        <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
+        <font font_name="Noto Sans Sundanese" postscript_name="NotoSansSundanese" style="normal" weight="400">NotoSansSundanese-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,9,32,37,168,254">
-        <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
+        <font font_name="Noto Sans Syloti Nagri" postscript_name="NotoSansSylotiNagri" style="normal" weight="400">NotoSansSylotiNagri-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,6-7,32,34,37-38,254">
-        <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+        <font font_name="Noto Sans Syriac Estrangela" postscript_name="NotoSansSyriacEstrangela" style="normal" weight="400">NotoSansSyriacEstrangela-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,23,254">
-        <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
+        <font font_name="Noto Sans Tagbanwa" postscript_name="NotoSansTagbanwa" style="normal" weight="400">NotoSansTagbanwa-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,26,32,34,37,254">
-        <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
+        <font font_name="Noto Sans Tai Tham" postscript_name="NotoSansTaiTham" style="normal" weight="400">NotoSansTaiTham-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,32,37,167,170,254">
-        <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
+        <font font_name="Noto Sans Tai Viet" postscript_name="NotoSansTaiViet" style="normal" weight="400">NotoSansTaiViet-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,15,32,37,254">
-        <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
+        <font font_name="Noto Sans Tibetan" postscript_name="NotoSansTibetan" style="normal" weight="400">NotoSansTibetan-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,3,32,45,254">
-        <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
+        <font font_name="Noto Sans Tifinagh" postscript_name="NotoSansTifinagh" style="normal" weight="400">NotoSansTifinagh-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,165-166,254">
-        <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+        <font font_name="Noto Sans Vai" postscript_name="NotoSansVai" style="normal" weight="400">NotoSansVai-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,160-164,254">
-        <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+        <font font_name="Noto Sans Yi" postscript_name="NotoSansYi" style="normal" weight="400">NotoSansYi-Regular.ttf</font>
     </family>
     <family fallback="true" pages="32-43">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+        <font font_name="Noto Sans Symbols" postscript_name="NotoSansSymbols" style="normal" weight="400">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family fallback="true" lang="zh-Hans" pages="0,2,32-39,41,43,46-159,249-250,254-255,497-498,512-513,518,524,531-533,553,565,572,574,577,584-586,597,602,606,610,612,614-615,617,619-620,632,639,644,646-647,651-652,654,662,664,671,679-682,687,689,691-696,698-702,704-718">
-        <font weight="400" style="normal">NotoSansSC-Regular.otf</font>
+        <font font_name="Noto Sans SC Regular" postscript_name="NotoSansSC-Regular" style="normal" weight="400">NotoSansSC-Regular.otf</font>
     </family>
     <family fallback="true" lang="zh-Hant" pages="0,32,34,46-48,50,52-159,249-250,254-255,498,512-657,660-661,663-678,685,760-761">
-        <font weight="400" style="normal">NotoSansTC-Regular.otf</font>
+        <font font_name="Noto Sans TC Regular" postscript_name="NotoSansTC-Regular" style="normal" weight="400">NotoSansTC-Regular.otf</font>
     </family>
     <family fallback="true" lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
-        <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
+        <font font_name="Noto Sans JP Regular" postscript_name="NotoSansJP-Regular" style="normal" weight="400">NotoSansJP-Regular.otf</font>
     </family>
     <family fallback="true" lang="ko" pages="0,17,32,48-50,169,172-215,255">
-        <font weight="400" style="normal">NotoSansKR-Regular.otf</font>
+        <font font_name="Noto Sans KR Regular" postscript_name="NotoSansKR-Regular" style="normal" weight="400">NotoSansKR-Regular.otf</font>
     </family>
     <family fallback="true" pages="0,17,32,49-50,172-215">
-        <font weight="400" style="normal">NanumGothic.ttf</font>
+        <font font_name="NanumGothic" postscript_name="NanumGothic" style="normal" weight="400">NanumGothic.ttf</font>
     </family>
     <family fallback="true" pages="0,32-33,35-39,41,43,48,50,224,254-255,496-502,4068">
-        <font weight="400" style="normal">NotoEmoji-Regular.ttf</font>
+        <font font_name="Noto Emoji" postscript_name="NotoEmoji" style="normal" weight="400">NotoEmoji-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,14,17,32,48-51,77-159,172-215,249-250,254-255,260">
-        <font weight="400" style="normal">DroidSansFallback.ttf</font>
+        <font font_name="Droid Sans Fallback" postscript_name="DroidSansFallback" style="normal" weight="400">DroidSansFallback.ttf</font>
     </family>
     <family fallback="true" lang="ja" pages="0,2-4,32-38,48,50-51,78-159,249-250,255">
-        <font weight="400" style="normal">MTLmr3m.ttf</font>
+        <font font_name="MotoyaLMaru W3 mono" postscript_name="MotoyaLMaru-W3-90ms-RKSJ-H" style="normal" weight="400">MTLmr3m.ttf</font>
     </family>
     <!--
         Tai Le and Mongolian are intentionally kept last, to make sure they don't override
         the East Asian punctuation for Chinese.
     -->
     <family fallback="true" pages="0,16,25,48,254">
-        <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
+        <font font_name="Noto Sans Tai Le" postscript_name="NotoSansTaiLe" style="normal" weight="400">NotoSansTaiLe-Regular.ttf</font>
     </family>
     <family fallback="true" pages="0,24,32,36-37,48,254">
-        <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+        <font font_name="Noto Sans Mongolian" postscript_name="NotoSansMongolian" style="normal" weight="400">NotoSansMongolian-Regular.ttf</font>
     </family>
 </familyset>
diff --git a/src/cobalt/css_parser/grammar.h b/src/cobalt/css_parser/grammar.h
index 6842105..2e21c88 100644
--- a/src/cobalt/css_parser/grammar.h
+++ b/src/cobalt/css_parser/grammar.h
@@ -105,7 +105,7 @@
 
 #if defined(OS_STARBOARD)
 #include "starboard/memory.h"
-#define YYFREE SbMemoryFree
+#define YYFREE SbMemoryDeallocate
 #define YYMALLOC SbMemoryAllocate
 #endif
 
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index a1b62f7..2b32f70 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -3431,15 +3431,17 @@
 
 comma_separated_box_shadow_list:
     validated_box_shadow_list {
-    if ($1) {
+    scoped_refptr<cssom::PropertyValue> shadow = MakeScopedRefPtrAndRelease($1);
+    if (shadow) {
       $$ = new cssom::PropertyListValue::Builder();
-      $$->push_back(MakeScopedRefPtrAndRelease($1));
+      $$->push_back(shadow);
     }
   }
   | comma_separated_box_shadow_list comma validated_box_shadow_list {
     $$ = $1;
-    if ($$ && $3) {
-      $$->push_back(MakeScopedRefPtrAndRelease($3));
+    scoped_refptr<cssom::PropertyValue> shadow = MakeScopedRefPtrAndRelease($3);
+    if ($$ && shadow) {
+      $$->push_back(shadow);
     }
   }
   ;
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index 85fe7c4..fe7b721 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -7975,7 +7975,7 @@
 
 TEST_F(ParserTest, ParsesFontFaceSrcLocalString) {
   scoped_refptr<cssom::CSSFontFaceDeclarationData> font_face =
-      parser_.ParseFontFaceDeclarationList("src: local('Roboto');",
+      parser_.ParseFontFaceDeclarationList("src: local('Roboto Regular');",
                                            source_location_);
 
   scoped_refptr<cssom::PropertyListValue> src_list =
@@ -7987,12 +7987,12 @@
       dynamic_cast<cssom::LocalSrcValue*>(
           src_list->get_item_modulo_size(0).get());
   ASSERT_TRUE(local_src);
-  EXPECT_EQ("Roboto", local_src->value());
+  EXPECT_EQ("Roboto Regular", local_src->value());
 }
 
 TEST_F(ParserTest, ParsesFontFaceSrcLocalIdentifier) {
   scoped_refptr<cssom::CSSFontFaceDeclarationData> font_face =
-      parser_.ParseFontFaceDeclarationList("src: local(Roboto);",
+      parser_.ParseFontFaceDeclarationList("src: local(Roboto Regular);",
                                            source_location_);
 
   scoped_refptr<cssom::PropertyListValue> src_list =
@@ -8004,7 +8004,7 @@
       dynamic_cast<cssom::LocalSrcValue*>(
           src_list->get_item_modulo_size(0).get());
   ASSERT_TRUE(local_src);
-  EXPECT_EQ("Roboto", local_src->value());
+  EXPECT_EQ("Roboto Regular", local_src->value());
 }
 
 TEST_F(ParserTest, ParsesFontFaceSrcUrlWithoutFormat) {
diff --git a/src/cobalt/dom/MemoryInfo.idl b/src/cobalt/dom/MemoryInfo.idl
new file mode 100644
index 0000000..4cf1920
--- /dev/null
+++ b/src/cobalt/dom/MemoryInfo.idl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This is a non-standard interface in Chromium.
+// https://docs.webplatform.org/wiki/apis/timing/properties/memory
+
+[
+  NoInterfaceObject,
+] interface MemoryInfo {
+  [CallWith=EnvironmentSettings] readonly attribute unsigned long totalJSHeapSize;
+  [CallWith=EnvironmentSettings] readonly attribute unsigned long usedJSHeapSize;
+};
diff --git a/src/cobalt/dom/Performance.idl b/src/cobalt/dom/Performance.idl
index 891adc3..45a0e2f 100644
--- a/src/cobalt/dom/Performance.idl
+++ b/src/cobalt/dom/Performance.idl
@@ -18,4 +18,7 @@
 
 interface Performance {
   readonly attribute PerformanceTiming timing;
+
+  // Chromium custom member.
+  readonly attribute MemoryInfo memory;
 };
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 5438684..6087406 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -175,6 +175,8 @@
         'media_query_list.h',
         'media_source.cc',
         'media_source.h',
+        'memory_info.cc',
+        'memory_info.h',
         'mime_type_array.cc',
         'mime_type_array.h',
         'named_node_map.cc',
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index a399a4e..281ac59 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -41,6 +41,7 @@
         'event_test.cc',
         'float32_array_test.cc',
         'float64_array_test.cc',
+        'font_cache_test.cc',
         'html_element_factory_test.cc',
         'html_element_test.cc',
         'keyboard_event_test.cc',
@@ -70,6 +71,7 @@
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
         '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
+        '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
       ],
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index 0ee045c..9c9750e 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -155,7 +155,7 @@
         return TryGetRemoteFont(source_iterator->GetUrl(), size, state);
       } else {
         scoped_refptr<render_tree::Font> font =
-            TryGetLocalFont(source_iterator->GetName(), style, size, state);
+            TryGetLocalFontByFaceName(source_iterator->GetName(), size, state);
         if (font != NULL) {
           return font;
         }
@@ -283,6 +283,7 @@
 
 const scoped_refptr<render_tree::Typeface>& FontCache::GetCachedLocalTypeface(
     const scoped_refptr<render_tree::Typeface>& typeface) {
+  DCHECK(typeface);
   // Check to see if a typeface with a matching id is already in the cache. If
   // it is not, then add the passed in typeface to the cache.
   scoped_refptr<render_tree::Typeface>& cached_typeface =
@@ -308,6 +309,7 @@
   // completed or the timer expires.
   if (requested_remote_typeface_iterator ==
       requested_remote_typeface_cache_.end()) {
+    DLOG(INFO) << "Requested remote font from " << url;
     // Create the remote typeface load event's callback. This callback occurs on
     // successful loads, failed loads, and when the request's timer expires.
     base::Closure typeface_load_event_callback = base::Bind(
@@ -317,7 +319,7 @@
     // the iterator from the return value of the map insertion.
     requested_remote_typeface_iterator =
         requested_remote_typeface_cache_
-            .insert(std::make_pair(
+            .insert(RequestedRemoteTypefaceMap::value_type(
                 url, new RequestedRemoteTypefaceInfo(
                          cached_remote_typeface, typeface_load_event_callback)))
             .first;
@@ -346,6 +348,7 @@
     const std::string& family, render_tree::FontStyle style, float size,
     FontListFont::State* state) {
   DCHECK(resource_provider());
+  DCHECK(resource_provider() != NULL);
   // Only request the local font from the resource provider if the family is
   // empty or the resource provider actually has the family. The reason for this
   // is that the resource provider's |GetLocalTypeface()| is guaranteed to
@@ -365,6 +368,28 @@
   }
 }
 
+scoped_refptr<render_tree::Font> FontCache::TryGetLocalFontByFaceName(
+    const std::string& font_face, float size, FontListFont::State* state) {
+  do {
+    if (font_face.empty()) {
+      break;
+    }
+    const scoped_refptr<render_tree::Typeface>& typeface(
+        resource_provider()->GetLocalTypefaceByFaceNameIfAvailable(font_face));
+    if (!typeface) {
+      break;
+    }
+    const scoped_refptr<render_tree::Typeface>& typeface_cached(
+        GetCachedLocalTypeface(typeface));
+
+    *state = FontListFont::kLoadedState;
+    return GetFontFromTypefaceAndSize(typeface_cached, size);
+  } while (false);
+
+  *state = FontListFont::kUnavailableState;
+  return NULL;
+}
+
 void FontCache::OnRemoteTypefaceLoadEvent(const GURL& url) {
   DCHECK(thread_checker_.CalledOnValidThread());
   RequestedRemoteTypefaceMap::iterator requested_remote_typeface_iterator =
diff --git a/src/cobalt/dom/font_cache.h b/src/cobalt/dom/font_cache.h
index e3a574a..8e5afc5 100644
--- a/src/cobalt/dom/font_cache.h
+++ b/src/cobalt/dom/font_cache.h
@@ -254,19 +254,22 @@
   // the constructor and an |OnRemoteFontLoadEvent| callback provided by the
   // font are registered with the remote typeface cache to be called when the
   // load finishes.
-  // If the font is loading but not currently available, |maybe_is_font_loading|
-  // will be set to true.
   scoped_refptr<render_tree::Font> TryGetRemoteFont(const GURL& url, float size,
                                                     FontListFont::State* state);
 
   // Returns NULL if the requested family is not empty and is not available in
   // the resource provider. Otherwise, returns the best matching local font.
-  // |maybe_is_font_loading| is always set to false.
   scoped_refptr<render_tree::Font> TryGetLocalFont(const std::string& family,
                                                    render_tree::FontStyle style,
                                                    float size,
                                                    FontListFont::State* state);
 
+  // Lookup by a typeface (aka font_face), typeface is defined as font family +
+  // style (weight, width, and style).
+  // Returns NULL if the requested font face is not found.
+  scoped_refptr<render_tree::Font> TryGetLocalFontByFaceName(
+      const std::string& font_face, float size, FontListFont::State* state);
+
   // Called when a remote typeface either successfully loads or fails to load.
   // In either case, the event can impact the fonts contained within the font
   // lists. As a result, the font lists need to have their loading fonts reset
diff --git a/src/cobalt/dom/font_cache_test.cc b/src/cobalt/dom/font_cache_test.cc
new file mode 100644
index 0000000..a46b631
--- /dev/null
+++ b/src/cobalt/dom/font_cache_test.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/dom/font_cache.h"
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/csp/content_security_policy.h"
+#include "cobalt/dom/font_face.h"
+#include "cobalt/loader/font/remote_typeface_cache.h"
+#include "cobalt/loader/font/typeface_decoder.h"
+#include "cobalt/loader/loader.h"
+#include "cobalt/loader/mock_loader_factory.h"
+#include "cobalt/render_tree/mock_resource_provider.h"
+#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/render_tree/resource_provider_stub.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+using ::testing::_;
+using ::testing::Return;
+
+const render_tree::FontStyle kNormalUpright(
+    render_tree::FontStyle::kNormalWeight,
+    render_tree::FontStyle::kUprightSlant);
+
+scoped_ptr<FontCache::FontFaceMap> CreateFontFaceMapHelper(
+    const std::string& family_name, const base::StringPiece local_font_name) {
+  scoped_ptr<FontCache::FontFaceMap> ffm(new FontCache::FontFaceMap());
+  dom::FontFaceStyleSet ffss;
+  dom::FontFaceStyleSet::Entry entry;
+  entry.sources.push_back(
+      FontFaceSource(local_font_name.as_string()));  // local()
+  entry.sources.push_back(FontFaceSource(
+      GURL("https://example.com/Dancing-Regular.woff")));  // url()
+  ffss.AddEntry(entry);
+  ffm->insert(FontCache::FontFaceMap::value_type(family_name, ffss));
+  return ffm.Pass();
+}
+
+class FontCacheTest : public ::testing::Test {
+ public:
+  FontCacheTest();
+  ~FontCacheTest() OVERRIDE {}
+
+  void DummyOnTypefaceLoadEvent() {}
+
+ protected:
+  ::testing::StrictMock<loader::MockLoaderFactory> loader_factory_;
+  scoped_refptr<cobalt::render_tree::Typeface> sample_typeface_;
+  ::testing::StrictMock<cobalt::render_tree::MockResourceProvider>
+      mock_resource_provider_;
+  cobalt::render_tree::ResourceProvider* mrp;
+  scoped_ptr<loader::font::RemoteTypefaceCache> rtc;
+  scoped_ptr<dom::FontCache> font_cache_;
+
+  MessageLoop message_loop_;
+};
+
+FontCacheTest::FontCacheTest()
+    : sample_typeface_(new render_tree::TypefaceStub(NULL)),
+      mrp(dynamic_cast<cobalt::render_tree::ResourceProvider*>(
+          &mock_resource_provider_)),
+      rtc(new loader::font::RemoteTypefaceCache(
+          "test_cache", 32 * 1024 /* 32 KB */,
+          base::Bind(&loader::MockLoaderFactory::CreateTypefaceLoader,
+                     base::Unretained(&loader_factory_)))),
+      font_cache_(
+          new dom::FontCache(&mrp,       // resource_provider
+                             rtc.get(),  // remotetypefacecache
+                             ALLOW_THIS_IN_INITIALIZER_LIST(base::Bind(
+                                 &FontCacheTest::DummyOnTypefaceLoadEvent,
+                                 base::Unretained(this))),
+                             "en-US")) {}
+
+TEST_F(FontCacheTest, FindPostscriptFont) {
+  const std::string family_name("Dancing Script");
+  const std::string postscript_font_name("DancingScript");
+  scoped_ptr<FontCache::FontFaceMap> ffm =
+      CreateFontFaceMapHelper(family_name, postscript_font_name);
+  font_cache_->SetFontFaceMap(ffm.Pass());
+
+  EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _))
+      .Times(0);
+
+  EXPECT_CALL(mock_resource_provider_,
+              GetLocalTypefaceIfAvailableMock(postscript_font_name))
+      .WillOnce(Return(sample_typeface_));
+
+  FontListFont::State state;
+  scoped_refptr<render_tree::Font> f =
+      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
+
+  EXPECT_TRUE(f);
+}
+
+TEST_F(FontCacheTest, UseRemote) {
+  std::string invalid_postscript_font_name = "DancingScriptInvalidName";
+  const std::string family_name("Dancing Script");
+  scoped_ptr<FontCache::FontFaceMap> ffm =
+      CreateFontFaceMapHelper(family_name, invalid_postscript_font_name);
+  font_cache_->SetFontFaceMap(ffm.Pass());
+
+  EXPECT_CALL(mock_resource_provider_,
+              GetLocalTypefaceIfAvailableMock(invalid_postscript_font_name))
+      .Times(1);
+  EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _));
+
+  FontListFont::State state;
+  scoped_refptr<render_tree::Font> f =
+      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
+  EXPECT_FALSE(f);
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/font_list.cc b/src/cobalt/dom/font_list.cc
index f91dc45..69fde52 100644
--- a/src/cobalt/dom/font_list.cc
+++ b/src/cobalt/dom/font_list.cc
@@ -240,6 +240,7 @@
       }
     }
   }
+  DCHECK(primary_font_);
 
   return primary_font_;
 }
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 06e16c02..fbd71cd 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -664,13 +664,13 @@
 
 void HTMLElement::InvalidateLayoutBoxesFromNodeAndAncestors() {
   layout_boxes_.reset();
-  cached_background_images_.clear();
+  ReleaseImagesAndInvalidateComputedStyleIfNecessary();
   Node::InvalidateLayoutBoxesFromNodeAndAncestors();
 }
 
 void HTMLElement::InvalidateLayoutBoxesFromNodeAndDescendants() {
   layout_boxes_.reset();
-  cached_background_images_.clear();
+  ReleaseImagesAndInvalidateComputedStyleIfNecessary();
   Node::InvalidateLayoutBoxesFromNodeAndDescendants();
 }
 
@@ -1083,5 +1083,12 @@
   return AsHTMLHtmlElement() != NULL;
 }
 
+void HTMLElement::ReleaseImagesAndInvalidateComputedStyleIfNecessary() {
+  if (!cached_background_images_.empty()) {
+    cached_background_images_.clear();
+    computed_style_valid_ = false;
+  }
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 2075d05..33ffb9a 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -289,6 +289,10 @@
   // https://www.w3.org/TR/html5/semantics.html#the-root-element.
   bool IsRootElement();
 
+  // Releases image resources and invalidates computed style if there are images
+  // associated with this html element in the image cache.
+  void ReleaseImagesAndInvalidateComputedStyleIfNecessary();
+
   // The directionality of the html element is determined by the 'dir'
   // attribute.
   // https://dev.w3.org/html5/spec-preview/global-attributes.html#the-directionality
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index e0f632c..eda59cb 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -53,6 +53,33 @@
 
 namespace {
 
+#define LOG_MEDIA_ELEMENT_ACTIVITIES 0
+
+#if LOG_MEDIA_ELEMENT_ACTIVITIES
+
+template <typename T>
+void LogMediaElementActivity(const char* function_name, const T& t) {
+  LOG(INFO) << function_name << " " << t;
+}
+
+template <typename T1, typename T2>
+void LogMediaElementActivity(const char* function_name, const T1& t1,
+                             const T2& t2) {
+  LOG(INFO) << function_name << " " << t1 << " " << t2;
+}
+
+#define MLOG() LOG(INFO) << __FUNCTION__
+#define MLOG_1(x) LogMediaElementActivity(__FUNCTION__, x)
+#define MLOG_2(x, y) LogMediaElementActivity(__FUNCTION__, x, y)
+
+#else  // LOG_MEDIA_ELEMENT_ACTIVITIES
+
+#define MLOG() do {} while (false)
+#define MLOG_1(x) do {} while (false)
+#define MLOG_2(x, y) do {} while (false)
+
+#endif  // LOG_MEDIA_ELEMENT_ACTIVITIES
+
 void RaiseMediaKeyException(WebMediaPlayer::MediaKeyException exception,
                             script::ExceptionState* exception_state) {
   DCHECK_NE(exception, WebMediaPlayer::kMediaKeyExceptionNoError);
@@ -119,25 +146,35 @@
       pending_load_(false),
       sent_stalled_event_(false),
       sent_end_event_(false) {
+  MLOG();
   html_media_element_count_log.Get().count++;
 }
 
 HTMLMediaElement::~HTMLMediaElement() {
+  MLOG();
   SetSourceState(MediaSource::kReadyStateClosed);
   html_media_element_count_log.Get().count--;
 }
 
+scoped_refptr<MediaError> HTMLMediaElement::error() const {
+  MLOG_1(error_->code());
+  return error_;
+}
+
 std::string HTMLMediaElement::src() const {
+  MLOG_1(GetAttribute("src").value_or(""));
   return GetAttribute("src").value_or("");
 }
 
 void HTMLMediaElement::set_src(const std::string& src) {
+  MLOG_1(src);
   SetAttribute("src", src);
   ClearMediaPlayer();
   ScheduleLoad();
 }
 
 uint16_t HTMLMediaElement::network_state() const {
+  MLOG_1(network_state_);
   return static_cast<uint16_t>(network_state_);
 }
 
@@ -145,13 +182,17 @@
   scoped_refptr<TimeRanges> buffered = new TimeRanges;
 
   if (!player_) {
+    MLOG_1("empty");
     return buffered;
   }
 
   const ::media::Ranges<base::TimeDelta>& player_buffered =
       player_->GetBufferedTimeRanges();
 
+  MLOG_1("================================");
   for (int i = 0; i < static_cast<int>(player_buffered.size()); ++i) {
+    MLOG_2(player_buffered.start(i).InSecondsF(),
+           player_buffered.end(i).InSecondsF());
     buffered->Add(player_buffered.start(i).InSecondsF(),
                   player_buffered.end(i).InSecondsF());
   }
@@ -177,6 +218,7 @@
   std::string result =
       html_element_context()->can_play_type_handler()->CanPlayType(mime_type,
                                                                    key_system);
+  MLOG_2(mime_type + ',' + key_system, result);
   DLOG(INFO) << "HTMLMediaElement::canPlayType(" << mime_type << ", "
              << key_system << ") -> " << result;
   return result;
@@ -186,15 +228,18 @@
     const std::string& key_system,
     const base::optional<scoped_refptr<Uint8Array> >& init_data,
     script::ExceptionState* exception_state) {
+  MLOG_1(key_system);
   // https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-generatekeyrequest
   // 1. If the first argument is null, throw a SYNTAX_ERR.
   if (key_system.empty()) {
+    MLOG_1("syntax error");
     DOMException::Raise(DOMException::kSyntaxErr, exception_state);
     return;
   }
 
   // 2. If networkState is NETWORK_EMPTY, throw an INVALID_STATE_ERR.
   if (network_state_ == kNetworkEmpty || !player_) {
+    MLOG_1("invalid state error");
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
@@ -211,6 +256,7 @@
   }
 
   if (exception != WebMediaPlayer::kMediaKeyExceptionNoError) {
+    MLOG_2("exception:", exception);
     RaiseMediaKeyException(exception, exception_state);
   }
 }
@@ -220,21 +266,25 @@
     const base::optional<scoped_refptr<Uint8Array> >& init_data,
     const base::optional<std::string>& session_id,
     script::ExceptionState* exception_state) {
+  MLOG_1(key_system);
   // https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-addkey
   // 1. If the first or second argument is null, throw a SYNTAX_ERR.
   if (key_system.empty() || !key) {
+    MLOG_1("syntax error");
     DOMException::Raise(DOMException::kSyntaxErr, exception_state);
     return;
   }
 
   // 2. If the second argument is an empty array, throw a TYPE_MISMATCH_ERR.
   if (!key->length()) {
+    MLOG_1("type mismatch error");
     DOMException::Raise(DOMException::kTypeMismatchErr, exception_state);
     return;
   }
 
   // 3. If networkState is NETWORK_EMPTY, throw an INVALID_STATE_ERR.
   if (network_state_ == kNetworkEmpty || !player_) {
+    MLOG_1("invalid state error");
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
@@ -253,6 +303,7 @@
   }
 
   if (exception != WebMediaPlayer::kMediaKeyExceptionNoError) {
+    MLOG_2("exception:", exception);
     RaiseMediaKeyException(exception, exception_state);
   }
 }
@@ -264,11 +315,13 @@
   // https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-addkey
   // 1. If the first argument is null, throw a SYNTAX_ERR.
   if (key_system.empty()) {
+    MLOG_1("syntax error");
     DOMException::Raise(DOMException::kSyntaxErr, exception_state);
     return;
   }
 
   if (!player_) {
+    MLOG_1("invalid state error");
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
@@ -277,28 +330,36 @@
   WebMediaPlayer::MediaKeyException exception =
       player_->CancelKeyRequest(key_system, session_id.value_or(""));
   if (exception != WebMediaPlayer::kMediaKeyExceptionNoError) {
+    MLOG_2("exception:", exception);
     RaiseMediaKeyException(exception, exception_state);
   }
 }
 
 WebMediaPlayer::ReadyState HTMLMediaElement::ready_state() const {
+  MLOG_1(ready_state_);
   return ready_state_;
 }
 
-bool HTMLMediaElement::seeking() const { return seeking_; }
+bool HTMLMediaElement::seeking() const {
+  MLOG_1(seeking_);
+  return seeking_;
+}
 
 float HTMLMediaElement::current_time(
     script::ExceptionState* exception_state) const {
   UNREFERENCED_PARAMETER(exception_state);
 
   if (!player_) {
+    MLOG_2("player is NULL", 0);
     return 0;
   }
 
   if (seeking_) {
+    MLOG_2("seeking", last_seek_time_);
     return last_seek_time_;
   }
 
+  MLOG_2("player time", player_->GetCurrentTime());
   return player_->GetCurrentTime();
 }
 
@@ -310,36 +371,49 @@
   // WebMediaPlayer::kReadyStateHaveNothing, then raise an INVALID_STATE_ERR
   // exception.
   if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing || !player_) {
+    MLOG_1("invalid state error");
     DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
     return;
   }
+  MLOG_2("seek to", time);
   Seek(time);
 }
 
 float HTMLMediaElement::duration() const {
   if (player_ && ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata) {
+    MLOG_2("player duration", player_->GetDuration());
     return player_->GetDuration();
   }
 
+  MLOG_1("NaN");
   return std::numeric_limits<float>::quiet_NaN();
 }
 
-bool HTMLMediaElement::paused() const { return paused_; }
+bool HTMLMediaElement::paused() const {
+  MLOG_1(paused_);
+  return paused_;
+}
 
 float HTMLMediaElement::default_playback_rate() const {
+  MLOG_1(default_playback_rate_);
   return default_playback_rate_;
 }
 
 void HTMLMediaElement::set_default_playback_rate(float rate) {
+  MLOG_1(rate);
   if (default_playback_rate_ != rate) {
     default_playback_rate_ = rate;
     ScheduleEvent(base::Tokens::ratechange());
   }
 }
 
-float HTMLMediaElement::playback_rate() const { return playback_rate_; }
+float HTMLMediaElement::playback_rate() const {
+  MLOG_1(playback_rate_);
+  return playback_rate_;
+}
 
 void HTMLMediaElement::set_playback_rate(float rate) {
+  MLOG_1(rate);
   if (playback_rate_ != rate) {
     playback_rate_ = rate;
     ScheduleEvent(base::Tokens::ratechange());
@@ -351,6 +425,7 @@
 }
 
 const scoped_refptr<TimeRanges>& HTMLMediaElement::played() {
+  MLOG();
   if (playing_) {
     float time = current_time(NULL);
     if (time > last_seek_time_) {
@@ -367,8 +442,10 @@
 
 scoped_refptr<TimeRanges> HTMLMediaElement::seekable() const {
   if (player_ && player_->GetMaxTimeSeekable() != 0) {
+    MLOG_2(0, player_->GetMaxTimeSeekable());
     return new TimeRanges(0, player_->GetMaxTimeSeekable());
   }
+  MLOG_1("empty");
   return new TimeRanges;
 }
 
@@ -376,12 +453,17 @@
   // 4.8.10.8 Playing the media resource
   // The ended attribute must return true if the media element has ended
   // playback and the direction of playback is forwards, and false otherwise.
+  MLOG_1(EndedPlayback() && playback_rate_ > 0);
   return EndedPlayback() && playback_rate_ > 0;
 }
 
-bool HTMLMediaElement::autoplay() const { return HasAttribute("autoplay"); }
+bool HTMLMediaElement::autoplay() const {
+  MLOG_1(HasAttribute("autoplay"));
+  return HasAttribute("autoplay");
+}
 
 void HTMLMediaElement::set_autoplay(bool autoplay) {
+  MLOG_1(autoplay);
   // The value of 'autoplay' is true when the 'autoplay' attribute is present.
   // The value of the attribute is irrelevant.
   if (autoplay) {
@@ -391,11 +473,18 @@
   }
 }
 
-bool HTMLMediaElement::loop() const { return loop_; }
+bool HTMLMediaElement::loop() const {
+  MLOG_1(loop_);
+  return loop_;
+}
 
-void HTMLMediaElement::set_loop(bool loop) { loop_ = loop; }
+void HTMLMediaElement::set_loop(bool loop) {
+  MLOG_1(loop);
+  loop_ = loop;
+}
 
 void HTMLMediaElement::Play() {
+  MLOG();
   // 4.8.10.9. Playing the media resource
   if (!player_ || network_state_ == kNetworkEmpty) {
     ScheduleLoad();
@@ -421,6 +510,7 @@
 }
 
 void HTMLMediaElement::Pause() {
+  MLOG();
   // 4.8.10.9. Playing the media resource
   if (!player_ || network_state_ == kNetworkEmpty) {
     ScheduleLoad();
@@ -437,36 +527,45 @@
   UpdatePlayState();
 }
 
-bool HTMLMediaElement::controls() const { return controls_; }
+bool HTMLMediaElement::controls() const {
+  MLOG_1(controls_);
+  return controls_;
+}
 
 void HTMLMediaElement::set_controls(bool controls) {
+  MLOG_1(controls);
   controls_ = controls;
   ConfigureMediaControls();
 }
 
 float HTMLMediaElement::volume(script::ExceptionState* exception_state) const {
   UNREFERENCED_PARAMETER(exception_state);
-
+  MLOG_1(volume_);
   return volume_;
 }
 
-void HTMLMediaElement::set_volume(float vol,
+void HTMLMediaElement::set_volume(float volume,
                                   script::ExceptionState* exception_state) {
-  if (vol < 0.0f || vol > 1.0f) {
+  MLOG_1(volume);
+  if (volume < 0.0f || volume > 1.0f) {
     DOMException::Raise(DOMException::kIndexSizeErr, exception_state);
     return;
   }
 
-  if (volume_ != vol) {
-    volume_ = vol;
+  if (volume_ != volume) {
+    volume_ = volume;
     UpdateVolume();
     ScheduleEvent(base::Tokens::volumechange());
   }
 }
 
-bool HTMLMediaElement::muted() const { return muted_; }
+bool HTMLMediaElement::muted() const {
+  MLOG_1(muted_);
+  return muted_;
+}
 
 void HTMLMediaElement::set_muted(bool muted) {
+  MLOG_1(muted);
   if (muted_ != muted) {
     muted_ = muted;
     // Avoid recursion when the player reports volume changes.
@@ -489,6 +588,7 @@
 }
 
 void HTMLMediaElement::CreateMediaPlayer() {
+  MLOG();
   if (src().empty()) {
     reduced_image_cache_capacity_request_ = base::nullopt;
   } else if (html_element_context()
@@ -726,6 +826,7 @@
 }
 
 void HTMLMediaElement::ClearMediaPlayer() {
+  MLOG();
   SetSourceState(MediaSource::kReadyStateClosed);
 
   player_.reset(NULL);
@@ -744,6 +845,7 @@
 }
 
 void HTMLMediaElement::NoneSupported() {
+  MLOG();
   StopPeriodicTimers();
   load_state_ = kWaitingForSource;
 
@@ -874,6 +976,7 @@
 }
 
 void HTMLMediaElement::ScheduleEvent(base::Token event_name) {
+  MLOG_1(event_name);
   scoped_refptr<Event> event =
       new Event(event_name, Event::kNotBubbles, Event::kCancelable);
   event->set_target(this);
@@ -1227,6 +1330,7 @@
 }
 
 void HTMLMediaElement::MediaEngineError(scoped_refptr<MediaError> error) {
+  MLOG_1(error->code());
   DLOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code();
 
   // 1 - The user agent should cancel the fetching process.
@@ -1370,6 +1474,7 @@
 
 void HTMLMediaElement::KeyAdded(const std::string& key_system,
                                 const std::string& session_id) {
+  MLOG_1(key_system);
   event_queue_.Enqueue(new MediaKeyCompleteEvent(key_system, session_id));
 }
 
@@ -1377,6 +1482,7 @@
                                 const std::string& session_id,
                                 MediaKeyErrorCode error_code,
                                 uint16 system_code) {
+  MLOG_1(key_system);
   MediaKeyError::Code code;
   switch (error_code) {
     case kUnknownError:
@@ -1411,6 +1517,7 @@
                                   const unsigned char* message,
                                   unsigned int message_length,
                                   const std::string& default_url) {
+  MLOG_1(key_system);
   event_queue_.Enqueue(new MediaKeyMessageEvent(
       key_system, session_id,
       new Uint8Array(NULL, message, message_length, NULL), default_url));
@@ -1420,12 +1527,14 @@
                                  const std::string& session_id,
                                  const unsigned char* init_data,
                                  unsigned int init_data_length) {
+  MLOG_1(key_system);
   event_queue_.Enqueue(new MediaKeyNeededEvent(
       key_system, session_id,
       new Uint8Array(NULL, init_data, init_data_length, NULL)));
 }
 
 void HTMLMediaElement::SetSourceState(MediaSource::ReadyState ready_state) {
+  MLOG_1(ready_state);
   if (!media_source_) {
     return;
   }
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index 7bd360e..1d9dfc2 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -53,7 +53,7 @@
   // Web API: HTMLMediaElement
   //
   // Error state
-  scoped_refptr<MediaError> error() const { return error_; }
+  scoped_refptr<MediaError> error() const;
 
   // Network state
   std::string src() const;
diff --git a/src/cobalt/dom/memory_info.cc b/src/cobalt/dom/memory_info.cc
new file mode 100644
index 0000000..75c99f8
--- /dev/null
+++ b/src/cobalt/dom/memory_info.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/dom/memory_info.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/dom_settings.h"
+#include "cobalt/script/javascript_engine.h"
+
+namespace cobalt {
+namespace dom {
+
+uint32 MemoryInfo::total_js_heap_size(
+    script::EnvironmentSettings* settings) const {
+  DOMSettings* dom_settings =
+      base::polymorphic_downcast<dom::DOMSettings*>(settings);
+  size_t total_memory =
+      dom_settings->javascript_engine()->UpdateMemoryStatsAndReturnReserved();
+  return static_cast<uint32>(total_memory);
+}
+
+uint32 MemoryInfo::used_js_heap_size(
+    script::EnvironmentSettings* settings) const {
+  DOMSettings* dom_settings =
+      base::polymorphic_downcast<dom::DOMSettings*>(settings);
+  size_t total_memory =
+      dom_settings->javascript_engine()->UpdateMemoryStatsAndReturnReserved();
+  return static_cast<uint32>(total_memory);
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/memory_info.h b/src/cobalt/dom/memory_info.h
new file mode 100644
index 0000000..dad9d79
--- /dev/null
+++ b/src/cobalt/dom/memory_info.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_DOM_MEMORY_INFO_H_
+#define COBALT_DOM_MEMORY_INFO_H_
+
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// This interface contains information related to JS memory usage.
+// This is a non-standard interface in Chromium.
+//   https://docs.webplatform.org/wiki/apis/timing/properties/memory
+class MemoryInfo : public script::Wrappable {
+ public:
+  MemoryInfo() {}
+
+  uint32 total_js_heap_size(script::EnvironmentSettings* settings) const;
+
+  uint32 used_js_heap_size(script::EnvironmentSettings* settings) const;
+
+  DEFINE_WRAPPABLE_TYPE(MemoryInfo);
+
+ private:
+  ~MemoryInfo() OVERRIDE {}
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryInfo);
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_MEMORY_INFO_H_
diff --git a/src/cobalt/dom/performance.cc b/src/cobalt/dom/performance.cc
index a2a9482..519c47c 100644
--- a/src/cobalt/dom/performance.cc
+++ b/src/cobalt/dom/performance.cc
@@ -17,12 +17,13 @@
 #include "cobalt/dom/performance.h"
 
 #include "base/time.h"
+#include "cobalt/dom/memory_info.h"
 
 namespace cobalt {
 namespace dom {
 
 Performance::Performance(const scoped_refptr<base::Clock>& clock)
-    : timing_(new PerformanceTiming(clock)) {}
+    : timing_(new PerformanceTiming(clock)), memory_(new MemoryInfo()) {}
 
 double Performance::Now() const {
   return timing_->GetNavigationStartClock()->Now().InMillisecondsF();
@@ -32,5 +33,7 @@
 
 scoped_refptr<PerformanceTiming> Performance::timing() const { return timing_; }
 
+scoped_refptr<MemoryInfo> Performance::memory() const { return memory_; }
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/performance.h b/src/cobalt/dom/performance.h
index cb2f0dd..5a382d1 100644
--- a/src/cobalt/dom/performance.h
+++ b/src/cobalt/dom/performance.h
@@ -17,13 +17,15 @@
 #ifndef COBALT_DOM_PERFORMANCE_H_
 #define COBALT_DOM_PERFORMANCE_H_
 
-#include "cobalt/script/wrappable.h"
 #include "cobalt/base/clock.h"
 #include "cobalt/dom/performance_timing.h"
+#include "cobalt/script/wrappable.h"
 
 namespace cobalt {
 namespace dom {
 
+class MemoryInfo;
+
 // Implements the Performance IDL interface, an instance of which is created
 // and owned by the Window object.
 //   https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
@@ -31,17 +33,21 @@
  public:
   explicit Performance(const scoped_refptr<base::Clock>& clock);
 
+  // Web API: Performance
+  scoped_refptr<PerformanceTiming> timing() const;
+  scoped_refptr<MemoryInfo> memory() const;
+
+  // Custom, not in any spec.
   // Returns the time since timing()->navigation_start(), in milliseconds.
   double Now() const;
 
-  scoped_refptr<PerformanceTiming> timing() const;
-
   DEFINE_WRAPPABLE_TYPE(Performance);
 
  private:
   ~Performance() OVERRIDE;
 
   scoped_refptr<PerformanceTiming> timing_;
+  scoped_refptr<MemoryInfo> memory_;
 
   DISALLOW_COPY_AND_ASSIGN(Performance);
 };
diff --git a/src/cobalt/dom/url_utils.cc b/src/cobalt/dom/url_utils.cc
index a31a07f..0ff0097 100644
--- a/src/cobalt/dom/url_utils.cc
+++ b/src/cobalt/dom/url_utils.cc
@@ -89,9 +89,15 @@
   return url_.has_ref() ? "#" + url_.ref() : "";
 }
 
+// Algorithm for set_hash:
+//   https://www.w3.org/TR/2014/WD-url-1-20141209/#dom-urlutils-hash
 void URLUtils::set_hash(const std::string& hash) {
+  // 3. Let input be the given value with a single leading "#" removed, if any.
+  std::string hash_value =
+      !hash.empty() && hash[0] == '#' ? hash.substr(1) : hash;
+
   GURL::Replacements replacements;
-  replacements.SetRefStr(hash);
+  replacements.SetRefStr(hash_value);
   GURL new_url = url_.ReplaceComponents(replacements);
   RunPreUpdateSteps(new_url, "");
 }
@@ -100,9 +106,15 @@
   return url_.has_query() ? "?" + url_.query() : "";
 }
 
+// Algorithm for set_search:
+//   https://www.w3.org/TR/2014/WD-url-1-20141209/#dom-urlutils-search
 void URLUtils::set_search(const std::string& search) {
+  // 3. Let input be the given value with a single leading "?" removed, if any.
+  std::string search_value =
+      !search.empty() && search[0] == '?' ? search.substr(1) : search;
+
   GURL::Replacements replacements;
-  replacements.SetQueryStr(search);
+  replacements.SetQueryStr(search_value);
   GURL new_url = url_.ReplaceComponents(replacements);
   RunPreUpdateSteps(new_url, "");
 }
@@ -111,6 +123,8 @@
 //   https://www.w3.org/TR/2014/WD-url-1-20141209/#pre-update-steps
 void URLUtils::RunPreUpdateSteps(const GURL& new_url,
                                  const std::string& value) {
+  DLOG(INFO) << "Update URL to " << new_url.spec();
+
   // 1. If value is not given, let value be the result of serializing the
   // associated url.
   // 2, Run the update steps with value.
diff --git a/src/cobalt/dom/url_utils_test.cc b/src/cobalt/dom/url_utils_test.cc
index 133b8b9..29c4721 100644
--- a/src/cobalt/dom/url_utils_test.cc
+++ b/src/cobalt/dom/url_utils_test.cc
@@ -92,6 +92,11 @@
   EXPECT_TRUE(url_utils.url().is_valid());
   EXPECT_EQ("https://user:pass@google.com:99/foo;bar?q=a#hash",
             url_utils.href());
+
+  url_utils.set_hash("#hash2");
+  EXPECT_TRUE(url_utils.url().is_valid());
+  EXPECT_EQ("https://user:pass@google.com:99/foo;bar?q=a#hash2",
+            url_utils.href());
 }
 
 TEST(URLUtilsTest, SetSearchShouldWorkAsExpected) {
@@ -100,6 +105,11 @@
   EXPECT_TRUE(url_utils.url().is_valid());
   EXPECT_EQ("https://user:pass@google.com:99/foo;bar?b=c#ref",
             url_utils.href());
+
+  url_utils.set_search("?d=e");
+  EXPECT_TRUE(url_utils.url().is_valid());
+  EXPECT_EQ("https://user:pass@google.com:99/foo;bar?d=e#ref",
+            url_utils.href());
 }
 
 }  // namespace dom
diff --git a/src/cobalt/h5vcc/H5vccSystem.idl b/src/cobalt/h5vcc/H5vccSystem.idl
index 25bbe3b..abf2017 100644
--- a/src/cobalt/h5vcc/H5vccSystem.idl
+++ b/src/cobalt/h5vcc/H5vccSystem.idl
@@ -20,6 +20,7 @@
   readonly attribute DOMString platform;
   readonly attribute DOMString region;
   readonly attribute DOMString version;
+  attribute boolean recordStats;
   boolean triggerHelp();
   DOMString getVideoContainerSizeOverride();
 };
diff --git a/src/cobalt/h5vcc/h5vcc.cc b/src/cobalt/h5vcc/h5vcc.cc
index 9792fcb..16cb6c9 100644
--- a/src/cobalt/h5vcc/h5vcc.cc
+++ b/src/cobalt/h5vcc/h5vcc.cc
@@ -27,7 +27,7 @@
       new H5vccRuntime(settings.event_dispatcher, settings.initial_deep_link);
   settings_ = new H5vccSettings(settings.media_module);
   storage_ = new H5vccStorage(settings.network_module);
-  system_ = new H5vccSystem();
+  system_ = new H5vccSystem(settings.on_set_record_stats);
 }
 
 }  // namespace h5vcc
diff --git a/src/cobalt/h5vcc/h5vcc.h b/src/cobalt/h5vcc/h5vcc.h
index 369cad3..8f46d2f 100644
--- a/src/cobalt/h5vcc/h5vcc.h
+++ b/src/cobalt/h5vcc/h5vcc.h
@@ -45,6 +45,7 @@
     account::AccountManager* account_manager;
     base::EventDispatcher* event_dispatcher;
     std::string initial_deep_link;
+    base::Callback<void(bool)> on_set_record_stats;
   };
 
   explicit H5vcc(const Settings& config);
diff --git a/src/cobalt/h5vcc/h5vcc_system.cc b/src/cobalt/h5vcc/h5vcc_system.cc
index 3bf8cf3..5b668cd 100644
--- a/src/cobalt/h5vcc/h5vcc_system.cc
+++ b/src/cobalt/h5vcc/h5vcc_system.cc
@@ -24,7 +24,9 @@
 namespace cobalt {
 namespace h5vcc {
 
-H5vccSystem::H5vccSystem() {}
+H5vccSystem::H5vccSystem(const base::Callback<void(bool)>& on_set_record_stats)
+    : on_set_record_stats_(on_set_record_stats) {
+}
 
 bool H5vccSystem::are_keys_reversed() const {
   return deprecated::PlatformDelegate::Get()->AreKeysReversed();
diff --git a/src/cobalt/h5vcc/h5vcc_system.h b/src/cobalt/h5vcc/h5vcc_system.h
index be0db79..3ce8764 100644
--- a/src/cobalt/h5vcc/h5vcc_system.h
+++ b/src/cobalt/h5vcc/h5vcc_system.h
@@ -26,13 +26,21 @@
 
 class H5vccSystem : public script::Wrappable {
  public:
-  H5vccSystem();
+  explicit H5vccSystem(const base::Callback<void(bool)>& on_set_record_stats);
 
   bool are_keys_reversed() const;
   std::string build_id() const;
   std::string platform() const;
   std::string region() const;
   std::string version() const;
+  bool record_stats() const {
+    return record_stats_;
+  }
+  void set_record_stats(bool record_stats) {
+    record_stats_ = record_stats;
+
+    on_set_record_stats_.Run(record_stats);
+  }
 
   bool TriggerHelp() const;
   std::string GetVideoContainerSizeOverride() const;
@@ -40,6 +48,8 @@
   DEFINE_WRAPPABLE_TYPE(H5vccSystem);
 
  private:
+  base::Callback<void(bool)> on_set_record_stats_;
+  bool record_stats_;
   DISALLOW_COPY_AND_ASSIGN(H5vccSystem);
 };
 
diff --git a/src/cobalt/layout/container_box.cc b/src/cobalt/layout/container_box.cc
index 1d55190..a2c79bb 100644
--- a/src/cobalt/layout/container_box.cc
+++ b/src/cobalt/layout/container_box.cc
@@ -400,7 +400,7 @@
 
 Vector2dLayoutUnit GetOffsetFromContainingBlockToStackingContext(
     Box* child_box) {
-  DCHECK(child_box->IsPositioned());
+  DCHECK(child_box->IsPositioned() || child_box->IsTransformed());
 
   Vector2dLayoutUnit relative_position;
   for (Box *containing_block = child_box->GetContainingBlock(),
@@ -454,7 +454,7 @@
     if (!current_box) {
       // Positioned elements may have their containing block farther
       // up the hierarchy than the stacking context, so handle this case here.
-      DCHECK(child_box->IsPositioned());
+      DCHECK(child_box->IsPositioned() || child_box->IsTransformed());
       return -GetOffsetFromContainingBlockToStackingContext(child_box);
     }
 #if !defined(NDEBUG)
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-2-font-face-font-family-hides-system-font-family.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-2-font-face-font-family-hides-system-font-family.html
index ce75b15..d50693a 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-2-font-face-font-family-hides-system-font-family.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-2-font-face-font-family-hides-system-font-family.html
@@ -34,11 +34,12 @@
     }
     @font-face {
       font-family: Roboto;
-      src: local(cursive);
+      src: local(DancingScript);  /* Try using a Postscript name of a typeface.
+                                   */
     }
     @font-face {
       font-family: serif;
-      src: local(casual);
+      src: local(Coming Soon);  /* Try using a Full Name of a typeface. */
     }
   </style>
 </head>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-first-available-local-font-face.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-first-available-local-font-face.html
index 18b941d..0d87b10 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-first-available-local-font-face.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-first-available-local-font-face.html
@@ -37,25 +37,25 @@
     }
     @font-face {
       font-family: font-face-1;
-      src: local(cursive);
+      src: local(DancingScript);  /* Lookup using font's postscript name. */
     }
     @font-face {
       font-family: font-face-2;
       src: local(invalid1),
-           local(casual);
+           local(Coming Soon);  /* Lookup using font's full name. */
     }
     @font-face {
       font-family: font-face-3;
       src: local(invalid1),
            local(invalid2),
-           local(sans-serif-smallcaps);
+           local(CarroisGothicSC-Regular);
     }
     @font-face {
       font-family: font-face-4;
       src: local(invalid1),
-           local(cursive),
+           local(DancingScript),  /* Looking using font's postscript name. */
            local(invalid2),
-           local(casual),
+           local(Coming Soon),  /* Lookup using font's full name. */
            local(invalid3);
     }
   </style>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-next-font-family-if-font-face-sources-unavailable.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-next-font-family-if-font-face-sources-unavailable.html
index 593ccdf..fe1ac19 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-next-font-family-if-font-face-sources-unavailable.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-use-next-font-family-if-font-face-sources-unavailable.html
@@ -39,12 +39,12 @@
     }
     @font-face {
       font-family: font-face-1;
-      src: local(cursive);
+      src: local(DancingScript);  /* Lookup using font's postscript name. */
     }
     @font-face {
       font-family: font-face-2;
       src: local(invalid1),
-           local(casual);
+           local(Coming Soon);  /* Lookup using font's full name. */
     }
     @font-face {
       font-family: font-face-3;
@@ -55,9 +55,9 @@
     @font-face {
       font-family: font-face-4;
       src: local(invalid1),
-           local(cursive),
+           local(DancingScript),  /* Lookup using font's postscript name. */
            local(invalid2),
-           local(casual),
+           local(Coming Soon),  /* Lookup using font's full name. */
            local(invalid3);
     }
   </style>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
index 0e42727..e894c76 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
index 74a3b50..b9bd6d1 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
@@ -67,29 +67,29 @@
     }
     @font-face {
       font-family: font-face-1;
-      src: local(Roboto);
+      src: local(Roboto-Regular);
     }
     @font-face {
       font-family: font-face-2;
-      src: local(cursive);
+      src: local(DancingScript);
       font-weight: normal;
       font-style: normal;
     }
     @font-face {
       font-family: font-face-2;
-      src: local(casual);
+      src: local(Coming Soon);
       font-weight: normal;
       font-style: italic;
     }
     @font-face {
       font-family: font-face-2;
-      src: local(sans-serif-smallcaps);
+      src: local(CarroisGothicSC-Regular);
       font-weight: bold;
       font-style: normal;
     }
     @font-face {
       font-family: font-face-2;
-      src: local(monospace);
+      src: local(Droid Sans Mono);
       font-weight: bold;
       font-style: italic;
     }
@@ -98,9 +98,13 @@
 <body>
   <div class="containing-block">
     <div class="font-face-1-normal-normal">The Hegemony Consul</div>
+    <!-- Note: font-face-1 selects a local font file, which only has an regular
+      style.  So, for the next three cases, a normal style is rendered.
+    -->
     <div class="font-face-1-normal-italic">The Hegemony Consul</div>
     <div class="font-face-1-bold-normal">The Hegemony Consul</div>
     <div class="font-face-1-bold-italic">The Hegemony Consul</div>
+
     <div class="font-face-2-normal-normal">The Hegemony Consul</div>
     <div class="font-face-2-normal-italic">The Hegemony Consul</div>
     <div class="font-face-2-bold-normal">The Hegemony Consul</div>
diff --git a/src/cobalt/loader/mock_loader_factory.h b/src/cobalt/loader/mock_loader_factory.h
new file mode 100644
index 0000000..b5314d6
--- /dev/null
+++ b/src/cobalt/loader/mock_loader_factory.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_LOADER_MOCK_LOADER_FACTORY_H_
+#define COBALT_LOADER_MOCK_LOADER_FACTORY_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/csp/content_security_policy.h"
+#include "cobalt/loader/font/typeface_decoder.h"
+#include "cobalt/loader/image/image_decoder.h"
+#include "cobalt/loader/loader.h"
+#include "cobalt/loader/loader_factory.h"
+#include "googleurl/src/gurl.h"
+
+namespace cobalt {
+namespace loader {
+
+class MockLoaderFactory {
+ public:
+  MOCK_METHOD5(CreateImageLoaderMock,
+               loader::Loader*(
+                   const GURL& url,
+                   const csp::SecurityCallback& url_security_callback,
+                   const image::ImageDecoder::SuccessCallback& success_callback,
+                   const image::ImageDecoder::FailureCallback& failure_callback,
+                   const image::ImageDecoder::ErrorCallback& error_callback));
+
+  MOCK_METHOD5(
+      CreateTypefaceLoaderMock,
+      loader::Loader*(
+          const GURL& url, const csp::SecurityCallback& url_security_callback,
+          const font::TypefaceDecoder::SuccessCallback& success_callback,
+          const font::TypefaceDecoder::FailureCallback& failure_callback,
+          const font::TypefaceDecoder::ErrorCallback& error_callback));
+
+  // This workaround is required since scoped_ptr has no copy constructor.
+  // see:
+  // https://groups.google.com/a/chromium.org/forum/#!msg/chromium-dev/01sDxsJ1OYw/I_S0xCBRF2oJ
+  scoped_ptr<Loader> CreateImageLoader(
+      const GURL& url, const csp::SecurityCallback& url_security_callback,
+      const image::ImageDecoder::SuccessCallback& success_callback,
+      const image::ImageDecoder::FailureCallback& failure_callback,
+      const image::ImageDecoder::ErrorCallback& error_callback) {
+    return scoped_ptr<Loader>(
+        CreateImageLoaderMock(url, url_security_callback, success_callback,
+                              failure_callback, error_callback));
+  }
+
+  scoped_ptr<Loader> CreateTypefaceLoader(
+      const GURL& url, const csp::SecurityCallback& url_security_callback,
+      const font::TypefaceDecoder::SuccessCallback& success_callback,
+      const font::TypefaceDecoder::FailureCallback& failure_callback,
+      const font::TypefaceDecoder::ErrorCallback& error_callback) {
+    return scoped_ptr<Loader>(
+        CreateTypefaceLoaderMock(url, url_security_callback, success_callback,
+                                 failure_callback, error_callback));
+  }
+
+  MOCK_METHOD0(Suspend, void());
+  MOCK_METHOD1(Resume, void(render_tree::ResourceProvider* resource_provider));
+};
+
+}  // namespace loader
+}  // namespace cobalt
+
+#endif  // COBALT_LOADER_MOCK_LOADER_FACTORY_H_
diff --git a/src/cobalt/media/media.gyp b/src/cobalt/media/media.gyp
index 0713216..70c6929 100644
--- a/src/cobalt/media/media.gyp
+++ b/src/cobalt/media/media.gyp
@@ -24,6 +24,7 @@
         'can_play_type_handler.h',
         'fetcher_buffered_data_source.cc',
         'fetcher_buffered_data_source.h',
+        'media_module.cc',
         'media_module.h',
         'media_module_stub.cc',
         'media_module_stub.h',
diff --git a/src/cobalt/media/media_module.cc b/src/cobalt/media/media_module.cc
new file mode 100644
index 0000000..945a8d2
--- /dev/null
+++ b/src/cobalt/media/media_module.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/media/media_module.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace cobalt {
+namespace media {
+
+namespace {
+
+void RunClosureAndSignal(const base::Closure& closure,
+                         base::WaitableEvent* event) {
+  closure.Run();
+  event->Signal();
+}
+
+void RunClosureOnMessageLoopAndWait(
+    const scoped_refptr<base::MessageLoopProxy>& message_loop,
+    const base::Closure& closure) {
+  base::WaitableEvent waitable_event(true, /* manual_reset */
+                                     false /* initially_signaled */);
+  message_loop->PostTask(
+      FROM_HERE, base::Bind(&RunClosureAndSignal, closure, &waitable_event));
+  waitable_event.Wait();
+}
+
+}  // namespace
+
+void MediaModule::Suspend() {
+  RunClosureOnMessageLoopAndWait(
+      message_loop_,
+      base::Bind(&MediaModule::SuspendTask, base::Unretained(this)));
+}
+
+void MediaModule::Resume() {
+  RunClosureOnMessageLoopAndWait(
+      message_loop_,
+      base::Bind(&MediaModule::ResumeTask, base::Unretained(this)));
+}
+
+void MediaModule::RegisterPlayer(WebMediaPlayer* player) {
+  RunClosureOnMessageLoopAndWait(message_loop_,
+                                 base::Bind(&MediaModule::RegisterPlayerTask,
+                                            base::Unretained(this), player));
+}
+
+void MediaModule::UnregisterPlayer(WebMediaPlayer* player) {
+  RunClosureOnMessageLoopAndWait(message_loop_,
+                                 base::Bind(&MediaModule::UnregisterPlayerTask,
+                                            base::Unretained(this), player));
+}
+
+void MediaModule::RegisterDebugState(WebMediaPlayer* player) {
+  void* debug_state_address = NULL;
+  size_t debug_state_size = 0;
+  if (player->GetDebugReportDataAddress(&debug_state_address,
+                                        &debug_state_size)) {
+    base::UserLog::Register(base::UserLog::kWebMediaPlayerState,
+                            "MediaPlyrState", debug_state_address,
+                            debug_state_size);
+  }
+}
+
+void MediaModule::DeregisterDebugState() {
+  base::UserLog::Deregister(base::UserLog::kWebMediaPlayerState);
+}
+
+void MediaModule::SuspendTask() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  for (Players::iterator iter = players_.begin(); iter != players_.end();
+       ++iter) {
+    DCHECK(!iter->second);
+    if (!iter->second) {
+      iter->first->Suspend();
+    }
+  }
+}
+
+void MediaModule::ResumeTask() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  for (Players::iterator iter = players_.begin(); iter != players_.end();
+       ++iter) {
+    DCHECK(!iter->second);
+    if (!iter->second) {
+      iter->first->Resume();
+    }
+  }
+}
+
+void MediaModule::RegisterPlayerTask(WebMediaPlayer* player) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  DCHECK(players_.find(player) == players_.end());
+  players_.insert(std::make_pair(player, false));
+
+  // Track debug state for the most recently added WebMediaPlayer instance.
+  RegisterDebugState(player);
+}
+
+void MediaModule::UnregisterPlayerTask(WebMediaPlayer* player) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  DCHECK(players_.find(player) != players_.end());
+  players_.erase(players_.find(player));
+
+  if (players_.empty()) {
+    DeregisterDebugState();
+  } else {
+    RegisterDebugState(players_.begin()->first);
+  }
+}
+
+}  // namespace media
+}  // namespace cobalt
diff --git a/src/cobalt/media/media_module.h b/src/cobalt/media/media_module.h
index 9415df5..3fa53bf 100644
--- a/src/cobalt/media/media_module.h
+++ b/src/cobalt/media/media_module.h
@@ -22,8 +22,12 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/threading/thread.h"
 #include "cobalt/base/user_log.h"
 #include "cobalt/math/size.h"
 #include "cobalt/media/can_play_type_handler.h"
@@ -59,6 +63,10 @@
   typedef ::media::WebMediaPlayer WebMediaPlayer;
   typedef render_tree::Image Image;
 
+  MediaModule() : thread_("media_module") {
+    thread_.Start();
+    message_loop_ = thread_.message_loop_proxy();
+  }
   virtual ~MediaModule() {}
 
   // Returns true when the setting is set successfully or if the setting has
@@ -70,6 +78,9 @@
     return false;
   }
 
+  void Suspend();
+  void Resume();
+
 #if !defined(COBALT_BUILD_TYPE_GOLD)
   virtual ::media::ShellRawVideoDecoderFactory* GetRawVideoDecoderFactory() {
     return NULL;
@@ -79,51 +90,8 @@
   // TODO: Move the following methods into class like MediaModuleBase
   // to ensure that MediaModule is an interface.
   // WebMediaPlayerDelegate methods
-  void RegisterPlayer(WebMediaPlayer* player) OVERRIDE {
-    DCHECK(players_.find(player) == players_.end());
-    players_.insert(std::make_pair(player, false));
-    // Track debug state for the most recently added WebMediaPlayer instance.
-    RegisterDebugState(player);
-  }
-
-  void UnregisterPlayer(WebMediaPlayer* player) OVERRIDE {
-    DCHECK(players_.find(player) != players_.end());
-    players_.erase(players_.find(player));
-    if (players_.empty()) {
-      DeregisterDebugState();
-    } else {
-      RegisterDebugState(players_.begin()->first);
-    }
-  }
-
-  // Functions to allow pause/resume of all active players.  Note that the
-  // caller should block the main thread to ensure that media related JS/DOM
-  // code doesn't get a chance to run between the call to PauseAllPlayers() and
-  // ResumeAllPlayers().  Otherwise the JS/DOM code might bring the players back
-  // to playback states.
-  void PauseAllPlayers() {
-    for (Players::iterator iter = players_.begin(); iter != players_.end();
-         ++iter) {
-      DCHECK(!iter->second);
-      if (!iter->second && !iter->first->IsPaused()) {
-        iter->first->Pause();
-        iter->second = true;
-      }
-    }
-  }
-
-  void ResumeAllPlayers() {
-    for (Players::iterator iter = players_.begin(); iter != players_.end();
-         ++iter) {
-      if (iter->second) {
-        DCHECK(iter->first->IsPaused());
-        if (iter->first->IsPaused()) {
-          iter->first->Play();
-        }
-        iter->second = false;
-      }
-    }
-  }
+  void RegisterPlayer(WebMediaPlayer* player) OVERRIDE;
+  void UnregisterPlayer(WebMediaPlayer* player) OVERRIDE;
 
   // This function should be defined on individual platform to create the
   // platform specific MediaModule.
@@ -133,23 +101,20 @@
       const Options& options = Options());
 
  private:
-  void RegisterDebugState(WebMediaPlayer* player) {
-    void* debug_state_address = NULL;
-    size_t debug_state_size = 0;
-    if (player->GetDebugReportDataAddress(&debug_state_address,
-                                          &debug_state_size)) {
-      base::UserLog::Register(base::UserLog::kWebMediaPlayerState,
-                              "MediaPlyrState", debug_state_address,
-                              debug_state_size);
-    }
-  }
-  void DeregisterDebugState() {
-    base::UserLog::Deregister(base::UserLog::kWebMediaPlayerState);
-  }
+  void RegisterDebugState(WebMediaPlayer* player);
+  void DeregisterDebugState();
+  void SuspendTask();
+  void ResumeTask();
+  void RegisterPlayerTask(WebMediaPlayer* player);
+  void UnregisterPlayerTask(WebMediaPlayer* player);
 
   // When the value of a particular player is true, it means the player is
   // paused by us.
   typedef std::map<WebMediaPlayer*, bool> Players;
+
+  // The thread that |players_| is accessed from,
+  base::Thread thread_;
+  scoped_refptr<base::MessageLoopProxy> message_loop_;
   Players players_;
 };
 
diff --git a/src/cobalt/media/media_module_stub.cc b/src/cobalt/media/media_module_stub.cc
index 90813e1..f9e218d 100644
--- a/src/cobalt/media/media_module_stub.cc
+++ b/src/cobalt/media/media_module_stub.cc
@@ -54,6 +54,10 @@
   }
   float GetMaxTimeSeekable() const OVERRIDE { return 0.f; }
 
+  // Suspend/Resume
+  void Suspend() OVERRIDE {}
+  void Resume() OVERRIDE {}
+
   // True if the loaded media has a playable video/audio track.
   bool HasVideo() const OVERRIDE { return false; }
   bool HasAudio() const OVERRIDE { return false; }
diff --git a/src/cobalt/media/sandbox/media_source_demuxer.cc b/src/cobalt/media/sandbox/media_source_demuxer.cc
index 0044342..29ac7f3 100644
--- a/src/cobalt/media/sandbox/media_source_demuxer.cc
+++ b/src/cobalt/media/sandbox/media_source_demuxer.cc
@@ -42,7 +42,7 @@
 using ::media::DemuxerStream;
 using ::media::ChunkDemuxer;
 
-typedef base::Callback<void(::media::Buffer*)> AppendBufferCB;
+typedef base::Callback<void(::media::DecoderBuffer*)> AppendBufferCB;
 
 const char kSourceId[] = "id";
 
@@ -268,8 +268,9 @@
   }
 }
 
-void MediaSourceDemuxer::AppendBuffer(::media::Buffer* buffer) {
+void MediaSourceDemuxer::AppendBuffer(::media::DecoderBuffer* buffer) {
   AUDescriptor desc = {0};
+  desc.is_keyframe = buffer->IsKeyframe();
   desc.offset = au_data_.size();
   desc.size = buffer->GetDataSize();
   desc.timestamp = buffer->GetTimestamp();
diff --git a/src/cobalt/media/sandbox/media_source_demuxer.h b/src/cobalt/media/sandbox/media_source_demuxer.h
index f86679a..3221ccb 100644
--- a/src/cobalt/media/sandbox/media_source_demuxer.h
+++ b/src/cobalt/media/sandbox/media_source_demuxer.h
@@ -21,7 +21,7 @@
 
 #include "base/basictypes.h"
 #include "base/time.h"
-#include "media/base/buffers.h"
+#include "media/base/decoder_buffer.h"
 #include "media/base/video_decoder_config.h"
 
 namespace cobalt {
@@ -32,6 +32,7 @@
 class MediaSourceDemuxer {
  public:
   struct AUDescriptor {
+    bool is_keyframe;
     size_t offset;
     size_t size;
     base::TimeDelta timestamp;
@@ -48,7 +49,7 @@
   const ::media::VideoDecoderConfig& config() const { return config_; }
 
  private:
-  void AppendBuffer(::media::Buffer* buffer);
+  void AppendBuffer(::media::DecoderBuffer* buffer);
 
   bool valid_;
   std::vector<uint8> au_data_;
diff --git a/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc b/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
index d0f37ef..8853031 100644
--- a/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
+++ b/src/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
@@ -65,7 +65,8 @@
     if (au_index_ < demuxer_->GetFrameCount()) {
       MediaSourceDemuxer::AUDescriptor desc = demuxer_->GetFrame(au_index_);
       current_au_buffer_ =
-          ::media::ShellBufferFactory::Instance()->AllocateBufferNow(desc.size);
+          ::media::ShellBufferFactory::Instance()->AllocateBufferNow(
+              desc.size, desc.is_keyframe);
       memcpy(current_au_buffer_->GetWritableData(), &au_data_[0] + desc.offset,
              desc.size);
       ++au_index_;
diff --git a/src/cobalt/network/persistent_cookie_store.cc b/src/cobalt/network/persistent_cookie_store.cc
index a5dd276..ce0926c 100644
--- a/src/cobalt/network/persistent_cookie_store.cc
+++ b/src/cobalt/network/persistent_cookie_store.cc
@@ -55,9 +55,13 @@
         get_all.ColumnString(6),
         base::Time::FromInternalValue(get_all.ColumnInt64(7)), expiry,
         get_all.ColumnBool(10), get_all.ColumnBool(11));
-    cookie->SetLastAccessDate(
-        base::Time::FromInternalValue(get_all.ColumnInt64(9)));
-    actual_cookies.push_back(cookie);
+    if (cookie) {
+      cookie->SetLastAccessDate(
+          base::Time::FromInternalValue(get_all.ColumnInt64(9)));
+      actual_cookies.push_back(cookie);
+    } else {
+      DLOG(ERROR) << "Failed to create cookie.";
+    }
   }
 
   return actual_cookies;
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
new file mode 100644
index 0000000..219a980
--- /dev/null
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_RENDER_TREE_MOCK_RESOURCE_PROVIDER_H_
+#define COBALT_RENDER_TREE_MOCK_RESOURCE_PROVIDER_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/render_tree/font.h"
+#include "cobalt/render_tree/font_provider.h"
+#include "cobalt/render_tree/glyph_buffer.h"
+#include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/render_tree/typeface.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace render_tree {
+
+class MockResourceProvider : public ResourceProvider {
+ public:
+  MOCK_METHOD0(Finish, void());
+  MOCK_METHOD1(PixelFormatSupported, bool(PixelFormat pixel_format));
+  MOCK_METHOD1(AlphaFormatSupported, bool(AlphaFormat alpha_format));
+  MOCK_METHOD3(AllocateImageDataMock,
+               ImageData*(const math::Size& size, PixelFormat pixel_format,
+                          AlphaFormat alpha_format));
+  MOCK_METHOD1(CreateImageMock, Image*(ImageData* pixel_data));
+  MOCK_METHOD2(AllocateRawImageMemoryMock,
+               RawImageMemory*(size_t size_in_bytes, size_t alignment));
+  MOCK_METHOD2(CreateMultiPlaneImageFromRawMemoryMock,
+               Image*(RawImageMemory* raw_image_memory,
+                      const MultiPlaneImageDataDescriptor& descriptor));
+  MOCK_CONST_METHOD1(HasLocalFontFamily, bool(const char* font_family_name));
+  MOCK_METHOD2(GetLocalTypefaceMock,
+               Typeface*(const char* font_family_name, FontStyle font_style));
+  MOCK_METHOD1(GetLocalTypefaceIfAvailableMock,
+               Typeface*(const std::string& font_face_name));
+  MOCK_METHOD3(GetCharacterFallbackTypefaceMock,
+               Typeface*(int32 utf32_character, FontStyle font_style,
+                         const std::string& language));
+  MOCK_METHOD2(CreateTypefaceFromRawDataMock,
+               Typeface*(RawTypefaceDataVector* raw_data,
+                         std::string* error_string));
+  MOCK_METHOD5(
+      CreateGlyphBufferMock,
+      render_tree::GlyphBuffer*(const char16* text_buffer, size_t text_length,
+                                const std::string& language, bool is_rtl,
+                                render_tree::FontProvider* font_provider));
+  MOCK_METHOD2(
+      CreateGlyphBufferMock,
+      render_tree::GlyphBuffer*(const std::string& utf8_string,
+                                const scoped_refptr<render_tree::Font>& font));
+  MOCK_METHOD6(GetTextWidth,
+               float(const char16* text_buffer, size_t text_length,
+                     const std::string& language, bool is_rtl,
+                     render_tree::FontProvider* font_provider,
+                     render_tree::FontVector* maybe_used_fonts));
+
+  scoped_ptr<ImageData> AllocateImageData(const math::Size& size,
+                                          PixelFormat pixel_format,
+                                          AlphaFormat alpha_format) {
+    return scoped_ptr<ImageData>(
+        AllocateImageDataMock(size, pixel_format, alpha_format));
+  }
+  scoped_refptr<Image> CreateImage(scoped_ptr<ImageData> pixel_data) {
+    return scoped_refptr<Image>(CreateImageMock(pixel_data.get()));
+  }
+  scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
+                                                    size_t alignment) {
+    return scoped_ptr<RawImageMemory>(
+        AllocateRawImageMemoryMock(size_in_bytes, alignment));
+  }
+  scoped_refptr<Image> CreateMultiPlaneImageFromRawMemory(
+      scoped_ptr<RawImageMemory> raw_image_memory,
+      const MultiPlaneImageDataDescriptor& descriptor) {
+    return scoped_refptr<Image>(CreateMultiPlaneImageFromRawMemoryMock(
+        raw_image_memory.get(), descriptor));
+  }
+  scoped_refptr<Typeface> GetLocalTypeface(const char* font_family_name,
+                                           FontStyle font_style) {
+    return scoped_refptr<Typeface>(
+        GetLocalTypefaceMock(font_family_name, font_style));
+  }
+  scoped_refptr<Typeface> GetLocalTypefaceByFaceNameIfAvailable(
+      const std::string& font_face_name) {
+    return scoped_refptr<Typeface>(
+        GetLocalTypefaceIfAvailableMock(font_face_name));
+  }
+  scoped_refptr<Typeface> GetCharacterFallbackTypeface(
+      int32 utf32_character, FontStyle font_style,
+      const std::string& language) {
+    return scoped_refptr<Typeface>(GetCharacterFallbackTypefaceMock(
+        utf32_character, font_style, language));
+  }
+  scoped_refptr<Typeface> CreateTypefaceFromRawData(
+      scoped_ptr<RawTypefaceDataVector> raw_data, std::string* error_string) {
+    return scoped_refptr<Typeface>(
+        CreateTypefaceFromRawDataMock(raw_data.get(), error_string));
+  }
+  scoped_refptr<render_tree::GlyphBuffer> CreateGlyphBuffer(
+      const char16* text_buffer, size_t text_length,
+      const std::string& language, bool is_rtl,
+      render_tree::FontProvider* font_provider) {
+    return scoped_refptr<render_tree::GlyphBuffer>(CreateGlyphBufferMock(
+        text_buffer, text_length, language, is_rtl, font_provider));
+  }
+  scoped_refptr<render_tree::GlyphBuffer> CreateGlyphBuffer(
+      const std::string& utf8_string,
+      const scoped_refptr<render_tree::Font>& font) {
+    return scoped_refptr<render_tree::GlyphBuffer>(
+        CreateGlyphBufferMock(utf8_string, font.get()));
+  }
+};
+
+}  // namespace render_tree
+}  // namespace cobalt
+
+#endif  // COBALT_RENDER_TREE_MOCK_RESOURCE_PROVIDER_H_
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index 72c106a..c1bd3cb 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -107,6 +107,16 @@
   virtual scoped_refptr<Typeface> GetLocalTypeface(const char* font_family_name,
                                                    FontStyle font_style) = 0;
 
+  // Given a set of typeface information, this method returns the locally
+  // available typeface that best fits the specified parameters. In the case
+  // where no typeface is found that matches the font family name, NULL is
+  // returned.
+  //
+  // Font's typeface (aka face name) is combination of a style and a font
+  // family.  Font's style consists of weight, and a slant (but not size).
+  virtual scoped_refptr<Typeface> GetLocalTypefaceByFaceNameIfAvailable(
+      const std::string& font_face_name) = 0;
+
   // Given a UTF-32 character, a set of typeface information, and a language,
   // this method returns the best-fit locally available fallback typeface that
   // provides a glyph for the specified character. In the case where no fallback
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index 1430f5f..bf0a8bd 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -213,6 +213,12 @@
     return make_scoped_refptr(new TypefaceStub(NULL));
   }
 
+  scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
+      const std::string& font_face_name) OVERRIDE {
+    UNREFERENCED_PARAMETER(font_face_name);
+    return make_scoped_refptr(new TypefaceStub(NULL));
+  }
+
   scoped_refptr<Typeface> GetCharacterFallbackTypeface(
       int32 utf32_character, FontStyle font_style,
       const std::string& language) OVERRIDE {
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 9c6a1e2..ae7cbd9 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -92,6 +92,13 @@
                                                    font_style);
 }
 
+scoped_refptr<render_tree::Typeface>
+ResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
+    const std::string& font_face_name) {
+  return skia_resource_provider_->GetLocalTypefaceByFaceNameIfAvailable(
+      font_face_name);
+}
+
 scoped_refptr<Typeface> ResourceProvider::GetCharacterFallbackTypeface(
     int32 utf32_character, FontStyle font_style, const std::string& language) {
   return skia_resource_provider_->GetCharacterFallbackTypeface(
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index 078a683..a7edd28 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -64,6 +64,9 @@
   scoped_refptr<render_tree::Typeface> GetLocalTypeface(
       const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
 
+  scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
+      const std::string& font_face_name) OVERRIDE;
+
   scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
       int32 utf32_character, render_tree::FontStyle font_style,
       const std::string& language) OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index d79a244..1f97820 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -25,6 +25,7 @@
 #include "cobalt/renderer/rasterizer/skia/gl_format_conversions.h"
 #include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/typeface.h"
 #include "third_party/ots/include/opentype-sanitiser.h"
 #include "third_party/ots/include/ots-memory-stream.h"
@@ -166,6 +167,25 @@
 }
 
 scoped_refptr<render_tree::Typeface>
+HardwareResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
+    const std::string& font_face_name) {
+  TRACE_EVENT0("cobalt::renderer",
+               "HardwareResourceProvider::GetLocalTypefaceIfAvailable()");
+
+  SkFontMgr_Cobalt* font_manager =
+      base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager_.get());
+
+  SkTypeface* typeface = font_manager->matchFaceNameOnlyIfFound(font_face_name);
+  SkiaTypeface* skia_type_face = NULL;
+  if (typeface != NULL) {
+    SkAutoTUnref<SkTypeface> typeface_unref_helper(typeface);
+    skia_type_face = new SkiaTypeface(typeface);
+  }
+
+  return scoped_refptr<render_tree::Typeface>(skia_type_face);
+}
+
+scoped_refptr<render_tree::Typeface>
 HardwareResourceProvider::GetCharacterFallbackTypeface(
     int32 character, render_tree::FontStyle font_style,
     const std::string& language) {
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 54a439d..8d85fea 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -63,6 +63,9 @@
   scoped_refptr<render_tree::Typeface> GetLocalTypeface(
       const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
 
+  scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
+      const std::string& font_face_name) OVERRIDE;
+
   scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
       int32 character, render_tree::FontStyle font_style,
       const std::string& language) OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/google_logging.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/google_logging.cc
index bab22ce..bc53d2f 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/google_logging.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/google_logging.cc
@@ -19,7 +19,9 @@
   base::StringAppendV(&message, format, ap);
   va_end(ap);
 
-  logging::LogMessage(file, line,
-                      fatal ? logging::LOG_FATAL : logging::LOG_INFO).stream()
-      << message;
+  if (fatal) {
+    logging::LogMessage(file, line, logging::LOG_FATAL).stream() << message;
+  } else if (DLOG_IS_ON(INFO)) {
+    logging::LogMessage(file, line, logging::LOG_INFO).stream() << message;
+  }
 }
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
index 013d0dc..63f1e67 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
@@ -250,6 +250,7 @@
     const char* name = attributes[i];
     const char* value = attributes[i + 1];
     size_t name_len = strlen(name);
+
     if (name_len == 4 && strncmp(name, "name", name_len) == 0) {
       SkAutoAsciiToLC to_lowercase(value);
       family->names.push_back().set(to_lowercase.lc());
@@ -268,25 +269,79 @@
 }
 
 void FontElementHandler(FontFileInfo* file, const char** attributes) {
-  // A <font> should have weight (integer) and style (normal, italic)attributes.
+  DCHECK(file != NULL);
+
+  // A <font> should have following attributes:
+  // weight (integer), style (normal, italic), font_name (string), and
+  // postscript_name (string).
   // The element should contain a filename.
+
+  enum SeenAttributeFlags {
+    kSeenNone = 0,
+    kSeenFontFullName = 1,
+    kSeenFontPostscriptName = 1 << 1,
+    kSeenWeight = 1 << 2,
+    kSeenStyle = 1 << 3
+  };
+
+  uint32_t seen_attributes_flag = kSeenNone;
   for (size_t i = 0; attributes[i] != NULL && attributes[i + 1] != NULL;
        i += 2) {
     const char* name = attributes[i];
     const char* value = attributes[i + 1];
-    size_t name_len = strlen(name);
-    if (name_len == 6 && strncmp("weight", name, name_len) == 0) {
-      if (!ParseNonNegativeInteger(value, &file->weight)) {
-        SkDebugf("---- Font weight %s (INVALID)", value);
-        file->weight = 0;
-      }
-    } else if (name_len == 5 && strncmp("style", name, name_len) == 0) {
-      size_t value_len = strlen(value);
-      if (value_len == 6 && strncmp("italic", value, value_len) == 0) {
-        file->style = FontFileInfo::kItalic_FontStyle;
-      }
+
+    switch (strlen(name)) {
+      case 9:
+        if (strncmp("font_name", name, 9) == 0) {
+          file->full_font_name = value;
+          seen_attributes_flag |= kSeenFontFullName;
+          continue;
+        }
+        break;
+      case 15:
+        if (strncmp("postscript_name", name, 15) == 0) {
+          file->postcript_name = value;
+          seen_attributes_flag |= kSeenFontPostscriptName;
+          continue;
+        }
+        break;
+      case 6:
+        if (strncmp("weight", name, 6) == 0) {
+          if (!ParseNonNegativeInteger(value, &file->weight)) {
+            DLOG(WARNING) << "Invalid font weight [" << value << "]";
+            file->weight = 0;
+          } else {
+            seen_attributes_flag |= kSeenWeight;
+          }
+          continue;
+        }
+        break;
+      case 5:
+        if (strncmp("style", name, 5) == 0) {
+          if (strncmp("italic", value, 6) == 0) {
+            file->style = FontFileInfo::kItalic_FontStyle;
+            seen_attributes_flag |= kSeenStyle;
+            continue;
+          } else if (strncmp("normal", value, 6) == 0) {
+            file->style = FontFileInfo::kNormal_FontStyle;
+            seen_attributes_flag |= kSeenStyle;
+            continue;
+          } else {
+            NOTREACHED() << "Unsupported style [" << value << "]";
+          }
+        }
+        break;
+      default:
+        break;
     }
+
+    NOTREACHED() << "Unsupported attribute [" << name << "]";
   }
+
+  DCHECK_EQ(seen_attributes_flag, kSeenFontFullName | kSeenFontPostscriptName |
+                                      kSeenWeight | kSeenStyle);
+  DCHECK(!file->full_font_name.isEmpty());
+  DCHECK(!file->postcript_name.isEmpty());
 }
 
 FontFamily* FindFamily(FamilyData* family_data, const char* family_name) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
index 21b6b4c..964fad6 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
@@ -32,6 +32,7 @@
 #include "SkString.h"
 #include "SkTArray.h"
 #include "SkTSearch.h"
+#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -257,8 +258,14 @@
                           ? SkFontStyle::kItalic_Slant
                           : SkFontStyle::kUpright_Slant);
 
-    styles_.push_back().reset(SkNEW_ARGS(SkFontStyleSetEntry_Cobalt,
-                                         (path_name, font_file.index, style)));
+    std::string full_font_name(font_file.full_font_name.c_str(),
+                               font_file.full_font_name.size());
+    std::string postcript_name(font_file.postcript_name.c_str(),
+                               font_file.postcript_name.size());
+
+    styles_.push_back().reset(SkNEW_ARGS(
+        SkFontStyleSetEntry_Cobalt,
+        (path_name, font_file.index, style, full_font_name, postcript_name)));
   }
 }
 
@@ -636,6 +643,79 @@
   return NULL;
 }
 
+SkTypeface* SkFontMgr_Cobalt::matchFullFontFaceNameHelper(
+    SkFontStyleSet_Cobalt* style_set,
+    SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt* style) const {
+  DCHECK(style_set != NULL);
+  if (!style) {
+    NOTREACHED() << "style should not be NULL.";
+    return NULL;
+  }
+
+  if (style->typeface == NULL) {
+    if (!style_set) {
+      return NULL;
+    }
+    style_set->CreateSystemTypeface(style);
+  }
+
+  if (style->typeface != NULL) {
+    return SkRef(style->typeface.get());
+  }
+
+  return NULL;
+}
+
+SkTypeface* SkFontMgr_Cobalt::matchFaceNameOnlyIfFound(
+    const std::string& font_face_name) const {
+  // Prioritize looking it up postscript name first since some of our client
+  // applications prefer this method to specify face names.
+  SkTypeface* typeface = matchPostScriptName(font_face_name);
+  if (typeface != NULL) return typeface;
+
+  typeface = matchFullFontFaceName(font_face_name);
+  if (typeface != NULL) return typeface;
+
+  return NULL;
+}
+
+SkTypeface* SkFontMgr_Cobalt::matchFullFontFaceName(
+    const std::string& font_face_name) const {
+  SkAutoMutexAcquire scoped_mutex(style_sets_mutex_);
+
+  if (font_face_name.empty()) {
+    return NULL;
+  }
+
+  FullFontNameToFontFaceInfoMap::const_iterator font_face_iterator =
+      fullfontname_to_fontface_info_map_.find(font_face_name);
+
+  if (font_face_iterator != fullfontname_to_fontface_info_map_.end()) {
+    return matchFullFontFaceNameHelper(
+        font_face_iterator->second.style_set_entry_parent,
+        font_face_iterator->second.style_set_entry);
+  }
+
+  return NULL;
+}
+
+SkTypeface* SkFontMgr_Cobalt::matchPostScriptName(
+    const std::string& font_face_name) const {
+  if (font_face_name.empty()) {
+    return NULL;
+  }
+
+  PostScriptToFontFaceInfoMap::const_iterator font_face_iterator =
+      postscriptname_to_fontface_info_map_.find(font_face_name);
+  if (font_face_iterator != postscriptname_to_fontface_info_map_.end()) {
+    return matchFullFontFaceNameHelper(
+        font_face_iterator->second.style_set_entry_parent,
+        font_face_iterator->second.style_set_entry);
+  }
+
+  return NULL;
+}
+
 SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyle(
     const char family_name[], const SkFontStyle& style) const {
   SkTypeface* tf = NULL;
@@ -768,6 +848,54 @@
       continue;
     }
 
+    for (SkAutoTUnref<SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt>*
+             font_style_set_entry = new_set->styles_.begin();
+         font_style_set_entry != new_set->styles_.end();
+         ++font_style_set_entry) {
+      if (!font_style_set_entry) {
+        NOTREACHED() << " Font Style entry is invalid";
+        continue;
+      } else if ((*font_style_set_entry).get() == NULL) {
+        NOTREACHED() << " Font Style entry is NULL";
+        continue;
+      }
+
+      const std::string& full_font_name =
+          (*font_style_set_entry)->full_font_name;
+      DCHECK(!full_font_name.empty());
+      if (fullfontname_to_fontface_info_map_.find(full_font_name) ==
+          fullfontname_to_fontface_info_map_.end()) {
+        DLOG(INFO) << "Adding Full font name [" << full_font_name << "].";
+        fullfontname_to_fontface_info_map_[full_font_name] =
+            FullFontNameToFontFaceInfoMap::mapped_type(
+                font_style_set_entry->get(), new_set.get());
+      } else {
+        // Purposely, not overwriting the entry gives priority to the
+        // earlier entry.  This is consistent with how fonts.xml gives
+        // priority to fonts that are specified earlier in the file.
+        NOTREACHED() << "Full font name [" << full_font_name
+                     << "] already registered in BuildNameToFamilyMap.";
+      }
+
+      const std::string& postscript_font_name =
+          (*font_style_set_entry)->font_postscript_name;
+      DCHECK(!postscript_font_name.empty());
+      if (postscriptname_to_fontface_info_map_.find(postscript_font_name) ==
+          postscriptname_to_fontface_info_map_.end()) {
+        DLOG(INFO) << "Adding Postscript name [" << postscript_font_name
+                   << "].";
+        postscriptname_to_fontface_info_map_[postscript_font_name] =
+            PostScriptToFontFaceInfoMap::mapped_type(
+                font_style_set_entry->get(), new_set.get());
+      } else {
+        // Purposely, not overwriting the entry gives priority to the
+        // earlier entry.  This is consistent with how fonts.xml gives
+        // priority to fonts that are specified earlier in the file.
+        NOTREACHED() << "Adding Postscript name [" << postscript_font_name
+                     << "] already registered in BuildNameToFamilyMap.";
+      }
+    }
+
     font_style_sets_.push_back().reset(SkRef(new_set.get()));
 
     if (named_font) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
index e09b727..cebba88 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
@@ -26,7 +26,6 @@
 #include "cobalt/base/poller.h"
 #include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h"
 #include "SkFontHost.h"
-#include "SkFontHost_FreeType_common.h"
 #include "SkFontDescriptor.h"
 #include "SkFontMgr.h"
 #include "SkThread.h"
@@ -48,15 +47,25 @@
  public:
   struct SkFontStyleSetEntry_Cobalt : public SkRefCnt {
     SkFontStyleSetEntry_Cobalt(const SkString& file_path, const int index,
-                               const SkFontStyle& style)
+                               const SkFontStyle& style,
+                               const std::string& full_name,
+                               const std::string& postscript_name)
         : font_file_path(file_path),
           ttc_index(index),
           font_style(style),
+          full_font_name(full_name.data(), full_name.size()),
+          font_postscript_name(postscript_name.data(), postscript_name.size()),
           typeface(NULL) {}
 
     const SkString font_file_path;
     const int ttc_index;
     const SkFontStyle font_style;
+
+    // these two members are declared as std::string since we have a hasher
+    // for std::string, but not SkString at this point.
+    const std::string full_font_name;
+    const std::string font_postscript_name;
+
     SkAutoTUnref<SkTypeface> typeface;
   };
 
@@ -129,7 +138,19 @@
   SkFontMgr_Cobalt(const char* directory,
                    const SkTArray<SkString, true>& default_fonts);
 
+  // Note: Unlike the other similar onMatch* functions, this function can return
+  // NULL.  This is so that we can try other things in case the match is not
+  // found.
+  SkTypeface* matchFaceNameOnlyIfFound(const std::string& font_face_name) const;
+
  protected:
+  // Note: These match*Name helper functions can return NULL.
+  SkTypeface* matchFullFontFaceName(const std::string& font_face_name) const;
+  SkTypeface* matchPostScriptName(const std::string& font_face_name) const;
+  SkTypeface* matchFullFontFaceNameHelper(
+      SkFontStyleSet_Cobalt* style_set,
+      SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt* style) const;
+
   // From SkFontMgr
   virtual int onCountFamilies() const SK_OVERRIDE;
 
@@ -184,6 +205,20 @@
   //  the same (non-replicated) set of typefaces.
   typedef base::hash_map<std::string, SkFontStyleSet_Cobalt*> NameToFamilyMap;
 
+  struct FontFaceInfo {
+    FontFaceInfo() : style_set_entry(NULL), style_set_entry_parent(NULL) {}
+    FontFaceInfo(SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt* entry,
+                 SkFontStyleSet_Cobalt* parent)
+        : style_set_entry(entry), style_set_entry_parent(parent) {}
+
+    SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt* style_set_entry;
+    SkFontStyleSet_Cobalt* style_set_entry_parent;
+  };
+
+  typedef base::hash_map<std::string, FontFaceInfo>
+      FullFontNameToFontFaceInfoMap;
+  typedef base::hash_map<std::string, FontFaceInfo> PostScriptToFontFaceInfoMap;
+
   void BuildNameToFamilyMap(const char* base_path,
                             SkTDArray<FontFamily*>* families);
   void FindDefaultFont(const SkTArray<SkString, true>& default_fonts);
@@ -223,6 +258,8 @@
 
   SkTArray<SkString> family_names_;
   NameToFamilyMap name_to_family_map_;
+  FullFontNameToFontFaceInfoMap fullfontname_to_fontface_info_map_;
+  PostScriptToFontFaceInfoMap postscriptname_to_fontface_info_map_;
 
   SkTArray<SkFontStyleSet_Cobalt*> fallback_families_;
 
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
index 5e12828..020517c 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
@@ -25,7 +25,6 @@
 #include "SkString.h"
 #include "SkTDArray.h"
 
-
 // The font_character_map namespace contains all of the constants, typedefs, and
 // utility functions used with determining whether or not a font supports a
 // character.
@@ -110,6 +109,9 @@
   int index;
   int weight;
   FontStyle style;
+
+  SkString full_font_name;
+  SkString postcript_name;
 };
 
 // A font family provides one or more names for a collection of fonts, each of
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkMemory_starboard.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkMemory_starboard.cc
index 8d843b7..531621f 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkMemory_starboard.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkMemory_starboard.cc
@@ -54,7 +54,7 @@
 
 void sk_free(void* p) {
   if (p) {
-    SbMemoryFree(p);
+    SbMemoryDeallocate(p);
   }
 }
 
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
index 04ca7c1..be05ccf 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -21,6 +21,7 @@
 #include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
 #include "cobalt/renderer/rasterizer/skia/font.h"
 #include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
 #include "cobalt/renderer/rasterizer/skia/software_image.h"
 #include "cobalt/renderer/rasterizer/skia/typeface.h"
 #include "third_party/ots/include/opentype-sanitiser.h"
@@ -108,7 +109,8 @@
 
 scoped_refptr<render_tree::Typeface> SoftwareResourceProvider::GetLocalTypeface(
     const char* font_family_name, render_tree::FontStyle font_style) {
-  TRACE_EVENT0("cobalt::renderer", "SoftwareResourceProvider::GetLocalFont()");
+  TRACE_EVENT0("cobalt::renderer",
+               "SoftwareResourceProvider::GetLocalTypeface()");
 
   SkAutoTUnref<SkTypeface> typeface(font_manager_->matchFamilyStyle(
       font_family_name, CobaltFontStyleToSkFontStyle(font_style)));
@@ -116,6 +118,24 @@
 }
 
 scoped_refptr<render_tree::Typeface>
+SoftwareResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
+    const std::string& font_face_name) {
+  TRACE_EVENT0("cobalt::renderer",
+               "SoftwareResourceProvider::GetLocalTypefaceIfAvailable()");
+
+  SkFontMgr_Cobalt* font_manager =
+      base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager_.get());
+
+  SkTypeface* typeface = font_manager->matchFaceNameOnlyIfFound(font_face_name);
+  if (typeface != NULL) {
+    SkAutoTUnref<SkTypeface> typeface_unref_helper(typeface);
+    return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
+  }
+
+  return NULL;
+}
+
+scoped_refptr<render_tree::Typeface>
 SoftwareResourceProvider::GetCharacterFallbackTypeface(
     int32 character, render_tree::FontStyle font_style,
     const std::string& language) {
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index 7be9105..6b979ce 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -58,6 +58,9 @@
   scoped_refptr<render_tree::Typeface> GetLocalTypeface(
       const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
 
+  scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
+      const std::string& font_face_name) OVERRIDE;
+
   scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
       int32 character, render_tree::FontStyle font_style,
       const std::string& language) OVERRIDE;
diff --git a/src/cobalt/script/javascript_engine.h b/src/cobalt/script/javascript_engine.h
index 7c5a607..cda4f91 100644
--- a/src/cobalt/script/javascript_engine.h
+++ b/src/cobalt/script/javascript_engine.h
@@ -41,6 +41,11 @@
   // Javascript object. This may mean collection needs to happen sooner.
   virtual void ReportExtraMemoryCost(size_t bytes) = 0;
 
+  // Updates the memory usage and returns the total memory that is reserved by
+  // the engine. This includes the part that is actually occupied by JS objects,
+  // and the part that is not yet.
+  virtual size_t UpdateMemoryStatsAndReturnReserved() = 0;
+
  protected:
   virtual ~JavaScriptEngine() {}
   friend class scoped_ptr<JavaScriptEngine>;
diff --git a/src/cobalt/script/javascriptcore/jsc_engine.cc b/src/cobalt/script/javascriptcore/jsc_engine.cc
index 08cfcbb..178f66b 100644
--- a/src/cobalt/script/javascriptcore/jsc_engine.cc
+++ b/src/cobalt/script/javascriptcore/jsc_engine.cc
@@ -21,7 +21,6 @@
 #include "base/synchronization/lock.h"
 #include "base/time.h"
 #include "cobalt/base/c_val.h"
-#include "cobalt/base/poller.h"
 #include "cobalt/script/javascriptcore/jsc_global_environment.h"
 #include "cobalt/script/javascriptcore/jsc_global_object.h"
 #include "third_party/WebKit/Source/JavaScriptCore/runtime/InitializeThreading.h"
@@ -33,12 +32,6 @@
 
 namespace {
 
-#if defined(__LB_SHELL__FOR_RELEASE__)
-const int kPollerPeriodMs = 2000;
-#else  // #if defined(__LB_SHELL__FOR_RELEASE__)
-const int kPollerPeriodMs = 20;
-#endif  // #if defined(__LB_SHELL__FOR_RELEASE__)
-
 class JSCEngineStats {
  public:
   JSCEngineStats();
@@ -58,31 +51,24 @@
     --js_engine_count_;
   }
 
+  size_t UpdateMemoryStatsAndReturnReserved() {
+    base::AutoLock auto_lock(lock_);
+    if (js_engine_count_.value() == 0) {
+      return 0;
+    }
+    return OSAllocator::getCurrentBytesAllocated();
+  }
+
  private:
   friend struct StaticMemorySingletonTraits<JSCEngineStats>;
 
-  void Update() {
-    base::AutoLock auto_lock(lock_);
-    if (js_engine_count_.value() > 0) {
-      js_memory_ = OSAllocator::getCurrentBytesAllocated();
-    }
-  }
-
   base::Lock lock_;
-  base::CVal<base::cval::SizeInBytes, base::CValPublic> js_memory_;
   base::CVal<size_t> js_engine_count_;
-  scoped_ptr<base::PollerWithThread> poller_;
 };
 
 JSCEngineStats::JSCEngineStats()
-    : js_memory_("Memory.JS", 0,
-                 "Total memory occupied by the JSC page allocator."),
-      js_engine_count_("Count.JS.Engine", 0,
-                       "Total JavaScript engine registered.") {
-  poller_.reset(new base::PollerWithThread(
-      base::Bind(&JSCEngineStats::Update, base::Unretained(this)),
-      base::TimeDelta::FromMilliseconds(kPollerPeriodMs)));
-}
+    : js_engine_count_("Count.JS.Engine", 0,
+                       "Total JavaScript engine registered.") {}
 
 }  // namespace
 
@@ -115,6 +101,10 @@
   global_data_->heap.reportExtraMemoryCost(bytes);
 }
 
+size_t JSCEngine::UpdateMemoryStatsAndReturnReserved() {
+  return JSCEngineStats::GetInstance()->UpdateMemoryStatsAndReturnReserved();
+}
+
 }  // namespace javascriptcore
 
 scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine() {
diff --git a/src/cobalt/script/javascriptcore/jsc_engine.h b/src/cobalt/script/javascriptcore/jsc_engine.h
index 5ef1068..7ee06e0 100644
--- a/src/cobalt/script/javascriptcore/jsc_engine.h
+++ b/src/cobalt/script/javascriptcore/jsc_engine.h
@@ -30,9 +30,12 @@
  public:
   JSCEngine();
   ~JSCEngine() OVERRIDE;
+
   scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
   void CollectGarbage() OVERRIDE;
   void ReportExtraMemoryCost(size_t bytes) OVERRIDE;
+  size_t UpdateMemoryStatsAndReturnReserved() OVERRIDE;
+
   JSC::JSGlobalData* global_data() { return global_data_.get(); }
   ScriptObjectRegistry* script_object_registry() {
     return &script_object_registry_;
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index 39f47bb..d5076b8 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -20,7 +20,6 @@
 
 #include "base/logging.h"
 #include "cobalt/base/c_val.h"
-#include "cobalt/base/poller.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/script/mozjs/mozjs_global_environment.h"
 #include "third_party/mozjs/cobalt_config/include/jscustomallocator.h"
@@ -43,12 +42,6 @@
   MozjsGlobalEnvironment::CheckEval
 };
 
-#if defined(__LB_SHELL__FOR_RELEASE__)
-const int kPollerPeriodMs = 2000;
-#else  // #if defined(__LB_SHELL__FOR_RELEASE__)
-const int kPollerPeriodMs = 20;
-#endif  // #if defined(__LB_SHELL__FOR_RELEASE__)
-
 class EngineStats {
  public:
   EngineStats();
@@ -68,36 +61,30 @@
     --engine_count_;
   }
 
-  void Update() {
+  size_t UpdateMemoryStatsAndReturnReserved() {
     base::AutoLock auto_lock(lock_);
+    if (engine_count_.value() == 0) {
+      return 0;
+    }
     allocated_memory_ =
         MemoryAllocatorReporter::Get()->GetCurrentBytesAllocated();
     mapped_memory_ = MemoryAllocatorReporter::Get()->GetCurrentBytesMapped();
-    memory_sum_ = allocated_memory_ + mapped_memory_;
+    return allocated_memory_ + mapped_memory_;
   }
 
  private:
   base::Lock lock_;
   base::CVal<size_t, base::CValPublic> allocated_memory_;
   base::CVal<size_t, base::CValPublic> mapped_memory_;
-  base::CVal<size_t, base::CValPublic> memory_sum_;
   base::CVal<size_t> engine_count_;
-
-  // Repeating timer to query the used bytes.
-  scoped_ptr<base::PollerWithThread> poller_;
 };
 
 EngineStats::EngineStats()
     : allocated_memory_("Memory.JS.AllocatedMemory", 0,
                         "JS memory occupied by the Mozjs allocator."),
       mapped_memory_("Memory.JS.MappedMemory", 0, "JS mapped memory."),
-      memory_sum_("Memory.JS", 0,
-                  "Total memory occupied by the Mozjs allocator and heap."),
       engine_count_("Count.JS.Engine", 0,
                     "Total JavaScript engine registered.") {
-  poller_.reset(new base::PollerWithThread(
-      base::Bind(&EngineStats::Update, base::Unretained(this)),
-      base::TimeDelta::FromMilliseconds(kPollerPeriodMs)));
 }
 
 }  // namespace
@@ -158,6 +145,10 @@
 
 void MozjsEngine::ReportExtraMemoryCost(size_t bytes) { NOTIMPLEMENTED(); }
 
+size_t MozjsEngine::UpdateMemoryStatsAndReturnReserved() {
+  return EngineStats::GetInstance()->UpdateMemoryStatsAndReturnReserved();
+}
+
 JSBool MozjsEngine::ContextCallback(JSContext* context, unsigned context_op) {
   JSRuntime* runtime = JS_GetRuntime(context);
   MozjsEngine* engine =
diff --git a/src/cobalt/script/mozjs/mozjs_engine.h b/src/cobalt/script/mozjs/mozjs_engine.h
index a678a15..2a86ce5 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.h
+++ b/src/cobalt/script/mozjs/mozjs_engine.h
@@ -34,6 +34,7 @@
   scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
   void CollectGarbage() OVERRIDE;
   void ReportExtraMemoryCost(size_t bytes) OVERRIDE;
+  size_t UpdateMemoryStatsAndReturnReserved() OVERRIDE;
 
  private:
   static JSBool ContextCallback(JSContext* context, unsigned context_op);
diff --git a/src/cobalt/speech/SpeechRecognitionResult.idl b/src/cobalt/speech/SpeechRecognitionResult.idl
index 6cc3071..ef1e97d 100644
--- a/src/cobalt/speech/SpeechRecognitionResult.idl
+++ b/src/cobalt/speech/SpeechRecognitionResult.idl
@@ -19,5 +19,6 @@
 interface SpeechRecognitionResult {
   readonly attribute unsigned long length;
   getter SpeechRecognitionAlternative item(unsigned long index);
-  readonly attribute boolean final;
+  // isFinal is different from the spec., but follows with Chromium.
+  readonly attribute boolean isFinal;
 };
diff --git a/src/cobalt/speech/mic.h b/src/cobalt/speech/mic.h
index 4eac5e1..b83b875 100644
--- a/src/cobalt/speech/mic.h
+++ b/src/cobalt/speech/mic.h
@@ -17,6 +17,8 @@
 #ifndef COBALT_SPEECH_MIC_H_
 #define COBALT_SPEECH_MIC_H_
 
+#include <string>
+
 #include "base/callback.h"
 #include "media/base/audio_bus.h"
 
@@ -60,6 +62,8 @@
   const ErrorCallback error_callback_;
 };
 
+std::string GetSpeechAPIKey();
+
 }  // namespace speech
 }  // namespace cobalt
 
diff --git a/src/cobalt/speech/mic_linux.cc b/src/cobalt/speech/mic_linux.cc
index 5621116..d7f6809 100644
--- a/src/cobalt/speech/mic_linux.cc
+++ b/src/cobalt/speech/mic_linux.cc
@@ -38,5 +38,7 @@
       new MicLinux(sample_rate, data_received, completion, error));
 }
 
+std::string GetSpeechAPIKey() { return ""; }
+
 }  // namespace speech
 }  // namespace cobalt
diff --git a/src/cobalt/speech/mic_starboard.cc b/src/cobalt/speech/mic_starboard.cc
index 1b164ba..c818dab 100644
--- a/src/cobalt/speech/mic_starboard.cc
+++ b/src/cobalt/speech/mic_starboard.cc
@@ -38,5 +38,7 @@
       new MicStarboard(sample_rate, data_received, completion, error));
 }
 
+std::string GetSpeechAPIKey() { return ""; }
+
 }  // namespace speech
 }  // namespace cobalt
diff --git a/src/cobalt/speech/mic_win.cc b/src/cobalt/speech/mic_win.cc
index 2025566..80c49fc 100644
--- a/src/cobalt/speech/mic_win.cc
+++ b/src/cobalt/speech/mic_win.cc
@@ -38,5 +38,7 @@
       new MicWin(sample_rate, data_received, completion, error));
 }
 
+std::string GetSpeechAPIKey() { return ""; }
+
 }  // namespace speech
 }  // namespace cobalt
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index 264a7e2..90ba3b1 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -56,6 +56,7 @@
         '<(DEPTH)/cobalt/base/base.gyp:base',
         '<(DEPTH)/cobalt/dom/dom.gyp:dom',
         '<(DEPTH)/third_party/flac/flac.gyp:libflac',
+        '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
       ],
       'include_dirs': [
         # Get protobuf headers from the chromium tree.
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index 6f6966e..d7cd9dd 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -34,9 +34,7 @@
       event_callback_(event_callback),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           recognizer_(network_module,
-                      base::Bind(&SpeechRecognitionManager::OnRecognizerResult,
-                                 base::Unretained(this)),
-                      base::Bind(&SpeechRecognitionManager::OnRecognizerError,
+                      base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
                                  base::Unretained(this)))),
       ALLOW_THIS_IN_INITIALIZER_LIST(mic_(Mic::Create(
           kSampleRate, base::Bind(&SpeechRecognitionManager::OnDataReceived,
@@ -93,12 +91,12 @@
   recognizer_.RecognizeAudio(dummy_audio_bus.Pass(), true);
 }
 
-void SpeechRecognitionManager::OnRecognizerResult(
-    const scoped_refptr<SpeechRecognitionEvent>& event) {
+void SpeechRecognitionManager::OnRecognizerEvent(
+    const scoped_refptr<dom::Event>& event) {
   if (!main_message_loop_->BelongsToCurrentThread()) {
     // Called from recognizer thread.
     main_message_loop_->PostTask(
-        FROM_HERE, base::Bind(&SpeechRecognitionManager::OnRecognizerResult,
+        FROM_HERE, base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
                               weak_this_, event));
     return;
   }
@@ -106,21 +104,6 @@
   event_callback_.Run(event);
 }
 
-void SpeechRecognitionManager::OnRecognizerError() {
-  if (!main_message_loop_->BelongsToCurrentThread()) {
-    // Called from recognizer thread.
-    main_message_loop_->PostTask(
-        FROM_HERE,
-        base::Bind(&SpeechRecognitionManager::OnRecognizerError, weak_this_));
-    return;
-  }
-
-  // TODO: Could be other error types based on the recognizer response.
-  event_callback_.Run(
-      scoped_refptr<SpeechRecognitionError>(new SpeechRecognitionError(
-          SpeechRecognitionError::kNetwork, "Recognition Failed.")));
-}
-
 void SpeechRecognitionManager::OnMicError() {
   if (!main_message_loop_->BelongsToCurrentThread()) {
     // Called from mic thread.
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index dda56b2..4c20577 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -55,8 +55,7 @@
   void OnMicError();
 
   // Callbacks from recognizer.
-  void OnRecognizerResult(const scoped_refptr<SpeechRecognitionEvent>& event);
-  void OnRecognizerError();
+  void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
 
   base::WeakPtrFactory<SpeechRecognitionManager> weak_ptr_factory_;
   // We construct a WeakPtr upon SpeechRecognitionManager's construction in
diff --git a/src/cobalt/speech/speech_recognition_result.h b/src/cobalt/speech/speech_recognition_result.h
index 6ebb193..a7432b1 100644
--- a/src/cobalt/speech/speech_recognition_result.h
+++ b/src/cobalt/speech/speech_recognition_result.h
@@ -49,7 +49,7 @@
   // The final MUST be set to true if this is the final time the speech service
   // will return this particular index value. If the value is false, then this
   // represents an interim result that could still be changed.
-  bool final() const { return final_; }
+  bool is_final() const { return final_; }
 
   DEFINE_WRAPPABLE_TYPE(SpeechRecognitionResult);
 
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc
index f46df90..a9f27b0 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -21,8 +21,12 @@
 #include "base/string_number_conversions.h"
 #include "base/string_util.h"
 #include "base/utf_string_conversions.h"
+#include "cobalt/deprecated/platform_delegate.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/network/network_module.h"
+#include "cobalt/speech/google_streaming_api.pb.h"
+#include "cobalt/speech/mic.h"
+#include "cobalt/speech/speech_recognition_error.h"
 #include "net/base/escape.h"
 #include "net/url_request/url_fetcher.h"
 
@@ -32,8 +36,6 @@
 namespace {
 const char kBaseStreamURL[] =
     "https://www.google.com/speech-api/full-duplex/v1";
-// TODO: hide this key to somewhere else.
-const char kSpeechAPIKey[] = "";
 const char kUp[] = "up";
 const char kDown[] = "down";
 const char kClient[] = "com.speech.tv";
@@ -66,16 +68,85 @@
   return url.ReplaceComponents(replacements);
 }
 
+SpeechRecognitionResultList::SpeechRecognitionResults
+ProcessProtoSuccessResults(const proto::SpeechRecognitionEvent& event) {
+  DCHECK_EQ(event.status(), proto::SpeechRecognitionEvent::STATUS_SUCCESS);
+
+  SpeechRecognitionResultList::SpeechRecognitionResults results;
+  for (int i = 0; i < event.result_size(); ++i) {
+    SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
+    const proto::SpeechRecognitionResult& kProtoResult = event.result(i);
+    for (int j = 0; j < kProtoResult.alternative_size(); ++j) {
+      const proto::SpeechRecognitionAlternative& kAlternative =
+          kProtoResult.alternative(j);
+      scoped_refptr<SpeechRecognitionAlternative> alternative(
+          new SpeechRecognitionAlternative(kAlternative.transcript(),
+                                           kAlternative.confidence()));
+      alternatives.push_back(alternative);
+    }
+    bool final = kProtoResult.has_final() && kProtoResult.final();
+    scoped_refptr<SpeechRecognitionResult> recognition_result(
+        new SpeechRecognitionResult(alternatives, final));
+    results.push_back(recognition_result);
+  }
+  return results;
+}
+
+// TODO: Feed error messages when creating SpeechRecognitionError.
+void ProcessAndFireErrorEvent(
+    const proto::SpeechRecognitionEvent& event,
+    const SpeechRecognizer::EventCallback& event_callback) {
+  scoped_refptr<dom::Event> error_event;
+  switch (event.status()) {
+    case proto::SpeechRecognitionEvent::STATUS_SUCCESS:
+      NOTREACHED();
+      return;
+    case proto::SpeechRecognitionEvent::STATUS_NO_SPEECH:
+      error_event =
+          new SpeechRecognitionError(SpeechRecognitionError::kNoSpeech, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_ABORTED:
+      error_event =
+          new SpeechRecognitionError(SpeechRecognitionError::kAborted, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_AUDIO_CAPTURE:
+      error_event =
+          new SpeechRecognitionError(SpeechRecognitionError::kAudioCapture, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_NETWORK:
+      error_event =
+          new SpeechRecognitionError(SpeechRecognitionError::kNetwork, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_NOT_ALLOWED:
+      error_event =
+          new SpeechRecognitionError(SpeechRecognitionError::kNotAllowed, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_SERVICE_NOT_ALLOWED:
+      error_event = new SpeechRecognitionError(
+          SpeechRecognitionError::kServiceNotAllowed, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_BAD_GRAMMAR:
+      error_event =
+          new SpeechRecognitionError(SpeechRecognitionError::kBadGrammar, "");
+      break;
+    case proto::SpeechRecognitionEvent::STATUS_LANGUAGE_NOT_SUPPORTED:
+      error_event = new SpeechRecognitionError(
+          SpeechRecognitionError::kLanguageNotSupported, "");
+      break;
+  }
+
+  DCHECK(error_event);
+  event_callback.Run(error_event);
+}
+
 }  // namespace
 
 SpeechRecognizer::SpeechRecognizer(network::NetworkModule* network_module,
-                                   const ResultCallback& result_callback,
-                                   const ErrorCallback& error_callback)
+                                   const EventCallback& event_callback)
     : network_module_(network_module),
       thread_("speech_recognizer"),
       started_(false),
-      result_callback_(result_callback),
-      error_callback_(error_callback) {
+      event_callback_(event_callback) {
   thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
 }
 
@@ -110,11 +181,26 @@
 void SpeechRecognizer::OnURLFetchDownloadData(
     const net::URLFetcher* source, scoped_ptr<std::string> download_data) {
   DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-  // TODO: Parse the serialized protocol buffers data.
-  NOTIMPLEMENTED();
 
-  UNREFERENCED_PARAMETER(source);
-  UNREFERENCED_PARAMETER(download_data);
+  if (source == downstream_fetcher_.get()) {
+    chunked_byte_buffer_.Append(*download_data);
+    while (chunked_byte_buffer_.HasChunks()) {
+      scoped_ptr<std::vector<uint8_t> > chunk =
+          chunked_byte_buffer_.PopChunk().Pass();
+
+      proto::SpeechRecognitionEvent event;
+      if (!event.ParseFromString(std::string(chunk->begin(), chunk->end()))) {
+        DLOG(WARNING) << "Parse proto string error.";
+        return;
+      }
+
+      if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
+        ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
+      } else {
+        ProcessAndFireErrorEvent(event, event_callback_);
+      }
+    }
+  }
 }
 
 void SpeechRecognizer::OnURLFetchComplete(const net::URLFetcher* source) {
@@ -157,10 +243,15 @@
   up_url = AppendQueryParameter(up_url, "client", kClient);
   up_url = AppendQueryParameter(up_url, "pair", pair);
   up_url = AppendQueryParameter(up_url, "output", "pb");
-  up_url = AppendQueryParameter(up_url, "key", kSpeechAPIKey);
+  up_url = AppendQueryParameter(up_url, "key", GetSpeechAPIKey());
 
+  // Language is required. If no language is specified, use the system language.
   if (!config.lang.empty()) {
     up_url = AppendQueryParameter(up_url, "lang", config.lang);
+  } else {
+    up_url = AppendQueryParameter(
+        up_url, "lang",
+        cobalt::deprecated::PlatformDelegate::Get()->GetSystemLanguage());
   }
 
   if (config.max_alternatives) {
@@ -195,6 +286,9 @@
   upstream_fetcher_.reset();
   downstream_fetcher_.reset();
   encoder_.reset();
+
+  // Clear the final results.
+  final_results_.clear();
 }
 
 void SpeechRecognizer::UploadAudioDataInternal(scoped_ptr<AudioBus> audio_bus,
@@ -216,5 +310,31 @@
   }
 }
 
+void SpeechRecognizer::ProcessAndFireSuccessEvent(
+    const SpeechRecognitionResults& new_results) {
+  SpeechRecognitionResults success_results;
+  size_t total_size = final_results_.size() + new_results.size();
+  success_results.reserve(total_size);
+  success_results = final_results_;
+  success_results.insert(success_results.end(), new_results.begin(),
+                         new_results.end());
+
+  size_t result_index = final_results_.size();
+  // Update final results list.
+  for (size_t i = 0; i < new_results.size(); ++i) {
+    if (new_results[i]->is_final()) {
+      final_results_.push_back(new_results[i]);
+    }
+  }
+
+  scoped_refptr<SpeechRecognitionResultList> recognition_list(
+      new SpeechRecognitionResultList(success_results));
+  scoped_refptr<SpeechRecognitionEvent> recognition_event(
+      new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
+                                 static_cast<uint32>(result_index),
+                                 recognition_list));
+  event_callback_.Run(recognition_event);
+}
+
 }  // namespace speech
 }  // namespace cobalt
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h
index 7b54584..cb76a05 100644
--- a/src/cobalt/speech/speech_recognizer.h
+++ b/src/cobalt/speech/speech_recognizer.h
@@ -23,6 +23,7 @@
 #include "base/threading/thread.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/speech/audio_encoder_flac.h"
+#include "cobalt/speech/chunked_byte_buffer.h"
 #include "cobalt/speech/speech_recognition_config.h"
 #include "cobalt/speech/speech_recognition_event.h"
 #include "media/base/audio_bus.h"
@@ -42,13 +43,12 @@
 class SpeechRecognizer : public net::URLFetcherDelegate {
  public:
   typedef ::media::AudioBus AudioBus;
-  typedef base::Callback<void(const scoped_refptr<SpeechRecognitionEvent>&)>
-      ResultCallback;
-  typedef base::Callback<void(void)> ErrorCallback;
+  typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
+  typedef SpeechRecognitionResultList::SpeechRecognitionResults
+      SpeechRecognitionResults;
 
   SpeechRecognizer(network::NetworkModule* network_module,
-                   const ResultCallback& result_callback,
-                   const ErrorCallback& error_callback);
+                   const EventCallback& event_callback);
   ~SpeechRecognizer() OVERRIDE;
 
   // Multiple calls to Start/Stop are allowed, the implementation should take
@@ -73,6 +73,7 @@
   void StopInternal();
   void UploadAudioDataInternal(scoped_ptr<AudioBus> audio_bus,
                                bool is_last_chunk);
+  void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
 
   // This is used for creating fetchers.
   network::NetworkModule* network_module_;
@@ -87,8 +88,11 @@
   scoped_ptr<net::URLFetcher> upstream_fetcher_;
   // Fetcher for receiving the streaming results.
   scoped_ptr<net::URLFetcher> downstream_fetcher_;
-  ResultCallback result_callback_;
-  ErrorCallback error_callback_;
+  EventCallback event_callback_;
+  // Used for processing proto buffer data.
+  ChunkedByteBuffer chunked_byte_buffer_;
+  // Used for accumulating final results.
+  SpeechRecognitionResults final_results_;
 };
 
 }  // namespace speech
diff --git a/src/cobalt/webdriver/element_driver.cc b/src/cobalt/webdriver/element_driver.cc
index a5c3315..6d05638 100644
--- a/src/cobalt/webdriver/element_driver.cc
+++ b/src/cobalt/webdriver/element_driver.cc
@@ -64,10 +64,12 @@
 ElementDriver::ElementDriver(
     const protocol::ElementId& element_id,
     const base::WeakPtr<dom::Element>& element, ElementMapping* element_mapping,
+    KeyboardEventInjector keyboard_event_injector,
     const scoped_refptr<base::MessageLoopProxy>& message_loop)
     : element_id_(element_id),
       element_(element),
       element_mapping_(element_mapping),
+      keyboard_injector_(keyboard_event_injector),
       element_message_loop_(message_loop) {}
 
 util::CommandResult<std::string> ElementDriver::GetTagName() {
@@ -157,7 +159,7 @@
 }
 
 util::CommandResult<void> ElementDriver::SendKeysInternal(
-    scoped_ptr<KeyboardEventVector> events) {
+    scoped_ptr<Keyboard::KeyboardEventVector> events) {
   typedef util::CommandResult<void> CommandResult;
   DCHECK_EQ(base::MessageLoopProxy::current(), element_message_loop_);
   if (!element_) {
@@ -174,7 +176,8 @@
     if (!element_) {
       return CommandResult(protocol::Response::kStaleElementReference);
     }
-    element_->DispatchEvent((*events)[i]);
+
+    keyboard_injector_.Run(element_.get(), (*events)[i]);
   }
   return CommandResult(protocol::Response::kSuccess);
 }
diff --git a/src/cobalt/webdriver/element_driver.h b/src/cobalt/webdriver/element_driver.h
index 22e2ee0..645a0e0 100644
--- a/src/cobalt/webdriver/element_driver.h
+++ b/src/cobalt/webdriver/element_driver.h
@@ -28,6 +28,7 @@
 #include "cobalt/dom/element.h"
 #include "cobalt/dom/keyboard_event.h"
 #include "cobalt/webdriver/element_mapping.h"
+#include "cobalt/webdriver/keyboard.h"
 #include "cobalt/webdriver/protocol/element_id.h"
 #include "cobalt/webdriver/protocol/keys.h"
 #include "cobalt/webdriver/protocol/search_strategy.h"
@@ -46,9 +47,14 @@
 // will map to a method on this class.
 class ElementDriver {
  public:
+  typedef base::Callback<void(scoped_refptr<dom::Element>,
+                              const dom::KeyboardEvent::Data&)>
+      KeyboardEventInjector;
+
   ElementDriver(const protocol::ElementId& element_id,
                 const base::WeakPtr<dom::Element>& element,
                 ElementMapping* element_mapping,
+                KeyboardEventInjector keyboard_injector,
                 const scoped_refptr<base::MessageLoopProxy>& message_loop);
   const protocol::ElementId& element_id() { return element_id_; }
 
@@ -67,7 +73,6 @@
       const std::string& property_name);
 
  private:
-  typedef std::vector<scoped_refptr<dom::KeyboardEvent> > KeyboardEventVector;
   typedef std::vector<protocol::ElementId> ElementIdVector;
 
   // Get the dom::Element* that this ElementDriver wraps. This must be called
@@ -75,7 +80,7 @@
   dom::Element* GetWeakElement();
 
   util::CommandResult<void> SendKeysInternal(
-      scoped_ptr<KeyboardEventVector> keyboard_events);
+      scoped_ptr<Keyboard::KeyboardEventVector> keyboard_events);
 
   // Shared logic between FindElement and FindElements.
   template <typename T>
@@ -90,6 +95,7 @@
   // These should only be accessed from |element_message_loop_|.
   base::WeakPtr<dom::Element> element_;
   ElementMapping* element_mapping_;
+  KeyboardEventInjector keyboard_injector_;
   scoped_refptr<base::MessageLoopProxy> element_message_loop_;
 
   friend class WindowDriver;
diff --git a/src/cobalt/webdriver/keyboard.cc b/src/cobalt/webdriver/keyboard.cc
index c69496f..32d4fbc 100644
--- a/src/cobalt/webdriver/keyboard.cc
+++ b/src/cobalt/webdriver/keyboard.cc
@@ -421,7 +421,7 @@
                    KeyLocationCode location) {
     const bool kIsRepeat = false;
     uint32 modifiers = GetModifierStateBitfield();
-    event_vector_->push_back(new dom::KeyboardEvent(
+    event_vector_->push_back(dom::KeyboardEvent::Data(
         type, location, modifiers, key_code, char_code, kIsRepeat));
   }
 
diff --git a/src/cobalt/webdriver/keyboard.h b/src/cobalt/webdriver/keyboard.h
index feeab6f..57a5c5f 100644
--- a/src/cobalt/webdriver/keyboard.h
+++ b/src/cobalt/webdriver/keyboard.h
@@ -32,7 +32,8 @@
     kReleaseModifiers,
     kKeepModifiers,
   };
-  typedef std::vector<scoped_refptr<dom::KeyboardEvent> > KeyboardEventVector;
+  typedef std::vector<dom::KeyboardEvent::Data>
+      KeyboardEventVector;
   static void TranslateToKeyEvents(const std::string& utf8_keys,
                                    TerminationBehaviour termination_behaviour,
                                    KeyboardEventVector* out_events);
diff --git a/src/cobalt/webdriver/keyboard_test.cc b/src/cobalt/webdriver/keyboard_test.cc
index 1f71598..7994bd5 100644
--- a/src/cobalt/webdriver/keyboard_test.cc
+++ b/src/cobalt/webdriver/keyboard_test.cc
@@ -31,25 +31,34 @@
 namespace webdriver {
 namespace {
 
-int32 GetKeyCode(const scoped_refptr<dom::KeyboardEvent>& event) {
-  DCHECK(event);
-  return event->key_code();
+int32 GetKeyCode(const dom::KeyboardEvent::Data& event) {
+  scoped_refptr<dom::KeyboardEvent> keyboard_event(
+            new dom::KeyboardEvent(event));
+  return keyboard_event->key_code();
 }
 
-int32 GetCharCode(const scoped_refptr<dom::KeyboardEvent>& event) {
-  return event->char_code();
+int32 GetCharCode(const dom::KeyboardEvent::Data& event) {
+  scoped_refptr<dom::KeyboardEvent> keyboard_event(
+            new dom::KeyboardEvent(event));
+  return keyboard_event->char_code();
 }
 
-uint32 GetModifierBitfield(const scoped_refptr<dom::KeyboardEvent>& event) {
-  return event->modifiers();
+uint32 GetModifierBitfield(const dom::KeyboardEvent::Data& event) {
+  scoped_refptr<dom::KeyboardEvent> keyboard_event(
+            new dom::KeyboardEvent(event));
+  return keyboard_event->modifiers();
 }
 
-std::string GetType(const scoped_refptr<dom::KeyboardEvent>& event) {
-  return event->type().c_str();
+std::string GetType(const dom::KeyboardEvent::Data& event) {
+  scoped_refptr<dom::KeyboardEvent> keyboard_event(
+            new dom::KeyboardEvent(event));
+  return keyboard_event->type().c_str();
 }
 
-int GetLocation(const scoped_refptr<dom::KeyboardEvent>& event) {
-  return event->location();
+int GetLocation(const dom::KeyboardEvent::Data& event) {
+  scoped_refptr<dom::KeyboardEvent> keyboard_event(
+            new dom::KeyboardEvent(event));
+  return keyboard_event->location();
 }
 
 class KeyboardTest : public ::testing::Test {
diff --git a/src/cobalt/webdriver/protocol/server_status.cc b/src/cobalt/webdriver/protocol/server_status.cc
index e98ebe2..bcb283c 100644
--- a/src/cobalt/webdriver/protocol/server_status.cc
+++ b/src/cobalt/webdriver/protocol/server_status.cc
@@ -16,9 +16,6 @@
 
 #include "cobalt/webdriver/protocol/server_status.h"
 
-// TODO: Support running WebDriver on platforms other than Linux.
-#include <sys/utsname.h>
-
 #include "cobalt/version.h"
 
 namespace cobalt {
@@ -26,11 +23,26 @@
 namespace protocol {
 
 ServerStatus::ServerStatus() {
-  struct utsname name_buffer;
-  if (uname(&name_buffer) == 0) {
-    os_name_ = name_buffer.sysname;
-    os_version_ = name_buffer.version;
-    os_arch_ = name_buffer.machine;
+  const size_t kSystemPropertyMaxLength = 1024;
+  char value[kSystemPropertyMaxLength];
+  bool result;
+
+  result = SbSystemGetProperty(kSbSystemPropertyPlatformName, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    os_name_ = value;
+  }
+
+  result = SbSystemGetProperty(kSbSystemPropertyFirmwareVersion, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    os_version_ = value;
+  }
+
+  result = SbSystemGetProperty(kSbSystemPropertyChipsetModelNumber, value,
+                               kSystemPropertyMaxLength);
+  if (result) {
+    os_arch_ = value;
   }
   build_time_ = __DATE__ " (" __TIME__ ")";
   build_version_ = COBALT_VERSION;
@@ -42,9 +54,16 @@
   build_value->SetString("version", status.build_version_);
 
   scoped_ptr<base::DictionaryValue> os_value(new base::DictionaryValue());
-  os_value->SetString("arch", status.os_arch_);
-  os_value->SetString("name", status.os_name_);
-  os_value->SetString("version", status.os_version_);
+
+  if (status.os_arch_) {
+    os_value->SetString("arch", *status.os_arch_);
+  }
+  if (status.os_name_) {
+    os_value->SetString("name", *status.os_name_);
+  }
+  if (status.os_version_) {
+    os_value->SetString("version", *status.os_version_);
+  }
 
   scoped_ptr<base::DictionaryValue> status_value(new base::DictionaryValue());
   status_value->Set("os", os_value.release());
diff --git a/src/cobalt/webdriver/protocol/server_status.h b/src/cobalt/webdriver/protocol/server_status.h
index dcd825a..cdef9a4 100644
--- a/src/cobalt/webdriver/protocol/server_status.h
+++ b/src/cobalt/webdriver/protocol/server_status.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
 #include "base/values.h"
 
 namespace cobalt {
@@ -36,9 +37,9 @@
   static scoped_ptr<base::Value> ToValue(const ServerStatus& status);
 
  private:
-  std::string os_name_;
-  std::string os_arch_;
-  std::string os_version_;
+  base::optional<std::string> os_name_;
+  base::optional<std::string> os_arch_;
+  base::optional<std::string> os_version_;
   std::string build_time_;
   std::string build_version_;
 };
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index 3e77e54..a21d3b6 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -158,10 +158,12 @@
     const protocol::WindowId& window_id,
     const base::WeakPtr<dom::Window>& window,
     const GetGlobalEnvironmentFunction& get_global_environment_function,
+    KeyboardEventInjector keyboard_injector,
     const scoped_refptr<base::MessageLoopProxy>& message_loop)
     : window_id_(window_id),
       window_(window),
       get_global_environment_(get_global_environment_function),
+      keyboard_injector_(keyboard_injector),
       window_message_loop_(message_loop),
       element_driver_map_deleter_(&element_drivers_),
       next_element_id_(0) {
@@ -351,6 +353,7 @@
   std::pair<ElementDriverMapIt, bool> pair_it =
       element_drivers_.insert(std::make_pair(
           element_id.id(), new ElementDriver(element_id, weak_element, this,
+                                             keyboard_injector_,
                                              window_message_loop_)));
   DCHECK(pair_it.second)
       << "An ElementDriver was already mapped to the element id: "
@@ -418,7 +421,7 @@
 }
 
 util::CommandResult<void> WindowDriver::SendKeysInternal(
-    scoped_ptr<KeyboardEventVector> events) {
+    scoped_ptr<Keyboard::KeyboardEventVector> events) {
   typedef util::CommandResult<void> CommandResult;
   DCHECK_EQ(base::MessageLoopProxy::current(), window_message_loop_);
   if (!window_) {
@@ -426,8 +429,7 @@
   }
 
   for (size_t i = 0; i < events->size(); ++i) {
-    // InjectEvent will send to the focused element.
-    window_->InjectEvent((*events)[i]);
+    keyboard_injector_.Run(scoped_refptr<dom::Element>(), (*events)[i]);
   }
   return CommandResult(protocol::Response::kSuccess);
 }
diff --git a/src/cobalt/webdriver/window_driver.h b/src/cobalt/webdriver/window_driver.h
index ae03a9f..f67d272 100644
--- a/src/cobalt/webdriver/window_driver.h
+++ b/src/cobalt/webdriver/window_driver.h
@@ -34,6 +34,7 @@
 #include "cobalt/dom/window.h"
 #include "cobalt/webdriver/element_driver.h"
 #include "cobalt/webdriver/element_mapping.h"
+#include "cobalt/webdriver/keyboard.h"
 #include "cobalt/webdriver/protocol/cookie.h"
 #include "cobalt/webdriver/protocol/frame_id.h"
 #include "cobalt/webdriver/protocol/keys.h"
@@ -54,11 +55,15 @@
 // will map to a method on this class.
 class WindowDriver : private ElementMapping {
  public:
+  typedef base::Callback<void(scoped_refptr<dom::Element>,
+                              const dom::KeyboardEvent::Data&)>
+      KeyboardEventInjector;
   typedef base::Callback<scoped_refptr<script::GlobalEnvironment>()>
       GetGlobalEnvironmentFunction;
   WindowDriver(const protocol::WindowId& window_id,
                const base::WeakPtr<dom::Window>& window,
                const GetGlobalEnvironmentFunction& get_global_environment,
+               KeyboardEventInjector keyboard_injector,
                const scoped_refptr<base::MessageLoopProxy>& message_loop);
   ~WindowDriver();
   const protocol::WindowId& window_id() { return window_id_; }
@@ -89,7 +94,6 @@
   typedef base::hash_map<std::string, ElementDriver*> ElementDriverMap;
   typedef ElementDriverMap::iterator ElementDriverMapIt;
   typedef std::vector<protocol::ElementId> ElementIdVector;
-  typedef std::vector<scoped_refptr<dom::KeyboardEvent> > KeyboardEventVector;
 
   // ScriptExecutor::ElementMapping implementation.
   protocol::ElementId ElementToId(
@@ -116,7 +120,7 @@
       const protocol::Script& script);
 
   util::CommandResult<void> SendKeysInternal(
-      scoped_ptr<KeyboardEventVector> keyboard_events);
+      scoped_ptr<Keyboard::KeyboardEventVector> keyboard_events);
 
   util::CommandResult<void> NavigateInternal(const GURL& url);
 
@@ -129,6 +133,8 @@
   // Bound to the WebDriver thread.
   base::ThreadChecker thread_checker_;
 
+  KeyboardEventInjector keyboard_injector_;
+
   // Anything that interacts with the window must be run on this message loop.
   scoped_refptr<base::MessageLoopProxy> window_message_loop_;
 
diff --git a/src/cobalt/webdriver_benchmarks/__init__.py b/src/cobalt/webdriver_benchmarks/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/__init__.py
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
new file mode 100755
index 0000000..41503c6
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -0,0 +1,221 @@
+#!/usr/bin/python
+"""Webdriver-based benchmarks for partial layout.
+
+Benchmarks for partial layout changes in Cobalt.
+"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import importlib
+import os
+import re
+import socket
+import sys
+import threading
+import unittest
+
+arg_parser = argparse.ArgumentParser(
+    description="Runs Webdriver-based Cobalt benchmarks")
+arg_parser.add_argument(
+    "-p", "--platform",
+    help="Cobalt platform, eg 'linux-x64x11'."
+    "Fetched from environment if absent.")
+arg_parser.add_argument(
+    "-e", "--executable",
+    help="Path to cobalt executable. "
+    "Auto-derived if absent.")
+arg_parser.add_argument(
+    "-c", "--config",
+    choices=["debug", "devel", "qa", "gold"],
+    help="Build config (eg, 'qa' or 'devel'). Not used if "
+    "--executable is specified. Fetched from environment "
+    "if needed and absent.")
+arg_parser.add_argument(
+    "-d", "--devkit_name",
+    help="Devkit or IP address for app_launcher."
+    "Current hostname used if absent.")
+arg_parser.add_argument(
+    "-o", "--log_file", help="Logfile pathname. stdout if absent.")
+
+# Pattern to match Cobalt log line for when the WebDriver port has been
+# opened.
+RE_WEBDRIVER_LISTEN = re.compile(r"Starting WebDriver server on port (\d+)$")
+
+COBALT_WEBDRIVER_CAPABILITIES = {
+    "browserName": "cobalt",
+    "javascriptEnabled": True,
+    "platform": "LINUX"
+}
+
+_webdriver = None
+
+
+def GetWebDriver():
+  """Returns the active connect WebDriver instance."""
+  return _webdriver
+
+
+def ImportSeleniumModule(submodule=None):
+  """Dynamically imports a selenium.webdriver submodule.
+
+  This is done because selenium 3.0 is not commonly pre-installed
+  on workstations, and we want to have a friendly error message for that
+  case.
+
+  Args:
+    submodule: module subpath underneath "selenium.webdriver"
+  Returns:
+    appropriate module
+  """
+  if submodule:
+    module_path = ".".join(("selenium", submodule))
+  else:
+    module_path = "selenium"
+  # As of this writing, Google uses selenium 3.0.0b2 internally, so
+  # thats what we will target here as well.
+  try:
+    module = importlib.import_module(module_path)
+    if submodule is None:
+      # Only the top-level module has __version__
+      if not module.__version__.startswith("3.0"):
+        raise ImportError("Not version 3.0.x")
+  except ImportError:
+    sys.stderr.write("Could not import {}\n"
+                     "Please install selenium >= 3.0.0b2.\n"
+                     "Commonly: \"sudo pip install 'selenium>=3.0.0b2'\"\n"
+                     .format(module_path))
+    sys.exit(1)
+  return module
+
+
+class CobaltRunner(object):
+  """Runs a Cobalt browser w/ a WebDriver client attached."""
+  test_script_started = threading.Event()
+  should_exit = threading.Event()
+  selenium_webdriver_module = None
+  webdriver = None
+  launcher = None
+  log_file_path = None
+  thread = None
+
+  def __init__(self, platform, executable, devkit_name, log_file_path):
+    self.selenium_webdriver_module = ImportSeleniumModule("webdriver")
+
+    script_path = os.path.dirname(__file__)
+    sys.path.append(script_path + "/../../tools/lbshell/")
+    app_launcher = importlib.import_module("app_launcher")
+    self.launcher = app_launcher.CreateLauncher(
+        platform, executable, devkit_name=devkit_name)
+
+    self.launcher.SetArgs(["--enable_webdriver"])
+    self.launcher.SetOutputCallback(self._HandleLine)
+    self.log_file_path = log_file_path
+
+  def __enter__(self):
+    self.thread = threading.Thread(target=self.Run)
+    self.thread.start()
+    self.WaitForStart()
+
+  def __exit__(self, exc_type, exc_value, traceback):
+    self.SetShouldExit()
+    self.thread.join()
+
+  def _HandleLine(self, line):
+    """Internal log line callback."""
+
+    done = self.should_exit.is_set()
+    # Wait for WebDriver port here then connect
+    if self.test_script_started.is_set():
+      return done
+
+    match = RE_WEBDRIVER_LISTEN.search(line)
+    if not match:
+      return done
+
+    port = match.group(1)
+    self._StartWebdriver(port)
+    return done
+
+  def SetShouldExit(self):
+    """Indicates cobalt process should exit. Done at next log line output."""
+    self.should_exit.set()
+
+  def _GetIPAddress(self):
+    return self.launcher.GetIPAddress()
+
+  def _StartWebdriver(self, port):
+    global _webdriver
+    url = "http://{}:{}/".format(self._GetIPAddress(), port)
+    self.webdriver = self.selenium_webdriver_module.Remote(
+        url, COBALT_WEBDRIVER_CAPABILITIES)
+    _webdriver = self.webdriver
+    self.test_script_started.set()
+
+  def WaitForStart(self):
+    """Waits for the webdriver client to attach to cobalt."""
+    self.test_script_started.wait()
+
+  def Run(self):
+    """Thread run routine."""
+
+    # Use stdout if log_file_path is unspecified
+    # If log_file_path is specified, make sure to close it
+    to_close = None
+    try:
+      if self.log_file_path:
+        log_file = open(self.log_file_path, "w")
+        to_close = log_file
+      else:
+        log_file = sys.stdout
+
+      self.launcher.SetOutputFile(log_file)
+      self.launcher.Run()
+    finally:
+      if to_close:
+        to_close.close()
+    return 0
+
+
+def GetCobaltExecutablePath(platform, config):
+  """Auto-derives a path to a cobalt executable."""
+  if config is None:
+    try:
+      config = os.environ["BUILD_TYPE"]
+    except KeyError:
+      sys.stderr.write("Must specify --config or --executable\n")
+      sys.exit(1)
+  script_dir = os.path.dirname(os.path.realpath(__file__))
+  out_dir = os.path.join(script_dir, "..", "..", "out")
+  executable_directory = os.path.join(out_dir, "{}_{}".format(platform, config))
+  return os.path.join(executable_directory, "cobalt")
+
+
+def main():
+  args = arg_parser.parse_args()
+
+  platform = args.platform
+  if platform is None:
+    try:
+      platform = os.environ["BUILD_PLATFORM"]
+    except KeyError:
+      sys.stderr.write("Must specify --platform\n")
+      sys.exit(1)
+
+  executable = args.executable
+  if executable is None:
+    executable = GetCobaltExecutablePath(platform, args.config)
+
+  devkit_name = args.devkit_name
+  if devkit_name is None:
+    devkit_name = socket.gethostname()
+
+  with CobaltRunner(platform, executable, devkit_name, args.log_file):
+    unittest.main()
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/src/cobalt/webdriver_benchmarks/tests/__init__.py b/src/cobalt/webdriver_benchmarks/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tests/__init__.py
diff --git a/src/cobalt/webdriver_benchmarks/tests/shelf.py b/src/cobalt/webdriver_benchmarks/tests/shelf.py
new file mode 100755
index 0000000..b81478b
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tests/shelf.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+"""Simple shelf navigation test."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+import time
+
+# The parent directory is a module
+sys.path.insert(0, os.path.dirname(os.path.dirname(
+    os.path.realpath(__file__))))
+
+# pylint: disable=C6204,C6203
+import tv
+import tv_testcase
+import partial_layout_benchmark
+
+# selenium imports
+keys = partial_layout_benchmark.ImportSeleniumModule("webdriver.common.keys")
+
+DEFAULT_SHELVES_COUNT = 10
+SHELF_ITEMS_COUNT = 10
+
+
+class ShelfTest(tv_testcase.TvTestCase):
+
+  def _wait_for_layout(self):
+    while int(self.get_webdriver().execute_script(
+        "return h5vcc.cVal.getValue('Event.MainWebModule.IsProcessing')")):
+      time.sleep(0.1)
+
+  def test_simple(self):
+    self.load_tv()
+    self.assert_displayed(tv.FOCUSED_SHELF)
+
+    print(str(self.get_webdriver().execute_script(
+        "h5vcc.system.recordStats = true")))
+
+    for _ in xrange(DEFAULT_SHELVES_COUNT):
+      self.send_keys(tv.FOCUSED_SHELF, keys.Keys.ARROW_DOWN)
+      self.poll_until_found(tv.FOCUSED_SHELF)
+      self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
+      self._wait_for_layout()
+
+    for _ in xrange(SHELF_ITEMS_COUNT):
+      self.send_keys(tv.FOCUSED_TILE, keys.Keys.ARROW_RIGHT)
+      self.poll_until_found(tv.FOCUSED_TILE)
+      self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
+      self._wait_for_layout()
+
+    print("ShelfTest event durations"
+          + str(self.get_webdriver().execute_script(
+              "return h5vcc.cVal.getValue("
+              "'Event.Durations.MainWebModule.KeyUp')")))
+
+if __name__ == "__main__":
+  tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/tv.py b/src/cobalt/webdriver_benchmarks/tv.py
new file mode 100644
index 0000000..f9c0411
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tv.py
@@ -0,0 +1,10 @@
+"""CSS Constants."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+FOCUSED_GUIDE = '.focused.guide'
+FOCUSED_SHELF = '.focused.selected.shelf'
+FOCUSED_SHELF_TITLE = '.focused.selected.shelf .shelf-header-title .main'
+FOCUSED_TILE = '.focused.tile'
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py
new file mode 100644
index 0000000..39bfe41
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -0,0 +1,149 @@
+"""Base class for WebDriver tests."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+import time
+import unittest
+import urlparse
+
+# This directory is a package
+sys.path.insert(0, os.path.abspath("."))
+# pylint: disable=C6204,C6203
+import partial_layout_benchmark
+import tv
+
+# selenium imports
+# pylint: disable=C0103
+WebDriverWait = partial_layout_benchmark.ImportSeleniumModule(
+    submodule="webdriver.support.ui").WebDriverWait
+
+ElementNotVisibleException = (
+    partial_layout_benchmark.ImportSeleniumModule(
+        submodule="common.exceptions").ElementNotVisibleException)
+
+BASE_URL = "https://www.youtube.com/tv"
+PAGE_LOAD_WAIT_SECONDS = 30
+
+
+class TvTestCase(unittest.TestCase):
+  """Base class for WebDriver tests.
+
+  Style note: snake_case is used for function names here so as to match
+  with an internal class with the same name.
+  """
+
+  def get_webdriver(self):
+    return partial_layout_benchmark.GetWebDriver()
+
+  def goto(self, path):
+    """Goes to a path off of BASE_URL.
+
+    Args:
+      path: URL path
+    Raises:
+      Underlying WebDriver exceptions
+    """
+    self.get_webdriver().get(urlparse.urljoin(BASE_URL, path))
+
+  def load_tv(self):
+    """Loads the main TV page and waits for it to display.
+
+    Raises:
+      Underlying WebDriver exceptions
+    """
+    self.goto("")
+    # Note that the internal tests use "expect_transition" which is
+    # a mechanism that sets a maximum timeout for a "@with_retries"
+    # decorator-driven success retry loop for subsequent webdriver requests.
+    #
+    # We'll skip that sophistication here.
+    self.poll_until_found(tv.FOCUSED_SHELF)
+
+  def poll_until_found(self, css_selector):
+    """Polls until an element is found.
+
+    Args:
+      css_selector: A CSS selector
+    Raises:
+      Underlying WebDriver exceptions
+    """
+    start_time = time.time()
+    while ((not self.find_elements(css_selector)) and
+           (time.time() - start_time < PAGE_LOAD_WAIT_SECONDS)):
+      time.sleep(1)
+    self.assert_displayed(css_selector)
+
+  def unique_find(self, unique_selector):
+    """Finds and returns a uniquely selected element.
+
+    Args:
+      unique_selector: A CSS selector that will select only one element
+    Raises:
+      Underlying WebDriver exceptions
+      AssertError: the element isn't unique
+    Returns:
+      Element
+    """
+    return self.find_elements(unique_selector, expected_num=1)[0]
+
+  def assert_displayed(self, css_selector):
+    """Asserts that an element is displayed.
+
+    Args:
+      css_selector: A CSS selector
+    Raises:
+      Underlying WebDriver exceptions
+      AssertError: the element isn't found
+    """
+    # TODO does not actually assert that it's visible, like webdriver.py
+    # probably does.
+    self.assertTrue(self.unique_find(css_selector))
+
+  def find_elements(self, css_selector, expected_num=None):
+    """Finds elements based on a selector.
+
+    Args:
+      css_selector: A CSS selector
+      expected_num: Expected number of matching elements
+    Raises:
+      Underlying WebDriver exceptions
+      AssertError: expected_num isn't met
+    Returns:
+      Array of selected elements
+    """
+    elements = self.get_webdriver().find_elements_by_css_selector(css_selector)
+    if expected_num is not None:
+      self.assertEqual(len(elements), expected_num)
+    return elements
+
+  def send_keys(self, css_selector, keys):
+    """Sends keys to an element uniquely identified by a selector.
+
+    This method retries for a timeout period if the selected element
+    could not be immediately found. If the retries do not succeed,
+    the underlying exception is passed through.
+
+    Raises:
+      Underlying WebDriver exceptions
+    """
+    start_time = time.time()
+    while True:
+      try:
+        element = self.unique_find(css_selector)
+        element.send_keys(keys)
+        return
+      except ElementNotVisibleException:
+        # TODO ElementNotVisibleException seems to be considered
+        # a "falsy" exception in the internal tests, which seems to mean
+        # it would not be retried. But here, it seems essential.
+        if time.time() - start_time >= PAGE_LOAD_WAIT_SECONDS:
+          raise
+        time.sleep(1)
+
+
+def main():
+  partial_layout_benchmark.main()
diff --git a/src/cobalt/xhr/xml_http_request_event_target.cc b/src/cobalt/xhr/xml_http_request_event_target.cc
index ef9eedb..20b9eeb 100644
--- a/src/cobalt/xhr/xml_http_request_event_target.cc
+++ b/src/cobalt/xhr/xml_http_request_event_target.cc
@@ -64,37 +64,65 @@
 
 void XMLHttpRequestEventTarget::set_onabort(
     const EventListenerScriptObject& listener) {
-  onabort_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    onabort_listener_ = base::nullopt;
+  } else {
+    onabort_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::abort(), listener);
 }
 void XMLHttpRequestEventTarget::set_onerror(
     const EventListenerScriptObject& listener) {
-  onerror_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    onerror_listener_ = base::nullopt;
+  } else {
+    onerror_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::error(), listener);
 }
 void XMLHttpRequestEventTarget::set_onload(
     const EventListenerScriptObject& listener) {
-  onload_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    onload_listener_ = base::nullopt;
+  } else {
+    onload_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::load(), listener);
 }
 void XMLHttpRequestEventTarget::set_onloadend(
     const EventListenerScriptObject& listener) {
-  onloadend_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    onloadend_listener_ = base::nullopt;
+  } else {
+    onloadend_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::loadend(), listener);
 }
 void XMLHttpRequestEventTarget::set_onloadstart(
     const EventListenerScriptObject& listener) {
-  onloadstart_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    onloadstart_listener_ = base::nullopt;
+  } else {
+    onloadstart_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::loadstart(), listener);
 }
 void XMLHttpRequestEventTarget::set_onprogress(
     const EventListenerScriptObject& listener) {
-  onprogress_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    onprogress_listener_ = base::nullopt;
+  } else {
+    onprogress_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::progress(), listener);
 }
 void XMLHttpRequestEventTarget::set_ontimeout(
     const EventListenerScriptObject& listener) {
-  ontimeout_listener_.emplace(this, listener);
+  if (listener.IsNull()) {
+    ontimeout_listener_ = base::nullopt;
+  } else {
+    ontimeout_listener_.emplace(this, listener);
+  }
   SetAttributeEventListener(base::Tokens::timeout(), listener);
 }
 }  // namespace xhr
diff --git a/src/glimp/gles/program.cc b/src/glimp/gles/program.cc
index ff02fbe..0309920 100644
--- a/src/glimp/gles/program.cc
+++ b/src/glimp/gles/program.cc
@@ -163,7 +163,7 @@
 // Clear all stored uniform information and values.
 void Program::ClearUniforms() {
   for (size_t i = 0; i < uniforms_.size(); ++i) {
-    SbMemoryFree(uniforms_[i].data);
+    SbMemoryDeallocate(uniforms_[i].data);
   }
   uniforms_.clear();
   active_uniform_locations_.clear();
@@ -206,7 +206,7 @@
     // We need to reallocate data if the information has changed.
     uniform->info = new_info;
 
-    SbMemoryFree(uniform->data);
+    SbMemoryDeallocate(uniform->data);
     uniform->data = SbMemoryAllocate(DataSizeForType(count, elem_size, type));
   }
   SbMemoryCopy(uniform->data, v, DataSizeForType(count, elem_size, type));
diff --git a/src/media/base/decoder_buffer.cc b/src/media/base/decoder_buffer.cc
index 31790ff..14c4151 100644
--- a/src/media/base/decoder_buffer.cc
+++ b/src/media/base/decoder_buffer.cc
@@ -23,8 +23,8 @@
 // static
 scoped_refptr<DecoderBuffer> DecoderBuffer::CreateEOSBuffer(
     base::TimeDelta timestamp) {
-  scoped_refptr<DecoderBuffer> eos = scoped_refptr<DecoderBuffer>(
-      new DecoderBuffer(NULL, 0));
+  scoped_refptr<DecoderBuffer> eos =
+      scoped_refptr<DecoderBuffer>(new DecoderBuffer(NULL, 0, true));
   eos->SetTimestamp(timestamp);
   return eos;
 }
@@ -34,12 +34,15 @@
   size_ = size;
 }
 
-DecoderBuffer::DecoderBuffer(uint8* reusable_buffer, size_t size)
-    : Buffer(kNoTimestamp(), kInfiniteDuration())
-    , buffer_(reusable_buffer)
-    , size_(size)
-    , allocated_size_(size)
-    , is_decrypted_(false) {
+DecoderBuffer::DecoderBuffer(uint8* reusable_buffer,
+                             size_t size,
+                             bool is_keyframe)
+    : Buffer(kNoTimestamp(), kInfiniteDuration()),
+      buffer_(reusable_buffer),
+      size_(size),
+      allocated_size_(size),
+      is_decrypted_(false),
+      is_keyframe_(is_keyframe) {
   if (buffer_) {
     // Retain a reference to the buffer factory, to ensure that we do not
     // outlive it.
diff --git a/src/media/base/decoder_buffer.h b/src/media/base/decoder_buffer.h
index 26e524f..16f5cce 100644
--- a/src/media/base/decoder_buffer.h
+++ b/src/media/base/decoder_buffer.h
@@ -33,12 +33,14 @@
       base::TimeDelta timestamp);
 
   // Buffer implementation.
-  virtual const uint8* GetData() const OVERRIDE { return buffer_; }
+  const uint8* GetData() const OVERRIDE { return buffer_; }
   // Data size can be less than allocated size after ShrinkTo is called.
-  virtual int GetDataSize() const OVERRIDE { return static_cast<int>(size_); }
+  int GetDataSize() const OVERRIDE { return static_cast<int>(size_); }
+
   int GetAllocatedSize() const { return static_cast<int>(allocated_size_); }
   // This is used by the data that we don't know the exact size before reading.
   void ShrinkTo(int size);
+  bool IsKeyframe() const { return is_keyframe_; }
 
   // Returns a read-write pointer to the buffer data.
   virtual uint8* GetWritableData() { return buffer_; }
@@ -55,7 +57,7 @@
   friend class ShellBufferFactory;
   // Should only be called by ShellBufferFactory, consumers should use
   // ShellBufferFactory::AllocateBuffer to make a DecoderBuffer.
-  DecoderBuffer(uint8* reusable_buffer, size_t size);
+  DecoderBuffer(uint8* reusable_buffer, size_t size, bool is_keyframe);
   // For deferred allocation create a shell buffer with buffer_ NULL but a
   // non-zero size. Then we use the SetBuffer() method below to actually
   // set the reusable buffer pointer when it becomes available
@@ -68,6 +70,7 @@
   scoped_refptr<ShellBufferFactory> buffer_factory_;
   scoped_ptr<DecryptConfig> decrypt_config_;
   bool is_decrypted_;
+  bool is_keyframe_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(DecoderBuffer);
 };
diff --git a/src/media/base/decoder_buffer_cache.cc b/src/media/base/decoder_buffer_cache.cc
new file mode 100644
index 0000000..92d35ee
--- /dev/null
+++ b/src/media/base/decoder_buffer_cache.cc
@@ -0,0 +1,126 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "media/base/decoder_buffer_cache.h"
+
+namespace media {
+
+DecoderBufferCache::DecoderBufferCache()
+    : audio_buffer_index_(0), video_buffer_index_(0) {}
+
+void DecoderBufferCache::AddBuffer(DemuxerStream::Type type,
+                                   const scoped_refptr<DecoderBuffer>& buffer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (type == DemuxerStream::AUDIO) {
+    audio_buffers_.push_back(buffer);
+    if (buffer->IsKeyframe()) {
+      audio_key_frame_timestamps_.push_back(buffer->GetTimestamp());
+    }
+  } else {
+    DCHECK_EQ(type, DemuxerStream::VIDEO);
+    video_buffers_.push_back(buffer);
+    if (buffer->IsKeyframe()) {
+      video_key_frame_timestamps_.push_back(buffer->GetTimestamp());
+    }
+  }
+}
+
+void DecoderBufferCache::ClearSegmentsBeforeMediaTime(
+    base::TimeDelta media_time) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  ClearSegmentsBeforeMediaTime(media_time, &audio_buffers_,
+                               &audio_key_frame_timestamps_);
+  ClearSegmentsBeforeMediaTime(media_time, &video_buffers_,
+                               &video_key_frame_timestamps_);
+}
+
+void DecoderBufferCache::ClearAll() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  audio_buffers_.clear();
+  audio_key_frame_timestamps_.clear();
+  video_buffers_.clear();
+  video_key_frame_timestamps_.clear();
+}
+
+void DecoderBufferCache::StartResuming() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  audio_buffer_index_ = 0;
+  video_buffer_index_ = 0;
+}
+
+scoped_refptr<DecoderBuffer> DecoderBufferCache::GetBuffer(
+    DemuxerStream::Type type) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (type == DemuxerStream::AUDIO) {
+    if (audio_buffer_index_ < audio_buffers_.size()) {
+      return audio_buffers_[audio_buffer_index_];
+    }
+    return NULL;
+  }
+
+  DCHECK_EQ(type, DemuxerStream::VIDEO);
+  if (video_buffer_index_ < video_buffers_.size()) {
+    return video_buffers_[video_buffer_index_];
+  }
+  return NULL;
+}
+
+void DecoderBufferCache::AdvanceToNextBuffer(DemuxerStream::Type type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (type == DemuxerStream::AUDIO) {
+    ++audio_buffer_index_;
+  } else {
+    DCHECK_EQ(type, DemuxerStream::VIDEO);
+    ++video_buffer_index_;
+  }
+}
+
+// static
+void DecoderBufferCache::ClearSegmentsBeforeMediaTime(
+    base::TimeDelta media_time,
+    Buffers* buffers,
+    KeyFrameTimestamps* key_frame_timestamps) {
+  // Use K to denote a key frame and N for non-key frame.  If the cache contains
+  // K N N N N N N N N K N N N N N N N N K N N N N N N N N
+  //                     |
+  //                 media_time
+  // Then we should remove everything before the key frame before the
+  // |media_time| and turn the cache into:
+  //                   K N N N N N N N N K N N N N N N N N
+  //                     |
+  //                 media_time
+  // So we need at least two keyframes before we can remove any frames.
+  while (key_frame_timestamps->size() > 1 &&
+         key_frame_timestamps->at(1) <= media_time) {
+    key_frame_timestamps->erase(key_frame_timestamps->begin());
+  }
+  if (key_frame_timestamps->empty()) {
+    return;
+  }
+  while (scoped_refptr<DecoderBuffer> buffer = buffers->front()) {
+    if (buffer->IsKeyframe() &&
+        buffer->GetTimestamp() == key_frame_timestamps->front()) {
+      break;
+    }
+    buffers->pop_front();
+  }
+}
+
+}  // namespace media
diff --git a/src/media/base/decoder_buffer_cache.h b/src/media/base/decoder_buffer_cache.h
new file mode 100644
index 0000000..49ba5ec
--- /dev/null
+++ b/src/media/base/decoder_buffer_cache.h
@@ -0,0 +1,70 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MEDIA_BASE_DECODER_BUFFER_CACHE_H_
+#define MEDIA_BASE_DECODER_BUFFER_CACHE_H_
+
+#include <deque>
+
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+
+namespace media {
+
+// This class can be used to hold media buffers in decoding order.  It also
+// provides function that given a media time, the function will find a key frame
+// before the media time and clear all buffers before the key frame.  This class
+// can be used to "replay" the video from the current playback position and can
+// be useful to implement suspend/resume.
+class DecoderBufferCache {
+ public:
+  DecoderBufferCache();
+
+  void AddBuffer(DemuxerStream::Type type,
+                 const scoped_refptr<DecoderBuffer>& buffer);
+  void ClearSegmentsBeforeMediaTime(base::TimeDelta media_time);
+  void ClearAll();
+
+  // Start resuming, reset indices to audio/video buffers to the very beginning.
+  void StartResuming();
+  scoped_refptr<DecoderBuffer> GetBuffer(DemuxerStream::Type type) const;
+  void AdvanceToNextBuffer(DemuxerStream::Type type);
+
+ private:
+  typedef std::deque<scoped_refptr<DecoderBuffer> > Buffers;
+  typedef std::deque<base::TimeDelta> KeyFrameTimestamps;
+
+  static void ClearSegmentsBeforeMediaTime(
+      base::TimeDelta media_time,
+      Buffers* buffers,
+      KeyFrameTimestamps* key_frame_timestamps);
+
+  base::ThreadChecker thread_checker_;
+
+  Buffers audio_buffers_;
+  KeyFrameTimestamps audio_key_frame_timestamps_;
+
+  Buffers video_buffers_;
+  KeyFrameTimestamps video_key_frame_timestamps_;
+
+  size_t audio_buffer_index_;
+  size_t video_buffer_index_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_DECODER_BUFFER_CACHE_H_
diff --git a/src/media/base/decoder_buffer_pool.cc b/src/media/base/decoder_buffer_pool.cc
index 3982108..0d52b2a 100644
--- a/src/media/base/decoder_buffer_pool.cc
+++ b/src/media/base/decoder_buffer_pool.cc
@@ -47,7 +47,8 @@
 
 scoped_refptr<DecoderBuffer> DecoderBufferPool::AllocateFromShellBufferFactory(
     size_t size_in_bytes) {
-  return ShellBufferFactory::Instance()->AllocateBufferNow(size_in_bytes);
+  return ShellBufferFactory::Instance()->AllocateBufferNow(size_in_bytes,
+                                                           false);
 }
 
 }  // namespace media
diff --git a/src/media/base/pipeline.h b/src/media/base/pipeline.h
index 0912c6b..ec4bb7c 100644
--- a/src/media/base/pipeline.h
+++ b/src/media/base/pipeline.h
@@ -77,6 +77,9 @@
 
   virtual ~Pipeline() {}
 
+  virtual void Suspend() {}
+  virtual void Resume() {}
+
   // Build a pipeline to using the given filter collection to construct a filter
   // chain, executing |seek_cb| when the initial seek/preroll has completed.
   //
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc
index 7c0f067..b9d2262 100644
--- a/src/media/base/sbplayer_pipeline.cc
+++ b/src/media/base/sbplayer_pipeline.cc
@@ -12,15 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <map>
 #include <vector>
 
 #include "base/basictypes.h"  // For COMPILE_ASSERT
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
 #include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/time.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/bind_to_loop.h"
@@ -34,9 +35,10 @@
 #include "media/base/pipeline.h"
 #include "media/base/pipeline_status.h"
 #include "media/base/ranges.h"
+#include "media/base/sbplayer_set_bounds_helper.h"
+#include "media/base/starboard_player.h"
 #include "media/base/video_decoder_config.h"
 #include "media/crypto/starboard_decryptor.h"
-#include "starboard/player.h"
 #include "ui/gfx/size.h"
 
 namespace media {
@@ -48,102 +50,6 @@
 
 namespace {
 
-SbMediaAudioCodec SbMediaAudioCodecFromMediaAudioCodec(AudioCodec codec) {
-  if (codec == kCodecAAC) {
-    return kSbMediaAudioCodecAac;
-  } else if (codec == kCodecVorbis) {
-    return kSbMediaAudioCodecVorbis;
-  } else if (codec == kCodecOpus) {
-    return kSbMediaAudioCodecOpus;
-  }
-  DLOG(ERROR) << "Unsupported audio codec " << codec;
-  return kSbMediaAudioCodecNone;
-}
-
-SbMediaVideoCodec SbMediaVideoCodecFromMediaVideoCodec(VideoCodec codec) {
-  if (codec == kCodecH264) {
-    return kSbMediaVideoCodecH264;
-  } else if (codec == kCodecVC1) {
-    return kSbMediaVideoCodecVc1;
-  } else if (codec == kCodecMPEG2) {
-    return kSbMediaVideoCodecMpeg2;
-  } else if (codec == kCodecTheora) {
-    return kSbMediaVideoCodecTheora;
-  } else if (codec == kCodecVP8) {
-    return kSbMediaVideoCodecVp8;
-  } else if (codec == kCodecVP9) {
-    return kSbMediaVideoCodecVp9;
-  }
-  DLOG(ERROR) << "Unsupported video codec " << codec;
-  return kSbMediaVideoCodecNone;
-}
-
-TimeDelta SbMediaTimeToTimeDelta(SbMediaTime timestamp) {
-  return TimeDelta::FromMicroseconds(timestamp * Time::kMicrosecondsPerSecond /
-                                     kSbMediaTimeSecond);
-}
-
-SbMediaTime TimeDeltaToSbMediaTime(TimeDelta timedelta) {
-  return timedelta.InMicroseconds() * kSbMediaTimeSecond /
-         Time::kMicrosecondsPerSecond;
-}
-
-bool IsEncrypted(const scoped_refptr<DemuxerStream>& stream) {
-  if (stream->type() == DemuxerStream::AUDIO) {
-    return stream->audio_decoder_config().is_encrypted();
-  } else {
-    DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
-    return stream->video_decoder_config().is_encrypted();
-  }
-}
-
-void FillDrmSampleInfo(const scoped_refptr<DecoderBuffer>& buffer,
-                       SbDrmSampleInfo* drm_info,
-                       SbDrmSubSampleMapping* subsample_mapping) {
-  DCHECK(drm_info);
-  DCHECK(subsample_mapping);
-
-  const DecryptConfig* config = buffer->GetDecryptConfig();
-  if (!config || config->iv().empty() || config->key_id().empty()) {
-    drm_info->initialization_vector_size = 0;
-    drm_info->identifier_size = 0;
-    drm_info->subsample_count = 0;
-    drm_info->subsample_mapping = NULL;
-    return;
-  }
-
-  DCHECK_LE(config->iv().size(), sizeof(drm_info->initialization_vector));
-  DCHECK_LE(config->key_id().size(), sizeof(drm_info->identifier));
-
-  if (config->iv().size() > sizeof(drm_info->initialization_vector) ||
-      config->key_id().size() > sizeof(drm_info->identifier)) {
-    drm_info->initialization_vector_size = 0;
-    drm_info->identifier_size = 0;
-    drm_info->subsample_count = 0;
-    drm_info->subsample_mapping = NULL;
-    return;
-  }
-
-  memcpy(drm_info->initialization_vector, &config->iv()[0],
-         config->iv().size());
-  drm_info->initialization_vector_size = config->iv().size();
-  memcpy(drm_info->identifier, &config->key_id()[0], config->key_id().size());
-  drm_info->identifier_size = config->key_id().size();
-  drm_info->subsample_count = config->subsamples().size();
-
-  if (drm_info->subsample_count > 0) {
-    COMPILE_ASSERT(sizeof(SbDrmSubSampleMapping) == sizeof(SubsampleEntry),
-                   SubSampleEntrySizesMatch);
-    drm_info->subsample_mapping =
-        reinterpret_cast<const SbDrmSubSampleMapping*>(
-            &config->subsamples()[0]);
-  } else {
-    drm_info->subsample_mapping = subsample_mapping;
-    subsample_mapping->clear_byte_count = 0;
-    subsample_mapping->encrypted_byte_count = buffer->GetDataSize();
-  }
-}
-
 // Used to post parameters to SbPlayerPipeline::StartTask() as the number of
 // parameters exceed what base::Bind() can support.
 struct StartTaskParameters {
@@ -156,32 +62,11 @@
   base::Closure duration_change_cb;
 };
 
-class SetBoundsCaller : public base::RefCountedThreadSafe<SetBoundsCaller> {
- public:
-  SetBoundsCaller() : player_(kSbPlayerInvalid) {}
-  void SetPlayer(SbPlayer player) {
-    base::Lock lock_;
-    player_ = player;
-  }
-  bool SetBounds(const gfx::Rect& rect) {
-    base::AutoLock auto_lock(lock_);
-    if (!SbPlayerIsValid(player_)) {
-      return false;
-    }
-    SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
-    return true;
-  }
-
- private:
-  base::Lock lock_;
-  SbPlayer player_;
-
-  DISALLOW_COPY_AND_ASSIGN(SetBoundsCaller);
-};
-
 // SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer
 // interface internally.
-class MEDIA_EXPORT SbPlayerPipeline : public Pipeline, public DemuxerHost {
+class MEDIA_EXPORT SbPlayerPipeline : public Pipeline,
+                                      public DemuxerHost,
+                                      public StarboardPlayer::Host {
  public:
   // Constructs a media pipeline that will execute on |message_loop|.
   SbPlayerPipeline(PipelineWindow window,
@@ -189,6 +74,8 @@
                    MediaLog* media_log);
   ~SbPlayerPipeline() OVERRIDE;
 
+  void Suspend() OVERRIDE;
+  void Resume() OVERRIDE;
   void Start(scoped_ptr<FilterCollection> filter_collection,
              const SetDecryptorReadyCB& decryptor_ready_cb,
              const PipelineStatusCB& ended_cb,
@@ -218,13 +105,6 @@
   SetBoundsCB GetSetBoundsCB() OVERRIDE;
 
  private:
-  // A map from raw data pointer returned by DecoderBuffer::GetData() to the
-  // DecoderBuffer and a reference count.  The reference count indicates how
-  // many instances of the DecoderBuffer is currently being decoded in the
-  // pipeline.
-  typedef std::map<const void*, std::pair<scoped_refptr<DecoderBuffer>, int> >
-      DecodingBuffers;
-
   void StartTask(const StartTaskParameters& parameters);
   void SetVolumeTask(float volume);
   void SetPlaybackRateTask(float volume);
@@ -245,31 +125,18 @@
   void OnDemuxerSeeked(PipelineStatus status);
   void OnDemuxerStopped();
   void OnDemuxerStreamRead(DemuxerStream::Type type,
-                           int ticket,
                            DemuxerStream::Status status,
                            const scoped_refptr<DecoderBuffer>& buffer);
 
-  void OnDecoderStatus(SbMediaType type,
-                       SbPlayerDecoderState state,
-                       int ticket);
-  void OnPlayerStatus(SbPlayerState state, int ticket);
-  void OnDeallocateSample(const void* sample_buffer);
-
-  static void DecoderStatusCB(SbPlayer player,
-                              void* context,
-                              SbMediaType type,
-                              SbPlayerDecoderState state,
-                              int ticket);
-  static void PlayerStatusCB(SbPlayer player,
-                             void* context,
-                             SbPlayerState state,
-                             int ticket);
-  static void DeallocateSampleCB(SbPlayer player,
-                                 void* context,
-                                 const void* sample_buffer);
+  // StarboardPlayer::Host implementation.
+  void OnNeedData(DemuxerStream::Type type) OVERRIDE;
+  void OnPlayerStatus(SbPlayerState state) OVERRIDE;
 
   void UpdateDecoderConfig(const scoped_refptr<DemuxerStream>& stream);
 
+  void SuspendTask(base::WaitableEvent* done_event);
+  void ResumeTask(base::WaitableEvent* done_event);
+
   // Message loop used to execute pipeline tasks.  It is thread-safe.
   scoped_refptr<base::MessageLoopProxy> message_loop_;
 
@@ -277,9 +144,6 @@
   // dtor and accesed once by SbPlayerCreate().
   PipelineWindow window_;
 
-  // The current ticket associated with the |player_|.
-  int ticket_;
-
   // Lock used to serialize access for the following member variables.
   mutable base::Lock lock_;
 
@@ -335,8 +199,9 @@
   bool video_read_in_progress_;
   TimeDelta duration_;
 
-  DecodingBuffers decoding_buffers_;
-  scoped_refptr<SetBoundsCaller> set_bounds_caller_;
+  scoped_refptr<SbPlayerSetBoundsHelper> set_bounds_helper_;
+
+  bool flushing_;
 
   // The following member variables can be accessed from WMPI thread but all
   // modifications to them happens on the pipeline thread.  So any access of
@@ -346,8 +211,9 @@
 
   // Temporary callback used for Start() and Seek().
   PipelineStatusCB seek_cb_;
-  SbMediaTime seek_time_;
-  SbPlayer player_;
+  base::TimeDelta seek_time_;
+  scoped_ptr<StarboardPlayer> player_;
+  bool suspended_;
 
   DISALLOW_COPY_AND_ASSIGN(SbPlayerPipeline);
 };
@@ -357,7 +223,6 @@
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
     MediaLog* media_log)
     : window_(window),
-      ticket_(SB_PLAYER_INITIAL_TICKET),
       message_loop_(message_loop),
       total_bytes_(0),
       natural_size_(0, 0),
@@ -367,12 +232,34 @@
       has_video_(false),
       audio_read_in_progress_(false),
       video_read_in_progress_(false),
-      set_bounds_caller_(new SetBoundsCaller),
-      seek_time_(0),
-      player_(kSbPlayerInvalid) {}
+      flushing_(false),
+      set_bounds_helper_(new SbPlayerSetBoundsHelper),
+      suspended_(false) {}
 
 SbPlayerPipeline::~SbPlayerPipeline() {
-  DCHECK(player_ == kSbPlayerInvalid);
+  DCHECK(!player_);
+}
+
+void SbPlayerPipeline::Suspend() {
+  DCHECK(!message_loop_->BelongsToCurrentThread());
+
+  base::WaitableEvent waitable_event(true, /* manual_reset */
+                                     false /* initially_signaled */);
+  message_loop_->PostTask(FROM_HERE,
+                          base::Bind(&SbPlayerPipeline::SuspendTask,
+                                     base::Unretained(this), &waitable_event));
+  waitable_event.Wait();
+}
+
+void SbPlayerPipeline::Resume() {
+  DCHECK(!message_loop_->BelongsToCurrentThread());
+
+  base::WaitableEvent waitable_event(true, /* manual_reset */
+                                     false /* initially_signaled */);
+  message_loop_->PostTask(FROM_HERE,
+                          base::Bind(&SbPlayerPipeline::ResumeTask,
+                                     base::Unretained(this), &waitable_event));
+  waitable_event.Wait();
 }
 
 void SbPlayerPipeline::Start(scoped_ptr<FilterCollection> filter_collection,
@@ -407,16 +294,15 @@
   DCHECK(stop_cb_.is_null());
   DCHECK(!stop_cb.is_null());
 
-  if (SbPlayerIsValid(player_)) {
-    set_bounds_caller_->SetPlayer(kSbPlayerInvalid);
-    SbPlayer player = player_;
+  if (player_) {
+    scoped_ptr<StarboardPlayer> player;
     {
       base::AutoLock auto_lock(lock_);
-      player_ = kSbPlayerInvalid;
+      player = player_.Pass();
     }
 
     DLOG(INFO) << "Destroying SbPlayer.";
-    SbPlayerDestroy(player);
+    player.reset();
     DLOG(INFO) << "SbPlayer destroyed.";
   }
 
@@ -437,20 +323,27 @@
     return;
   }
 
-  if (!SbPlayerIsValid(player_)) {
+  if (!player_) {
     seek_cb.Run(PIPELINE_ERROR_INVALID_STATE);
   }
 
   DCHECK(seek_cb_.is_null());
   DCHECK(!seek_cb.is_null());
 
-  // Increase |ticket_| so all upcoming need data requests from the SbPlayer
-  // are ignored.
-  ++ticket_;
+  flushing_ = true;
+
+  if (audio_read_in_progress_ || video_read_in_progress_) {
+    message_loop_->PostTask(
+        FROM_HERE, base::Bind(&SbPlayerPipeline::Seek, this, time, seek_cb));
+    return;
+  }
+
+  player_->PrepareForSeek();
+
   {
     base::AutoLock auto_lock(lock_);
     seek_cb_ = seek_cb;
-    seek_time_ = TimeDeltaToSbMediaTime(time);
+    seek_time_ = time;
   }
   demuxer_->Seek(time, BindToCurrentLoop(base::Bind(
                            &SbPlayerPipeline::OnDemuxerSeeked, this)));
@@ -500,17 +393,16 @@
 TimeDelta SbPlayerPipeline::GetMediaTime() const {
   base::AutoLock auto_lock(lock_);
 
-  if (!SbPlayerIsValid(player_)) {
+  if (!seek_cb_.is_null()) {
+    return seek_time_;
+  }
+  if (!player_) {
     return TimeDelta();
   }
-  if (!seek_cb_.is_null()) {
-    return SbMediaTimeToTimeDelta(seek_time_);
-  }
-  SbPlayerInfo info;
-  SbPlayerGetInfo(player_, &info);
-  statistics_.video_frames_decoded = info.total_video_frames;
-  statistics_.video_frames_dropped = info.dropped_video_frames;
-  return SbMediaTimeToTimeDelta(info.current_media_pts);
+  base::TimeDelta media_time;
+  player_->GetInfo(&statistics_.video_frames_decoded,
+                   &statistics_.video_frames_dropped, &media_time);
+  return media_time;
 }
 
 Ranges<TimeDelta> SbPlayerPipeline::GetBufferedTimeRanges() {
@@ -564,7 +456,7 @@
 
 Pipeline::SetBoundsCB SbPlayerPipeline::GetSetBoundsCB() {
 #if SB_IS(PLAYER_PUNCHED_OUT)
-  return base::Bind(&SetBoundsCaller::SetBounds, set_bounds_caller_);
+  return base::Bind(&SbPlayerSetBoundsHelper::SetBounds, set_bounds_helper_);
 #else   // SB_IS(PLAYER_PUNCHED_OUT)
   return Pipeline::SetBoundsCB();
 #endif  // SB_IS(PLAYER_PUNCHED_OUT)
@@ -594,16 +486,16 @@
 void SbPlayerPipeline::SetVolumeTask(float volume) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
-  if (SbPlayerIsValid(player_)) {
-    SbPlayerSetVolume(player_, volume_);
+  if (player_) {
+    player_->SetVolume(volume_);
   }
 }
 
 void SbPlayerPipeline::SetPlaybackRateTask(float volume) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
-  if (SbPlayerIsValid(player_)) {
-    SbPlayerSetPause(player_, playback_rate_ == 0.0);
+  if (player_) {
+    player_->SetPause(playback_rate_ == 0.0);
   }
 }
 
@@ -654,41 +546,36 @@
 void SbPlayerPipeline::CreatePlayer(SbDrmSystem drm_system) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
+  if (suspended_) {
+    message_loop_->PostTask(
+        FROM_HERE,
+        base::Bind(&SbPlayerPipeline::CreatePlayer, this, drm_system));
+    return;
+  }
+
+  // TODO:  Check |suspended_| here as the pipeline can be suspended before the
+  // player is created.  In this case we should delay creating the player as the
+  // creation of player may fail.
   const AudioDecoderConfig& audio_config =
       demuxer_->GetStream(DemuxerStream::AUDIO)->audio_decoder_config();
-  SbMediaAudioHeader audio_header;
-  audio_header.format_tag = 0x00ff;
-  audio_header.number_of_channels =
-      ChannelLayoutToChannelCount(audio_config.channel_layout());
-  audio_header.samples_per_second = audio_config.samples_per_second();
-  audio_header.average_bytes_per_second = 1;
-  audio_header.block_alignment = 4;
-  audio_header.bits_per_sample = audio_config.bits_per_channel();
-  audio_header.audio_specific_config_size = 0;
-
   const VideoDecoderConfig& video_config =
       demuxer_->GetStream(DemuxerStream::VIDEO)->video_decoder_config();
 
-  SbMediaAudioCodec audio_codec =
-      SbMediaAudioCodecFromMediaAudioCodec(audio_config.codec());
-  SbMediaVideoCodec video_codec =
-      SbMediaVideoCodecFromMediaVideoCodec(video_config.codec());
-
   {
     base::AutoLock auto_lock(lock_);
-    player_ =
-        SbPlayerCreate(window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION,
-                       drm_system, &audio_header, DeallocateSampleCB,
-                       DecoderStatusCB, PlayerStatusCB, this);
+    player_.reset(new StarboardPlayer(message_loop_, audio_config, video_config,
+                                      window_, drm_system, this,
+                                      set_bounds_helper_.get()));
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
 
-  if (SbPlayerIsValid(player_)) {
-    set_bounds_caller_->SetPlayer(player_);
+  if (player_->IsValid()) {
     return;
   }
 
+  player_.reset();
+
   PipelineStatusCB seek_cb;
   {
     base::AutoLock auto_lock(lock_);
@@ -750,14 +637,8 @@
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   if (status == PIPELINE_OK) {
-    if (audio_read_in_progress_ || video_read_in_progress_) {
-      message_loop_->PostTask(
-          FROM_HERE,
-          base::Bind(&SbPlayerPipeline::OnDemuxerSeeked, this, status));
-      return;
-    }
-    ++ticket_;
-    SbPlayerSeek(player_, seek_time_, ticket_);
+    flushing_ = false;
+    player_->Seek(seek_time_);
   }
 }
 
@@ -773,7 +654,6 @@
 
 void SbPlayerPipeline::OnDemuxerStreamRead(
     DemuxerStream::Type type,
-    int ticket,
     DemuxerStream::Status status,
     const scoped_refptr<DecoderBuffer>& buffer) {
   DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO)
@@ -782,21 +662,14 @@
   if (!message_loop_->BelongsToCurrentThread()) {
     message_loop_->PostTask(
         FROM_HERE, base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this,
-                              type, ticket, status, buffer));
+                              type, status, buffer));
     return;
   }
 
   scoped_refptr<DemuxerStream> stream = demuxer_->GetStream(type);
 
-  if (ticket != ticket_) {
-    if (status == DemuxerStream::kConfigChanged) {
-      UpdateDecoderConfig(stream);
-    }
-    return;
-  }
-
   // In case if Stop() has been called.
-  if (!SbPlayerIsValid(player_)) {
+  if (!player_) {
     return;
   }
 
@@ -824,115 +697,57 @@
     NOTIMPLEMENTED() << "Update decoder config";
     UpdateDecoderConfig(stream);
     stream->Read(
-        base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this, type, ticket));
+        base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this, type));
     return;
   }
 
-  bool is_encrypted = IsEncrypted(stream);
-  SbDrmSampleInfo drm_info;
-  SbDrmSubSampleMapping subsample_mapping;
-
-  if (is_encrypted && !buffer->IsEndOfStream()) {
-    FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
-  }
-
   if (type == DemuxerStream::AUDIO) {
     audio_read_in_progress_ = false;
-    if (buffer->IsEndOfStream()) {
-      SbPlayerWriteEndOfStream(player_, kSbMediaTypeAudio);
-      return;
-    }
-    DecodingBuffers::iterator iter = decoding_buffers_.find(buffer->GetData());
-    if (iter == decoding_buffers_.end()) {
-      decoding_buffers_[buffer->GetData()] = std::make_pair(buffer, 1);
-    } else {
-      ++iter->second.second;
-    }
-    SbPlayerWriteSample(player_, kSbMediaTypeAudio, buffer->GetData(),
-                        buffer->GetDataSize(),
-                        TimeDeltaToSbMediaTime(buffer->GetTimestamp()), NULL,
-                        is_encrypted ? &drm_info : NULL);
-    return;
+  } else {
+    video_read_in_progress_ = false;
   }
 
-  video_read_in_progress_ = false;
-  if (buffer->IsEndOfStream()) {
-    SbPlayerWriteEndOfStream(player_, kSbMediaTypeVideo);
-    return;
-  }
-  SbMediaVideoSampleInfo video_info;
-  NOTIMPLEMENTED() << "Fill video_info";
-  video_info.is_key_frame = false;
-  video_info.frame_width = 1;
-  video_info.frame_height = 1;
-  DecodingBuffers::iterator iter = decoding_buffers_.find(buffer->GetData());
-  if (iter == decoding_buffers_.end()) {
-    decoding_buffers_[buffer->GetData()] = std::make_pair(buffer, 1);
-  } else {
-    ++iter->second.second;
-  }
-  SbPlayerWriteSample(player_, kSbMediaTypeVideo, buffer->GetData(),
-                      buffer->GetDataSize(),
-                      TimeDeltaToSbMediaTime(buffer->GetTimestamp()),
-                      &video_info, is_encrypted ? &drm_info : NULL);
+  player_->WriteBuffer(type, buffer);
 }
 
-void SbPlayerPipeline::OnDecoderStatus(SbMediaType type,
-                                       SbPlayerDecoderState state,
-                                       int ticket) {
+void SbPlayerPipeline::OnNeedData(DemuxerStream::Type type) {
   DCHECK(message_loop_->BelongsToCurrentThread());
+
   // In case if Stop() has been called.
-  if (!SbPlayerIsValid(player_)) {
-    return;
-  }
-  if (ticket != ticket_) {
+  if (!player_) {
     return;
   }
 
-  switch (state) {
-    case kSbPlayerDecoderStateNeedsData:
-      break;
-    case kSbPlayerDecoderStateBufferFull:
-      DLOG(WARNING) << "kSbPlayerDecoderStateBufferFull has been deprecated.";
-      return;
-    case kSbPlayerDecoderStateDestroyed:
-      return;
+  if (flushing_) {
+    return;
   }
 
-  DCHECK_EQ(state, kSbPlayerDecoderStateNeedsData);
-  if (type == kSbMediaTypeAudio) {
+  if (type == DemuxerStream::AUDIO) {
     if (audio_read_in_progress_) {
       return;
     }
     audio_read_in_progress_ = true;
   } else {
-    DCHECK_EQ(type, kSbMediaTypeVideo);
+    DCHECK_EQ(type, DemuxerStream::VIDEO);
     if (video_read_in_progress_) {
       return;
     }
     video_read_in_progress_ = true;
   }
-  DemuxerStream::Type stream_type =
-      (type == kSbMediaTypeAudio) ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
-  scoped_refptr<DemuxerStream> stream = demuxer_->GetStream(stream_type);
-  stream->Read(base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this,
-                          stream_type, ticket));
+  scoped_refptr<DemuxerStream> stream = demuxer_->GetStream(type);
+  stream->Read(base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this, type));
 }
 
-void SbPlayerPipeline::OnPlayerStatus(SbPlayerState state, int ticket) {
+void SbPlayerPipeline::OnPlayerStatus(SbPlayerState state) {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   // In case if Stop() has been called.
-  if (!SbPlayerIsValid(player_)) {
-    return;
-  }
-  if (ticket != ticket_) {
+  if (!player_) {
     return;
   }
   switch (state) {
     case kSbPlayerStateInitialized:
-      ++ticket_;
-      SbPlayerSeek(player_, 0, ticket_);
+      NOTREACHED();
       break;
     case kSbPlayerStatePrerolling:
       break;
@@ -960,57 +775,10 @@
   }
 }
 
-void SbPlayerPipeline::OnDeallocateSample(const void* sample_buffer) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  DecodingBuffers::iterator iter = decoding_buffers_.find(sample_buffer);
-  DCHECK(iter != decoding_buffers_.end());
-  if (iter == decoding_buffers_.end()) {
-    LOG(ERROR) << "SbPlayerPipeline::OnDeallocateSample encounters unknown "
-               << "sample_buffer " << sample_buffer;
-    return;
-  }
-  --iter->second.second;
-  if (iter->second.second == 0) {
-    decoding_buffers_.erase(iter);
-  }
-}
-
-// static
-void SbPlayerPipeline::DecoderStatusCB(SbPlayer player,
-                                       void* context,
-                                       SbMediaType type,
-                                       SbPlayerDecoderState state,
-                                       int ticket) {
-  SbPlayerPipeline* pipeline = reinterpret_cast<SbPlayerPipeline*>(context);
-  pipeline->message_loop_->PostTask(
-      FROM_HERE, base::Bind(&SbPlayerPipeline::OnDecoderStatus, pipeline, type,
-                            state, ticket));
-}
-
-// static
-void SbPlayerPipeline::PlayerStatusCB(SbPlayer player,
-                                      void* context,
-                                      SbPlayerState state,
-                                      int ticket) {
-  SbPlayerPipeline* pipeline = reinterpret_cast<SbPlayerPipeline*>(context);
-  pipeline->message_loop_->PostTask(
-      FROM_HERE,
-      base::Bind(&SbPlayerPipeline::OnPlayerStatus, pipeline, state, ticket));
-}
-
-// static
-void SbPlayerPipeline::DeallocateSampleCB(SbPlayer player,
-                                          void* context,
-                                          const void* sample_buffer) {
-  SbPlayerPipeline* pipeline = reinterpret_cast<SbPlayerPipeline*>(context);
-  pipeline->message_loop_->PostTask(
-      FROM_HERE, base::Bind(&SbPlayerPipeline::OnDeallocateSample, pipeline,
-                            sample_buffer));
-}
-
 void SbPlayerPipeline::UpdateDecoderConfig(
     const scoped_refptr<DemuxerStream>& stream) {
   DCHECK(message_loop_->BelongsToCurrentThread());
+
   if (stream->type() == DemuxerStream::AUDIO) {
     stream->audio_decoder_config();
   } else {
@@ -1018,9 +786,49 @@
     const VideoDecoderConfig& decoder_config = stream->video_decoder_config();
     base::AutoLock auto_lock(lock_);
     natural_size_ = decoder_config.natural_size();
+    player_->UpdateVideoResolution(static_cast<int>(natural_size_.width()),
+                                   static_cast<int>(natural_size_.height()));
   }
 }
 
+void SbPlayerPipeline::SuspendTask(base::WaitableEvent* done_event) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(done_event);
+  DCHECK(!suspended_);
+
+  if (suspended_) {
+    done_event->Signal();
+    return;
+  }
+
+  if (player_) {
+    player_->Suspend();
+  }
+
+  suspended_ = true;
+
+  done_event->Signal();
+}
+
+void SbPlayerPipeline::ResumeTask(base::WaitableEvent* done_event) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(done_event);
+  DCHECK(suspended_);
+
+  if (!suspended_) {
+    done_event->Signal();
+    return;
+  }
+
+  if (player_) {
+    player_->Resume();
+  }
+
+  suspended_ = false;
+
+  done_event->Signal();
+}
+
 }  // namespace
 
 #endif  // SB_HAS(PLAYER)
diff --git a/src/media/base/sbplayer_set_bounds_helper.cc b/src/media/base/sbplayer_set_bounds_helper.cc
new file mode 100644
index 0000000..5e96a94
--- /dev/null
+++ b/src/media/base/sbplayer_set_bounds_helper.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "media/base/sbplayer_set_bounds_helper.h"
+
+#include "media/base/starboard_player.h"
+
+namespace media {
+
+void SbPlayerSetBoundsHelper::SetPlayer(StarboardPlayer* player) {
+  base::Lock lock_;
+  player_ = player;
+}
+
+bool SbPlayerSetBoundsHelper::SetBounds(const gfx::Rect& rect) {
+  base::AutoLock auto_lock(lock_);
+  if (!player_) {
+    return false;
+  }
+  player_->SetBounds(rect);
+  return true;
+}
+
+}  // namespace media
diff --git a/src/media/base/sbplayer_set_bounds_helper.h b/src/media/base/sbplayer_set_bounds_helper.h
new file mode 100644
index 0000000..06affcf
--- /dev/null
+++ b/src/media/base/sbplayer_set_bounds_helper.h
@@ -0,0 +1,43 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MEDIA_BASE_SBPLAYER_SET_BOUNDS_HELPER_H_
+#define MEDIA_BASE_SBPLAYER_SET_BOUNDS_HELPER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "ui/gfx/rect.h"
+
+namespace media {
+
+class StarboardPlayer;
+
+class SbPlayerSetBoundsHelper
+    : public base::RefCountedThreadSafe<SbPlayerSetBoundsHelper> {
+ public:
+  SbPlayerSetBoundsHelper() : player_(NULL) {}
+
+  void SetPlayer(StarboardPlayer* player);
+  bool SetBounds(const gfx::Rect& rect);
+
+ private:
+  base::Lock lock_;
+  StarboardPlayer* player_;
+
+  DISALLOW_COPY_AND_ASSIGN(SbPlayerSetBoundsHelper);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_SBPLAYER_SET_BOUNDS_HELPER_H_
diff --git a/src/media/base/shell_buffer_factory.cc b/src/media/base/shell_buffer_factory.cc
index 6ff2f08..dc78fe9 100644
--- a/src/media/base/shell_buffer_factory.cc
+++ b/src/media/base/shell_buffer_factory.cc
@@ -55,7 +55,9 @@
   }
 }
 
-bool ShellBufferFactory::AllocateBuffer(size_t size, AllocCB cb) {
+bool ShellBufferFactory::AllocateBuffer(size_t size,
+                                        bool is_keyframe,
+                                        AllocCB cb) {
   TRACE_EVENT1("media_stack", "ShellBufferFactory::AllocateBuffer()", "size",
                size);
   // Zero-size buffers are allocation error, allocate an EOS buffer explicity
@@ -77,7 +79,7 @@
     if (pending_allocs_.size() == 0) {
       uint8* bytes = Allocate_Locked(size);
       if (bytes) {
-        instant_buffer = new DecoderBuffer(bytes, size);
+        instant_buffer = new DecoderBuffer(bytes, size, is_keyframe);
         TRACE_EVENT0(
             "media_stack",
             "ShellBufferFactory::AllocateBuffer() finished allocation.");
@@ -89,7 +91,7 @@
       TRACE_EVENT0("media_stack",
                    "ShellBufferFactory::AllocateBuffer() deferred.");
       pending_allocs_.push_back(
-          std::make_pair(cb, new DecoderBuffer(NULL, size)));
+          std::make_pair(cb, new DecoderBuffer(NULL, size, is_keyframe)));
     }
   }
 
@@ -101,7 +103,8 @@
 }
 
 scoped_refptr<DecoderBuffer> ShellBufferFactory::AllocateBufferNow(
-    size_t size) {
+    size_t size,
+    bool is_keyframe) {
   TRACE_EVENT1("media_stack", "ShellBufferFactory::AllocateBufferNow()", "size",
                size);
   // Zero-size buffers are allocation error, allocate an EOS buffer explicity
@@ -121,7 +124,8 @@
         "ShellBufferFactory::AllocateBufferNow() failed as size is too large.");
     return NULL;
   }
-  scoped_refptr<DecoderBuffer> buffer = new DecoderBuffer(bytes, size);
+  scoped_refptr<DecoderBuffer> buffer =
+      new DecoderBuffer(bytes, size, is_keyframe);
   TRACE_EVENT0("media_stack",
                "ShellBufferFactory::AllocateBufferNow() finished allocation.");
   DCHECK(!buffer->IsEndOfStream());
diff --git a/src/media/base/shell_buffer_factory.h b/src/media/base/shell_buffer_factory.h
index eb172d7..8a18156 100644
--- a/src/media/base/shell_buffer_factory.h
+++ b/src/media/base/shell_buffer_factory.h
@@ -75,10 +75,10 @@
   // Returns false if the allocator will never be able to allocate a buffer
   // of the requested size. Note that if memory is currently available this
   // function will call the callback provided _before_ returning true.
-  bool AllocateBuffer(size_t size, AllocCB cb);
+  bool AllocateBuffer(size_t size, bool is_keyframe, AllocCB cb);
   // This function tries to allocate a DecoderBuffer immediately. It returns
   // NULL on failure.
-  scoped_refptr<DecoderBuffer> AllocateBufferNow(size_t size);
+  scoped_refptr<DecoderBuffer> AllocateBufferNow(size_t size, bool is_keyframe);
   // Returns a newly allocated byte field if there's room for it, or NULL if
   // there isn't. Note that this raw allocation method provides no guarantee
   // that ShellBufferFactory will still exist when the memory is to be freed.
diff --git a/src/media/base/shell_cached_decoder_buffer.cc b/src/media/base/shell_cached_decoder_buffer.cc
index 8fe62b9..28873ba 100644
--- a/src/media/base/shell_cached_decoder_buffer.cc
+++ b/src/media/base/shell_cached_decoder_buffer.cc
@@ -22,7 +22,7 @@
     const scoped_refptr<media::DecoderBuffer>& source_buffer,
     void* destination,
     FreeCB free_cb)
-    : media::DecoderBuffer(NULL, 0),  // Initialize with empty data first.
+    : media::DecoderBuffer(NULL, 0, source_buffer->IsKeyframe()),
       source_buffer_(source_buffer),
       free_cb_(free_cb) {
   DCHECK(source_buffer);
diff --git a/src/media/base/shell_cached_decoder_buffer.h b/src/media/base/shell_cached_decoder_buffer.h
index e0f1c27..db3baad 100644
--- a/src/media/base/shell_cached_decoder_buffer.h
+++ b/src/media/base/shell_cached_decoder_buffer.h
@@ -38,10 +38,10 @@
       FreeCB free_cb);
   ~ShellCachedDecoderBuffer();
 
-  virtual const media::DecryptConfig* GetDecryptConfig() const OVERRIDE {
+  const media::DecryptConfig* GetDecryptConfig() const OVERRIDE {
     return source_buffer_->GetDecryptConfig();
   }
-  virtual void SetDecryptConfig(scoped_ptr<media::DecryptConfig>) OVERRIDE {
+  void SetDecryptConfig(scoped_ptr<media::DecryptConfig>) OVERRIDE {
     NOTREACHED();
   }
 
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
new file mode 100644
index 0000000..6febc39
--- /dev/null
+++ b/src/media/base/starboard_player.cc
@@ -0,0 +1,394 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "media/base/starboard_player.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "media/base/starboard_utils.h"
+
+namespace media {
+
+StarboardPlayer::StarboardPlayer(
+    const scoped_refptr<base::MessageLoopProxy>& message_loop,
+    const AudioDecoderConfig& audio_config,
+    const VideoDecoderConfig& video_config,
+    SbWindow window,
+    SbDrmSystem drm_system,
+    Host* host,
+    SbPlayerSetBoundsHelper* set_bounds_helper)
+    : message_loop_(message_loop),
+      window_(window),
+      drm_system_(drm_system),
+      host_(host),
+      set_bounds_helper_(set_bounds_helper),
+      weak_this_(AsWeakPtr()),
+      frame_width_(1),
+      frame_height_(1),
+      ticket_(SB_PLAYER_INITIAL_TICKET),
+      volume_(1.0),
+      paused_(true),
+      state_(kPlaying) {
+  DCHECK(audio_config.IsValidConfig());
+  DCHECK(video_config.IsValidConfig());
+  DCHECK(host_);
+  DCHECK(set_bounds_helper_);
+
+  audio_config_.CopyFrom(audio_config);
+  video_config_.CopyFrom(video_config);
+
+  CreatePlayer();
+}
+
+StarboardPlayer::~StarboardPlayer() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  set_bounds_helper_->SetPlayer(NULL);
+  SbPlayerDestroy(player_);
+}
+
+void StarboardPlayer::UpdateVideoResolution(int frame_width, int frame_height) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  frame_width_ = frame_width;
+  frame_height_ = frame_height;
+}
+
+void StarboardPlayer::WriteBuffer(DemuxerStream::Type type,
+                                  const scoped_refptr<DecoderBuffer>& buffer) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  // When |state_| is kPlaying, cache all buffer appended.  When |state_| is
+  // kSuspended, there may still be left-over buffers appended from the pipeline
+  // so they also should be cached.
+  // When |state_| is resuming, all buffers come from the cache and shouldn't be
+  // cached.
+  if (state_ != kResuming) {
+    decoder_buffer_cache_.AddBuffer(type, buffer);
+  }
+
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    return;
+  }
+
+  DCHECK(SbPlayerIsValid(player_));
+
+  if (buffer->IsEndOfStream()) {
+    SbPlayerWriteEndOfStream(player_, DemuxerStreamTypeToSbMediaType(type));
+    return;
+  }
+
+  DecodingBuffers::iterator iter = decoding_buffers_.find(buffer->GetData());
+  if (iter == decoding_buffers_.end()) {
+    decoding_buffers_[buffer->GetData()] = std::make_pair(buffer, 1);
+  } else {
+    ++iter->second.second;
+  }
+
+  SbDrmSampleInfo drm_info;
+  SbDrmSubSampleMapping subsample_mapping;
+  bool is_encrypted = buffer->GetDecryptConfig();
+  SbMediaVideoSampleInfo video_info;
+
+  drm_info.subsample_count = 0;
+  video_info.is_key_frame = buffer->IsKeyframe();
+  video_info.frame_width = frame_width_;
+  video_info.frame_height = frame_height_;
+
+  if (is_encrypted) {
+    FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
+  }
+  SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
+                      buffer->GetData(), buffer->GetDataSize(),
+                      TimeDeltaToSbMediaTime(buffer->GetTimestamp()),
+                      type == DemuxerStream::VIDEO ? &video_info : NULL,
+                      drm_info.subsample_count > 0 ? &drm_info : NULL);
+}
+
+void StarboardPlayer::SetBounds(const gfx::Rect& rect) {
+  DCHECK(SbPlayerIsValid(player_));
+
+  SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void StarboardPlayer::PrepareForSeek() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+  ++ticket_;
+}
+
+void StarboardPlayer::Seek(base::TimeDelta time) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  decoder_buffer_cache_.ClearAll();
+
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    preroll_timestamp_ = time;
+    return;
+  }
+
+  // If a seek happens during resuming, the pipeline will write samples from the
+  // seek target time again so resuming can be aborted.
+  if (state_ == kResuming) {
+    state_ = kPlaying;
+  }
+
+  DCHECK(SbPlayerIsValid(player_));
+
+  ++ticket_;
+  SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
+}
+
+void StarboardPlayer::SetVolume(float volume) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  volume_ = volume;
+
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    return;
+  }
+
+  DCHECK(SbPlayerIsValid(player_));
+  SbPlayerSetVolume(player_, volume);
+}
+
+void StarboardPlayer::SetPause(bool pause) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(SbPlayerIsValid(player_));
+
+  SbPlayerSetPause(player_, pause);
+  paused_ = pause;
+}
+
+void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
+                              uint32* video_frames_dropped,
+                              base::TimeDelta* media_time) {
+  DCHECK(video_frames_decoded);
+  DCHECK(video_frames_dropped);
+  DCHECK(media_time);
+
+  base::AutoLock auto_lock(lock_);
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+
+    *video_frames_decoded = cached_video_frames_decoded_;
+    *video_frames_dropped = cached_video_frames_dropped_;
+    *media_time = preroll_timestamp_;
+    return;
+  }
+
+  DCHECK(SbPlayerIsValid(player_));
+
+  SbPlayerInfo info;
+  SbPlayerGetInfo(player_, &info);
+  *video_frames_decoded = info.total_video_frames;
+  *video_frames_dropped = info.dropped_video_frames;
+  *media_time = SbMediaTimeToTimeDelta(info.current_media_pts);
+}
+
+void StarboardPlayer::Suspend() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  // Check if the player is already suspended.
+  if (state_ == kSuspended) {
+    DCHECK(!SbPlayerIsValid(player_));
+    return;
+  }
+
+  DCHECK(SbPlayerIsValid(player_));
+
+  SbPlayerSetPause(player_, true);
+
+  base::AutoLock auto_lock(lock_);
+
+  state_ = kSuspended;
+
+  SbPlayerInfo info;
+  SbPlayerGetInfo(player_, &info);
+  cached_video_frames_decoded_ = info.total_video_frames;
+  cached_video_frames_dropped_ = info.dropped_video_frames;
+  preroll_timestamp_ = SbMediaTimeToTimeDelta(info.current_media_pts);
+
+  set_bounds_helper_->SetPlayer(NULL);
+  SbPlayerDestroy(player_);
+
+  player_ = kSbPlayerInvalid;
+}
+
+void StarboardPlayer::Resume() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  // Check if the player is already resumed.
+  if (state_ != kSuspended) {
+    DCHECK(SbPlayerIsValid(player_));
+    return;
+  }
+
+  DCHECK(!SbPlayerIsValid(player_));
+
+  decoder_buffer_cache_.StartResuming();
+
+  CreatePlayer();
+
+  base::AutoLock auto_lock(lock_);
+  state_ = kResuming;
+}
+
+void StarboardPlayer::CreatePlayer() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  SbMediaAudioHeader audio_header;
+  // TODO: Make this work with non AAC audio.
+  audio_header.format_tag = 0x00ff;
+  audio_header.number_of_channels =
+      ChannelLayoutToChannelCount(audio_config_.channel_layout());
+  audio_header.samples_per_second = audio_config_.samples_per_second();
+  audio_header.average_bytes_per_second = 1;
+  audio_header.block_alignment = 4;
+  audio_header.bits_per_sample = audio_config_.bits_per_channel();
+  audio_header.audio_specific_config_size = 0;
+
+  SbMediaAudioCodec audio_codec =
+      MediaAudioCodecToSbMediaAudioCodec(audio_config_.codec());
+  SbMediaVideoCodec video_codec =
+      MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
+
+  player_ = SbPlayerCreate(window_, video_codec, audio_codec,
+                           SB_PLAYER_NO_DURATION, drm_system_, &audio_header,
+                           &StarboardPlayer::DeallocateSampleCB,
+                           &StarboardPlayer::DecoderStatusCB,
+                           &StarboardPlayer::PlayerStatusCB, this);
+  set_bounds_helper_->SetPlayer(this);
+}
+
+void StarboardPlayer::OnDecoderStatus(SbPlayer player,
+                                      SbMediaType type,
+                                      SbPlayerDecoderState state,
+                                      int ticket) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  if (player_ != player || ticket != ticket_) {
+    return;
+  }
+
+  DCHECK_NE(state_, kSuspended);
+
+  switch (state) {
+    case kSbPlayerDecoderStateNeedsData:
+      break;
+    case kSbPlayerDecoderStateBufferFull:
+      DLOG(WARNING) << "kSbPlayerDecoderStateBufferFull has been deprecated.";
+      return;
+    case kSbPlayerDecoderStateDestroyed:
+      return;
+  }
+
+  if (state_ == kResuming) {
+    DemuxerStream::Type stream_type = SbMediaTypeToDemuxerStreamType(type);
+    if (decoder_buffer_cache_.GetBuffer(stream_type)) {
+      WriteBuffer(stream_type, decoder_buffer_cache_.GetBuffer(stream_type));
+      decoder_buffer_cache_.AdvanceToNextBuffer(stream_type);
+      return;
+    }
+    if (!decoder_buffer_cache_.GetBuffer(DemuxerStream::AUDIO) &&
+        !decoder_buffer_cache_.GetBuffer(DemuxerStream::VIDEO)) {
+      state_ = kPlaying;
+    }
+  }
+
+  host_->OnNeedData(SbMediaTypeToDemuxerStreamType(type));
+}
+
+void StarboardPlayer::OnPlayerStatus(SbPlayer player,
+                                     SbPlayerState state,
+                                     int ticket) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  if (player_ != player) {
+    return;
+  }
+
+  DCHECK_NE(state_, kSuspended);
+
+  if (ticket != SB_PLAYER_INITIAL_TICKET && ticket != ticket_) {
+    return;
+  }
+
+  if (state == kSbPlayerStateInitialized) {
+    if (ticket_ == SB_PLAYER_INITIAL_TICKET) {
+      ++ticket_;
+    }
+    SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
+    SetVolume(volume_);
+    if (paused_) {
+      SetPause(paused_);
+    }
+    return;
+  }
+  host_->OnPlayerStatus(state);
+}
+
+void StarboardPlayer::OnDeallocateSample(const void* sample_buffer) {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+
+  DecodingBuffers::iterator iter = decoding_buffers_.find(sample_buffer);
+  DCHECK(iter != decoding_buffers_.end());
+  if (iter == decoding_buffers_.end()) {
+    LOG(ERROR) << "StarboardPlayer::OnDeallocateSample encounters unknown "
+               << "sample_buffer " << sample_buffer;
+    return;
+  }
+  --iter->second.second;
+  if (iter->second.second == 0) {
+    decoding_buffers_.erase(iter);
+  }
+}
+
+// static
+void StarboardPlayer::DecoderStatusCB(SbPlayer player,
+                                      void* context,
+                                      SbMediaType type,
+                                      SbPlayerDecoderState state,
+                                      int ticket) {
+  StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
+  helper->message_loop_->PostTask(
+      FROM_HERE, base::Bind(&StarboardPlayer::OnDecoderStatus,
+                            helper->weak_this_, player, type, state, ticket));
+}
+
+// static
+void StarboardPlayer::PlayerStatusCB(SbPlayer player,
+                                     void* context,
+                                     SbPlayerState state,
+                                     int ticket) {
+  StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
+  helper->message_loop_->PostTask(
+      FROM_HERE, base::Bind(&StarboardPlayer::OnPlayerStatus,
+                            helper->weak_this_, player, state, ticket));
+}
+
+// static
+void StarboardPlayer::DeallocateSampleCB(SbPlayer player,
+                                         void* context,
+                                         const void* sample_buffer) {
+  StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
+  helper->message_loop_->PostTask(
+      FROM_HERE, base::Bind(&StarboardPlayer::OnDeallocateSample,
+                            helper->weak_this_, sample_buffer));
+}
+
+}  // namespace media
diff --git a/src/media/base/starboard_player.h b/src/media/base/starboard_player.h
new file mode 100644
index 0000000..bf2bb5a
--- /dev/null
+++ b/src/media/base/starboard_player.h
@@ -0,0 +1,145 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MEDIA_BASE_STARBOARD_PLAYER_H_
+#define MEDIA_BASE_STARBOARD_PLAYER_H_
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decoder_buffer_cache.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/sbplayer_set_bounds_helper.h"
+#include "media/base/video_decoder_config.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+
+namespace media {
+
+// TODO: Add switch to disable caching
+class StarboardPlayer : public base::SupportsWeakPtr<StarboardPlayer> {
+ public:
+  class Host {
+   public:
+    virtual void OnNeedData(DemuxerStream::Type type) = 0;
+    virtual void OnPlayerStatus(SbPlayerState state) = 0;
+
+   protected:
+    ~Host() {}
+  };
+
+  StarboardPlayer(const scoped_refptr<base::MessageLoopProxy>& message_loop,
+                  const AudioDecoderConfig& audio_config,
+                  const VideoDecoderConfig& video_config,
+                  SbWindow window,
+                  SbDrmSystem drm_system,
+                  Host* host,
+                  SbPlayerSetBoundsHelper* set_bounds_helper);
+  ~StarboardPlayer();
+
+  bool IsValid() const { return SbPlayerIsValid(player_); }
+
+  void UpdateVideoResolution(int frame_width, int frame_height);
+
+  void WriteBuffer(DemuxerStream::Type type,
+                   const scoped_refptr<DecoderBuffer>& buffer);
+  void SetBounds(const gfx::Rect& rect);
+
+  void PrepareForSeek();
+  void Seek(base::TimeDelta time);
+
+  void SetVolume(float volume);
+  void SetPause(bool pause);
+  void GetInfo(uint32* video_frames_decoded,
+               uint32* video_frames_dropped,
+               base::TimeDelta* media_time);
+
+  void Suspend();
+  void Resume();
+
+ private:
+  enum State {
+    kPlaying,
+    kSuspended,
+    kResuming,
+  };
+
+  // A map from raw data pointer returned by DecoderBuffer::GetData() to the
+  // DecoderBuffer and a reference count.  The reference count indicates how
+  // many instances of the DecoderBuffer is currently being decoded in the
+  // pipeline.
+  typedef std::map<const void*, std::pair<scoped_refptr<DecoderBuffer>, int> >
+      DecodingBuffers;
+
+  void CreatePlayer();
+
+  void OnDecoderStatus(SbPlayer player,
+                       SbMediaType type,
+                       SbPlayerDecoderState state,
+                       int ticket);
+  void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket);
+  void OnDeallocateSample(const void* sample_buffer);
+
+  static void DecoderStatusCB(SbPlayer player,
+                              void* context,
+                              SbMediaType type,
+                              SbPlayerDecoderState state,
+                              int ticket);
+  static void PlayerStatusCB(SbPlayer player,
+                             void* context,
+                             SbPlayerState state,
+                             int ticket);
+  static void DeallocateSampleCB(SbPlayer player,
+                                 void* context,
+                                 const void* sample_buffer);
+
+  // The following variables are initialized in the ctor and never changed.
+  const scoped_refptr<base::MessageLoopProxy> message_loop_;
+  AudioDecoderConfig audio_config_;
+  VideoDecoderConfig video_config_;
+  const SbWindow window_;
+  const SbDrmSystem drm_system_;
+  Host* const host_;
+  SbPlayerSetBoundsHelper* const set_bounds_helper_;
+  const base::WeakPtr<StarboardPlayer> weak_this_;
+
+  // The following variables are only changed or accessed from the
+  // |message_loop_|.
+  int frame_width_;
+  int frame_height_;
+  DecodingBuffers decoding_buffers_;
+  int ticket_;
+  float volume_;
+  bool paused_;
+  DecoderBufferCache decoder_buffer_cache_;
+
+  // The following variables can be accessed from GetInfo(), which can be called
+  // from any threads.  So some of their usages have to be guarded by |lock_|.
+  base::Lock lock_;
+  State state_;
+  SbPlayer player_;
+  uint32 cached_video_frames_decoded_;
+  uint32 cached_video_frames_dropped_;
+  base::TimeDelta preroll_timestamp_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_STARBOARD_PLAYER_H_
diff --git a/src/media/base/starboard_utils.cc b/src/media/base/starboard_utils.cc
new file mode 100644
index 0000000..94fba6b
--- /dev/null
+++ b/src/media/base/starboard_utils.cc
@@ -0,0 +1,128 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "media/base/starboard_utils.h"
+
+#include "base/logging.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace media {
+
+SbMediaAudioCodec MediaAudioCodecToSbMediaAudioCodec(AudioCodec codec) {
+  if (codec == kCodecAAC) {
+    return kSbMediaAudioCodecAac;
+  } else if (codec == kCodecVorbis) {
+    return kSbMediaAudioCodecVorbis;
+  } else if (codec == kCodecOpus) {
+    return kSbMediaAudioCodecOpus;
+  }
+  DLOG(ERROR) << "Unsupported audio codec " << codec;
+  return kSbMediaAudioCodecNone;
+}
+
+SbMediaVideoCodec MediaVideoCodecToSbMediaVideoCodec(VideoCodec codec) {
+  if (codec == kCodecH264) {
+    return kSbMediaVideoCodecH264;
+  } else if (codec == kCodecVC1) {
+    return kSbMediaVideoCodecVc1;
+  } else if (codec == kCodecMPEG2) {
+    return kSbMediaVideoCodecMpeg2;
+  } else if (codec == kCodecTheora) {
+    return kSbMediaVideoCodecTheora;
+  } else if (codec == kCodecVP8) {
+    return kSbMediaVideoCodecVp8;
+  } else if (codec == kCodecVP9) {
+    return kSbMediaVideoCodecVp9;
+  }
+  DLOG(ERROR) << "Unsupported video codec " << codec;
+  return kSbMediaVideoCodecNone;
+}
+
+TimeDelta SbMediaTimeToTimeDelta(SbMediaTime timestamp) {
+  return TimeDelta::FromMicroseconds(timestamp * Time::kMicrosecondsPerSecond /
+                                     kSbMediaTimeSecond);
+}
+
+SbMediaTime TimeDeltaToSbMediaTime(TimeDelta timedelta) {
+  return timedelta.InMicroseconds() * kSbMediaTimeSecond /
+         Time::kMicrosecondsPerSecond;
+}
+
+DemuxerStream::Type SbMediaTypeToDemuxerStreamType(SbMediaType type) {
+  if (type == kSbMediaTypeAudio) {
+    return DemuxerStream::AUDIO;
+  }
+  DCHECK_EQ(type, kSbMediaTypeVideo);
+  return DemuxerStream::VIDEO;
+}
+
+SbMediaType DemuxerStreamTypeToSbMediaType(DemuxerStream::Type type) {
+  if (type == DemuxerStream::AUDIO) {
+    return kSbMediaTypeAudio;
+  }
+  DCHECK_EQ(type, DemuxerStream::VIDEO);
+  return kSbMediaTypeVideo;
+}
+
+void FillDrmSampleInfo(const scoped_refptr<DecoderBuffer>& buffer,
+                       SbDrmSampleInfo* drm_info,
+                       SbDrmSubSampleMapping* subsample_mapping) {
+  DCHECK(drm_info);
+  DCHECK(subsample_mapping);
+
+  const DecryptConfig* config = buffer->GetDecryptConfig();
+  if (!config || config->iv().empty() || config->key_id().empty()) {
+    drm_info->initialization_vector_size = 0;
+    drm_info->identifier_size = 0;
+    drm_info->subsample_count = 0;
+    drm_info->subsample_mapping = NULL;
+    return;
+  }
+
+  DCHECK_LE(config->iv().size(), sizeof(drm_info->initialization_vector));
+  DCHECK_LE(config->key_id().size(), sizeof(drm_info->identifier));
+
+  if (config->iv().size() > sizeof(drm_info->initialization_vector) ||
+      config->key_id().size() > sizeof(drm_info->identifier)) {
+    drm_info->initialization_vector_size = 0;
+    drm_info->identifier_size = 0;
+    drm_info->subsample_count = 0;
+    drm_info->subsample_mapping = NULL;
+    return;
+  }
+
+  memcpy(drm_info->initialization_vector, &config->iv()[0],
+         config->iv().size());
+  drm_info->initialization_vector_size = config->iv().size();
+  memcpy(drm_info->identifier, &config->key_id()[0], config->key_id().size());
+  drm_info->identifier_size = config->key_id().size();
+  drm_info->subsample_count = config->subsamples().size();
+
+  if (drm_info->subsample_count > 0) {
+    COMPILE_ASSERT(sizeof(SbDrmSubSampleMapping) == sizeof(SubsampleEntry),
+                   SubSampleEntrySizesMatch);
+    drm_info->subsample_mapping =
+        reinterpret_cast<const SbDrmSubSampleMapping*>(
+            &config->subsamples()[0]);
+  } else {
+    drm_info->subsample_count = 1;
+    drm_info->subsample_mapping = subsample_mapping;
+    subsample_mapping->clear_byte_count = 0;
+    subsample_mapping->encrypted_byte_count = buffer->GetDataSize();
+  }
+}
+
+}  // namespace media
diff --git a/src/media/base/starboard_utils.h b/src/media/base/starboard_utils.h
new file mode 100644
index 0000000..ff3323b
--- /dev/null
+++ b/src/media/base/starboard_utils.h
@@ -0,0 +1,42 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MEDIA_BASE_STARBOARD_UTILS_H_
+#define MEDIA_BASE_STARBOARD_UTILS_H_
+
+#include "media/base/audio_decoder_config.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/video_decoder_config.h"
+#include "starboard/drm.h"
+#include "starboard/media.h"
+
+namespace media {
+
+SbMediaAudioCodec MediaAudioCodecToSbMediaAudioCodec(AudioCodec codec);
+SbMediaVideoCodec MediaVideoCodecToSbMediaVideoCodec(VideoCodec codec);
+
+base::TimeDelta SbMediaTimeToTimeDelta(SbMediaTime timestamp);
+SbMediaTime TimeDeltaToSbMediaTime(base::TimeDelta timedelta);
+
+DemuxerStream::Type SbMediaTypeToDemuxerStreamType(SbMediaType type);
+SbMediaType DemuxerStreamTypeToSbMediaType(DemuxerStream::Type type);
+
+void FillDrmSampleInfo(const scoped_refptr<DecoderBuffer>& buffer,
+                       SbDrmSampleInfo* drm_info,
+                       SbDrmSubSampleMapping* subsample_mapping);
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_STARBOARD_UTILS_H_
diff --git a/src/media/base/stream_parser_buffer.cc b/src/media/base/stream_parser_buffer.cc
index 769f67b..6a6c4a5 100644
--- a/src/media/base/stream_parser_buffer.cc
+++ b/src/media/base/stream_parser_buffer.cc
@@ -54,21 +54,19 @@
 }
 
 #if defined(__LB_SHELL__) || defined(COBALT)
-StreamParserBuffer::StreamParserBuffer(
-    uint8* data,
-    int data_size,
-    bool is_keyframe)
-    : DecoderBuffer(data, data_size),
-      is_keyframe_(is_keyframe),
+StreamParserBuffer::StreamParserBuffer(uint8* data,
+                                       int data_size,
+                                       bool is_keyframe)
+    : DecoderBuffer(data, data_size, is_keyframe),
       decode_timestamp_(kNoTimestamp()),
       config_id_(kInvalidConfigId) {
   SetDuration(kNoTimestamp());
 }
 #else
-StreamParserBuffer::StreamParserBuffer(const uint8* data, int data_size,
+StreamParserBuffer::StreamParserBuffer(const uint8* data,
+                                       int data_size,
                                        bool is_keyframe)
-    : DecoderBuffer(data, data_size),
-      is_keyframe_(is_keyframe),
+    : DecoderBuffer(data, data_size, is_keyframe),
       decode_timestamp_(kNoTimestamp()),
       config_id_(kInvalidConfigId) {
   SetDuration(kNoTimestamp());
diff --git a/src/media/base/stream_parser_buffer.h b/src/media/base/stream_parser_buffer.h
index eecc147..a5471d9 100644
--- a/src/media/base/stream_parser_buffer.h
+++ b/src/media/base/stream_parser_buffer.h
@@ -19,8 +19,6 @@
   static scoped_refptr<StreamParserBuffer> CopyFrom(
       const uint8* data, int data_size, bool is_keyframe);
 
-  bool IsKeyframe() const { return is_keyframe_; }
-
   // Decode timestamp. If not explicitly set, or set to kNoTimestamp(), the
   // value will be taken from the normal timestamp.
   base::TimeDelta GetDecodeTimestamp() const;
@@ -39,7 +37,6 @@
 #endif
   virtual ~StreamParserBuffer();
 
-  bool is_keyframe_;
   base::TimeDelta decode_timestamp_;
   int config_id_;
   DISALLOW_COPY_AND_ASSIGN(StreamParserBuffer);
diff --git a/src/media/filters/chunk_demuxer.cc b/src/media/filters/chunk_demuxer.cc
index ab2b490..76cc2c8 100644
--- a/src/media/filters/chunk_demuxer.cc
+++ b/src/media/filters/chunk_demuxer.cc
@@ -32,6 +32,9 @@
 
 #if defined(OS_STARBOARD)
 #include "starboard/configuration.h"
+#if SB_HAS_QUIRK(SEEK_TO_KEYFRAME)
+#define CHUNK_DEMUXER_SEEK_TO_KEYFRAME
+#endif  // SB_HAS_QUIRK(SEEK_TO_KEYFRAME)
 #endif  // defined(OS_STARBOARD)
 
 using base::TimeDelta;
@@ -232,6 +235,7 @@
   void Seek(TimeDelta time);
   void CancelPendingSeek();
   bool IsSeekPending() const;
+  base::TimeDelta GetSeekKeyframeTimestamp() const;
 
   // Add buffers to this stream.  Buffers are stored in SourceBufferStreams,
   // which handle ordering and overlap resolution.
@@ -375,6 +379,11 @@
   return stream_->IsSeekPending();
 }
 
+base::TimeDelta ChunkDemuxerStream::GetSeekKeyframeTimestamp() const {
+  base::AutoLock auto_lock(lock_);
+  return stream_->GetSeekKeyframeTimestamp();
+}
+
 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) {
   base::AutoLock auto_lock(lock_);
   DCHECK(!end_of_stream_);
@@ -646,6 +655,7 @@
                            const NeedKeyCB& need_key_cb,
                            const LogCB& log_cb)
     : state_(WAITING_FOR_INIT),
+      delayed_audio_seek_(false),
       host_(NULL),
       open_cb_(open_cb),
       need_key_cb_(need_key_cb),
@@ -695,11 +705,19 @@
     base::AutoLock auto_lock(lock_);
 
     if (state_ == INITIALIZED || state_ == ENDED) {
-      if (audio_)
-        audio_->Seek(time);
-
       if (video_)
         video_->Seek(time);
+#if defined(CHUNK_DEMUXER_SEEK_TO_KEYFRAME)
+      // We only need to do a delayed audio seek when there are both audio and
+      // video streams and the seek on the video stream is pending.
+      delayed_audio_seek_ = audio_ && video_ && video_->IsSeekPending();
+      if (audio_ && !delayed_audio_seek_) {
+        audio_->Seek(video_->GetSeekKeyframeTimestamp());
+      }
+#else   // defined(CHUNK_DEMUXER_SEEK_TO_KEYFRAME)
+      if (audio_)
+        audio_->Seek(time);
+#endif  // defined(CHUNK_DEMUXER_SEEK_TO_KEYFRAME)
 
       if (IsSeekPending_Locked()) {
         DVLOG(1) << "Seek() : waiting for more data to arrive.";
@@ -961,6 +979,12 @@
         return false;
     }
 
+    if (delayed_audio_seek_ && !video_->IsSeekPending()) {
+      DCHECK(audio_);
+      audio_->Seek(video_->GetSeekKeyframeTimestamp());
+      delayed_audio_seek_ = false;
+    }
+
     // Check to see if data was appended at the pending seek point. This
     // indicates we have parsed enough data to complete the seek.
     if (old_seek_pending && !IsSeekPending_Locked() && !seek_cb_.is_null()) {
diff --git a/src/media/filters/chunk_demuxer.h b/src/media/filters/chunk_demuxer.h
index e127c08..6e09181 100644
--- a/src/media/filters/chunk_demuxer.h
+++ b/src/media/filters/chunk_demuxer.h
@@ -186,6 +186,7 @@
 
   PipelineStatusCB init_cb_;
   PipelineStatusCB seek_cb_;
+  bool delayed_audio_seek_;
 
   scoped_refptr<ChunkDemuxerStream> audio_;
   scoped_refptr<ChunkDemuxerStream> video_;
diff --git a/src/media/filters/shell_demuxer.cc b/src/media/filters/shell_demuxer.cc
index 6671985..3d18888 100644
--- a/src/media/filters/shell_demuxer.cc
+++ b/src/media/filters/shell_demuxer.cc
@@ -342,7 +342,8 @@
   // AllocateBuffer will return false if the requested size is larger
   // than the maximum limit for a single buffer.
   if (!ShellBufferFactory::Instance()->AllocateBuffer(
-          au->GetMaxSize(), base::Bind(&ShellDemuxer::BufferAllocated, this))) {
+          au->GetMaxSize(), au->IsKeyframe(),
+          base::Bind(&ShellDemuxer::BufferAllocated, this))) {
     DLOG(ERROR) << "buffer allocation failed.";
     host_->OnDemuxerError(PIPELINE_ERROR_COULD_NOT_RENDER);
     return;
diff --git a/src/media/filters/source_buffer_stream.cc b/src/media/filters/source_buffer_stream.cc
index 31e64b6..7f3678d 100644
--- a/src/media/filters/source_buffer_stream.cc
+++ b/src/media/filters/source_buffer_stream.cc
@@ -968,6 +968,7 @@
     SetSelectedRange(ranges_.front());
     ranges_.front()->SeekToStart();
     seek_pending_ = false;
+    seek_buffer_timestamp_ = GetNextBufferTimestamp();
     return;
   }
 
@@ -986,12 +987,18 @@
   SetSelectedRange(*itr);
   selected_range_->Seek(timestamp);
   seek_pending_ = false;
+  seek_buffer_timestamp_ = GetNextBufferTimestamp();
 }
 
 bool SourceBufferStream::IsSeekPending() const {
   return seek_pending_;
 }
 
+base::TimeDelta SourceBufferStream::GetSeekKeyframeTimestamp() const {
+  DCHECK(!IsSeekPending());
+  return seek_buffer_timestamp_;
+}
+
 void SourceBufferStream::OnSetDuration(base::TimeDelta duration) {
   RangeList::iterator itr = ranges_.end();
   for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) {
diff --git a/src/media/filters/source_buffer_stream.h b/src/media/filters/source_buffer_stream.h
index 4f0ff4b..babcb65 100644
--- a/src/media/filters/source_buffer_stream.h
+++ b/src/media/filters/source_buffer_stream.h
@@ -72,6 +72,11 @@
   // buffered data and is waiting for more data to be appended.
   bool IsSeekPending() const;
 
+  // Returns the timestamp of the keyframe before the seek timestamp.  Note that
+  // this value is only valid (thus this function should only be called) when
+  // IsSeekPending() returns false.
+  base::TimeDelta GetSeekKeyframeTimestamp() const;
+
   // Notifies the SourceBufferStream that the media duration has been changed to
   // |duration| so it should drop any data past that point.
   void OnSetDuration(base::TimeDelta duration);
@@ -269,6 +274,9 @@
   // false if no Seek() has been requested or the Seek() is completed.
   bool seek_pending_;
 
+  // The timestamp of the keyframe right before the seek timestamp.
+  base::TimeDelta seek_keyframe_timestamp_;
+
   // Timestamp of the last request to Seek().
   base::TimeDelta seek_buffer_timestamp_;
 
diff --git a/src/media/media.gyp b/src/media/media.gyp
index c2faa35..12ebe12 100644
--- a/src/media/media.gyp
+++ b/src/media/media.gyp
@@ -716,7 +716,15 @@
             ['sb_media_platform == "starboard"', {
               'sources': [
                 'audio/shell_audio_streamer_starboard.cc',
+                'base/decoder_buffer_cache.cc',
+                'base/decoder_buffer_cache.h',
+                'base/sbplayer_set_bounds_helper.cc',
+                'base/sbplayer_set_bounds_helper.h',
                 'base/sbplayer_pipeline.cc',
+                'base/starboard_player.cc',
+                'base/starboard_player.h',
+                'base/starboard_utils.cc',
+                'base/starboard_utils.h',
                 'crypto/starboard_decryptor.cc',
                 'crypto/starboard_decryptor.h',
               ],
diff --git a/src/media/player/web_media_player.h b/src/media/player/web_media_player.h
index ef6b89a..6b97161 100644
--- a/src/media/player/web_media_player.h
+++ b/src/media/player/web_media_player.h
@@ -102,6 +102,10 @@
   virtual const Ranges<base::TimeDelta>& GetBufferedTimeRanges() = 0;
   virtual float GetMaxTimeSeekable() const = 0;
 
+  // Suspend/Resume
+  virtual void Suspend() = 0;
+  virtual void Resume() = 0;
+
   // True if the loaded media has a playable video/audio track.
   virtual bool HasVideo() const = 0;
   virtual bool HasAudio() const = 0;
diff --git a/src/media/player/web_media_player_impl.cc b/src/media/player/web_media_player_impl.cc
index 1defe0f..d3b4a19 100644
--- a/src/media/player/web_media_player_impl.cc
+++ b/src/media/player/web_media_player_impl.cc
@@ -536,6 +536,14 @@
   return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
 }
 
+void WebMediaPlayerImpl::Suspend() {
+  pipeline_->Suspend();
+}
+
+void WebMediaPlayerImpl::Resume() {
+  pipeline_->Resume();
+}
+
 bool WebMediaPlayerImpl::DidLoadingProgress() const {
   DCHECK_EQ(main_loop_, MessageLoop::current());
   return pipeline_->DidLoadingProgress();
diff --git a/src/media/player/web_media_player_impl.h b/src/media/player/web_media_player_impl.h
index fcc1030..6b1db75 100644
--- a/src/media/player/web_media_player_impl.h
+++ b/src/media/player/web_media_player_impl.h
@@ -137,6 +137,10 @@
   const Ranges<base::TimeDelta>& GetBufferedTimeRanges() OVERRIDE;
   float GetMaxTimeSeekable() const OVERRIDE;
 
+  // Suspend/Resume
+  void Suspend() OVERRIDE;
+  void Resume() OVERRIDE;
+
   // True if the loaded media has a playable video/audio track.
   bool HasVideo() const OVERRIDE;
   bool HasAudio() const OVERRIDE;
diff --git a/src/nb/reuse_allocator.cc b/src/nb/reuse_allocator.cc
index 70898ae..c12375d 100644
--- a/src/nb/reuse_allocator.cc
+++ b/src/nb/reuse_allocator.cc
@@ -266,21 +266,8 @@
 }
 
 void ReuseAllocator::Free(void* memory) {
-  if (!memory) {
-    return;
-  }
-
-  AllocatedBlockMap::iterator it = allocated_blocks_.find(memory);
-  SB_DCHECK(it != allocated_blocks_.end());
-
-  // Mark this block as free and remove it from the allocated set.
-  const MemoryBlock& block = (*it).second;
-  AddFreeBlock(block);
-
-  SB_DCHECK(block.size() <= total_allocated_);
-  total_allocated_ -= block.size();
-
-  allocated_blocks_.erase(it);
+  bool result = TryFree(memory);
+  SB_DCHECK(result);
 }
 
 void ReuseAllocator::PrintAllocations() const {
@@ -302,4 +289,25 @@
   SB_LOG(INFO) << "Total allocations: " << allocated_blocks_.size();
 }
 
+bool ReuseAllocator::TryFree(void* memory) {
+  if (!memory) {
+    return true;
+  }
+
+  AllocatedBlockMap::iterator it = allocated_blocks_.find(memory);
+  if (it == allocated_blocks_.end()) {
+    return false;
+  }
+
+  // Mark this block as free and remove it from the allocated set.
+  const MemoryBlock& block = (*it).second;
+  AddFreeBlock(block);
+
+  SB_DCHECK(block.size() <= total_allocated_);
+  total_allocated_ -= block.size();
+
+  allocated_blocks_.erase(it);
+  return true;
+}
+
 }  // namespace nb
diff --git a/src/nb/reuse_allocator.h b/src/nb/reuse_allocator.h
index 7686930..71d43d8 100644
--- a/src/nb/reuse_allocator.h
+++ b/src/nb/reuse_allocator.h
@@ -65,6 +65,8 @@
 
   void PrintAllocations() const;
 
+  bool TryFree(void* memory);
+
  private:
   class MemoryBlock {
    public:
diff --git a/src/nb/reuse_allocator_benchmark.cc b/src/nb/reuse_allocator_benchmark.cc
index 5e48ba7..ac53c08 100644
--- a/src/nb/reuse_allocator_benchmark.cc
+++ b/src/nb/reuse_allocator_benchmark.cc
@@ -181,7 +181,7 @@
   void* Allocate(std::size_t size, std::size_t alignment) SB_OVERRIDE {
     return SbMemoryAllocateAligned(alignment, size);
   }
-  void Free(void* memory) SB_OVERRIDE { SbMemoryFree(memory); }
+  void Free(void* memory) SB_OVERRIDE { SbMemoryDeallocate(memory); }
   std::size_t GetCapacity() const SB_OVERRIDE { return 0; }
   std::size_t GetAllocated() const SB_OVERRIDE { return 0; }
   void PrintAllocations() const SB_OVERRIDE {}
@@ -210,7 +210,7 @@
   SB_DLOG(INFO) << "Allocator high water mark: " << allocator_high_water_mark;
   SB_DLOG(INFO) << "Elapsed (ms): " << elapsed_time / kSbTimeMillisecond;
 
-  SbMemoryFree(fixed_no_free_memory);
+  SbMemoryDeallocate(fixed_no_free_memory);
 
   // Test again using the system allocator, to compare performance.
   DefaultAllocator default_allocator;
diff --git a/src/net/http/http_auth_handler_ntlm.cc b/src/net/http/http_auth_handler_ntlm.cc
index ceca94a..aa0142a 100644
--- a/src/net/http/http_auth_handler_ntlm.cc
+++ b/src/net/http/http_auth_handler_ntlm.cc
@@ -15,7 +15,7 @@
 
 #if defined(OS_STARBOARD)
 #include "starboard/memory.h"
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #endif
 
 namespace net {
diff --git a/src/net/http/http_auth_handler_ntlm_portable.cc b/src/net/http/http_auth_handler_ntlm_portable.cc
index dbcfd94..63cdcee 100644
--- a/src/net/http/http_auth_handler_ntlm_portable.cc
+++ b/src/net/http/http_auth_handler_ntlm_portable.cc
@@ -7,7 +7,7 @@
 #if defined(OS_STARBOARD)
 #include "starboard/memory.h"
 #define malloc SbMemoryAllocate
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #else
 #include <stdlib.h>
 #endif
diff --git a/src/starboard/client_porting/poem/stdio_poem.h b/src/starboard/client_porting/poem/stdio_poem.h
index d68c688..eab3f05 100644
--- a/src/starboard/client_porting/poem/stdio_poem.h
+++ b/src/starboard/client_porting/poem/stdio_poem.h
@@ -34,10 +34,10 @@
 #define sprintf SbStringFormatUnsafeF
 #define vsscanf SbStringScan
 #define sscanf SbStringScanF
-#define malloc(sz) SbMemoryAllocateUnchecked(sz)
+#define malloc(sz) SbMemoryAllocate(sz)
 #define calloc(c, s) SbMemoryCalloc(c, s)
-#define free(a) SbMemoryFree(a)
-#define realloc(m, sz) SbMemoryReallocateUnchecked(m, sz)
+#define free(a) SbMemoryDeallocate(a)
+#define realloc(m, sz) SbMemoryReallocate(m, sz)
 
 #endif  // POEM_NO_EMULATION
 
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index b0bc5df..0503988 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -19,8 +19,12 @@
   'targets': [
     {
       'target_name': 'common',
-      'type': 'none',
+      'type': 'static_library',
+      'variables': {
+        'includes_starboard': 1,
+      },
       'sources': [
+        'memory.cc',
         'move.h',
         'reset_and_return.h',
         'scoped_ptr.h',
diff --git a/src/starboard/common/memory.cc b/src/starboard/common/memory.cc
new file mode 100644
index 0000000..6067214
--- /dev/null
+++ b/src/starboard/common/memory.cc
@@ -0,0 +1,88 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/memory.h"
+
+inline void* SbMemoryAllocateImpl(size_t size);
+inline void* SbMemoryAllocateAlignedImpl(size_t alignment, size_t size);
+inline void* SbMemoryReallocateImpl(void* memory, size_t size);
+
+void* SbMemoryAllocate(size_t size) {
+  void* memory = SbMemoryAllocateImpl(size);
+  return memory;
+}
+
+void* SbMemoryAllocateAligned(size_t alignment, size_t size) {
+  void* memory = SbMemoryAllocateAlignedImpl(alignment, size);
+  return memory;
+}
+
+void* SbMemoryReallocate(void* memory, size_t size) {
+  void* new_memory = SbMemoryReallocateImpl(memory, size);
+  return new_memory;
+}
+
+void SbMemoryDeallocate(void* memory) {
+  SbMemoryFree(memory);
+}
+
+void SbMemoryDeallocateAligned(void* memory) {
+  SbMemoryFreeAligned(memory);
+}
+
+// Same as SbMemoryReallocateUnchecked, but will abort() in the case of an
+// allocation failure
+void* SbMemoryReallocateChecked(void* memory, size_t size) {
+  void* address = SbMemoryReallocateUnchecked(memory, size);
+  SbAbortIfAllocationFailed(size, address);
+  return address;
+}
+
+// Same as SbMemoryAllocateAlignedUnchecked, but will abort() in the case of an
+// allocation failure
+void* SbMemoryAllocateAlignedChecked(size_t alignment, size_t size) {
+  void* address = SbMemoryAllocateAlignedUnchecked(alignment, size);
+  SbAbortIfAllocationFailed(size, address);
+  return address;
+}
+
+void* SbMemoryAllocateChecked(size_t size) {
+  void* address = SbMemoryAllocateUnchecked(size);
+  SbAbortIfAllocationFailed(size, address);
+  return address;
+}
+
+inline void* SbMemoryAllocateImpl(size_t size) {
+#if SB_ABORT_ON_ALLOCATION_FAILURE
+  return SbMemoryAllocateChecked(size);
+#else
+  return SbMemoryAllocateUnchecked(size);
+#endif
+}
+
+inline void* SbMemoryAllocateAlignedImpl(size_t alignment, size_t size) {
+#if SB_ABORT_ON_ALLOCATION_FAILURE
+  return SbMemoryAllocateAlignedChecked(alignment, size);
+#else
+  return SbMemoryAllocateAlignedUnchecked(alignment, size);
+#endif
+}
+
+inline void* SbMemoryReallocateImpl(void* memory, size_t size) {
+#if SB_ABORT_ON_ALLOCATION_FAILURE
+  return SbMemoryReallocateChecked(memory, size);
+#else
+  return SbMemoryReallocateUnchecked(memory, size);
+#endif
+}
diff --git a/src/starboard/common/scoped_ptr.h b/src/starboard/common/scoped_ptr.h
index a762268..b62f1c6 100644
--- a/src/starboard/common/scoped_ptr.h
+++ b/src/starboard/common/scoped_ptr.h
@@ -362,7 +362,7 @@
 // passed as a template argument to scoped_ptr_malloc below.
 class ScopedPtrMallocFree {
  public:
-  inline void operator()(void* x) const { SbMemoryFree(x); }
+  inline void operator()(void* x) const { SbMemoryDeallocate(x); }
 };
 
 // scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index e075b47..1334ac8 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -186,6 +186,26 @@
 #endif  // SB_IS(COMPILER_MSVC)
 #endif  // !defined(SB_UNLIKELY)
 
+// SB_DEPRECATED(int Foo(int bar));
+//   Annotates the function as deprecated, which will trigger a compiler
+//   warning when referenced.
+#if SB_IS(COMPILER_GCC)
+#define SB_DEPRECATED(FUNC) FUNC __attribute__((deprecated))
+#elif SB_IS(COMPILER_MSVC)
+#define SB_DEPRECATED(FUNC) __declspec(deprecated) FUNC
+#else
+// Empty definition for other compilers.
+#define SB_DEPRECATED(FUNC) FUNC
+#endif
+
+// SB_DEPRECATED_EXTERNAL(...) annotates the function as deprecated for
+// external clients, but not deprecated for starboard.
+#if defined(STARBOARD_IMPLEMENTATION)
+#define SB_DEPRECATED_EXTERNAL(FUNC) FUNC
+#else
+#define SB_DEPRECATED_EXTERNAL(FUNC) SB_DEPRECATED(FUNC)
+#endif
+
 // A macro to disallow the copy constructor and operator= functions
 // This should be used in the private: declarations for a class
 #define SB_DISALLOW_COPY_AND_ASSIGN(TypeName) \
@@ -368,6 +388,20 @@
 #error "Your platform must define SB_HAS_THREAD_PRIORITY_SUPPORT."
 #endif
 
+#if SB_HAS(THREAD_PRIORITY_SUPPORT)
+
+#if !defined(SB_HAS_REAL_TIME_PRIORITY_SUPPORT)
+#error "Your platform must define SB_HAS_REAL_TIME_PRIORITY_SUPPORT."
+#endif
+
+#else  // SB_HAS(THREAD_PRIORITY_SUPPORT)
+
+#if defined(SB_HAS_REAL_TIME_PRIORITY_SUPPORT)
+#error "Don't define SB_HAS_REAL_TIME_PRIORITY_SUPPORT without priorities."
+#endif
+
+#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
+
 #if !defined(SB_PREFERRED_RGBA_BYTE_ORDER)
 // Legal values for SB_PREFERRED_RGBA_BYTE_ORDER are defined in this file above
 // as SB_PREFERRED_RGBA_BYTE_ORDER_*.
diff --git a/src/starboard/examples/glclear/main.cc b/src/starboard/examples/glclear/main.cc
index 7d520ec..129c06c 100644
--- a/src/starboard/examples/glclear/main.cc
+++ b/src/starboard/examples/glclear/main.cc
@@ -125,7 +125,7 @@
   }
   SB_DCHECK(surface_ != EGL_NO_SURFACE);
 
-  SbMemoryFree(configs);
+  SbMemoryDeallocate(configs);
 
   eglQuerySurface(display_, surface_, EGL_WIDTH, &egl_surface_width_);
   eglQuerySurface(display_, surface_, EGL_HEIGHT, &egl_surface_height_);
diff --git a/src/starboard/memory.h b/src/starboard/memory.h
index f418c30..f48b44e 100644
--- a/src/starboard/memory.h
+++ b/src/starboard/memory.h
@@ -11,12 +11,18 @@
 // 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.
-
+//
 // Memory allocation, alignment, copying, and comparing.
+//
+// DO NOT CALL SbMemoryAllocateUnchecked(), SbMemoryAllocateChecked()
+// SbMemoryReallocateUnchecked(), SbMemoryReallocateChecked(),
+// SbMemoryAllocateAlignedUnchecked(), SbMemoryAllocateAlignedChecked(),
+// SbMemoryFree(), SbMemoryFreeAligned(). They are internal.
 
 #ifndef STARBOARD_MEMORY_H_
 #define STARBOARD_MEMORY_H_
 
+#include "starboard/configuration.h"
 #include "starboard/export.h"
 #include "starboard/system.h"
 #include "starboard/types.h"
@@ -27,16 +33,6 @@
 
 #define SB_MEMORY_MAP_FAILED ((void*)-1)  // NOLINT(readability/casting)
 
-#if defined(SB_ABORT_ON_ALLOCATION_FAILURE)
-#define SbMemoryAllocate SbMemoryAllocateChecked
-#define SbMemoryReallocate SbMemoryReallocateChecked
-#define SbMemoryAllocateAligned SbMemoryAllocateAlignedChecked
-#else
-#define SbMemoryAllocate SbMemoryAllocateUnchecked
-#define SbMemoryReallocate SbMemoryReallocateUnchecked
-#define SbMemoryAllocateAligned SbMemoryAllocateAlignedUnchecked
-#endif
-
 // The bitwise OR of these flags should be passed to SbMemoryMap to indicate
 // how the mapped memory can be used.
 typedef enum SbMemoryMapFlags {
@@ -71,16 +67,10 @@
 // Allocates a chunk of memory of at least |size| bytes, returning it. If unable
 // to allocate the memory, it returns NULL. If |size| is 0, it may return NULL
 // or it may return a unique pointer value that can be passed to
-// SbMemoryFree. Meant to be a drop-in replacement for malloc.
-SB_EXPORT void* SbMemoryAllocateUnchecked(size_t size);
-
-// Same as SbMemoryAllocateUnchecked, but will abort() in the case of an
-// allocation failure.
-static SB_C_FORCE_INLINE void* SbMemoryAllocateChecked(size_t size) {
-  void* address = SbMemoryAllocateUnchecked(size);
-  SbAbortIfAllocationFailed(size, address);
-  return address;
-}
+// SbMemoryDeallocate. Meant to be a drop-in replacement for malloc.
+//
+// This memory function should be called from the Cobalt codebase.
+SB_EXPORT void* SbMemoryAllocate(size_t size);
 
 // Attempts to resize |memory| to be at least |size| bytes, without touching the
 // contents. If it cannot perform the fast resize, it will allocate a new chunk
@@ -89,42 +79,72 @@
 // return NULL, leaving the given memory chunk unchanged. |memory| may be NULL,
 // in which case it behaves exactly like SbMemoryAllocateUnchecked.  If |size|
 // is 0, it may return NULL or it may return a unique pointer value that can be
-// passed to SbMemoryFree. Meant to be a drop-in replacement for realloc.
-SB_EXPORT void* SbMemoryReallocateUnchecked(void* memory, size_t size);
+// passed to SbMemoryDeallocate. Meant to be a drop-in replacement for realloc.
+//
+// This memory function should be called from the Cobalt codebase.
+SB_EXPORT void* SbMemoryReallocate(void* memory, size_t size);
 
-// Same as SbMemoryReallocateUnchecked, but will abort() in the case of an
-// allocation failure
-static SB_C_FORCE_INLINE void* SbMemoryReallocateChecked(void* memory,
-                                                         size_t size) {
-  void* address = SbMemoryReallocateUnchecked(memory, size);
-  SbAbortIfAllocationFailed(size, address);
-  return address;
-}
+// Allocates a chunk of memory of at least |size| bytes, aligned to
+// |alignment|, returning it. |alignment| must be a power of two, otherwise
+// behavior is undefined. If unable to allocate the memory, it returns NULL.
+// If |size| is 0, it may return NULL or it may return a unique aligned pointer
+// value that can be passed to SbMemoryDeallocateAligned. Meant to be a drop-in
+// replacement for memalign.
+//
+// This memory function should be called from the Cobalt codebase.
+SB_EXPORT void* SbMemoryAllocateAligned(size_t alignment, size_t size);
 
 // Frees a previously allocated chunk of memory. If |memory| is NULL, then it
 // does nothing.  Meant to be a drop-in replacement for free.
-SB_EXPORT void SbMemoryFree(void* memory);
-
-// Allocates a chunk of memory of at least |size| bytes, aligned to |alignment|,
-// returning it. |alignment| must be a power of two, otherwise behavior is
-// undefined. If unable to allocate the memory, it returns NULL. If |size| is 0,
-// it may return NULL or it may return a unique aligned pointer value that can
-// be passed to SbMemoryFreeAligned. Meant to be a drop-in replacement for
-// memalign.
-SB_EXPORT void* SbMemoryAllocateAlignedUnchecked(size_t alignment, size_t size);
-
-// Same as SbMemoryAllocateAlignedUnchecked, but will abort() in the case of an
-// allocation failure
-static SB_C_FORCE_INLINE void* SbMemoryAllocateAlignedChecked(size_t alignment,
-                                                              size_t size) {
-  void* address = SbMemoryAllocateAlignedUnchecked(alignment, size);
-  SbAbortIfAllocationFailed(size, address);
-  return address;
-}
+//
+// This memory function should be called from the Cobalt codebase.
+SB_EXPORT void SbMemoryDeallocate(void* memory);
 
 // Frees a previously allocated chunk of aligned memory. If |memory| is NULL,
 // then it does nothing.  Meant to be a drop-in replacement for _aligned_free.
-SB_EXPORT void SbMemoryFreeAligned(void* memory);
+//
+// This memory function should be called from the Cobalt codebase.
+SB_EXPORT void SbMemoryDeallocateAligned(void* memory);
+
+/////////////////////////////////////////////////////////////////
+// The following functions must be provided by Starboard ports.
+/////////////////////////////////////////////////////////////////
+
+// This is the implementation of SbMemoryAllocate that must be
+// provided by Starboard ports.
+//
+// DO NOT CALL. Call SbMemoryAllocate(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void* SbMemoryAllocateUnchecked(size_t size));
+
+// This is the implementation of SbMemoryReallocate that must be
+// provided by Starboard ports.
+//
+// DO NOT CALL. Call SbMemoryReallocate(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void* SbMemoryReallocateUnchecked(void* memory, size_t size));
+
+// This is the implementation of SbMemoryAllocateAligned that must be
+// provided by Starboard ports.
+//
+// DO NOT CALL. Call SbMemoryAllocateAligned(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void* SbMemoryAllocateAlignedUnchecked(size_t alignment,
+                                                     size_t size));
+
+// This is the implementation of SbMemoryDeallocate that must be provided by
+// Starboard ports.
+//
+// DO NOT CALL. Call SbMemoryDeallocate(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void SbMemoryFree(void* memory));
+
+// This is the implementation of SbMemoryFreeAligned that must be provided by
+// Starboard ports.
+//
+// DO NOT CALL. Call SbMemoryDeallocateAligned(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void SbMemoryFreeAligned(void* memory));
 
 #if SB_HAS(MMAP)
 // Allocates |size_bytes| worth of physical memory pages and maps them into an
@@ -206,6 +226,32 @@
   return result;
 }
 
+/////////////////////////////////////////////////////////////////
+// Deprecated. Do not use.
+/////////////////////////////////////////////////////////////////
+
+// Same as SbMemoryAllocateUnchecked, but will abort() in the case of an
+// allocation failure.
+//
+// DO NOT CALL. Call SbMemoryAllocate(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void* SbMemoryAllocateChecked(size_t size));
+
+// Same as SbMemoryReallocateUnchecked, but will abort() in the case of an
+// allocation failure.
+//
+// DO NOT CALL. Call SbMemoryReallocate(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void* SbMemoryReallocateChecked(void* memory, size_t size));
+
+// Same as SbMemoryAllocateAlignedUnchecked, but will abort() in the case of an
+// allocation failure.
+//
+// DO NOT CALL. Call SbMemoryAllocateAligned(...) instead.
+SB_DEPRECATED_EXTERNAL(
+    SB_EXPORT void* SbMemoryAllocateAlignedChecked(size_t alignment,
+                                                   size_t size));
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/starboard/nplb/memory_allocate_aligned_test.cc b/src/starboard/nplb/memory_allocate_aligned_test.cc
index 72cb82b..dbdc217 100644
--- a/src/starboard/nplb/memory_allocate_aligned_test.cc
+++ b/src/starboard/nplb/memory_allocate_aligned_test.cc
@@ -29,7 +29,7 @@
     void* memory = SbMemoryAllocateAligned(align, kSize);
     ASSERT_NE(static_cast<void*>(NULL), memory);
     EXPECT_TRUE(SbMemoryIsAligned(memory, align));
-    SbMemoryFreeAligned(memory);
+    SbMemoryDeallocateAligned(memory);
   }
 }
 
@@ -43,7 +43,7 @@
     if (memory) {
       EXPECT_TRUE(SbMemoryIsAligned(memory, align));
     }
-    SbMemoryFreeAligned(memory);
+    SbMemoryDeallocateAligned(memory);
   }
 }
 
@@ -60,7 +60,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i));
   }
 
-  SbMemoryFreeAligned(memory);
+  SbMemoryDeallocateAligned(memory);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_allocate_test.cc b/src/starboard/nplb/memory_allocate_test.cc
index 1da3ae6..87e252c 100644
--- a/src/starboard/nplb/memory_allocate_test.cc
+++ b/src/starboard/nplb/memory_allocate_test.cc
@@ -24,20 +24,20 @@
 TEST(SbMemoryAllocateTest, AllocatesNormally) {
   void* memory = SbMemoryAllocate(kSize);
   EXPECT_NE(static_cast<void*>(NULL), memory);
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryAllocateTest, AllocatesZero) {
   void* memory = SbMemoryAllocate(0);
   // We can't expect anything here because some implementations may return an
   // allocated zero-size memory block, and some implementations may return NULL.
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryAllocateTest, AllocatesOne) {
   void* memory = SbMemoryAllocate(1);
   EXPECT_NE(static_cast<void*>(NULL), memory);
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryAllocateTest, CanReadWriteToResult) {
@@ -52,7 +52,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_compare_test.cc b/src/starboard/nplb/memory_compare_test.cc
index f0d07c1..2057df7 100644
--- a/src/starboard/nplb/memory_compare_test.cc
+++ b/src/starboard/nplb/memory_compare_test.cc
@@ -36,8 +36,8 @@
   int result = SbMemoryCompare(memory1, memory2, kSize);
   EXPECT_EQ(0, result);
 
-  SbMemoryFree(memory1);
-  SbMemoryFree(memory2);
+  SbMemoryDeallocate(memory1);
+  SbMemoryDeallocate(memory2);
 }
 
 TEST(SbMemoryCompareTest, SunnyDayLessAndMore) {
@@ -62,8 +62,8 @@
   result = SbMemoryCompare(memory2, memory1, kSize);
   EXPECT_GT(0, result);
 
-  SbMemoryFree(memory1);
-  SbMemoryFree(memory2);
+  SbMemoryDeallocate(memory1);
+  SbMemoryDeallocate(memory2);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_copy_test.cc b/src/starboard/nplb/memory_copy_test.cc
index 1142615..f8d3342 100644
--- a/src/starboard/nplb/memory_copy_test.cc
+++ b/src/starboard/nplb/memory_copy_test.cc
@@ -41,8 +41,8 @@
     EXPECT_EQ(data2[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory1);
-  SbMemoryFree(memory2);
+  SbMemoryDeallocate(memory1);
+  SbMemoryDeallocate(memory2);
 }
 
 TEST(SbMemoryCopyTest, CopiesZeroData) {
@@ -65,8 +65,8 @@
     EXPECT_EQ(data2[i], static_cast<char>(0));
   }
 
-  SbMemoryFree(memory1);
-  SbMemoryFree(memory2);
+  SbMemoryDeallocate(memory1);
+  SbMemoryDeallocate(memory2);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_free_aligned_test.cc b/src/starboard/nplb/memory_deallocate_aligned_test.cc
similarity index 84%
rename from src/starboard/nplb/memory_free_aligned_test.cc
rename to src/starboard/nplb/memory_deallocate_aligned_test.cc
index 5fbc8da..39cea85 100644
--- a/src/starboard/nplb/memory_free_aligned_test.cc
+++ b/src/starboard/nplb/memory_deallocate_aligned_test.cc
@@ -21,16 +21,16 @@
 
 const size_t kSize = 1024 * 128;
 
-TEST(SbMemoryFreeAlignedTest, FreesAligned) {
+TEST(SbMemoryDeallocateAlignedTest, DeallocatesAligned) {
   const size_t kMaxAlign = 4096 + 1;
   for (size_t align = 2; align < kMaxAlign; align <<= 1) {
     void* memory = SbMemoryAllocateAligned(align, kSize);
-    SbMemoryFreeAligned(memory);
+    SbMemoryDeallocateAligned(memory);
   }
 }
 
-TEST(SbMemoryFreeAlignedTest, FreesNull) {
-  SbMemoryFreeAligned(NULL);
+TEST(SbMemoryDeallocateAlignedTest, FreesNull) {
+  SbMemoryDeallocateAligned(NULL);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_free_test.cc b/src/starboard/nplb/memory_deallocate_test.cc
similarity index 86%
rename from src/starboard/nplb/memory_free_test.cc
rename to src/starboard/nplb/memory_deallocate_test.cc
index 3e25330..9bd8739 100644
--- a/src/starboard/nplb/memory_free_test.cc
+++ b/src/starboard/nplb/memory_deallocate_test.cc
@@ -21,14 +21,14 @@
 
 const size_t kSize = 1024 * 128;
 
-TEST(SbMemoryFreeTest, FreesNormally) {
+TEST(SbMemoryDeallocateTest, FreesNormally) {
   void* memory = SbMemoryAllocate(kSize);
   EXPECT_NE(static_cast<void*>(NULL), memory);
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
-TEST(SbMemoryFreeTest, FreesNull) {
-  SbMemoryFree(NULL);
+TEST(SbMemoryDeallocateTest, FreesNull) {
+  SbMemoryDeallocate(NULL);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_move_test.cc b/src/starboard/nplb/memory_move_test.cc
index 590a51e..c4b0e39 100644
--- a/src/starboard/nplb/memory_move_test.cc
+++ b/src/starboard/nplb/memory_move_test.cc
@@ -41,8 +41,8 @@
     EXPECT_EQ(data2[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory1);
-  SbMemoryFree(memory2);
+  SbMemoryDeallocate(memory1);
+  SbMemoryDeallocate(memory2);
 }
 
 TEST(SbMemoryMoveTest, MovesZeroData) {
@@ -65,8 +65,8 @@
     EXPECT_EQ(data2[i], static_cast<char>(i + 1));
   }
 
-  SbMemoryFree(memory1);
-  SbMemoryFree(memory2);
+  SbMemoryDeallocate(memory1);
+  SbMemoryDeallocate(memory2);
 }
 
 TEST(SbMemoryMoveTest, MovesOverlappingDataForward) {
@@ -84,7 +84,7 @@
     EXPECT_EQ(data[i + 3], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryMoveTest, MovesOverlappingDataBackwards) {
@@ -102,7 +102,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i + 3));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_reallocate_test.cc b/src/starboard/nplb/memory_reallocate_test.cc
index 72d0036..7ca252c 100644
--- a/src/starboard/nplb/memory_reallocate_test.cc
+++ b/src/starboard/nplb/memory_reallocate_test.cc
@@ -24,20 +24,20 @@
 TEST(SbMemoryReallocateTest, AllocatesNormally) {
   void* memory = SbMemoryReallocate(NULL, kSize);
   EXPECT_NE(static_cast<void*>(NULL), memory);
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, AllocatesZero) {
   void* memory = SbMemoryReallocate(NULL, 0);
   // We can't expect anything here because some implementations may return an
   // allocated zero-size memory block, and some implementations may return NULL.
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, AllocatesOne) {
   void* memory = SbMemoryReallocate(NULL, 1);
   EXPECT_NE(static_cast<void*>(NULL), memory);
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, CanReadWriteToResult) {
@@ -52,7 +52,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, ReallocatesSmaller) {
@@ -74,7 +74,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, ReallocatesBigger) {
@@ -104,7 +104,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, ReallocatestoZero) {
@@ -112,7 +112,7 @@
   ASSERT_NE(static_cast<void*>(NULL), memory);
   memory = SbMemoryReallocate(memory, 0);
   // See allocates zero above.
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemoryReallocateTest, ReallocatestoSameSize) {
@@ -134,7 +134,7 @@
     EXPECT_EQ(data[i], static_cast<char>(i));
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/memory_set_test.cc b/src/starboard/nplb/memory_set_test.cc
index 0874104..a086170 100644
--- a/src/starboard/nplb/memory_set_test.cc
+++ b/src/starboard/nplb/memory_set_test.cc
@@ -34,7 +34,7 @@
     ASSERT_EQ('\xCD', data[i]);
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemorySetTest, SetsZeroData) {
@@ -50,7 +50,7 @@
     ASSERT_EQ(static_cast<char>(i), data[i]);
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 TEST(SbMemorySetTest, IgnoresExtraData) {
@@ -66,7 +66,7 @@
     ASSERT_EQ('\xCD', data[i]);
   }
 
-  SbMemoryFree(memory);
+  SbMemoryDeallocate(memory);
 }
 
 }  // namespace
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 1938621..da63d88 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -114,8 +114,8 @@
         'memory_compare_test.cc',
         'memory_copy_test.cc',
         'memory_find_byte_test.cc',
-        'memory_free_aligned_test.cc',
-        'memory_free_test.cc',
+        'memory_deallocate_aligned_test.cc',
+        'memory_deallocate_test.cc',
         'memory_get_stack_bounds_test.cc',
         'memory_map_test.cc',
         'memory_move_test.cc',
diff --git a/src/starboard/nplb/socket_send_to_test.cc b/src/starboard/nplb/socket_send_to_test.cc
index b7b2f06..9db1d36 100644
--- a/src/starboard/nplb/socket_send_to_test.cc
+++ b/src/starboard/nplb/socket_send_to_test.cc
@@ -15,19 +15,78 @@
 // SendTo is largely tested with ReceiveFrom, so look there for more invovled
 // tests.
 
+#include "starboard/memory.h"
+#include "starboard/nplb/socket_helpers.h"
 #include "starboard/socket.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace starboard {
 namespace nplb {
 namespace {
 
+// Thread entry point to continuously write to a socket that is expected to
+// be closed on another thread.
+void* SendToServerSocketEntryPoint(void* trio_as_void_ptr) {
+  ConnectedTrio* trio = static_cast<ConnectedTrio*>(trio_as_void_ptr);
+  // The contents of this buffer are inconsequential.
+  const size_t kBufSize = 1024;
+  char* send_buf = new char[kBufSize];
+  SbMemorySet(send_buf, 0, kBufSize);
+
+  // Continue sending to the socket until it fails to send. It's expected that
+  // SbSocketSendTo will fail when the server socket closes, but the application
+  // should
+  // not terminate.
+  SbTime start = SbTimeGetNow();
+  SbTime now = start;
+  SbTime kTimeout = kSbTimeSecond;
+  int result = 0;
+  while (result >= 0 && (now - start < kTimeout)) {
+    result = SbSocketSendTo(trio->server_socket, send_buf, kBufSize, NULL);
+    now = SbTimeGetNow();
+  }
+
+  delete[] send_buf;
+  return NULL;
+}
+
 TEST(SbSocketSendToTest, RainyDayInvalidSocket) {
   char buf[16];
   int result = SbSocketSendTo(NULL, buf, sizeof(buf), NULL);
   EXPECT_EQ(-1, result);
 }
 
+TEST(SbSocketSendToTest, RainyDaySendToClosedSocket) {
+  ConnectedTrio trio =
+      CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
+  EXPECT_NE(trio.client_socket, kSbSocketInvalid);
+  EXPECT_NE(trio.server_socket, kSbSocketInvalid);
+  EXPECT_NE(trio.listen_socket, kSbSocketInvalid);
+
+  // We don't need the listen socket, so close it.
+  EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
+
+  // Start a thread to write to the client socket.
+  const bool kJoinable = true;
+  SbThread send_thread = SbThreadCreate(
+      0, kSbThreadNoPriority, kSbThreadNoAffinity, kJoinable, "SendToTest",
+      SendToServerSocketEntryPoint, static_cast<void*>(&trio));
+
+  // Close the client, which should cause writes to the server socket to fail.
+  EXPECT_TRUE(SbSocketDestroy(trio.client_socket));
+
+  // Wait for the thread to exit and check the last socket error.
+  void* thread_result;
+  EXPECT_TRUE(SbThreadJoin(send_thread, &thread_result));
+  // Check that the server_socket failed, as expected.
+  EXPECT_EQ(SbSocketGetLastError(trio.server_socket), kSbSocketErrorFailed);
+
+  // Clean up the server socket.
+  EXPECT_TRUE(SbSocketDestroy(trio.server_socket));
+}
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/string_duplicate_test.cc b/src/starboard/nplb/string_duplicate_test.cc
index d61e0c2..17b794b 100644
--- a/src/starboard/nplb/string_duplicate_test.cc
+++ b/src/starboard/nplb/string_duplicate_test.cc
@@ -26,7 +26,7 @@
   EXPECT_NE(kNull, dupe);
   EXPECT_EQ(0, SbStringCompareNoCase(input, dupe));
   EXPECT_EQ(SbStringGetLength(input), SbStringGetLength(dupe));
-  SbMemoryFree(dupe);
+  SbMemoryDeallocate(dupe);
 }
 
 TEST(SbStringDuplicateTest, SunnyDay) {
diff --git a/src/starboard/raspi/1/gyp_configuration.gypi b/src/starboard/raspi/1/gyp_configuration.gypi
index 12a59f3..1b41d36 100644
--- a/src/starboard/raspi/1/gyp_configuration.gypi
+++ b/src/starboard/raspi/1/gyp_configuration.gypi
@@ -32,7 +32,8 @@
 
     # This should have a default value in cobalt/base.gypi. See the comment
     # there for acceptable values for this variable.
-    'javascript_engine': 'javascriptcore',
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 1,
 
     # RasPi 1 is ARMv6
     'arm_version': 6,
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index d60f480..0d04361 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -82,7 +82,7 @@
 }
 
 void ReleaseBuffer(AVCodecContext*, AVFrame* frame) {
-  SbMemoryFree(frame->opaque);
+  SbMemoryDeallocate(frame->opaque);
   frame->opaque = NULL;
 
   // The FFmpeg API expects us to zero the data pointers in this callback.
diff --git a/src/starboard/shared/posix/socket_receive_from.cc b/src/starboard/shared/posix/socket_receive_from.cc
index 6acea4c..c4819b6 100644
--- a/src/starboard/shared/posix/socket_receive_from.cc
+++ b/src/starboard/shared/posix/socket_receive_from.cc
@@ -27,6 +27,12 @@
                         char* out_data,
                         int data_size,
                         SbSocketAddress* out_source) {
+#if defined(MSG_NOSIGNAL)
+  const int kRecvFlags = MSG_NOSIGNAL;
+#else
+  const int kRecvFlags = 0;
+#endif
+
   if (!SbSocketIsValid(socket)) {
     errno = EBADF;
     return -1;
@@ -52,7 +58,8 @@
       }
     }
 
-    ssize_t bytes_read = recv(socket->socket_fd, out_data, data_size, 0);
+    ssize_t bytes_read =
+        recv(socket->socket_fd, out_data, data_size, kRecvFlags);
     if (bytes_read >= 0) {
       socket->error = kSbSocketOk;
       return static_cast<int>(bytes_read);
@@ -66,8 +73,9 @@
     return -1;
   } else if (socket->protocol == kSbSocketProtocolUdp) {
     sbposix::SockAddr sock_addr;
-    ssize_t bytes_read = recvfrom(socket->socket_fd, out_data, data_size, 0,
-                                  sock_addr.sockaddr(), &sock_addr.length);
+    ssize_t bytes_read =
+        recvfrom(socket->socket_fd, out_data, data_size, kRecvFlags,
+                 sock_addr.sockaddr(), &sock_addr.length);
 
     if (bytes_read >= 0) {
       if (out_source) {
diff --git a/src/starboard/shared/posix/socket_send_to.cc b/src/starboard/shared/posix/socket_send_to.cc
index 23e134d..6cd0c70 100644
--- a/src/starboard/shared/posix/socket_send_to.cc
+++ b/src/starboard/shared/posix/socket_send_to.cc
@@ -26,6 +26,11 @@
                    const char* data,
                    int data_size,
                    const SbSocketAddress* destination) {
+#if defined(MSG_NOSIGNAL)
+  const int kSendFlags = MSG_NOSIGNAL;
+#else
+  const int kSendFlags = 0;
+#endif
   if (!SbSocketIsValid(socket)) {
     errno = EBADF;
     return -1;
@@ -39,7 +44,8 @@
       return -1;
     }
 
-    ssize_t bytes_written = send(socket->socket_fd, data, data_size, 0);
+    ssize_t bytes_written =
+        send(socket->socket_fd, data, data_size, kSendFlags);
     if (bytes_written >= 0) {
       socket->error = kSbSocketOk;
       return static_cast<int>(bytes_written);
@@ -70,8 +76,8 @@
       sockaddr_length = sock_addr.length;
     }
 
-    ssize_t bytes_written = sendto(socket->socket_fd, data, data_size, 0,
-                                   sockaddr, sockaddr_length);
+    ssize_t bytes_written = sendto(socket->socket_fd, data, data_size,
+                                   kSendFlags, sockaddr, sockaddr_length);
     if (bytes_written >= 0) {
       socket->error = kSbSocketOk;
       return static_cast<int>(bytes_written);
diff --git a/src/starboard/shared/pthread/thread_create.cc b/src/starboard/shared/pthread/thread_create.cc
index 1971d0d..8718ba4 100644
--- a/src/starboard/shared/pthread/thread_create.cc
+++ b/src/starboard/shared/pthread/thread_create.cc
@@ -16,23 +16,27 @@
 
 #include <pthread.h>
 #include <sched.h>
+#include <sys/resource.h>
 #include <unistd.h>
 
 #include "starboard/log.h"
 #include "starboard/shared/pthread/is_success.h"
 #include "starboard/string.h"
 
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
+#if SB_HAS(THREAD_PRIORITY_SUPPORT) && SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
 #if !defined(_POSIX_PRIORITY_SCHEDULING)
 #error "The _POSIX_PRIORITY_SCHEDULING define indicates that a pthreads \
 system supports thread priorities, however this define is not \
 defined on this system, contradicting the Starboard configuration \
 indicating that priority scheduling is supported."
 #endif  // !defined(_POSIX_PRIORITY_SCHEDULING)
-#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
+#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT) && SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
 
 namespace {
 
+#if SB_HAS(THREAD_PRIORITY_SUPPORT)
+#if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+
 int SbThreadPriorityToPthread(SbThreadPriority priority) {
   switch (priority) {
     case kSbThreadPriorityLowest:
@@ -52,14 +56,39 @@
   }
 }
 
+#else  // SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+
+int SbThreadPriorityToNice(SbThreadPriority priority) {
+  switch (priority) {
+    case kSbThreadPriorityLowest:
+      return 10;
+    case kSbThreadPriorityLow:
+      return 5;
+    case kSbThreadNoPriority:
+    // Fall through on purpose to default to kThreadPriority_Normal.
+    case kSbThreadPriorityNormal:
+      return -5;
+    case kSbThreadPriorityHigh:
+      return -15;
+    case kSbThreadPriorityHighest:
+      return -18;
+    case kSbThreadPriorityRealTime:
+      return -19;
+    default:
+      SB_NOTREACHED();
+      return 0;
+  }
+}
+
+#endif  // #if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+#endif  // #if SB_HAS(THREAD_PRIORITY_SUPPORT)
+
 struct ThreadParams {
   SbThreadAffinity affinity;
   SbThreadEntryPoint entry_point;
   char name[128];
   void* context;
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
   SbThreadPriority priority;
-#endif  // #if SB_HAS(THREAD_PRIORITY_SUPPORT)
 };
 
 void* ThreadFunc(void* context) {
@@ -72,6 +101,7 @@
   }
 
 #if SB_HAS(THREAD_PRIORITY_SUPPORT)
+#if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
   // Use Linux' regular scheduler for lowest priority threads.  Real-time
   // priority threads (of any priority) will always have priority over
   // non-real-time threads (e.g. threads whose scheduler is setup to be
@@ -84,7 +114,12 @@
         SbThreadPriorityToPthread(thread_params->priority);
     sched_setscheduler(0, SCHED_FIFO, &thread_sched_param);
   }
-#endif  // #if SB_HAS(THREAD_PRIORITY_SUPPORT)
+#else   // #if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+  // If we don't have real time thread priority support, then set the nice
+  // value instead for soft priority support.
+  setpriority(PRIO_PROCESS, 0, SbThreadPriorityToNice(thread_params->priority));
+#endif  // SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+#endif  // SB_HAS(THREAD_PRIORITY_SUPPORT)
 
   delete thread_params;
 
@@ -143,9 +178,7 @@
     params->name[0] = '\0';
   }
 
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
   params->priority = priority;
-#endif  // #if SB_HAS(THREAD_PRIORITY_SUPPORT)
 
   SbThread thread = kSbThreadInvalid;
   result = pthread_create(&thread, &attributes, ThreadFunc, params);
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index 26e08db..4498156 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -77,7 +77,7 @@
           reinterpret_cast<SbAtomicPtr>(NULL)));
   SB_DCHECK(old_instance);
   SB_DCHECK(old_instance == this);
-  SbMemoryFree(start_link_);
+  SbMemoryDeallocate(start_link_);
 }
 
 int Application::Run(int argc, char** argv) {
@@ -141,7 +141,7 @@
 #endif  // SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT)
 
 void Application::SetStartLink(const char* start_link) {
-  SbMemoryFree(start_link_);
+  SbMemoryDeallocate(start_link_);
   if (start_link) {
     start_link_ = SbStringDuplicate(start_link);
   } else {
diff --git a/src/starboard/shared/starboard/log_message.cc b/src/starboard/shared/starboard/log_message.cc
index 8e654b4..444bcd6 100644
--- a/src/starboard/shared/starboard/log_message.cc
+++ b/src/starboard/shared/starboard/log_message.cc
@@ -51,7 +51,11 @@
 }
 
 SbLogPriority GetMinLogLevel() {
+#if SB_LOGGING_IS_OFFICIAL_BUILD
+  return SB_LOG_FATAL;
+#else
   return g_min_log_level;
+#endif
 }
 
 void Break() {
diff --git a/src/starboard/shared/starboard/new.cc b/src/starboard/shared/starboard/new.cc
index bf4f97b..7e08ed0 100644
--- a/src/starboard/shared/starboard/new.cc
+++ b/src/starboard/shared/starboard/new.cc
@@ -22,7 +22,7 @@
 }
 
 void operator delete(void* pointer) {
-  SbMemoryFree(pointer);
+  SbMemoryDeallocate(pointer);
 }
 
 void* operator new[](size_t size) {
@@ -30,5 +30,5 @@
 }
 
 void operator delete[](void* pointer) {
-  SbMemoryFree(pointer);
+  SbMemoryDeallocate(pointer);
 }
diff --git a/src/starboard/string.h b/src/starboard/string.h
index 9b72dfa..5782023 100644
--- a/src/starboard/string.h
+++ b/src/starboard/string.h
@@ -77,7 +77,8 @@
                                  int destination_size);
 
 // Copies the string |source| into a buffer allocated by this function that can
-// be freed with SbMemoryFree. Meant to be a drop-in replacement for strdup.
+// be freed with SbMemoryDeallocate. Meant to be a drop-in replacement for
+// strdup.
 SB_EXPORT char* SbStringDuplicate(const char* source);
 
 // Finds the first occurrence of |character| in |str|, returning a pointer to
diff --git a/src/starboard/time_zone.h b/src/starboard/time_zone.h
index c194154..ce67d09 100644
--- a/src/starboard/time_zone.h
+++ b/src/starboard/time_zone.h
@@ -30,7 +30,7 @@
 // For example: PST/PDT is 480 minutes (28800 seconds, 8 hours).
 typedef int SbTimeZone;
 
-// Gets the system's current SbTimeZone.
+// Gets the system's current SbTimeZone in minutes.
 SB_EXPORT SbTimeZone SbTimeZoneGetCurrent();
 
 // Gets the three-letter code of the current timezone in standard time,
diff --git a/src/testing/gtest/src/gtest.cc b/src/testing/gtest/src/gtest.cc
index 430695d..837c26f 100644
--- a/src/testing/gtest/src/gtest.cc
+++ b/src/testing/gtest/src/gtest.cc
@@ -772,7 +772,9 @@
 
 // Returns the current time in milliseconds.
 TimeInMillis GetTimeInMillis() {
-#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) || defined(__LB_XB1__)
+#if GTEST_OS_STARBOARD
+  return SbTimeGetNow() / kSbTimeMillisecond;
+#elif GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) || defined(__LB_XB1__)
   // Difference between 1970-01-01 and 1601-01-01 in milliseconds.
   // http://analogous.blogspot.com/2005/04/epoch.html
   const TimeInMillis kJavaEpochToWinFileTimeDelta =
diff --git a/src/third_party/libevent/starboard/event-starboard-internal.h b/src/third_party/libevent/starboard/event-starboard-internal.h
index 98d02a1..33a0747 100644
--- a/src/third_party/libevent/starboard/event-starboard-internal.h
+++ b/src/third_party/libevent/starboard/event-starboard-internal.h
@@ -20,7 +20,7 @@
 #include "starboard/memory.h"
 
 #define calloc SbMemoryCalloc
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #define malloc SbMemoryAllocate
 #define realloc SbMemoryReallocate
 
diff --git a/src/third_party/libwebp/starboard_private.h b/src/third_party/libwebp/starboard_private.h
index 5733157..242ef00 100644
--- a/src/third_party/libwebp/starboard_private.h
+++ b/src/third_party/libwebp/starboard_private.h
@@ -29,7 +29,7 @@
 
 #define abs(x) webp_abs(x)
 #define calloc SbMemoryCalloc
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #define malloc SbMemoryAllocate
 #define memcpy SbMemoryCopy
 #define memmove SbMemoryMove
diff --git a/src/third_party/libxml/src/timsort.h b/src/third_party/libxml/src/timsort.h
index 2a8f236..101847b 100644
--- a/src/third_party/libxml/src/timsort.h
+++ b/src/third_party/libxml/src/timsort.h
@@ -22,7 +22,7 @@
 #include "starboard/system.h"
 #include "starboard/types.h"
 #define exit(x) SbSystemBreakIntoDebugger()
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
 #define memcpy SbMemoryCopy
 #define realloc SbMemoryReallocate
 #else
diff --git a/src/third_party/libxml/starboard/config.h b/src/third_party/libxml/starboard/config.h
index ff6d707..1f42d44 100644
--- a/src/third_party/libxml/starboard/config.h
+++ b/src/third_party/libxml/starboard/config.h
@@ -343,7 +343,7 @@
 #define XML_REALLOC SbMemoryReallocate
 
 /* free() wrapping */
-#define XML_FREE SbMemoryFree
+#define XML_FREE SbMemoryDeallocate
 
 /* memcmp() wrapping */
 #define XML_MEMCMP SbMemoryCompare
diff --git a/src/third_party/mozjs/js/src/TraceLogging.cpp b/src/third_party/mozjs/js/src/TraceLogging.cpp
index e82bde2..e1c346a 100644
--- a/src/third_party/mozjs/js/src/TraceLogging.cpp
+++ b/src/third_party/mozjs/js/src/TraceLogging.cpp
@@ -59,6 +59,12 @@
 
     return(result);
 }
+#else
+static __inline__ uint64_t
+rdtsc(void)
+{
+    return 0;
+}
 #endif
 
 const char* const TraceLogging::type_name[] = {
@@ -157,7 +163,7 @@
 void
 TraceLogging::log(Type type, JSScript* script)
 {
-    this->log(type, script->filename, script->lineno);
+    this->log(type, script->filename(), script->lineno);
 }
 
 void
diff --git a/src/third_party/mozjs/js/src/assembler/assembler/MIPSAssembler.h b/src/third_party/mozjs/js/src/assembler/assembler/MIPSAssembler.h
index 45619c3..3beacdc 100644
--- a/src/third_party/mozjs/js/src/assembler/assembler/MIPSAssembler.h
+++ b/src/third_party/mozjs/js/src/assembler/assembler/MIPSAssembler.h
@@ -31,11 +31,10 @@
 
 #if ENABLE(ASSEMBLER) && CPU(MIPS)
 
-#include "AssemblerBuffer.h"
+#include "assembler/assembler/AssemblerBuffer.h"
 #include "assembler/wtf/Assertions.h"
 #include "assembler/wtf/SegmentedVector.h"
 
-#include "methodjit/Logging.h"
 #define IPFX  "        %s"
 #define ISPFX "        "
 #ifdef JS_METHODJIT_SPEW
@@ -181,6 +180,10 @@
         {
         }
 
+        bool isSet() const {
+            return m_offset != -1;
+        };
+
     private:
         JmpSrc(int offset)
             : m_offset(offset)
diff --git a/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h b/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h
index 16159f3..b92cddc 100644
--- a/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h
+++ b/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerCodeRef.h
@@ -30,6 +30,7 @@
 #ifndef assembler_assembler_MacroAssemblerCodeRef_h
 #define assembler_assembler_MacroAssemblerCodeRef_h
 
+#include "assembler/wtf/Assertions.h"
 #include "assembler/wtf/Platform.h"
 #include "assembler/jit/ExecutableAllocator.h"
 
diff --git a/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h b/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h
index 6c6db42..50a18d1 100644
--- a/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h
+++ b/src/third_party/mozjs/js/src/assembler/assembler/MacroAssemblerMIPS.h
@@ -2335,6 +2335,7 @@
             m_assembler.lwc1(dest, addrTempRegister, address.offset);
             m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, address.offset + 4);
         } else {
+            /*
                 sll     addrTemp, address.index, address.scale
                 addu    addrTemp, addrTemp, address.base
                 li      immTemp, address.offset
@@ -2349,6 +2350,7 @@
                              immTempRegister);
             m_assembler.lwc1(dest, addrTempRegister, 0);
             m_assembler.lwc1(FPRegisterID(dest + 1), addrTempRegister, 4);
+        }
 #else
         if (address.offset >= -32768 && address.offset <= 32767
             && !m_fixedWidth) {
@@ -2781,6 +2783,13 @@
         MIPSAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
     }
 
+public:
+    void load16Unaligned(BaseIndex address, RegisterID dest) {
+        JS_NOT_REACHED("NYI_COBALT");
+    }
+    void load8(BaseIndex address, RegisterID dest) {
+        JS_NOT_REACHED("NYI_COBALT");
+    }
 };
 
 }
diff --git a/src/third_party/mozjs/js/src/jit/AsmJS.cpp b/src/third_party/mozjs/js/src/jit/AsmJS.cpp
index 60d15f2..774019e 100644
--- a/src/third_party/mozjs/js/src/jit/AsmJS.cpp
+++ b/src/third_party/mozjs/js/src/jit/AsmJS.cpp
@@ -588,7 +588,6 @@
 // outer parent +/- expression pass in Use::AddOrSub so that the inner
 // expression knows to return type Int instead of Intish.
 //
-// TODO: remove the remaining use of Use
 class Use
 {
   public:
@@ -1527,13 +1526,13 @@
         JS_ASSERT(elemIndex == module_->numFuncPtrTableElems());
 
         // Global accesses in function bodies
-#ifdef JS_CPU_ARM
+#if defined(JS_CPU_ARM)
         JS_ASSERT(globalAccesses_.length() == 0);
         // The AsmJSHeapAccess offsets need to be updated to reflect the
         // "actualOffset" (an ARM distinction).
         module_->convertBoundsChecksToActualOffset(masm_);
 
-#else
+#elif defined(JS_CPU_X86) || defined(JS_CPU_X64)
 
         for (unsigned i = 0; i < globalAccesses_.length(); i++) {
             AsmJSGlobalAccess access = globalAccesses_[i];
@@ -5028,7 +5027,7 @@
 
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * STACK_SLOT_SIZE +
                                              NonVolatileRegs.fpus().size() * sizeof(double);
-#ifndef JS_CPU_ARM
+#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
 static bool
 GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc)
 {
@@ -5128,7 +5127,7 @@
     masm.ret();
     return true;
 }
-#else
+#elif defined(JS_CPU_ARM)  // defined(JS_CPU_X86) || defined(JS_CPU_X64
 static bool
 GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc)
 {
@@ -5241,6 +5240,105 @@
     masm.abiret();
     return true;
 }
+#elif defined(JS_CPU_MIPS)  // defined(JS_CPU_X86) || defined(JS_CPU_X64
+static bool
+GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc)
+{
+    MacroAssembler &masm = m.masm();
+
+    // In constrast to the system ABI, the Ion convention is that all registers
+    // are clobbered by calls. Thus, we must save the caller's non-volatile
+    // registers.
+    //
+    // NB: GenerateExits assumes that masm.framePushed() == 0 before
+    // PushRegsInMask(NonVolatileRegs).
+    masm.setFramePushed(0);
+    masm.PushRegsInMask(NonVolatileRegs);
+    JS_ASSERT(masm.framePushed() == FramePushedAfterSave);
+
+    // Remember the stack pointer in the current AsmJSActivation. This will be
+    // used by error exit paths to set the stack pointer back to what it was
+    // right after the (C++) caller's non-volatile registers were saved so that
+    // they can be restored.
+    Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
+    LoadAsmJSActivationIntoRegister(masm, activation);
+    masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()));
+
+    // Get 'argv' into a non-arg register and save it on the stack.
+    Register argv = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
+    masm.movePtr(IntArgReg0, argv);
+    masm.Push(argv);
+
+    // Bump the stack for the call.
+    // const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name());
+    // unsigned stackDec = StackDecrementForCall(masm, func.sig().args());
+    // masm.reserveStack(stackDec);
+    // CAREFUL_COBALT
+    // Bump the stack for the call.
+    const ModuleCompiler::Func &func = *m.lookupFunction(exportedFunc.name());
+    unsigned stackDec = StackDecrementForCall(masm, func.argMIRTypes());
+    masm.reserveStack(stackDec);
+
+    // Copy parameters out of argv and into the registers/stack-slots specified by
+    // the system ABI.
+    for (ABIArgIter iter(func.argMIRTypes()); !iter.done(); iter++) {
+        unsigned argOffset = iter.index() * sizeof(uint64_t);
+        Address src(argv, argOffset);
+        switch (iter->kind()) {
+          case ABIArg::GPR:
+            masm.load32(src, iter->gpr());
+            break;
+          case ABIArg::FPU:
+            masm.loadDouble(src, iter->fpu());
+            break;
+          case ABIArg::Stack:
+            if (iter.mirType() == MIRType_Int32) {
+                masm.load32(src, scratch);
+                masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
+            } else {
+                JS_ASSERT(iter.mirType() == MIRType_Double);
+                masm.loadDouble(src, ScratchFloatReg);
+                masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase()));
+            }
+            break;
+        }
+    }
+
+    // Call into the real function.
+    AssertStackAlignment(masm);
+    // masm.call(CallSiteDesc::Entry(), func.code());
+    masm.call(func.codeLabel());
+
+    // Pop the stack and recover the original 'argv' argument passed to the
+    // trampoline (which was pushed on the stack).
+    masm.freeStack(stackDec);
+    masm.Pop(argv);
+
+    // Store the return value in argv[0]
+    switch (func.returnType().which()) {
+      case RetType::Void:
+        break;
+      case RetType::Signed:
+        masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0));
+        break;
+      case RetType::Double:
+        masm.canonicalizeDouble(ReturnFloatReg);
+        masm.storeDouble(ReturnFloatReg, Address(argv, 0));
+        break;
+    }
+
+    // Restore clobbered non-volatile registers of the caller.
+    masm.PopRegsInMask(NonVolatileRegs);
+
+    JS_ASSERT(masm.framePushed() == 0);
+
+    masm.move32(Imm32(true), ReturnReg);
+    masm.abiret();
+    return true;
+}
+#else  // defined(JS_CPU_X86) || defined(JS_CPU_X64
+#error "Unknown CPU architecture."
 #endif
 
 static bool
@@ -5445,14 +5543,14 @@
     CodeOffsetLabel label;
 #if defined(JS_CPU_X64)
     label = masm.leaRipRelative(i->gpr());
-#else
+#else  // defined(JS_CPU_X64)
     if (i->kind() == ABIArg::GPR) {
         label = masm.movlWithPatch(Imm32(0), i->gpr());
     } else {
         label = masm.movlWithPatch(Imm32(0), scratch);
         masm.movl(scratch, Operand(StackPointer, i->offsetFromArgBase()));
     }
-#endif
+#endif // defined(JS_CPU_X64)
     unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex);
     m.addGlobalAccess(AsmJSGlobalAccess(label.offset(), globalDataOffset));
     i++;
@@ -5499,14 +5597,18 @@
     // registers to restore.
     masm.freeStack(stackDec);
     masm.ret();
-#else
+#else  // defined(JS_CPU_X86) || defined(JS_CPU_X64)
     const unsigned arrayLength = Max<size_t>(1, exit.argTypes().length());
     const unsigned arraySize = arrayLength * sizeof(Value);
     const unsigned reserveSize = AlignBytes(arraySize, StackAlignment) +
         ShadowStackSpace;
     const unsigned callerArgsOffset = reserveSize + NativeFrameSize + sizeof(int32_t);
     masm.setFramePushed(0);
+
+#if defined(JS_CPU_ARM)
     masm.Push(lr);
+#endif  // defined(JS_CPU_ARM)
+
     masm.reserveStack(reserveSize + sizeof(int32_t));
 
     // Store arguments
@@ -5544,15 +5646,15 @@
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
 #if defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
         masm.loadValue(argv, softfpReturnOperand);
-#else
+#else  // defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
         masm.loadDouble(argv, ReturnFloatReg);
-#endif
+#endif  // defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
         break;
     }
 
     masm.freeStack(reserveSize + sizeof(int32_t));
     masm.ret();
-#endif
+#endif  // defined(JS_CPU_X86) || defined(JS_CPU_X64)
 }
 
 static int32_t
@@ -5886,7 +5988,7 @@
     masm.align(CodeAlignment);
     masm.bind(&m.operationCallbackLabel());
 
-#ifndef JS_CPU_ARM
+#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     // Be very careful here not to perturb the machine state before saving it
     // to the stack. In particular, add/sub instructions may set conditions in
     // the flags register.
@@ -5906,10 +6008,12 @@
     // We know that StackPointer is word-aligned, but not necessarily
     // stack-aligned, so we need to align it dynamically.
     masm.mov(StackPointer, ABIArgGenerator::NonVolatileReg);
+
 #if defined(JS_CPU_X86)
     // Ensure that at least one slot is pushed for passing 'cx' below.
     masm.push(Imm32(0));
 #endif
+
     masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer);
     if (ShadowStackSpace)
         masm.subPtr(Imm32(ShadowStackSpace), StackPointer);
@@ -5933,7 +6037,8 @@
     masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP)
     masm.popFlags();              // after this, nothing that sets conditions
     masm.ret();                   // pop resumePC into PC
-#else
+
+#elif defined(JS_CPU_ARM)  // defined(JS_CPU_X86) || defined(JS_CPU_X64)
     masm.setFramePushed(0);         // set to zero so we can use masm.framePushed() below
     masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(Registers::AllMask & ~(1<<Registers::sp)), FloatRegisterSet(uint32_t(0))));   // save all GP registers,excep sp
 
@@ -5981,8 +6086,11 @@
     masm.transferReg(lr);
     masm.finishDataTransfer();
     masm.ret();
-
-#endif
+#elif defined(JS_CPU_MIPS)  // defined(JS_CPU_X86) || defined(JS_CPU_X64)
+    JS_NOT_REACHED("NYI_COBALT");
+#else  // defined(JS_CPU_X86) || defined(JS_CPU_X64)
+#error "Unknown CPU architecture.";
+#endif // defined(JS_CPU_X86) || defined(JS_CPU_X64)
 
 }
 
diff --git a/src/third_party/mozjs/js/src/jit/AsmJS.h b/src/third_party/mozjs/js/src/jit/AsmJS.h
index 2e4ac30..2f13754 100644
--- a/src/third_party/mozjs/js/src/jit/AsmJS.h
+++ b/src/third_party/mozjs/js/src/jit/AsmJS.h
@@ -7,6 +7,11 @@
 #ifndef jit_AsmJS_h
 #define jit_AsmJS_h
 
+#if defined(JS_CPU_MIPS)
+#include <stddef.h>
+#include "vm/ObjectImpl.h"
+#endif  // defined(JS_CPU_MIPS)
+
 #ifdef XP_MACOSX
 # include <pthread.h>
 # include <mach/mach.h>
diff --git a/src/third_party/mozjs/js/src/jit/BaselineBailouts.cpp b/src/third_party/mozjs/js/src/jit/BaselineBailouts.cpp
index 5d89825..eeebfa2 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineBailouts.cpp
+++ b/src/third_party/mozjs/js/src/jit/BaselineBailouts.cpp
@@ -344,9 +344,9 @@
         JS_ASSERT(BaselineFrameReg == FramePointer);
         priorOffset -= sizeof(void *);
         return virtualPointerAtStackOffset(priorOffset);
-#elif defined(JS_CPU_X64) || defined(JS_CPU_ARM)
-        // On X64 and ARM, the frame pointer save location depends on the caller of the
-        // the rectifier frame.
+#elif defined(JS_CPU_X64) || defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
+        // On X64, ARM, and MIPS, the frame pointer save location depends on
+        // the caller of the the rectifier frame.
         BufferPointer<IonRectifierFrameLayout> priorFrame =
             pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset);
         FrameType priorType = priorFrame->prevType();
@@ -1175,9 +1175,10 @@
     SnapshotIterator snapIter(iter);
 
     RootedFunction callee(cx, iter.maybeCallee());
+    RootedScript scr(cx, iter.script());
     if (callee) {
         IonSpew(IonSpew_BaselineBailouts, "  Callee function (%s:%u)",
-                callee->existingScript()->filename(), callee->existingScript()->lineno);
+                scr->filename(), scr->lineno);
     } else {
         IonSpew(IonSpew_BaselineBailouts, "  No callee!");
     }
@@ -1194,7 +1195,6 @@
     RootedScript caller(cx);
     jsbytecode *callerPC = NULL;
     RootedFunction fun(cx, callee);
-    RootedScript scr(cx, iter.script());
     AutoValueVector startFrameFormals(cx);
     while (true) {
         IonSpew(IonSpew_BaselineBailouts, "    FrameNo %d", frameNo);
@@ -1217,7 +1217,7 @@
         caller = scr;
         callerPC = callPC;
         fun = nextCallee;
-        scr = fun->existingScript();
+        scr = fun->existingScriptForInlinedFunction();
         snapIter.nextFrame();
 
         frameNo++;
diff --git a/src/third_party/mozjs/js/src/jit/BaselineCompiler.cpp b/src/third_party/mozjs/js/src/jit/BaselineCompiler.cpp
index 173cc71..42de75c 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineCompiler.cpp
+++ b/src/third_party/mozjs/js/src/jit/BaselineCompiler.cpp
@@ -294,6 +294,8 @@
     // On ARM, save the link register before calling.  It contains the return
     // address.  The |masm.ret()| later will pop this into |pc| to return.
     masm.push(lr);
+#elif defined(JS_CPU_MIPS)
+    masm.push(ra);
 #endif
 
     masm.setupUnalignedABICall(2, scratch);
diff --git a/src/third_party/mozjs/js/src/jit/BaselineCompiler.h b/src/third_party/mozjs/js/src/jit/BaselineCompiler.h
index a28775d..f8f78c7 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineCompiler.h
+++ b/src/third_party/mozjs/js/src/jit/BaselineCompiler.h
@@ -26,8 +26,12 @@
 # include "x86/BaselineCompiler-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineCompiler-x64.h"
-#else
+#elif defined(JS_CPU_ARM)
 # include "arm/BaselineCompiler-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/BaselineCompiler-mips.h"
+#else
+# error "Unknown CPU architecture."
 #endif
 
 namespace js {
diff --git a/src/third_party/mozjs/js/src/jit/BaselineHelpers.h b/src/third_party/mozjs/js/src/jit/BaselineHelpers.h
index 2c400d5..4d73840 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineHelpers.h
+++ b/src/third_party/mozjs/js/src/jit/BaselineHelpers.h
@@ -15,6 +15,8 @@
 # include "x64/BaselineHelpers-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/BaselineHelpers-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/BaselineHelpers-mips.h"
 #else
 # error "Unknown architecture!"
 #endif
diff --git a/src/third_party/mozjs/js/src/jit/BaselineIC.cpp b/src/third_party/mozjs/js/src/jit/BaselineIC.cpp
index ed1f2c9..a9e7ef5 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineIC.cpp
+++ b/src/third_party/mozjs/js/src/jit/BaselineIC.cpp
@@ -656,7 +656,7 @@
     masm.bind(&isTenured);
 
     // void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
-#ifdef JS_CPU_ARM
+#if defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
     saveRegs.add(BaselineTailCallReg);
 #endif
     saveRegs = GeneralRegisterSet::Intersect(saveRegs, GeneralRegisterSet::Volatile());
@@ -1918,8 +1918,12 @@
 
     // Compare payload regs of R0 and R1.
     Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
+#if defined(JS_CPU_MIPS)
+    masm.cmp32Set(cond, left, right, left);
+#else
     masm.cmp32(left, right);
     masm.emitSet(cond, left);
+#endif
 
     // Box the result and return
     masm.tagValue(JSVAL_TYPE_BOOLEAN, left, R0);
@@ -2090,9 +2094,18 @@
 
         // Compare payload regs of R0 and R1.
         Assembler::Condition cond = JSOpToCondition(op_, /* signed = */true);
+#if defined(JS_CPU_MIPS)
+        masm.cmp32Set(
+            cond,
+            lhsIsInt32_ ? int32Reg : boolReg,
+            lhsIsInt32_ ? boolReg : int32Reg,
+            R0.scratchReg()
+        );
+#else
         masm.cmp32(lhsIsInt32_ ? int32Reg : boolReg,
                    lhsIsInt32_ ? boolReg : int32Reg);
         masm.emitSet(cond, R0.scratchReg());
+#endif
 
         // Box the result and return
         masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.scratchReg(), R0);
@@ -2218,8 +2231,12 @@
     masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
 
     Label ifFalse;
+#if defined(JS_CPU_MIPS)
+    masm.branchTestInt32Truthy(false, R0, &ifFalse);
+#else
     Assembler::Condition cond = masm.testInt32Truthy(false, R0);
     masm.j(cond, &ifFalse);
+#endif
 
     masm.moveValue(BooleanValue(true), R0);
     EmitReturnFromIC(masm);
@@ -2245,8 +2262,12 @@
     masm.branchTestString(Assembler::NotEqual, R0, &failure);
 
     Label ifFalse;
+#if defined(JS_CPU_MIPS)
+    masm.branchTestStringTruthy(false, R0, &ifFalse);
+#else
     Assembler::Condition cond = masm.testStringTruthy(false, R0);
     masm.j(cond, &ifFalse);
+#endif
 
     masm.moveValue(BooleanValue(true), R0);
     EmitReturnFromIC(masm);
@@ -2292,8 +2313,12 @@
     Label failure, ifTrue;
     masm.branchTestDouble(Assembler::NotEqual, R0, &failure);
     masm.unboxDouble(R0, FloatReg0);
+#if defined(JS_CPU_MIPS)
+    masm.branchTestDoubleTruthy(true, FloatReg0, &ifTrue);
+#else
     Assembler::Condition cond = masm.testDoubleTruthy(true, FloatReg0);
     masm.j(cond, &ifTrue);
+#endif
 
     masm.moveValue(BooleanValue(false), R0);
     EmitReturnFromIC(masm);
@@ -2320,8 +2345,12 @@
 
     Register objReg = masm.extractObject(R0, ExtractTemp0);
     Register scratch = R1.scratchReg();
+#if defined(JS_CPU_MIPS)
+    masm.branchTestObjectTruthy(false, objReg, scratch, &slowPath, &ifFalse);
+#else
     Assembler::Condition cond = masm.branchTestObjectTruthy(false, objReg, scratch, &slowPath);
     masm.j(cond, &ifFalse);
+#endif
 
     // If object doesn't emulate undefined, it evaulates to true.
     masm.moveValue(BooleanValue(true), R0);
@@ -2335,6 +2364,9 @@
     masm.setupUnalignedABICall(1, scratch);
     masm.passABIArg(objReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ObjectEmulatesUndefined));
+#if defined(JS_CPU_MIPS)
+    masm.convertBoolToInt32(ReturnReg, ReturnReg);
+#endif
     masm.xor32(Imm32(1), ReturnReg);
     masm.tagValue(JSVAL_TYPE_BOOLEAN, ReturnReg, R0);
     EmitReturnFromIC(masm);
@@ -2825,6 +2857,15 @@
       case JSOP_ADD: {
         Label fixOverflow;
 
+#if defined(JS_CPU_MIPS)
+        masm.branchAdd32(Assembler::Overflow, rhsReg, lhsReg, &fixOverflow);
+        masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0);
+        EmitReturnFromIC(masm);
+
+        masm.bind(&fixOverflow);
+        masm.sub32(rhsReg, lhsReg);
+        // Proceed to failure below.
+#else
         masm.add32(rhsReg, lhsReg);
         masm.j(Assembler::Overflow, &fixOverflow);
         masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0);
@@ -2833,11 +2874,21 @@
         masm.bind(&fixOverflow);
         masm.sub32(rhsReg, lhsReg);
         masm.jump(&failure);
+#endif
         break;
       }
       case JSOP_SUB: {
         Label fixOverflow;
 
+#if defined(JS_CPU_MIPS)
+        masm.branchSub32(Assembler::Overflow, rhsReg, lhsReg, &fixOverflow);
+        masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0);
+        EmitReturnFromIC(masm);
+
+        masm.bind(&fixOverflow);
+        masm.add32(rhsReg, lhsReg);
+        // Proceed to failure below.
+#else
         masm.sub32(rhsReg, lhsReg);
         masm.j(Assembler::Overflow, &fixOverflow);
         masm.tagValue(JSVAL_TYPE_INT32, lhsReg, R0);
@@ -2846,6 +2897,7 @@
         masm.bind(&fixOverflow);
         masm.add32(rhsReg, lhsReg);
         masm.jump(&failure);
+#endif
         break;
       }
       case JSOP_BITOR: {
@@ -7268,8 +7320,17 @@
         Address expectedScript(BaselineStubReg, ICCall_Scripted::offsetOfCalleeScript());
         masm.branchPtr(Assembler::NotEqual, expectedScript, callee, &failure);
     } else {
+#if defined(JS_CPU_MIPS)
+        if (isConstructing_) {
+            masm.branchIfNotInterpretedConstructor(callee, regs.getAny(), &failure);
+        } else {
+            masm.branchIfFunctionHasNoScript(callee, &failure);
+        }
+        masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+#else
         masm.branchIfFunctionHasNoScript(callee, &failure);
         masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+#endif
     }
 
     // Load the start of the target IonCode.
@@ -7901,8 +7962,13 @@
 
     // Set output to true if props_cursor < props_end.
     masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_end)), scratch);
+#if defined(JS_CPU_MIPS)
+    Address cursorAddr = Address(nativeIterator, offsetof(NativeIterator, props_cursor));
+    masm.cmpPtrSet(Assembler::LessThan, cursorAddr, scratch, scratch);
+#else
     masm.cmpPtr(Address(nativeIterator, offsetof(NativeIterator, props_cursor)), scratch);
     masm.emitSet(Assembler::LessThan, scratch);
+#endif
 
     masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, R0);
     EmitReturnFromIC(masm);
diff --git a/src/third_party/mozjs/js/src/jit/BaselineIC.h b/src/third_party/mozjs/js/src/jit/BaselineIC.h
index 63da318..3928295 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineIC.h
+++ b/src/third_party/mozjs/js/src/jit/BaselineIC.h
@@ -1025,11 +1025,17 @@
     inline GeneralRegisterSet availableGeneralRegs(size_t numInputs) const {
         GeneralRegisterSet regs(GeneralRegisterSet::All());
         JS_ASSERT(!regs.has(BaselineStackReg));
+
 #ifdef JS_CPU_ARM
         JS_ASSERT(!regs.has(BaselineTailCallReg));
+#elif defined(JS_CPU_MIPS)
+        JS_ASSERT(!regs.has(BaselineTailCallReg));
+        JS_ASSERT(!regs.has(BaselineSecondScratchReg));
 #endif
+
         regs.take(BaselineFrameReg);
         regs.take(BaselineStubReg);
+
 #ifdef JS_CPU_X64
         regs.take(ExtractTemp0);
         regs.take(ExtractTemp1);
diff --git a/src/third_party/mozjs/js/src/jit/BaselineRegisters.h b/src/third_party/mozjs/js/src/jit/BaselineRegisters.h
index 0bc20fd..ea625a7 100644
--- a/src/third_party/mozjs/js/src/jit/BaselineRegisters.h
+++ b/src/third_party/mozjs/js/src/jit/BaselineRegisters.h
@@ -13,8 +13,12 @@
 # include "x86/BaselineRegisters-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineRegisters-x64.h"
-#else
+#elif defined(JS_CPU_ARM)
 # include "arm/BaselineRegisters-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/BaselineRegisters-mips.h"
+#else
+#error "Unknown CPU architecture."
 #endif
 
 namespace js {
diff --git a/src/third_party/mozjs/js/src/jit/CodeGenerator.cpp b/src/third_party/mozjs/js/src/jit/CodeGenerator.cpp
index ab8ea02..4ab2f2f 100644
--- a/src/third_party/mozjs/js/src/jit/CodeGenerator.cpp
+++ b/src/third_party/mozjs/js/src/jit/CodeGenerator.cpp
@@ -372,9 +372,14 @@
     // Perform a fast-path check of the object's class flags if the object's
     // not a proxy.  Let out-of-line code handle the slow cases that require
     // saving registers, making a function call, and restoring registers.
+#if defined(JS_CPU_MIPS)
+    masm.branchTestObjectTruthy(false, objreg, scratch, ool->entry(), ifTruthy);
+    masm.jump(ifFalsy);
+#else
     Assembler::Condition cond = masm.branchTestObjectTruthy(true, objreg, scratch, ool->entry());
     masm.j(cond, ifTruthy);
     masm.jump(ifFalsy);
+#endif
 }
 
 void
@@ -385,7 +390,9 @@
                                OutOfLineTestObject *ool)
 {
     Register tag = masm.splitTagForTest(value);
+#if !defined(JS_CPU_MIPS)
     Assembler::Condition cond;
+#endif
 
     // Eventually we will want some sort of type filter here. For now, just
     // emit all easy cases. For speed we use the cached tag for all comparison,
@@ -402,8 +409,12 @@
 
     Label notInt32;
     masm.branchTestInt32(Assembler::NotEqual, tag, &notInt32);
+#if defined(JS_CPU_MIPS)
+    masm.branchTestInt32Truthy(false, value, ifFalsy);
+#else
     cond = masm.testInt32Truthy(false, value);
     masm.j(cond, ifFalsy);
+#endif
     masm.jump(ifTruthy);
     masm.bind(&notInt32);
 
@@ -423,15 +434,23 @@
     // Test if a string is non-empty.
     Label notString;
     masm.branchTestString(Assembler::NotEqual, tag, &notString);
+#if defined(JS_CPU_MIPS)
+    masm.branchTestStringTruthy(false, value, ifFalsy);
+#else
     cond = masm.testStringTruthy(false, value);
     masm.j(cond, ifFalsy);
+#endif
     masm.jump(ifTruthy);
     masm.bind(&notString);
 
     // If we reach here the value is a double.
     masm.unboxDouble(value, fr);
+#if defined(JS_CPU_MIPS)
+    masm.branchTestDoubleTruthy(false, fr, ifFalsy);
+#else
     cond = masm.testDoubleTruthy(false, fr);
     masm.j(cond, ifFalsy);
+#endif
     masm.jump(ifTruthy);
 }
 
@@ -1560,12 +1579,25 @@
 
     // Guard that calleereg is actually a function object.
     masm.loadObjClass(calleereg, nargsreg);
+#if defined(JS_CPU_MIPS)
+    masm.branchPtr(Assembler::NotEqual, nargsreg, ImmWord(&JSFunction::class_), &uncompiled);
+#else
     masm.cmpPtr(nargsreg, ImmWord(&JSFunction::class_));
     if (!bailoutIf(Assembler::NotEqual, call->snapshot()))
         return false;
+#endif
 
+#if defined(JS_CPU_MIPS)
+    // Guard that calleereg is an interpreted function with a JSScript.
+    // If we are constructing, also ensure the callee is a constructor.
+    if (call->mir()->isConstructing())
+        masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &uncompiled);
+    else
+        masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
+#else
     // Guard that calleereg is an interpreted function with a JSScript:
     masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
+#endif
 
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
@@ -1587,8 +1619,12 @@
 
     // Check whether the provided arguments satisfy target argc.
     masm.load16ZeroExtend(Address(calleereg, offsetof(JSFunction, nargs)), nargsreg);
+#if defined(JS_CPU_MIPS)
+    masm.branch32(Assembler::Above, nargsreg, Imm32(call->numStackArgs()), &thunk);
+#else
     masm.cmp32(nargsreg, Imm32(call->numStackArgs()));
     masm.j(Assembler::Above, &thunk);
+#endif
 
     masm.jump(&makeCall);
 
@@ -1883,9 +1919,16 @@
     // Unless already known, guard that calleereg is actually a function object.
     if (!apply->hasSingleTarget()) {
         masm.loadObjClass(calleereg, objreg);
+#if defined(JS_CPU_MIPS)
+        ImmWord ptr = ImmWord(&JSFunction::class_);
+        if (!bailoutCmpPtr(Assembler::NotEqual, objreg, ptr, apply->snapshot())) {
+            return false;
+        }
+#else
         masm.cmpPtr(objreg, ImmWord(&JSFunction::class_));
         if (!bailoutIf(Assembler::NotEqual, apply->snapshot()))
             return false;
+#endif
     }
 
     // Copy the arguments of the current function.
@@ -1937,11 +1980,19 @@
         // Check whether the provided arguments satisfy target argc.
         if (!apply->hasSingleTarget()) {
             masm.load16ZeroExtend(Address(calleereg, offsetof(JSFunction, nargs)), copyreg);
+#if defined(JS_CPU_MIPS)
+            masm.branch32(Assembler::Below, argcreg, copyreg, &underflow);
+#else
             masm.cmp32(argcreg, copyreg);
             masm.j(Assembler::Below, &underflow);
+#endif
         } else {
+#if defined(JS_CPU_MIPS)
+            masm.branch32(Assembler::Below, argcreg, Imm32(apply->getSingleTarget()->nargs), &underflow);
+#else
             masm.cmp32(argcreg, Imm32(apply->getSingleTarget()->nargs));
             masm.j(Assembler::Below, &underflow);
+#endif
         }
 
         // Skip the construction of the rectifier frame because we have no
@@ -2022,8 +2073,14 @@
     masm.loadValue(Address(StackPointer, 0), out);
     masm.adjustStack(sizeof(Value));
 
+#if defined(JS_CPU_MIPS)
+    Label undefined;
+    masm.branchTestUndefined(Assembler::Equal, out, &undefined);
+    return bailoutFrom(&undefined, lir->snapshot());
+#else
     Assembler::Condition cond = masm.testUndefined(Assembler::Equal, out);
     return bailoutIf(cond, lir->snapshot());
+#endif
 }
 
 bool
@@ -2645,10 +2702,14 @@
     masm.passABIArg(temp1);
     masm.passABIArg(temp2);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots));
-
+#if defined(JS_CPU_MIPS)
+    if (!bailoutTestPtr(Assembler::Zero, output, output, lir->snapshot()))
+        return false;
+#else
     masm.testPtr(output, output);
     if (!bailoutIf(Assembler::Zero, lir->snapshot()))
         return false;
+#endif
 
     return true;
 }
@@ -3307,6 +3368,20 @@
 
     JS_ASSERT(first == output);
 
+#if defined(JS_CPU_MIPS)
+    Label done;
+    Assembler::Condition cond = ins->mir()->isMax()
+                                ? Assembler::GreaterThan
+                                : Assembler::LessThan;
+
+    if (ins->second()->isConstant()) {
+        masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done);
+        masm.move32(Imm32(ToInt32(ins->second())), output);
+    } else {
+        masm.branch32(cond, first, ToRegister(ins->second()), &done);
+        masm.move32(ToRegister(ins->second()), output);
+    }
+#else
     if (ins->second()->isConstant())
         masm.cmp32(first, Imm32(ToInt32(ins->second())));
     else
@@ -3323,6 +3398,7 @@
     else
         masm.mov(ToRegister(ins->second()), output);
 
+#endif
 
     masm.bind(&done);
     return true;
@@ -3331,6 +3407,22 @@
 bool
 CodeGenerator::visitAbsI(LAbsI *ins)
 {
+#if defined(JS_CPU_MIPS)
+    Register input = ToRegister(ins->input());
+    Label positive;
+
+    JS_ASSERT(input == ToRegister(ins->output()));
+    masm.branchTest32(Assembler::NotSigned, input, input, &positive);
+    masm.neg32(input);
+    LSnapshot *snapshot = ins->snapshot();
+    if (snapshot && !bailoutCmp32(Assembler::Equal, input, Imm32(INT32_MIN), snapshot))
+        return false;
+    masm.bind(&positive);
+
+    return true;
+
+#else  // defined(JS_CPU_MIPS)
+
     Register input = ToRegister(ins->input());
     Label positive;
 
@@ -3343,6 +3435,7 @@
     masm.bind(&positive);
 
     return true;
+#endif  // defined(JS_CPU_MIPS)
 }
 
 bool
@@ -3755,6 +3848,13 @@
 
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
+#if defined(JS_CPU_MIPS)
+    Assembler::Condition cond = JSOpToCondition(compareType, op);
+    if (compareType == MCompare::Compare_Null)
+        masm.testNullSet(cond, value, output);
+    else
+        masm.testUndefinedSet(cond, value, output);
+#else
     Assembler::Condition cond = JSOpToCondition(compareType, op);
     if (compareType == MCompare::Compare_Null)
         cond = masm.testNull(cond, value);
@@ -3762,6 +3862,7 @@
         cond = masm.testUndefined(cond, value);
 
     masm.emitSet(cond, output);
+#endif
     return true;
 }
 
@@ -3821,6 +3922,13 @@
 
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
+#if defined(JS_CPU_MIPS)
+    Assembler::Condition cond = JSOpToCondition(compareType, op);
+    if (compareType == MCompare::Compare_Null)
+        testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
+    else
+        testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
+#else
     Assembler::Condition cond = JSOpToCondition(compareType, op);
     if (compareType == MCompare::Compare_Null)
         cond = masm.testNull(cond, value);
@@ -3828,6 +3936,8 @@
         cond = masm.testUndefined(cond, value);
 
     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
+#endif
+
     return true;
 }
 
@@ -3961,8 +4071,13 @@
     masm.store16(scratch, Address(to, 0));
     masm.addPtr(Imm32(2), from);
     masm.addPtr(Imm32(2), to);
+
+#if defined(JS_CPU_MIPS)
+    masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
+#else
     masm.sub32(Imm32(1), len);
     masm.j(Assembler::NonZero, &start);
+#endif
 }
 
 IonCode *
@@ -4221,17 +4336,92 @@
                 return true;
             return bailout(lir->snapshot());
         }
+#if defined(JS_CPU_MIPS)
+        return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(index), lir->snapshot());
+#else
         masm.cmp32(ToOperand(lir->length()), Imm32(index));
         return bailoutIf(Assembler::BelowOrEqual, lir->snapshot());
+#endif
     }
     if (lir->length()->isConstant()) {
+#if defined(JS_CPU_MIPS)
+        return bailoutCmp32(Assembler::AboveOrEqual, ToRegister(lir->index()), Imm32(ToInt32(lir->length())), lir->snapshot());
+#else
         masm.cmp32(ToRegister(lir->index()), Imm32(ToInt32(lir->length())));
         return bailoutIf(Assembler::AboveOrEqual, lir->snapshot());
+#endif
     }
+#if defined(JS_CPU_MIPS)
+    return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), ToRegister(lir->index()), lir->snapshot());
+#else
     masm.cmp32(ToOperand(lir->length()), ToRegister(lir->index()));
     return bailoutIf(Assembler::BelowOrEqual, lir->snapshot());
+#endif
 }
 
+#if defined(JS_CPU_MIPS)
+bool
+CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange *lir)
+{
+    int32_t min = lir->mir()->minimum();
+    int32_t max = lir->mir()->maximum();
+    JS_ASSERT(max >= min);
+
+    Register temp = ToRegister(lir->getTemp(0));
+    if (lir->index()->isConstant()) {
+        int32_t nmin, nmax;
+        int32_t index = ToInt32(lir->index());
+        if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
+            return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(nmax),
+                                lir->snapshot());
+        }
+        masm.mov(Imm32(index), temp);
+    } else {
+        masm.mov(ToRegister(lir->index()), temp);
+    }
+
+    // If the minimum and maximum differ then do an underflow check first.
+    // If the two are the same then doing an unsigned comparison on the
+    // length will also catch a negative index.
+    if (min != max) {
+        if (min != 0) {
+            Label bail;
+            masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
+            if (!bailoutFrom(&bail, lir->snapshot()))
+                return false;
+        }
+
+        if (!bailoutCmp32(Assembler::LessThan, temp, Imm32(0), lir->snapshot()))
+            return false;
+
+        if (min != 0) {
+            int32_t diff;
+            if (SafeSub(max, min, &diff))
+                max = diff;
+            else
+                masm.sub32(Imm32(min), temp);
+        }
+    }
+
+    // Compute the maximum possible index. No overflow check is needed when
+    // max > 0. We can only wraparound to a negative number, which will test as
+    // larger than all nonnegative numbers in the unsigned comparison, and the
+    // length is required to be nonnegative (else testing a negative length
+    // would succeed on any nonnegative index).
+    if (max != 0) {
+        if (max < 0) {
+            Label bail;
+            masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
+            if (!bailoutFrom(&bail, lir->snapshot()))
+                return false;
+        } else {
+            masm.add32(Imm32(max), temp);
+        }
+    }
+
+    return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), temp, lir->snapshot());
+}
+#else
 bool
 CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange *lir)
 {
@@ -4261,7 +4451,6 @@
             if (!bailoutIf(Assembler::Overflow, lir->snapshot()))
                 return false;
         }
-
         masm.cmp32(temp, Imm32(0));
         if (!bailoutIf(Assembler::LessThan, lir->snapshot()))
             return false;
@@ -4290,13 +4479,19 @@
     masm.cmp32(ToOperand(lir->length()), temp);
     return bailoutIf(Assembler::BelowOrEqual, lir->snapshot());
 }
+#endif
 
 bool
 CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower *lir)
 {
     int32_t min = lir->mir()->minimum();
+#if defined(JS_CPU_MIPS)
+    return bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
+                        lir->snapshot());
+#else
     masm.cmp32(ToRegister(lir->index()), Imm32(min));
     return bailoutIf(Assembler::LessThan, lir->snapshot());
+#endif
 }
 
 class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
@@ -4325,12 +4520,24 @@
 bool
 CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot)
 {
+#if defined(JS_CPU_MIPS)
+    Label bail;
+    if (index->isConstant()) {
+        masm.branchTestMagic(Assembler::Equal,
+                             Address(elements, ToInt32(index) * sizeof(js::Value)), &bail);
+    } else {
+        masm.branchTestMagic(Assembler::Equal,
+                             BaseIndex(elements, ToRegister(index), TimesEight), &bail);
+    }
+    return bailoutFrom(&bail, snapshot);
+#else
     Assembler::Condition cond;
     if (index->isConstant())
         cond = masm.testMagic(Assembler::Equal, Address(elements, ToInt32(index) * sizeof(js::Value)));
     else
         cond = masm.testMagic(Assembler::Equal, BaseIndex(elements, ToRegister(index), TimesEight));
     return bailoutIf(cond, snapshot);
+#endif
 }
 
 bool
@@ -4428,6 +4635,94 @@
 static const VMFunction SetObjectElementInfo =
     FunctionInfo<SetObjectElementFn>(SetObjectElement);
 
+#if defined(JS_CPU_MIPS)
+bool
+CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
+{
+    Register object, elements;
+    LInstruction *ins = ool->ins();
+    const LAllocation *index;
+    MIRType valueType;
+    ConstantOrRegister value;
+
+    if (ins->isStoreElementHoleV()) {
+        LStoreElementHoleV *store = ins->toStoreElementHoleV();
+        object = ToRegister(store->object());
+        elements = ToRegister(store->elements());
+        index = store->index();
+        valueType = store->mir()->value()->type();
+        value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
+    } else {
+        LStoreElementHoleT *store = ins->toStoreElementHoleT();
+        object = ToRegister(store->object());
+        elements = ToRegister(store->elements());
+        index = store->index();
+        valueType = store->mir()->value()->type();
+        if (store->value()->isConstant())
+            value = ConstantOrRegister(*store->value()->toConstant());
+        else
+            value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
+    }
+
+    // If index == initializedLength, try to bump the initialized length inline.
+    // If index > initializedLength, call a stub. Note that this relies on the
+    // condition flags sticking from the incoming branch.
+    Label callStub;
+    // Had to reimplement for MIPS because there are no flags.
+    Address initLength(elements, ObjectElements::offsetOfInitializedLength());
+    masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub);
+
+    Int32Key key = ToInt32Key(index);
+
+    // Check array capacity.
+    masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
+                   key, &callStub);
+
+    // Update initialized length. The capacity guard above ensures this won't overflow,
+    // due to NELEMENTS_LIMIT.
+    masm.bumpKey(&key, 1);
+    masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
+
+    // Update length if length < initializedLength.
+    Label dontUpdate;
+    masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
+                   key, &dontUpdate);
+    masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength()));
+    masm.bind(&dontUpdate);
+
+    masm.bumpKey(&key, -1);
+
+    if (ins->isStoreElementHoleT() && valueType != MIRType_Double) {
+        // The inline path for StoreElementHoleT does not always store the type tag,
+        // so we do the store on the OOL path. We use MIRType_None for the element type
+        // so that storeElementTyped will always store the type tag.
+        storeElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None, elements,
+                          index);
+        masm.jump(ool->rejoin());
+    } else {
+        // Jump to the inline path where we will store the value.
+        masm.jump(ool->rejoinStore());
+    }
+
+    masm.bind(&callStub);
+    saveLive(ins);
+
+    pushArg(Imm32(current->mir()->strict()));
+    pushArg(value);
+    if (index->isConstant())
+        pushArg(Imm32(ToInt32(index)));
+    else
+        pushArg(ToRegister(index));
+    pushArg(object);
+    // if (!callVM(SetDenseElementInfo, ins))
+    if (!callVM(SetObjectElementInfo, ins))
+        return false;
+
+    restoreLive(ins);
+    masm.jump(ool->rejoin());
+    return true;
+}
+#else
 bool
 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
 {
@@ -4584,6 +4879,7 @@
     JS_ASSERT(false);
     return false;
 }
+#endif
 
 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense);
@@ -4971,8 +5267,12 @@
 
     // Set output to true if props_cursor < props_end.
     masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
+#if defined(JS_CPU_MIPS)
+    masm.cmpPtrSet(Assembler::LessThan, Address(output, offsetof(NativeIterator, props_cursor)), temp, output);
+#else
     masm.cmpPtr(Address(output, offsetof(NativeIterator, props_cursor)), temp);
     masm.emitSet(Assembler::LessThan, output);
+#endif
 
     masm.bind(ool->rejoin());
     return true;
@@ -5225,6 +5525,7 @@
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::ION_CODE);
+
     if (!code)
         return false;
 
@@ -5373,9 +5674,16 @@
     const ValueOperand value = ToValue(ins, LUnboxDouble::Input);
 
     if (ins->mir()->fallible()) {
+#if defined(JS_CPU_MIPS)
+        Label bail;
+        masm.branchTestInt32(Assembler::NotEqual, value, &bail);
+        if (!bailoutFrom(&bail, ins->snapshot()))
+            return false;
+#else
         Assembler::Condition cond = masm.testInt32(Assembler::NotEqual, value);
         if (!bailoutIf(cond, ins->snapshot()))
             return false;
+#endif
     }
     masm.int32ValueToDouble(value, ToFloatRegister(ins->output()));
     masm.jump(ool->rejoin());
@@ -6050,6 +6358,28 @@
     return true;
 }
 
+#if defined(JS_CPU_MIPS)
+bool
+CodeGenerator::visitLoadElementV(LLoadElementV *load)
+{
+    Register elements = ToRegister(load->elements());
+    const ValueOperand out = ToOutValue(load);
+
+    if (load->index()->isConstant())
+        masm.loadValue(Address(elements, ToInt32(load->index()) * sizeof(Value)), out);
+    else
+        masm.loadValue(BaseIndex(elements, ToRegister(load->index()), TimesEight), out);
+
+    if (load->mir()->needsHoleCheck()) {
+        Label testMagic;
+        masm.branchTestMagic(Assembler::Equal, out, &testMagic);
+        if (!bailoutFrom(&testMagic, load->snapshot()))
+            return false;
+    }
+
+    return true;
+}
+#else
 bool
 CodeGenerator::visitLoadElementV(LLoadElementV *load)
 {
@@ -6069,6 +6399,7 @@
 
     return true;
 }
+#endif
 
 bool
 CodeGenerator::visitLoadElementHole(LLoadElementHole *lir)
@@ -6825,8 +7156,12 @@
     masm.jump(&done);
 
     masm.bind(&notFunction);
+#if defined(JS_CPU_MIPS)
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, call)), ImmWord((void*)NULL), output);
+#else
     masm.cmpPtr(Address(output, offsetof(js::Class, call)), ImmWord((void *)NULL));
     masm.emitSet(Assembler::NonZero, output);
+#endif
     masm.bind(&done);
 
     return true;
@@ -6880,8 +7215,12 @@
 
     masm.loadObjClass(lhs, temp);
     masm.loadObjClass(rhs, output);
+#if defined(JS_CPU_MIPS)
+    masm.cmpPtrSet(Assembler::Equal, temp, output, output);
+#else
     masm.cmpPtr(temp, output);
     masm.emitSet(Assembler::Equal, output);
+#endif
 
     return true;
 }
diff --git a/src/third_party/mozjs/js/src/jit/CodeGenerator.h b/src/third_party/mozjs/js/src/jit/CodeGenerator.h
index 94854d4..e92477c 100644
--- a/src/third_party/mozjs/js/src/jit/CodeGenerator.h
+++ b/src/third_party/mozjs/js/src/jit/CodeGenerator.h
@@ -13,8 +13,10 @@
 # include "x64/CodeGenerator-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/CodeGenerator-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/CodeGenerator-mips.h"
 #else
-#error "CPU Not Supported"
+# error "CPU Not Supported"
 #endif
 
 namespace js {
diff --git a/src/third_party/mozjs/js/src/jit/Ion.cpp b/src/third_party/mozjs/js/src/jit/Ion.cpp
index c12df1b..66cce66 100644
--- a/src/third_party/mozjs/js/src/jit/Ion.cpp
+++ b/src/third_party/mozjs/js/src/jit/Ion.cpp
@@ -38,6 +38,10 @@
 # include "x64/Lowering-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/Lowering-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/Lowering-mips.h"
+#else
+# error "Unknown CPU architecture."
 #endif
 #include "gc/Marking.h"
 
@@ -185,6 +189,11 @@
     functionWrappers_(NULL),
     osrTempData_(NULL),
     flusher_(NULL)
+#if defined(JS_CPU_MIPS)
+    ,
+    exceptionTail_(NULL),
+    bailoutTail_(NULL)
+#endif
 {
 }
 
@@ -194,6 +203,108 @@
     freeOsrTempData();
 }
 
+#if defined(JS_CPU_MIPS)
+bool
+IonRuntime::initialize(JSContext *cx)
+{
+    // JS_ASSERT(cx->runtime()->currentThreadHasExclusiveAccess());
+    // JS_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
+
+    // AutoCompartment ac(cx, cx->atomsCompartment());
+
+    IonContext ictx(cx, NULL);
+    AutoFlushCache afc("IonRuntime::initialize");
+
+    execAlloc_ = cx->runtime()->getExecAlloc(cx);
+    if (!execAlloc_)
+        return false;
+
+    if (!cx->compartment()->ensureIonCompartmentExists(cx))
+        return false;
+
+    functionWrappers_ = cx->new_<VMWrapperMap>(cx);
+    if (!functionWrappers_ || !functionWrappers_->init())
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting exception tail stub");
+    exceptionTail_ = generateExceptionTailStub(cx);
+    if (!exceptionTail_)
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting bailout tail stub");
+    bailoutTail_ = generateBailoutTailStub(cx);
+    if (!bailoutTail_)
+        return false;
+
+    if (cx->runtime()->jitSupportsFloatingPoint) {
+        IonSpew(IonSpew_Codegen, "# Emitting bailout tables");
+
+        // Initialize some Ion-only stubs that require floating-point support.
+        if (!bailoutTables_.reserve(FrameSizeClass::ClassLimit().classId()))
+            return false;
+
+        for (uint32_t id = 0;; id++) {
+            FrameSizeClass class_ = FrameSizeClass::FromClass(id);
+            if (class_ == FrameSizeClass::ClassLimit())
+                break;
+            bailoutTables_.infallibleAppend((IonCode *)NULL);
+            bailoutTables_[id] = generateBailoutTable(cx, id);
+            if (!bailoutTables_[id])
+                return false;
+        }
+
+        IonSpew(IonSpew_Codegen, "# Emitting bailout handler");
+        bailoutHandler_ = generateBailoutHandler(cx);
+        if (!bailoutHandler_)
+            return false;
+
+        IonSpew(IonSpew_Codegen, "# Emitting invalidator");
+        invalidator_ = generateInvalidator(cx);
+        if (!invalidator_)
+            return false;
+    }
+
+    IonSpew(IonSpew_Codegen, "# Emitting sequential arguments rectifier");
+    argumentsRectifier_ = generateArgumentsRectifier(cx, SequentialExecution, &argumentsRectifierReturnAddr_);
+    if (!argumentsRectifier_)
+        return false;
+
+#ifdef JS_THREADSAFE
+    IonSpew(IonSpew_Codegen, "# Emitting parallel arguments rectifier");
+    parallelArgumentsRectifier_ = generateArgumentsRectifier(cx, ParallelExecution, NULL);
+    if (!parallelArgumentsRectifier_)
+        return false;
+#endif
+
+    IonSpew(IonSpew_Codegen, "# Emitting EnterJIT sequence");
+    enterJIT_ = generateEnterJIT(cx, EnterJitOptimized);
+    if (!enterJIT_)
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting EnterBaselineJIT sequence");
+    enterBaselineJIT_ = generateEnterJIT(cx, EnterJitBaseline);
+    if (!enterBaselineJIT_)
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting Pre Barrier for Value");
+    valuePreBarrier_ = generatePreBarrier(cx, MIRType_Value);
+    if (!valuePreBarrier_)
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting Pre Barrier for Shape");
+    shapePreBarrier_ = generatePreBarrier(cx, MIRType_Shape);
+    if (!shapePreBarrier_)
+        return false;
+
+    IonSpew(IonSpew_Codegen, "# Emitting VM function wrappers");
+    for (VMFunction *fun = VMFunction::functions; fun; fun = fun->next) {
+        if (!generateVMWrapper(cx, *fun))
+            return false;
+    }
+
+    return true;
+}
+#else
 bool
 IonRuntime::initialize(JSContext *cx)
 {
@@ -270,6 +381,7 @@
 
     return true;
 }
+#endif
 
 IonCode *
 IonRuntime::debugTrapHandler(JSContext *cx)
@@ -2368,6 +2480,69 @@
     runtime_ = rt;
 }
 
+#if defined(JS_CPU_MIPS)
+AutoFlushCache::~AutoFlushCache()
+{
+    if (!runtime_) {
+        return;
+    }
+
+    flushAnyway();
+    IonSpewCont(IonSpew_CacheFlush, ">", name_);
+    if (runtime_->flusher() == this) {
+        IonSpewFin(IonSpew_CacheFlush);
+        runtime_->setFlusher(NULL);
+    }
+}
+
+void
+AutoFlushCache::update(uintptr_t newStart, size_t len) {
+    uintptr_t newStop = newStart + len;
+    if (this == NULL) {
+        // just flush right here and now.
+        JSC::ExecutableAllocator::cacheFlush((void*)newStart, len);
+        return;
+    }
+    used_ = true;
+    if (!start_) {
+        IonSpewCont(IonSpew_CacheFlush,  ".");
+        start_ = newStart;
+        stop_ = newStop;
+        return;
+    }
+
+    if (newStop < start_ - 4096 || newStart > stop_ + 4096) {
+        // If this would add too many pages to the range, bail and just do the flush now.
+        IonSpewCont(IonSpew_CacheFlush, "*");
+        JSC::ExecutableAllocator::cacheFlush((void*)newStart, len);
+        return;
+    }
+    start_ = Min(start_, newStart);
+    stop_ = Max(stop_, newStop);
+    IonSpewCont(IonSpew_CacheFlush, ".");
+}
+
+void
+AutoFlushCache::flushAnyway()
+{
+    if (!runtime_)
+        return;
+
+    IonSpewCont(IonSpew_CacheFlush, "|", name_);
+
+    if (!used_)
+        return;
+
+    if (start_) {
+        JSC::ExecutableAllocator::cacheFlush((void *)start_, size_t(stop_ - start_ + sizeof(Instruction)));
+    } else {
+        JSC::ExecutableAllocator::cacheFlush(NULL, 0xff000000);
+    }
+    used_ = false;
+}
+
+#endif  // defined(JS_CPU_MIPS)
+
 AutoFlushInhibitor::AutoFlushInhibitor(IonCompartment *ic)
   : ic_(ic),
     afc(NULL)
diff --git a/src/third_party/mozjs/js/src/jit/IonCaches.h b/src/third_party/mozjs/js/src/jit/IonCaches.h
index cf13df9..610f8da 100644
--- a/src/third_party/mozjs/js/src/jit/IonCaches.h
+++ b/src/third_party/mozjs/js/src/jit/IonCaches.h
@@ -10,6 +10,11 @@
 #include "IonCode.h"
 #include "Registers.h"
 
+#if defined(JS_CPU_MIPS)
+#include "jit/mips/Assembler-mips.h"
+#include "jit/shared/Assembler-shared.h"
+#endif  // defined(JS_CPU_MIPS)
+
 #include "vm/ForkJoin.h"
 
 class JSFunction;
@@ -337,13 +342,16 @@
     // Offset from the initial jump to the rejoin label.
 #ifdef JS_CPU_ARM
     static const size_t REJOIN_LABEL_OFFSET = 4;
+#elif defined(JS_CPU_MIPS)
+    // The size of jump created by MacroAssemblerMIPSCompat::jumpWithPatch.
+    static const size_t REJOIN_LABEL_OFFSET = 4 * sizeof(void *);
 #else
     static const size_t REJOIN_LABEL_OFFSET = 0;
 #endif
 
     CodeLocationLabel rejoinLabel() const {
         uint8_t *ptr = initialJump_.raw();
-#ifdef JS_CPU_ARM
+#if defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
         uint32_t i = 0;
         while (i < REJOIN_LABEL_OFFSET)
             ptr = Assembler::nextInstruction(ptr, &i);
diff --git a/src/third_party/mozjs/js/src/jit/IonCompartment.h b/src/third_party/mozjs/js/src/jit/IonCompartment.h
index 88107ed..d1b0dbf 100644
--- a/src/third_party/mozjs/js/src/jit/IonCompartment.h
+++ b/src/third_party/mozjs/js/src/jit/IonCompartment.h
@@ -195,6 +195,22 @@
         if (!flusher_ || !fl)
             flusher_ = fl;
     }
+
+#if defined(JS_CPU_MIPS)
+    // Shared post-bailout-handler tail.
+    IonCode* bailoutTail_;
+    IonCode* getBailoutTail() const {
+        return bailoutTail_;
+    }
+
+    IonCode* exceptionTail_;
+    IonCode* getExceptionTail() const {
+        return exceptionTail_;
+    }
+
+    IonCode *generateExceptionTailStub(JSContext *cx);
+    IonCode *generateBailoutTailStub(JSContext *cx);
+#endif
 };
 
 class IonCompartment
diff --git a/src/third_party/mozjs/js/src/jit/IonFrames.cpp b/src/third_party/mozjs/js/src/jit/IonFrames.cpp
index 6cdd786..4d0fc05 100644
--- a/src/third_party/mozjs/js/src/jit/IonFrames.cpp
+++ b/src/third_party/mozjs/js/src/jit/IonFrames.cpp
@@ -1307,7 +1307,7 @@
         // Inlined functions may be clones that still point to the lazy script
         // for the executed script, if they are clones. The actual script
         // exists though, just make sure the function points to it.
-        script_ = callee_->existingScript();
+        script_ = callee_->existingScriptForInlinedFunction();
 
         pc_ = script_->code + si_.pcOffset();
     }
diff --git a/src/third_party/mozjs/js/src/jit/IonFrames.h b/src/third_party/mozjs/js/src/jit/IonFrames.h
index fcd33e6..573d7ea 100644
--- a/src/third_party/mozjs/js/src/jit/IonFrames.h
+++ b/src/third_party/mozjs/js/src/jit/IonFrames.h
@@ -292,6 +292,8 @@
 # include "jit/shared/IonFrames-x86-shared.h"
 #elif defined (JS_CPU_ARM)
 # include "jit/arm/IonFrames-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "jit/mips/IonFrames-mips.h"
 #else
 # error "unsupported architecture"
 #endif
diff --git a/src/third_party/mozjs/js/src/jit/IonMacroAssembler.cpp b/src/third_party/mozjs/js/src/jit/IonMacroAssembler.cpp
index fe4bd23..0a563e2 100644
--- a/src/third_party/mozjs/js/src/jit/IonMacroAssembler.cpp
+++ b/src/third_party/mozjs/js/src/jit/IonMacroAssembler.cpp
@@ -134,6 +134,44 @@
 template void MacroAssembler::guardType(const ValueOperand &value, types::Type type,
                                         Register scratch, Label *matched, Label *miss);
 
+#if defined(JS_CPU_MIPS)
+void
+MacroAssembler::PushRegsInMask(RegisterSet set)
+{
+    int32_t diffG = set.gprs().size() * STACK_SLOT_SIZE;
+    int32_t diffF = set.fpus().size() * sizeof(double);
+
+    reserveStack(diffG);
+    // BackwardIterator in 31.
+    for (GeneralRegisterIterator iter(set.gprs()); iter.more(); iter++) {
+        diffG -= STACK_SLOT_SIZE;
+        storePtr(*iter, Address(StackPointer, diffG));
+    }
+    MOZ_ASSERT(diffG == 0);
+
+    // Double values have to be aligned. We reserve extra space so that we can
+    // start writing from the first aligned location.
+    // We reserve a whole extra double so that the buffer has even size.
+    ma_and(SecondScratchReg, sp, Imm32(~(StackAlignment - 1)));
+    reserveStack(diffF + sizeof(double));
+
+    // ForwardIterator in 31.
+    for (FloatRegisterIterator iter(set.fpus()); iter.more(); iter++) {
+        // Use assembly s.d because we have alligned the stack.
+        // :TODO: (Bug 972836) Fix this once odd regs can be used as
+        // float32 only. For now we skip saving odd regs for O32 ABI.
+
+        // :TODO: (Bug 985881) Make a switch for N32 ABI.
+        if ((*iter).code() % 2 == 0) {
+            as_sd(*iter, SecondScratchReg, -diffF);
+        }
+
+        diffF -= sizeof(double);
+    }
+
+    MOZ_ASSERT(diffF == 0);
+}
+#else  // defined(JS_CPU_MIPS)
 void
 MacroAssembler::PushRegsInMask(RegisterSet set)
 {
@@ -172,10 +210,14 @@
 #endif
     JS_ASSERT(diffF == 0);
 }
+#endif  // defined(JS_CPU_MIPS)
 
 void
 MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore)
 {
+#if defined(JS_CPU_MIPS)
+    MacroAssemblerSpecific::PopRegsInMaskIgnore(set, ignore);
+#else
     int32_t diffG = set.gprs().size() * STACK_SLOT_SIZE;
     int32_t diffF = set.fpus().size() * sizeof(double);
     const int32_t reservedG = diffG;
@@ -219,6 +261,7 @@
         freeStack(reservedG);
     }
     JS_ASSERT(diffG == 0);
+#endif  // defined(JS_CPU_MIPS)
 }
 
 void
@@ -270,8 +313,14 @@
             convertUInt32ToDouble(temp, dest.fpu());
         } else {
             load32(src, dest.gpr());
-            test32(dest.gpr(), dest.gpr());
+
+#if defined(JS_CPU_MIPS)
+            branchTest32(Assembler::Signed, dest.gpr(), dest.gpr(), fail);
+#else
+            test32(dest.gpr(), dest.gpr());  // Resolved mips conflict.
             j(Assembler::Signed, fail);
+#endif
+
         }
         break;
       case TypedArray::TYPE_FLOAT32:
@@ -311,12 +360,20 @@
       case TypedArray::TYPE_UINT32:
         // Don't clobber dest when we could fail, instead use temp.
         load32(src, temp);
+#if defined(JS_CPU_MIPS)
+        // Nothing...
+#else
         test32(temp, temp);
+#endif
         if (allowDouble) {
             // If the value fits in an int32, store an int32 type tag.
             // Else, convert the value to double and box it.
             Label done, isDouble;
+#if defined(JS_CPU_MIPS)
+            branchTest32(Assembler::Signed, temp, temp, &isDouble);
+#else
             j(Assembler::Signed, &isDouble);
+#endif
             {
                 tagValue(JSVAL_TYPE_INT32, temp, dest);
                 jump(&done);
@@ -329,7 +386,11 @@
             bind(&done);
         } else {
             // Bailout if the value does not fit in an int32.
+#if defined(JS_CPU_MIPS)
+            branchTest32(Assembler::Signed, temp, temp, fail);
+#else
             j(Assembler::Signed, fail);
+#endif
             tagValue(JSVAL_TYPE_INT32, temp, dest);
         }
         break;
@@ -642,8 +703,12 @@
     branchTest32(Assembler::Zero, result, atomBit, &notAtom);
     branchTest32(Assembler::Zero, temp, atomBit, &notAtom);
 
+#if defined(JS_CPU_MIPS)
+    cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result);
+#else
     cmpPtr(left, right);
     emitSet(JSOpToCondition(MCompare::Compare_String, op), result);
+#endif
     jump(&done);
 
     bind(&notAtom);
@@ -682,6 +747,7 @@
     bind(&osrRemoved);
 }
 
+#if !defined(JS_CPU_MIPS)
 void
 MacroAssembler::performOsr()
 {
@@ -740,6 +806,7 @@
     enterOsr(calleeToken, code);
     ret();
 }
+#endif  // !defined(JS_CPU_MIPS)
 
 void
 MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
@@ -1251,3 +1318,44 @@
     if (!done())
         gen_.next(types_[i_]);
 }
+
+#if defined(JS_CPU_MIPS)
+void MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch, Label *label)
+{
+    // 16-bit loads are slow and unaligned 32-bit loads may be too so
+    // perform an aligned 32-bit load and adjust the bitmask accordingly.
+    JS_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0);
+    JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
+    JS_STATIC_ASSERT(IS_LITTLE_ENDIAN);
+
+    // Emit code for the following test:
+    //
+    // bool isInterpretedConstructor() const {
+    //     return isInterpreted() && !isFunctionPrototype() && !isArrow() &&
+    //         (!isSelfHostedBuiltin() || isSelfHostedConstructor());
+    // }
+
+    // First, ensure it's a scripted function.
+    load32(Address(fun, JSFunction::offsetOfNargs()), scratch);
+    branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::INTERPRETED << 16), label);
+
+    // Common case: if IS_FUN_PROTO, ARROW and SELF_HOSTED are not set,
+    // the function is an interpreted constructor and we're done.
+    Label done;
+    uint32_t bits = (JSFunction::IS_FUN_PROTO | JSFunction::ARROW | JSFunction::SELF_HOSTED) << 16;
+    branchTest32(Assembler::Zero, scratch, Imm32(bits), &done);
+    {
+        // The callee is either Function.prototype, an arrow function or
+        // self-hosted. None of these are constructible, except self-hosted
+        // constructors, so branch to |label| if SELF_HOSTED_CTOR is not set.
+        branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::SELF_HOSTED_CTOR << 16), label);
+
+#ifdef DEBUG
+        // Function.prototype should not have the SELF_HOSTED_CTOR flag.
+        branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::IS_FUN_PROTO << 16), &done);
+        breakpoint();
+#endif
+    }
+    bind(&done);
+}
+#endif
diff --git a/src/third_party/mozjs/js/src/jit/IonMacroAssembler.h b/src/third_party/mozjs/js/src/jit/IonMacroAssembler.h
index c6c08f2..f1206d8 100644
--- a/src/third_party/mozjs/js/src/jit/IonMacroAssembler.h
+++ b/src/third_party/mozjs/js/src/jit/IonMacroAssembler.h
@@ -18,6 +18,10 @@
 # include "jit/x64/MacroAssembler-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "jit/arm/MacroAssembler-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "jit/mips/MacroAssembler-mips.h"
+#else
+# error "Unknown CPU architecture."
 #endif
 #include "jit/AsmJS.h"
 #include "jit/IonCompartment.h"
@@ -174,6 +178,22 @@
         branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
     }
 
+#if defined(JS_CPU_MIPS)
+    template <typename Value>
+    void branchTestMIRType(Condition cond, const Value &val, MIRType type, Label *label) {
+        switch (type) {
+          case MIRType_Null:      return branchTestNull(cond, val, label);
+          case MIRType_Undefined: return branchTestUndefined(cond, val, label);
+          case MIRType_Boolean:   return branchTestBoolean(cond, val, label);
+          case MIRType_Int32:     return branchTestInt32(cond, val, label);
+          case MIRType_String:    return branchTestString(cond, val, label);
+          case MIRType_Object:    return branchTestObject(cond, val, label);
+          case MIRType_Double:    return branchTestDouble(cond, val, label);
+          default:
+            JS_NOT_REACHED("Bad MIRType");
+        }
+    }
+#else  // defined(JS_CPU_MIPS)
     template <typename Value>
     Condition testMIRType(Condition cond, const Value &val, MIRType type) {
         JS_ASSERT(type == MIRType_Null    || type == MIRType_Undefined  ||
@@ -198,6 +218,7 @@
         cond = testMIRType(cond, val, type);
         j(cond, label);
     }
+#endif  // defined(JS_CPU_MIPS)
 
     // Branches to |label| if |reg| is false. |reg| should be a C++ bool.
     void branchIfFalseBool(const Register &reg, Label *label) {
@@ -358,6 +379,10 @@
         branchTest32(Assembler::NonZero, address, Imm32(bit), label);
     }
 
+#if defined(JS_CPU_MIPS)
+    void branchIfNotInterpretedConstructor(Register fun, Register scratch, Label *label);
+#endif
+
     using MacroAssemblerSpecific::Push;
 
     void Push(jsid id, Register scratchReg) {
@@ -721,6 +746,23 @@
         return ret;
     }
 
+#if defined(JS_CPU_MIPS)
+    void branchTestObjectTruthy(bool truthy, Register objReg, Register scratch,
+                                Label *slowCheck, Label *checked)
+    {
+        // The branches to out-of-line code here implement a conservative version
+        // of the JSObject::isWrapper test performed in EmulatesUndefined.  If none
+        // of the branches are taken, we can check class flags directly.
+        loadObjClass(objReg, scratch);
+
+        branchPtr(Assembler::Equal, scratch, ImmWord(&ObjectProxyClass), slowCheck);
+        branchPtr(Assembler::Equal, scratch, ImmWord(&OuterWindowProxyClass), slowCheck);
+        branchPtr(Assembler::Equal, scratch, ImmWord(&FunctionProxyClass), slowCheck);
+
+        Condition cond = truthy ? Assembler::Zero : Assembler::NonZero;
+        branchTest32(cond, Address(scratch, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED), checked);
+    }
+#else  // defined(JS_CPU_MIPS)
     Condition branchTestObjectTruthy(bool truthy, Register objReg, Register scratch,
                                      Label *slowCheck)
     {
@@ -735,6 +777,7 @@
         test32(Address(scratch, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED));
         return truthy ? Assembler::Zero : Assembler::NonZero;
     }
+#endif  // defined(JS_CPU_MIPS)
 
     void tagCallee(Register callee, ExecutionMode mode);
     void clearCalleeTag(Register callee, ExecutionMode mode);
diff --git a/src/third_party/mozjs/js/src/jit/IonSpewer.cpp b/src/third_party/mozjs/js/src/jit/IonSpewer.cpp
index 1c67001..5a2e306 100644
--- a/src/third_party/mozjs/js/src/jit/IonSpewer.cpp
+++ b/src/third_party/mozjs/js/src/jit/IonSpewer.cpp
@@ -219,7 +219,7 @@
     if (LoggingChecked)
         return;
     LoggingChecked = true;
-    const char *env = getenv("IONFLAGS");
+    const char* env = NULL;
     if (!env)
         return;
     if (strstr(env, "help")) {
@@ -261,6 +261,7 @@
         exit(0);
         /*NOTREACHED*/
     }
+
     if (ContainsFlag(env, "aborts"))
         EnableChannel(IonSpew_Abort);
     if (ContainsFlag(env, "alias"))
diff --git a/src/third_party/mozjs/js/src/jit/LIR-Common.h b/src/third_party/mozjs/js/src/jit/LIR-Common.h
index fb43cfc..877cb27 100644
--- a/src/third_party/mozjs/js/src/jit/LIR-Common.h
+++ b/src/third_party/mozjs/js/src/jit/LIR-Common.h
@@ -1495,6 +1495,43 @@
     }
 };
 
+#if defined(JS_CPU_MIPS)
+class LCompareDAndBranch : public LControlInstructionHelper<2, 2, 0>
+{
+    MCompare *cmpMir_;
+
+  public:
+    LIR_HEADER(CompareDAndBranch)
+    LCompareDAndBranch(MCompare *cmpMir, const LAllocation &left, const LAllocation &right,
+                       MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+      : cmpMir_(cmpMir)
+    {
+        setOperand(0, left);
+        setOperand(1, right);
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    MBasicBlock *ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock *ifFalse() const {
+        return getSuccessor(1);
+    }
+    const LAllocation *left() {
+        return getOperand(0);
+    }
+    const LAllocation *right() {
+        return getOperand(1);
+    }
+    MTest *mir() const {
+        return mir_->toTest();
+    }
+    MCompare *cmpMir() const {
+        return cmpMir_;
+    }
+};
+#else  // defined(JS_CPU_MIPS)
 class LCompareDAndBranch : public LControlInstructionHelper<2, 2, 0>
 {
   public:
@@ -1524,6 +1561,7 @@
         return mir_->toCompare();
     }
 };
+#endif  // defined(JS_CPU_MIPS)
 
 class LCompareS : public LInstructionHelper<1, 2, 1>
 {
diff --git a/src/third_party/mozjs/js/src/jit/LIR.h b/src/third_party/mozjs/js/src/jit/LIR.h
index 436faf0..27e90ad 100644
--- a/src/third_party/mozjs/js/src/jit/LIR.h
+++ b/src/third_party/mozjs/js/src/jit/LIR.h
@@ -1440,6 +1440,10 @@
 # include "shared/LIR-x86-shared.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/LIR-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/LIR-mips.h"
+#else
+# error "Unknown CPU architecture."
 #endif
 
 #undef LIR_HEADER
diff --git a/src/third_party/mozjs/js/src/jit/LOpcodes.h b/src/third_party/mozjs/js/src/jit/LOpcodes.h
index dfbd53a..f9539b5 100644
--- a/src/third_party/mozjs/js/src/jit/LOpcodes.h
+++ b/src/third_party/mozjs/js/src/jit/LOpcodes.h
@@ -247,6 +247,10 @@
 # include "x64/LOpcodes-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/LOpcodes-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/LOpcodes-mips.h"
+#else
+# error "Unknown CPU architecture."
 #endif
 
 #define LIR_OPCODE_LIST(_)          \
diff --git a/src/third_party/mozjs/js/src/jit/Lowering.cpp b/src/third_party/mozjs/js/src/jit/Lowering.cpp
index 19e7425..a7db9c6 100644
--- a/src/third_party/mozjs/js/src/jit/Lowering.cpp
+++ b/src/third_party/mozjs/js/src/jit/Lowering.cpp
@@ -657,7 +657,11 @@
         if (comp->isDoubleComparison()) {
             LAllocation lhs = useRegister(left);
             LAllocation rhs = useRegister(right);
+#if defined(JS_CPU_MIPS)
+            LCompareDAndBranch *lir = new LCompareDAndBranch(comp, lhs, rhs, ifTrue, ifFalse);
+#else
             LCompareDAndBranch *lir = new LCompareDAndBranch(lhs, rhs, ifTrue, ifFalse);
+#endif
             return add(lir, comp);
         }
 
@@ -2260,7 +2264,12 @@
         return assignSafepoint(lir, ins);
     }
 
+#if defined(JS_CPU_MIPS)
+    LGetPropertyCacheT *lir = new LGetPropertyCacheT(useRegister(ins->object()),
+                                                              tempForDispatchCache(ins->type()));
+#else
     LGetPropertyCacheT *lir = newLGetPropertyCacheT(ins);
+#endif
     if (!define(lir, ins))
         return false;
     return assignSafepoint(lir, ins);
diff --git a/src/third_party/mozjs/js/src/jit/Lowering.h b/src/third_party/mozjs/js/src/jit/Lowering.h
index 3d67a2d..b2df2ce 100644
--- a/src/third_party/mozjs/js/src/jit/Lowering.h
+++ b/src/third_party/mozjs/js/src/jit/Lowering.h
@@ -20,6 +20,8 @@
 # include "x64/Lowering-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/Lowering-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/Lowering-mips.h"
 #else
 # error "CPU!"
 #endif
diff --git a/src/third_party/mozjs/js/src/jit/MIR.h b/src/third_party/mozjs/js/src/jit/MIR.h
index ef1df1b..ad4a52b 100644
--- a/src/third_party/mozjs/js/src/jit/MIR.h
+++ b/src/third_party/mozjs/js/src/jit/MIR.h
@@ -3476,6 +3476,25 @@
 
     bool fallible();
     bool truncate();
+
+#if defined(JS_CPU_MIPS)
+    // isTruncatedDirectly_ defaults to false in SpiderMonkey 31.
+    bool isTruncatedIndirectly() const {
+        return false;
+    }
+    bool canTruncateInfinities() const {
+        return isTruncated();
+    }
+    bool canTruncateRemainder() const {
+        return isTruncated();
+    }
+    bool canTruncateOverflow() const {
+        return isTruncated() || isTruncatedIndirectly();
+    }
+    bool canTruncateNegativeZero() const {
+        return isTruncated() || isTruncatedIndirectly();
+    }
+#endif
 };
 
 class MMod : public MBinaryArithInstruction
@@ -3511,6 +3530,29 @@
 
     void computeRange();
     bool truncate();
+
+#if defined(JS_CPU_MIPS)
+    // Can be negativeDividend defaults to true in SpiderMonkey31 and is set
+    // to 0 in several areas that analyze the code.  Since false seems to be
+    // more restrictive, we return that here.  This seems to be ok so far,
+    // however we should be careful when this code is run.
+    bool canBeNegativeDividend() const {
+        JS_ASSERT(specialization_ == MIRType_Int32);
+        SB_LOG(WARNING) << "canBeNegativeDividend() returning false";
+        return false;
+    }
+
+    bool
+    canBeDivideByZero() const {
+        JS_ASSERT(specialization_ == MIRType_Int32);
+        return !rhs()->isConstant() || rhs()->toConstant()->value().toInt32() == 0;
+    }
+
+    // unsigned_ defaults to false in SpiderMonkey31.
+    bool isUnsigned() const {
+        return false;
+    }
+#endif
 };
 
 class MConcat
diff --git a/src/third_party/mozjs/js/src/jit/MoveEmitter.h b/src/third_party/mozjs/js/src/jit/MoveEmitter.h
index b861ac0..792b8ef 100644
--- a/src/third_party/mozjs/js/src/jit/MoveEmitter.h
+++ b/src/third_party/mozjs/js/src/jit/MoveEmitter.h
@@ -11,6 +11,8 @@
 # include "jit/shared/MoveEmitter-x86-shared.h"
 #elif defined(JS_CPU_ARM)
 # include "jit/arm/MoveEmitter-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "jit/mips/MoveEmitter-mips.h"
 #else
 # error "CPU Not Supported"
 #endif
diff --git a/src/third_party/mozjs/js/src/jit/MoveResolver.h b/src/third_party/mozjs/js/src/jit/MoveResolver.h
index 228aba4..4705dc8 100644
--- a/src/third_party/mozjs/js/src/jit/MoveResolver.h
+++ b/src/third_party/mozjs/js/src/jit/MoveResolver.h
@@ -74,6 +74,11 @@
         bool isEffectiveAddress() const {
             return kind_ == EFFECTIVE_ADDRESS;
         }
+#if defined(JS_CPU_MIPS)
+        bool isMemoryOrEffectiveAddress() const {
+            return isMemory() || isEffectiveAddress();
+        }
+#endif
         Register reg() const {
             JS_ASSERT(isGeneralReg());
             return Register::FromCode(code_);
diff --git a/src/third_party/mozjs/js/src/jit/ParallelFunctions.cpp b/src/third_party/mozjs/js/src/jit/ParallelFunctions.cpp
index ba1513b..f6e73ef 100644
--- a/src/third_party/mozjs/js/src/jit/ParallelFunctions.cpp
+++ b/src/third_party/mozjs/js/src/jit/ParallelFunctions.cpp
@@ -81,7 +81,7 @@
 
     if (traceMode == NotSet) {
         // Racy, but that's ok.
-        const char *env = getenv("IONFLAGS");
+        const char* env = "";
         if (strstr(env, "trace-all"))
             traceMode = All;
         else
diff --git a/src/third_party/mozjs/js/src/jit/RegisterAllocator.h b/src/third_party/mozjs/js/src/jit/RegisterAllocator.h
index c6199a6..b4f76cd 100644
--- a/src/third_party/mozjs/js/src/jit/RegisterAllocator.h
+++ b/src/third_party/mozjs/js/src/jit/RegisterAllocator.h
@@ -309,7 +309,7 @@
 #if defined(JS_CPU_X64)
         if (mir->compilingAsmJS())
             allRegisters_.take(AnyRegister(HeapReg));
-#elif defined(JS_CPU_ARM)
+#elif defined(JS_CPU_ARM) || defined(JS_CPU_MIPS)
         if (mir->compilingAsmJS()) {
             allRegisters_.take(AnyRegister(HeapReg));
             allRegisters_.take(AnyRegister(GlobalReg));
diff --git a/src/third_party/mozjs/js/src/jit/RegisterSets.h b/src/third_party/mozjs/js/src/jit/RegisterSets.h
index a181b61..f6f860f 100644
--- a/src/third_party/mozjs/js/src/jit/RegisterSets.h
+++ b/src/third_party/mozjs/js/src/jit/RegisterSets.h
@@ -445,12 +445,17 @@
     uint32_t bits() const {
         return bits_;
     }
+
     uint32_t size() const {
+#if defined(JS_CPU_MIPS)
+        return __builtin_popcount(bits_);
+#else
         uint32_t sum2  = (bits_ & 0x55555555) + ((bits_ & 0xaaaaaaaa) >> 1);
         uint32_t sum4  = (sum2  & 0x33333333) + ((sum2  & 0xcccccccc) >> 2);
         uint32_t sum8  = (sum4  & 0x0f0f0f0f) + ((sum4  & 0xf0f0f0f0) >> 4);
         uint32_t sum16 = (sum8  & 0x00ff00ff) + ((sum8  & 0xff00ff00) >> 8);
         return sum16;
+#endif
     }
     bool operator ==(const TypedRegisterSet<T> &other) const {
         return other.bits_ == bits_;
@@ -794,6 +799,12 @@
     {}
 #endif
 
+#if defined(JS_CPU_MIPS)
+    explicit AsmJSHeapAccess(uint32_t offset)
+      : offset_(offset)
+    {}
+#endif
+
     uint32_t offset() const { return offset_; }
     unsigned opLength() const { return opLength_; }
     bool isLoad() const { return loadedReg_ != UINT8_MAX; }
diff --git a/src/third_party/mozjs/js/src/jit/Registers.h b/src/third_party/mozjs/js/src/jit/Registers.h
index 6997c6d..52b53ac 100644
--- a/src/third_party/mozjs/js/src/jit/Registers.h
+++ b/src/third_party/mozjs/js/src/jit/Registers.h
@@ -15,6 +15,10 @@
 # include "x64/Architecture-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "arm/Architecture-arm.h"
+#elif defined(JS_CPU_MIPS)
+# include "mips/Architecture-mips.h"
+#else
+# error "Unknown CPU architecture."
 #endif
 #include "FixedArityList.h"
 
diff --git a/src/third_party/mozjs/js/src/jit/Snapshots.cpp b/src/third_party/mozjs/js/src/jit/Snapshots.cpp
index 80d219d..fa3bb73 100644
--- a/src/third_party/mozjs/js/src/jit/Snapshots.cpp
+++ b/src/third_party/mozjs/js/src/jit/Snapshots.cpp
@@ -344,7 +344,11 @@
 {
     JS_ASSERT(uint32_t(type) <= MAX_TYPE_FIELD_VALUE);
     JS_ASSERT(uint32_t(regCode) <= MAX_REG_FIELD_VALUE);
+#if defined(JS_CPU_MIPS)
+    // Note: This static assert fails on MIPS, but seems safe.
+#else
     JS_STATIC_ASSERT(Registers::Total < MIN_REG_FIELD_ESC);
+#endif
 
     uint8_t byte = uint32_t(type) | (regCode << 3);
     writer_.writeByte(byte);
diff --git a/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp
index d0d61ff..639b567 100644
--- a/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp
+++ b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.cpp
@@ -47,30 +47,6 @@
     return js::jit::GetMIPSFlags() & HWCAP_FPU;
 }
 
-Registers::Code
-Registers::FromName(const char *name)
-{
-    for (size_t i = 0; i < Total; i++) {
-        if (strcmp(GetName(i), name) == 0)
-            return Code(i);
-    }
-
-    return Invalid;
-}
-
-FloatRegisters::Code
-FloatRegisters::FromName(const char *name)
-{
-    for (size_t i = 0; i < Total; i++) {
-        if (strcmp(GetName(i), name) == 0)
-            return Code(i);
-    }
-
-    return Invalid;
-}
-
-
-
 } // namespace ion
 } // namespace js
 
diff --git a/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h
index 0bc7346..5b36a12 100644
--- a/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/Architecture-mips.h
@@ -15,11 +15,22 @@
 // gcc appears to use _mips_hard_float to denote
 // that the target is a hard-float target.
 #ifdef _mips_hard_float
-#define JS_CODEGEN_MIPS_HARDFP
+#define JS_CPU_MIPS_HARDFP
 #endif
 namespace js {
 namespace jit {
 
+// In bytes: slots needed for potential memory->memory move spills.
+//   +8 for cycles
+//   +4 for gpr spills
+//   +8 for double spills
+static const ptrdiff_t STACK_SLOT_SIZE = 4;
+// static const uint32_t STACK_SLOT_SIZE = 4;
+static const uint32_t DOUBLE_STACK_ALIGNMENT = 2;
+
+// An offset that is illegal for a local variable's stack allocation.
+static const int32_t INVALID_STACK_SLOT = -1;
+
 // Shadow stack space is not required on MIPS.
 static const uint32_t ShadowStackSpace = 0;
 
@@ -36,7 +47,7 @@
 class Registers
 {
   public:
-    enum RegisterID {
+    typedef enum RegisterID {
         r0 = 0,
         r1,
         r2,
@@ -102,7 +113,7 @@
         fp = r30,
         ra = r31,
         invalid_reg
-    };
+    } RegisterID;
     typedef RegisterID Code;
 
     static const char *GetName(Code code) {
@@ -117,8 +128,6 @@
         return GetName(Code(i));
     }
 
-    static Code FromName(const char *name);
-
     static const Code StackPointer = sp;
     static const Code Invalid = invalid_reg;
 
@@ -144,6 +153,8 @@
         (1 << Registers::t6) |
         (1 << Registers::t7);
 
+    // We use this constant to save registers when entering functions. This
+    // is why $ra is added here even though it is not "Non Volatile".
     static const uint32_t NonVolatileMask =
         (1 << Registers::s0) |
         (1 << Registers::s1) |
@@ -152,7 +163,8 @@
         (1 << Registers::s4) |
         (1 << Registers::s5) |
         (1 << Registers::s6) |
-        (1 << Registers::s7);
+        (1 << Registers::s7) |
+        (1 << Registers::ra);
 
     static const uint32_t WrapperMask =
         VolatileMask |         // = arguments
@@ -176,8 +188,8 @@
 
     // Registers returned from a JS -> JS call.
     static const uint32_t JSCallMask =
-        (1 << Registers::v0) |
-        (1 << Registers::v1);
+        (1 << Registers::a2) |
+        (1 << Registers::a3);
 
     // Registers returned from a JS -> C call.
     static const uint32_t CallMask =
@@ -254,8 +266,6 @@
         return GetName(Code(i));
     }
 
-    static Code FromName(const char *name);
-
     static const Code Invalid = invalid_freg;
 
     static const uint32_t Total = 32;
diff --git a/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp
index 57fe57b..7b53b33 100644
--- a/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp
+++ b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.cpp
@@ -14,7 +14,7 @@
 
 #include "assembler/jit/ExecutableAllocator.h"
 #include "gc/Marking.h"
-#include "jit/JitCompartment.h"
+#include "jit/IonCompartment.h"
 
 using mozilla::DebugOnly;
 
@@ -30,7 +30,7 @@
 ABIArg
 ABIArgGenerator::next(MIRType type)
 {
-    MOZ_ASSUME_UNREACHABLE("NYI");
+    JS_NOT_REACHED("NYI");
     return ABIArg();
 }
 const Register ABIArgGenerator::NonArgReturnVolatileReg0 = t0;
@@ -126,7 +126,7 @@
 
     Assembler::updateLuiOriValue(inst1, inst2, (uint32_t)label.raw());
 
-    AutoFlushICache::flush(uintptr_t(inst1), 8);
+    AutoFlushCache::updateTop(uintptr_t(inst1), 8);
 }
 
 void
@@ -150,7 +150,7 @@
         updateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value);
     }
 
-    AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
+    AutoFlushCache::updateTop((uintptr_t)buffer, m_buffer.size());
 }
 
 uint32_t
@@ -166,7 +166,7 @@
 }
 
 uint8_t *
-Assembler::PatchableJumpAddress(JitCode *code, uint32_t pe_)
+Assembler::PatchableJumpAddress(IonCode *code, uint32_t pe_)
 {
     return code->raw() + pe_;
 }
@@ -201,20 +201,20 @@
     return Assembler::extractLuiOriValue(inst, inst->next());
 }
 
-static JitCode *
+static IonCode *
 CodeFromJump(Instruction *jump)
 {
     uint8_t *target = (uint8_t *)Assembler::extractLuiOriValue(jump, jump->next());
-    return JitCode::FromExecutable(target);
+    return IonCode::FromExecutable(target);
 }
 
 void
-Assembler::TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader)
+Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader)
 {
     RelocationIterator iter(reader);
     while (iter.read()) {
-        JitCode *child = CodeFromJump((Instruction *)(code->raw() + iter.offset()));
-        MarkJitCodeUnbarriered(trc, &child, "rel32");
+        IonCode *child = CodeFromJump((Instruction *)(code->raw() + iter.offset()));
+        MarkIonCodeUnbarriered(trc, &child, "rel32");
     }
 }
 
@@ -246,7 +246,7 @@
 }
 
 void
-Assembler::TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader)
+Assembler::TraceDataRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader)
 {
     ::TraceDataRelocations(trc, code->raw(), reader);
 }
@@ -277,10 +277,10 @@
 {
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch &rp = jumps_[i];
-        if (rp.kind == Relocation::JITCODE) {
-            JitCode *code = JitCode::FromExecutable((uint8_t *)rp.target);
-            MarkJitCodeUnbarriered(trc, &code, "masmrel32");
-            JS_ASSERT(code == JitCode::FromExecutable((uint8_t *)rp.target));
+        if (rp.kind == Relocation::IONCODE) {
+            IonCode *code = IonCode::FromExecutable((uint8_t *)rp.target);
+            MarkIonCodeUnbarriered(trc, &code, "masmrel32");
+            JS_ASSERT(code == IonCode::FromExecutable((uint8_t *)rp.target));
         }
     }
     if (dataRelocations_.length()) {
@@ -346,7 +346,7 @@
       case NotSigned:
         return Signed;
       default:
-        MOZ_ASSUME_UNREACHABLE("unexpected condition");
+        JS_NOT_REACHED("unexpected condition");
         return Equal;
     }
 }
@@ -384,7 +384,7 @@
       case DoubleLessThanOrEqualOrUnordered:
         return DoubleGreaterThan;
       default:
-        MOZ_ASSUME_UNREACHABLE("unexpected condition");
+        JS_NOT_REACHED("unexpected condition");
         return DoubleEqual;
     }
 }
@@ -450,7 +450,7 @@
 BufferOffset
 Assembler::writeInst(uint32_t x, uint32_t *dest)
 {
-    if (dest == nullptr)
+    if (dest == NULL)
         return m_buffer.putInt(x);
 
     writeInstStatic(x, dest);
@@ -460,7 +460,7 @@
 void
 Assembler::writeInstStatic(uint32_t x, uint32_t *dest)
 {
-    JS_ASSERT(dest != nullptr);
+    JS_ASSERT(dest != NULL);
     *dest = x;
 }
 
@@ -585,7 +585,7 @@
       case Assembler::LessThanOrEqual:
         return InstImm(op_blez, s, zero, BOffImm16(0));
       default:
-        MOZ_ASSUME_UNREACHABLE("Condition not supported.");
+        JS_NOT_REACHED("Condition not supported.");
     }
 }
 
@@ -1336,7 +1336,7 @@
     inst[3] = InstNOP();
 
     // Ensure everyone sees the code that was just written into memory.
-    AutoFlushICache::flush(uintptr_t(inst), patchWrite_NearCallSize());
+    AutoFlushCache::updateTop(uintptr_t(inst), patchWrite_NearCallSize());
 }
 
 uint32_t
@@ -1371,8 +1371,8 @@
 }
 
 void
-Assembler::patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
-                                   PatchedImmPtr expectedValue)
+Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue,
+                                   ImmWord expectedValue)
 {
     Instruction *inst = (Instruction *) label.raw();
 
@@ -1383,14 +1383,7 @@
     // Replace with new value
     Assembler::updateLuiOriValue(inst, inst->next(), uint32_t(newValue.value));
 
-    AutoFlushICache::flush(uintptr_t(inst), 8);
-}
-
-void
-Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue)
-{
-    patchDataWithValueCheck(label, PatchedImmPtr(newValue.value),
-                            PatchedImmPtr(expectedValue.value));
+    AutoFlushCache::updateTop(uintptr_t(inst), 8);
 }
 
 // This just stomps over memory with 32 bits of raw data. Its purpose is to
@@ -1412,7 +1405,7 @@
 Assembler::nextInstruction(uint8_t *inst_, uint32_t *count)
 {
     Instruction *inst = reinterpret_cast<Instruction*>(inst_);
-    if (count != nullptr)
+    if (count != NULL)
         *count += sizeof(Instruction);
     return reinterpret_cast<uint8_t*>(inst->next());
 }
@@ -1457,7 +1450,7 @@
             return branch;
         }
 
-        MOZ_ASSUME_UNREACHABLE("Error creating long branch.");
+        JS_NOT_REACHED("Error creating long branch.");
         return branch;
 
       case op_cop1:
@@ -1472,7 +1465,7 @@
         return branch;
     }
 
-    MOZ_ASSUME_UNREACHABLE("Error creating long branch.");
+    JS_NOT_REACHED("Error creating long branch.");
     return branch;
 }
 
@@ -1485,7 +1478,7 @@
     // We converted beq to andi, so now we restore it.
     inst->setOpcode(op_beq);
 
-    AutoFlushICache::flush(uintptr_t(inst), 4);
+    AutoFlushCache::updateTop(uintptr_t(inst), 4);
 }
 
 void
@@ -1498,7 +1491,7 @@
     // Replace "beq $zero, $zero, offset" with "andi $zero, $zero, offset"
     inst->setOpcode(op_andi);
 
-    AutoFlushICache::flush(uintptr_t(inst), 4);
+    AutoFlushCache::updateTop(uintptr_t(inst), 4);
 }
 
 void
@@ -1520,10 +1513,12 @@
         *i2 = nop;
     }
 
-    AutoFlushICache::flush(uintptr_t(i2), 4);
+    AutoFlushCache::updateTop(uintptr_t(i2), 4);
 }
 
 void Assembler::updateBoundsCheck(uint32_t heapSize, Instruction *inst)
 {
-    MOZ_ASSUME_UNREACHABLE("NYI");
+    JS_NOT_REACHED("NYI");
 }
+
+
diff --git a/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h
index c5ce1f1..3514e2c 100644
--- a/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/Assembler-mips.h
@@ -7,9 +7,11 @@
 #ifndef jit_mips_Assembler_mips_h
 #define jit_mips_Assembler_mips_h
 
-#include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/Util.h"
+
+#include "jit/shared/Assembler-shared.h"
 
 #include "jit/CompactBuffer.h"
 #include "jit/IonCode.h"
@@ -21,59 +23,60 @@
 namespace js {
 namespace jit {
 
-static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero };
-static MOZ_CONSTEXPR_VAR Register at = { Registers::at };
-static MOZ_CONSTEXPR_VAR Register v0 = { Registers::v0 };
-static MOZ_CONSTEXPR_VAR Register v1 = { Registers::v1 };
-static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 };
-static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 };
-static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 };
-static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 };
-static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 };
-static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 };
-static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 };
-static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 };
-static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 };
-static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 };
-static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 };
-static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 };
-static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 };
-static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 };
-static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 };
-static MOZ_CONSTEXPR_VAR Register s3 = { Registers::s3 };
-static MOZ_CONSTEXPR_VAR Register s4 = { Registers::s4 };
-static MOZ_CONSTEXPR_VAR Register s5 = { Registers::s5 };
-static MOZ_CONSTEXPR_VAR Register s6 = { Registers::s6 };
-static MOZ_CONSTEXPR_VAR Register s7 = { Registers::s7 };
-static MOZ_CONSTEXPR_VAR Register t8 = { Registers::t8 };
-static MOZ_CONSTEXPR_VAR Register t9 = { Registers::t9 };
-static MOZ_CONSTEXPR_VAR Register k0 = { Registers::k0 };
-static MOZ_CONSTEXPR_VAR Register k1 = { Registers::k1 };
-static MOZ_CONSTEXPR_VAR Register gp = { Registers::gp };
-static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp };
-static MOZ_CONSTEXPR_VAR Register fp = { Registers::fp };
-static MOZ_CONSTEXPR_VAR Register ra = { Registers::ra };
+static const MOZ_CONSTEXPR Register zero = { Registers::zero };
+static const MOZ_CONSTEXPR Register at = { Registers::at };
+static const MOZ_CONSTEXPR Register v0 = { Registers::v0 };
+static const MOZ_CONSTEXPR Register v1 = { Registers::v1 };
+static const MOZ_CONSTEXPR Register a0 = { Registers::a0 };
+static const MOZ_CONSTEXPR Register a1 = { Registers::a1 };
+static const MOZ_CONSTEXPR Register a2 = { Registers::a2 };
+static const MOZ_CONSTEXPR Register a3 = { Registers::a3 };
+static const MOZ_CONSTEXPR Register t0 = { Registers::t0 };
+static const MOZ_CONSTEXPR Register t1 = { Registers::t1 };
+static const MOZ_CONSTEXPR Register t2 = { Registers::t2 };
+static const MOZ_CONSTEXPR Register t3 = { Registers::t3 };
+static const MOZ_CONSTEXPR Register t4 = { Registers::t4 };
+static const MOZ_CONSTEXPR Register t5 = { Registers::t5 };
+static const MOZ_CONSTEXPR Register t6 = { Registers::t6 };
+static const MOZ_CONSTEXPR Register t7 = { Registers::t7 };
+static const MOZ_CONSTEXPR Register s0 = { Registers::s0 };
+static const MOZ_CONSTEXPR Register s1 = { Registers::s1 };
+static const MOZ_CONSTEXPR Register s2 = { Registers::s2 };
+static const MOZ_CONSTEXPR Register s3 = { Registers::s3 };
+static const MOZ_CONSTEXPR Register s4 = { Registers::s4 };
+static const MOZ_CONSTEXPR Register s5 = { Registers::s5 };
+static const MOZ_CONSTEXPR Register s6 = { Registers::s6 };
+static const MOZ_CONSTEXPR Register s7 = { Registers::s7 };
+static const MOZ_CONSTEXPR Register t8 = { Registers::t8 };
+static const MOZ_CONSTEXPR Register t9 = { Registers::t9 };
+static const MOZ_CONSTEXPR Register k0 = { Registers::k0 };
+static const MOZ_CONSTEXPR Register k1 = { Registers::k1 };
+static const MOZ_CONSTEXPR Register gp = { Registers::gp };
+static const MOZ_CONSTEXPR Register sp = { Registers::sp };
+static const MOZ_CONSTEXPR Register fp = { Registers::fp };
+static const MOZ_CONSTEXPR Register ra = { Registers::ra };
 
-static MOZ_CONSTEXPR_VAR Register ScratchRegister = at;
-static MOZ_CONSTEXPR_VAR Register SecondScratchReg = t8;
+static const MOZ_CONSTEXPR Register ScratchRegister = at;
+static const MOZ_CONSTEXPR Register SecondScratchReg = t8;
 
 // Use arg reg from EnterJIT function as OsrFrameReg.
-static MOZ_CONSTEXPR_VAR Register OsrFrameReg = a3;
-static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = s3;
-static MOZ_CONSTEXPR_VAR Register CallTempReg0 = t0;
-static MOZ_CONSTEXPR_VAR Register CallTempReg1 = t1;
-static MOZ_CONSTEXPR_VAR Register CallTempReg2 = t2;
-static MOZ_CONSTEXPR_VAR Register CallTempReg3 = t3;
-static MOZ_CONSTEXPR_VAR Register CallTempReg4 = t4;
-static MOZ_CONSTEXPR_VAR Register CallTempReg5 = t5;
+static const MOZ_CONSTEXPR Register OsrFrameReg = a3;
+static const MOZ_CONSTEXPR Register ArgumentsRectifierReg = s3;
+static const MOZ_CONSTEXPR Register CallTempReg0 = t0;
+static const MOZ_CONSTEXPR Register CallTempReg1 = t1;
+static const MOZ_CONSTEXPR Register CallTempReg2 = t2;
+static const MOZ_CONSTEXPR Register CallTempReg3 = t3;
+static const MOZ_CONSTEXPR Register CallTempReg4 = t4;
+static const MOZ_CONSTEXPR Register CallTempReg5 = t5;
+static const MOZ_CONSTEXPR Register CallTempReg6 = t6;
 
-static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0;
-static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1;
-static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2;
-static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3;
-static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin
-static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin
-static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 };
+static const MOZ_CONSTEXPR Register IntArgReg0 = a0;
+static const MOZ_CONSTEXPR Register IntArgReg1 = a1;
+static const MOZ_CONSTEXPR Register IntArgReg2 = a2;
+static const MOZ_CONSTEXPR Register IntArgReg3 = a3;
+static const MOZ_CONSTEXPR Register GlobalReg = s6; // used by Odin
+static const MOZ_CONSTEXPR Register HeapReg = s7; // used by Odin
+static const MOZ_CONSTEXPR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 };
 static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs);
 
 class ABIArgGenerator
@@ -98,38 +101,38 @@
     static const Register NonArgReturnVolatileReg1;
 };
 
-static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1;
+static const MOZ_CONSTEXPR Register PreBarrierReg = a1;
 
-static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
-static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg };
+static const MOZ_CONSTEXPR Register InvalidReg = { Registers::invalid_reg };
+static const MOZ_CONSTEXPR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg };
 
-static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = v1;
-static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = v0;
-static MOZ_CONSTEXPR_VAR Register StackPointer = sp;
-static MOZ_CONSTEXPR_VAR Register FramePointer = fp;
-static MOZ_CONSTEXPR_VAR Register ReturnReg = v0;
-static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::f0 };
-static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::f18 };
-static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloatReg = { FloatRegisters::f16 };
+static const MOZ_CONSTEXPR Register JSReturnReg_Type = v1;
+static const MOZ_CONSTEXPR Register JSReturnReg_Data = v0;
+static const MOZ_CONSTEXPR Register StackPointer = sp;
+static const MOZ_CONSTEXPR Register FramePointer = fp;
+static const MOZ_CONSTEXPR Register ReturnReg = v0;
+static const MOZ_CONSTEXPR FloatRegister ReturnFloatReg = { FloatRegisters::f0 };
+static const MOZ_CONSTEXPR FloatRegister ScratchFloatReg = { FloatRegisters::f18 };
+static const MOZ_CONSTEXPR FloatRegister SecondScratchFloatReg = { FloatRegisters::f16 };
 
-static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::f30 };
+static const MOZ_CONSTEXPR FloatRegister NANReg = { FloatRegisters::f30 };
 
-static MOZ_CONSTEXPR_VAR FloatRegister f0  = {FloatRegisters::f0};
-static MOZ_CONSTEXPR_VAR FloatRegister f2  = {FloatRegisters::f2};
-static MOZ_CONSTEXPR_VAR FloatRegister f4  = {FloatRegisters::f4};
-static MOZ_CONSTEXPR_VAR FloatRegister f6  = {FloatRegisters::f6};
-static MOZ_CONSTEXPR_VAR FloatRegister f8  = {FloatRegisters::f8};
-static MOZ_CONSTEXPR_VAR FloatRegister f10 = {FloatRegisters::f10};
-static MOZ_CONSTEXPR_VAR FloatRegister f12 = {FloatRegisters::f12};
-static MOZ_CONSTEXPR_VAR FloatRegister f14 = {FloatRegisters::f14};
-static MOZ_CONSTEXPR_VAR FloatRegister f16 = {FloatRegisters::f16};
-static MOZ_CONSTEXPR_VAR FloatRegister f18 = {FloatRegisters::f18};
-static MOZ_CONSTEXPR_VAR FloatRegister f20 = {FloatRegisters::f20};
-static MOZ_CONSTEXPR_VAR FloatRegister f22 = {FloatRegisters::f22};
-static MOZ_CONSTEXPR_VAR FloatRegister f24 = {FloatRegisters::f24};
-static MOZ_CONSTEXPR_VAR FloatRegister f26 = {FloatRegisters::f26};
-static MOZ_CONSTEXPR_VAR FloatRegister f28 = {FloatRegisters::f28};
-static MOZ_CONSTEXPR_VAR FloatRegister f30 = {FloatRegisters::f30};
+static const MOZ_CONSTEXPR FloatRegister f0  = {FloatRegisters::f0};
+static const MOZ_CONSTEXPR FloatRegister f2  = {FloatRegisters::f2};
+static const MOZ_CONSTEXPR FloatRegister f4  = {FloatRegisters::f4};
+static const MOZ_CONSTEXPR FloatRegister f6  = {FloatRegisters::f6};
+static const MOZ_CONSTEXPR FloatRegister f8  = {FloatRegisters::f8};
+static const MOZ_CONSTEXPR FloatRegister f10 = {FloatRegisters::f10};
+static const MOZ_CONSTEXPR FloatRegister f12 = {FloatRegisters::f12};
+static const MOZ_CONSTEXPR FloatRegister f14 = {FloatRegisters::f14};
+static const MOZ_CONSTEXPR FloatRegister f16 = {FloatRegisters::f16};
+static const MOZ_CONSTEXPR FloatRegister f18 = {FloatRegisters::f18};
+static const MOZ_CONSTEXPR FloatRegister f20 = {FloatRegisters::f20};
+static const MOZ_CONSTEXPR FloatRegister f22 = {FloatRegisters::f22};
+static const MOZ_CONSTEXPR FloatRegister f24 = {FloatRegisters::f24};
+static const MOZ_CONSTEXPR FloatRegister f26 = {FloatRegisters::f26};
+static const MOZ_CONSTEXPR FloatRegister f28 = {FloatRegisters::f28};
+static const MOZ_CONSTEXPR FloatRegister f30 = {FloatRegisters::f30};
 
 // MIPS CPUs can only load multibyte data that is "naturally"
 // four-byte-aligned, sp register should be eight-byte-aligned.
@@ -371,9 +374,6 @@
     ff_c_ule_fmt   = 55,
 };
 
-class MacroAssemblerMIPS;
-class Operand;
-
 // A BOffImm16 is a 16 bit immediate that is used for branches.
 class BOffImm16
 {
@@ -565,7 +565,7 @@
 class Assembler;
 typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer;
 
-class Assembler : public AssemblerShared
+class Assembler
 {
   public:
 
@@ -582,6 +582,7 @@
         LessThanOrEqual,
         Overflow,
         Signed,
+        Unsigned,
         NotSigned,
         Zero,
         NonZero,
@@ -647,7 +648,7 @@
   public:
     uint32_t actualOffset(uint32_t) const;
     uint32_t actualIndex(uint32_t) const;
-    static uint8_t *PatchableJumpAddress(JitCode *code, uint32_t index);
+    static uint8_t *PatchableJumpAddress(IonCode *code, uint32_t index);
   protected:
 
     // structure for fixing up pc-relative loads/jumps when a the machine code
@@ -724,12 +725,6 @@
     void copyPreBarrierTable(uint8_t *dest);
 
     bool addCodeLabel(CodeLabel label);
-    size_t numCodeLabels() const {
-        return codeLabels_.length();
-    }
-    CodeLabel codeLabel(size_t i) {
-        return codeLabels_[i];
-    }
 
     // Size of the instruction stream, in bytes.
     size_t size() const;
@@ -742,13 +737,13 @@
     size_t bytesNeeded() const;
 
     // Write a blob of binary into the instruction stream *OR*
-    // into a destination address. If dest is nullptr (the default), then the
+    // into a destination address. If dest is NULL (the default), then the
     // instruction gets written into the instruction stream. If dest is not null
     // it is interpreted as a pointer to the location that we want the
     // instruction to be written.
-    BufferOffset writeInst(uint32_t x, uint32_t *dest = nullptr);
+    BufferOffset writeInst(uint32_t x, uint32_t *dest = NULL);
     // A static variant for the cases where we don't want to have an assembler
-    // object at all. Normally, you would use the dummy (nullptr) object.
+    // object at all. Normally, you would use the dummy (NULL) object.
     static void writeInstStatic(uint32_t x, uint32_t *dest);
 
   public:
@@ -938,15 +933,15 @@
     void as_break(uint32_t code);
 
   public:
-    static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
-    static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
+    static void TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader);
+    static void TraceDataRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader);
 
   protected:
     InstImm invertBranch(InstImm branch, BOffImm16 skipOffset);
     void bind(InstImm *inst, uint32_t branch, uint32_t target);
-    void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
-        enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind));
-        if (kind == Relocation::JITCODE)
+    void addPendingJump(BufferOffset src, void* target, Relocation::Kind kind) {
+        enoughMemory_ &= jumps_.append(RelativePatch(src, target, kind));
+        if (kind == Relocation::IONCODE)
             writeRelocation(src);
     }
 
@@ -978,16 +973,14 @@
                                         Register reg, uint32_t value);
 
     static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
-    static void patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
-                                        PatchedImmPtr expectedValue);
-    static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
-                                        ImmPtr expectedValue);
+    static void patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue,
+                                        ImmWord expectedValue);
     static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm);
     static uint32_t alignDoubleArg(uint32_t offset) {
         return (offset + 1U) &~ 1U;
     }
 
-    static uint8_t *nextInstruction(uint8_t *instruction, uint32_t *count = nullptr);
+    static uint8_t *nextInstruction(uint8_t *instruction, uint32_t *count = NULL);
 
     static void ToggleToJmp(CodeLocationLabel inst_);
     static void ToggleToCmp(CodeLocationLabel inst_);
@@ -997,9 +990,6 @@
     static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst);
     void processCodeLabels(uint8_t *rawCode);
 
-    bool bailed() {
-        return m_buffer.bail();
-    }
 }; // Assembler
 
 // An Instruction is a structure for both encoding and decoding any and all
@@ -1246,7 +1236,7 @@
 {
     JS_ASSERT(usedArgSlots >= NumIntArgRegs);
     // Even register arguments have place reserved on stack.
-    return usedArgSlots * sizeof(intptr_t);
+    return usedArgSlots * STACK_SLOT_SIZE;
 }
 
 } // namespace jit
diff --git a/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp
index 4cde9d7..2d5364e 100644
--- a/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp
+++ b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.cpp
@@ -9,6 +9,10 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 
+#include "jit/Bailouts.h"
+#include "jit/IonCompartment.h"
+#include "jit/IonFrames-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
@@ -21,7 +25,7 @@
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
-    type_ = JitFrame_IonJS;
+    type_ = IonFrame_OptimizedJS;
     topFrameSize_ = current_ - sp;
     topIonScript_ = script()->ionScript();
 
@@ -32,14 +36,19 @@
 
     // Compute the snapshot offset from the bailout ID.
     JitActivation *activation = activations.activation()->asJit();
-    JSRuntime *rt = activation->compartment()->runtimeFromMainThread();
-    JitCode *code = rt->jitRuntime()->getBailoutTable(bailout->frameClass());
+
+    JSCompartment* jsCompartment = activation->compartment();
+
+    IonCompartment* ionCompartment = jsCompartment->ionCompartment();
+
+    IonCode* code = ionCompartment->getBailoutTable(bailout->frameClass());
+
     uintptr_t tableOffset = bailout->tableOffset();
     uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw());
 
-    MOZ_ASSERT(tableOffset >= tableStart &&
+    JS_ASSERT(tableOffset >= tableStart &&
               tableOffset < tableStart + code->instructionsSize());
-    MOZ_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
+    JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0);
 
     uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
     MOZ_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
@@ -57,7 +66,7 @@
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
     current_ = (uint8_t*) bailout->fp();
-    type_ = JitFrame_IonJS;
+    type_ = IonFrame_OptimizedJS;
     topFrameSize_ = current_ - bailout->sp();
     snapshotOffset_ = osiIndex->snapshotOffset();
 }
diff --git a/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h
index dd14d1e..2f82a77 100644
--- a/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/Bailouts-mips.h
@@ -8,7 +8,6 @@
 #define jit_mips_Bailouts_mips_h
 
 #include "jit/Bailouts.h"
-#include "jit/JitCompartment.h"
 
 namespace js {
 namespace jit {
@@ -26,8 +25,8 @@
     };
 
   protected:
-    mozilla::Array<double, FloatRegisters::Total> fpregs_;
-    mozilla::Array<uintptr_t, Registers::Total> regs_;
+    double    fpregs_[FloatRegisters::Total];
+    uintptr_t regs_[Registers::Total];
 
     uintptr_t snapshotOffset_;
     uintptr_t padding_;
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp
index 69c935b..aa925a4 100644
--- a/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp
+++ b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.cpp
@@ -9,8 +9,8 @@
 using namespace js;
 using namespace js::jit;
 
-BaselineCompilerMIPS::BaselineCompilerMIPS(JSContext *cx, TempAllocator &alloc,
+BaselineCompilerMIPS::BaselineCompilerMIPS(JSContext *cx,
                                            HandleScript script)
-  : BaselineCompilerShared(cx, alloc, script)
+  : BaselineCompilerShared(cx, script)
 {
 }
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h
index 7db49b3..e160900 100644
--- a/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/BaselineCompiler-mips.h
@@ -15,7 +15,7 @@
 class BaselineCompilerMIPS : public BaselineCompilerShared
 {
   protected:
-    BaselineCompilerMIPS(JSContext *cx, TempAllocator &alloc, HandleScript script);
+    BaselineCompilerMIPS(JSContext *cx, HandleScript script);
 };
 
 typedef BaselineCompilerMIPS BaselineCompilerSpecific;
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h b/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h
index 565e8df..ea87db6 100644
--- a/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/BaselineHelpers-mips.h
@@ -79,7 +79,7 @@
 }
 
 inline void
-EmitTailCallVM(JitCode *target, MacroAssembler &masm, uint32_t argSize)
+EmitTailCallVM(IonCode *target, MacroAssembler &masm, uint32_t argSize)
 {
     // We assume during this that R0 and R1 have been pushed, and that R2 is
     // unused.
@@ -99,7 +99,7 @@
     // keep it there through the stub calls), but the VMWrapper code being
     // called expects the return address to also be pushed on the stack.
     MOZ_ASSERT(BaselineTailCallReg == ra);
-    masm.makeFrameDescriptor(t6, JitFrame_BaselineJS);
+    masm.makeFrameDescriptor(t6, IonFrame_BaselineJS);
     masm.subPtr(Imm32(sizeof(IonCommonFrameLayout)), StackPointer);
     masm.storePtr(t6, Address(StackPointer, IonCommonFrameLayout::offsetOfDescriptor()));
     masm.storePtr(ra, Address(StackPointer, IonCommonFrameLayout::offsetOfReturnAddress()));
@@ -116,11 +116,11 @@
     masm.addPtr(Imm32(sizeof(intptr_t) * 2), reg);
     masm.subPtr(BaselineStackReg, reg);
 
-    masm.makeFrameDescriptor(reg, JitFrame_BaselineStub);
+    masm.makeFrameDescriptor(reg, IonFrame_BaselineStub);
 }
 
 inline void
-EmitCallVM(JitCode *target, MacroAssembler &masm)
+EmitCallVM(IonCode *target, MacroAssembler &masm)
 {
     EmitCreateStubFrameDescriptor(masm, t6);
     masm.push(t6);
@@ -153,7 +153,7 @@
     // BaselineStubFrame if needed.
 
     // Push frame descriptor and return address.
-    masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
+    masm.makeFrameDescriptor(scratch, IonFrame_BaselineJS);
     masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
     masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor)));
     masm.storePtr(BaselineTailCallReg, Address(StackPointer,
@@ -241,7 +241,7 @@
 }
 
 inline void
-EmitCallTypeUpdateIC(MacroAssembler &masm, JitCode *code, uint32_t objectOffset)
+EmitCallTypeUpdateIC(MacroAssembler &masm, IonCode *code, uint32_t objectOffset)
 {
     // R0 contains the value that needs to be typechecked.
     // The object we're updating is a boxed Value on the stack, at offset
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp
index 777f5a6..68a0a06 100644
--- a/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp
+++ b/src/third_party/mozjs/js/src/jit/mips/BaselineIC-mips.cpp
@@ -178,7 +178,7 @@
         }
         break;
       default:
-        MOZ_ASSUME_UNREACHABLE("Unhandled op for BinaryArith_Int32.");
+        JS_NOT_REACHED("Unhandled op for BinaryArith_Int32.");
     }
 
     EmitReturnFromIC(masm);
@@ -207,7 +207,7 @@
         masm.neg32(R0.payloadReg());
         break;
       default:
-        MOZ_ASSUME_UNREACHABLE("Unexpected op");
+        JS_NOT_REACHED("Unexpected op");
         return false;
     }
 
diff --git a/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h b/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h
index 6ecdfea..2c93ed0 100644
--- a/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/BaselineRegisters-mips.h
@@ -14,31 +14,31 @@
 namespace js {
 namespace jit {
 
-static MOZ_CONSTEXPR_VAR Register BaselineFrameReg = s5;
-static MOZ_CONSTEXPR_VAR Register BaselineStackReg = sp;
+static const Register BaselineFrameReg = s5;
+static const Register BaselineStackReg = sp;
 
-static MOZ_CONSTEXPR_VAR ValueOperand R0(v1, v0);
-static MOZ_CONSTEXPR_VAR ValueOperand R1(s7, s6);
-static MOZ_CONSTEXPR_VAR ValueOperand R2(t7, t6);
+static const ValueOperand R0(v1, v0);
+static const ValueOperand R1(s7, s6);
+static const ValueOperand R2(t7, t6);
 
 // BaselineTailCallReg and BaselineStubReg
 // These use registers that are not preserved across calls.
-static MOZ_CONSTEXPR_VAR Register BaselineTailCallReg = ra;
-static MOZ_CONSTEXPR_VAR Register BaselineStubReg = t5;
+static const Register BaselineTailCallReg = ra;
+static const Register BaselineStubReg = t5;
 
-static MOZ_CONSTEXPR_VAR Register ExtractTemp0 = InvalidReg;
-static MOZ_CONSTEXPR_VAR Register ExtractTemp1 = InvalidReg;
+static const Register ExtractTemp0 = InvalidReg;
+static const Register ExtractTemp1 = InvalidReg;
 
 // Register used internally by MacroAssemblerMIPS.
-static MOZ_CONSTEXPR_VAR Register BaselineSecondScratchReg = SecondScratchReg;
+static const Register BaselineSecondScratchReg = SecondScratchReg;
 
 // Note that BaselineTailCallReg is actually just the link register.
 // In MIPS code emission, we do not clobber BaselineTailCallReg since we keep
 // the return address for calls there.
 
 // FloatReg0 must be equal to ReturnFloatReg.
-static MOZ_CONSTEXPR_VAR FloatRegister FloatReg0 = f0;
-static MOZ_CONSTEXPR_VAR FloatRegister FloatReg1 = f2;
+static const FloatRegister FloatReg0 = f0;
+static const FloatRegister FloatReg1 = f2;
 
 } // namespace jit
 } // namespace js
diff --git a/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp
index fd9be18..5e5c085 100644
--- a/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -12,9 +12,10 @@
 #include "jscompartment.h"
 #include "jsnum.h"
 
+#include "jit/PerfSpewer.h"
 #include "jit/CodeGenerator.h"
 #include "jit/IonFrames.h"
-#include "jit/JitCompartment.h"
+#include "jit/IonCompartment.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "vm/Shape.h"
@@ -23,16 +24,15 @@
 
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
+#include "jit/MoveEmitter.h"
+
 using namespace js;
 using namespace js::jit;
 
-using mozilla::FloorLog2;
-using mozilla::NegativeInfinity;
-using JS::GenericNaN;
-
 // shared
 CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
-  : CodeGeneratorShared(gen, graph, masm)
+  : CodeGeneratorShared(gen, graph, masm),
+    deoptLabel_(NULL)
 {
 }
 
@@ -49,13 +49,15 @@
         masm.checkStackAlignment();
     }
 
+    returnLabel_ = new HeapLabel();
+
     return true;
 }
 
 bool
 CodeGeneratorMIPS::generateEpilogue()
 {
-    masm.bind(&returnLabel_);
+    masm.bind(returnLabel_);
 #if JS_TRACE_LOGGING
     masm.tracelogStop();
 #endif
@@ -79,30 +81,7 @@
                                  MBasicBlock *mir, Assembler::DoubleCondition cond)
 {
     Label *label = mir->lir()->label();
-    if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
-        // Note: the backedge is initially a jump to the next instruction.
-        // It will be patched to the target block's label during link().
-        RepatchLabel rejoin;
-
-        CodeOffsetJump backedge;
-        Label skip;
-        if (fmt == Assembler::DoubleFloat)
-            masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
-        else
-            masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
-
-        backedge = masm.jumpWithPatch(&rejoin);
-        masm.bind(&rejoin);
-        masm.bind(&skip);
-
-        if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry)))
-            MOZ_CRASH();
-    } else {
-        if (fmt == Assembler::DoubleFloat)
-            masm.branchDouble(cond, lhs, rhs, mir->lir()->label());
-        else
-            masm.branchFloat(cond, lhs, rhs, mir->lir()->label());
-    }
+    masm.branchDouble(cond, lhs, rhs, mir->lir()->label());
 }
 
 bool
@@ -143,7 +122,7 @@
 bool
 CodeGeneratorMIPS::visitCompareAndBranch(LCompareAndBranch *comp)
 {
-    Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
+    Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop());
     if (comp->right()->isConstant()) {
         emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond,
                    comp->ifTrue(), comp->ifFalse());
@@ -164,9 +143,9 @@
     if (!CodeGeneratorShared::generateOutOfLineCode())
         return false;
 
-    if (deoptLabel_.used()) {
+    if (deoptLabel_) {
         // All non-table-based bailouts will go here.
-        masm.bind(&deoptLabel_);
+        masm.bind(deoptLabel_);
 
         // Push the frame size, so the handler can recover the IonScript.
         // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk
@@ -174,7 +153,9 @@
         // the same.
         masm.move32(Imm32(frameSize()), ra);
 
-        JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
+        // IonCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
+        IonCompartment *ion = GetIonContext()->compartment->ionCompartment();
+        IonCode *handler = ion->getGenericBailoutHandler();
 
         masm.branch(handler);
     }
@@ -185,16 +166,13 @@
 bool
 CodeGeneratorMIPS::bailoutFrom(Label *label, LSnapshot *snapshot)
 {
-    if (masm.bailed())
-        return false;
-    MOZ_ASSERT(label->used());
-    MOZ_ASSERT(!label->bound());
+    JS_ASSERT(label->used() && !label->bound());
 
     CompileInfo &info = snapshot->mir()->block()->info();
     switch (info.executionMode()) {
       case ParallelExecution: {
         // in parallel mode, make no attempt to recover, just signal an error.
-        OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
+        OutOfLineParallelAbort* ool = oolParallelAbort(ParallelBailoutUnsupported,
                                              snapshot->mir()->block(),
                                              snapshot->mir()->pc());
         masm.retarget(label, ool->entry());
@@ -203,7 +181,7 @@
       case SequentialExecution:
         break;
       default:
-        MOZ_ASSUME_UNREACHABLE("No such execution mode");
+        JS_NOT_REACHED("No such execution mode");
     }
 
     if (!encode(snapshot))
@@ -212,11 +190,11 @@
     // Though the assembler doesn't track all frame pushes, at least make sure
     // the known value makes sense. We can't use bailout tables if the stack
     // isn't properly aligned to the static frame size.
-    MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
+    JS_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
                   frameClass_.frameSize() == masm.framePushed());
 
     // We don't use table bailouts because retargeting is easier this way.
-    OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed());
+    OutOfLineBailout *ool = new OutOfLineBailout(snapshot, masm.framePushed());
     if (!addOutOfLineCode(ool)) {
         return false;
     }
@@ -237,11 +215,15 @@
 bool
 CodeGeneratorMIPS::visitOutOfLineBailout(OutOfLineBailout *ool)
 {
+    if (!deoptLabel_) {
+        deoptLabel_ = new HeapLabel();
+    }
+
     // Push snapshotOffset and make sure stack is aligned.
     masm.subPtr(Imm32(2 * sizeof(void *)), StackPointer);
     masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0));
 
-    masm.jump(&deoptLabel_);
+    masm.jump(deoptLabel_);
     return true;
 }
 
@@ -284,7 +266,7 @@
     masm.ma_b(&done, ShortJump);
 
     masm.bind(&nan);
-    masm.loadConstantDouble(GenericNaN(), output);
+    masm.loadStaticDouble(&js_NaN, output);
     masm.ma_b(&done, ShortJump);
 
     masm.bind(&returnSecond);
@@ -304,15 +286,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitAbsF(LAbsF *ins)
-{
-    FloatRegister input = ToFloatRegister(ins->input());
-    MOZ_ASSERT(input == ToFloatRegister(ins->output()));
-    masm.as_abss(input, input);
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitSqrtD(LSqrtD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
@@ -322,15 +295,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitSqrtF(LSqrtF *ins)
-{
-    FloatRegister input = ToFloatRegister(ins->input());
-    FloatRegister output = ToFloatRegister(ins->output());
-    masm.as_sqrts(output, input);
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitAddI(LAddI *ins)
 {
     const LAllocation *lhs = ins->getOperand(0);
@@ -437,7 +401,8 @@
             }
             break;
           default:
-            uint32_t shift = FloorLog2(constant);
+            uint32_t shift;
+            JS_FLOOR_LOG2(shift, constant);
 
             if (!mul->canOverflow() && (constant > 0)) {
                 // If it cannot overflow, we can do lots of optimizations.
@@ -453,7 +418,9 @@
                 // If the constant cannot be encoded as (1<<C1), see if it can
                 // be encoded as (1<<C1) | (1<<C2), which can be computed
                 // using an add and a shift.
-                uint32_t shift_rest = FloorLog2(rest);
+                uint32_t shift_rest;
+                JS_FLOOR_LOG2(shift_rest, rest);
+
                 if (src != dest && (1u << shift_rest) == rest) {
                     masm.ma_sll(dest, src, Imm32(shift - shift_rest));
                     masm.add32(src, dest);
@@ -620,12 +587,6 @@
                 return false;
         }
 
-        if (!mir->canBeNegativeDividend()) {
-            // Numerator is unsigned, so needs no adjusting. Do the shift.
-            masm.ma_sra(dest, lhs, Imm32(shift));
-            return true;
-        }
-
         // Adjust the value so that shifting produces a correctly rounded result
         // when the numerator is negative. See 10-1 "Signed Division by a Known
         // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight.
@@ -770,7 +731,7 @@
     }
     if (mir->canBeNegativeDividend()) {
         if (!mir->isTruncated()) {
-            MOZ_ASSERT(mir->fallible());
+            JS_ASSERT(mir->fallible());
             if (!bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot()))
                 return false;
         } else {
@@ -787,20 +748,22 @@
     Register src = ToRegister(ins->getOperand(0));
     Register dest = ToRegister(ins->getDef(0));
     Register tmp = ToRegister(ins->getTemp(0));
+
     MMod *mir = ins->mir();
 
     if (!mir->isTruncated() && mir->canBeNegativeDividend()) {
-        MOZ_ASSERT(mir->fallible());
+        JS_ASSERT(mir->fallible());
 
         Label bail;
         masm.ma_mod_mask(src, dest, tmp, ins->shift(), &bail);
         if (!bailoutFrom(&bail, ins->snapshot()))
             return false;
     } else {
-        masm.ma_mod_mask(src, dest, tmp, ins->shift(), nullptr);
+        masm.ma_mod_mask(src, dest, tmp, ins->shift(), NULL);
     }
     return true;
 }
+
 bool
 CodeGeneratorMIPS::visitBitNotI(LBitNotI *ins)
 {
@@ -839,7 +802,7 @@
             masm.ma_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
         break;
       default:
-        MOZ_ASSUME_UNREACHABLE("unexpected binary opcode");
+        JS_NOT_REACHED("unexpected binary opcode");
     }
 
     return true;
@@ -880,7 +843,7 @@
             }
             break;
           default:
-            MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
+            JS_NOT_REACHED("Unexpected shift op");
         }
     } else {
         // The shift amounts should be AND'ed into the 0-31 range
@@ -902,7 +865,7 @@
             }
             break;
           default:
-            MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
+            JS_NOT_REACHED("Unexpected shift op");
         }
     }
 
@@ -937,7 +900,7 @@
     Label done, skip;
 
     // Masm.pow(-Infinity, 0.5) == Infinity.
-    masm.loadConstantDouble(NegativeInfinity<double>(), ScratchFloatReg);
+    masm.loadConstantDouble(js_NegativeInfinity, ScratchFloatReg);
     masm.ma_bc1d(input, ScratchFloatReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump);
     masm.as_negd(output, ScratchFloatReg);
     masm.ma_b(&done, ShortJump);
@@ -953,6 +916,8 @@
     return true;
 }
 
+typedef MoveResolver::MoveOperand MoveOperand;
+
 MoveOperand
 CodeGeneratorMIPS::toMoveOperand(const LAllocation *a) const
 {
@@ -1041,7 +1006,7 @@
     // To fill in the CodeLabels for the case entries, we need to first
     // generate the case entries (we don't yet know their offsets in the
     // instruction stream).
-    OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(mir);
+    OutOfLineTableSwitch *ool = new OutOfLineTableSwitch(mir);
     if (!addOutOfLineCode(ool))
         return false;
 
@@ -1075,33 +1040,7 @@
         masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
         break;
       default:
-        MOZ_ASSUME_UNREACHABLE("unexpected opcode");
-    }
-    return true;
-}
-
-bool
-CodeGeneratorMIPS::visitMathF(LMathF *math)
-{
-    const LAllocation *src1 = math->getOperand(0);
-    const LAllocation *src2 = math->getOperand(1);
-    const LDefinition *output = math->getDef(0);
-
-    switch (math->jsop()) {
-      case JSOP_ADD:
-        masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
-        break;
-      case JSOP_SUB:
-        masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
-        break;
-      case JSOP_MUL:
-        masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
-        break;
-      case JSOP_DIV:
-        masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
-        break;
-      default:
-        MOZ_ASSUME_UNREACHABLE("unexpected opcode");
+        JS_NOT_REACHED("unexpected opcode");
     }
     return true;
 }
@@ -1144,43 +1083,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitFloorF(LFloorF *lir)
-{
-    FloatRegister input = ToFloatRegister(lir->input());
-    FloatRegister scratch = ScratchFloatReg;
-    Register output = ToRegister(lir->output());
-
-    Label skipCheck, done;
-
-    // If Nan, 0 or -0 check for bailout
-    masm.loadConstantFloat32(0.0, scratch);
-    masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
-
-    // If binary value is not zero, it is NaN or -0, so we bail.
-    masm.moveFromDoubleLo(input, SecondScratchReg);
-    if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()))
-        return false;
-
-    // Input was zero, so return zero.
-    masm.move32(Imm32(0), output);
-    masm.ma_b(&done, ShortJump);
-
-    masm.bind(&skipCheck);
-    masm.as_floorws(scratch, input);
-    masm.moveFromDoubleLo(scratch, output);
-
-    if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
-        return false;
-
-    if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()))
-        return false;
-
-    masm.bind(&done);
-
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitRound(LRound *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
@@ -1247,83 +1149,11 @@
 }
 
 bool
-CodeGeneratorMIPS::visitRoundF(LRoundF *lir)
-{
-    FloatRegister input = ToFloatRegister(lir->input());
-    FloatRegister temp = ToFloatRegister(lir->temp());
-    FloatRegister scratch = ScratchFloatReg;
-    Register output = ToRegister(lir->output());
-
-    Label bail, negative, end, skipCheck;
-
-    // Load 0.5 in the temp register.
-    masm.loadConstantFloat32(0.5, temp);
-
-    // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
-    masm.loadConstantFloat32(0.0, scratch);
-    masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump);
-
-    // If Nan, 0 or -0 check for bailout
-    masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
-
-    // If binary value is not zero, it is NaN or -0, so we bail.
-    masm.moveFromFloat32(input, SecondScratchReg);
-    if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()))
-        return false;
-
-    // Input was zero, so return zero.
-    masm.move32(Imm32(0), output);
-    masm.ma_b(&end, ShortJump);
-
-    masm.bind(&skipCheck);
-    masm.loadConstantFloat32(0.5, scratch);
-    masm.as_adds(scratch, input, scratch);
-    masm.as_floorws(scratch, scratch);
-
-    masm.moveFromFloat32(scratch, output);
-
-    if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
-        return false;
-
-    if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()))
-        return false;
-
-    masm.jump(&end);
-
-    // Input is negative, but isn't -0.
-    masm.bind(&negative);
-    masm.as_adds(temp, input, temp);
-
-    // If input + 0.5 >= 0, input is a negative number >= -0.5 and the
-    // result is -0.
-    masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail);
-    if (!bailoutFrom(&bail, lir->snapshot()))
-        return false;
-
-    // Truncate and round toward zero.
-    // This is off-by-one for everything but integer-valued inputs.
-    masm.as_floorws(scratch, temp);
-    masm.moveFromFloat32(scratch, output);
-
-    if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
-        return false;
-
-    masm.bind(&end);
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitTruncateDToInt32(LTruncateDToInt32 *ins)
 {
     return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()));
 }
 
-bool
-CodeGeneratorMIPS::visitTruncateFToInt32(LTruncateFToInt32 *ins)
-{
-    return emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()));
-}
-
 static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 };
 
 FrameSizeClass
@@ -1400,22 +1230,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitBoxFloatingPoint(LBoxFloatingPoint *box)
-{
-    const LDefinition *payload = box->getDef(PAYLOAD_INDEX);
-    const LDefinition *type = box->getDef(TYPE_INDEX);
-    const LAllocation *in = box->getOperand(0);
-
-    FloatRegister reg = ToFloatRegister(in);
-    if (box->type() == MIRType_Float32) {
-        masm.convertFloat32ToDouble(reg, ScratchFloatReg);
-        reg = ScratchFloatReg;
-    }
-    masm.ma_mv(reg, ValueOperand(ToRegister(type), ToRegister(payload)));
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitUnbox(LUnbox *unbox)
 {
     // Note that for unbox, the type and payload indexes are switched on the
@@ -1440,14 +1254,6 @@
     return true;
 }
 
-bool
-CodeGeneratorMIPS::visitFloat32(LFloat32 *ins)
-{
-    const LDefinition *out = ins->getDef(0);
-    masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
-    return true;
-}
-
 Register
 CodeGeneratorMIPS::splitTagForTest(const ValueOperand &value)
 {
@@ -1478,29 +1284,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitTestFAndBranch(LTestFAndBranch *test)
-{
-    FloatRegister input = ToFloatRegister(test->input());
-
-    MBasicBlock *ifTrue = test->ifTrue();
-    MBasicBlock *ifFalse = test->ifFalse();
-
-    masm.loadConstantFloat32(0.0, ScratchFloatReg);
-    // If 0, or NaN, the result is false.
-
-    if (isNextBlock(ifFalse->lir())) {
-        branchToBlock(Assembler::SingleFloat, input, ScratchFloatReg, ifTrue,
-                      Assembler::DoubleNotEqual);
-    } else {
-        branchToBlock(Assembler::SingleFloat, input, ScratchFloatReg, ifFalse,
-                      Assembler::DoubleEqualOrUnordered);
-        jumpToBlock(ifTrue);
-    }
-
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitCompareD(LCompareD *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
@@ -1513,19 +1296,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitCompareF(LCompareF *comp)
-{
-    FloatRegister lhs = ToFloatRegister(comp->left());
-    FloatRegister rhs = ToFloatRegister(comp->right());
-    Register dest = ToRegister(comp->output());
-
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
-    masm.ma_cmp_set_float32(dest, lhs, rhs, cond);
-    return true;
-}
-
-
-bool
 CodeGeneratorMIPS::visitCompareDAndBranch(LCompareDAndBranch *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
@@ -1547,27 +1317,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitCompareFAndBranch(LCompareFAndBranch *comp)
-{
-    FloatRegister lhs = ToFloatRegister(comp->left());
-    FloatRegister rhs = ToFloatRegister(comp->right());
-
-    Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
-    MBasicBlock *ifTrue = comp->ifTrue();
-    MBasicBlock *ifFalse = comp->ifFalse();
-
-    if (isNextBlock(ifFalse->lir())) {
-        branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond);
-    } else {
-        branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse,
-                      Assembler::InvertCondition(cond));
-        jumpToBlock(ifTrue);
-    }
-
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitCompareB(LCompareB *lir)
 {
     MCompare *mir = lir->mir();
@@ -1601,7 +1350,7 @@
 bool
 CodeGeneratorMIPS::visitCompareBAndBranch(LCompareBAndBranch *lir)
 {
-    MCompare *mir = lir->cmpMir();
+    MCompare *mir = lir->mir();
     const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
     const LAllocation *rhs = lir->rhs();
 
@@ -1649,7 +1398,7 @@
 bool
 CodeGeneratorMIPS::visitCompareVAndBranch(LCompareVAndBranch *lir)
 {
-    MCompare *mir = lir->cmpMir();
+    MCompare *mir = lir->mir();
     Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
     const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
     const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
@@ -1666,32 +1415,13 @@
 }
 
 bool
-CodeGeneratorMIPS::visitBitAndAndBranch(LBitAndAndBranch *lir)
-{
-    if (lir->right()->isConstant())
-        masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right())));
-    else
-        masm.ma_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right()));
-    emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(),
-               lir->ifFalse());
-    return true;
-}
-
-bool
-CodeGeneratorMIPS::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir)
+CodeGeneratorMIPS::visitUInt32ToDouble(LUInt32ToDouble *lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
     return true;
 }
 
 bool
-CodeGeneratorMIPS::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
-{
-    masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitNotI(LNotI *ins)
 {
     masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0),
@@ -1722,28 +1452,6 @@
 }
 
 bool
-CodeGeneratorMIPS::visitNotF(LNotF *ins)
-{
-    // Since this operation is not, we want to set a bit if
-    // the float32 is falsey, which means 0.0, -0.0 or NaN.
-    FloatRegister in = ToFloatRegister(ins->input());
-    Register dest = ToRegister(ins->output());
-
-    Label falsey, done;
-    masm.loadConstantFloat32(0.0, ScratchFloatReg);
-    masm.ma_bc1s(in, ScratchFloatReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump);
-
-    masm.move32(Imm32(0), dest);
-    masm.ma_b(&done, ShortJump);
-
-    masm.bind(&falsey);
-    masm.move32(Imm32(1), dest);
-
-    masm.bind(&done);
-    return true;
-}
-
-bool
 CodeGeneratorMIPS::visitLoadSlotV(LLoadSlotV *load)
 {
     const ValueOperand out = ToOutValue(load);
@@ -1925,19 +1633,8 @@
     return true;
 }
 
-bool
-CodeGeneratorMIPS::visitInterruptCheck(LInterruptCheck *lir)
-{
-    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
-    if (!ool)
-        return false;
-
-    masm.branch32(Assembler::NotEqual,
-                  AbsoluteAddress(GetIonContext()->runtime->addressOfInterrupt()), Imm32(0),
-                  ool->entry());
-    masm.bind(ool->rejoin());
-    return true;
-}
+typedef bool (*InterruptCheckFn)(JSContext *);
+static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
 
 bool
 CodeGeneratorMIPS::generateInvalidateEpilogue()
@@ -1956,122 +1653,26 @@
     // Push the Ion script onto the stack (when we determine what that
     // pointer is).
     invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
-    JitCode *thunk = gen->jitRuntime()->getInvalidationThunk();
+    IonCode* thunk = GetIonContext()->compartment->ionCompartment()->getInvalidationThunk();
 
     masm.branch(thunk);
 
     // We should never reach this point in JIT code -- the invalidation thunk
     // should pop the invalidated JS frame and return directly to its caller.
-    masm.assumeUnreachable("Should have returned directly to its caller instead of here.");
+    // masm.assumeUnreachable("Should have returned directly to its caller instead of here.");
+    masm.breakpoint();
     return true;
 }
 
 void
-DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
+ParallelGetPropertyIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
 {
-    // Can always use the scratch register on MIPS.
+    // Can always use the scratch register on ARM.
+    JS_ASSERT(ins->isGetPropertyCacheV() || ins->isGetPropertyCacheT());
     addState->dispatchScratch = ScratchRegister;
 }
 
 bool
-CodeGeneratorMIPS::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
-{
-    MOZ_ASSUME_UNREACHABLE("NYI");
-}
-
-bool
-CodeGeneratorMIPS::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
-{
-    MOZ_ASSUME_UNREACHABLE("NYI");
-}
-
-bool
-CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
-{
-    const MAsmJSLoadHeap *mir = ins->mir();
-    const LAllocation *ptr = ins->ptr();
-    const LDefinition *out = ins->output();
-
-    bool isSigned;
-    int size;
-    bool isFloat = false;
-    switch (mir->viewType()) {
-      case ArrayBufferView::TYPE_INT8:    isSigned = true;  size =  8; break;
-      case ArrayBufferView::TYPE_UINT8:   isSigned = false; size =  8; break;
-      case ArrayBufferView::TYPE_INT16:   isSigned = true;  size = 16; break;
-      case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
-      case ArrayBufferView::TYPE_INT32:   isSigned = true;  size = 32; break;
-      case ArrayBufferView::TYPE_UINT32:  isSigned = false; size = 32; break;
-      case ArrayBufferView::TYPE_FLOAT64: isFloat  = true;  size = 64; break;
-      case ArrayBufferView::TYPE_FLOAT32: isFloat  = true;  size = 32; break;
-      default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
-    }
-
-    if (ptr->isConstant()) {
-        MOZ_ASSERT(mir->skipBoundsCheck());
-        int32_t ptrImm = ptr->toConstant()->toInt32();
-        MOZ_ASSERT(ptrImm >= 0);
-        if (isFloat) {
-            if (size == 32) {
-                masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out));
-            } else {
-                masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out));
-            }
-        }  else {
-            masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm),
-                         static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
-        }
-        return true;
-    }
-
-    Register ptrReg = ToRegister(ptr);
-
-    if (mir->skipBoundsCheck()) {
-        if (isFloat) {
-            if (size == 32) {
-                masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
-            } else {
-                masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
-            }
-        } else {
-            masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
-                         static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
-        }
-        return true;
-    }
-
-    BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
-
-    Label outOfRange;
-    Label done;
-    masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump);
-    // Offset is ok, let's load value.
-    if (isFloat) {
-        if (size == 32)
-            masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
-        else
-            masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
-    } else {
-        masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
-                     static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
-    }
-    masm.ma_b(&done, ShortJump);
-    masm.bind(&outOfRange);
-    // Offset is out of range. Load default values.
-    if (isFloat) {
-        if (size == 32)
-            masm.convertDoubleToFloat32(NANReg, ToFloatRegister(out));
-        else
-            masm.moveDouble(NANReg, ToFloatRegister(out));
-    } else {
-        masm.move32(Imm32(0), ToRegister(out));
-    }
-    masm.bind(&done);
-
-    return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
-}
-
-bool
 CodeGeneratorMIPS::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
     const MAsmJSStoreHeap *mir = ins->mir();
@@ -2090,17 +1691,16 @@
       case ArrayBufferView::TYPE_UINT32:  isSigned = false; size = 32; break;
       case ArrayBufferView::TYPE_FLOAT64: isFloat  = true;  size = 64; break;
       case ArrayBufferView::TYPE_FLOAT32: isFloat  = true;  size = 32; break;
-      default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
+      default: JS_NOT_REACHED("unexpected array type");
     }
 
     if (ptr->isConstant()) {
-        MOZ_ASSERT(mir->skipBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         MOZ_ASSERT(ptrImm >= 0);
 
         if (isFloat) {
             if (size == 32) {
-                masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm));
+                JS_NOT_REACHED("No 32-bit floats in SpiderMonkey 24.");
             } else {
                 masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm));
             }
@@ -2114,18 +1714,16 @@
     Register ptrReg = ToRegister(ptr);
     Address dstAddr(ptrReg, 0);
 
-    if (mir->skipBoundsCheck()) {
-        if (isFloat) {
-            if (size == 32) {
-                masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
-            } else
-                masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
-        } else {
-            masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
-                          static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
-        }
-        return true;
+    if (isFloat) {
+        if (size == 32) {
+            JS_NOT_REACHED("No 32-bit floats in SpiderMonkey 24.");
+        } else
+            masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
+    } else {
+        masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
+                      static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
     }
+    return true;
 
     BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
 
@@ -2135,7 +1733,7 @@
     // Offset is ok, let's store value.
     if (isFloat) {
         if (size == 32) {
-            masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
+            JS_NOT_REACHED("No 32-bit floats in SM42.");
         } else
             masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
     } else {
@@ -2253,8 +1851,6 @@
     unsigned addr = mir->globalDataOffset();
     if (mir->type() == MIRType_Int32)
         masm.load32(Address(GlobalReg, addr), ToRegister(ins->output()));
-    else if (mir->type() == MIRType_Float32)
-        masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
     else
         masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
     return true;
@@ -2270,8 +1866,6 @@
     unsigned addr = mir->globalDataOffset();
     if (mir->value()->type() == MIRType_Int32)
         masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr));
-    else if (mir->value()->type() == MIRType_Float32)
-        masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
     else
         masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
     return true;
@@ -2321,23 +1915,152 @@
 }
 
 bool
-CodeGeneratorMIPS::visitNegF(LNegF *ins)
+CodeGeneratorMIPS::visitOsrValue(LOsrValue *value)
 {
-    FloatRegister input = ToFloatRegister(ins->input());
-    FloatRegister output = ToFloatRegister(ins->output());
+    const LAllocation *frame   = value->getOperand(0);
+    const ValueOperand out     = ToOutValue(value);
 
-    masm.as_negs(output, input);
+    const ptrdiff_t frameOffset = value->mir()->frameOffset();
+
+    masm.loadValue(Address(ToRegister(frame), frameOffset), out);
     return true;
 }
 
 bool
-CodeGeneratorMIPS::visitForkJoinGetSlice(LForkJoinGetSlice *ins)
+CodeGeneratorMIPS::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
 {
-    MOZ_ASSUME_UNREACHABLE("NYI");
+    JS_NOT_REACHED("NYI");
 }
 
-JitCode *
-JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
+bool
+CodeGeneratorMIPS::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
 {
-    MOZ_ASSUME_UNREACHABLE("NYI");
+    JS_NOT_REACHED("NYI");
 }
+
+
+bool
+CodeGeneratorMIPS::visitInterruptCheck(LInterruptCheck *lir)
+{
+    OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
+    if (!ool)
+        return false;
+
+    masm.branch32(Assembler::NotEqual,
+                  AbsoluteAddress((void*)&gen->compartment->rt->interrupt), Imm32(0),
+                  ool->entry());
+    masm.bind(ool->rejoin());
+    return true;
+}
+
+bool
+CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
+{
+    const MAsmJSLoadHeap *mir = ins->mir();
+    const LAllocation *ptr = ins->ptr();
+    const LDefinition *out = ins->output();
+
+    bool isSigned;
+    int size;
+    bool isFloat = false;
+    switch (mir->viewType()) {
+      case ArrayBufferView::TYPE_INT8:    isSigned = true;  size =  8; break;
+      case ArrayBufferView::TYPE_UINT8:   isSigned = false; size =  8; break;
+      case ArrayBufferView::TYPE_INT16:   isSigned = true;  size = 16; break;
+      case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
+      case ArrayBufferView::TYPE_INT32:   isSigned = true;  size = 32; break;
+      case ArrayBufferView::TYPE_UINT32:  isSigned = false; size = 32; break;
+      case ArrayBufferView::TYPE_FLOAT64: isFloat  = true;  size = 64; break;
+      case ArrayBufferView::TYPE_FLOAT32: isFloat  = true;  size = 32; break;
+      default: JS_NOT_REACHED("unexpected array type");
+    }
+
+    if (ptr->isConstant()) {
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        MOZ_ASSERT(ptrImm >= 0);
+        if (isFloat) {
+            masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out));
+        }  else {
+            masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm),
+                         static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
+        }
+        return true;
+    }
+
+    Register ptrReg = ToRegister(ptr);
+
+    BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
+
+    Label outOfRange;
+    Label done;
+    masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump);
+    // Offset is ok, let's load value.
+    if (isFloat) {
+        if (size == 32)
+            JS_NOT_REACHED("No 32-bit floats in SpiderMonkey 24.");
+        else
+            masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
+    } else {
+        masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
+                     static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
+    }
+    masm.ma_b(&done, ShortJump);
+    masm.bind(&outOfRange);
+    // Offset is out of range. Load default values.
+    if (isFloat) {
+        masm.moveDouble(NANReg, ToFloatRegister(out));
+    } else {
+        masm.move32(Imm32(0), ToRegister(out));
+    }
+    masm.bind(&done);
+
+    return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
+}
+
+bool
+CodeGeneratorMIPS::visitBoxDouble(LBoxDouble* box)
+{
+    const LDefinition *payload = box->getDef(PAYLOAD_INDEX);
+    const LDefinition *type = box->getDef(TYPE_INDEX);
+    const LAllocation *in = box->getOperand(0);
+
+    FloatRegister reg = ToFloatRegister(in);
+    masm.ma_mv(reg, ValueOperand(ToRegister(type), ToRegister(payload)));
+    return true;
+}
+
+bool
+CodeGeneratorMIPS::visitMoveGroup(LMoveGroup *group)
+{
+    if (!group->numMoves())
+        return true;
+
+    MoveResolver &resolver = masm.moveResolver();
+
+    for (size_t i = 0; i < group->numMoves(); i++) {
+        const LMove &move = group->getMove(i);
+
+        const LAllocation *from = move.from();
+        const LAllocation *to = move.to();
+
+        // No bogus moves.
+        JS_ASSERT(*from != *to);
+        JS_ASSERT(!from->isConstant());
+        JS_ASSERT(from->isDouble() == to->isDouble());
+
+        MoveResolver::Move::Kind kind = from->isDouble() ? MoveResolver::Move::DOUBLE : MoveResolver::Move::GENERAL;
+
+        if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), kind))
+            return false;
+    }
+
+    if (!resolver.resolve())
+        return false;
+
+    MoveEmitter emitter(masm);
+    emitter.emit(resolver);
+    emitter.finish();
+
+    return true;
+}
+
diff --git a/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h
index 95933a4..43b7ecd 100644
--- a/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h
+++ b/src/third_party/mozjs/js/src/jit/mips/CodeGenerator-mips.h
@@ -26,8 +26,8 @@
 
   protected:
     // Label for the common return path.
-    NonAssertingLabel returnLabel_;
-    NonAssertingLabel deoptLabel_;
+    HeapLabel *returnLabel_;
+    HeapLabel *deoptLabel_;
 
     inline Address ToAddress(const LAllocation &a) {
         MOZ_ASSERT(a.isMemory());
@@ -70,7 +70,7 @@
         return ToOperand(def->output());
     }
 
-    MoveOperand toMoveOperand(const LAllocation *a) const;
+    MoveResolver::MoveOperand toMoveOperand(const LAllocation* a) const;
 
     template <typename T1, typename T2>
     bool bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot *snapshot) {
@@ -87,7 +87,7 @@
               return bailoutCmp32(c, lhs.toReg(), rhs, snapshot);
         if (lhs.getTag() == Operand::MEM)
               return bailoutCmp32(c, lhs.toAddress(), rhs, snapshot);
-        MOZ_ASSUME_UNREACHABLE("Invalid operand tag.");
+        MOZ_NOT_REACHED("Invalid operand tag.");
         return false;
     }
     template<typename T>
@@ -114,30 +114,28 @@
     bool generateEpilogue();
     bool generateOutOfLineCode();
 
+    void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
+                       MBasicBlock *mir, Assembler::DoubleCondition cond);
+
+    // Generate a jump to the start of the specified block, adding information
+    // if this is a loop backedge. Use this in place of jumping directly to
+    // mir->lir()->label(), or use getJumpLabelForBranch() if a label to use
+    // directly is needed.
+    void jumpToBlock(MBasicBlock *mir) {
+        // No jump necessary if we can fall through to the next block.
+        if (isNextBlock(mir->lir())) {
+            return;
+        }
+
+        masm.jump(mir->lir()->label());
+    }
+
     template <typename T>
     void branchToBlock(Register lhs, T rhs, MBasicBlock *mir, Assembler::Condition cond)
     {
         Label *label = mir->lir()->label();
-        if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
-            // Note: the backedge is initially a jump to the next instruction.
-            // It will be patched to the target block's label during link().
-            RepatchLabel rejoin;
-            CodeOffsetJump backedge;
-            Label skip;
-
-            masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
-            backedge = masm.jumpWithPatch(&rejoin);
-            masm.bind(&rejoin);
-            masm.bind(&skip);
-
-            if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry)))
-                MOZ_CRASH();
-        } else {
-            masm.ma_b(lhs, rhs, label, cond);
-        }
+        masm.ma_b(lhs, rhs, label, cond);
     }
-    void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
-                       MBasicBlock *mir, Assembler::DoubleCondition cond);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
@@ -152,6 +150,7 @@
             jumpToBlock(mirTrue);
         }
     }
+
     void testNullEmitBranch(Assembler::Condition cond, const ValueOperand &value,
                             MBasicBlock *ifTrue, MBasicBlock *ifFalse)
     {
@@ -169,9 +168,7 @@
     // Instruction visitors.
     virtual bool visitMinMaxD(LMinMaxD *ins);
     virtual bool visitAbsD(LAbsD *ins);
-    virtual bool visitAbsF(LAbsF *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
-    virtual bool visitSqrtF(LSqrtF *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitBitNotI(LBitNotI *ins);
@@ -192,30 +189,20 @@
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
-    virtual bool visitTestFAndBranch(LTestFAndBranch *test);
     virtual bool visitCompareD(LCompareD *comp);
-    virtual bool visitCompareF(LCompareF *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
-    virtual bool visitCompareFAndBranch(LCompareFAndBranch *comp);
     virtual bool visitCompareB(LCompareB *lir);
     virtual bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     virtual bool visitCompareV(LCompareV *lir);
     virtual bool visitCompareVAndBranch(LCompareVAndBranch *lir);
-    virtual bool visitBitAndAndBranch(LBitAndAndBranch *lir);
-    virtual bool v